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.
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2009-01-16 19: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
8217faf2bfdfa7daf10135d41ddd421e?d=identicon&s=25 Jeff Cohen (jeff)
on 2009-01-16 20:37
(Received via mailing list)
On Jan 16, 12:59 pm, Wes Gamble <rails-mailing-l...@andreas-s.net>
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
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2009-01-16 21: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
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-01-16 21:33
(Received via mailing list)
On Jan 16, 2009, at 3:19 PM, Wes Gamble 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 Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2009-01-16 21: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
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-01-16 21:57
(Received via mailing list)
On 16 Jan 2009, at 20:44, Wes Gamble 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
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2009-01-16 22:54
Frederick Cheung wrote:
> On 16 Jan 2009, at 20:44, Wes Gamble 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
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-01-16 23:23
(Received via mailing list)
On Jan 16, 2009, at 4:54 PM, Wes Gamble 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 Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2009-01-17 00: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.