Lib folders and application controllers


#1

What’s the best way to handle items in your engine’s lib folder? I have
a
couple of support files I would normally place in my lib folder and then
include in my application controller. In developing my engine, I placed
these files in the lib folder within my engine. Then, I did what I
would
normally do and added the include code to the application controller in
my
engine. I quickly realized that this was being overwritten with the
surrounding application’s application controller, and not wanting to
require
those applications to delete their application controller, I just pasted
the
code into the surrounding application controller (there might be a
better
way to do this, but one of the lines is a before_filter).

Anyway, that works when I actually run the application, but now, when I
run
my tests, I get an error saying:

`const_missing’: uninitialized constant
ApplicationController::AuthenticatedSystem (NameError)

So, I guess my question is, what is the best practice regarding both
files
in your engine’s lib folder as well as including code in the surrounding
application’s application controller?

Thanks,
Trey


#2

I’m not 100% sure this is what you mean, but are you trying to include
a module into ApplicationController?

It sounds like you’re creating an application controller class
within your plugin, which is not what you want to do. If you really
want to have the module included into ApplicationController
automatically whenever the plugin is loaded, try adding something like
this to your init.rb:

ActionController::Base.send(:include, MyModule)

HTH,

James


#3

Indeed, this is exactly what I needed for including the module. And I
loaded the before_filter with

ActionController::Base.send(:before_filter, :my_filter)

One question, where exactly in the flow does the init.rb come in? I
mean,
could I also do

ApplicationController.send(:include, MyModule) to be a little more
precise? I tried and got a some errors about uninitialized constant
ApplicationController, so I’m guessing that class isn’t loaded yet.

Oh well, what works works. Is there any drawbacks to adding these to
the
ActionController::Base instead of the ApplicationController? I realize
that
ApplicationController inherits from ActionController::Base (and thus it
works), I am just curious.

Trey


#4

At the point your plugins are loaded, it’s best to assume that nothing
specific to your application is available - including
ApplicationController. Consider, for example, if ApplicationController
had references (includes, method invokations) which were provided by
plugins that are loaded after this one? It’s definitely going to
crash and burn.

Another historical reason is that in development mode
ApplicationController was unloaded and then reloaded for each request.
This would mean that any additions you’d made to the class would be
lost after the first time you hit your development server.
ActionController::Base is a part of the framework, and so isn’t
reloaded since it’s never expected to change.

HTH

James


#5

The approach I’ve used is to place the relevant code in the my_engine.rb
module under lib, then include MyEngine in application.rb

That approach has worked fairly well for me.

  • Tony

#6

That makes sense. It just feels a little weird sending something to
ActionController::Base (I’d rather programatically add these lines to
the
surrounding app’s application controller), but I think I can get over
it.

One further question though:

I mentioned in my last email, that I did the same thing with a
before_filter
and put the following in my init.rb

ActionController::Base.send(:before_filter, :my_filter)

This works when I run the site with script/server, but if I run my tests
with rake test:plugins PLUGIN=my_plugin, it doesn’t invoke the
before_filter. I’ve placed a puts line at the top of the my_filter
method,
so I can see when it’s called, and through scirpt/server it is called
with
every request, as expected, but through the tests, it never gets called.

The odd thing is that I’ve verified that init.rb is getting called and
that
my other line in init.rb, ActionController::Base.send(:include,
MyModule) is
being loaded. Should I not be doing this with before filters? Or
should I
be doing something else in the test to make sure the before_filter is
set up
correctly.

Trey


#7

We’re experiencing some problems which look like the earlier mentioned
ones.

Somehow the relations within our models are gone after one request. This
isn’t the case when we are in production mode, so it has something to do
with loading.
In the init.rb we are extending the ActionController::Base by using
“ActionController::Base.class_eval do”

if we use send on calls to ActiveRecord like permission.send(:find,
:all) it only works on the first call. As soon as we reload the page, it
breaks. Again in production mode it just works.

We will be wading through the rails core and post back if we find
something.
Help is greatly appreciated.

Kind regards,
Jeroen van Doorn


#8

I have a similar problem with certain calls in my init.rb only being
picked up on the first request…

It appears to me like init.rb is only getting run once during start-up.
This would explain why the mixins disappear after the first request -
when the entire environment gets reloaded, anything that was added in
init.rb will not get added the second time!

Having said that, other plugins are still being loaded and mixed in
correctly on subsequent requests… I have a feeling it’s to do with the
way engines enhanced plugins are loaded but I’m not entirely sure how…

Tekin


#9

We have a workarround, but we think it’s quite dirty!

you could just add an empty model file to your apps model dir and just
require the original model.
In a test engine we don’t have an init.rb and the relations work
properly over there.

Keep you posted.

Jeroen

Tekin S. schreef:


#10

Do you think this may be related to the recent changes to the way
dependencies are reloaded?

http://weblog.rubyonrails.org/2006/8/11/reloading-revamped/

Also, I came across:

Dependencies.log_activity = true

In my case, I am directly accessing, and modifying another plugin’s
model in an init.rb.

From a quick scan of the dependency log, it looks like my model is
getting reloaded during each request, which is happening after
initialisation and thus overriding anything in the init.rb

It looks like engines reloads all plugins after initialisation to add
the engines enhancements, which would explain why modifications are
picked up for the first request, as it happens after the app has been
loaded.

I guess the neat fix would be to get engines to reload after each
request and not just in after_initialize()


#11

We’re really lost. I don’t have clue where to look. We’re still going
through rails and engines code, but we just don’t get it at this moment.

We’re not modifying other models b.t.w., but if we don’t use a init.rb
at all it just works. A solution would be to put all the code in a lib
and include
it in the appcontroller, but I don’t think this is a elegant solution.
Anyway I really want to understand why things are going wrong.

I’m going home, have a good night’s sleep and have a look at it again
tomorrow.

@James A., do you have any clue on what we’re doing wrong ?

Kind regards,
Jeroen van Doorn

Tekin S. schreef:


#12

On 2/27/07, Tekin S. removed_email_address@domain.invalid wrote:

It appears to me like init.rb is only getting run once during start-up.

This is definitely the case, and is typically why you don’t mix
modules from plugins into anything that will get reloaded. For
example, if you’re adding methods to all controllers, you should
include your module into ActionController::Base, not
ApplicationController. The same applies for the rest of your
application…


#13

I guess the neat fix would be to get engines to reload after each
request and not just in after_initialize()

Or to not modify another plugin’s model in an init.rb!!


#14

Of course. I need to rethink my architecture, specifically how my
plugins integrate together.

On the most simple level, I have a cms plugin that provides a generic
cms, and other plugins will add additional functionality to it
(catalogue/ecommerce, etc).

I basically need to tell my Page model in the cms plugin that it has
additional behaviours. At the moment, I simply have a class attribute
array called @behaviours which the other plugins are adding to in their
init.rb’s.

Obviously this is unworkable. Is there an obvious way to modify models
in other plugins in such a way that it get’s picked up on reloads?

My current thinking is that maybe the Page model should check for and
load any enhancements into itself, looking in each plugin…


#15

Really? You can’t mix a module from a plugin into your app - you
have to add it into Rails?? That just seems somehow wrong to me. I
thought I could mix a module in anywhere I want - if the main class
gets reloaded, why won’t it’s dependencies get reloaded as well? Is
that in general, or only with engine plugins?

In any case, I’m running into the same problem with ActiveRbac.
First run, everything works. Second run, methods for a model just
disappear. In order to create a model that doesn’t have to be
completely copied into the application to be overridden, they created
a model file that simply includes a mixin module with all the real
code. So to override the model in my app I create the model file,
include the mixin files, and my new functions.

The problem seems to be in one of the models that I don’t override.
First run everything is fine, second run non of it’s methods are
found. It’s not just the mixed in ones. I added a method straight
in to the model - it wasn’t available on the second run. Here is an
interesting error I get if I try to do a find on the model:

ArgumentError: A copy of ActiveRbacMixins::UserMixins::Core has been
removed from the module tree but is still active!

This worked fine in the previous Rails and Engines. So this is new
behavior. What’s the best way to fix it?


#16

I think my description may have not been very clear, there is infact
very little magic going on at all!

Each page has a behaviour attribute. For most pages, this is simply left
blank. If however, it is set to something, like for example, ‘contact’
or ‘news’, when the page is rendered, extra ‘stuff’ is added to the
rendered page using partials.

This all works fine, all I want is for the page model to have a list of
potential behaviours - nothing more than an array of strings - which is
what I need to add to from future plugins… Does that make sense?

I guess I could put this in the application somewhere, it just makes
sense for it to happen in the plugins.

Thanks for all the great work on engines by the way, it’s made it much
easier for us to share rails code between our projects, the way it
should be.


#17

Why create the model file at all in your app? Couldn’t you simply open
the module and override/add your functions?


#18

On 2/27/07, Tekin S. removed_email_address@domain.invalid wrote:

Obviously this is unworkable. Is there an obvious way to modify models
in other plugins in such a way that it get’s picked up on reloads?

This is one of the occasions where you might actually want to adopt
“less magic”. Is there any way you could architect it so each plugin
contributes a feature to a Page model within the application? I.E.

class Page < AR::Base
with_cms_features # from the cms plugin
and_versioning # from your versioning plugin, for instance
format_with_markdown # from your text processing plugin

# ...

end

The lesson learned from the login+user engine saga is: don’t try to
have plugins depend on other plugins. It gets way too complicated. Is
there any way you can avoid this? I appreciate that this might really
be the advice you need at the moment; if I think of anything tonight
I’ll fire of another mail to the list.


#19

On 2/27/07, Brett W. removed_email_address@domain.invalid wrote:

Really? You can’t mix a module from a plugin into your app - you
have to add it into Rails?? That just seems somehow wrong to me. I
thought I could mix a module in anywhere I want - if the main class
gets reloaded, why won’t it’s dependencies get reloaded as well? Is
that in general, or only with engine plugins?

You’re talking about a few things here. Imaging the situation:

app/models/a.rb:
class A
end

app/models/b.rb
class B
include C
end

vendor/plugins/monkey/lib/c.rb
module C
end

vendor/plugins/monkey/init.rb:
A.send(:include, C)

When your application first loads, init.rb is evaluated, and C is
included into A. C is also included into B, as per normal. At the end
of the request, A and B are dutifully unloaded to make way for any
changed versions. The next request causes Rails to try to find A
again, which it does in app/models/a.rb, and so it loads that file.
However, because vendor/plugins/monkey/init.rb is only ever evaluated
at the start of the request cycle, the “new” A will NOT include the
module C.

Since B explicitly refers to C, when Rails tries to reload B from
app/models/b.rb, it re-reads the class definition and includes C as
you’d expect.

The point here is that the only place where it states that A should
include C is a file that is only ever evaluated once, and that
information is lost when the target class is unloaded. This is what
plugin developers typically send includes to ActionController::Base,
not ApplicationController.

In any case, I’m running into the same problem with ActiveRbac.
First run, everything works. Second run, methods for a model just
disappear. In order to create a model that doesn’t have to be
completely copied into the application to be overridden, they created
a model file that simply includes a mixin module with all the real
code. So to override the model in my app I create the model file,
include the mixin files, and my new functions.

That’s basically what’s going on with class B, above.

The problem seems to be in one of the models that I don’t override.
First run everything is fine, second run non of it’s methods are
found. It’s not just the mixed in ones.

Just to be sure that I know what you’re referring to:

  • there is a model within a plugin
  • it has, say, method “do_something” defined directly in the class
  • on the second request, the model class is still available but the
    method has disappeared

Is that right?

I added a method straight
in to the model - it wasn’t available on the second run. Here is an
interesting error I get if I try to do a find on the model:

ArgumentError: A copy of ActiveRbacMixins::UserMixins::Core has been
removed from the module tree but is still active!

I can’t speak for ActiveRbac specifically, but I’ve never, ever
encountered this error. Very weird.

This worked fine in the previous Rails and Engines. So this is new
behavior. What’s the best way to fix it?

Since any version of ActiveRbac that was compatible with the engines
plugin 1.1.x releases isn’t compatible with the 1.2 release, you
should also note that ActiveRbac must’ve changed.

If you can reproduce this in a clean project using the engines plugin
and a toy plugin to contain the model, please zip it up and email it
directly to me. I’m very keen to resolve this, but I need to be able
to reproduce the behaviour…

Cheers


#20

Sorry, what I meant was; maybe having the second model file in your app
is what’s causing the problem?

http://api.rails-engines.org/classes/Engines/RailsExtensions/Dependencie
s.html

Why create the model file at all in your app? Couldn’t you simply
open the module and override/add your functions?