jskit_rails

jskit_rails is a gem that let's you seamlessly integrate rails with JSKit.

Installation

Add jskit_rails to your Gemfile:

gem "jskit_rails"

Bundle it up:

bundle install

Usage

Add the jskit helper to your layout (i.e. app/views/layouts/application.html.erb):

<%= jskit %>

Add the jskit javascript (i.e. app/assets/javascripts/application.js):

//= require jskit_rails

That's it, now all controller actions will be triggered on the JSKit dispatcher.

Controllers

JSKit offers controllers as a basic building block for JavaScript functionality. Making a folder inside app/assets/javascripts named controllers is a great place to put these:

mkdir app/assets/javascripts/controllers

Now simply require that entire directory in your application.js file:

//= require_tree ./controllers

Events

There are three events triggered on every page rendered: an application controller global event, a controller global event and a controller action event. Given a Posts controller, when rendering the index action, you will notice the three events triggered where the <%= jskit %> snippet was placed:

App.Dispatcher.trigger("controller:application:all");
App.Dispatcher.trigger("controller:pages:all");
App.Dispatcher.trigger("controller:pages:index");

This allows you to integrate your JavaScript at key points in your rails application with minimal coupling. An event triggered with no corresponding JSKit controller or action defined has no effect.

Application Controller

It's common to have some JavaScript that runs on every page of your application. In the past, you may have slapped random bits of code inside a jQuery $(document).ready block but not with JSKit. JSKit makes an explicit yet minimally coupled connection between your Rails app and your client-side code. To define application-wide behavior, define an application controller in app/assets/controller/application_controller.js:

App.createController("Application", {
  all: function() {
    // This handler will be triggered on all pages
  }
});

The all method is automatically wired to the controller:application:all event, which is automatically triggered on each page via the <%= jskit %> snippet. You now have a simple, testable controller to define behavior in your application.

All other controllers are defined in the same way, the only difference is that your other controllers will have actions defined. Assuming you have a Posts controller in ruby, whose index action needs a bit of JavaScript to spice up the template. You would simply create a corresponding Posts controller in app/assets/javascripts/posts_controller.js:

App.createController("Posts", {
  actions: ["index"],

  index: function() {
    // do stuff on the index page
  }
});

Here you can see that the actions array tells JSKit to wire up the index method to the controller:posts:index event. This event is automatically fired by the <%= jskit %> snippet.

Mapped Events

Sometimes you may want to map and action to a method with a different name, or you may want to map multiple actions to the same method. This is accomplished using mapped actions. Instead of using a string in the actions array, use an object to map the action name to the controller's method:

App.createController("Posts", {
  actions: [
    "index",
    { new: "setupForm" },
    { edit: "setupForm" },
    { create: "setupForm" }
  ],

  index: function() {
    // do stuff on the index page
  },

  setupForm: function() {
    // setup the posts form
  }
});

Here you can see that the new, edit, and create actions are all being wired up to the same method setupForm. This allows you to reuse common behavior that is needed accross multiple actions.

Finally, you may wish to have some functionality that runs on every action of a controller, to do this, simply define an all method. The all method is automatically wired to the controller:<controller name>:all event:

App.createController("Posts", {
  ...

  all: function() {
    // do something on every action of the controller
  },

  ...
});

This event structure is a simple and powerful way to coordinate your Rails application with the client-side code.

Event Payloads

In addition to simply triggering events, you may optionally pass data to these events. You can pass arbitrary data to any of the three events triggered on page render. To pass data to the application controller's all event:

class ApplicationController < ApplicationController
  before_action :set_jskit_payload

  ...

  private 

  def set_jskit_payload
    set_app_payload(current_user)
  end
end

This will pass the current user object to the Application controller. You can also pass multiple values:

class ApplicationController < ApplicationController
  before_action :set_jskit_payload

  ...

  private 

  def set_jskit_payload
    set_app_payload(current_user, [1, 2, 3], { some: "hash" })
  end
end

This will pass each item in the array as an argument to the event handler on the controller. Note that each item will have to_json called on it automatically, so there is no need to do it yourself. The above example will produce the following triggered event:

...
App.Dispatcher.trigger("controller:application:all", { first_name: "John", last_name: "Doe" }, [1, 2, 3], { "some": "hash" });
...

This allows you to share data from your Rails app without explicit knowldge of how your client-side code will consume it. You may also set the controller and action event payloads in the same way:

class PostsController < ApplicationController
  before_action :set_jskit_payload

  def index
    set_action_payload("PostsController#index")
  end

  private

  def set_jskit_payload
    set_controller_payload("PostsController")
  end
end

This should be everything you need to design and test basic client-side interactions with JSKit. If you'd like to see a working example check out this repo.