mirror of
https://github.com/kbparagua/paloma
synced 2023-03-27 23:21:17 -04:00
489 lines
13 KiB
Markdown
489 lines
13 KiB
Markdown
Paloma
|
|
======
|
|
Paloma provides a sexy and logical way of organizing Rails javascript files.
|
|
Its core feature is a powerful yet simple way to execute page-specific javascript code.
|
|
|
|
But there are more sexy features Paloma has to offer!
|
|
|
|
Advantages
|
|
-
|
|
* Javascript files are organized per controller just like app/views folder of Rails.
|
|
* Javascript file per controller's action.
|
|
* Choose what specific javascript codes to run per page.
|
|
* Easily make ruby variables available on your javascript files.
|
|
|
|
Quick Example
|
|
-
|
|
The javascript callback file `/assets/javascripts/paloma/users/new.js`:
|
|
|
|
```javascript
|
|
Paloma.callbacks['users']['new'] = function(params){
|
|
// This will only run after executing users/new action
|
|
alert('Hello New Sexy User');
|
|
};
|
|
```
|
|
|
|
The Rails controller `app/controllers/users_controller.rb`:
|
|
|
|
```ruby
|
|
def UsersController < ApplicationController
|
|
def new
|
|
@user = User.new
|
|
# No special function to call, the javascript callback will be executed automatically
|
|
# just for this specific action.
|
|
end
|
|
end
|
|
```
|
|
|
|
That's it! Simply Sexy!
|
|
|
|
Minimum Requirements
|
|
-
|
|
* jQuery 1.7 or higher
|
|
* Rails 3.1 or higher
|
|
|
|
|
|
Install
|
|
-
|
|
Without bundler:
|
|
```
|
|
sudo gem install paloma
|
|
```
|
|
|
|
With bundler, add this to your Gemfile:
|
|
```
|
|
gem 'paloma'
|
|
```
|
|
|
|
Setup
|
|
-
|
|
On setup, the `paloma` folder will be generated in `app/assets/javascripts/` containing its required files. Run:
|
|
```
|
|
rails g paloma:setup
|
|
```
|
|
|
|
Require `paloma` in your `application.js`:
|
|
```
|
|
//= require paloma
|
|
```
|
|
|
|
Basic Directory Structure
|
|
-
|
|
`paloma` folder contains the javascript callbacks.
|
|
|
|
* paloma
|
|
* [controller]
|
|
* [action].js
|
|
* [other_action].js
|
|
* [other_controller]
|
|
* [action].js
|
|
* [other_action].js
|
|
* [more_action].js
|
|
* [namespace]
|
|
* [controller]
|
|
* [action].js
|
|
|
|
Generators
|
|
-
|
|
|
|
1. Generate a controller folder containing its required files:
|
|
```
|
|
rails g paloma:add [controller]
|
|
```
|
|
**Example:**
|
|
```
|
|
rails g paloma:add users
|
|
```
|
|
|
|
**Generates:**
|
|
* /paloma
|
|
* /users
|
|
|
|
|
|
2. Generate a callback file for a controller's action:
|
|
```
|
|
rails g paloma:add [controller] [action]
|
|
```
|
|
**Example:**
|
|
```
|
|
rails g paloma:add users new
|
|
```
|
|
|
|
**Generates:**
|
|
* /paloma
|
|
* /users
|
|
* new.js
|
|
|
|
|
|
3. Generate multiple callback files:
|
|
```
|
|
rails g paloma:add [controller] [action_1] [action_2] ... [action_n]
|
|
```
|
|
**Example:**
|
|
```
|
|
rails g paloma:add users new create edit update
|
|
```
|
|
|
|
**Generates:**
|
|
* /paloma
|
|
* /users
|
|
* new.js
|
|
* create.js
|
|
* edit.js
|
|
* update.js
|
|
|
|
4. Generate namespaced controller and callbacks:
|
|
```
|
|
rails g paloma:add [namespace]/[controller] [action_1] [action_2] ... [action_n]
|
|
```
|
|
|
|
**Example:**
|
|
```
|
|
rails g paloma:add admin/users new
|
|
```
|
|
|
|
**Generates:**
|
|
* /paloma
|
|
* /admin
|
|
* /users
|
|
* new.js
|
|
|
|
**Notes:**
|
|
|
|
* You can directly run `rails g paloma:add [controller] [action]` or `rails g paloma:add [namespace]/[controller] [action]` even the controller folder is not yet
|
|
existing on `paloma` folder. It will be created automatically.
|
|
|
|
* Controller folder and action javascript files will automatically be created after running `rails g controller` or `rails g scaffold`.
|
|
|
|
Advanced Callbacks
|
|
-
|
|
By default Paloma will execute the callback that matches the current controller and action if it finds one.
|
|
For instance, if the current response is from the `new` action of the `Users` controller, then Paloma will try to execute `callbacks['users']['new']` if it exists.
|
|
|
|
You can manipulate callback behavior by using the `js` command before the `render` or `redirect_to` command in your controllers.
|
|
|
|
1. Preventing the Callback to execute.
|
|
|
|
```ruby
|
|
def destroy
|
|
user = User.find params[:id]
|
|
user.destroy
|
|
|
|
js false
|
|
end
|
|
```
|
|
|
|
`callbacks["controller"]["destroy"]` will not be executed.
|
|
|
|
2. Using other action's callback from the same controller.
|
|
|
|
```ruby
|
|
def edit
|
|
@user = User.find params[:id]
|
|
js :new
|
|
end
|
|
```
|
|
|
|
This will execute `callback["controllers"]["new"]` instead of `callback["controllers"]["edit"]`.
|
|
|
|
3. Using other action's callback from other controller.
|
|
|
|
```ruby
|
|
def index
|
|
@users = User.all
|
|
js :controller => 'clients', :action => 'index'
|
|
end
|
|
```
|
|
|
|
This will execute `callbacks["clients"]["index"]` instead of `callbacks["controllers"]["index"]`.
|
|
|
|
|
|
4. Using other action's callback from a namespaced controller.
|
|
|
|
```ruby
|
|
class UsersController < ApplicationController
|
|
def destroy
|
|
@user = User.find params[:id]
|
|
@user.destroy
|
|
|
|
js :controller => 'admin/users', :action => :destroy
|
|
end
|
|
end
|
|
```
|
|
|
|
This will execute `callbacks["admin/users"]["destroy"]` instead of `callbacks["users"]["destroy"]`.
|
|
|
|
|
|
Passing Parameters
|
|
-
|
|
You can also pass parameters to the callback by passing a `:params` key to `js`. The passed parameters
|
|
will be available on the callback by the `params` object.
|
|
|
|
**Example:**
|
|
|
|
`users_controller.rb`
|
|
```ruby
|
|
def destroy
|
|
user = User.find params[:id]
|
|
user.destroy
|
|
|
|
js :params => {:user_id => params[:id]}
|
|
end
|
|
```
|
|
|
|
`/paloma/users/destroy.js`
|
|
```javascript
|
|
Paloma.callbacks['users']['destroy'] = function(params){
|
|
var id = params['user_id'];
|
|
alert('User ' + id + ' deleted.');
|
|
};
|
|
```
|
|
|
|
|
|
Default Parameters
|
|
-
|
|
`params['controller']` - controller name without its namespace.
|
|
|
|
`params['namespace']` - controller's namespace name.
|
|
|
|
`params['action']` - controller's action that triggers the filter or callback.
|
|
|
|
`params['controller_path']` - controller name with its namespace.
|
|
|
|
`params['callback_controller']` - callback's controller name without its namespace.
|
|
|
|
`params['callback_namespace']` - callback's namespace name.
|
|
|
|
`params['callback_action']` - callback's action name.
|
|
|
|
`params['callback_controller_path']` - callback's controller with its namespace.
|
|
|
|
## Filters
|
|
|
|
This is almost similar to Rails controller filters. These are functions executed either `before`, `after`,
|
|
or even `around` (before and after) a Paloma callback is executed.
|
|
|
|
Filters are defined on `_filters.js` files in Paloma's root folder, namespace folder, or controller folder.
|
|
|
|
### Syntax
|
|
|
|
```javascript
|
|
filter.as('filter name').before_all().perform(function(params){
|
|
alert("I'm a before filter!");
|
|
});
|
|
|
|
filter.as('another filter').after_all().perform(function(params){
|
|
alert("I'm an after filter");
|
|
});
|
|
```
|
|
|
|
### Specify Actions To Filter
|
|
|
|
1. For all actions.
|
|
|
|
```javascript
|
|
filter.as('name').before_all()
|
|
filter.as('name').after_all()
|
|
filter.as('name').around_all()
|
|
```
|
|
|
|
2. Only for specific action/s.
|
|
|
|
```javascript
|
|
filter.as('name').before('new', 'edit', 'update')
|
|
filter.as('name').after('destroy')
|
|
filter.as('name').around('show')
|
|
```
|
|
|
|
3. Except for specific action/s.
|
|
|
|
```javascript
|
|
filter.as('name').except_before('new')
|
|
filter.as('name').except_after('destroy', 'edit')
|
|
filter.as('name').except_around('show')
|
|
```
|
|
|
|
### Filter Inheritance
|
|
|
|
All `_filters.js` inherit filters defined on the global `_filters.js` file.
|
|
Controller's `_filters.js` will also inherit filters defined on its namespace `_filters.js` if it exists.
|
|
|
|
|
|
### Execution Time
|
|
|
|
Global filters (on `/paloma/_filters.js`) are executed first, then Namespace filters if any,
|
|
then Controller filters.
|
|
|
|
Before filters, as you've guessed, are executed before the callback is called.
|
|
The order of execution is based on the order of declaration.
|
|
After before filters, around filters are executed then the callback is finally executed.
|
|
After filters are called after the callback is executed, then it will execute the around filters again.
|
|
|
|
### Shared Variable Between Filter and Callback
|
|
|
|
Automatically, filters has an access to the `params` object passed via the `js` ruby method.
|
|
But you can also make a variable visible both on a filter and a callback using the `_x` object.
|
|
|
|
**Example:**
|
|
|
|
on `_filters.js`:
|
|
|
|
```javascript
|
|
filter.as('filter name').before('new').perform(function(params){
|
|
_x.sharedVariable = "Sexy Paloma";
|
|
});
|
|
```
|
|
|
|
on `new.js`:
|
|
|
|
```javascript
|
|
Paloma.callbacks['controller']['new'] = function(params){
|
|
alert(_x.sharedVariable); // outputs "Sexy Paloma";
|
|
});
|
|
```
|
|
|
|
### Skipping Filters
|
|
|
|
You can skip filters using the `skip_*_filter` or `skip_*_filters` command.
|
|
You can also specify which action or actions the filter skip is applicable using `only` or `except` command.
|
|
|
|
```javascript
|
|
filter.skip_before_filter('filter A'); // skip 'filter A' for all actions
|
|
filter.skip_after_filters('filter A', 'filter B').only('new', 'edit'); // skip 'filter A' and 'filter B' for 'new' and 'edit' actions.
|
|
filter.skip_around_filter('filter A').except('destroy'); // skip 'filter A' for all actions except for 'destroy'.
|
|
```
|
|
|
|
##Locals
|
|
|
|
Locals are variables or methods which can be made locally available within a controller or a namespace. Locals can also be made available throughout the whole Paloma files (globally).
|
|
|
|
The motivation of Locals is to organize helper methods and helper variables within a namespace or controller.
|
|
|
|
1. **Application-wide Locals**
|
|
|
|
Defined on `paloma/_locals.js`.
|
|
This contains methods and variables that are intended to be available globally.
|
|
|
|
|
|
2. **Namespace-wide Locals**
|
|
|
|
Defiend on `paloma/namespace/_locals.js`.
|
|
This contains methods and variables that are intended to be available on the specific namespace only.
|
|
|
|
|
|
3. **Controller-wide Locals**
|
|
|
|
Defined on `paloma/controller/_locals.js` or `paloma/namespace/controller/_locals.js`.
|
|
This contains methods and variables that are intended to be available on the specific controller only.
|
|
|
|
|
|
###Creating Locals
|
|
Locals can be created using the `locals` object inside `_filters.js` file.
|
|
|
|
**Example:**
|
|
|
|
```javascript
|
|
locals.helperMethod = function(){
|
|
return "Hello World";
|
|
};
|
|
|
|
locals.helperVariable = "WOW!";
|
|
```
|
|
|
|
###Accessing Locals
|
|
Locals can be accessed in your filter and callback files using the `_l` object.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
Paloma.callbacks['users']['new'] = function(params){
|
|
alert("Hello Sexy User");
|
|
|
|
_l.helperMethod();
|
|
|
|
console.log(_l.helperVariable);
|
|
};
|
|
```
|
|
|
|
###Accessing Locals From Other Controller/Namespace
|
|
Sometimes there is a need to use other's local methods and variables.
|
|
You can achieve this by using the `Paloma.locals` object or its alias `_L`.
|
|
|
|
**Example**
|
|
```javascript
|
|
Paloma.callbacks['users']['new'] = function(params){
|
|
_L.otherController.helperMethod(); // accessing local helperMethod() of the otherController
|
|
_L['otherController'].helperVariable;
|
|
}
|
|
```
|
|
|
|
|
|
###Locals Inheritance
|
|
`_locals.js` inherits locals from its parent `_locals.js`, either from namespace or application-wide.
|
|
You can also override locals inherited from parents.
|
|
|
|
**Example**
|
|
|
|
`paloma/_locals.js` contains:
|
|
```javascript
|
|
locals.globalMethod = function(){ console.log("I'm from Global"); }
|
|
```
|
|
|
|
`paloma/namespace/_locals.js` contains:
|
|
```javascript
|
|
locals.namespaceMethod = function(){ console.log("I'm from Namespace"); }
|
|
locals.anotherNamespaceMethod = function(){ console.log("I'm also from Namespace"); }
|
|
```
|
|
|
|
`paloma/namespace/controller/_locals.js` contains:
|
|
```javascript
|
|
locals.controllerMethod = function(){ console.log("I'm from Controller"); }
|
|
locals.anotherNamespacedMethod = function(){ console.log("Override!"); }; // Overrides namespace local
|
|
```
|
|
|
|
Since `controller` is under the global `_local.js` and namespace `_local.js` it automatically inherits all their locals.
|
|
So you can do something like this inside the controller callback files (or filter files):
|
|
|
|
```javascript
|
|
Paloma.callbacks['namespace/controller']['action'] = function(params){
|
|
_l.controllerMethod(); // outputs "I'm from Controller"
|
|
_l.namespacedMethod(); // outputs "I'm from Namespace"
|
|
_l.globalMethod(); // outputs "I'm from Global"
|
|
_l.anotherNamespacedMethod(); // outputs "Override!"
|
|
};
|
|
```
|
|
|
|
Callback Chains
|
|
-
|
|
Callback chains are created after a redirect action. The chain will continue to increase its length until a render action is detected.
|
|
|
|
**Example:**
|
|
|
|
```ruby
|
|
def first_action
|
|
redirect_to second_action_path
|
|
end
|
|
|
|
def second_action
|
|
redirect_to third_action_path
|
|
end
|
|
|
|
def third_action
|
|
render :template => 'third_action_view'
|
|
end
|
|
```
|
|
|
|
A request for `first_action` will lead to 2 redirects until it reaches the `third_action` and renders a result on the browser. When the `third_action` renders its response, Paloma will execute the callbacks for all the 3 actions.
|
|
|
|
The order of execution will be `[controllers]/first_action` first, then `[controllers]/second_action`, and finally `[controllers]/third_action`.
|
|
|
|
|
|
Gotchas
|
|
-
|
|
* Callbacks will not be executed if the response is `js`, `json`, `xml` or any other format except `html`.
|
|
This will not work: `render "something.js.erb"`
|
|
|
|
Credits
|
|
-
|
|
* [Karl Bryan Paragua](http://www.daftcoder.com "Daftcoder.com")
|
|
* Bianca Camille Esmero
|