Forum: Ruby on Rails Factoring code out of a model but not as a plugin

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.
petermichaux (Guest)
on 2005-11-21 01:24
(Received via mailing list)
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
francois.beausoleil (Guest)
on 2005-11-21 03:29
(Received via mailing list)
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 !
petermichaux (Guest)
on 2005-11-21 04:41
(Received via mailing list)
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
francois.beausoleil (Guest)
on 2005-11-21 04:41
(Received via mailing list)
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 !
petermichaux (Guest)
on 2005-11-21 04:53
(Received via mailing list)
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
gerret.apelt (Guest)
on 2005-11-21 07:30
(Received via mailing list)
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 :p

cheers
Gerret
petermichaux (Guest)
on 2005-11-21 08:14
(Received via mailing list)
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
paul.vaillant (Guest)
on 2005-11-21 08:39
(Received via mailing list)
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
petermichaux (Guest)
on 2005-11-21 08:39
(Received via mailing list)
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
This topic is locked and can not be replied to.