Rails Engines and extending the issue model
Added by Dario Nuevo almost 16 years ago
Hi guys..
First: I'm a routined developer, but into Ruby since 3 days now.. So I'm a Ruby (+ Rails) n00b ;-)
Anyway, I managed to create a new plugin, use some hooks, override some views with the help of the Rails and Rails Engines documentation.. all well..
But now I'm in the situation that I want to alter the issue model. And as I've read, overriding models is such a thing which can be complicated.
The whole goal of the operation: I've got issues which estimated hours value should be the sum of all related item's estimated hours value. That's it.
But for that, I've got to modify the model to ensure that the estimated_hours value is consistend within Redmine, especially within the listings and edit view.
So I ask you guys: Do you have any hints how to realize this, how to handle model-altering in general? Best practices, anything? Even a link to some article/documentation would be great..
Regards
Replies (12)
RE: Rails Engines and extending the issue model - Added by Eric Davis almost 16 years ago
Dario Nuevo wrote:
But now I'm in the situation that I want to alter the issue model. And as I've read, overriding models is such a thing which can be complicated.
Yea, you rarely want to override a Model. Instead you should either add new methods to a Model or wrap an existing method.
The whole goal of the operation: I've got issues which estimated hours value should be the sum of all related item's estimated hours value. That's it.
But for that, I've got to modify the model to ensure that the estimated_hours value is consistend within Redmine, especially within the listings and edit view.
I do a lot of Model wrapping in my plugins. If you want to see some ideas on how to do it, checkout my Redmine plugins on my GitHub account
A quick example of adding a new method can be found on my Budget plugin. Here I add a new method to Issue called deliverable_subject
and also declare a relationship.
A quick example of wrapping an existing method can be found on my Rate plugin. Here the alias_method_chain
lets me hook into the UsersHelper and wrap the user_settings_tabs
method. So when the Redmine core calls user_settings_tabs
the codepath looks like:
- Redmine core calls
UsersHelper#user_settings_tabs
UsersHelper#user_settings_tabs
runs (which is actuallyUsersHelper#user_settings_tabs_with_rate_tab
)UsersHelper#user_settings_tabs_with_rate_tab
calls the originalUsersHelper#user_settings_tabs
(renamed toUsersHelper#user_settings_tabs_without_rate_tab
)- The result then has a new Hash added to it
UsersHelper#user_settings_tabs_with_rate_tab
returns the combined result to the Redmine core, which is then rendered
alias_method_chain
is a pretty advanced method but it's also really powerful. It took me a few months to really understand when it should be used. If you have any questions, post your code and I'll try to help you out.
Eric
RE: Rails Engines and extending the issue model - Added by Dario Nuevo over 15 years ago
Hi Eric
Thank you very much for the valuable information! ;) I'll try it out as soon I can get my hands on ruby again ;-) I'm occupied with php stuff at the moment..
Thanks for your time!
Regards
Dario
RE: Rails Engines and extending the issue model - Added by Chris Peterson over 15 years ago
Can the same method that you used to wrap an existing method in your rate plugin be used to wrap an existing method in a controller. I tried to use a similar semantic as your plugin, except use SortController instead of SortHelper. However I always end up with and error: uninitialized constant ApplicationController. I assume this has to do with the order of loading, ApplicationController is loaded with the application, which comes after the plugins. Is there anyway around this?
Thanks,
Chris
RE: Rails Engines and extending the issue model - Added by Eric Davis over 15 years ago
Try adding require_dependency 'application'
to the top of your file (or require_dependency 'application_controller'
, Rails decided to change it's name and I forget which is right in 2.2.x).
Eric
RE: Rails Engines and extending the issue model - Added by Emilio González Montaña over 15 years ago
I've the same problem, I can't extend a controller, but with models, helpers it works...
The error running with mongrel is:
/var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:279:in `load_missing_constant': uninitialized constant ApplicationController (NameError) from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:468:in `const_missing' from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:480:in `const_missing' from /var/www/redmine-0.8.3/app/controllers/projects_controller.rb:18 from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:216:in `load_without_new_constant_marking' from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:216:in `load_file' from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:355:in `new_constants_in' from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:215:in `load_file' from /var/www/redmine-0.8.3/vendor/rails/activesupport/lib/active_support/dependencies.rb:96:in `require_or_load' ... 33 levels... from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/../lib/mongrel/command.rb:212:in `run' from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:281 from /usr/bin/mongrel_rails:19:in `load' from /usr/bin/mongrel_rails:19
I'm trying to extend a controller to customize Redmine from a plugin...
This is part of the code of init.rb:
require "timelog_controller_patch"
And this is the timelog_controller_patch.rb:
require_dependency "projects_controller" module TimelogControllerPatch end ProjectsController.send(:include, TimelogControllerPatch)
Anyone has any clue about this?
RE: Rails Engines and extending the issue model - Added by Emilio González Montaña over 15 years ago
Fixed adding:
require "application"
RE: Rails Engines and extending the issue model - Added by Hung Nguyen about 14 years ago
Error: stack level too deep¶
When I use method alias_method_chain
to wrap an exist method with my method. It raises this error 'stack level too deep'.
module CoreQueriesHelperPatch def self.included(base) # :nodoc: base.class_eval do unloadable # Send unloadable so it will not be unloaded in development def column_content_with_column_content_patch(column, issue) temp = column_content_without_column_content_patch(column, issue) ...<do something else> end alias_method_chain :column_content, :column_content_patch end end end
Reason:¶
Follow steps above, I thinks my method runs like:- Redmine core calls
QueriesHelper#exist_method
QueriesHelper#exist_method
runs (which is actuallyQueriesHelper#exist_method_with_my_method
)QueriesHelper#exist_method_with_my_method
call the originalQueriesHelper#exist_method
(renamed toQueriesHelper#exist_method_without_my_method
)- Loop step 1...
Solution:
I try to check alias_method_chain
if it run.
module CoreQueriesHelperPatch def self.included(base) # :nodoc: @is_wrap = false base.class_eval do unloadable # Send unloadable so it will not be unloaded in development def column_content_with_column_content_patch(column, issue) temp = column_content_without_column_content_patch(column, issue) ...<do something else> end if !@is_wrap alias_method_chain :column_content, :column_content_patch @is_wrap = true end end end
Hope it's useful!
RE: Rails Engines and extending the issue model - Added by Eric Davis about 14 years ago
Hung Nguyen wrote:
When I use method
alias_method_chain
to wrap an exist method with my method. It raises this error 'stack level too deep'.
When using alias_method_chain, I found it easier to check if the module was added in init.rb. For example:
require 'dispatcher'
Dispatcher.to_prepare :redmine_contracts do
unless Query.included_modules.include? RedmineContracts::Patches::QueryPatch # <<<<<- Only include the module if it hasn't been already
Query.send(:include, RedmineContracts::Patches::QueryPatch)
end
end
http://github.com/edavis10/redmine_contracts/blob/master/init.rb#L68-70
Eric Davis
RE: Rails Engines and extending the issue model - Added by Hung Nguyen about 14 years ago
Very useful, thanks!
Regards,
Hung Nguyen
RE: Rails Engines and extending the issue model - Added by Hung Nguyen about 14 years ago
Eric Davis wrote:
Hung Nguyen wrote:
When I use method
alias_method_chain
to wrap an exist method with my method. It raises this error 'stack level too deep'.When using alias_method_chain, I found it easier to check if the module was added in init.rb. For example:
[...]
http://github.com/edavis10/redmine_contracts/blob/master/init.rb#L68-70
Eric Davis
After I read code in your init.rb
, I have some misunderstanding things.
1. Why don't you check all of plugin when they include patch, you just check Query, not Issue or Project. In my opinion, it will help to increase Redmine performance. When you do that, your plugin just include patch once, then they won't do again. Otherwise, each time action, helper.... was called, they also include patch again.
2. As I know, before you include patch in init.rb
. You need to require '<name of patch file>'
. But I don't see where you do that, or Redmine doesn't need? Can you explain it?
require 'redmine_contracts/patches/query_patch' require_dependency 'query' unless Query.included_modules.include? RedmineContracts::Patches::QueryPatch Query.send(:include, RedmineContracts::Patches::QueryPatch) end
Thanks!
Hung Nguyen
RE: Rails Engines and extending the issue model - Added by Eric Davis about 14 years ago
Hung Nguyen wrote:
1. Why don't you check all of plugin when they include patch, you just check Query, not Issue or Project. In my opinion, it will help to increase Redmine performance. When you do that, your plugin just include patch once, then they won't do again. Otherwise, each time action, helper.... was called, they also include patch again.
In production the Dispatcher is only run at startup so each model is only patched once. In development mode, the performance wouldn't matter because of the class loading and unloading done by Rails. So the extra check isn't needed since it's just extra code.
2. As I know, before you include patch in
init.rb
. You need torequire '<name of patch file>'
. But I don't see where you do that, or Redmine doesn't need? Can you explain it?
Rails will autoload classes it can find in lib/
. If you look at my patch names, they match the directory structure where the patches are stored. So I don't have to explicitly require
them.
Eric Davis
RE: Rails Engines and extending the issue model - Added by Hung Nguyen about 14 years ago
Hi Eric,
Your information is very helpful. Thanks you so much.
Hung Nguyen