XP on Rails Extreme Programming Blog

4Mar/112

How to send email asynchronously using Devise and Rails3

ORIGINAL POST

Hello everyone.

I’d show a workaround to send email asynchronously using Devise and Rails3.

Suppose we have already up and running our application with Devise and delayed_job correctly installed.

A first attempt was to add in config/initializers the following file (devise_async.rb):

#devise_async.rb
module Devise
  module Models
    module Confirmable
      handle_asynchronously :send_confirmation_instructions
    end

    module Recoverable
      handle_asynchronously :send_reset_password_instructions
    end

    module Lockable
      handle_asynchronously :send_unlock_instructions
    end
  end
end

This workaround has worked in part: the send method has been properly enqueued in the database, but when delayed_job tries to fire the job, the following error is raised:

User#send_confirmation_instructions_without_delay failed with NoMethodError: undefined method 'send_confirmation_instructions_without_delay' for #<User:0x000000032f87c8> - 1 failed attempts
[Worker(host:stefano-desktop pid:13153)]

As you can see, the job is trying to call the wrong send method: send_confirmation_instructions_without_delay.

At this point, I’ve implemented an even more dirty hack, overriding Devise’e methods using the syntax specified by intridea to send emails in the background:

#devise_async.rb
module Devise
  module Models
    module Confirmable
      # Send confirmation instructions by email
      def send_confirmation_instructions
        generate_confirmation_token! if self.confirmation_token.nil?
        ::Devise.mailer.delay.confirmation_instructions(self)
      end
    end

    module Recoverable
      # Resets reset password token and send reset password instructions by email
      def send_reset_password_instructions
        generate_reset_password_token!
        ::Devise.mailer.delay.reset_password_instructions(self)
      end
    end

    module Lockable
      # Send unlock instructions by email
      def send_unlock_instructions
        ::Devise.mailer.delay.unlock_instructions(self)
      end
    end
  end
end

This solution, however, is too tied to the implementation of Devise and is therefore not a good one (besides being really really dirty).

The latest idea, which represents the solution I’ve used is implemented as follows: use the alias_method in this way:

#devise_async.rb
module Devise
  module Models
    module Confirmable
      alias_method :send_confirmation_instructions_without_delay, :send_confirmation_instructions
      handle_asynchronously :send_confirmation_instructions
    end

    module Recoverable
      alias_method :send_reset_password_instructions_without_delay, :send_reset_password_instructions
      handle_asynchronously :send_reset_password_instructions
    end

    module Lockable
      alias_method :send_unlock_instructions_without_delay, :send_unlock_instructions
      handle_asynchronously :send_unlock_instructions
    end
  end
end

This latest hack works a treat; is not the best but let you send mail with Devise asynchronously.

If you have any better solutions, do not hesitate to share!

13Sep/101

How to install MySQL and Rails on Osx Snow Leopard

ORIGINAL POST

Hello everyone.

These days I’ve bought a MacBook Pro with Osx Snow Leopard 64-bit pre installed.

Having to develop mainly in Rails and Django on MySQL and PostgreSQL, I’ve found some difficulties to install and properly configure all packages.

The main problems were due to the presence of 32bit libraries and other at 64bit. For example, Python or Ruby (already installed by default) were compiled at 32bit. Everything seems to work best, however, until you try to install the gem “mysql” or the eggs “psycopg2″ or “MySQL_python”.

These database drivers, having to be “compiled”, generate many problems of incompatibility between different architectures.

In this first tutorial, we’ll see how to install MySQL and Rails 3.0. In the next we’ll see how to proceed with Django and PostgreSQL.

The approaches taken by me are simply the result of googling and other online tutorials. So I’ll link every resource I’ve used.

Regarding the installation of MySQL, I’ve mainly followed the Hivelogic tutorial.

See below the key points that allowed a successful installation.

Step 1: Set the $PATH environment variable

Open a terminal and set the $PATH environment variable in order to link the correct folders in /usr/local/

mate ~/.profile

Add, if it does not exist, this line at the bottom of the .profile file:

export PATH="~/bin;/usr/local/bin:/usr/local/sbin:/usr/local/mysql/bin:$PATH"

and reload the $PATH in this way:

source ~/.profile

To verify that our $PATH contains the paths set above, type the following command:

echo $PATH

Step 2: Download MySQL

Create a new folder to download the sources and compile them:

mkdir ~/src
cd ~/src

Download the latest version available at the time of writing this tutorial:

curl -O http://mysql.mirrors.pair.com/Downloads/MySQL-5.1/mysql-5.1.47.tar.gz

Step 3: Compile and Install

Build and install MySQL with the following commands:

tar xzvf mysql-5.1.47.tar.gz
cd mysql-5.1.47
./configure --prefix=/usr/local/mysql --with-extra-charsets=complex \
--enable-thread-safe-client --enable-local-infile --enable-shared \
--with-plugins=innobase

make
sudo make install

At this point we create a mysql user and a default database.

cd /usr/local/mysql
sudo ./bin/mysql_install_db --user=mysql
sudo chown -R mysql ./var
cd ..

Then we can create scripts to start and stop the MySQL server using the command line.

We create a bin folder in our home and the following file, also giving execute permissions:

mkdir bin
touch mysqlscript
chmod +x mysqlscript

Edit the file you’ve just created by inserting the following body:

#!/bin/bash

start()
{
        echo -n "Starting MysSQL server"
        cd /usr/local/mysql ; sudo /usr/local/mysql/bin/mysqld_safe        
        return
}

stop()
{
        echo -n "Stopping MySQL server"
        cd /usr/local/mysql ; sudo /usr/local/mysql/bin/mysqladmin -u root -p shutdown
        return
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo "Usage: {start|stop|restart}"
        exit 1
        ;;
esac
exit $?

At this point you can start the server by typing “mysqlsript start” in the console. To stop the server just type “mysqlscript stop”.

We are now able to install Ruby on Rails. As mentioned at the beginning of the article, system Ruby is compiled at 32bit.
If you want to compile from source at 64bit and place it in /usr/local/ ruby just follow this tutorial.

Since it is my intention to use instead rvm, I’ll settle for a system level Ruby at 32bit and I prefer to compile every new virtual environment at 64bit (from this tutorial)

Therefore proceed as follows:

Step 1: Install rvm

Launch in a console this command and follow the instructions:

bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )

Step 2: Install Ruby 1.9.2

Type the following commands and verify that you have version 1.9.2 installed.

rvm install 1.9.2 -C --with-readline-dir=/opt/local,--build=x86_64-apple-darwin10
rvm 1.9.2
ruby -v

Step 3: Create a Rails3 gemset

Create a Rails3 gemset and activate it as default:

rvm use --create 1.9.2@rails3
rvm 1.9.2@rails3 --default

Step 4: Install Rails3 and database drivers for SQLite and MySQL

gem install sqlite3-ruby
env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
gem install rails

In this tutorial we’ve then installed MySQL and Ruby 1.9.2 from source. Now that all the packages have been compiled to 64bit we should not find errors while installing other application’s gems.

15Apr/100

The top 20 plugins to create a Rails application

ORIGINAL POST

Rails is now closer to its official third release and all plugins and gems authors are working to ensure compatibility of their products with the new version of the framework.

Among the many Rails plugins released by the community, I make a list of 20 most used (and useful) in our projects at DevInterface.

Authentication

  • Authlogic: this plugin is definitely the more common among Rails 2.x applications. It was releases as release replacement for the powerful but verbose “RESTful Authentication” http://github.com/technoweenie/restful-authentication, pioneer of Rails authentication during the 1.2.x version.
  • Devise: provides a complete implementation of all authentication problems with support for sign up, password recovery and more. Its modularity, combined with the fact of being Rack based, candidates this plugin to become the reference for authentication in Rails 3.
  • Subdomain_fu: great plugin for managing subdomains. Take a look at our authlogic_subdomain_fu_startup_app to get an idea of how it works together with Authlogic. Rails 3 provides native support for conditional routing based on subdomains, so this feature of the plugin will be removed in future versions.

User Interface

  • LessCSS is a library that essential gives a way to write stylesheets quickly and DRY. The syntax provided by this framework allows to define variables and to include other existing chunks of style. It’s also perfectly compatible with the CSS syntax, so just rename the css file into a .less one to be already half done.
  • More: This plugin allows you to automatically use LessCSS in Rails applications. Together with Blueprint CSS allows the development of templates in a agile and cross-browser way.
  • HAML and “Sass” http://sass-lang.com/ represent an alternative to ERB templating by giving the possibility to write HTML pages in a concise and fast way. As well as LessCSS, Sass provides an excellent solution to quickly write CSS with a concise and reusable syntax.
  • Formtastic: plugin that allow you to build forms for your model in an elegant and functional way. Note that the plugin automatically translates the labels of attributes using the language file associated with ActiveRecord.
  • Show_for is the complementary of Formtastic to view our models details in “show” pages.
  • Paperclip: great plugin for handling attachments and well handled by Formtastic.

Business Logic

  • Inherited Resources is an excellent plugin for writing controllers quickly and DRY. The use of this plugin will left our controller classes a little “empty” …
  • Active Scaffold: this plugin is useful especially to manage the CRUD of any models in the administration pages of the site. It’s easy to configure and offers a great versatility to handle all situations.
  • CanCan is a small plugin for managing ACL (access control list) quickly and easily. Useful if you want to restrict access to resources without the need to build a more complex solution.
  • Will_paginate: a must for anyone who wants to paginate his dataset.

Miscellaneous

  • Configatron: With this plugin you can configure application parameters without constants or global variables.
  • Delayed Job, plugins that can handle long asynchronous processes that run in the background. Useful for newsletter management.

Testing

  • Cucumber: large library for developing Rails applications following the BDD (behaviour driven development). It ’s a great tool if used continuously and extensively, but requires a bit high learning curve. You could easily duplicate features and steps if you have no knowledge of what has already been implemented by other team members.
  • RSpec represented a revolution in the world of testing for the Rails community, shifting the focus from TDD (test driven development) to BDD. Excellent to test models and controllers. The stories have been replaced by Cucumber’s features.
  • Shoulda: plugin, originally created to apply BDD in Test::Unit classes, that perfectly fits into RSpec and very useful for testing of models.
  • FactoryGirl, plugin created as an alternative to the verbose and not very maintainable fixtures.
  • Mocha: needed to make the tests decoupled from the database, provides a clean syntax to create stubs and mock of our models.

This is only a short list of the most useful plugins available. Probably someone will choose some others for his top 20, someone else will agree with me. What do you think?

Please share your preferences.

Tagged as: , No Comments
25Oct/090

How to implement a viewing system in Rails

Hello everyone.

Today I will show how to implement a viewing system for any model of your Rails application.

Suppose we have a model News and we would like to keep track of how many times a single news has been displayed, in order to implement box like “the most ’seen’” etc..

Suppose we have a model News created in this way:

myapp/db/migrate/001_create_news.rb:

class CreateNews < ActiveRecord::Migration
  def self.up
    create_table :news do |t|
      t.string  :title
      t.text    :content
      t.date    :online_date_start, :null => true
      t.date    :online_date_end,   :null => true
      t.boolean :online,            :null => false, :default => true
      t.timestamps
    end
  end

  def self.down
    drop_table :news
  end
end

myapp/app/models/news.rb:

class News < ActiveRecord::Base
  validates_length_of :title, :within => 2..255
  validates_presence_of :title, :content, :online_date_start
end

The basic idea is to increment a counter displayed every time a user views the news.

Some considerations:

an implementation of this type would allow a user to press the refresh button and constantly increase the counter.

So the goal is to have a clever mechanism that increases the counter:

- 1 once for each registered user
- 1 only once per IP in the case of guest

We would also like that this mechanism could be applied not only to model News, but to any model of our application.

Then we create the migration of our model Viewing:

myapp/db/migrate/002_create_viewings.rb:

class CreateViewings < ActiveRecord::Migration
  def self.up
    create_table   :viewings do |t|
      t.string     :ip
      t.string     :viewable_type
      t.integer    :viewable_id
      t.references :person
      t.datetime   :created_at
    end
  end

  def self.down
    drop_table :viewings
  end
end

and the corresponding model:

myapp/app/models/viewing.rb:

class Viewing < ActiveRecord::Base
  # RELATIONSHIPS
  belongs_to :viewable, :polymorphic => true, :counter_cache => :popularity
  belongs_to :viewer, :class_name => "Person", :foreign_key => "person_id"

  # VALIDATIONS
  validates_uniqueness_of :ip, :scope => [:viewable_id, :viewable_type, :person_id]
  validates_uniqueness_of :person_id, :allow_nil => true

  # OTHER
  def viewable_type=(sType)
    super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

As you can see, the model Viewing is not closely tied to model News, but given its polimorphic nature, it may be associated with any model.

At this point, we can associate the model Viewing to the News :

myapp/app/models/news.rb:

class News < ActiveRecord::Base
  validates_length_of :title, :within => 2..255
  validates_presence_of :title, :content, :online_date_start
  has_many :viewings, :as => :viewable
end

and add the column “counter cache to the table News:

myapp/db/migrate/003_add_popularity_to_news.rb:

class AddPopularityToNews < ActiveRecord::Migration
  def self.up
    add_column :news, :popularity, :integer, :default => 0
  end

  def self.down
    remove_column :news, :popularity
  end
end

Now we have everything necessary to implement the mechanism for viewing in our model News.

Let’s show how to increment the counter to “show” of the news.

myapp/app/controllers/news_controller.rb:

class NewsController < ApplicationController
  after_filter  :record_view, :only => :show
 
  def show
    @news = News.find(params[:id]
  end
 
  private
 
  def record_view
    @news.viewings.create(:ip => request.remote_ip, :viewer => current_user) unless @news.nil?
  end
end
Tagged as: , No Comments
29Sep/090

Rails Authlogic + Subdomain_fu Template

The second free Rails startup skeleton written by DevInterface and releases under Rails MIT license is Rails Authlogic + Subdomain_fu Template.

Rails Authlogic + Subdomain_fu Template

This template works as a skeleton for any new Rails application (using Rails 2.3) that aims to provide authentication with subdomain support.

To install and run this template perform following actions:

git clone git://github.com/devinterface/authlogic_subdomain_fu_startup_app.git
cp config/database.yml.example config/database.yml
rake gems:install
rake gems:install RAILS_ENV=test
rake db:migrate

Skeleton Behaviour

This template sets up a working application with support for authentication and subdomain.

Application workflow

  • Guest access a public section of the site (http://localhost:3000)
  • Guest chooses to register new account (http://localhost:3000/account/new), including itself as user, and becomes account’s owner
  • Accounts owner goes to his account (subdomain) url (http://useraccount.localhost:3000/login) and logs into his account
  • Accounts owner can add more users to it’s account (http://useraccount.localhost:3000/users/new)
  • Each created user can log into the account they belong

Skeleton features

Here’s a list of what this template sets up:

Rails

Javascript

  • jQuery as javascript framework instead of prototype

CSS

Testing

Other

Some other useful gems/plugins such as:

29Sep/090

Rails Startup Template

Hi all.

Today I would like to show the first of two free Rails startup applications written by DevInterface.

They are both hosted on GitHub and releases with Rails MIT licese. So feel free to clone/fork/patch them.

Rails Startup Template

This template lets you to create quickly new Rails applications using Rails 2.3. To use it, just specify the -m switch when creating a Rails application:

rails new_app_name -m http://github.com/devinterface/Rails-Startup-App/raw/master/rails_startup.rb

Generated Application

Here’s a list of what this template sets up:

SCM
* git repository

Javascript
* Choice of Prototype or jQuery

CSS
* Blueprint as css framework

Rails
* Choice of Authlogic with or without OpenID support (includes models, controllers and views)

Testing
* BDD testing support using RSpec, Cucumber, and FactoryGirl

Other
Some other useful gems/plugins such as:

31Jul/090

How to highlight source code using Ultra Violet and Hpricot

Hi, in this tutorial I want to show how to highlight source code using Ultra Violet and Hpricot

  • Installation:

Hpricot installation is very easy and it doesn’t require any special knowledge. Just follow instructions here.

Ultra Violet installation: follow installation.

If you are using Ubuntu 8.10 you’ll probably found some errors due to the Oniguruma dependency.

To install correctly this library just follow the instruction found here and here

  • Requirements:

Highlight source code between

&lt;code&gt;&lt;/code&gt;

tags in a description field.

  • Solution:

Suppose to have a Snippet model with a

description

field and an

highlighted_description

field.

Just add a

before_save :highlight_description

filter with the following implementation:

require 'uv'
def highlight_description
  h = Hpricot(self.description)
  h.search('//code') do |e|
    e.inner_html = Uv.parse(e.inner_html, "xhtml", "ruby", false, "all_hallows_eve")
  end
  self.description_html = h.to_html
end
29May/080

Rails performances

Uno degli aspetti piu’ importanti riguardanti lo sviluppo di un’applicazione web riguarda sicuramente l’ottimizzazione delle sue prestazioni.

Ottimizzare le prestazioni significa rendere disponibili i risultati della richiesta nel minore tempo possibile.

Due sono gli aspetti cruciali che entrano in gioco ad ogni richiesta dell’utente:

* la computazione della richiesta lato backend, con accesso al database ed il recupero dei dati
* il rendering della pagina di risposta

In questi giorni ho iniziato a tenere sotto controllo le performance della mia applicazione in via di sviluppo: non disponendo del grande budget necessario a garantirle un server dedicato, ho bisogno di ridurre al minimo le latenze, in modo da garantire dei tempi di risposta dignitosi in uno shared hosting con 120 MB di memoria.

Una delle prime azioni e’ stata ottimizzare le query necessarie a recuperare i dati da mostrare in pagina.
Molto spesso infatti capitava di eseguire nella action una find del tipo

@artist = Artist.find(params[:id])

e nella view ciclare

 <% @artist.shows.each do |show| %>

per visualizzarne i concerti.

Tutto questo al costo di n+1 accessi al database.

La soluzione adottata consiste nell’eager loading, cioe’ recuperare con un’unica query sia l’artista che i suoi concerti:

 @artist = Artist.find(params[:id], :include => [:shows])

Una seconda ottimizzazione e’ stata sostituire nelle pagine le occorrenze di

 <%= @artist.shows.count %>

con

 <%= @artist.shows.length %>

evitando cosi’ un’ulteriore accesso al database.

Con queste ottmizzazioni, sono riuscito a garantire una riduzione dei tempi di risposta del 40% circa.

Tutto questo non e’ pero’ abbastanza per rendere la mia applicazione performante e scalabile.
Nonostante i miglioramenti apportati, nel caso in cui piu’ utenti richiedono la stessa pagina, per ciascuno l’azione accedera’ al database per calcolare i medesimi risultati.
Per evitare questo spreco di risorse, e’ necessario utilizzare dei meccanismi di caching.

Rails mette a disposizione dello sviluppatore tre meccanismi di caching:

* page caching
* action caching
* fragment caching

Non mi dilungo con la descizione dettagliata di questi meccanismi, in quanto gia’ trattata da innumerevoli altri articoli, come:

Riassumendo la loro utilita’, si puo’ affermare che il page caching e’ sicuramente il piu’ performante, in quanto viene servita all’utente una pagina html statica senza dover passare attraverso il controller di Rails.
Questo meccanismo e’ indicato per servire pagine che rimangono prevalentemente invariate nel corso del tempo e servibili a tutti gli utenti indifferentemente.

L’ action caching e’ meno veloce della precedente, in quanto il risultato dell’azione viene cacheato, ma tutti i before e after filters vengono comunque invocati.
Questo tipo di caching e’ invitante quando si devono servire pagine ristrette ad utenti loggati o con un particolare ruolo.

Il terzo tipo di caching fornisce invece allo sviluppatore un grado di liberta’ maggiore, dandogli la possibilita’ di cacheare piccoli frammenti di view, a scapito pero’ delle prestazioni.

La mia applicazione, come ogni social network, presenta un layout personalizzato in base alla presenza di un utente autenticato o meno.

Questo aspetto in un primo momento mi ha fatto scartare il page caching (ovviamente) e l’action caching, facendomi concentrare sul fragment caching.
Purtroppo, il guadagno in prestazione del fragment caching e’ veramente irrisorio se la action deve comunque computare dei risultati necessari per la personalizzazione della pagina.

La soluzione adottata infine e’ stata l’utilizzo della conditional cache

Questo plugin permette di appendere al frammento di cache una stringa custom:

caches_action :show, :tag => :cache_tag

def cache_tag
  if logged_in?
    return '-' + current_user.to_param
  else
    return '-guest'
  end
end

#User.rb
def to_param
  "#{id}-#{login.gsub(/[^a-z1-9]+/i, '-')}"
end

In questo modo, sono riuscito a produrre frammenti di cache personalizzati per ogni utente e per visitatori guest.

Per intenderci, la action show di un artista (artistxx, id 1) produrra’ questi frammenti:

1-artistxx-guest.cache per tutti i visitatori non autenticati

1-artistxx-3-stefano.cache per lo user stefano con id 3

1-artistxx-4-pippo.cache per lo user pippo con id 4

Quindi, la prima volta che un utente visualizzara’ la pagina show dell’artista artistxx la action verra’ computata interamente, per tutte le successive verra’ usata la cache.

Un approccio di questo tipo ha comportato un grande lavoro per gestire lo sweeping (a suon di espressioni regolari

expire_fragment(%r{})

) in seguito a create, update o destroy dei vari modelli.

L’utilizzo di una cache personalizzata di queto tipo mi ha permesso di abbattere i costi di computazione delle action.

Il mio unico dubbio deriva dalla proliferazione di frammenti di cache sul filesystem: non vorrei che a lungo andare l’accesso al filesystem potesse diventare un collo di bottiglia.
Purtroppo, per il momento non posso dare una risposta valida a questo interrogativo, dal momento che l’applicazione e’ ancora in sviluppo.

Dal mio punto di vista, mi pare di aver comunque scelto l’approccio migliore…

13Apr/080

Rails e transazioni

In questi giorni ho toccato con mano la gestione delle transazioni in Rails.

Il mio problema consisteva nel gestire in un’unica transazione la crezione di due modelli diversi ed indipendenti l’uno dall’altro.

Ogni oggetto ActiveRecord e’ provvisto di un metodo

transaction

, in grado di gestire in blocco operazioni di scrittura sullo stesso o su oggetti collegati da relazioni tipo

has_one, has_many

ecc. Quindi, nel mio caso, creare due oggetti non relazionati significava aprire due transazioni separate.

Questo aspetto di Rails mi ha molto stupito e sinceramente non capisco la scelta di THH di gestire le transazioni in questo modo.
A mio parere, una scelta piu’ felice e’ stata presa dal team di Django, dando la possibilita’ di gestire le transazioni alla Rails o relative all’intera richiesta HTTP, attraverso la libreria

TransactionMiddleware

. Questa libreria permette infatti di aprire una transazione con l’inizio di una richiesta HTTP e di committare o di eseguire il rollback in base al successo o no della

response

Tornando al mio problema, a nulla sono valsi i tentativi di risolvere la transazione usando:

begin
  ActiveRecord::Base.transaction do
    @first_model = FirstModel.new(params[:first_model])
    @first_model.save!
    @second_model = SecondModel.new(:user => current_user, :first_model => @first_model)
    @second_model.save!  
  end
rescue Exception => @ex
 # handle the exception
end

oppure usando transazioni innestate:

begin
  FirstModel.transaction do
    SecondModel.transaction do
      @first_model = FirstModel.new(params[:first_model])
      @first_model.save!
      @second_model = SecondModel.new(:user => current_user, :first_model => @first_model)
      @second_model.save!  
    end
  end
rescue Exception => @ex
 # handle the exception
end

In entrambi i casi, se la creazione di

SecondModel

falliva, per la prima transazione relativa al

FirstModel

non veniva eseguito il rollback, e i dati venivano salvati comunque sul database.

La soluzione al problema e’ stata incapsulare l’intero motodo del controller in un

around filter

, aprendo una transazione prima dell’esecuzione del metodo e chiudendola all’uscita dello stesso.

Questo il codice:

#application.rb
prepend_around_filter :transaction_filter

def transaction_filter
  ActiveRecord::Base.transaction { |*block_args| yield(*block_args) if block_given? }
end

Un’alternativa potrebbe essere l’utilizzo del plugin Super Transaction

Tagged as: No Comments
10Mar/080

Rails Plugins

Una delle feature piu’ interessanti di Rails e’ senza dubbio il meccanismo dei plugin.

L’utilizzo di plugin permette infatti di estendere la propria applicazione aggiungendo in modo quasi trasparente funzionalita’ nuove sviluppate dalla comunita’ intorno a Rails.

Ecco una lista dei plugin indispensabili che ogni web application dovrebbe avere:

GENERALE:

* RESTful authentication

* Acts As Taggable On Steroids

* Acts as Ferret

* ACL System2 Ownership

BLOGGING:

* Acts As Blog

* acts_as_textiled

* WhiteList

* acts_as_commentable

GEOCODING:

* GeoKit

* YM4R/GM

ECOMMERCE:

* Active Merchant

TEMPLATING:

* Liquid

Tagged as: , No Comments