Archive for the ‘Uncategorized’ Category

Small Rspec revelations, RJS

Friday, February 29th, 2008

The closing scene of this series. Specing RJS templates. Again a topic which is not on the forefront of the RSpec community.  And again a blog post saved the day. It took me a little bit of time for figure out what kind of RJS type the response object should expect. To illustrate an example RSpec:


response.should have_rjs(:chained_replace_html, "some_id") do
response.should have_text(/some random text/)
end

The response object should have an RJS object but what type? According to the assert_select_rjs method, which the have_rjs method wraps, there are only a hand full of these types but apparently not all of them are listed. As I found out when my inline RJS:


render :update do |page|
page[update_div].update(text)
end

None of the mentioned types responded correctly. But I noticed that the blog post mentioned earlier used an other type. Browsing through the source code of assert_select_rjs I found my missing type: chained_replace_html. Hooray! Everything worked fine and dandy from there on.

One last hiccup was that code in the block passed to the have_rjs method does not scope by default the response.should directive, so this is wrong:


response.should have_rjs(:chained_replace_html, "some_id") do
have_text(/some random text/)
end

Obviously.

Small Rspec revelations, Modules

Wednesday, February 27th, 2008

It looks like this is going to be a three parter. For our current project we wanted to abstract some of the functionality which appeared in our models to a module. As we are taking TDD more seriously nowadays I set out to write a spec for the module first. Right. For a normal spec you need an instance of the object you are specing. But by their very nature models can not be instantiated! So how to write a spec then? Here you go:


before(:each) do
klass = Class.new { include FicklePricing }
@foo = klass.new
@foo.stub!(:id).and_return(1)
end

In this case we have created a class with does nothing but to include our module. And this almost empty class can by instantiated and used in this specs. Trivial example:


it "should respond_to price method" do
@item.should respond_to(:get_price)
end

Small Rspec revelations, ActionMailer

Tuesday, February 26th, 2008

At the office we are getting more and more serious about RSpec . Today we ran into two things we had not done so far. Specing an ActionMailer and a module. The Rails Way only covered Test::Unit and that looked dreadful. Way to much code. I still think I should be coding applications not tests. A quick google turned up not so much, nor did the RSpec site help. Fortunately someone on the RSpec mailinglist brought the subject up. With that I knew enough to get the tests working.


before(:each) do
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
end

This before block set the method for delivery method to :test. Normally it default to :smtp and an other possibility is :sendmail. The second directive tells ActionMailer to actually send the emails(who would have guessed?). The deliveries array keeps track of every email sent.

Now you can just send your email as you usually would:


mail = Notifications.deliver_some_random_email("name")
ActionMailer::Base.deliveries.size.should == 1
mail.body.should =~ /name/

Rock on RSpec.

Building a RESTful CMS

Thursday, February 21st, 2008

Today is the day we at Innovationfactory finish the development of a site maintained by a CMS. As this was the first CMS we build in RESTful Rails initially we where unsure how to proceed. There is some discussion(and here and here) on whether or not the admin functionality should be seperated from the non admin functionality. On one hand this makes sense. As an admin you are doing something vastly different than a normal user visiting the site. On the other hand it doesn’t. Both the admin and visitor interact with the same resources. Only the view and options presented are differtent. In the end we choose to seperate the admin functionality from the visitors functionality, thus creating two controllers for one resource. We deemed this better as the underlying resource is still the same but the interaction is different. And interaction is taken care of in a controller. Hence two controllers for two distinct way of interacting with our resources.

The usage of self in Ruby

Tuesday, January 1st, 2008

This confused me for some time and I figured I write it down for others to enjoy. When should one use the keyword self in AR:B? For this it is important to realise that Ruby is an interpreted language. Whenever Ruby sees an assignment like bla = 'boe' it first looks for a local varialbe by that name(bla in this case) and if that does not exists it then looks for a method bla=


class Foo
  def bar
    text = 'fake' #assigns a variable 'text'
    text.gsub!() #changes the variable 'text'
  end

  def bla
    text.gsub!() #does not find a variable 'text', then searches for a method 'text'(and fails in this case)
  end
end

So if you want to change a field in an ActiveRecord model you need to tell Ruby not to create a local variable but to use the generated setter method.


class Foo < AR:B
  def bar
    self.text = 'fake' #assigns the field 'text'
    text.gsub!() #changes the variable 'text'
  end
end

Modules, inheritance and classes

Wednesday, December 12th, 2007

I can be a bit of a purist when it comes to programming. I love to have my code DRY and go to lengths to achieve that. Today I wrote a piece of code which would enable the duplication of Rails models. This in order to support some templating. Well it’s a little bit more than ’some’ as I need to copy the entire model with all associated models(and their associated models, and their …). To keep things flexible I did not wanted to loop over a couple predefined fields and do all the copying there:


copy = Project.new
p = Project.find(:first)
copy.title = p.title
copy.description = p.description
p.members.each do |member|
  copy.members.build(:name => member.name, :function => member.function)
end

This is prone to errors not to mention ugly(and a lot of dumb work).

So I figured I’d write something better. This was quickly done:


class Project < AR:B
  cattr_accessor :non_duplicatable_columns
  non_duplicatable_columns = [primary_key, 'created_at', 'updated_at','created_on', 'updated_on'] + column_names.select {|column_name| column_name =~ /_id$/}      

  def copy
    copy_columns = self.class.column_names - self.class.non_duplicatable_columns
    a = Hash.new
    copy_columns.each {|cc| a[cc] = self.send(cc) }
    clone = Project.create(a)
  end
end

But I needed this piece of functionality in several models. So what’s a good programmer to do? Refactor the code into a module of course! But there I hit some snags. The method cattr_accessor, for example, is a bit of a strange beast. It is not documented but is should be read like ‘class attribute accessor’. The method creates class instance methods. This is something different from instance methods which you usually use. Instance methods are callable on an object which is instance of a class.


p = Project.new #class instance method
p.copy #instance method

Defining class instance methods goes with the

self

keyword. It reminded me of the Java static keyword.


def self.foo
  #do something, can be called as: FullClassName.foo
end

Keep the eye on the ball here, self points to the object on which this method is called. When defining the copy method the method column_names is called but this is a class instance method from the class subclassing AR:B. So the objects looks at itself, asks who the fathering class is and calls the desired method from that class.

The method

cattr_accessor

can no longer float freely in the class definition. It needs a container as the module is read at the moment the Rails stack is initialize and at that point the module is not necessarily hooked into a class in which a cattr_accessor makes sense. Luckily the module “http://ruby-doc.org/core/classes/Module.html”:Module defines a method included which is called whenever the module is, well, included. Using this we can still define the

non_duplicatable_columns

on a per model basis.

Finalized the module looks like this(stored in lib/duplicatable.rb):


module Duplicatable

  def Duplicatable.included(base)
    base.cattr_accessor :non_duplicatable_columns
    base.non_duplicatable_columns = [base.primary_key, 'created_at', 'updated_at','created_on', 'updated_on'] + base.column_names.select {|column_name| column_name =~ /_id$/}
  end

  def copy
    copy_columns = self.class.column_names - self.class.non_duplicatable_columns
    a = Hash.new
    copy_columns.each {|cc| a[cc] = self.send(cc) }
    clone = self.class.create(a)
  end
end

And a simple include Duplicatable adds all its functionality to a model.
Why not use a super class here? That would require calling a initialize function(to set the non_duplicatable_columns) in each model with would extend this super class. Which would result in more code and thus more places where this can break.

Living on the Edge

Monday, December 10th, 2007

As a Rails developer I am still a bit wet behind the ears. Following the lead of a more senior developer I picked up the habit of developing all(that is four so far) applications in Rails Edge with the help of RSpec, Edge RSpec.

Last weekend I picked up a pet project which a colleague and I came up with. Nothing difficult and we figured we could get this done within the week. Boy was I wrong. Setting the whole thing up took me the better part of the day. Of course partially due to my own inexperience but the fact that I was using Edge for everything didn’t help.

I’ll skip all the things which went horribly wrong, this is what I _should_ have done:


$ rails new_prj
$ cd new_prj
$ piston import http://dev.rubyonrails.org/svn/rails/trunk vendor/rails
$ piston import svn://rubyforge.org/var/svn/rspec/trunk/rspec vendor/plugins/rspec
$ piston import svn://rubyforge.org/var/svn/rspec/trunk/rspec_on_rails vendor/plugins/rspec_on_rails
$ ./script/generate rspec

Don’t try to freeze rails using rake rails:freeze:edge. This leads to pain and the absence of a framework dependency causing the ./script/server command to fail.
Don’t try to use the current release of RSpec in combination with the edge version of Rails. This again leads to suffering and mysteriously failing tests.
Don’t forget that running ./script/generate rspec_model is not enough to get the entire RSpec framework to behave. It’ll work to some degree but has some crucial functionality missing. For example the ./script/spec command is missing.
Don’t expect Restful authentication to play nice with RSpec through Vaporbase’s spec files. Don’t get me wrong the guys of Vaporbase did a great job it just didn’t work for me. The class UserNotifier should be UserMailer in my instance and the protected helper methods in the sessions_controller_spec.rb are ran by rspec throwing weird errors. So these should be moved to the spec_helper.rb file.

As a final bonus, ever wanted to spec a single file?


$ ./script/spec spec/controllers/foo_controller_spec.rb --color -b

Nested namespaces in RESTful Rails

Thursday, November 29th, 2007

Using RESTful Rails definitely has it advantages. The clean code was the primary reason why I switched all my projects toward REST. But I found that the nesting of resources broke down when using modules. I’d like to keep my code base nice and logically separated. For example the admin controller should reside in the app/controllers/admin/ directory. This will make your code base clearer and thus easier to maintain. And beside that it will generate pretty URL’s in which it is instantly clear where one is.

In the current project I integrated Beast and thought it was a good idea to keep this in a separate module as well. But the application I was working on required that forums only had relevance in the context of a project(there are many many different projects). So the obvious thing to do is to create a namespace nested in the projects resource.


map.resources :projects do |projects|
projects.resources :members
projects.namespace(:wiki) do |wiki_namespace|
wiki_namespace.resources :pages, :member => {:auto_save => :put}
end
end

And of course I wouldn’t be writing this post if this would work. Somehow the routes generated completely omitted the ‘wiki’ part:


new_project_page GET    /projects/:project_id/pages/new                                                        {:action=>"new", :controller=>"pages"}
formatted_new_project_page GET    /projects/:project_id/pages/new.:format                                                {:action=>"new", :controller=>"pages"}
edit_project_page GET    /projects/:project_id/pages/:id/edit                                                   {:action=>"edit", :controller=>"pages"}
formatted_edit_project_page GET    /projects/:project_id/pages/:id/edit.:format                                           {:action=>"edit", :controller=>"pages"}
auto_save_project_page PUT    /projects/:project_id/pages/:id/auto_save                                              {:action=>"auto_save", :controller=>"pages"}
formatted_auto_save_project_page PUT    /projects/:project_id/pages/:id/auto_save.:format                                      {:action=>"auto_save", :controller=>"pages"}
project_page GET    /projects/:project_id/pages/:id                                                        {:action=>"show", :controller=>"pages"}

Weird. Not to mention inconvenient. Eventually I got it to work:


map.resources :projects do |projects|
projects.resources :members
projects.resources :pages, :controller => "Wiki::Pages", :path_prefix => "/projects/:project_id/wiki", :name_prefix => "project_wiki_", :member => {:auto_save => :put}
end

The :path_prefix parameter appears to overwrite the path_prefix defined by the block defining the scope. I would have preferred to write :path_prefix => "wiki". As one can see this solution is very unbecoming. And my RSpec tests started failing… With RoutingErrors. Rails worked just fine but RSpec did not play nice with this hack(because that is what this is, a hack). The RSpec mailinglist nor the Rails mailinglist could help.

This led me to abandon testing. Yes, I know, bad idea. Which I soon found out. This time I dove in the source code and found out that the namespace method takes an argument called namespace. Fiddling around with this parameter I got to the following solution:


map.resources :projects do |projects|
projects.resources :members
projects.namespace(:wiki, :namespace => '') do |wiki_namespace|
wiki_namespace.resources :pages, :member => {:auto_save => :put}
end
end

This generated the correct routes and satisfied RSpec! Don’t ask me for the rational behind this obviously strange behavior.

attachment_fu dances with Capistrano

Tuesday, November 13th, 2007

It is one of these joyous revelations. Today I was having a discussion with a fellow developer on attachment_fu. I am very much a fan of plugins, this means less work for me. In which case I win. He, however, refrained from using attachment_fu. Instead writing his own file upload mechanism. This seemed strange to me and I asked him why. He then correctly pointed out that with attachment_fu he was unable to save files outside his application root directory.

So why is this a problem? Everyone I know is using Capistrano as their preferred way of deploying. The way Capistrano does subsequent deploys is by linking the new release to the current directory and unlinking the old one. Everything in the old release suddenly becomes unavailible! And that includes the files by attachment_fu. And article on Almost Effortless pointed this out several months ago and posted a solution in the form of a Capistrano recipe. But who wants to write their own recipes?

However I seemed to remember seeing a @system@ directory on our live server. And there is! In Capistrano 2.0 every public directory has a link to a system directory which is unaffected by deploys. How well these Capistrano people thought this out. Brilliant. Now it is only a matter of using the :path_prefix argument.


has_attachment :content_type => :image,
:storage => :file_system,
:path_prefix => 'public/system',
:max_size => 500.kilobytes,
:resize_to => '320x200>',
:thumbnails => { :thumb => '100x100>' }

Set svn propset svn:ignore "*" public/system/ and you are ready to go!

Acts_as_versioned quirks

Sunday, November 11th, 2007

The Rails plugin acts_as_versioned by Rick Olson is a blessing when one wants to do some simple versioning. Building a wiki springs into mind which was exactly what I wanted to use it for. Following the learn_to post I was able to get most of it working. However I my case, and I imagine others, I did not want to version control my entire pages table. I do not care about the @created_at@ and @updated_at@ fields to be versioned. But using the default migration tactic of building a versioned table did not work (passing a block to the Page.create_versioned_table). Instead I needed to configure my model with:

self.non_versioned_columns << 'project_id' << 'project_user_id' << 'locked_by' << 'locked_at' << 'created_at' << 'updated_at'

Not something I would expect.

And it turned out there were more unexpected ‘features’. The inclusion of the timestamps in my Page model seemed superfluous. The generated @page_versions@ table included them by default. But there is no easy way for dropping these columns from the @pages@ table. Sure I could write a migration doing just that but by then the generated @page_versions@ table already contained these columns and apparently the generated class Page::Version referenced to these column. This obviously led to errors. Instead I ended up dropping and recreating the entire table. Unfortunately MySQL died complaining that the column @version@ already exists in the @pages@ table. You’ll have to remove that column too.

Finding a previous version has changed somewhat too. According to Rails Recipes;


>> p = Page.find(:first)
>> p = p.find_version(3)
NoMethodError: undefined method `find_version' for #<Page:0x329a5d8>

This should work but it does not. Actually I would love for it to work like that. The alternative is;


>> p = Page.find(:first)
>> array = Page.find_versions(:first)
>> p = p.revert_to(arr[2])

Not as pretty but it works.

On the bright side there is this neat feature called @:if_changed@. This allows you to control when a new version should be created. The directive is given as an argument in the acts_as_versioned declaration:

acts_as_versioned :if_changed => [:title, :text, :member_id]

Sweet now a new version is created only when the title, the text or the owner changes.

In conclusion, the acts_as_versioned plugin is a great extension but seems a bit outdated as not all functionality works(at least not in Rails 2.0RC1). Also it is better to create the documentation from the source as all the online documentation is very old.