RSpec’ing with Time.now
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
BaseApp: a quick start for your Rails App
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!
Skinny Controllers and Overweight Models
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.
ActiveRecord Read Only Model
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
baan banen Rails Ruby rubyenrails RubyOnRails
by Ariejan de Vroom
leave a comment
Zoek jij ‘n uitdagende baan??
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!!!
imagemagick libMagickWand magick Rails rmagick Ruby Ruby on Rails RubyOnRails
by Ariejan de Vroom
leave a comment
Debian Etch: RMagick LoadError
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.
Rails Snippet: Caching expensive calls
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
Plugins Rails Ruby Ruby on Rails Throttler Throttling
by Ariejan de Vroom
leave a comment
Ruby on Rails plugin: Throttler
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
bdd Rails rspec Ruby Ruby on Rails RubyOnRails tdd testing
by Link Pilot
leave a comment
RSpec 1.1 Released: Now Supports Rails 2.0
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.
cache find join order Rails Ruby Ruby on Rails select sort
by Ariejan de Vroom
2 comments
Rails: calculated column caching
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.
