Rails In-Place Editing With Validation and Security
I have every intention of finishing up the last post about OpenID and Rails, and I promise that the next post will be about creating accounts using OpenID. However, that is a personal project, and I simply haven't had time to work on it much in the last few weeks.
I did have the opportunity to work with the in_place_editing plugin for Rails, and thought I would post about some of the tweaks that I had to make to get it working the way that I wanted. For those that have used in_place_editing with Rails in the past, please note that as of Rails 2.0 this functionality was removed from the Rails core and made into a plugin instead.
The Challenge
First of all, the project involves users of a website submitting information via a secure form using the https protocol. I wanted the user to be able to review the information that they had submitted, and give them an opportunity to make minor edits before moving on to a checkout process. Naturally, I thought that in_place_editing would be a great option and since it is extremely easy to do in Rails, I decided to go that route.
I did, however, run into a little snag with getting the plugin to do a couple of things. The first is that I needed the model validations to still work. The second is that I needed the in-place editing to be able to work with an SSL certificate, which it does not do out-of-the-box. Luckily I managed to figure out both problems, and here is how.
Setting Up
The first thing we need to do is to be sure and have all the plugins that we are going to be using. In your Rails app, type the following commands to install the SSL Requirement plugin:
script/plugin install git://github.com/rails/ssl_requirement.git
script/plugin install git://github.com/rails/in_place_editing.git
In Place Editing
With that in place, let's take a look at a simple controller example for putting these together. In the following example controller, we are using a new, create and show actions to make our example. Again, the user will be submitting information (new), a record will be created (create) and then they will be able to review the information that they submitted (show).
class ExamplesController < ApplicationController
ssl_required :new, :create, :show
in_place_edit_for :name
in_place_edit_for :email
in_place_edit_for :phone
def new
@example = Example.new
end
def create
@example = Example.new(params[:example])
success = @example and @ example.save
if success and @example.errors.empty?
flash[:notice] = "Example submitted successfuly."
redirect_to example_path(@example)
else
render :action => :new
end
end
def show
@example = Example.find(params[:id])
end
end
As you can see, this controller is pretty standard. The only thing that is different from a normal controller are the declarations at the top. The first is for the ssl_requirement plugin, which states that the new, create and show actions all must be secure. It's important to note that this plugin works by re-directing to the secure action if you attempt to get at it with http, which causes posted variables to be lost. This causes problems in many place, most notably with the in_place_editing plugin.
After the SSL declarations, you can see that I have decided to add the methods for in_place_editing to the controller for the name, email and phone fields. So far, nothing out of the ordinary. If you have used in-place editing before, you know that all we need now is a simple method in our view file that will handle all the in-place editing magic for us:
<%= in_place_editor_field :example, 'name' %>
But, as you may already now, this will not respect the validation rules for your model. In other words, if we have a validates_presence_of :name rule in our example model, and then we delete the name field using the in-place editing, it will remove the name and there will not be a warning. This is not good, since validations are there for a reason. Wouldn't it be nice if we could give our users the ability to edit in place, but also respect the validation rules? Well, you can.
I struggled a bit to get this solution to work in all environments, and in the end just ended up adding the in_place_edit_with_validation_for method to the lib/in_place_editing.rb file of the plugin. That seemed to solve my issues. Then, all I needed to do was change the controller so that my in-place editing declarations look like:
in_place_edit_with_validation_for :name
in_place_edit_with_validation_for :email
in_place_edit_with_validation_for :phone
This handy new method tweaks the in-place editing so that it will check the model validations first, and respond accordingly. For example, we can put a regular expression validation in to make sure that the email address or phone number are valid, or make sure that there is always a value in the name field.
In our view, we need to make one small adjustment:
<%= in_place_editor_field :example, 'name', {}, :script => true %>
The :script => true option tells the in-place editor fields that they need to evaluate any scripts that come back as a response. Now, when we make changes using the in-place editing fields, our validations will be respected. If the submitted data is not valid for that model, a JavaScript alert will appear and the value will not be saved. Very nice, but we still have a problem.
When there is no value for the field, if that is a possibility, there is nothing to click on to invoke the in_place_editing fields. This is where another handy option comes into play called :external_control. This option lets us specify the id of a page element that, when clicked, will bring up the editing fields. Keep in mind that we only want this to happen if the field is empty, since that is the only time it will be needed. Make the following change to our view file:
<% if @example.name.blank? %><a id="edit_name">Edit</a><% end -%>
<%= in_place_editor_field :example, 'name', {}, {:script => true, :external_control => 'edit_name'} %>
Now, if there is no name present, the user will be presented with an Edit link that will bring up the editing fields. Not too bad, we've now overcome a couple of usability issues that were making our process a bit unreliable.
Making It Work Securely
The only thing that is stopping us from deploying this process is that when you test this with an SSL in place, you will notice that the in_place_editing functionality shoot requests using the http protocol. This is fine for most cases, but we want our process to be secure, so we need to make sure that those methods are included in our ssl_requirement declarations, making it look like the following:
ssl_required :new, :create, :show, :set_example_name, :set_example_email, :set_example_phone
As you can see, the in_place_editing plugin adds actions to your controller like set_example_name, which it uses to send the changes that the user is making to. By making sure that these actions only respond with the https protocol, we are half-way there. However, if you test your application with an SSL certificate in place, you will notice that it does not work.
This is because the plugin is still attempting to send the requests using the http protocol, and then the ssl_requirement plugin is redirecting them to the https protocol, which messes things up for us. What we need is a way to have those requests go using the https protocol in the first place. In order to achieve that, I had to make a small change to the in_pace_editing plugin. Open up lib/in_place_macros_helper.rb and look for the in_place_editor_field method. You will find the following line of code:
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
Which you will want to change to look like this:
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id, :protocol => 'https', :only_path => false })
This simply tells Rails to use the https protocol when constructing the URLs for the in-place editing, and to also include the full path rather than a relative path. You should now be able to successfully use in_place_editing with an SSL certificate so that your data is secure.
Hopefully this helps out someone that is having the same problem that I was having. In-place editing is a great tool that helps to create intuitive and simple user interfaces, and being able to use it for secure applications is something that might come in handy to some developers out there.

Connect with us