Forum: Ruby on Rails Removing an association on an AR model via monkey patching

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Wes G. (Guest)
on 2009-01-16 20:59
How can I remove an association from an AR model with a monkey patch?

Assume:

class Foo < AR::Base
  has_many :bars
end

It's a long story, but in one particular data loading (not running the
app. context), I'd like to remove the has_many association to :bars.

Can I do that?

Thanks,
Wes
Jeff C. (Guest)
on 2009-01-16 21:37
(Received via mailing list)
On Jan 16, 12:59 pm, Wes G. <removed_email_address@domain.invalid>
wrote:
>
> Can I do that?

It sounds like you want to make sure that code that does this:

@foo.bars

would raise a NoMethodFound exception?  If so, you can just reopen the
class:

class Foo
  def bars
    raise NoMethodError.new("undefined method 'bars'")
  end
end


Then,

@foo.bars  # => raises exception


However, this isn't really removing all of remnants of the
association, so again it depends on what constitutes "removed enough"
for  your situation.


Jeff
purpleworkshops.com
Wes G. (Guest)
on 2009-01-16 22:19
Here's my real problem:

I am trying to load a fixture into a database which acts as a secondary
DB to my application.

The fixture file uses Erb to generate appropriate fixtures based on
something like this:

<% Invoice.find_all_by_im_invoice_state("Paid").each do |inv| %>
  ...generate a bunch of fixtures...
<% end %>

The problem is that the Invoice class has an association which is not
present in the DB into which I am loading the data.  The Invoice class's
table is present, which is all that I need to properly generate the
fixtures.  However, the class for this other has_many association that
I'm not interested in is not present.

So I'd like to temporarily remove the association for the duration of
the fixture load.  Hoping I can just monkey patch within the Erb.

Thanks,
Wes
Rob B. (Guest)
on 2009-01-16 22:33
(Received via mailing list)
On Jan 16, 2009, at 3:19 PM, Wes G. wrote:
>  ...generate a bunch of fixtures...
> the fixture load.  Hoping I can just monkey patch within the Erb.
>
> Thanks,
> Wes


Unless you reference the association, it won't be accessed. Am I (Are
we) missing something here?  Just don't use the association.

-Rob

Rob B.    http://agileconsultingllc.com
removed_email_address@domain.invalid
Wes G. (Guest)
on 2009-01-16 22:44
When I call Invoice.find_all_by_im_invoice_state("Paid").each the
Invoice class is loaded.

When the Invoice class is loaded, all of its class level association
directives are executed.  When it hits the one that doesn't have a
backing table, it fails.

So I need to be able to modify the Invoice class to remove the offending
association before the fixture load occurs.

Wes
Frederick C. (Guest)
on 2009-01-16 22:57
(Received via mailing list)
On 16 Jan 2009, at 20:44, Wes G. wrote:

>
> When I call Invoice.find_all_by_im_invoice_state("Paid").each the
> Invoice class is loaded.
>
> When the Invoice class is loaded, all of its class level association
> directives are executed.  When it hits the one that doesn't have a
> backing table, it fails.

So it's at the point where the class (not the data) is loaded that the
problem occurs ? You might be in a tight spot then because it's going
to be hard to remove the association before the class exists. I can
think of two ways, neither of which is very nice.

define a new class Invoice < ActiveRecord::Base inside your fixture
generation code. if you don't need any of Invoice's methods then
you're fine with just this

Override ActiveRecord::Base.has_many to check if self is invoice and
the association name is the bad one. if it is, do nothing. if not let
the normal code run.

Fred
Wes G. (Guest)
on 2009-01-16 23:54
Frederick C. wrote:
> On 16 Jan 2009, at 20:44, Wes G. wrote:
>
>> When the Invoice class is loaded, all of its class level association
>> directives are executed.  When it hits the one that doesn't have a
>> backing table, it fails.
>
> So it's at the point where the class (not the data) is loaded that the
> problem occurs ? You might be in a tight spot then because it's going
> to be hard to remove the association before the class exists.

Actually, the class needs to exist and I just need to temporarily remove
the association.

> define a new class Invoice < ActiveRecord::Base inside your fixture
> generation code. if you don't need any of Invoice's methods then
> you're fine with just this

I need to be able to do a find on Invoice, which is why I have the
problem in the first place, so this probably won't work.

> Override ActiveRecord::Base.has_many to check if self is invoice and
> the association name is the bad one. if it is, do nothing. if not let
> the normal code run.

This may work and I will give it a shot.  Thanks for the idea.

Wes
Rob B. (Guest)
on 2009-01-17 00:23
(Received via mailing list)
On Jan 16, 2009, at 4:54 PM, Wes G. wrote:

>> problem occurs ? You might be in a tight spot then because it's going
>> to be hard to remove the association before the class exists.
>
> Actually, the class needs to exist and I just need to temporarily
> remove
> the association.

Can you show us the has_many part of your Invoice class?

Product is a model in one of my client projects.  I can do:
$ script/console
Loading development environment.
irb> Product
=> Product
irb> class Product; has_many :foobars; end
=> [:build_to_foobars]
irb> p=Product.find(:first)
=> #<Product:0x3edb0e0 @attributes={...}>
irb> p.methods.include?('foobars')
=> true
irb> p.foobars
NameError: uninitialized constant Product::Foobar

Notice that there is no error when the:
   has_many :foobars
gets processed.  When I find a product, it has a foobars method, but
it doesn't work.

Perhaps you are defining your association is a way that causes pre-
mature loading of the associated model?

>> the normal code run.
>
> This may work and I will give it a shot.  Thanks for the idea.
>
> Wes

'Cause this seems like a lot of work that might not even be necessary.

-Rob

Rob B.    http://agileconsultingllc.com
removed_email_address@domain.invalid
Wes G. (Guest)
on 2009-01-17 01:51
The has_many part of the Invoice class is actually provided by a plugin.
And inside of that plugin, a set_table_name was being done to point to
the offensive table.

I got this to work by making the inclusion of that plugin dependent on
the setting of RAILS_ENV.  Which isn't pretty, but works.

Not sure that I will leave the change in, but I did (finally) get my
data loaded.  Thankfully, I have Erb to make the fixtures as flexible as
I need them to be.

Thanks for all of the help, everyone.

Wes
This topic is locked and can not be replied to.