http://www.developer.com/

Back to article

Techniques to Pass and Read URL Parameters Using Rails


February 19, 2009

Introduction

"Ruby on Rails" (RoR) has always been quite handy as far as passing and reading URL parameters are concerned. However, there is a common perception that the URL parameters get passed as a simple string. This article discusses the RoR representation of URL parameters and how users can use this information to parse and use URL parameters effectively and efficiently.

Rails and URL Parameters

First, you need to learn about parameter passing in Rails with an example. Listing 1 shows a URL with a set of parameters being sent to the recipient server that has been hosted on the local machine.

Listing 1.

http://localhost/users/create?user[first_name]=dhanabal&user
   [last_name]=arumugam&arr []=1&arr []=7&arr []=3&id=15

As the users of Rails may already be aware, "users" stands for the controller class "UsersController" and "create" is the method within the "UserController" class.

"user[first_name]=dhanabal&user[last_name]=arumugam&arr
   []=1&arr []=7&arr []=3&id=15"

are the parameters to the method "create".

Typically, the URL parameters are sent as a set of simple name value pairs, as in the example "name=tom&age=21". In the Rails convention, also, parameters are sent as a set of name value pairs. However, there is a slight difference. Rails eases parameter passing and makes the object relational mapping by qualifying the parameters with the model object and the model attributes. In the example above, the term "user" maps to the model (table) user and "first_name" maps to the attribute "first_name" of the model class (column first_name of the table user).

Arrays

"Rails" allows the users to send an array of values as parameters as well. In Listing 1, the values "arr []=1&arr []=7&arr []=3" represent an array. The name of the array is "arr" and the values at the indices 0, 1, and 2 respectively.

Rails Parameters and HashMaps

All the URL parameters are represented internally in Rails as a set of HashMaps. This is contrary to the usual belief that URL parameters are sent as nothing but a simple string (although Rails does not inhibit users from reading the parameters as simple strings). In the example cited above, the internal mapping of the parameters as a HashMap will be as depicted in Figure 1.

Figure 1.

It can be easily seen from Figure 1 that Rails maps the main URL parameters into a simple HashMap containing a key and a value. For the parameter "id" that is passed, the value 15 is kept directly in the value section. However, for complex parameters such as "user", the value field holds the address of the HashMap where the actual values are kept. The array "arr" is also treated similarly to the parameter "user".

Rails Code and Parameter HashMap

A careful investigation of the URL parameters and the way Rails structures those parameters helps the user pass those parameters easily, as shown in the snippet in Listing 2.

Listing 2.

 1 params.each {|key,value|
 2    puts "key=>#{key}"
 3    if value.class.to_s != "String"
 4       value.each {|k,v|
 5       puts "Hash inner key=>#{k}"
 6       puts "Hash inner value=>#{v}"
 7
 8    elseif value.class.to_s != "array"
 9       puts "Hash inner key=>#{key}"
10       puts "Hash inner value=>#{value}"
11    else
12       puts "single Hash=>#{value}"
13    end

In the example cited in Listing 2, Line 1 is the place where the URL parameters are actually parsed. In Rails, the URL parameters are stored in the default object "params". "params" has a "Hash data structure". If the "key" happens to be a simple value, it gets printed out as shown in Line 12. In this particular case, the field "key" might hold the values "user", "id", and "arr" respectively. If the parameter is of type array (in this case "arr" parameter), again the values are printed out. However, in the case of a complex type like "user", the values are further parsed to extract the subkeys and their respective values. In the case of "user", the subkey "k" will hold the values "first_name" and "last_name" respectively, in subsequent iterations.

Rails Parameters and Net::Http

Rails has some peculiar characteristics when one tries to send parameters using Net:HTTP. Take the case of Uri shown in the Listing 3 when using Net::HTTP with the Uri shown in Listing 3 with 'Accept' => 'text/xml' or 'Accept' => 'image/jpeg' and so forth because the second argument will result in an a additional parameter "A"=>"" in the params(Hash) for 'post' and 'put' methods.

Listing 3.

response = http.post('/controller/action?id=1&type=user,
   'Accept' => 'text/xml')


This results in an internal construction like this: params = {"id"=>1,"type"=>"user","A"=>""} => {"id"=>1,"type"=>"user","A"=>""}

This may cause problems when parameters are used as a whole in any assignment or operation. Take the case of the example shown in Listing 4 where an attempt is made to find a tuple based on the parameters (condition) passed in Listing 3. Although the user is passing only two parameters—id and type—in the snippet shown in Listing 3, an additional parameter appears in the implicit parameter HashArray "params" by name "A". The existence of this parameter "A" may cause the lookup (query) to fail.

Listing 4.

model_obj = Model.find(:all,:conditions=>params)

The following error occurs:
ActiveRecord::StatementInvalid:
   Mysql::Error: Unknown column 'model.A' in 'where clause':
   SELECT * FROM model WHERE (model.`A` = ''
   AND model.`id` = 1 AND model.`type` = 'user')

The solution is to use an empty argument in post or put the request as shown in Listing 5.

Listing 5.

response = http.post('/controller/action?id=1&type=user,'')

or by explicitly removing the parameter "A".

Passing a parameter with name "A" in

http.post('/controller/action?A=abcd&id=1&type=user,'Accept'
   => 'text/xml')

will overwrite the empty "A"=>"").

NOTE
The above problem does not occur when using CURL.

Rails Parameters and Functional Tests

Typically, data is passed from the functional (or unit) tests in Rails, as shown in Listing 6. In Listing 6, the user passed three parameters, "id", "name", and "type", respectively.

post :create, :id => 300, :name => "ravi", :type => 1

The common notion about Rails is that the parameters are passed as a simple string. However, this is not the case as discussed in the section titled "Rails Parameters and HashMaps." Rails parameters get captured in a HashMap; that holds true in the case of functional or unit tests as well. In the case of regular URL parameters like the ones passed from a browser or Net::Http, interpretation of the parameters as a simple string might still hold good and one may be able to parse the parameters and do validations with simple string search, regular expressions, or sophisticated tools like Treetop. The parsing of the parameters as strings works very well too with a tool like Treetop. However, on the flip side, once the user has done all the unit testing of the controller and jumps into writing the Rails Functional Tests, the user realizes that the parsing of the URL parameters as a simple string doesn't hold good any longer. This is so because paramters passed through controller tests, as shown in Listing 6, get passed as a "pair" in a HashMap and not as a string. It is always wise to read the parameters shown in Listing 2 as parameters.

Summary

This particular article discussed a set of wrong notions that users have about Rails parameters and how one needs to deal with them in real life.

To Contact the Authors

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date