Ruby on Rails: autentificación con clearance

Un detalle que no falta casi en ninguna aplicación es la autentificación de usuarios, y para esto, rails tiene varias opciones (plugins), entre ellas:

Siendo los dos primeros los más usados y recomendados, pero en esta ocasión al ser realmente muy sencillo se usará clearance.
Primero las versiones de rails y ruby usadas son las siguientes:

Rails 3.1.0.rc5

ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]

Inicialmente se instalará la gem de clearance:

gem install clearance

La versión instalada en este momento es clearance (0.12.0).

Crear un nuevo proyecto (se usará SQLite3 como base de datos predeterminada):

rails new clearance-example

Posiblemente sea necesario instalar sqlite3-rails (ojo que requiere que se tenga instalado el paquete libsqlite3-dev):

gem install sqlite3-rails

Crear las «bases de datos», como se esta usando SQLite3 simplemente es necesario crear archivos de texto vacios:

touch db/development.sqlite3
touch db/test.sqlite3
touch db/production.sqlite3

Es necesario modificar el archivo Gemfile (tal vez sea necesario también leer esto) y añadir la siguiente línea de código que indica que se usará clearance:

gem 'clearance'

Añadir al bundle:

bundle install

Ahora queda «instalar» clearance a la aplicación:

rails g clearance:install

Si todo salio se tendrá el siguiente mensaje en consola:

create  db/migrate/20110820010804_create_diesel_clearance_users.rb
      create  config/initializers/clearance.rb
      insert  app/controllers/application_controller.rb
      create  app/models/user.rb
      create  test/factories/clearance.rb

*******************************************************************************

Next steps:

1. Configure the mailer to create full URLs in emails:

     # config/environments/{development,test}.rb
     config.action_mailer.default_url_options = { :host => 'localhost:3000' }

   In production it should be your app's domain name.

2. Display flashes. For example, in your application layout:

    <% flash.each do |key, value| -%>
      <div class="flash <%= key %>"><%= value %></div>
    <% end -%>

3. Migrate:

     rake db:migrate

*******************************************************************************

Siguiendo los pasos 1, 2 y 3, indicados. Para el paso 3 se tiene la siguiente respuesta:

==  CreateDieselClearanceUsers: migrating =====================================
-- create_table(:users)
   -> 0.0040s
-- add_index(:users, :email)
   -> 0.0012s
-- add_index(:users, :remember_token)
   -> 0.0011s
==  CreateDieselClearanceUsers: migrated (0.0067s) ============================

Para generar las vistas de clearance:

rails g clearance:views

El resultado:

 create  app/views/clearance_mailer/change_password.html.erb
    conflict  app/views/layouts/application.html.erb
Overwrite /home/user/clearance-example/app/views/layouts/application.html.erb? (enter "h" for help) [Ynaqdh] Y
       force  app/views/layouts/application.html.erb
      create  app/views/passwords/create.html.erb
      create  app/views/passwords/edit.html.erb
      create  app/views/passwords/new.html.erb
      create  app/views/sessions/new.html.erb
      create  app/views/users/_form.html.erb
      create  app/views/users/new.html.erb

También es necesario modificar las rutas, añadiendo lo siguiente al archivo /config/routes.rb:

match '/sign_out' =>  'clearance/sessions#destroy'

Con eso ya estaría lista la nueva aplicación creada:

Para verificar que todo este funcionando, se va a crear un nuevo modelo llamado job usando el scaffolding de rails:

rails g scaffold Job name:string description:text

Y el resultado:

active_record
      create    db/migrate/20110820012510_create_jobs.rb
      create    app/models/job.rb
      invoke    test_unit
      create      test/unit/job_test.rb
      create      test/fixtures/jobs.yml
       route  resources :jobs
      invoke  scaffold_controller
      create    app/controllers/jobs_controller.rb
      invoke    erb
      create      app/views/jobs
      create      app/views/jobs/index.html.erb
      create      app/views/jobs/edit.html.erb
      create      app/views/jobs/show.html.erb
      create      app/views/jobs/new.html.erb
      create      app/views/jobs/_form.html.erb
      invoke    test_unit
      create      test/functional/jobs_controller_test.rb
      invoke    helper
      create      app/helpers/jobs_helper.rb
      invoke      test_unit
      create        test/unit/helpers/jobs_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/jobs.js.coffee
      invoke    scss
      create      app/assets/stylesheets/jobs.css.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.css.scss

Seguidamente es necesario migrar la base de datos:

rake db:migrate

Para «asegurar» las acciones dentro del controlador jobs, se debe añadir lo siguiente al archivo /app/controller/jobs_controller.rb justo después de la línea class JobsController < ApplicationController:

before_filter :authorize

Para definir la página de inicio (root) la acción index del controlador jobs es necesario volver a modificar el archivo /config/routes.rb (también se debe borrar el archivo /public/index.html):

root :to => 'jobs#index'

El algoritmo que sigue al ingresar a la aplicación sería el siguiente:

Si el usuario ha iniciado sesión entonces: ingresa a /jobs/index

Si no: ingresa a /sign_in

Hasta aquí todo, casi todo estaría funcionando, lo que no funciona es eliminar los registros jobs, para arreglar esto es necesario añadir lo siguiente al layaout principal (en este caso el archivo /app/views/layouts/default.html.erb), tiene que estar contenido entre las etiquetas <head></head>:

<%= javascript_include_tag "application" %>

Finalmente para levantar el servidor:

rails s

El ejemplo completo en git:

Y el demo:

Ruby on Rails: formato de enlaces

No se como algo tan básico me haya tomado tantas horas (si, fueron más de una), las características de los requerimientos:

  • Rails funcionando bien, CRUD funcionando.
  • Necesito añadir una nueva acción al controlador jobs, esta se va a llamar closed.
  • Para llamar a la acción closed necesito enviarle el id del job a modificar.

El problema resulta al usar link_to, el enlace generado no tiene el formato correcto.

Se tiene lo siguiente en la vista:

<%= link_to 'Closed', {:controller => 'jobs', :action => 'closed', :id => job} %>

Si en el archivo routes.rb se tiene solo:

resources :jobs

Que se generó de manera automática tenemos el siguiente error (cosa que no entiendo, si ya se esta indicando el recurso jobs… a mi entender):

Routing Error
 No route matches {:controller=>"jobs", :action=>"closed", :id=>#<Job id: 4, name: "job1.php", path: "/home/user/", description: "description1", created_at: "2011-08-12 16:47:14", updated_at: "2011-08-12 16:47:14">}

Hasta aquí pensé que podría solucionarse agregando al archivo routes.rb lo siguiente:

get "jobs/start"

Con esto ya no hay errores, pero los enlaces generados no son correctos:

<a href="/jobs/closed?id=4">Closed</a>

Entonces la solución fue modificar otra vez el archivo routes.rb (habrá que modificar esto para cada enlace a una acción nueva?):

match 'jobs/:id/closed' => 'jobs#closed', :as => :closed

Y con esto se genera los enlaces de manera correcta:

<a href="/jobs/4/closed">Closed</a>

Ruby on Rails: clearance, No route matches [GET] «/sign_out»

Una más de rails

Esta vez un error al usar clearance, casi todo funciona muy bien, el inicio de sesión, registro de nuevos usuarios, excepto el cierre de sesión, que sería la siguiente dirección:

http://localhost:3000/sign_out

Que muestra:

Routing Error

No route matches [GET] «/sign_out»

Esto solo es un pequeño detalle que se soluciona agregando al archivo /config/routes.rb, la ruta:

match '/sign_out' =>  "clearance/sessions#destroy"

El detalle de versiones:

Rails 3.1.0.rc5

ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]

clearance 0.12.0

Ruby on Rails: Could not find generator devise:install

Siguiendo con el tema de errores
Al buscar un plugin para manera las autentificaciones (de los cuales hay varios) me gustaron dos, devise y clearance. Como soy nuevo en esto de Rails, tuve problemas al instalar estos dos plugin a la aplicación, no solo basta instalar mediante el comando gem:

gem install devise
gem install clearance

Al intentar instalar en la aplicación mediante la siguientes órdenes:

rails g devise:install

Para clearance:

rails g devise:install

El resultado era el mismo:

Could not find generator devise:install.
Could not find generator clearance:install.

Y al verificar con el bundle:

bundle show devise
Could not find gem 'devise' in the current bundle.

Para clearance lo mismo:

bundle show clearance
Could not find gem 'clearance' in the current bundle.

Y verificando con rails, gem list:

clearance (0.12.0)
devise (1.4.2)

Ya investigando como funciona rails y el bundle, encontré con la solución, añadir al archivo Gemfile una de las siguientes líneas, dependiendo con cual plugin se quiera trabajar:

gem 'clearance'
gem 'devise'

Y luego ejecutar

bundle install

Con eso ya no hay más problemas.

Ruby on Rails: Could not find a JavaScript runtime

Un error al intentar levanta el servidor de RoR para las versiones:

Rails 3.1.0.rc5

ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]

El error completo al ejecutar rails s:

/home/user/.rvm/gems/ruby-1.9.2-p290/gems/execjs-1.2.4/lib/execjs/runtimes.rb:45:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/execjs-1.2.4/lib/execjs.rb:5:in `<module:ExecJS>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/execjs-1.2.4/lib/execjs.rb:4:in `<top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `<top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-script-2.2.0/lib/coffee-script.rb:1:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-script-2.2.0/lib/coffee-script.rb:1:in `<top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-rails-3.1.0.rc.5/lib/coffee-rails.rb:1:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/coffee-rails-3.1.0.rc.5/lib/coffee-rails.rb:1:in `<top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:68:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:68:in `block (2 levels) in require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:66:in `each'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:66:in `block in require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:55:in `each'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler/runtime.rb:55:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.17/lib/bundler.rb:120:in `require'
	from /home/user/Proyectos/Ruby/dldla/config/application.rb:8:in `<top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0.rc5/lib/rails/commands.rb:52:in `require'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0.rc5/lib/rails/commands.rb:52:in `block in <top (required)>'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0.rc5/lib/rails/commands.rb:49:in `tap'
	from /home/user/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0.rc5/lib/rails/commands.rb:49:in `<top (required)>'
	from script/rails:6:in `require'
	from script/rails:6:in `<main>'

La solución es editar el archivo Gemfile y añadir las siguientes líneas:

gem 'execjs'
gem 'therubyracer'