Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added 2 strategies for extending engine models

  • Loading branch information...
commit 890b9dd4439986e306ec4fc0067a00effa606204 1 parent c12024b
Weston Platter authored July 22, 2012

Showing 1 changed file with 78 additions and 1 deletion. Show diff stats Hide diff stats

  1. 79  guides/source/engines.textile
79  guides/source/engines.textile
Source Rendered
@@ -657,6 +657,84 @@ h3. Improving engine functionality
657 657
 
658 658
 This section looks at overriding or adding functionality to the views, controllers and models provided by an engine.
659 659
 
  660
+h4. Overriding Models
  661
+
  662
+Engine Models can be extended by (1) implementing decorators, or (2) including modules.
  663
+
  664
+h5. Decorators
  665
+
  666
+Decorators extends Engine's model classes in the main application by open classing Engine models at run time execution.
  667
+
  668
+<ruby>
  669
+# MyApp/app/decorators/models/blorgh/post_decorator.rb
  670
+
  671
+Blorgh::Post.class_eval do
  672
+  def time_since_created
  673
+    Time.current - created_at
  674
+  end
  675
+end
  676
+</ruby>
  677
+
  678
+h5. Modules
  679
+
  680
+The other strategy is to create modules within the Engine holding all the models' code and include these in the respective Engine's model classes. Thus the Engine's model classes contain a mere include line referencing the respective module.
  681
+
  682
+Engine models can be overriden in the main application by creating a file with the Engine's same namespace and including the module originally referenced in the Engine's model class. ["**ActiveSupport::Concern**":http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html] helps manage the correct ordering of module dependencies at run time (it's worth it to reach the linked documentation).
  683
+
  684
+<ruby>
  685
+# MyApp/app/models/blorgh/post.rb
  686
+# overrides Blorgh's original Post model
  687
+
  688
+class Blorgh::Post < ActiveRecord::Base
  689
+  include Blorgh::Concerns::Models::Post
  690
+
  691
+  def time_since_created
  692
+    Time.current - created_at
  693
+  end
  694
+end
  695
+
  696
+
  697
+# Blorgh/app/models/post.rb
  698
+# this class is overriden by the MyApp Post model
  699
+
  700
+class Post < ActiveRecord::Base
  701
+  include Blorgh::Concerns::Models::Post
  702
+end
  703
+
  704
+
  705
+# Blorgh/app/concerns/models/post
  706
+
  707
+module Blorg::Concerns::Models::Post
  708
+  extend ActiveSupport::Concern
  709
+  
  710
+  # 'included do' causes the code within to be evaluated in the conext
  711
+  #   where it is included, rather be executed in the module's context.
  712
+  included do
  713
+    attr_accessor :author_name
  714
+    belongs_to :author, :class_name => "User"
  715
+     
  716
+    before_save :set_author
  717
+     
  718
+    private
  719
+    
  720
+    def set_author
  721
+      self.author = User.find_or_create_by_name(author_name)
  722
+    end
  723
+  end
  724
+
  725
+  # methods defined here will be instance methods by default
  726
+  def some_method
  727
+    'some method string'
  728
+  end
  729
+
  730
+  module ClassMethods
  731
+    def some_class_method
  732
+      'some class method string'
  733
+    end
  734
+  end
  735
+end
  736
+</ruby>
  737
+
660 738
 h4. Overriding views
661 739
 
662 740
 When Rails looks for a view to render, it will first look in the +app/views+ directory of the application. If it cannot find the view there, then it will check in the +app/views+ directories of all engines which have this directory.
@@ -772,7 +850,6 @@ s.add_dependency "moo"
772 850
 To specify a dependency that should only be installed as a development
773 851
 dependency of the application, specify it like this:
774 852
 
775  
-<ruby>
776 853
 s.add_development_dependency "moo"
777 854
 </ruby>
778 855
 

0 notes on commit 890b9dd

Ryan Bigg

Why are you overriding Blorgh's original post model? That seems like a Bad Idea(tm).

You would be re-defining the class here and therefore completely disregarding any methods that may be in the engine's class.

What I would recommend doing is going with the decorator pattern continuously, and showing that you can just use a decorator or separate the logic out into sensible components (modules) and include them using a decorator. I'd prefer the module approach because when want to know where the method is located, it will declare that it's from that module:

module Bar
  def a
    puts "a"
  end
end

class Foo
  include Bar
end

p Foo.new.method(:a)

Outputs: #<Method: Foo(Bar)#a> indicating that the method "a" is provided by the module Bar which is included into the class Foo.

So yeah, I think decorators with some modules would be the best way to go here.

Weston Platter

I think it is important to give the developer the ability to redefine methods. Why? I've worked with Spree a bit, and we've overriden controllers and models to implement client required logic. For example,

module Bar
  def a
    puts "a"
  end
end

class Foo
  include Bar
  def a
    "a prime"
  end
end

# Foo.new.method(:a)
# => #<Method: Foo#a> 

# f = Foo.new
# f.a
# => a prime

I still think the extended model file should be in the MyApp/app/models folder for initialization purposes.

Please sign in to comment.
Something went wrong with that request. Please try again.