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.

Design patterns or SOLID design

I’m using Rails and Ruby over 3 years now, but with each month I know there is a lot new things to learn. Recently I’m trying to learn much more about design patterns, and how to use them.

If You are new to design patterns and want to know why it is worth to learn this stuff I recommend You watching this session from this year’s GORUCO (GOtham RUby COnference):

MySQL, collations and Rails – or server, database, connection – that is not all

Last few weeks were somewhat crazy time. But I’m seeing finish line not so far away and I hope to find more time for blogging in upcoming weeks. At least I have few things to share, which could be interesting for other Rails developers.

So let’s start with MySQL and it’s collations. I have wrote about them few months ago. Few days ago, I had finally installed Linux on my notebook (last two months I was running Linux from USB drive). And it seems that there were some minor changes in my environment. I was working on CARtoteka, and when I was running migrations they were stopped in half with error like :

Mysql::Error: Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and
(utf8_general_ci,COERCIBLE) for operation '=': 
SELECT count(DISTINCT  companies`.id) AS count_all FROM `companies`  
LEFT OUTER JOIN `addresses` ON  addresses.company_id = companies.id 
WHERE (city = 'Pozna?' and moderated)

What the heck? I have doublechecked I have set connection collation and other – utf_general_ci as I could expect.

After some digging I have found what was a cause.

Continue reading

It’s time to say goodbye!

Well, last 6 years I was using Windows as a main OS for my home/developer computers. Partly it was not mine decision, since my employers required me to run Windows. But, since I’ve jumped out of regular 9to5 two years ago this was not true anymore. It was convenient for me to use Windows, and during first year I was developing bigger application which was meant to be run on Windows. And it was Ruby application, so it was wise to develop on Windows. Almost year ago I have finished this assignment and Windows was still on my notebook.

Last weeks finally made me to change that. As You probably know I have converted to TDD development style. Having tests covering work already done (and already squeezed bugs) is very handy, but when running even partial tests take to much time – it decreases productivity. Waiting for test to complete (to know if they are green and I can move to another task or I have introduced some other bugs ;) and need to work on code) causes my mind do wander to other topics. You know, Internet has a lot distractions to offer. I was suffering in more or less silence – my laptop is old, I thought (and it is old indeed) and changing OS won’t help much. But then I have read this blog post about some comparisons between Ruby implementations in context of performance.

Enough! I have said to myself – I was busy last months and it was hard to find some time, and jump with replacing Windows with Linux during some work with deadlines. Quick browsing through pile of old PC parts I have accumulated through last years, and tada! Old 20 GB HDD and USB20 – IDE interface made my day (or rather all-night). After some issues with not working CDROM drive I’ve managed to install Ubuntu on this drive.

Ubuntu on my desktop have arrived

Ubuntu on my desktop have arrived. Image (c) karindalziel

Boot from USB and… I was surprised how far Linux desktop have advanced with easy of use and integration since I was last time trying. Even my backup GPRS modem was recognized and Ubuntu has configuration for my GSM operator. Cool.

Then I have installed my dev environment (of course I have not used Ruby and gems from Ubuntu’s packages, I have compiled my own) and did ultimate test.

The same notebook, HDDs of comparable speed (or rather comparable in slowness :)) ) – both 5400 RPM with small cache and the same Rails project- around 300 assertions (100 in unit tests and 200 in functionals). Time of execution of rake test: Windows XP, Ruby 1.8.6.p111 – 2:49. Ubuntu, Ruby 1.8.7-p72 own compilation – 0:29.

Bye, bye Windows…

I will stay in this setup (USB drive) for some time, and when I will be sure that something small won’t be annoying problem I’ll make final switch.

UPDATE
This post was in draft mode few days and today I can say – I’ll make switch. The only issue I had it was issue with sound. At first I thought something does not work. But it turned out I’m too old fashioned. I was using command line tools to make sure volume is set as needed, and I did something wrong since – no sound for me. I had just doubleclick speaker control in Ubuntu’s taskbar to un-mute PCM and CD and get music playing – as work in home freelancer with 3 kids working sound is a must, believe me :))