# Factoring code out of a model but not as a plugin

**URL:** <https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486>\
**Category:** Rails\
**Created:** [November 21, 2005, 12:24am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486 "2005-11-21T00:24:42Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 12:24am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/1 "2005-11-21T00:24:42Z")

</div>

Hi,

I would like to factor the following code out of my Product model so  
that I  
can mix it into any model. Since the code depends on other models a  
plugin  
would not be stand alone. I have tried to make a module in a separate  
file  
in app/models/ and require it to include, extend Product but nothing I  
have  
tried works. Any suggestions how I can get the has\_many statement and my  
method\_missing method out of Product? They work perfectly in Product but  
I  
can’t share them with other models this way.

Thanks!  
Peter

class Product \< ActiveRecord::Base

has\_many :virtual\_attribute\_values,  
:foreign\_key =\> ‘owner\_id’,  
:conditions =\> “owner\_class = ‘#{self.to\_s}’”,  
:dependent =\> true

def method\_missing(method\_id, \*args, &block)  
end

end

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 3:41am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/2 "2005-11-21T03:41:46Z")

</div>

Hi Francois,

I tried what you said but it doesn’t work. When I use @  
product.test\_virtual\_attribute in a view Rails uses ActiveRecord’s  
method\_missing instead of mine. (My method\_missing does include a  
“super”  
call.) It is like my module is not getting included.

If I move my method\_missing back into Product and only leave the  
has\_many  
statement in the module then I get an error like  
“virtual\_attribute\_values  
unknown method or attribute”. Again, it looks like my module is not  
getting  
included.

I’m stumped.

Any ideas?

Peter

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 2:29am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/3 "2005-11-21T02:29:28Z")

</div>

2005/11/20, Peter M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> I would like to factor the following code out of my Product model so that I  
> can mix it into any model. Since the code depends on other models a plugin  
> would not be stand alone. I have tried to make a module in a separate file  
> in app/models/ and require it to include, extend Product but nothing I have  
> tried works. Any suggestions how I can get the has\_many statement and my  
> method\_missing method out of Product? They work perfectly in Product but I  
> can’t share them with other models this way.

# lib/virtual\_attributes.rb

module VirtualAttributes  
has\_many :virtual\_attribute\_values,  
:foreign\_key =\> ‘owner\_id’,  
:conditions =\> “owner\_class = ‘#{self.to\_s}’”,  
:dependent =\> true

def method\_missing(method\_id, \*args, &block)  
end  
end

# app/models/product.rb

class Product \< ActiveRecord::Base  
include VirtualAttributes  
end

WARNING: Untested, but should be along those lines.

Please be warned that you MUST call method\_missing because the  
ActiveRecord::Base framework relies on method\_missing by itself. So,  
if you can’t find the attribute, just super away.

Bye !

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 3:53am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/4 "2005-11-21T03:53:49Z")

</div>

I tried all these things and still no luck. I don’t see anything in  
development.log indicating RAILS\_DEFAULT\_LOGGER.warn

I also tried putting a line of garbage in the module

asdfsdaf.sadfsadfasdf

and that didn’t cause an error.

Scratching my head.

Peter

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 3:41am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/5 "2005-11-21T03:41:46Z")

</div>

2005/11/20, Peter M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> If I move my method\_missing back into Product and only leave the has\_many  
> statement in the module then I get an error like “virtual\_attribute\_values  
> unknown method or attribute”. Again, it looks like my module is not getting  
> included.
> 
> I’m stumped.
> 
> Any ideas?

Try to require the virtual attr library at the end of environment.rb.  
At least worth a try. Also, in the file, put a logging statement near  
the start and end, just to verify that the file is indeed loaded. A  
simple $stderr.puts or RAILS\_DEFAULT\_LOGGER.warn is enough.

Good luck !

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 7:14am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/6 "2005-11-21T07:14:54Z")

</div>

I tried this and got the obviously expected error I was hoping for. All  
of  
this is in product.rb

module Asdf  
asdfasdf.asdffsad  
end  
class Product \< ActiveRecord::Base  
include Asdf  
end

So either Rails is not making the factored out module’s file visible to  
Product or Ruby is not complaining when it cannot find this file?

-Peter

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 6:30am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/7 "2005-11-21T06:30:35Z")

</div>

Hi Peter,

your problem is similar to one one of mine, so I’m also hoping for  
some help on this 🙂

I want to override the [] and []= methods in any ActiveRecord::Base  
descendants that I choose, by mixing in module “RosterAccess”. For  
example,

class Chessboard \< ActiveRecord::Base  
include RosterAccess  
end

board = Chessboard.new  
board[1,1] = Queen.new(:white)

However my overridden []= method never gets called. ActiveRecord’s  
implementation gets called instead, and that doesnt make the Queen  
happy 😛

cheers  
Gerret

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 7:39am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/8 "2005-11-21T07:39:04Z")

</div>

Peter M. \<petermichaux@…\> writes:

> I tried this and got the obviously expected error I was hoping for. All of  
> this is in product.rb
> 
> * * *
> 
> Rails mailing list  
> Rails@…  
> [http://lists.rubyonrails.org/mailman/listinfo/rails](http://lists.rubyonrails.org/mailman/listinfo/rails)

In your case peter, because you want to add virtual ‘fields’/attribute,  
overriding read\_attribute and write\_attribute might work just as well.  
If you  
look as ActsAsI18n, this is what I do as well. I’ve boiled it down to  
the basics  
below. In particular note that the original read/write\_attribute methods  
are  
aliased and that the new methods intercept, check if they should be  
called and  
then fallback to the original methods. If you don’t have a list of field  
names,  
then you may want to check for the original methods raising an  
exception, then  
catch with your implementation instead.

By overwriting the read/write\_attribute methods, you handle all methods  
of  
accessing the attributes (via the method missing which calls these  
methods, via  
[]/[]= and via read/write).

* * *

require ‘active\_support’  
require ‘active\_record’

module ActiveRecord #:nodoc:  
module Acts #:nodoc:  
module I18n  
def self.append\_features(base)  
super  
base.extend(ClassMethods)  
end

```
module ClassMethods
	def acts_as_i18n(options = {})
		## this prevents acts_as_i18n from being called multiple times
		return if

```

self.included\_modules.include?(ActiveRecord::Acts::I18n::InstanceMethods)

```
		## finally, add instance and singleton methods
		class_eval do
			alias_method :i18n_orig_read_attribute, :read_attribute
			alias_method :i18n_orig_write_attribute, :write_attribute
			include ActiveRecord::Acts::I18n::InstanceMethods
		end
	end
end

module InstanceMethods
	def i18n_get_field(f); end
	def read_attribute(attr)
		if self.class.i18n_fields.include?(attr.to_sym)
			return i18n_get_field
		else
			return i18n_orig_read_attribute(attr)
		end
	end

	def i18n_set_field(f,v); end
	def write_attribute(attr,val)
		if self.class.i18n_fields.include?(attr.to_sym)
			return i18n_set_field(attr,val)
		else
			return i18n_orig_write_attribute(attr,val)
		end
	end
end

```

end  
end  
end

---

<div class="post-metadata">

**Author:** ![petermichaux](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/46a35a/32.png) [@petermichaux](https://www.ruby-forum.com/u/petermichaux)\
**Post date:** [November 21, 2005, 7:39am UTC](https://www.ruby-forum.com/t/factoring-code-out-of-a-model-but-not-as-a-plugin/55486/9 "2005-11-21T07:39:05Z")

</div>

Problem solved. It was a matter of restarting WEBrick so it would see  
the  
changes in lib/ files. Seems like all is working well now.

I guess it is part of how WEBrick works but if I make changes outside of  
the  
app/ directory they are not noticed unless I restart WEBrick. This is a  
bit  
of a pain when developing (tweaking CSS, for example).

Any way to make development life with WEBrick a little easier?

Thanks,  
Peter
