Factoring code out of a model but not as a plugin


#1

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


#2

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


#3

2005/11/20, Peter M. 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 !


#4

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


#5

2005/11/20, Peter M. 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 !


#6

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


#7

Hi Peter,

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

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 :stuck_out_tongue:

cheers
Gerret


#8

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

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


#9

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