RSS
 

Archive for the ‘Ruby on Rails’ Category

Pluralize in every languages

08 Sep

This article show you how to use that handy helper rails, pluralize with all the languages through the use of I18n.

We assume that the application is internationalized, so model and attributes are in YAML configuration file.
Now we add the terms for which we want to internationalize the plurality: models, attributes etc..

  activerecord: &activerecord
    models: &models
      user: "User"
      users: "Users"
      activity: "Activity"
      activities: "Activities"
      task: "Task"
      tasks: "Tasks"
      project: "Project"
      projects: "Projects"
 
    attributes: &attributes
      activity:
        task: "Task"
        day: "Day"
        hours: "Hours"
        hour: "Hour"
        description: "Description"

Assume that our model “Task” is related with the model “activity” which has a field named “Hours”. We want to create a phrase that describes how many activities there are for that task and the activity total hours.

According to data in the database, we get sentences like:
This task has 1 activity with an amount of 1 hour.
This task has 13 activities with an amount of 0 hours.
This task has 1 activity with an amount of 15 hours.

…and for all others languages:
Questo incarico ha 1 attività per un totale di 15 ore.
Diese Aufgabe haben 1-Aktivität für eine Gesamtmenge von 15 Stunden.
Cette tâche a 1 activité pour un total de 15 heures.
Esta tarea tiene 1 actividad para un total de 15 horas.
このタスクは、 15時間の量1活性を持つ

We would to avoid:
This task has 1 activities with an amount of 1 hours
Questo incarico ha 1 impieghi per un totale di 1 ore
etc.

Add the phrase in each file YAML I18n:

  #en.yml
  task_activities: "This task has <strong>{{activities}}</strong> with an amount of <strong>{{hours}}</strong>."
  #it.yml
  task_activities: "Questo incarico ha <strong>{{activities}}</strong> per un totale di <strong>{{hours}}</strong>."
  #etc.

To do this, we’ll use the handy helper rails, pluralize, which will be amended to delete the number from the result. We’ll pluralize the English word to use as a key to I18n.

We create into application helper:

module ApplicationHelper
   #Pluralize without the number
   def I18n_pluralize(count, singular, plural = nil)
     ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize))
   end
end

From the view, we translate the phrase with I18n passing the number of activities and total hours (for description reasons, I put into the view some operations which would consider more appropriate place in the controllers):

  <% tot_hours = @activities.map{|a| a.hours}.inject{|tot,h| tot+h}  || 0 %>
  <% str_hour = t("attributes.activity.#{I18n_pluralize(tot_hours,'hour')}") %>
  <% str_activity = t("models.#{I18n_pluralize(@activities.size,'activity')}") %>
  <%= t :task_activities, :activities => "#{@activities.size} #{str_activity}", :hours => "#{@tot_hours} #{str_hour}" %>

Observe this line in slow motion:

t("attributes.activity.#{I18n_pluralize(tot_hours,'hour')}")

According on the hours number we get the key I18n: hour or hours

chiave = I18n_pluralize(tot_hours,'hour')

equal to

t("attributes.activity.#{chiave}")

if tot_hours = 1 then:

t("attributes.activity.hour")

else:

t("attributes.activity.hours")

This is an outcome:
Example Pluralize I18n

 
 

Create skeleton applications with generators and templates

19 Mar

Last update on April 1th, 2010

Rails is a wonderful framework which help us to develop a modern web application. However, when you create a new one, you should perform several pretty boring operations: add plugins / gems, rescue a starting layout and whatever you usually use in yours applications.
To solve this, from version 2.3 you may use templates. You find lots of examples on internet, i give you another that show you how to take advantage of it combining with a cool rails generator. This guide explains how to make one. If you are impatient you can choose something already done, forking an existing project so did I. I customized Ryan Bates’s nifty-generators adding some new features:

  • nifty_layout now supports multiple styles: classic, and blackwhite cloudy. It are only a starting point
  • nifty_layout: I added the configuration file config.rb which is a container for all the application constants for example application name, the default style etc..
  • I introduced localization support: nifty_layout added YAML files for language en and it. To manage, some code is inserted into application controller: with the lang parameter you change the language setting (for example ?lang=en) that will be maintained throughout the session. The nifty_scaffold adds resources and its attributes, ready to be translated.
  • nifty_layout: the posts division has been moved into a partial in the shared folder. I preferred to do this to use it even through ajax
  • nifty_scaffold: the “new” and “edit” views now use two partial: render @ model which calls the second with the fields inside. So do the original version, it supports haml and sass, furthermore, I added support for formtastic. So you can choose the preferred combination!

The template I will show you download these new generators for you, however, these are the sources or you can even download latest gem and template.

What the generator should do and what the template?
Well, templates should be a container of things to do, executed only one time after creating a new project. Generators should create something like adding new files or customize something that already exists and can be executes whenever you want as well as within a template.

If you never have used a template before, you could start with a simple one or a tutorial could be even better:
this screencast show you how to make a template.
This is an article in html format.

I see many examples on internet which are filled with configuration’s parameters, that cause me strong headaches. I opted for one that ask me what i want. Surely, isn’t perfect yet but I think it’s a good base :

#This is a Rails 2.3 template
#Written by Marco Mastrodonato, last update on 01/04/2010 
#
# USAGE: rails yourapp -m template.rb
 
#This is the unique parameter, after first execution use FALSE to avoid installation
install_gems = false
git_http = true
 
WINDOWS = (RUBY_PLATFORM =~ /dos|win32|cygwin/i) || (RUBY_PLATFORM =~ /(:?mswin|mingw)/) 
 
#plugin/install doesn't work on my windows systems
def myplugin(name, url)
  url.sub! 'git://', 'http://' if git_http
  if WINDOWS
    #run "ruby script/plugin install #{url}"
    run "git clone #{url} vendor/plugins/#{name}"
  else
    plugin name, :git => url
  end
end
 
puts '*' * 40
puts "* Processing template#{WINDOWS ? ' on windows system': ''}..."
puts '*' * 40
 
use_git = yes?("Do you think to use git ?")
if use_git
  git :init
  file ".gitignore", <<-EOS.gsub(/^    /, '')
    .DS_Store
    log/*.log
    tmp/**/*
    config/database.yml
    db/*.sqlite3
    nbproject/*
  EOS
end
 
#Everyone must to have it!
plugin 'will_paginate',
        :git => "git://github.com/mislav/will_paginate.git" if yes?("Will Paginate ?")
 
# Attachments with no extra database tables, only one library to install for image processing
plugin 'paperclip',
       :git => "git://github.com/thoughtbot/paperclip.git" if yes?("Paperclip ?")
 
formtastic = yes?("Formtastic ?")
if formtastic
  #gem 'justinfrench-formtastic', :lib => 'formtastic', :source => 'http://gems.github.com'
  #rake "gems:install" if install_gem
 
  #A Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.
  plugin 'formtastic',
        :git => "git://github.com/justinfrench/formtastic.git"
  #Adds reflective access to validations
  plugin 'validation_reflection',
        :git => "git://github.com/redinger/validation_reflection.git"
  generate "formtastic"
end
 
if yes?("Add testing framework ?")
  # RSpec's official Ruby on Rails plugin  
  plugin 'rspec', 
    :git => 'git://github.com/dchelimsky/rspec.git'
 
  plugin 'rspec-rails',
         :git => "git://github.com/dchelimsky/rspec-rails.git"
  generate "rspec"
 
  # BDD that talks to domain experts first and code 2nd
  plugin 'cucumber',
         :git => "git://github.com/aslakhellesoy/cucumber.git"
end
 
if yes?("Add authentication ?")
  puts "0. None"
  puts "1. Devise/warden"
  puts "2. Authlogic"
  puts "3. Restful authentication"
  choose = ask("Choose one:")
  case choose
    when "1"
      gem "warden", :version => "0.9.6"
      gem "devise", :version => "1.0.4"
      rake "gems:install" if install_gems
      generate "devise_install"
      generate "devise", "User"
      generate "devise_views"
      #User.create!(:email => 'admin@administrator.com', :password => 'secret')
    when "2"
      gem "authlogic"
      rake "gems:install" if install_gems
      generate "session", "user_session"
      generate "model", "user"
      generate "controller", "user_sessions"
      route "map.resource :user_session"
      create_users_file = Dir['db/migrate/*_create_users.rb'].first
      file create_users_file, <<-EOS.gsub(/^        /, '')
        class CreateUsers < ActiveRecord::Migration
          def self.up
            create_table :users do |t|
              t.string    :login,               :null => false                # optional, you can use email instead, or both
              t.string    :email,               :null => false                # optional, you can use login instead, or both
              t.string    :crypted_password,    :null => false                # optional, see below
              t.string    :password_salt,       :null => false                # optional, but highly recommended
              t.string    :persistence_token,   :null => false                # required
              t.string    :single_access_token, :null => false                # optional, see Authlogic::Session::Params
              t.string    :perishable_token,    :null => false                # optional, see Authlogic::Session::Perishability
 
              # Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present.
              t.integer   :login_count,         :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
              t.integer   :failed_login_count,  :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
              t.datetime  :last_request_at                                    # optional, see Authlogic::Session::MagicColumns
              t.datetime  :current_login_at                                   # optional, see Authlogic::Session::MagicColumns
              t.datetime  :last_login_at                                      # optional, see Authlogic::Session::MagicColumns
              t.string    :current_login_ip                                   # optional, see Authlogic::Session::MagicColumns
              t.string    :last_login_ip                                      # optional, see Authlogic::Session::MagicColumns
 
              t.timestamps
            end
 
            add_index :users, :email
            add_index :users, :login, :unique => true
          end
 
          def self.down
            drop_table :users
          end
        end
      EOS
      file 'app/models/user.rb', <<-EOS.gsub(/^        /, '')
        class User < ActiveRecord::Base
          acts_as_authentic
        end
      EOS
      file 'spec/models/user_spec.rb', <<-EOS.gsub(/^        /, '')
        require 'spec_helper'
 
        describe User do
          before(:each) do
            @valid_attributes = {
              :login                 => "login",
              :email                 => "some@thing.com",
              :password              => "password",
              :password_confirmation => "password"
            }
          end
 
          it "should create a new instance given valid attributes" do
            User.create!(@valid_attributes)
          end
        end
      EOS
    when "3"
      plugin 'restful-authentication-i18n',
          :git => "git://github.com/dcrec1/restful-authentication-i18n.git"
      #—include-activation \ —stateful \ —rspec \ —skip-migration \ —skip-routes \ —old-passwords 
      generate "authenticated", "user", "sessions", "—include-activation"
  end
 
  if yes?("Authorization ?")
    gem "cancan"
    rake "gems:install" if install_gems
    #plugin 'cancan', :git => "git://github.com/ryanb/cancan.git" 
    file 'spec/models/ability.rb', <<-EOS.gsub(/^      /, '')
      class Ability  
        include CanCan::Ability  
 
        def initialize(user)  
          user ||= User.new # Guest user  
          if user.role? :admin  
            can :manage, :all  
          else  
            can :read, :all  
          end  
        end  
      end  
    EOS
    generate "migration", "add_roles_mask_to_users roles_mask:integer"
    puts "Add these lines to user model:"
    puts <<-EOS.gsub(/^      /, '')
      named_scope :with_role, lambda { |role| {:conditions => "roles_mask & \#{2**ROLES.index(role.to_s)} > 0 "} }
      def roles=(roles)
        self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
      end
      def roles
        ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
      end
      def role_symbols
        roles.map(&:to_sym)
      end
    EOS
 
  end
 
end          
 
haml = yes?("Haml?")
if haml
  # The world's greatest templating system
  #plugin 'haml', :git => "git://github.com/nex3/haml.git"
  gem "haml", :lib => "haml"
  rake "gems:install" if install_gems
  run "haml --rails ."
end
 
if yes?("Layout ?")
  plugin "marcomd-nifty-generators", 
    :lib => "marcomd-nifty-generators",
    :git => "git://github.com/marcomd/nifty-generators.git"
  puts " 1. classic"
  puts " 2. cloudy"
  puts " 3. blackwhite"
  puts "or. write names separed by space"
  style = ask("Choose style:")
  style = case style
    when "1" then "classic"
    when "2" then "cloudy"
    when "3" then "blackwhite"
    else style
  end
  generate "nifty_layout", "application #{style} #{haml ? '--haml' : ''} #{formtastic ? '--formtastic' : ''}"
end
 
# Link to local copy of edge rails
#inside('vendor') { run 'ln -s ~/dev/rails/rails rails' } if yes?("Rails ?")
rake("rails:freeze:gems") if yes?("Freeze rails gems ?")
 
# Mailer dummy config
initializer "mailer.rb", <<-EOS.gsub(/^  /, '')
  mailer_options = YAML.load_file("\#{Rails.root}/config/mailer.yml")
  ActionMailer::Base.smtp_settings = mailer_options
EOS
file "config/mailer.yml", <<-EOS.gsub(/^  /, '')
  :address: mail.authsmtp.com
  :domain: yourdomain.com
  :authentication: :login
  :user_name: USERNAME
  :password: PASSWORD
EOS
 
if yes?("Generate controller home ? (suggested)")
  generate "controller", "home", "index"
  route "map.root :controller => :home"
else
 
end
 
rake "gems:install" if install_gems
# Unpack all gems to vendor/gems
rake "gems:unpack" if yes?("Unpack to vendor/gems ?")
rake "db:create:all"
rake "db:migrate"
File.unlink "public/index.html"
 
git :add => ".", :commit => "-m 'initial commit'" if use_git
 
puts "ENJOY!"

This is a result:


marco@d9400:~/Rails$ rails Blog -m marcomd.rb
create bla bla bla
applying template: marcomd.rb
Processing template...
plugin will_paginate
Unpacking objects: 100% (57/57), done.
From git://github.com/mislav/will_paginate
* branch HEAD -> FETCH_HEAD
Paperclip ?
y
plugin paperclip
Unpacking objects: 100% (78/78), done.
From git://github.com/thoughtbot/paperclip
* branch HEAD -> FETCH_HEAD
Formtastic ?
y
gem justinfrench-formtastic
generating formtastic_stylesheets
Add testing framework ?
y
plugin rspec
From git://github.com/dchelimsky/rspec
* branch HEAD -> FETCH_HEAD
plugin rspec-rails
From git://github.com/dchelimsky/rspec-rails
* branch HEAD -> FETCH_HEAD
plugin cucumber
From git://github.com/aslakhellesoy/cucumber
* branch HEAD -> FETCH_HEAD
Add authentication ?
y
0. None
1. Devise/warden
2. Authlogic
3. Restful authentication
Choose one:
1
gem warden
gem devise
generating devise_install
Authorization ?
n
Haml?
n
Layout ?
y
plugin marcomd-nifty-generators
From git://github.com/marcomd/nifty-generators
* branch HEAD -> FETCH_HEAD
1. classic
2. cloudy
3. blackwhite
or. write names separed by space
Choose style:
2
generating nifty_layout
Freeze rails gems ?
n
initializer mailer.rb
file config/mailer.yml
Generate controller home ? (suggested)
y
generating controller
route map.root :controller => :home
Unpack to vendor/gems ?
n
rake db:create:all
rake db:migrate
ENJOY!
applied marcomd.rb

And here we are! Now let’s start up mongrel…


marco@d9400:~/Rails$ cd Blog/
marco@d9400:~/Rails/Blog$ script/server
=> Booting Mongrel
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server

anche check our new blog
This is only the beginning

Now, let’s add the post resource and migrate:


marco@d9400:~/Rails$ cd Blog/
marco@d9400:~/Rails/Blog$ script/generate nifty_scaffold post title:string body:text
exists app/models
create app/models/post.rb
exists db/migrate
create db/migrate/20100319175545_create_posts.rb
exists test/unit
create test/unit/post_test.rb
exists test/fixtures
create test/fixtures/posts.yml
exists app/controllers
create app/controllers/posts_controller.rb
exists app/helpers
create app/helpers/posts_helper.rb
create app/views/posts
create app/views/posts/index.html.erb
create app/views/posts/show.html.erb
create app/views/posts/new.html.erb
create app/views/posts/edit.html.erb
create app/views/posts/_post.html.erb
create app/views/posts/_fields.html.erb
route map.resources :posts
exists test/functional
create test/functional/posts_controller_test.rb
marco@d9400:~/Rails/Blog$ rake db:migrate
(in /home/marco/Rails/Blog)
== CreatePosts: migrating ====================================================
-- create_table(:posts)
-> 0.0016s
== CreatePosts: migrated (0.0017s) ===========================================

Add post resource

Controllers and views uses I18n messages, for this reason, the new resource must be add to localization files. Nifty_scaffold will do it for you :

#config/locales/en.yml
en:
  activerecord:
    models: &models
      post: "Post"
      posts: "Posts"
    #DO NOT REMOVE MODELS
    attributes: &attributes
      title: "Title"
      body: "Body"
    #DO NOT REMOVE ATTRIBUTES
 
  formtastic:
    titles:
    labels:
    hints:
      post:
        title: "Choose a good title for your article"
        body: "Choose a good body for your article"
    #DO NOT REMOVE HINTS
    actions: &actions
      create: "Create my {{model}}"
      update: "Save changes"
  <<: *models
  <<: *attributes
  <<: *actions
 
#config/locales/it.yml
it:
  activerecord:
    models: &models
      post: "Messaggio"
      posts: "Messaggi"
    #DO NOT REMOVE MODELS
    attributes: &attributes
      title: "Titolo"
      body: "Corpo"
    #DO NOT REMOVE ATTRIBUTES
 
  formtastic:
    titles:
    labels:
    hints:
      post:
        title: "Choose a good title for your article"
        body: "Choose a good body for your article"
    #DO NOT REMOVE HINTS
    actions: &actions
      create: "Crea {{model}}"
      update: "Salva le modifiche"
  #DO NOT REMOVE FORMTASTIC
  <<: *models
  <<: *attributes
  <<: *actions

I have to use tag #DO NOT REMOVE ecc. to put attributes and resource name inside so please don’t remove it.

Post created sucessfully

I recently began testing Haml and Sass version for which there may be still some bugs.
I hope this can be helpful, enjoy!

 
 

Manage routing errors

20 Nov

Our application can only respond to certain requests, of course, to those that we have anticipated. For all other generates an error depending on the type:

  • Routing errors
  • Errors inside the controller

Routing errors

Small premise: In a traditional application, to retrieve a nonexistent page from the web server, we received a code 404. In a framework with a pattern mvc instead, requests through controllers and this allows us to define the behavior. The requests are performed according to the routing rules and in Rails for convention, all the controllers can be addressed. If we open the file routes.rb, at the bottom we discover why:

#configroutes.rb
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'

To ensure that only certain controllers can be routed, just delete those two lines at the bottom of the file that only defines the form of the request, leaving only the explicit routes. End of the preface.

If there isn’t a route, this generates a routing error because Rails does not know what we want. To handle this event, simply add one last road before you generate the error, which will inform them that has not found other valid roads:

#configroutes.rb
 
#at the bottom
map.catch_all "*anything" , :controller => "home" , :action => "unknown_request"

We saw how catch the not handled routes and directs it to the controller where we want to manage it, for that example at home controller using the method unknown_request:

#appcontrollershome_controller.rb
def unknown_request
  respond_to do |format|
    format.html do
      flash[:error] = I18n.t(:unknown_request)
      redirect_to root_path
    end
    format.xml  {  render :xml => {:root => I18n.t(:unknown_request) }, :status => :unprocessable_entity }
  end
end

Errors inside the controller

If the controller has been addressed, does not mean that the request is correct, because each has a series of operations that must exist and must be able to operate on the resource and manage the exceptions raised by the database.

Since version 2.0 of Rails, we can use “rescue_from” in the controller:

#appcontrollersmy_controller.rb
 
class MyController < ApplicationController
  rescue_from ActionController::UnknownAction, :with => :action_not_found
  rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
 
  def action_not_found
    render 'shared/action_not_found', :layout => true, :status => 404
  end
  def record_not_found
    render 'shared/record_not_found', :layout => true, :status => 404
  end
end

Here are the views:

#appviewssharedaction _not_found.html.erb
<h2>Sorry, the action doesn't exists!</h2>
 
#appviewssharedrecord_not_found.html.erb
<h2>Sorry, the record doesn't exists!</h2>

Another more concise way is to pass a proc as a parameter to “rescue_from”:

#appcontrollersmy_controller.rb
rescue_from(ActiveRecord::RecordNotFound) { |e| render 'shared/record_not_found', :layout => true, :status => 404 }
 
Comments Off

Posted in Ruby on Rails

 

FreeTTS, a java library used from jRuby on Rails

23 Aug

In this article we will see how to use the Java classes contained in a JAR file.
For this purpose we use a nice open source library developed by Carnegie Mellon University: FreeTTS.
The acronym TTS means Text To speech, makes it possible to transform a text into audio format. We’ll use it in a jRuby on Rails’s project to make us read the text we enter in the db.

In a previous article, we have seen how to configure the environment, so let’s start creating the new application:


C:>rails ProvaFreeTTS

In the lib folder from the root, create a sub folder freetts. Download the file freetts-1.2.2freetts-1.2.2-bin.zip, unpack the contents to a temporary folder, copy only the contents of the folder lib (jar files and JSAPI) into the dir just created: your_applibfreetts.

Now we create the jar’s interface to the jar:

#libfreetts.rb
 
require 'freetts/freetts.jar'
 
import com.sun.speech.freetts.Voice
import com.sun.speech.freetts.VoiceManager
import com.sun.speech.freetts.util.Utilities
 
class FreeTTS
  def initialize
    @voice = VoiceManager.getInstance.getVoice(Utilities.getProperty("voice16kName","kevin16"))
    @voice.allocate
  end
 
  def speak(txt=nil)
    return nil unless txt
    @voice.speak txt
  end
end

Let’s create a simple resource “sentence” with a single field “body“.


C:ProvaFreeTTS>jruby script/generate scaffold sentence body:text

Now we create two new operations, as I explained in detail in a previous article.

We start from the controller by adding at the bottom :

  #appcontrollerssentences_controller.rb
 
  def speak
    @sentence = Sentence.new(params[:sentence])
    require 'freetts'
    tts = FreeTTS.new
    tts.speak @sentence.body
    render (@sentence.new_record? ? :new : :edit)
  end
 
  def read
    @sentence = Sentence.find(params[:id])
    require 'freetts'
    tts = FreeTTS.new
    tts.speak @sentence.body
    redirect_to :back
  end

Now it’s the views turn.

We create a new partial where we’ll move into the resource’s data form, so to have only one form for every operation:

#appviewssentence_sentence.html.erb
 
<% form_for(@sentence) do |f| %>
  <%= f.error_messages %>
 
  <p>
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </p>
  <p>
    <%= f.submit 'Update' %>
  </p>
<% end %>
 
<h2>Preview</h2>
<% form_for @sentence, :url => speak_sentences_path, :method => :put do |f| %>
  <%= f.error_messages %>
 
  <p>
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </p>
  <p>
    <%= f.submit 'Speak' %>
  </p>
<% end %>

Add a clone of the form created by the scaffold but with a different action, to call the transaction speak.

Now modify the views created automatically:

#appviewssentencenew.html.erb
 
<h1>New sentence</h1>
 
<%= render @sentence %>
 
<%= link_to 'Back', sentences_path %>
 
#appviewssentenceedit.html.erb
 
<h1>Editing sentence</h1>
 
<%= render @sentence %>
 
<%= link_to 'Show', @sentence %> |
<%= link_to 'Back', sentences_path %>

Now, we modify the index in order to retrieve the second operation: the “read”. Basically, clicking on the corresponding line, will read the text previously stored in the db.

We only need to add a line to obtain this view:

#appviewssentenceindex.html.erb
<h1>Listing sentences</h1>
 
<table>
  <tr>
    <th>Body</th>
  </tr>
 
<% @sentences.each do |sentence| %>
  <tr>
    <td><%=h sentence.body %></td>
    <td><%= link_to 'Read', read_sentence_path(sentence) %></td>
    <td><%= link_to 'Show', sentence %></td>
    <td><%= link_to 'Edit', edit_sentence_path(sentence) %></td>
    <td><%= link_to 'Destroy', sentence, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>
 
<br />
 
<%= link_to 'New sentence', new_sentence_path %>

We modify the routing:

#configroutes.rb
 
#change map.resources :sentences with
map.resources :sentences, :member => { :read => :get }, :collection => { :speak => :put }
 
#add
map.root :controller => "sentences"

Configure the database.yml file, we create the db and tables with rake and start up Glassfish as explained in this previous article (find database.yml).

As we have seen, using java libraries with jruby on rails is very simple, we are ready to make our server to say the most dirty words!

The project can be downloaded from here.

Enjoy!

 
Comments Off

Posted in JRuby, Ruby on Rails

 

Add an operation to a resource in a RESTful system

20 Aug

My apologize in advance for having used a robot translator, a small help to save a little time:

To create a RESTful application with rails is very simple:

C:>rails MyApp

In a REST system, the application consists of Web resources with a set of linked transactions. With the scaffold generate a resource and the four basic functions to manage it, called CRUD (Create, Read, Update, Delete)

C:>script/generate scaffold assets name:string

We created the base from which to test this short article.

Suppose you want to add a new operation to our resource Asset, for example we want to handle the copy. We start by creating a new address by changing the scaffold created by:

#config/routes.rb
map.resources :assets, :member => { :copy => :get }

Let’s check the results:
C:>rake routes

…ensuring that there is the new path:

copy_asset GET /assets/:id/copy(.:format) {:controller=>"assets", :action=>"copy"}

The copy is useful in many contexts because it allows for the inclusion of a new resource from an existing.
Translate into code, and let’s create the new controller:

#app/controllers/assets_controller.rb
 
# GET /assets/copy
# GET /assets/copy.xml
def copy
  @asset = asset.new
  @asset.attributes = Asset.find(params[:id]).attributes
 
  respond_to do |format|
    format.html { render :new }
    format.xml  { render :xml => @asset }
  end
end

You can also create the view, depends on the transaction that has to be managed. This example uses the view of the new as they are very similar and both the recall: create.

Create or not create a new action on the resource is pure philosophy, leaving aside the question how we create a new target, we refer to a collection of resources:

#config/routes.rb
map.resources :assets, :member => { :copy => :get }, :collection => { :createcp => :put }

I used a collection because in this case does not need a reference to an existing resource, if I had used: the member for the path generation dell’uri would have required.

if we test again the routes should add this:

createcp_asset PUT /assets/createcp(.:format) {:controller=>"assets", :action=>"createcp"}

then we create the new view and form with the uri of the new action and specify the method (for detail put but I indicated to use an equivalent post):

#app/views/assets/copy.html.erb
 
<% form_for(@asset, :url => createcp_assets_path, :method=>:put do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label t(:name) %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit t(:create) %>
  </p>
<% end %>

and the new action in the controller:

#app/controllers/assets_controller.rb
 
# POST /assets/createcp
# POST /assets/createcp.xml
def createcp
  @asset = Asset.new(params[:asset])
 
  #do something
 
  respond_to do |format|
    if @asset.save
      flash[:notice] = I18n.t(:created, :model => I18n.t('model.asset'))
      format.html { redirect_to assets_path }
      format.xml  { render :xml => @asset, :status => :created, :location => @asset }
    else
      format.html { render :action => "copy" }
      format.xml  { render :xml => @asset.errors, :status => :unprocessable_entity }
    end
  end
end

To invoke the new operation by the index:

#app/views/assets/index.html.erb
<%= link_to t(:copy), copy_asset_path(asset) %>

Finally, these are the words used in the example for internationalization (if you need it):

create: "Crea"
copy: "Copia"
created: "The item {{model}} was created successfully."
name: "Name"
model:
  asset: "Asset"
 
Comments Off

Posted in Ruby on Rails