Debug failing test with assert_dom_equal
assert_dom_equal('foo', @response.body)
right before the failing tests and you’ll get the full response body for the test dumped to your console.
It’s useful for discovering things like the fact that you forgot the = in <%= yield %> in your test layout.
"Extending" classes: redefining an instance method from a module via alias_method
The Typo plugin is finished, but it’s gotten late and I should review it before deploying and releasing it into the wild. A quick post on more I learned, though….
I had to do a fair bit of research to get the second part of the plugin working – the part where I overload/redefine a controller method from my plugin, specifically the permalink method of an ArticlesController. You can spend a lot of time reading about the intricacies of how mixins work in ruby, without getting working code. Dig through ruby-talk and you’ll learn about the difference between include/extend, what happens to “self” in different cases, the reasons for the odd module nesting and, ultimately why the ClassMethods convention caught on.
It’s not so straight-forward to overload a method, apparently, in ruby. God knows I tried each different nesting include/extend combo 6 times, with no luck. Then I came across an easier way for my case, alias_method. While that usage wasn’t exactly what I wanted, the only problem was that it did too much. I just had to drop off the unneccessary bits, and walla:
module TypoPermalinkWithId
....
module ModifyArticlesController
def self.included(base)
base.class_eval do
alias_method :permalink, :modified_permalink
end
end
def modified_permalink
article = this_blog.published_articles.find_by_permalink(*params.values_at(:year, :month, :day, :title))
redirect_to article.permalink_url and return if article.nil? and article = Article.find_by_id(params[:title].to_i)
redirect_to article.permalink_url and return if article.nil? and article = Article.find_by_title(params[:title])
display_article(article)
end
end
end
ArticlesController.send :include, TypoPermalinkWithId::ModifyArticlesController
Testing plugins : Remember to require 'action_controller/test_process'
Before I could even get started on actually coding the second piece of the Typo plugin, I spent a lot of time wondering why I was getting the following error when trying to run my tests:
NameError: uninitialized constant ActionController::TestRequest
I kept looking at the the other plugin I’ve been working on which the tests run just fine on and scratching my head. It turns out in the working plugin I had stashed the “require ‘action_controller/test_process’” line in the testing controller I made, so I didn’t notice the difference. I’ll be moving that back for clarity tomorrow, but remember kids, if you are doing controller driven tests outside of Rails (like in a plugin) put in that line.
ActiveRecord Observers are a very handy thing for plugins
While developing the first piece of the typo-permalink-with-id plugin I spent a lot of time trying to get the module extend/include and module_eval stuff just right so that my code had everything it needed to not through an error when trying to add a after_create to the app code itself. After a bunch if google around I found exactly what I need in teh v2 Agile book. It wasn’t something plug-in specific, but it is a godsend to plugin writers: ActiveRecord:Observer.
Instead of trying to get your after/before code properly mixed into the app code, you can have it nice and cleanly attached via an Observer. All of a sudden my code became very simple.
module TypoPermalinkWithId
class ArticleObserver < ActiveRecord::Observer
observe Article
def after_create(article)
article.permalink = "#{article.id}-#{article.permalink}"
article.save
end
end
ArticleObserver.instance
end
Testing plugins : loading fixtures and environment
require 'test/unit'
require 'active_record'
require 'active_record/fixtures'
RAILS_ENV = "test"
require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
class TypoPermalinkWithIdTest < Test::Unit::TestCase
def setup
fixtures_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../../test/fixtures'))
Fixtures.create_fixtures(fixtures_dir, File.basename("blogs.yml", '.*'))
Fixtures.create_fixtures(fixtures_dir, File.basename("users.yml", '.*'))
end
def test_fixtures_loaded
assert_not_nil Blog.find_by_id(1), "blogs.yml not loaded"
assert_not_nil User.find_by_id(1), "users.yml not loaded"
end
Edit: This idea slightly cribbed from Loading Fixtures in a Migration
Mocked out ActiveRecord for testing plugin
class Resource < ActiveRecord::Base
@@count = 1
def self.count() @@count end
def self.columns() @columns ||= []; end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
def self.find(param)
return param == :all ? [Resource.new{ |m| m.id = 1; m.name = 'bob' }] : Resource.new{ |m| m.id = 1; m.name = 'bob' }
end
def save() @@count += 1 and return true if valid? end
def destroy() @@count -= 1 end
column :name, :string
end
Manually setting id in Rails 2
In testing DynamicScaffoldResource, I have been working on sort of mocking/overloading ActiveRecord so that it doesn’t touch the DB, so I can minimize how much it depends on the root Rails environment to test, since it’s a plug-in.
class Resource < ActiveRecord::Base
def self.columns() @columns ||= []; end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
def self.find(param)
return param == :all ? [Resource.new(:id => 1, :name => 'bob')] : Resource.new(:id: => 1, :name => 'bob')
end
column :id, :integer
column :name, :string
validates_presence_of :name
end
Well Rails treats the id as a special field, since it is. I tried letting it implicitely add it, and explicitely as above, but there was no way I seemed to be able to set it manually and access it (maybe the problem is prior to a save?), so my edit named route in the view wouldn’t choke (the show path had no problem with the nil, which I might not have caught for a while). While there is probably some way to open up access to the id column, this mailing list post opened up an alternative that works, just use:
Resource.new{ |m| m.id = 1; m.name = 'bob' }

Articles via rss or email