Archive

Posts Tagged ‘Ruby’

RSpec’ing with Time.now

November 5th, 2008

I’m currently writing some RSpec tests that use Time.now.

I want my model to calculate a duration and store the future time in the database. I’ve already specced the calculation of the duration, but I also want to spec that everything gets saved correctly. Here’s my first spec:

it "should do stuff" do
  m = Model.create()
  m.expires_at.should eql(Time.now + some_value)
end

This fails.

It fails because Time.now is quite accurate and some milliseconds have passed between the two calls.

So how do you test this kind of behaviour? Get out your gloves, because we’re going to start stubbing!

What you need to do is stub out Time#now to return a constant value within this test. This way, both calls will use the same Time.now value and thus yield the same result. This in turn makes your test pass (if the saving goes well, of course).

it "should do stuff" do
  @time_now = Time.parse("Feb 24 1981")
  Time.stub!(:now).and_return(@time_now)
 
  m = Model.creat()
  m.expires_at.should eql(Time.now + some_value)
end

General , , , ,

BaseApp: a quick start for your Rails App

September 28th, 2008

For the impatient: http://github.com/ariejan/baseapp

Got issues? Feature requests or patches? http://baseapp.lighthouseapp.com/

Every Rails developer has at least once developed an application that needed user authentication and some basic UI features like tabs and a sidebar. Ask yourself now: “how often have you installed and extended the restful_authentication plugin?”.

Yes, I have done it quite a few times and everytime I find myself writing the same code over and over again. User login, password reset, ‘forgot password’ functionality. I’ve build the same basic UI over and over again. Added administrator users and roles.

Are you tired of doing the same old things over and over again? I was! So, I created BaseApp.

BaseApp is a Ruby on Rails application which contains a lot of code you want in your project by default. To give you an idea of what is does out of the box:

  • User Authentication including password recovery, account activation and account suspensio.
  • Admin interface where the admin user can easily manage users and tweak app settings
  • Default CSS-based UI with tabs and a sidebar. Very acceptable by default and easy to customise.

BaseApp is currently based on Rails 2.1.1. And although it’s a pretty complete package and ready to be used for your next project, it still needs a bit of work. Check out the README for features that should be in BaseApp.

Of course, BaseApp is open source so fork a copy at the GitHub and send me those patches (of pull requests)!

There are tons of feaures that can be included into BaseApp, so the next big thing is to include some sort of configuration that allows you to disable/enable certain BaseApp features.

So, go right ahead! Use it! Fork it! Send me those pull requests!

General , , ,

Skinny Controllers and Overweight Models

August 17th, 2008

All Rails developers know the slogan “Skinny Controllers, Fat Models” and I heartily agree with it. Every conference you go to, you hear it. But there’s a problem! My Fat models got overweight!

What happened? By stuffing all applications logic in the Models, they become fat, very fat. Although this is supposed to be a good thing, I don’t like it. My models get so fat that it takes me forever to scroll through it and find the method I’m working on. There must be a better way!

Well, yes there is: create modules! Normally you’d write a module to reuse your code in different places, but there’s no rule that says you may not use a module only once.

So, I package all related code (e.g. Authentication, state management, managing associated objects, etc) into different modules and place them in the /lib directory. Let’s say you have a a bunch of methods to handle keep a counter on your User model

Class User < ActiveRecord::Base
  attr_accessor :counter
 
  def up
    counter += 1
  end
 
  def down
    counter -= 1
  end
 
  def reset
    counter = 0
  end
end

You could create a new file lib/counter.rb and include that module in your User model.

Class User < ActiveRecord::Base
  attr_accessor :counter
  include Counter
end
module Counter
  def up
    counter += 1
  end
 
  def down
    counter -= 1
  end
 
  def reset
    counter = 0
  end
end

As you can see, this keeps your fat User model clean and makes it easier for you to find code that applies to a certain function.

Blog , , ,

ActiveRecord Read Only Model

August 17th, 2008

ActiveRecord is great in providing CRUD for your data models. In some cases, however, it’s necessary to prevent write access to these models. The data may be provided by an external source and should only be used as a reference in your application, for example.

I’m going to show you how you can easily mark a Model as read only all the time. In this example I have a Item model like this:

class Item < ActiveRecord::Base
end

ActiveRecord::Base provides two methods that may be of interest here:

def readonly!
  @readonly = true
end
 
def readonly?
  defined?(@readonly) && @readonly == true
end

The first method sets the record to read only. This is great, but we don’t want to set the read only property every time we load a model. The second, readonly?, return true if the object is read only or false if it isn’t.

So, if we return true on the readonly? method, our object is marked as read only. Great!

class Item < ActiveRecord::Base
  def readonly?
    true
  end
end

That is all! All Item objects are now marked as read only all the time. If you try to write to the model, you’ll receive an error.

item = Item.find(:first)
item.update_attributes(:name => 'Some item name')
=> ActiveRecord::RecordReadOnly

Blog, General , , ,

Zoek jij ‘n uitdagende baan??

June 6th, 2008

First off, sorry to all the English-reading people, but this post is intended for my Dutch audience.

Even wat updates voor mijn Nederlandstalige publiek. Ik heb wat nieuwtjes voor jullie, dus lees snel verder!

Allereerst wil ik even melden dat ik a.s. dinsdag (10 juni) te vinden zal zijn op RubyEnRails 2008. Dus, ben jij er ook, laat ‘t me even weten! Laat even ‘n commentaartje achter, of nog beter: bel/sms me even dinsdag op 06-17103624, dan spreken we elkaar zeker!

Dan nog even een oproep voor alle Nederlandse Ruby, Java en PHP developers. Als jij echt coole webapps kan (of wilt gaan) maken, dan moeten we praten! Kabisa zoekt namelijk op korte termijn Ruby en Java developers. Ik kan er zelf over meepraten: bij Kabisa werken is leuk, afwisselend en het biedt je alles wat je van een goede ICT job mag verwachten.

Spreek me dinsdag even aan (zie m’n nummer hierboven) als je meer wilt weten over werken bij Kabisa! Of maak gewoon voor de lol even ‘n praatje!

Ben jij ‘n Ruby, Java of PHP developer - en ben jij toe aan een leuke ICT baan? Neem dan ‘ns contact met me op, want ik ben op zoek naar collega’s!

Tot dinsdag bij Ruby en Rails 2008!!!

General , , , , ,

Debian Etch: RMagick LoadError

April 10th, 2008

If you’re on Debian Etch, you may encounter the following error

libMagickWand.so.1: cannot open shared object file: No such file or directory - /usr/lib/ruby/gems/1.8/gems/rmagick-2.3.0/lib/RMagick2.so

This basically means that the libMagickWand.so.1 file cannot be found. However, it is available on your system. All you need to do to fix it, is tell your box to look in the right place for the file.

To fix this issue once and for all, open up /etc/ld.so.conf.d/whatever_file_is_here. The whatever_file_is_here is named after the kernel you have installed.

In this file, add the following line at the bottom

/usr/local/lib

Save the file and next run the ‘ldconfig’ command. This will reread the configuration file you just edited. Now, restart your Rails app and you’ll notice the error is gone and all is good again.

ldconfig

This change will be kept after you reboot, so you won’t encounter this error any time soon again.

General , , , , , , ,

Rails Snippet: Caching expensive calls

April 9th, 2008

In Rails, from time to time, you may encounter you have a method you call several times, but which returns always the same result. For example, have the following:

class Person < ActiveRecord::Base
  has_many :articles
 
  def get_approved_articles
    self.articles.find(:all, :conditions => {:approved => true}, :order => 'approved_on DESC')
  end
end

A query is fired every time you call Person#get_approved_articles. To cache the result of the query during this request, just add a bit of magic

class Person < ActiveRecord::Base
  has_many :articles
 
  def get_approved_articles
    @approved_articles ||= self.articles.find(:all, :conditions => {:approved => true}, :order => 'approved_on DESC')
  end
end

This will return the @approved_articles value if it exists. If it doesn’t, which is the first time you access the method, the query is run and stored in @approved_articles for later use.

Note: I know it’s much easier to define this kind of behaviour, but it’s just an illustration.

class Person < ActiveRecord::Base
  has_many :articles
  has_many :approved_articles, :class_name => "Article", :conditions => {:approved => true}, :order => 'approved_on DESC'
end

General , , ,

Ruby on Rails plugin: Throttler

February 7th, 2008

For those of you who have missed it: I’ve released a plugin yesterday that allows you to throttle your Rails app.

Read the original announcement and installation/usage instructions
Read how you can put Throttler to good use in your app

Blog , , , , ,

RSpec 1.1 Released: Now Supports Rails 2.0

December 17th, 2007

The team behind RSpec, a Behavior-Driven Development based “testing” library, have announced the release of RSpec 1.1.0. This will be of particular interest to Rails 2.0 developers as support has now been added, along with interoperability with Test::Unit. RSpec 1.1 also includes a Rails tool called “RailsStory” that allows you write “user stories” that can be tested out on the fly.

Blog, Linkage , , , , , , ,

Rails: calculated column caching

December 6th, 2007

Sometimes you’re working on a Rails project and you think: “hey! This should be easy!”. Well, most of the time it is. I’m working on a project that allows people to rate objects (what they really are doesn’t matter at all).

I’m using the acts_as_rateable plugin which creates an extra database table containing all ratings. I also have a table with my objects. Using the plugin I’m now able to do the following:

obj = Object.find(:first)
obj.add_rating Rating.new(:rating => 4)
obj.add_rating Rating.new(:rating => 5)
obj.rating
=> 4.5

This works all perfectly, until you want to sort objects by rating. You could construct a huge SQL query to join the two tables, but that’s not really efficient, especially when your database gets bigger.

The solution is very easy and even more elegant. Use a cache! For this, you’ll first have to add a new field to the objects table. Do this in a migration:

add_column :objects, :rating_cache, :float

Now, in the Object model, add the following method:

def rate_with(rating)
  add_rating(rating)
  update_attribute('rating_cache', self.rating)
end

You’ll need to change your controller from using #add_rating to #rate_with. The syntax is exactly the same. Now, when you add a rating, we also store the average rating in the rating_cache column.

To get back to the sorting problem, you can now use the rating_cache column to sort Objects.

Object.find(:all, :order => 'rating_cache DESC')

Of course, you can use this trick on all sorts of relations. Have fun with it.

Blog, Features , , , , , , , ,