While crafting the Passenger-Monit plugin, I thought that it'll be most appropriate to use the initializer, i.e.
module PassengerMonit
class Railtie < Rails::Railtie
initializer "my_plugin.some_init_task" do
# my initialization tasks
end
end
end
However, for some reason the code in the block was never executed.
I finally made a work-around with
config.before_initialize {}
but I'm curious, why the initializer wasn't executed. What could have prevented it from running?
In your gemfile, did you add
gem "Passenger-Monit"
Otherwise you need to put it in your load path and require it in your application.
http://www.igvita.com/2010/08/04/rails-3-internals-railtie-creating-plugins/
Good luck!
Related
In Rails 5, when a callback needs to cancel later callbacks, the recommended process is documented as "you should explicitly throw :abort."
My question is: how is it recommended that this exception be caught?
My current solution is to catch UncaughtThrowError in my ApplicationController - given the way it's documented, I thought this feature would trigger some magic in Rails or a Rack middleware to immediately move to the rendering (ActionView) phase.
Here are some examples of how to use the throw/catch in ruby.
For Rails, I think something like this will do the trick:
class SomeController
def some_method
if catch(:abort) { mode.save }
# success
else
# failure
end
end
end
You would probably have to write a method that overrides the underlying method for processing callbacks to have a catch :abort {} that handles it, which feels like too much magic.
I think using a rescue_from UncaughtThrowError in the controller you're throwing in seems a better approach vs. having it in the ApplicationController unless this is really common callback, in which case you probably want a Concern?
I went down this road for a day, and then decided to not use a callback for what I wanted to do, which eliminated the whole need for the callbacks, throws, rescues, and catches.
Still, I'm sure there are good cases for this approach, but it's not well documented as to how.
I have a class that looks like this:
module API
module MyNamespace
class Base < ActiveRecord::Base
end
end
end
module API
module MyNamespace
class MyClass < Base
end
end
end
When trying to upgrade from ruby 1.9.3 to 2.1.5, I started getting an error when executing the entire spec suite.
superclass must be a Class (Module given)
This is strange, because API::MyNamespace::Base is a class. Also if I run the tests as part of a smaller run (individual spec, or everything in the API namespace), then everything is fine.
I'm not really sure what to try here.
I think I hit this once and it's stupid. I think I changed it from actually using the words module API etc to just doing
class API::MyNamespace::MyClass
and it was happier with that (even though they're logically equivalent)
[This is a follow-up question to "How I can modularize Rails model?"]
Is there anyway to organize classes in app/models directory of Rails?
Do I have to use single top-level namespace for all of the classes?
Initial motivation is, I want place business logic classes which do not inherited from ActiveRecord::Base into app/models directory. Searching this site reveals many answers which recommend to place business logics classes in the app/models directory. I posted a different question, and got recommendation that it is possible to place such classes into lib directory.
Now I'm curious. I'd like to place these classes into different namespace and directory in apps/models directory which recommended by others. Is it possible?
Actually, I experiment that, but it seems to me that is what rails is not expected. If I create such a class (like SomeModName::ClassInMod in some_mod_name/class_in_mod.rb ) it does not get loaded. Also, I defined constants inside the module. Since they're not loaded, I can't use them. Actually, with rspec it work without problem, but with rails server, the class does not loaded. I'm sure it is related to autoloading issue.
In addition to the classes mentioned above, classes inherited from ActiveRecord::Base can be placed into some namespaces inside a module. I'm curious whether this work well or not too.
So the question in other words: can I make rails happy by configuring these files to be loaded, or is not the way rails designed?
Yes, you can define an ActiveRecord class in a module. The easy way is to use the generator:
./script/rails generate model logic/phase_logic
./script/rails generate model logic/evaluation_logic
Observe, that Rails will create additionally a file with the definition of the module. In this case:
# app/models/logic.rb
module Logic
...
end
# app/models/logic/phase_logics.rb
class Logic::PhaseLogic < ActiveRecord::Base
...
end
# app/models/logic/evaluation_logics.rb
class Logic::EvaluationLogic < ActiveRecord::Base
...
end
Your problems with constants defined in the module were caused by the fact, that you defined the constants in the definition module "wrapped" around only one model, from the two you have created. A very important part in understanding ruby (and Rails) - especially for people who have strong background in compiled languages - is to remember that there is no compilation phase, so the definition of some class is read and executed only when that specific class is used. Sometimes a week after the application server has been started and served thousands of requests.
Thus, as I said in the previous question's answer, the problem with autoloading was that sometimes the definition of the constants was read after the definition which was trying to use them. The order was random - if the first used object happened to be EvaluationLogic, then the error emerged. It the first object happened to be PhaseLogic, everything was fine.
Now, when you have a file for the module itself, and define constants in that file (app/models/logic.rb), autoloading will be able to find and execute the definitions before any class starts to use them. I hope everything will be OK.
UPDATE
I wrongly checked the edgerails guide instead of the currently correct Rails 3 guide (which has no mention of after_initialize). Not sure why the edgerails guide is "incorrect" though - I thought edgerails guide was supposed to be the latest up-to-date guide?
I'm leaving this question as-is just in case someone comes looking for the same "problem".
Macro-style call to after_initialize is the way to go.
Should after_initialize be used as method or macro-style call ?
This works, but gives a deprecation warning:
def after_initialize
logger.info "Called after_initialize"
end
DEPRECATION WARNING: Base#after_initialize has been deprecated,
please use Base.after_initialize :method instead.
This works, and there is no warning:
after_initialize :do_this_after_initialize
def do_this_after_initialize
logger.info "Called after_initialize"
end
But the Active Record Validations and Callbacks Guide in 10.4 after_initialize and after_find says:
...If you try to register
after_initialize or after_find using
macro-style class methods, they will
just be ignored. This behaviour is
due to performance reasons, since
after_initialize and after_find will
both be called for each record found
in the database, significantly slowing
down the queries...
So that means that the macro-style usage is inefficient versus the method-style way?
(I guess the guide is wrong, 'cos code is king :D)
Another Update
A commit here from January 28, 2011, suggests that the correct way is still to use the macro-style call, not a def after_initialize.
The call backs should be used as macro style in your model: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
I'm trying to extract some common code into a gem.
I'm thinking that acts_as_somethingis a good strategy for simple re-use.
Is there a good tutorial that discusses this for rails3 gems? I've found several that discuss rails2 (such as http://guides.rubyonrails.org/plugins.html) but that is specific to rails2
here are some of the tutorials/blogs I've already read:
http://guides.rubyonrails.org/plugins.html
http://www.thoughtsincomputation.com/categories/coding-gems-for-rails-3-tutorials (this is an excellent start but doesn't cover the act_as_* issue or controllers)
thanks
UPDATE: I've added a blog post based on this answer, but with much more detail: http://thoughtsincomputation.com/posts/coding-an-acts_as-gem-for-rails-3
--
I'm not aware of another tutorial source off the top of my head, but here are some general tips.
Rails 3 makes use of a really useful feature called Railtie - see http://api.rubyonrails.org/classes/Rails/Railtie.html .
So, if I were implementing an acts_as_* gem, I'd start there. My railtie might look something like:
# lib/acts_as_awesome/railtie.rb
require 'rails'
require 'acts_as_awesome'
module ActsAsAwesome
class Railtie < Rails::Railtie
config.to_prepare do
ApplicationController.send(:extend, ActsAsAwesome::Hook)
end
end
end
and the ActsAsAwesome::Hook code:
# lib/acts_as_awesome/hook.rb
module ActsAsAwesome::Hook
def acts_as_awesome(*args)
options = args.extract_options!
# do the things that make the controller awesome.
include ActsAsAwesome::InstanceMethods
before_filter :an_awesome_filter
end
end
I feel the concepts here are sound and have used similar processes before. Basically, it would tell Rails to execute the to_prepare block once during production and before each request in development (we want that because ApplicationController will be reloaded at those times, potentially wiping out our hook method); and the hook is just that: it adds a hook to all controllers (or rather, all controllers that extend ApplicationController) to allow the user to introduce the real "Awesome" code into their controllers without otherwise affecting controllers that don't need it.
The #acts_as_awesome hook doesn't, in itself, convey the Awesome functionality. That's because not all controllers might need this functionality. Instead, the method is responsible for introducing the real awesome stuff, via the ActsAsAwesome::InstanceMethods module. This way, the user only gets the Awesome functionality if they explicitly call the acts_as_awesome method. It also adds a before filter to the controller to demonstrate that the code in this method would be evaluated exactly the same as if it were in the target controller class itself.
This technique should work exactly the same if you're targeting models instead of controllers: just inject your hook into ActiveRecord::Base. As AR:B is only loaded at Rails boot, you should probably be able to put that into an initializer (refer to the Railtie docs), but I reserve the right to be mistaken here.
A gotcha concerning the railtie: the documentation reads as if it should have been autodetected, but I often have problems with this. To get around it, simply require the railtie from your gem's main source file (in the above example, that would be lib/acts_as_awesome.rb).
You can see the entire ActsAsAwesome source in all its glory at my github account:
http://github.com/sinisterchipmunk/acts_as_awesome
I hope this is helpful. Your question was somewhat high-level so a high-level response is the best I can do.
-Colin MacKenzie IV
http://thoughtsincomputation.com
#sinisterchipmnk