 Rails Engines and extending the issue model
Rails Engines and extending the issue model
Added by Dario Nuevo over 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 over 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Eric Davis over 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_tabsruns (which is actually- UsersHelper#user_settings_tabs_with_rate_tab)
- UsersHelper#user_settings_tabs_with_rate_tabcalls the original- UsersHelper#user_settings_tabs(renamed to- UsersHelper#user_settings_tabs_without_rate_tab)
- The result then has a new Hash added to it
- UsersHelper#user_settings_tabs_with_rate_tabreturns 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 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Dario Nuevo over 16 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 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Chris Peterson over 16 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 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Eric Davis over 16 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 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Emilio González Montaña over 16 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 16 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Emilio González Montaña over 16 years ago
  
  Fixed adding:
require "application"
     RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 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_methodruns (which is actually- QueriesHelper#exist_method_with_my_method)
- QueriesHelper#exist_method_with_my_methodcall the original- QueriesHelper#exist_method(renamed to- QueriesHelper#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 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Eric Davis about 15 years ago
  
  Hung Nguyen wrote:
When I use method
alias_method_chainto 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 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 years ago
  
  Very useful, thanks!
Regards,
Hung Nguyen
     RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 years ago
  
  Eric Davis wrote:
Hung Nguyen wrote:
When I use method
alias_method_chainto 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 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Eric Davis about 15 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 15 years ago
    RE: Rails Engines and extending the issue model
    -
    Added by Hung Nguyen about 15 years ago
  
  Hi Eric,
Your information is very helpful. Thanks you so much.
Hung Nguyen