When will_paginate renders strange URLs after Rails upgrade to 3.2

To be honest I didn’t expect I will write again here at this blog. But good news I did manage to find some time to write!

Since 2011 I’m not freelancing anymore, but all the time I use Rails in Nettigo. Codebase used there has it’s roots in Rails 1.2 or even earlier release – first migrations have not timestamps at beginning of filename but numbers, this change was introduced at Rails 2.1. In other words there was a few upgrades in live of this application, and there will be some in future ;) Some upgrades were easy but sometimes You can encounter problems which looks strange and after few hours debugging You have still no idea what is going on :)

Right now I’m in upgrade process from Rails 3.1 to 3.2. It went quite smooth (or at least I think so, since I have not deployed to production environment :) it yet) with one exception – will_paginate have started generating wrong URLs. For example on main page instead of just adding page parameter it have changed path to /about?page=2&controller=Public/products. It looked strange since root was defined, links with link_to were generated for it, page was displayed, but when will_paginate called url_for, it returned garbage – redirecting to About page instead of keeping original URL.

To keep long story short (as usual best solution is to leave code, get some sleep and see at problem again with fresh mind), this diff illustrates what was a cause:

Root was served from controller in namespace (module Public), and with change in routing engine (Rails 3.2 introduced new one) capital letter did the difference.

Passing current user id to Rails models

Long time since last post, isn’t it? Well, I’m completely busy with my work in Nettigo, selling Arduinos and other nice gears :) I do not do gigs anymore, so there is a lot less reasons to write about Rails.

But I still use this framework, since my backend software is written in Rails, so here is one thing which I think can be useful for You.

Often we want to pass ID of current user to Rails model internals. Most answers are don’t do that, this is controller work to do authorization stuff, MVC is to prevent such thins, etc.

here was not related picture (as usual on this blog). Some readers were complaining it was not only unrelated but even offending. Well, maybe they were right, so, I have removed that picture.

Yeah, right, but what if we want to keep some kind of audit trail what is happening with given model? This is not authorization, just who done what.

Since in my application case audit records are created in observers simple passing user id as some additional parameter is no go for me – I don’t want to change interface just to pass user info, that breaks too much things.

So, I have chosen this approach:

def with_user user, &block
  if user.blank?
    yield
  else
    Thread.current[:user] = user.id
    yield
    Thread.current[:user] = nil
  end
end

And each operation I want to track I invoke:

with_user(current_user) do
 model.do_some_stuff
end

Thread.current acts as a hash accessible in current thread and allow us to pass some info skipping MVC isolation (You wanted that, right? :) )

In observers I can access user ID and store it with record. If it is not present, audit trails is being marked as created by System. That means probably cron job, or console action (well on console I can run code inside with_user block when needed).

This is safe as long each request is being processed in single thread and we make sure that user ID will be cleared after our operation. When ID will stay in Thread.current, next request being processed by this Rails app instance will have access to that value and wrong audit records will be created.

Code has to be updated, since any exception will occur inside block, code after yield will be skipped and user ID won’t be erased from Thread.current. Here it is, final version:

def with_user user, &block
  if user.blank?
    yield
  else
    Thread.current[:user] = user.id

    begin
      yield
    ensure
      Thread.current[:user] = nil
    end
  end
end

Code inside ensure will be executed each time – both when exception was raised and when execution was clean.

wsdl2ruby and failing SSL certificate validation

When You are about to use Ruby to connect to some SOAP-like API You will probably use SOAP4r gem. First step would be to generate client code from WSDL provided by API. If it is served via HTTPS and server has SSL certificate not signed by some common CA (like self signed certs) You will experience following error running wsdl2ruby.rb:

at depth 0 - 20: unable to get local issuer certificate
F, [2011-01-18T10:13:40.816069 #4035] 
FATAL -- app: Detected an exception. Stopping ... 
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: 
certificate verify failed (OpenSSL::SSL::SSLError)

HTTPClient (this is gem used by SOAP4r to do all HTTP communication) tries to validate SSL certificates by default. Good, this is what SSL was thought for :) HTTPClient allows to use own CA (which is better solution than to lower security, especially on production :) ), however I don’t know how to pass this via SOPA4r abstraction (ssl-config.set-trust-ca('path') in HTTPClient (UPDATE – OK I think here You can find info how to specify CA, client key and cert in SOAP client). We can tell HTTPClient not to check SSL certs via options of SOAP::RPC::Driver when using client code:

d = SomeSOAP::ClientClass.new
d.options['protocol.http.ssl_config.verify_mode'] = OpenSSL::SSL::VERIFY_NONE

This may be not very wise, so use that if You know what You are doing.

And what about wsdl2ruby.rb

Above example relates to scenario when You have generated client code, which should connect to HTTPS. But how to recover from certificate verify failed when running wsdl2ruby.rb (in other words – when generating client code and WSDL is on HTTPS server with self signed cert)?

First solution is to download WSDL and generate client code from local file. Will do the trick unless WSDL imports some additional XSDs via HTTPS URL…

This gist gave me idea how to solve that (BTW – very useful trick to debug HTTP traffic if You can not use proxy like Charles Proxy). I did checked out SOAP4r gem code and updated wsdl2ruby.rb adding at begin:

BEGIN {
  require 'rubygems'
  require 'httpclient'
  HTTPClient.class_eval do
    alias_method '__initialize__', 'initialize'
    	    
    def initialize(*args,&block)
      __initialize__(*args, &block)
    ensure
      self.ssl_config.verify_mode=OpenSSL::SSL::VERIFY_NONE
    end
  end
}

That’s it. Well, almost. I was unlucky – due to some configuration twists WSDL and other referenced resources were hosted in some dev environment with wrong network configuration. Fixing that was totally out of my reach.

What was wrong? WSDL was accessible via HTTPS. All resources imported inside were referenced via HTTPS, too. But. When accessing these HTTPS URLs (other than WSDL) from outside network (where I was) plain HTTP error message was displayed, so SOAP code could not be generated. When URL was changed to HTTP – proper XML file was available.

So, another hack to wsdl2ruby.rb:

class  HTTPClient
    alias_method '__get_content__', 'get_content'
    def get_content(uri, query = nil, extheader = {}, &block)
     uri = URI.parse(uri.to_s.gsub(/^https:/,'http:'))
     if block_given?
       __get_content__(uri, query, extheader) {block}
     else
       __get_content__(uri, query, extheader)
     end
    end
end

Now all requests to HTTPS are converted to plain HTTP. Again – do it when You understand what You are doing :)

Where Ruby brought me

When I started this blog (Apr 2006) I’ve just have discovered Ruby on Rails. During this almost five years many things have changed. And finally Ruby brought me to this place:

Massimo and Arduino Logo at Maker Faire 2010 in NYC

Massimo and Arduino Logo at Maker Faire 2010 in NYC
CC by http://www.flickr.com/photos/mattrichardson/

And now small announcement. As You can see I was publishing on this blog very seldom in recent months. And reason is very simple. Almost two years ago I have started side business (do freelancer have side businesses? :)) ) selling Arduinos in Poland (and whole Europe). After slow start, it have took off, and between my work as freelancer and running shop there were no time to write on this blog.

Recently I have made decision to stop freelancing and devote all my time to Arduino (and electronics) related topics. Will I stop writing about Ruby? I guess not. Shop is on some hosted platform, but I do run some custom RoR software as backend. I plan to migrate at some point to own e-commerce solution, so I’m not leaving Ruby world.

Posted in Uncategorized | 1 Reply

ActiveMerchant: dumping traffic between Your app and payment gateway

Just a quick note – if You need to dump all traffic between payment gateway and Your application using ActiveMerchant just add to Your development.rb:

ActiveMerchant::Billing::QuantumGateway.wiredump_device = File.open("/tmp/q.log", "a+")
ActiveMerchant::Billing::QuantumGateway.wiredump_device.sync = true

Just remember – it will work ONLY You have one application instance, since it uses simple file handle to write all traffic. And replace QunatumGateway with gateway used by You.

Posted in Uncategorized | 1 Reply

ArgumentError: marshal data too short when loading session data

I was stuck for a while when application maintained by me have started to throw ArgumentError: marshal data too short errors in random places When user have encountered that problem then it was unable to use application at all.

Marshal

Marshal CC http://www.flickr.com/photos/qmnonic/

Logs were showing that it happens when Rails was trying to create session object. Session store was in ActiveRecord and sessions table was not corrupted.

After watching that for a while it have shown that places in code were this exception was thrown were random but there was pattern. Page visited before was common in each case.

It have turned out that application was storing in session whole ActiveRecord object. Like:

 session[:some_info] = @variable

And later we were trying to use that way:

@variable = Model.find session[:some_info]

Due to Rails magic AcitiveRecord’s find when provided with AR object will return that object (of course if is the same model). Code was working well (maybe not very effectively since You should avoid storing large objects in session) until object stored that way started to grow. Application was collecting some data and amount of data stored have grown to that point that after Marshal.dump size of string was more than 64 kB. And this is default size of text field used to store session data in MySQL.

When You try to store too much data in text field in MySQL, excessive data is being truncated, so Marshal.load throws that exception.

To have that error solved is enough to store just object id in session (session[:some_info] = @variable.id).