Archive

Archive for January, 2010

complex associations in rails activerecord

January 30th, 2010 1 comment

During playing with CanCan from ryanb show in last railscasts I did planned some models and find me in quite strange situation – I knew the connection, but I could not easily get the result.

My models look something like:

class User < ActiveRecord::Base
  has_many :assigments
  has_many :groups, :through => :assigments
end
class Assigment < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end
class Group < ActiveRecord::Base
  has_many :assigments
  has_many :users, :through => :assigments
  has_many :responsibilities
  has_many :roles, :through => :responsibilities
end
class Responsibility < ActiveRecord::Base
  belongs_to :role
  belongs_to :group
end
class Role < ActiveRecord::Base
  has_many :responsibilities
  has_many :groups, :through => :responsibilities
end

So my first try was to get roles through groups manually:

User.first.groups.map{|g| 
  g.roles
}.flatten.map{|r|
  r.name.to_sym
}.uniq

But this solution is waste of resources, for system with a lot of possible roles it might be very inefficient, it takes first all groups of user and then ittereting through them gets all it’s roles. it will generate a lot of database queries.
So after some searching I got following code:

User.first.roles.map{|r|r.name.to_sym}.uniq

To make it working I used one quite nice feature of Active record – :finder_sql – so in user.rb I have added following line:

has_many :roles, :finder_sql => 'SELECT r.* FROM users u LEFT JOIN assigments a ON u.id=a.user_id LEFT JOIN responsibilities res ON a.group_id=res.group_id LEFT JOIN roles r ON res.role_id=r.id'

This makes it possible to get all the roles for a user just in one call, everything calculated on database side. Unfortunately there is one down side of this – the SQL code might be not portable to other databases, so use it only when You are sure You will stick to one database.
There is also other way around, it allows to get the same data in two sql calls, without using SQL queries, just on pure ActiveRecord usage:

Responsibility.find_all_by_group_id(
  u.assigments(:select=>'assigments.group_id').map{|a|a.group_id},
  :select=>'roles.name',
  :joins=>:role
).map{|r| r.name.to_sym }.uniq

There are many ways to archive the same goal, knowing them is only an part – knowing how they work, makes us aware how to chose the path.

on 2009-01-31 13:15 added:
Another way going out of the Group class:

Group.find(
  :all,
  :select=>'roles.name',
  :joins=>[:users,:roles],
  :conditions=>{:users=>{:id=>1}}
).map{|g|g.name.to_sym}.uniq

Did You liked this post or maybe not, vote on it at dzone.

exception iteration in ruby

January 29th, 2010 Comments off

There are many ways to iterate in ruby, many methods, but going back to assembler roots of mine I found another one:

>> a=[1,2,3]
=> [1, 2, 3]
>> begin puts a.shift; raise unless a.blank?; rescue; retry; end
1
2
3
=> nil

This code is only prof of concept, it should not be used for iterations, by example if it would be used without prior initialization of “a” variable it would be an endless loop as on exception “NameError” it will retry in endless loop.

There are other places where this could be used, but even possible retry should not be used without some kind of endless loop protection like:

>> i=10; begin puts IO.read('some.file'); rescue; if i>0 then puts "#{i} ..."; i-=3; sleep 1; retry; end; end
10 ...
9 ...
8 ...
7 ...
some file created
=> nil

In this example a file was created during run of the command – on second console by just running “echo some file created > some.file”.

There are other ways to do the same, maybe not so cool like that above but probably most of developers will prefer something like:

>> 10.downto(1){|i| begin puts IO.read('some.file'); break; rescue; puts "#{i} ..."; sleep 3; end }
10 ...
9 ...
8 ...
some file created
=> nil

Did You liked this post or maybe not, vote on it at dzone.

Categories: Development Tags: , , ,

string to class in ruby on rails

January 26th, 2010 Comments off

There are few ways to have a class from a string, most know are:

  • Kernel.const_get(‘User’)
  • eval(‘User’)
  • ‘User’.constantize

The order in which I have named them is important – a bit important, Kernel.const_get is 10 times faster then constantize and 5 times faster then eval.

The reason of such speed for Kernel.const_get is from it has to maintain list of all constants in the application, tests show it may even behave faster then storing names and Class mapping in a hash.

The difference is not big on simple calls to create just one or two classes, but on heavy loaded systems this might give some more percents of the hardware.

Did You liked this post or maybe not, vote on it at dzone.

openSUSE rescue CD get yast running

January 9th, 2010 Comments off

As everybody knows Windows does great job during installation – makes computer start only IT :)

There are many ways to make it working again with linux, my steps were:

  • boot computer from some opensuse CD (should be possible with any)
  • select rescue bootmenu entry
  • if asked for user enter “root”
  • enter “fdisk -l” to see yours linux device – for me it was /dev/sdb3 – first one with Id 83
  • prepare access to your system

    mount /dev/sdb3 /mnt
    mount -o bind /dev/ /mnt/dev/
    mount -o bind /proc/ /mnt/proc
    chroot /mnt/ /bin/bash
    mount /sys

  • now enter “yast2 bootloader”
  • select Other (Alt+e) -> Propose new configuration (Alt+p), OK (Alt+o)
  • exit chroot with “exit” and restart computer with “reboot”

steps up to chroot are know and are quite similar to installation of gentoo system, but “mount /sys” is something new and is required to make “yast2 bootloader” or “yast2 disk” working in chroot environment.

Categories: Linux Tags: , , , , ,

download all railscasts

January 6th, 2010 Comments off

I think most of You knows http://railscasts.com/, probably you were using one of the scripts:

http://maximilianoguzman.com.ar/2009/05/26/railscasts-downloader
http://gakshay.blogspot.com/2009/03/railscasts-crawler-download-all.html

And probably there is more such scripts, but I would propose another solution, on line of bash code:

wget -q -O – http://feeds.feedburner.com/railscasts | awk -F \” ‘/media:content/ {print $4}’ | head -n 2 | wget -i – -c

Update 2010-01-09: “head -n 2” is for controling ammount, change it to something bigger if You need more then 2.