1
0
Fork 0
mirror of https://github.com/kbparagua/paloma synced 2023-03-27 23:21:17 -04:00
page-specific javascript for Rails done right
Find a file
2013-03-05 07:04:07 +08:00
app New templates 2013-03-05 07:04:07 +08:00
lib Callback spec completed 2013-02-24 01:42:59 +08:00
spec/test_app New templates 2013-03-05 07:03:13 +08:00
vendor/assets/javascripts Specs for locals 2013-03-02 22:49:24 +08:00
.gitignore Refactored and Tested generators 2012-12-22 03:51:44 +08:00
Changelog.md AddGenerator - multiple actions 2013-01-04 17:33:49 +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
paloma.gemspec Remove database_cleaner gem 2013-02-21 18:44:09 +08:00
README.md Update README.md 2013-03-04 16:22:48 +08:00
TODO.md Update TODO.md 2013-01-28 22:24:58 +08:00

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.');
};

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:

```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";
});
```

##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