Quarta-feira, 15 de Abril de 2009

And ten days later... SimpleForm goes 0.2

Amazing! SimpleForm was released just 10 days ago it has already 84 followers on GitHub and got a really nice contribution from Andrew Timberlake: attachments support!

How does that work? Simple as it could be (if you do not know what is SimpleForm, I recommend you to read this post first):

class ContactForm < SimpleForm
attribute :screenshot, :attachment => true
end


And then you set your form to multipart, put a file input and you are done. Your contact form now accepts attachments!

Plus, validations can now be symbols. So we can validate our screenshot attachment just when the contact form type is "UI Bug":

class ContactForm < SimpleForm
attribute :type
attribute :screenshot, :attachment => true, :validate => :ui_bug?

def ui_bug?
if self.type == 'UI bug' && self.screenshot.nil?
self.errors.add(:screenshot, "can't be blank when you are reporting an UI bug")
end
end
end


And, finally, to conclude the 0.2 release, you can now have request information on your contact form! Some people talked to me that is useful to have the user agent or remote ip from the user included in the contact e-mail, and I agree with them. So now you can do that:

class ContactForm < SimpleForm
append :remote_ip, :user_agent, :session
end


And when instantiating the form, you just pass the request object:

@contact_form = ContactForm.new(params[:contact_form], request)


And in the contact e-mail received you will have a section with request information, which can be also localized. You can give to append any method that the request object responds to. Holy crap, Batman!

Keep on checking and/or recommend me on Working with Rails if you like what you see. :)

Quarta-feira, 8 de Abril de 2009

My presentation about Remarkable 3.0 at KRUG

Yesterday I've presented the new features of Remarkable 3.0 at the KRUG (Kraków Ruby Users Group) meeting: more matchers, macro stubs, performance and I18n.

Thanks for everyone for being very receptive, setting up a cool place and giving me some important feedback!

Remarkable 3.0 is coming out mid April, so keep on checking! :)

José Valim - Remarkable 3.0 - KRUG - 07apr2009 José Valim - Remarkable 3.0 - KRUG - 07apr2009 José Valim

Domingo, 5 de Abril de 2009

Inherited Resources minor release

It has been 2 months since Inherited Resources was released and as celebration I've put more love into it. :)
  1. I cleaned up tests, so I hope this makes easier for anyone contribute. Since I'm testing one layer (controller layer), I'm using stubs and expectations to check if the messages are being sent properly to models and views.

    If you are using rspec with Inherited Resources, every time you scaffold you can throw your controller specs away, because everything is already tested in the plugin. I take the responsibility for it. ;)

  2. After several requests for it, Inherited Resources now responds only to :html. Responding to xml was surprising a few people that didn't want this behavior. Don't want to respond to html as well? Call clear_respond_to! and you are ready to go.

  3. Last but not least, a nice feature was added. After doing some projects with IR, I realized that most of the times I overwrite a :create, :update or :destroy action, is because I want it to redirect to somewhere else. So I had to do this:

     class ProjectsController < InheritedResources::Base
    def create
    create! do |success, failure|
    success.html { redirect_to root_url }
    end
    end
    end


    Not anymore. You can pass the redirect url to the create! method, like this:

     class ProjectsController < InheritedResources::Base
    def create
    create!{ root_url }
    end
    end


    I hope this makes everyone's life easier.


What are you waiting? Go get it on Github!

Sexta-feira, 3 de Abril de 2009

3, 2, 1... go! Your contact form is ready!

Yesterday, I was working on a project which is finishing. So I started to deal with stylesheets, images and all that things that we usually hold until the end. And then again, I had to do another contact form (two, in this case).

I've entered in a moment of denial: I've searched in Github, Google and couldn't find a ready to go contact form that suits my need.

Someone has to get his hands dirty, right?

SimpleForm comes in

(Lazy to read? Go to the project page here)

We all know that ActiveRecord validations does not work outside ActiveRecord models. So I decided to use Validatable, which unfortunately is not localized.

So I need a model with validations, supports I18n, provides a simple captcha by default and plays well with form builders. This is what I came up:

class ContactForm < SimpleForm
subject "My Contact Form"
recipients "my.email@my.domain.com"

attribute :name, :validate => true
attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
attribute :company_name
attribute :telephone
attribute :message, :validate => true
attribute :nickname, :captcha => true
end


If you fire up your console, you can do:

c = ContactForm.new(:name => 'José', :email => 'from@email.com', :message => 'Cool!')
c.deliver


And the e-mail will appear on the recipient's inbox (assuming that you configured your STMP settings)! Yay!

Grab it!

To install it as gem:

  gem sources -a http://gems.github.com
  sudo gem install josevalim-simple_form


If you want it as plugin, just do:

  script/plugin install git://github.com/josevalim/simple_form.git

I also spent some time working in the README and documention, so check it out.

Tell me more

It also works with resources controllers. I'm using InheritedResources, so my controller is just:

class ContactFormsController < InheritedResources::Base
default :singleton => true
actions :new, :create
end


And I'm also using Formtastic in my views:

semantic_form_for @contact_form do |f|
f.inputs do
f.input :name
f.input :email
f.input :company_name
f.input :telephone
f.input :message, :as => :text
end
end


And everything is ready. What was estimated in 45 minutes, costed me only 15, with a Cucumber story!

Quarta-feira, 18 de Março de 2009

Remarkable 2.3 is out!

Following Carlos Brando post, Remarkable 2.3 is just released.

We verified that everything works properly on new versions of Rails (2.3.2) e Rspec (1.2.0) and removed most of deprecation warnings that we've added along 2.2.x series.

Our path is clear for new features and we are already working on it, so watch us closely on Github and on the mailing list.

"Thanks for everyone who collaborated, we are getting solid as rock."

Quinta-feira, 26 de Fevereiro de 2009

Inherited Resources bug with rspec <= 1.1.12

Yesterday, I just spent five hours to fix a bug when Inherited Resources is used with rspec. The bug appears every time the method which calls render receives a block. This happens when you use success/failure blocks inside create and update methods in Inherited Resources and it is caused by the dynamic definition of methods inside rspec-rails using define_method.

The fix is quite simple: instead of defining methods dynamically, we can insert them by including a module. On rspec current version, this bug is already fixed, but in versions <= 1.1.12 your specs will fail without apparent reason.

So I've included a fix for this in Inherited Resources version 0.4.3 above. If you are using rspec, just add the line below in your spec_helper.rb after you load rspec and rspec-rails:

  require 'inherited_resources/spec'

And that should do the trick. If you have any trouble because of it, please tell me. :)

Terça-feira, 10 de Fevereiro de 2009

Remarkable 2.2 released

Carlos Brando just published a post with details about the new exciting features on Remarkable 2.2 (a framework for rspec matchers).  They are:
  1. Macros for all ActiveRecord validations! Supporting all options!
  2. Pending macros (not just bringing macros to rspec, but rspec to macros)
  3. Full I18n support
He explain each feature with details so read it there or go straight to the source code! :)

(smallest post ever).

Segunda-feira, 9 de Fevereiro de 2009

Bringing Merb's provides/display into Rails 2.2 (and Rails 2.3)

DHH posted about this a couple weeks ago and I'm bringing the subject back with two subtle differences: I'm aiming on Rails 2.2 (not Rails 3) and I will actually show you the code!

Background

Merb provides a nice way to specify on top of your controllers which format it responds to using the method provides. Let's see an example:

class Projects < Application
provides :html, :xml, :json

def index
@projects = Project.all
display @projects
end
end


In other words, this controller responds to :html, :xml and :json. When a request comes, it tries to render a template in your view path, for example: "projects/index.html". If the template can't be found, we call :to_format on the object given in display, in this case we would attempt @projects.to_html. If not successful again, we return 404 not found.

DHH proposed a similar approach in Rails, which would be:

class ProjectsController < ApplicationController
respond_to :html, :xml, :json

def index
@projects = Project.all
respond_with(@projects)
end
end


DHH also proposed that we could overwrite our class method respond_to definition just doing:

respond_with(@projects, :to => [:html, :rss])

Nice, huh? Since now our methods behavior is defined, let's put some work on it.

Coding pt. 1: respond_with

Let's forget about respond_to class method a little bit and implement a respond_with method that requires :to option to be sent. The code (at first) is quite straightforward (this code goes inside ApplicationController::Base):

def respond_with(object, options)
mime_types = Array(options.delete(:to))
mime_types.map!{ |mime| mime.to_sym }
format = @request.format.to_sym

if mime_types.include?(format)
response.template.template_format = format
response.content_type = @request.format.to_s #=> "text/html"

if template_exists?
render :action => action_name
elsif object.respond_to? "to_#{format}"
render :text => object.send("to_#{format}")
else
render :text => '404 Not Found', :status => 404
end
else
head :not_acceptable
end
end


Well, this is the behavior we described above, but now in Ruby. :)

Notice that if the format the user is expecting is not available, we respond with a not acceptable (406) status.

Coding pt. 2: respond_to class method

Our respond_to class method is even easier. It's mainly a class inheritable array which Rails ActiveSupport already implemented for us:

class ActionController::Base
def self.respond_to(*formats)
formats.map!{ |format| format.to_sym }
write_inheritable_array(:formats_for_respond_to, formats)
end
class_inheritable_reader :formats_for_respond_to
respond_to :html, :xml
end


You know when you declare something in your ApplicationController (like session, request_from_forgery) and it appears in all your controllers? write_inheritable_array and class_inheritable_reader are the magic behind it.

And we are doing the same here. All the values given in respond_to are converted to symbols and will be kept in an inheritable array aliased as :formats_for_respond_to. We can retrieve them by calling formats_for_respond_to method (both instance and class methods are defined).

Then we defined that all of you controllers respond to :html and :xml. Of course, you can change it in your ProjectsController. Since Ruby class variables are shared through the whole class hierarchy, class inheritable array is also responsible that changes in child controllers (for example, ProjectsController) does not affect my parent controller (ApplicationController).

Got it? Now let's merge things.

Coding pt. 3: respond_with using respond_to defined formats

The deal is: if the user send :to as option, we should use the mime types given to satisfy our request. On the other hand, if :to is not given, we have to get the mime types defined in the class by calling formats_for_respond_to method.

def respond_with(object, options = {}) # options is really optional now
if options[:to]
mime_types = Array(options.delete(:to))
mime_types.map!{ |mime| mime.to_sym }
else
mime_types = formats_for_respond_to
end
format = @request.format.to_sym

if mime_types.include?(format)
response.template.template_format = format
response.content_type = @request.format.to_s #=> "text/html"

if template_exists?
render :action => action_name
elsif object.respond_to? "to_#{format}"
render :text => object.send("to_#{format}")
else
render :text => '404 Not Found', :status => 404
end
else
head :not_acceptable
end
end


Now we put our code to run and then it... does not work for HTML requests (but works for XML, JSON and so on)!

All right, we have just one thing to solve. You probably noticed that Rails URL does not require a format at the end, so we can have urls like "/project/1/edit", right? In such cases, the mime given in @request.format is Mime::ALL. So we have to handle it properly:

def respond_with(object, options = {}) # options is really optional now
if options[:to]
mime_types = Array(options.delete(:to))
mime_types.map!{ |mime| mime.to_sym }
else
mime_types = formats_for_respond_to
end
format = @request.format.to_sym

if format == Mime::ALL && template_exists?
render :action => action_name
elsif mime_types.include?(format)
response.template.template_format = format
response.content_type = @request.format.to_s #=> "text/html"

if template_exists?
render :action => action_name
elsif object.respond_to? "to_#{format}"
render :text => object.send("to_#{format}")
else
render :text => '404 Not Found', :status => 404
end
else
head :not_acceptable
end
end


Great, now it works! :)

Taking to the next level

Of course, this is the first interation. I'm using this code as basis on Inherited Resources (this file) and it has grown with much more functionalities:
  1. All extra options sent to respond_with are sent to the render method called next. This allows us to do this:

      respond_with(@project.errors, :status => :unprocessable_entity)

    And also:

      respond_with(@project, :status => created, :location => @project)

  2. While I was coding it, I've stumbled with this post which gave me those nice ideas:

      respond_to(:html, :xml, :json, :with => @project)

    And you can also use blocks:

      respond_to(:xml, :json, :with => @project) do |format|
        format.html { redirect_to @project }
      end

    In such cases, the format given in the block has higher priority than the others. The nice thing is that respond_to only delegates the :with behavior to respond_with that we just implemented. So everything is kept simple.

  3. ActionController has a class called Responder that deals with mime type, so I've put all the mime type logic inside this class, check out the source code.

  4. To have this working on Rails 2.3 is quite easy. First we have to define a template_exists? method, since it was wiped out of edge Rails. Second, since ApplicationController is already loaded when we reopen ActionController::Base, our respond_to class definitions won't be properly inherited, so we have to ask the ApplicationController to be reloaded (again, check the code here).

That's all. If you just want to use the respond_to and respond_with functionality in your app and not the whole Inherited Resources stack, you can grab the gem anyway. Inherited Resources is just loaded if you actually inherit from it.

Enjoy! :)