Forum: Ruby on Rails Having trouble extending a class from a Rails 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.
Jason F. (Guest)
on 2009-04-09 00:27
I have class in a plugin that I want to crack open and add some
functionality to for a particular application. So, I created a file by
the same name as the class in my app/models folder and added some
methods to the class, but, I can't seem to get Rails (2.2.2) to pick-up
the extended definition.

I did find while trying to debug the problem that if I paste the
extended definition into, say, environment.rb it works fine.  Also, if I
require_dependency "#{RAILS_ROOT}/app/models/my_class.rb" in the
plugin's version of the class it also works fine (obviously not
desirable).

All of this leads me to believe that my problem has something to do with
the class loader, but, I can't figure it out.  I've laid out an example
below that illustrates my problem. Any help would be much appreciated.

Thanks,
Jason


# vendor/plugins/my_plugin/init.rb
require 'my_plugin'

# vendor/plugins/my_plugin/lib/my_plugin.rb
module Jason
  class MyClass
    def a_method
      # ...
    end
  end
end

# app/models/my_class.rb
module Jason
  class MyClass
    def another_method
      # ...
    end
  end
end

$ script/console
>> Jason::MyClass.new.another_method
NoMethodError: undefined method `another_method' for
#<Jason::MyClass:0x408b444>
Maurício L. (Guest)
on 2009-04-09 01:25
(Received via mailing list)
Unfortunately it will never work the way you want it to.

Place this code into an initializer file (under the initializers
folder) or place it under the "lib" folder and do an explicit require.

Also, if all you want to do is to add a method to a class defined
elsewhere, do it like this:

Jason::MyClass.class_eval do
  def another_method
    # do whatever you want to do here.
  end
end

-
Maurício Linhares
http://alinhavado.wordpress.com/ (pt-br) | http://blog.codevader.com/
(en)



On Wed, Apr 8, 2009 at 5:27 PM, Jason F.
Jason F. (Guest)
on 2009-04-09 01:40
Thanks for the reply, Maurício.  Out of curiosity, what is the reason
that this does not work the way that I thought it would.  I am planning
to delve into the Rails class loader code tonight, but, if you can shed
any light on this I'd much appreciated it.

> Place this code into an initializer file (under the initializers
> folder) or place it under the "lib" folder and do an explicit require.

I've actually had similar problems trying to use initializers with
plugins in the past.  Specifically, I've tried to use an initializer to
define application specific values for constants used by my plugin. For
example:

# config/initializers/my_class.rb
class Jason::MyClass
  MY_CONSTANT = "the application's value for this constant"
end

Rails, in this case, appears to load the class definition from the
initializer and stops there, i.e., it never loads the rest of the class
definition from the plugin. So, calling a method defined in the plugin's
version of the class like a_method, would also result in the
NoMethodError...

$ script/console
>> Jason::MyClass.new.a_method
NoMethodError: undefined method `a_method' for
#<Jason::MyClass:0x408b444>

> Jason::MyClass.class_eval do
>   def another_method
>     # do whatever you want to do here.
>   end
> end

I actually tried this approach.  I placed this exact code in my
app/models/my_class.rb version of the class and it results in the same
NoMethodError. However, if I place it in environment.rb it works like a
charm!  :-/

It seems to me that once the class has been defined either by the plugin
or by an initializer (it appears the initializer is loaded, then the
plugin, then the model), Rails will not allow any modifications to it.
Any additional thoughts?

Jason
Maurício L. (Guest)
on 2009-04-09 01:49
(Received via mailing list)
On Wed, Apr 8, 2009 at 6:40 PM, Jason F.
<removed_email_address@domain.invalid> wrote:
> It seems to me that once the class has been defined either by the plugin
> or by an initializer (it appears the initializer is loaded, then the
> plugin, then the model), Rails will not allow any modifications to it.
> Any additional thoughts?

You just figured out the problem by yourself :)

If the class is already loaded, Rails will not try to load it again,
no matter what you do, that's why I showed you the class_eval
approach. Using class_eval (instead of directly "defining" a class)
will make Rails try to load the class from somewhere else (that would
be your plugin) and you woudn't have any method missing or class not
being loaded issues.

Once again, place that code into an initializer and you're done, but
placing it under app/models won't work.

-
Maurício Linhares
http://alinhavado.wordpress.com/ (pt-br) | http://blog.codevader.com/
(en)
Jason F. (Guest)
on 2009-04-09 18:28
Maurício Linhares wrote:

Thanks again for the reply Maurício.

> You just figured out the problem by yourself :)

I'm curious as to why it works if I paste the extended class definition
into environment.rb.  I also found that if I include the following at
the end of my environment.rb it works as I thought it might.  Do you
know why this is an exception to the rule?

# config/environment.rb
require 'jason/my_class'

> If the class is already loaded, Rails will not try to load it again,
> no matter what you do, that's why I showed you the class_eval
> approach. Using class_eval (instead of directly "defining" a class)
> will make Rails try to load the class from somewhere else (that would
> be your plugin) and you woudn't have any method missing or class not
> being loaded issues.

I did try the class_eval approach, however, based on your comment, I was
putting the code in the wrong spot.  I had placed the code into the
app/models/my_class.rb.  I guess it needs to go into an initializer.  Do
you know why that is?

Thanks again for the reply!
Jason
This topic is locked and can not be replied to.