Skip to content

Partial reuse between Rails & JS, the easy way!

Writing JavaScript code to display data retrieved from a JSON API can be easy and clean if you use templates.

If you don’t use JS templates and still manipulate the DOM of your page to fill in data from a JSON object, you’re just doing it wrong :).

There are lots of templating plugins available in JavaScript like jquery-tmpl, or even the template method from the great Underscore.js library.

But what is the easiest solution to reuse view partials between the Rails application and the JavaScript code? Often I have to display a partial statically in my view using a render call with some Ruby objects and render the same partial in JavaScript. I don’t want to maintain 2 versions of my partial.

After looking and testing many different solutions, one of the easiest is to use Mustache, a Rails gem plugin called Stache and ICanHaz.js.

Here is a Rails 3.1 example which provides a simple user partial.
This partial is used in the UsersController index action to display the first users.
Then it is reused in JavaScript in an AJAX pagination by fetching the next users in JSON.

In my Gemfile, we add :

gem 'jquery-rails'
gem 'will_paginate'

gem 'mustache'
gem 'stache'
# ...

Then I copy ICanHaz.js in assets/javascripts.

The UsersController is standard and responds to HTML and JSON…

class UsersController < ApplicationController
  respond_to :html, :json
  
  def index
    @users = User.page(params[:page])

    respond_with(@users)
  end

  def new
    @user = User.new
  end
end

Then we can write the user partial in Mustache (app/views/users/_user.html.mustache).

<div class="widget-user">
  <h3>User</h3>

  <p><strong>First name</strong> : {{ first_name }}</p>
  <p><strong>Last name</strong> : {{ last_name }}</p>
  <p><strong>Adress</strong> : {{ address }}</p>
  <p><strong>Zip code</strong> : {{ zip_code }}</p>
  <p><strong>Phone</strong> : {{ phone }}</p>
</div>

Remember that Mustache is a logic-less templating system, so you should not access complex objects with nested variables…

Thanks to the Stache gem, we can render this partial in our HAML (or Erb) view! (app/views/users/index.html.haml)

#users-list
  - @users.each do |user|
    = render "users/user", user.attributes.symbolize_keys

= button_tag "Load next >>", id: "duplicate-widget"

= template_include_tag "users/user"

When rendering the Mustache partial, we send it all the attributes of the user, so we don’t have to build a long Hash manually.
Notice the template_include_tag call. This helper is provided by Stache and directly puts the partial raw code in the view, so that we’ll be able to use it in JavaScript.

Next we’ll write the Load next button behavior in CoffeeScript which will fetch the next users then use the Mustache partial to render them.
Thanks to ICanHaz, we can render the user partial in just… one line of code!

window.page_number = 2

$(document).ready ->
  $("#duplicate-widget").click ->
    $.getJSON "/users.json?page=#{ window.page_number }", (users) ->
      window.page_number++
      $.each users, ->
        widget = ich.user_template(this) # render the user partial with the JSON object
        $("#users-list").append(widget)

    false
Publicités

You should already use CoffeeScript in your Rails app!

In the latest weeks, I had to write some Javascript at work and I decided to evaluate CoffeeScript.

As you may know, CoffeeScript is a language written in Javascript that compiles into… Javascript. But it has a great syntax that takes some great ideas from Ruby and Python.
If you want to discover CoffeeScript, just go through this great presentation.

I wanted to know if it is now possible and a good thing to use CoffeeScript in a production Rails application.
After some evaluations I decided that I’ll use CoffeeScript to develop my next JavaSript code. Here is why…

The most important thing is that CoffeeScript compiles into readable JavaScript. You can open your ‘compiled’ Javascript and clearly understand the code, ie. : it does not change your variables names or function names or add a lot of garbage code… So if one day I decide to stop using CoffeeScript for whatever reason, I’ll still be able to work and go on on the compiled script directly.

IMHO, the Rails gems to integrate CoffeeScript into a Rails app are know mature and work very well.

If you have never used CoffeeScript before you may want to know if it’s possible to use CoffeeScript with your existing JS librairies (jQuery, etc.), to mix JS code with CoffeeScript code, etc… And of course, it’s possible and very easy ! So you should really give it a try.

How to use CoffeeScript in your Rails application?

It’s really easy.
Barista is the Rails gem that will take care of your CoffeeScript files and compiles them into JavaScript when you request them in your application.

The latest versions of Barista depends on the coffee-script gem. This gem only auto-detects the available compilers on your computer as there are several ways to install CoffeeScript. You may choose to follow the official documentation and install it using node.js, but if you don’t want to setup node.js, you can use the therubyracer gem and you won’t have to install anything else!
therubyracer includes V8 (the fast Javascript engine from Google).

Finally the coffee-script gem depends on the coffee-script-source gem. This gem only contains the CoffeeScript source code and it is synced with official releases. Thanks to this gem, you can lock your application to a particular CoffeeScript version.

So, this was a great explanation speech… But in summary, if you want a fast way to include CoffeeScript in your Rails app, here is what you have to add to your Gemfile :

  gem 'therubyracer', :require => false
  gem 'barista', '0.7.0.pre3'

Then you may run :

  rails generate barista:install

this will generate the initializer config/initializers/barista_config.rb which you may edit to change the Barista configuration.
As I’m using SASS/SCSS which compiles from app/stylesheets to public/stylesheets/compiled, I thought that a cool configuration would be to let Barista compiles my CoffeeScript into public/javascripts/compiled :-).

So I added this line to my Barista configuration file :

  c.output_root = Rails.root.join("public", "javascripts", "compiled")

Now, restart your server and test your installation.
Create a file app/coffeescripts/hello.coffee, then add :

  $(document).ready ->
    alert "Hello world from Coffee!"

Add javascript_include_tag « compiled/hello » to one of your view and check that everything works!
Congrats! Now, a good idea would be to let Barista and Coffee in your app and write your next JS code in Coffee…
Barista will detect changes in your CoffeeScript files and compiles them after each request to your webserver (only in development environment).

To avoid writing compiled/ in front of all my compiled scripts, I added this simple helper to my project :

  def coffee_script_include_tag(*sources)
    javascript_include_tag(*(sources.map { |js| "compiled/#{js}" }))
  end

Now for the deployment part. You may version the CoffeeScript compiled files…
But I prefer to ignore them and to lock the project to a specific coffee-script-source gem version. That will ensure that all the developers work with the same CoffeeScript version.

Then I use the barista:brew rake task in my Capistrano file using an after hook.
This rake task will just compile all the CoffeeScript source files during deployment. So you can safely pack them using your prefered asset packager after compilation…

Now, have a coffee!

Quick and simple geocoding without external librairies

When you want to implement geocoding for one of your Ruby on Rails models, the first thing you may do is to include the good old geokit gem

But do you know that if you just want to retrieve and store lat/lng in your records, geokit or another external library is unnecessary ?
And moreover, geokit still uses the old Google Maps API v2… Google has updated his Maps API (v3) and it is very easy to consume it using simple GET calls returning JSON. And, icing on the cake : Google API v3 does not require an API key! So no more headache managing multiple API keys for all your environments…

Here is a small example (geocode the street field). This is a Mongoid model, but of course it would work with ActiveRecord or anything else…

class Place
  include Mongoid::Document
  field :name
  field :street
  
  field :lat, :type => Float
  field :lng, :type => Float
  
  def do_geocode!
    response = Net::HTTP.get_response(URI.parse("http://maps.googleapis.com/maps/api/geocode/json?address=#{Rack::Utils.escape(street)}&sensor=false"))
    json = ActiveSupport::JSON.decode(response.body)
    self.lat, self.lng = json["results"][0]["geometry"]["location"]["lat"], json["results"][0]["geometry"]["location"]["lng"]
  rescue
    false # For now, fail silently...
  end
end
ree-1.8.7-2010.02 > place = Place.new
 => #<Place _id: 4ca27a360e3b7d62d7000005, name: nil, lng: nil, street: nil, lat: nil> 
ree-1.8.7-2010.02 > place.street = "Paris, France"
 => "Paris, France" 
ree-1.8.7-2010.02 > place.do_geocode!
 => [48.8566667, 2.3509871] 

Rails 3, HTML 5 and client-side forms validations using Validator

There are many client-side JS forms validations librairies available on the Internet. There are also many Rails plugins and FormBuilders gems.
While working on a new project using Rails 3, I wanted to find an easy way to validate my forms client-side using JS. I also usually don’t use plugins like Formtastic, I prefer writing my own custom FormBuilder for each project in collaboration with the webdesigner…

I found Validator by the flowplayer team. This Javascript library is just great. Why? Because it uses some HTML 5 new attributes to define validations on the form inputs. And guess what? It works perfectly with the new Rails 3 HTML 5 helpers.
You can just map this library on all your <form> tags in your application.js, use some HTML 5 attributes and get instant and unobtrusive client-side validations.

In your view (form) :

  ...
  <%= f.email_field :email %>
  ...

In your application.js :

  $("form").validator();  // Validate all forms...

Add the validator library in your main layout, and « BOOM! » instant validation!

What if your text_field is mandatory? Just add :required => true in the options, like this :

  ...
  <%= f.text_field :street, :required => true %>
  ...

You can also do some Regexp validation using the « pattern » attribute, like this :

  ...
  <%= f.text_field :login, :required => true, :pattern => "[0-9a-zA-Z_\-]{6,}" %>
  ...

Alongside email_field, there are other HTML 5 helpers that are included by default in Rails 3 : number_field, range_field, search_field, telephone_field, url_field, see the ActionView::Helpers::FormHelper API for more information about these new helpers.

Of course you cannot do complex validations or validations that require a server-side callback (like uniqueness validations) only by using HTML 5 attributes. But IMHO, the few attributes used by Validator are really a good start if you want something light and don’t want to use some bloated library or plugin. Take a look at the Validator documentation to see all the attributes and advanced options you can use (i18n is also possible!).

And… One more thing ! I wrote a simple module that easily integrates with any custom FormBuilder. It does simple reflection on the current model instance to automaticaly set some HTML 5 attributes. Here it is…

Gist

module FormBuilderHelpers
  class Html5
    def self.options_from(object, attribute)
      return {} unless object.class.respond_to?(:validators_on)
      
      {}.tap do |options|
        options[:required] = true if object.class.validators_on(attribute).any? { |validator| validator.is_a?(ActiveModel::Validations::PresenceValidator) }
        
        if validator = object.class.validators_on(attribute).select { |validator| validator.is_a?(ActiveModel::Validations::InclusionValidator) && validator.options[:in].is_a?(Range) }.last
          options[:min] = validator.options[:in].min
          options[:max] = validator.options[:in].max
        end
      end
    end
  end
end

As you can see for now, it just returns a default options hash with the required and min/max attribute (if the model instance class has validates_presence_of or validates_inclusion_of with a Range).
At first, I also wanted to add the pattern attribute if a validates_format_of is used, but unfortunately the Ruby and JS regexps are often different… I did not find an easy way to reuse them directly (if you find a workaround, tell me :-)).

Then, in your FormBuilder, just use the module to get the default options hash :

  ...
  def text_field(attribute, options = {})
    options.reverse_merge!(FormBuilderHelpers::Html5.options_from(object, attribute))
    ...
  end
  ...

Easy « paranoid » with Rails 3 and state_machine

Do you remember the good old « acts_as_paranoid » plugin for Rails? The bahaviour of the plugin is simple : you want to « soft »-delete the records in your database.

When your users use your app to destroy a record, they don’t see it anymore, but the record is still kept in the database for security purposes.

Since the latest versions of Rails 2 it’s been very easy to implement this kind of behaviour using default_scope. With Rails 3, the state_machine gem and the « unscoped » method the syntax is even nicer!

class Post < ActiveRecord::Base
  state_machine :state, :initial => :published do
    event :delete do
      transition all => :deleted
    end
  end
  
  default_scope without_state(:deleted)
end
  post = Post.find(1)  # => load Post with ID=1
  post.delete  # => post is now soft-deleted!

  Post.find(1)  # => ActiveRecord::RecordNotFound

  Post.unscoped.find(1)  # => really load it if we want
  Post.unscoped do
    # do your stuff WITH all Post records... for example in your admin area.
  end

Capistrano task for Bundler 1.0

UPDATE ! In case you don’t know : there is now a Capistrano task integrated in Bundler (see it there). So if your needs are basic, you may use it instead of writing your own.

4 august 2010 : updated for Bundler 1.0rc3. –production option is now called –deployment to avoid confusion with some common groups names :).

A new Bundler version (1.0rc3) has been released!

Now it includes a new option named –deployment to isolate the gems.

This is the option you need to use in your deployment tasks. Here is my new Capistrano task to deploy using Bundler 1.0rc3. I will update it until the final version arrive…

namespace :bundler do  
  task :create_symlink, :roles => :app do
    set :bundle_dir, File.join(release_path, 'vendor', 'bundle')

    shared_dir = File.join(shared_path, 'bundle')
    run "rm -rf #{bundle_dir}"
    run "mkdir -p #{shared_dir} && ln -s #{shared_dir} #{bundle_dir}"
  end

  task :bundle_new_release, :roles => :app do
    bundler.create_symlink
    run "cd #{release_path} ; bundle install --deployment --without development test"
  end
end

Android vers iOS : ce qui me manquerait…

Alors qu’Apple vient tout juste de sortir la 4ème version majeure de son système d’exploitation mobile (iOS 4), je réfléchissais à la possibilité de repasser du côté de la Pomme. L’iPhone 4 paraît certes bien alléchant… Mes pulsions de fanboy Apple seraient-elles assez fortes pour me faire jeter mon Nexus One (tout juste passé à Android 2.2) ? Lui qui n’a que quelques mois..?

Peut-être pas… Car lors de mon passage sur Android, j’ai été littéralement bluffé. Bluffé par la qualité et le niveau fonctionnel d’Android. Oui, je l’ai dit assez fort : Google a réussi, en même pas 2 ans (et grâce au rachat de quelques boîtes), a créer un OS mobile du niveau de l’iPhone.

Plutôt que de faire un comparatif Android vs iOS (ça serait long et barbant), je préfère sur ce billet lister les quelques fonctionnalités que j’apprécie vraiment dans Android et qui ne sont toujours pas présentes dans iOS… Le genre de fonctionnalités qui pourraient bien retarder mon retour… Bien sûr, l’iPhone et iOS ont aussi leurs avantages, mais ce n’est cette fois ci pas le sujet :-).

C’est parti !

  • La barre de notifications Android

Je suis clairement déçu qu’Apple n’ait pas repensé la gestion et l’affichage des notifications (SMS, mails, appels manqués, etc.). Sur Android, pas de notifications qui s’affichent au fur et à mesure sur l’écran lorsque le téléphone est bloqué. Chaque application peut rajouter une notification dans une file. L’utilisateur peut faire apparaître la file des notifications en faisant glisser son doigt de haut en bas de l’écran… Simple comme bonjour. Je suis devenu fan de cette fonctionnalité…

  • Google Navigation

C’est la bonne surprise en ce début des vacances d’été : Google rend disponible son logiciel de navigation GPS à quelques pays d’Europe dont la France. Comme tous les logiciels de Google, c’est fonctionnel, gratuit et ça marche bien. On rentre sa destination complète dans un seul champ texte comme sur un moteur de recherche et c’est parti ! Gros avantage (et désavantage) : le logiciel utilise Google Maps, la carte est chargée en temps réel… ce qui ne le rend accessible que dans votre pays d’origine à moins que vous appréciez payer des milliers d’euros de roaming data. Quelques fonctionnalités pratiques et qui permettent de frimer avec vos amis : reconnaissance vocale pour donner la destination, annonce par synthèse vocale des rues et noms des voies, et même affichage Google Street View lorsque vous arrivez à destination (ULTIME !!!).

  • Les widgets

Ah les widgets… Clairement la fonctionnalité où l’on se dit au départ : « c’est naze, c’est gadget, ça sert à rien. ». Oui et non au final. Personnellement, afficher un widget « horloge géante » prenant la moitié de la place est une idée qui ne m’enchante guère. Mais par contre, je me suis mis à apprécier le widget Google Actualités par exemple. Comme son nom l’indique ce widget fait simplement défiler des actualités de Google News. Je ne compte pas le nombre d’actualités dont j’ai pu prendre connaissance grâce à ce widget. Des actualités que j’aurais zappé si elles étaient restées au fond d’une application dédiée, comme Le Monde ou Libération, car je les ai vues s’afficher à un moment où je voulais faire autre chose avec mon téléphone… Tout ça pour dire : les widgets, et plus particulièrement certains widgets peuvent être utiles (oui oui !).

  • Les applications de l’Android Market

Tout le monde est au courant que Google est bien plus permissif qu’Apple au niveau des applications. Les applications que l’on peut trouver sur l’Android Market peuvent avoir « quasiment » tout le contrôle sur le téléphone comme rester en tâche de fond, changer les paramètres du téléphone (certaines applis permettent de changer les paramètres selon la position GPS) ou exécuter des scripts externes (émulateurs de consoles de jeux, etc.).

Je ne rentrerai pas dans le débat de trouver le juste équilibre entre trop laxiste (et risque d’instabilité) et trop restrictif (à la Apple). Certaines applications risquent de me manquer, surtout les émulateurs… (et l’application Bonjour Madame ;-)).

  • La synchro des contacts avec les réseaux sociaux

La synchronisation des contacts avec Facebook, Twitter et Gmail, avec possibilité de fusion est plutôt fonctionnelle. Côté pratique : la synchro Facebook permet par exemple de récupérer la photo Facebook de vos amis sans frais…

  • Tethering 3G via Wifi

Posséder un Nexus One d’origine sous 2.2 me permet désormais de partager la 3G via Wifi et sans forfait spécial… Je ne sais pas si Orange ou les autres fournisseurs d’accès peuvent détecter cela et transférer les communications en hors forfait. J’ai tout simplement utilisé cette fonctionnalité raisonnablement chez Orange sans constater de hors forfait pour l’instant…

  • Les outils de développement gratuits et sans frais

Sans rentrer dans les détails du style « qui a le SDK de meilleure qualité » ou « qui a la plus grosse », se dire que l’on peut tester des applications sur son Android sans avoir à les signer numériquement ou payer la redevance Apple de 99$ par an est clairement un « plus ». <troll>Et Java c’est plus sympa et accessible que l’Objective-C !</troll>

C’est fini pour ma petite liste de fonctionnalités que j’apprécie sur Android… J’aurais pu la continuer, comme je pourrais faire l’inverse et lister les fonctionnalités d’iOS exclusives… Mais étant un fanboy Apple, ça n’aurait pas été intéressant ni original :).

Maintenant, à vous. Quelles sont les fonctionnalités exclusives à iOS, Android ou Blackberry que vous adorez ?