page-specific javascript for Rails done right
Go to file
kbparagua 6049fb72e6 2.0.6 2013-05-27 11:28:42 +08:00
app Bug Fix: Stupid type on _callback_hook.html.erb 2013-05-23 00:44:57 +08:00
lib Now hooks ActionController::Flash#redirect_to so that flash messages work with redirect_to 2013-05-26 22:16:41 +01:00
spec/test_app Addition of selenium-webdriver into Gemfile 2013-05-10 22:14:41 +02:00
vendor/assets/javascripts Bug Fix: prevent javascript error when callback doesn't exist. 2013-04-01 22:27:43 +08:00
.gitignore Remove spec/tmp 2013-03-09 12:12:44 +08:00
Changelog.md 2.0.6 2013-05-27 11:28:42 +08:00
Gemfile Test commit with new git user 2012-12-21 14:29:09 +08:00
License Create License 2012-12-19 02:41:52 -08:00
README.md Update README.md 2013-03-10 10:27:31 +08:00
TODO.md Update TODO.md 2013-01-28 22:24:58 +08:00
paloma.gemspec 2.0.6 2013-05-27 11:28:42 +08:00

README.md

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:

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:

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
  1. 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
  1. 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
  1. 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.

    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.

    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.

    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.

    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

def destroy
    user = User.find params[:id]
    user.destroy
    
    js :params => {:user_id => params[:id]}
end

/paloma/users/destroy.js

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

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.

    filter.as('name').before_all()
    filter.as('name').after_all()
    filter.as('name').around_all()
    
  2. Only for specific action/s.

    filter.as('name').before('new', 'edit', 'update')
    filter.as('name').after('destroy')
    filter.as('name').around('show')
    
  3. Except for specific action/s.

    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:

filter.as('filter name').before('new').perform(function(params){
    _x.sharedVariable = "Sexy Paloma";
});

on new.js:

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.

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:

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

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

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:

locals.globalMethod = function(){ console.log("I'm from Global"); }

paloma/namespace/_locals.js contains:

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:

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):

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:

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