Today, I was building a controller action that was polymorphically finding and associating an object. I could have used a simple dumb solution, such as this:

app/controllers/watches_controller.rb

1 class WatchesController < ApplicationController
2 def create
3 watch</span> = <span class="co">Watch</span>.build(<span class="sy">:watcher</span> =&gt; current_person) <span class="no"> 4</span> <span class="iv">watch.subject = case
5 when params[:person_id]
6 Person.find(params[:person_id])
7 when params[:event_id]
8 Event.find(params[:event_id])
9 else
10 raise ArgumentError, "Don’t know how to handle other keys… #{params.keys.inspect}"
11 end
12 watch</span>.save! <span class="no">13</span> flash[<span class="sy">:notice</span>] = <span class="s"><span class="dl">&quot;</span><span class="k">You're watching </span><span class="il"><span class="idl">#{</span><span class="iv">watch.subject.name}"
14 redirect_to root_url
15 end
16 end

That sucked. Really bad. Why did I have to have a case/switch statement in my controller? Why not use a simpler alternative? I could have gone the full polymorphic route too:

app/controllers/watches_controller.rb

1 class WatchesController < ApplicationController
2 def create
3 watch</span> = <span class="co">Watch</span>.build(<span class="sy">:watcher</span> =&gt; current_person) <span class="no"> 4</span> <span class="no"> <strong>5</strong></span> <span class="c"># For illustration purposes, this is fine, but INSECURE!!!</span> <span class="no"> 6</span> <span class="iv">watch.subject_type = params[:subject_type]
7 watch</span>.subject_id = params[<span class="sy">:subject_id</span>] <span class="no"> 8</span> <span class="iv">watch.save!
9
10 flash[:notice] = "You’re watching #{@watch.subject.name}"
11 redirect_to root_url
12 end
13 end

Then, I remembered that my controller was already a ResourceController implementation. I opened up the code and used this instead:

app/controllers/watches_controller.rb

1 class WatchesController < ResourceController::Base
2 belongs_to :person, :event
3
4 create.before do
5 watch</span>.watcher = current_person <span class="no">6</span> <span class="iv">watch.subject = parent_object
7 end
8 end

I needed to add a bit of infrastructure (some routes and has_many declarations in my models), but those were benefits (I know I’ll need them later anyway). My controller code is simpler, and the intention is clearer, I think.

In the same vein, are you following #standup on Twitter?

I just got hit by this famous error again. And I am not the only one:

Now, I know I should put my patches where my mouth is. And that’s exactly what I’m trying to do here. My application is on the 1.2 branch, and I get this error if I have the Response Logger plugin loaded. Trying again with Edge Rails, I again get the error. If I try with WEBrick or Mongrel, same error. Loading the console produces the same error. Using Windows or Linux changes nothing: same error.

The exact backtrace is:


1 $ ruby script\console
2 Loading development environment.
3 ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:249:in `load_missing_constant’: Expected ./script/../config/../config/../vendor/plugins/response_logger/lib/response_logger.rb to define ResponseLogger (LoadError)
4 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:452:in `const_missing’
5 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:464:in `const_missing’
6 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:260:in `load_missing_constant’
7 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:452:in `const_missing’
8 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:260:in `load_missing_constant’
9 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:452:in `const_missing’
10 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:260:in `load_missing_constant’
11 from ./script/../config/../config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:468:in `const_missing’
12 … 14 levels…
13 from C:/ruby/lib/ruby/1.8/irb/init.rb:250:in `load_modules’
14 from C:/ruby/lib/ruby/1.8/irb/init.rb:21:in `setup’
15 from C:/ruby/lib/ruby/1.8/irb.rb:54:in `start’
16 from C:/ruby/bin/irb.bat:20

In this application, I am using Engines, along with LoginEngine. You really need the right version of Engines with the right version of Rails for things to work out well: Rails 1.2 requires Engines 1.2.

As I reported earlier, I now use plugin_assets in my applications. Two days ago, I ran


1 $ piston status —show-updates

and noticed some changes in the PluginAWeek.org plugins.

Naïvely, I ran:


1 $ piston update vendor/plugins/*

Now, rake assets:update breaks silently. It won’t find any plugins, no matter what. I finally traced the problem to this:


1 Index: loaded_plugins.rb
2 ===============
3 —- loaded_plugins.rb (revision 310)
4 + loaded_plugins.rb (revision 311)
5 @</span> -1,4 +1,4 <span class="chg">@
6 -$LOADED_PLUGINS = []
7 +$LOADED_PLUGINS = ActiveSupport::OrderedHash.new

The full diff can be obtained as


1 $ svn diff -r 310:311 http://svn.pluginaweek.org/trunk/plugins/rails/loaded_plugins/lib

For the moment, I’m using this fix:


1 $ svn diff
2 Index: vendor/plugins/loaded_plugins/tasks/loaded_plugins_tasks.rake
3 ===============
4 - vendor/plugins/loaded_plugins/tasks/loaded_plugins_tasks.rake (revision 2648)
5 + vendor/plugins/loaded_plugins/tasks/loaded_plugins_tasks.rake (working copy)
6 @ -11,5 +11,5 @
7 plugins = $LOADED_PLUGINS.empty? ? nil : $LOADED_PLUGINS
8 end
9
10 – plugins.kind_of?(Hash) ? plugins.values : plugins
11 + plugins.respond_to?(:values) ? plugins.values : plugins
12 end
13 \ No newline at end of file

Just a warning for everyone out there who might be using plugin_assets.

A minor plugin for all of you. This plugin merely adds two new methods to Time and Date:

test/time_extensions_test.rb

1 class TimeExtensionsTest < Test::Unit::TestCase
2 def test_really_in_future
3 assert 1.second.from_now.in_future?
4 end
5
6 def test_in_future_but_past
7 assert !1.second.ago.in_future?
8 end
9
10 def test_really_in_past
11 assert 1.second.ago.in_past?
12 end
13
14 def test_in_past_but_future
15 assert !1.second.from_now.in_future?
16 end
17 end

This makes for much more readable code:


1 if @post.published_at.in_future? then
2 # do something
3 else
4 # do something else
5 end

Installation:


1 $ script/plugin install \
2 svn://svn.teksol.info/svn/rails/plugins/time_extensions \
3 vendor/plugins/time_extensions

Aaron, of PluginAWeek fame, released PluginAssets.
This plugin handles copying assets from your plugins to your public/ folder. I decided to give it a whirl, since I needed it for a plugin I’m writing.

I use Piston to manage my external dependencies, so I simply Pistoned the code from Aaron’s repository:


1 $ piston import \
2 http://svn.pluginaweek.org/trunk/plugins/action_pack/plugin_assets \
3 vendor/plugins/plugin_assets
4 Exported r85 from ‘http://svn.pluginaweek.org/trunk/plugins/action_pack/plugin_assets’ to ‘vendor/plugins/plugin_assets’
5
6 $ rake assets:update
7 (in D:/wwwroot/staging.teksol.info)
8 rake aborted!
9 undefined method `require_plugin’ for #<Object:0×27bfa3c>
10
11 (See full trace by running task with —trace)

Oops, not good… So, two dependencies later:


1 $ piston import \
2 http://svn.pluginaweek.org/trunk/plugins/rails/loaded_plugins \
3 vendor/plugins/loaded_plugins
4 Exported r85 from ‘http://svn.pluginaweek.org/trunk/plugins/rails/loaded_plugins’ to ‘vendor/plugins/loaded_plugins’
5
6 $ piston import \
7 http://svn.pluginaweek.org/trunk/plugins/rails/plugin_dependencies \
8 vendor/plugins/plugin_dependencies
9 Exported r85 from ‘http://svn.pluginaweek.org/trunk/plugins/rails/plugin_dependencies’ to ‘vendor/plugins/plugin_dependencies’
10
11 $ rake —trace assets:update
12 (in D:/staging.teksol.info)
13 * Invoke assets:update (first_time)
14 *
Invoke environment (first_time)
15 ** Execute environment
16 rake aborted!
17 undefined method `require_plugin’ for #<Object:0×27bfa3c>
18 D:/staging.teksol.info/config/../vendor/plugins/plugin_assets/lib/plugin_assets.rb:1

Whoa, this is even worse. I have to apply a known “workaround” to get plugin_assets to load:


1 $ mv vendor/plugins/plugin_dependencies \
2 vendor/plugins/aaa_plugin_dependencies
3 $ mv vendor/plugins/loaded_plugins \
4 vendor/plugins/aaa_loaded_plugins
5 $ rake assets:update
6 (in D:/wwwroot/staging.teksol.info)
7
8 Mirroring assets for link_with_icon:
9

It does look like the dependencies aren’t well considered here. Mind you, Aaron is doing a fantastic job.

Update 2006-11-10 3 PM EDT: Aaron did a nice job of reworking the code to not require as many dependencies. Read Revisited: plugin_assets and loaded_plugins for the details.

CriteriaQuery is a Rails plugin which eases arbitrary query building.

From the README:

If you frequently face the problem of constructing complex dynamic queries, you will get some benefit out of this. Consider the following search form (taken from a real application):

Name (first or last): [               ]
Customer Category: [ Please Select ][^]
Last call between: [ ] and [ ]
Active Contacts only: [x]
Address
State: [ ]
City: [ ]
Street Addres: [ ]




CriteriaQuery allows you to write the above as:



1 pq = Person.query
2
3 pq.disjunction.first_name_eq(params[:name]).last_name_eq(params[:name]) if params[:name]
4 pq.category_id_eq(params[:category]) if params[:category]
5
6 address = pq.join("address")
7 address.state_eq(params[:address[:state]]) if params[:address[:state]]
8
9 end




1 pq = Person.query
2
3 pq.disjunction.first_name_eq(params[:name]).last_name_eq(params[:name]) if params[:name]
4 pq.category_id_eq(params[:category]) if params[:category]
5
6 address = pq.join("address")
7 address.state_eq(params[:address[:state]]) if params[:address[:state]]
8
9 end

This is all fine and dandy, except using the current version (r41 from the repository), you cannot add arbitrary conditions, such as LENGTH(name) &gt; 0.

ActiveRecord scopes to the rescue. I recently hit that in an application where we allow the admin to find only non anonymous estimates. Non anonymous is defined as having either a phone or an E-Mail address.


1 conds = LENGTH > 0 OR LENGTH > 0 if filters</span>[<span class="sy">:non_anonymous</span>] <span class="no">2</span> <span class="co">Estimate</span>.with_scope(<span class="sy">:find</span> =&gt; {<span class="sy">:conditions</span> =&gt; conds}) <span class="r">do</span> <span class="no">3</span> <span class="iv">estimates = query.find(:order => updated_at DESC)
4 end

Of course, you will ask “why in the world aren’t your fields ”sql">NULL to begin with if they don’t contain a value ?" Now, that is a very interesting question which I will now explore with gusto !

I installed the AssetPackager plugin. Scott, the manager of the plugin, suggests running the asset:packager:build_all task after the symlink phase.

I disagree with that because as soon as the symlink is done, it means we’re live. We haven’t yet built the files, so users will receive 404s instead of the compressed files.

Instead, I suggest doing it after updating the code. So, his Capistrano task changes to this instead:

config/deploy.rb

1 task :after_update_code, :roles => [:web] do
2 run <<-EOF
3 cd
#{release_path} &&
4 rake RAILS_ENV=production asset:packager:build_all

5 EOF

6 end

Unrelated note: From the comments on Scott’s announcement (see Timur Vafin’s comments), I am not the only one writing asse*r*t_packager. Must be all those darn assertions I write all day long…

This article is obsolete because it uses features that were replaced many times over. If you want an HABTM selector widget, look at MultipleSelect by Daniel Rodríguez Troitiño.

I just uploaded a new version of the HABTM Helper Plugin. No new features, except it now works…

There was a bug which would cause an error when the selected elements were not updated by the end-user. All the IDs were bunched together, instead of being separated by commas.

This is an artifact of the fact that the view helper was not built using behavioral driven development.

Anyway, it now all works, and is in production on two projects of mine.

Hope it helps someone !

This article is obsolete because it uses features that were replaced many times over. If you want an HABTM selector widget, look at MultipleSelect by Daniel Rodríguez Troitiño.

I just wrote another plugin: habtm_helper_plugin. This plugin will generate HTML code to manage has_and_belongs_to_many relationships.

What does it look like ? See here:

HABTM generated manager

Release 0.1.0 is up for grabs, in one of four ways:

  • svn propset http://opensvn.csie.org/habtm_helper_plugin/tags/0.1.0 svn:externals vendor/plugins WARNING: Destructive if you already set other externals
  • script/plugin http://opensvn.csie.org/habtm_helper_plugin/tags/0.1.0
  • svn export http://opensvn.csie.org/habtm_helper_plugin/trunk vendor/plugins/habtm_helper/tags/0.1.0
  • Download habtm-helper-plugin-0.1.0.zip

Full documentation in source. If you want to know, the filter was developed using RSpec. I wrote a lenghty article on my experience using Behavioral Driven Design at Building a plugin on RSpec

The project is hosted on OpenSVN. The project and Trac wiki/bug database is available at the habtm_helper_plugin project home page.

This article and the plugin it describes are obsolete. There are replacements for neighter. The code is still available if you want to play around with it.

A new day, a new release !

What’s changed ?

  • A heinous bug was corrected that prevented flash.now from being correctly appended to.
  • #show_flash_messages was always returning the enclosing div. Now, it will only return something if the keys contain something.
  • Assertions for your unit tests. See below for details.
  • Renamed middle package from flash_plugin_helper to flash_helper_plugin. This should not have any impact on your code.

The interesting feature in this version are the new assertions. For example, the following code:


1 def test_destroy
2 post :destroy, :id => contacts(:bob).id
3
4 assert_match /bob.*destroyed successfully/i,
5 flash[:notice],
6 "User notified that Bob was deleted"
7 end

can be expressed like this instead:


1 def test_destroy
2 post :destroy, :id => contacts(:bob).id
3
4 assert_success_flash_contains \
5 /bob.*destroyed successfully/i,
6 User notified that Bob was deleted
7 end

The reverse is also true:


1 def test_pre_authorization_granted
2 get :index, {}, {:user_id => accounts(:bob).id}
3
4 assert_any_flash_does_not_contain \
5 /not authorized/i,
6 Authorization granted
7 end

There are eight assertion methods added to Test::Unit::TestCase:

  • #assert_any_flash_contains(string_or_regexp, msg=nil)
  • #assert_any_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_failure_flash_contains(string_or_regexp, msg=nil)
  • #assert_failure_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_success_flash_contains(string_or_regexp, msg=nil)
  • #assert_success_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_message_flash_contains(string_or_regexp, msg=nil)
  • #assert_message_flash_does_not_contain(string_or_regexp, msg=nil)

The any variants of the assertions check all message keys: :notice, :message and :warning. All messages are concatenated, and the assertion is made against the resulting string.

In addition, four building block assertions are also added:

  • #assert_flash_contains(key, string_or_regexp, msg=nil)
  • #assert_flash_does_not_contain(key, string_or_regexp, msg=nil)
  • #assert_in_contents(string_or_regexp, contents, msg=nil)
  • #assert_not_in_contents(string_or_regexp, contents, msg=nil)

And last, but not least, the #flash_contents(key) method returns the flash’s contents, as a string, whether it was an Array or a String to begin with.

If anybody is using this in production, would you drop me an E-Mail ?

Enjoy !

This article and the plugin it describes are obsolete. There are replacements for neighter. The code is still available if you want to play around with it.

Release 0.2 is now upon us. Release 0.1, described in Flash Helper Plugin – Flash Standardization was so broken as to be unusable. From bad requires, to invalid syntax and missing classes/modules, things looked pretty bleak.

Fortunately, things are better. Release 0.2 has no new features, but at least, it has the benefit of working.

Installation stays the same:

  • svn export http://opensvn.csie.org/flash_helper_plugin/tags/0.2 vendor/plugins/flash_helper
  • svn propset svn:externals vendor/plugins flash_helper http://opensvn.csie.org/flash_helper_plugin/tags/0.2 be very careful with this one
  • Download from RubyForge.org and unpack into vendor/plugins

Everyone, report any bugs and enjoy !

This article is obsolete, as is the plugin. The article does not have a replacement, and the plugin died a kind death. It’s code is still available if anybody wishes to take the reins and go with it.

Over on the Rails mailing list, Luke Randall is proposing that Rails applications and generators standardize on some keys for the flash:

This being a very good idea, here is an excerpt of his proposition:

Based on my own experiences, I’d suggest something along
the lines of the following (the names are the best I can think of,
anyone is welcome to suggest better ones):


  • :notice for positive feedback (action successful, etc)
  • :message for neutral feedback (reminders, etc)
  • :warning for negative feedback (action unsuccessful, error encountered, etc)

(emphasis mine)

In the spirit of open source, I started coding a new Rails plugin: the FlashHelperPlugin. With it, you will be able to do:

app/controllers/users_controller.rb

1 class UsersController < ApplicationController
2 def authenticate
3 if authenticated? then
4 flash_success(Authenticated successfully)
5 redirect_back_or_default :controller => home
6 else
7 flash_failure(Failed authentication)
8 end
9 end

app/views/layouts/application.rhtml

1 <html>
2 <head>
3 <%= stylesheet_link_tag ‘scaffold’, ‘application’,
4 :media => ‘all’ >
5 </head>
6 <body>
7 <= show_flash_messages >
8 <= @content_for_layout %>

9 </body>
10 </html>

Actually, what I need now is a nudge to get the tests working. Also, WEBrick fails silently on startup with this plugin in vendor/plugins.

If you want to help, go on over to the flash_helper plugin project page for more information. Get started by checking out the code from Subversion.

This article is obsolete now that Rails 2.3 has Engines in core.

Over on the Rails mailing list, James Adams reports the initial release of Rails Engines.

What are engines ? They are plugins, more or less, except they also bundle controllers, views, partials, models and everything else you’d usually need in a component.

In fact, Rails Components could very well be implemented using Rails Engines.

I wonder how routes are handled. Can engines provide their own routes, or must the engine user define the routes he wants ? Gotta try that login_engine and see for myself.

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on