Thursday, November 19, 2009

Open up your pages for outside requests

My previous post helps to POST a form to another site. Rails (in the current version anyway!) includes a basic solution to make XSS (Cross Site Scripting) or CSRF (Cross-Site Request Forgery) harder. In the application controller the protect_from_forgery method checks requests via a token. Of course from the outside you're not able to provide the right token...

Turning this protection off (with care and alternative protection measures, I suggest!) on specific actions is possible. This is an example.


protect_from_forgery :except => [:process_payment]


<

Rails redirect_to with POST?

During the development of a small application we needed to redirect to an other website and POST some values to that specific url. That was harder than expected.

The first idea in my way to solve this was to use redirect_to. But the requirement is to use the http POST method. Redirect_to can't POST! It also seems that the http specification doesn't support POST redirects, so that's a good reason.

But how to get it done then? After some googling I found a way to "redirect" via a post. The browser has to do it! Put a form on a page, the fields/parameters to be posted in it and let it submit on load. To hide the visibility of fields I made them hidden (that's common, I'd say).

The simplified code of the page:


<h2>Back to the shop....</h2>

<script type="text/javascript">
Event.observe(window, 'load', function() {
document.forms[0].submit();
});
</script>


<% form_tag @merchant.redirect_url, :method => :post do %>

<%= hidden_field_tag :paid, @paid %>
<%= hidden_field_tag :order_id, @payment.merchant_order_id %>

<% end %>


I'm not a Javascript developer yet, but I think this code is neat and straightforward. When the window/body is loaded it fires a submit on the first form. Normally the one and only one available.

Unfortunately I wasn't able to find this solution easily. So I hope it helps you!

<

Sunday, November 8, 2009

Product.find: dynamic condition use in active record find

I've had some fighting time with active record. Although it's really not hard, I had some trouble getting it done nicely in Ruby/Rails. I'm learning... I've seen more people having this type of problem while googling, so I wanted to share my little experience.

I needed a list of products which could be filtered by two ways. 1. I needed to filter "All" or the "Not yet offered" products (via a select box), and 2. additionally I wanted to provide a "contains" edit field.

The expected result was that the list would be regenerated when something changed. In the case of the edit field, I wait 0,6 seconds before starting the request. The combination provides an intuitive filtering mechanism.

The following code snippet is my current basic solution for this:

conditions = []

# Contains field has value
conditions << "(title LIKE '%#{params[:prod_search]}%' OR code LIKE #{params[:prod_search]}%')" unless params[:prod_search].empty?

# Non-offered selected
conditions << "id NOT IN (SELECT distinct(product_id) from shop_offers)" if params[:prod_filter] == "non_offered"

@products_avail = Product.find(:all, :conditions => conditions.join(" AND ")

Wednesday, November 4, 2009

observe_field in rails (2.3.4)

The last two days I was busy finding out why my field observer didn't work. I followed the book (AWDR3) but somehow it failed.

I used a controller on a sublevel (pim/offers) and that made things a bit harder. I wanted to add an action which wasn't found. I had to add it to the routes.rb (via the :collection argument!). The default expected method is :post, so take care of that.

For the observer I needed, I had to provide the year and month fields. The book gave a option of concatenating some things. This is by the way where I lost a lot of time, the books "encodeURIComponent(value)" part was not copied exactly by me I started with an Uppercase E. After more then a day I found it, yeah!
After I found that nasty error I was looking for a way to pass multiple parameters. I found this: "Form.serializeElements($('month_date', 'year_date'))". You provide a comma seperated list of id's and their values are serialized for you.

Below the copied part of a working piece of a rails based field observer.


<%= observe_field :month_date, :update => "list",
:before => "Element.show('spinner')",
:complete => "Element.hide('spinner')",
:url => {:action => :month_offers, :only_path => false},
:with => "Form.serializeElements($('month_date', 'year_date'))"
%>


Hope I helped some people with this post. Good luck!

<