Transponder

Build Status Gem Version Coverage Status Code Climate

Transponder Banner

Transponder is a opinionated library for assisting in working with front end heavy rails app.

8kb uncompressed / 2kb minified & compressed (gzip) (client side library)

Example App Using Transponder

Try opening the Kontax app in multiple browser and update something, and see how transponder's abstraction make this sort of thing very clean and easy for a rails app.

Installation

Add this line to your application's Gemfile:

gem 'transponder'

And then execute:

$ bundle

Or install it yourself as:

$ gem install transponder

Generate a basic Module

A transponder module provides some basic structure for your javascript code

rails g transponder:install application

This will generate a transponder 'module' in your app/assets/javascripts folder by the name of 'application', you can change the application to something else, but we recommend sticking with defaults until you understand more about transponder.

Generate a Presenter

Presenters is perhaps one of the most important thing about Transponder, it allows you to use your server side templates in your client side code, cleanly and allows better reuseability of code.

rails g transponder:presenter contacts

Running this command will generate a presenter in your Transponder module application/presenters with the name contacts_presenter.coffee

How is this better than Rails UJS?

Typically with Rails UJS you would create a view with something like this

Lets say you have a basic contacts_controller.rb

class ContactsController < ApplicationController
  respond_to :json, :html, :js

  def index
    @contacts = Contact.all
    respond_with @contacts
  end
end

In your index.js.erb you would then have something like this

$('#contacts').html("<%= j render @contacts %>");

This Javascript code is evaled in the browser and content of the node with the id #contacts gets replaced with server side template that came from <%= j render @contacts %> This is fine however it has a few problems. First of all if you use coffeescript it has to be compiled in real time as its responding which adds to your response time. Secondly if you want to do more complex things in your response things can get very messy. Code reuse isn't that great either.

With Transponder you have a consistent way of working with your server side template. Lets take a look at the difference

In your index.js.erb transponder version would look something like this.

["#contacts", "<%= xms_event %>", "<%= j render @contacts %>"]

Your server side response code using transponder will mostly likely look something like this. There is consistency to it. The first element is the DOM node you want to manipulate, the second element is what will allow the client side Transponder code to know which Presenter is responsible for this response and lastly we have the server side generated content.

So what happens once this response gets to the client

Well in our presenter we would do something like this

class Application.Presenters.ContactsPresenter extends Transponder.Presenter
  presenterName: 'contacts'
  module: 'application'

  index: ->
    $(@element).html(@response)
    # ... do more stuff ...

The first 3 lines of code are generated by the presenter generator you were just using before the only line of code you should pay attention to here is the last 2. Basically the @element is the dom element you specified in index.js.erb and the @response is the content that was rendered by the server.

In the presenter you can do pretty much anything you want to your response before it gets output to the DOM. This gives a nice structure and consistency to the whole pattern. It allows you to mix server side templates with full client side programmability.

Testing is also much easier as now you've shifted the responsibility of the client side behavior to the client. We have more documentation coming on how to test your presenters.

Services

Services are meant to be an easy way to manage functionality of a widget on a page. What does this mean?

Generally we will have elements on the page that do more than just display information, they have to listen to some even like a mouse click or a tap and then act on that. They may have 1 simple functionality or multiple functions Services are a way to manage that.

Services are designed to be idempotent. You can run a service on a page repeatedly and it will not apply to the widgets that already have the same service applied. This can be very useful when working with pages that use pjax / turbolinks / single page apps etc...

Service Generator

To generate a service simply type

rails g transponder:service contacts_search

In our initializers/manifest.coffee file we have something like this

Application.services_manifest = ->
  $('body').trigger 'application:services:contacts_search'

Basically we want to trigger this service on the page. Since we're using 'body' as the element the service will run on every page. If we apply a class to the html <body> tag to something like this in our application layout file <body class="<%= controller_name %> <%= action_name %>" we can trigger specific services on specific pages.

For example if we're on the controller index page and we want to trigger the contacts search only when we're on that page we can do something like this

Application.services_manifest = ->
  $('body.contacts.index').trigger 'application:services:contacts_search'

This gives us very fine grained control as to which services should run on which pages.

We have this in our erb some where on our page

<%= form_tag contacts_path, class: 'navbar-form navbar-left contacts_search', remote: true do %>
  <div class='form-group'>
    <%= text_field_tag :query, nil,class: 'form-control', placeholder: "Search for someone", id: 'search-field' %>
  </div>
<% end %>

which renders down to

<form accept-charset="UTF-8" action="/contacts" class="navbar-form navbar-left contacts_search contacts_search_active" data-remote="true" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
  <div class="form-group">
    <input class="form-control" id="search-field" name="query" placeholder="Search for someone" type="text">
  </div>
</form>

The service will detect the element on the page with the matching class to the serviceName: and apply the behavior to those elements.

class Application.Services.ContactsSearch extends Transponder.Service
  serviceName: 'contacts_search'
  module: 'application'

  init: ->
    @element.on 'keyup', "#search-field", @submitSearch

  search: _.debounce ( (e) -> 
    field = @element.find('#search-field')
    $.ajax 
      url: @element.prop('action')
      dataType: 'script'
      data: 
        query: field.val()
  ), 600

  submitSearch: (field) =>
    @search(field)

  serve: ->
    @init()

TODO - Whats Coming

  • Add Documentation
  • Video Screencasts
  • Add more features to Kontax
  • More documentation on Services

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Credits

Gem developed by Zack Siri of Artellectual

Bitdeli Badge