Polymorphic joins work one way, but not the other


#1

I’ve been using polymorphic joins from edge rails based on about 5 lines
of advice Rick O. gave me over IRC. There’s some sparse documentation
popping up but it seems to cover a somewhat different case.

Anyway - I pretty much got it to work - save for one detail. Here’s what
I have:

class Event < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
#events table has attachable_type and attachable_id fields
end

class Task < ActiveRecord::Base
has_many :events, :as => :attachable
#tasks tables is nothing special
end

and now a unit test:

#there’s some fixtures above
def test_polymorphics
assert tasks(:simple_task).events << events(:simple_event_1)
assert tasks(:simple_task).events << events(:simple_event_2)
assert events(:simple_event_1).save!
assert tasks(:simple_task).save!
assert_equal 2, tasks(:simple_task).events.size
assert_equal “simple event 1”,
tasks(:simple_task).events.find(1).note
assert_equal “simple task”, events(:simple_event_1).task.title
end

All asserts pass except the last one:

ruby test/unit/task_test.rb
Loaded suite test/unit/task_test
Started
E.
Finished in 1.103896 seconds.

  1. Error:
    test_polymorphics(TaskTest):
    NoMethodError: undefined method task' for #<Event:0x22258a0> _RAILS_ROOT_/config/../vendor/rails/activerecord/lib/active_record/base.rb:1565:inmethod_missing’
    test/unit/task_test.rb:17:in `test_polymorphics’

2 tests, 7 assertions, 0 failures, 1 errors

(path replaced with RAILS_ROOT since I’m secretive like that)

In other words - I can do stuff like @task.events << @event but NOT
@event.task = @task ! (or indeed @event.task.* ) @tasks.events <<
definitely works - I even checked the db and the attachment_type/id
fields are set exactly as expected.

It kind of makes sense - since I never actually mention :tasks anywhere
in Event’s model definition. But how exactly do I mention it?

I looked over the example in the wiki but it doesn’t seem to touch on
this problem at all (and it kind of tries to do something different too)

  • SO - any help is apretiated.

It could also be that this particular piece of functionality simply
hasn’t been implemented yet…


#2

In other words - I can do stuff like @task.events << @event but NOT
@event.task = @task ! (or indeed @event.task.* ) @tasks.events <<
definitely works - I even checked the db and the attachment_type/id
fields are set exactly as expected.

Try this:

class Event < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true

def task=(task)
self.attachable = task
end
end

If you have a lot of properties, you can earn points towards your
metaprogramming merit badge:

class Event < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true

%w(task project stuff).each do |model|
define_method("#{model}=") do |value|
self.attachable = value
end
end
end


Rick O.
http://techno-weenie.net


#3

Oh my! So it isn’t implemented!

Are there plans to do so later?

The way you mention only adds the = method right? It won’t add all of
task’s methods proper from what I can tell.

It would be nice to rework the syntax for the new polymorphic stuff a
little. It’s a complicated concept, sure, but it’s really a lot less
approachable then the rest of AR-magic.

Here’s what I’d like.
class Comment < AR:B
belongs_to :page, :or => :file, :or => :thing. :or => :other_thing,
:as => :attachable
end

It isn’t quite as elegant (since there would have to be an ‘implied’ :or
=> before :page and :as is actually changing behavior from has_many) but
it would be far more easy to understand imho.

Rick O. wrote:

In other words - I can do stuff like @task.events << @event but NOT
@event.task = @task ! (or indeed @event.task.* ) @tasks.events <<
definitely works - I even checked the db and the attachment_type/id
fields are set exactly as expected.

Try this:

class Event < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true

def task=(task)
self.attachable = task
end
end

If you have a lot of properties, you can earn points towards your
metaprogramming merit badge:

class Event < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true

%w(task project stuff).each do |model|
define_method("#{model}=") do |value|
self.attachable = value
end
end
end


Rick O.
http://techno-weenie.net