active_decorator 0.8.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/.travis.yml +92 -25
  4. data/CHANGELOG.md +52 -0
  5. data/Gemfile +1 -6
  6. data/README.md +68 -49
  7. data/Rakefile +5 -1
  8. data/active_decorator.gemspec +15 -4
  9. data/gemfiles/Gemfile-rails.4.2.x +4 -5
  10. data/gemfiles/Gemfile-rails.5.0.x +3 -5
  11. data/gemfiles/Gemfile-rails.5.1.x +6 -0
  12. data/gemfiles/Gemfile-rails.5.2.x +7 -0
  13. data/gemfiles/Gemfile-rails.6.0.x +7 -0
  14. data/gemfiles/Gemfile-rails.6.1.x +8 -0
  15. data/gemfiles/Gemfile-rails.edge +18 -0
  16. data/lib/active_decorator/config.rb +8 -1
  17. data/lib/active_decorator/decorated.rb +5 -0
  18. data/lib/active_decorator/decorator.rb +52 -20
  19. data/lib/active_decorator/helpers.rb +21 -7
  20. data/lib/active_decorator/monkey/abstract_controller/rendering.rb +3 -0
  21. data/lib/active_decorator/monkey/action_controller/base/rescue_from.rb +5 -2
  22. data/lib/active_decorator/monkey/action_view/collection_renderer.rb +12 -0
  23. data/lib/active_decorator/monkey/action_view/object_renderer.rb +12 -0
  24. data/lib/active_decorator/monkey/action_view/partial_renderer.rb +22 -8
  25. data/lib/active_decorator/monkey/active_record/associations.rb +61 -0
  26. data/lib/active_decorator/railtie.rb +36 -2
  27. data/lib/active_decorator/version.rb +2 -1
  28. data/lib/active_decorator/view_context.rb +19 -21
  29. data/lib/active_decorator.rb +6 -1
  30. data/lib/generators/rails/USAGE +8 -0
  31. data/lib/generators/rails/decorator_generator.rb +4 -13
  32. data/lib/generators/rails/templates/decorator.rb +2 -0
  33. data/lib/generators/rspec/decorator_generator.rb +7 -4
  34. data/lib/generators/rspec/templates/decorator_spec.rb +4 -2
  35. data/lib/generators/test_unit/decorator_generator.rb +8 -4
  36. data/lib/generators/test_unit/templates/decorator_test.rb +4 -2
  37. metadata +133 -57
  38. data/gemfiles/Gemfile-rails.3.2.x +0 -11
  39. data/gemfiles/Gemfile-rails.4.0.x +0 -9
  40. data/gemfiles/Gemfile-rails.4.1.x +0 -9
  41. data/test/configuration_test.rb +0 -27
  42. data/test/controllers/fake_detection_test.rb +0 -9
  43. data/test/fake_app/app/views/authors/index.html.erb +0 -16
  44. data/test/fake_app/app/views/authors/show.html.erb +0 -2
  45. data/test/fake_app/app/views/authors/show.json.jbuilder +0 -3
  46. data/test/fake_app/app/views/book_mailer/thanks.text.erb +0 -1
  47. data/test/fake_app/app/views/books/_book.html.erb +0 -2
  48. data/test/fake_app/app/views/books/_book.json.jbuilder +0 -3
  49. data/test/fake_app/app/views/books/_book_locals.html.erb +0 -2
  50. data/test/fake_app/app/views/books/errata.html.erb +0 -1
  51. data/test/fake_app/app/views/books/error.html.erb +0 -1
  52. data/test/fake_app/app/views/books/index.html.erb +0 -4
  53. data/test/fake_app/app/views/books/purchase.html.erb +0 -1
  54. data/test/fake_app/app/views/books/show.html.erb +0 -4
  55. data/test/fake_app/app/views/movies/show.html.erb +0 -1
  56. data/test/fake_app/fake_app.rb +0 -198
  57. data/test/features/action_view_helpers_test.rb +0 -37
  58. data/test/features/controller_ivar_test.rb +0 -40
  59. data/test/features/jbuilder_test.rb +0 -15
  60. data/test/features/name_error_handling_test.rb +0 -17
  61. data/test/features/partial_test.rb +0 -28
  62. data/test/test_helper.rb +0 -30
@@ -1,16 +1,30 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # On the fly delegation from the decorator to the decorated object and the helpers.
2
4
  module ActiveDecorator
3
5
  module Helpers
4
6
  def method_missing(method, *args, &block)
5
7
  super
6
- rescue NoMethodError, NameError
7
- begin
8
- (view_context = ActiveDecorator::ViewContext.current).send method, *args, &block
9
- rescue NoMethodError
10
- raise NoMethodError, "undefined method `#{method}` for either #{self} or #{view_context}"
11
- rescue NameError
12
- raise NameError, "undefined local variable `#{method}` for either #{self} or #{view_context}"
8
+ rescue NoMethodError, NameError => e1
9
+ # the error is not mine, so just releases it as is.
10
+ raise e1 if e1.name != method
11
+
12
+ if (view_context = ActiveDecorator::ViewContext.current)
13
+ begin
14
+ view_context.send method, *args, &block
15
+ rescue NoMethodError => e2
16
+ raise e2 if e2.name != method
17
+
18
+ raise NoMethodError.new("undefined method `#{method}' for either #{self} or #{view_context}", method)
19
+ rescue NameError => e2
20
+ raise e2 if e2.name != method
21
+
22
+ raise NameError.new("undefined local variable `#{method}' for either #{self} or #{view_context}", method)
23
+ end
24
+ else # AC::API would have not view_context
25
+ raise e1
13
26
  end
14
27
  end
28
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
15
29
  end
16
30
  end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # A monkey-patch for Rails controllers/mailers that auto-decorates ivars
4
+ # that are passed to views.
2
5
  module ActiveDecorator
3
6
  module Monkey
4
7
  module AbstractController
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # A monkey-patch for Action Controller to pass the controller view_context over
4
+ # to `render` invocation in `rescue_from` block.
2
5
  module ActiveDecorator
3
6
  module Monkey
4
7
  module ActionController
5
8
  module Base
6
9
  def rescue_with_handler(*)
7
- ActiveDecorator::ViewContext.push(view_context)
10
+ ActiveDecorator::ViewContext.push(view_context) if defined?(view_context)
8
11
  super
9
12
  ensure
10
- ActiveDecorator::ViewContext.pop
13
+ ActiveDecorator::ViewContext.pop if defined?(view_context)
11
14
  end
12
15
  end
13
16
  end
@@ -0,0 +1,12 @@
1
+ module ActiveDecorator
2
+ module Monkey
3
+ module ActionView
4
+ module CollectionRenderer
5
+ def render_collection_with_partial(collection, *)
6
+ ActiveDecorator::Decorator.instance.decorate collection
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveDecorator
2
+ module Monkey
3
+ module ActionView
4
+ module ObjectRenderer
5
+ def render_object_with_partial(object, *)
6
+ ActiveDecorator::Decorator.instance.decorate object
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,20 +1,34 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # A monkey-patch for Action View `render :partial` that auto-decorates `locals` values.
2
4
  module ActiveDecorator
3
5
  module Monkey
4
6
  module ActionView
5
7
  module PartialRenderer
8
+ if Rails.version.to_f >= 6.1
9
+ def initialize(*)
10
+ super
11
+
12
+ @locals.each_value do |v|
13
+ ActiveDecorator::Decorator.instance.decorate v
14
+ end
15
+ end
16
+ end
17
+
6
18
  private
7
19
 
8
- def setup(*)
9
- super
20
+ if Rails.version.to_f < 6.1
21
+ def setup(*)
22
+ super
10
23
 
11
- @locals.values.each do |v|
12
- ActiveDecorator::Decorator.instance.decorate v
13
- end if @locals
14
- ActiveDecorator::Decorator.instance.decorate @object if @object && (@object != nil)
15
- ActiveDecorator::Decorator.instance.decorate @collection unless @collection.blank?
24
+ @locals.each_value do |v|
25
+ ActiveDecorator::Decorator.instance.decorate v
26
+ end if @locals
27
+ ActiveDecorator::Decorator.instance.decorate @object if @object
28
+ ActiveDecorator::Decorator.instance.decorate @collection unless @collection.blank?
16
29
 
17
- self
30
+ self
31
+ end
18
32
  end
19
33
  end
20
34
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A monkey-patch for Active Record that enables association auto-decoration.
4
+ module ActiveDecorator
5
+ module Monkey
6
+ module ActiveRecord
7
+ module Associations
8
+ module Association
9
+ def target
10
+ ActiveDecorator::Decorator.instance.decorate_association(owner, super)
11
+ end
12
+ end
13
+
14
+ if Rails.version.to_f < 5.1
15
+ module CollectionAssociation
16
+ private
17
+ def first_nth_or_last(*)
18
+ ActiveDecorator::Decorator.instance.decorate_association(owner, super)
19
+ end
20
+ end
21
+ end
22
+
23
+ module CollectionProxy
24
+ def take(*)
25
+ ActiveDecorator::Decorator.instance.decorate_association(@association.owner, super)
26
+ end
27
+
28
+ if Rails.version.to_f >= 5.1
29
+ def last(*)
30
+ ActiveDecorator::Decorator.instance.decorate_association(@association.owner, super)
31
+ end
32
+
33
+ private
34
+
35
+ def find_nth_with_limit(*)
36
+ ActiveDecorator::Decorator.instance.decorate_association(@association.owner, super)
37
+ end
38
+
39
+ def find_nth_from_last(*)
40
+ ActiveDecorator::Decorator.instance.decorate_association(@association.owner, super)
41
+ end
42
+ end
43
+ end
44
+
45
+ module CollectionAssociation
46
+ private
47
+
48
+ def build_record(*)
49
+ ActiveDecorator::Decorator.instance.decorate_association(@owner, super)
50
+ end
51
+ end
52
+ end
53
+
54
+ module AssociationRelation
55
+ def spawn(*)
56
+ ActiveDecorator::Decorator.instance.decorate_association(@association.owner, super)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'active_decorator/view_context'
3
- require 'rails'
4
2
 
5
3
  module ActiveDecorator
6
4
  class Railtie < ::Rails::Railtie
@@ -8,6 +6,14 @@ module ActiveDecorator
8
6
  ActiveSupport.on_load :action_view do
9
7
  require 'active_decorator/monkey/action_view/partial_renderer'
10
8
  ActionView::PartialRenderer.send :prepend, ActiveDecorator::Monkey::ActionView::PartialRenderer
9
+
10
+ if Rails.version.to_f >= 6.1
11
+ require 'active_decorator/monkey/action_view/collection_renderer'
12
+ ActionView::CollectionRenderer.send :prepend, ActiveDecorator::Monkey::ActionView::CollectionRenderer
13
+
14
+ require 'active_decorator/monkey/action_view/object_renderer'
15
+ ActionView::ObjectRenderer.send :prepend, ActiveDecorator::Monkey::ActionView::ObjectRenderer
16
+ end
11
17
  end
12
18
 
13
19
  ActiveSupport.on_load :action_controller do
@@ -17,17 +23,45 @@ module ActiveDecorator
17
23
  require 'active_decorator/monkey/action_controller/base/rescue_from'
18
24
  ActionController::Base.send :prepend, ActiveDecorator::Monkey::ActionController::Base
19
25
 
26
+ require 'active_decorator/view_context'
20
27
  ActionController::Base.send :include, ActiveDecorator::ViewContext::Filter
21
28
  end
22
29
 
30
+ if Rails::VERSION::MAJOR >= 5
31
+ ActiveSupport.on_load :action_controller do
32
+ if self == ActionController::API
33
+ require 'active_decorator/monkey/abstract_controller/rendering'
34
+ ::ActionController::API.send :prepend, ActiveDecorator::Monkey::AbstractController::Rendering
35
+
36
+ require 'active_decorator/monkey/action_controller/base/rescue_from'
37
+ ::ActionController::API.send :prepend, ActiveDecorator::Monkey::ActionController::Base
38
+ end
39
+ end
40
+ end
41
+
23
42
  ActiveSupport.on_load :action_mailer do
24
43
  require 'active_decorator/monkey/abstract_controller/rendering'
25
44
  ActionMailer::Base.send :prepend, ActiveDecorator::Monkey::AbstractController::Rendering
26
45
 
27
46
  if ActionMailer::Base.respond_to? :before_action
47
+ require 'active_decorator/view_context'
28
48
  ActionMailer::Base.send :include, ActiveDecorator::ViewContext::Filter
29
49
  end
30
50
  end
51
+
52
+ ActiveSupport.on_load :active_record do
53
+ require 'active_decorator/monkey/active_record/associations'
54
+ ActiveRecord::Associations::Association.send :prepend, ActiveDecorator::Monkey::ActiveRecord::Associations::Association
55
+
56
+ if Rails.version.to_f < 5.1
57
+ ActiveRecord::Associations::CollectionAssociation.send :prepend, ActiveDecorator::Monkey::ActiveRecord::Associations::CollectionAssociation
58
+ end
59
+
60
+ ActiveRecord::AssociationRelation.send :prepend, ActiveDecorator::Monkey::ActiveRecord::AssociationRelation
61
+
62
+ ActiveRecord::Associations::CollectionProxy.send :prepend, ActiveDecorator::Monkey::ActiveRecord::Associations::CollectionProxy
63
+ ActiveRecord::Associations::CollectionAssociation.send :prepend, ActiveDecorator::Monkey::ActiveRecord::Associations::CollectionAssociation
64
+ end
31
65
  end
32
66
  end
33
67
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveDecorator
3
- VERSION = '0.8.0'
4
+ VERSION = '1.4.0'
4
5
  end
@@ -1,18 +1,30 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # A module that carries the controllers' view_context to decorators.
2
4
  module ActiveDecorator
3
5
  module ViewContext
4
6
  class << self
5
7
  def current
6
- Thread.current[:active_decorator_view_contexts].last
8
+ view_context_stack.last
7
9
  end
8
10
 
9
11
  def push(view_context)
10
- Thread.current[:active_decorator_view_contexts] ||= []
11
- Thread.current[:active_decorator_view_contexts] << view_context
12
+ view_context_stack.push view_context
12
13
  end
13
14
 
14
15
  def pop
15
- Thread.current[:active_decorator_view_contexts].pop if Thread.current[:active_decorator_view_contexts]
16
+ view_context_stack.pop
17
+ end
18
+
19
+ def view_context_stack
20
+ Thread.current[:active_decorator_view_contexts] ||= []
21
+ end
22
+
23
+ def run_with(view_context)
24
+ push view_context
25
+ yield
26
+ ensure
27
+ pop
16
28
  end
17
29
  end
18
30
 
@@ -20,23 +32,9 @@ module ActiveDecorator
20
32
  extend ActiveSupport::Concern
21
33
 
22
34
  included do
23
- if Rails::VERSION::MAJOR >= 4
24
- around_action do |controller, blk|
25
- begin
26
- ActiveDecorator::ViewContext.push controller.view_context
27
- blk.call
28
- ensure
29
- ActiveDecorator::ViewContext.pop
30
- end
31
- end
32
- else
33
- around_filter do |controller, blk|
34
- begin
35
- ActiveDecorator::ViewContext.push controller.view_context
36
- blk.call
37
- ensure
38
- ActiveDecorator::ViewContext.pop
39
- end
35
+ around_action do |controller, blk|
36
+ ActiveDecorator::ViewContext.run_with(controller.view_context) do
37
+ blk.call
40
38
  end
41
39
  end
42
40
  end
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_decorator/version'
3
4
  require 'active_decorator/decorator'
4
- require 'active_decorator/railtie'
5
+ begin
6
+ require 'rails'
7
+ require 'active_decorator/railtie'
8
+ rescue LoadError
9
+ end
5
10
  require 'active_decorator/config'
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Stubs out a decorator module in app/decorators directory.
3
+
4
+ Examples:
5
+ `rails g decorator book`
6
+
7
+ This creates:
8
+ app/decorators/book_decorator.rb
@@ -1,19 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Rails
3
4
  module Generators
4
- class DecoratorGenerator < Rails::Generators::NamedBase
5
- source_root File.expand_path('../templates', __FILE__)
6
-
7
- desc <<DESC
8
- Description:
9
- Stubs out a decorator module in app/decorators directory.
10
-
11
- Examples:
12
- `rails g decorator book`
13
-
14
- This creates:
15
- app/decorators/book_decorator.rb
16
- DESC
5
+ class DecoratorGenerator < NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+ check_class_collision suffix: "Decorator"
17
8
 
18
9
  def create_decorator_file
19
10
  template 'decorator.rb', File.join('app/decorators', class_path, "#{file_name}_decorator.rb")
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module <%= class_name %>Decorator
2
4
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Rspec
3
- class DecoratorGenerator < ::Rails::Generators::NamedBase
4
- source_root File.expand_path('../templates', __FILE__)
4
+ module Generators
5
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
5
7
 
6
- def create_spec_file
7
- template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{file_name}_decorator_spec.rb")
8
+ def create_spec_file
9
+ template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{file_name}_decorator_spec.rb")
10
+ end
8
11
  end
9
12
  end
10
13
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require '<%= File.exists?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
2
4
 
3
- describe <%= singular_name.camelize %>Decorator do
4
- let(:<%= singular_name %>) { <%= class_name %>.new.extend <%= singular_name.camelize %>Decorator }
5
+ RSpec.describe <%= class_name %>Decorator do
6
+ let(:<%= singular_name %>) { <%= class_name %>.new.extend <%= class_name %>Decorator }
5
7
  subject { <%= singular_name %> }
6
8
  it { should be_a <%= class_name %> }
7
9
  end
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module TestUnit
3
- class DecoratorGenerator < ::Rails::Generators::NamedBase
4
- source_root File.expand_path('../templates', __FILE__)
4
+ module Generators
5
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+ check_class_collision suffix: "DecoratorTest"
5
8
 
6
- def create_test_file
7
- template 'decorator_test.rb', File.join('test/decorators', class_path, "#{file_name}_decorator_test.rb")
9
+ def create_test_file
10
+ template 'decorator_test.rb', File.join('test/decorators', class_path, "#{file_name}_decorator_test.rb")
11
+ end
8
12
  end
9
13
  end
10
14
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
- class <%= singular_name.camelize %>DecoratorTest < ActiveSupport::TestCase
5
+ class <%= class_name %>DecoratorTest < ActiveSupport::TestCase
4
6
  def setup
5
- @<%= singular_name %> = <%= class_name %>.new.extend <%= singular_name.camelize %>Decorator
7
+ @<%= singular_name %> = <%= class_name %>.new.extend <%= class_name %>Decorator
6
8
  end
7
9
 
8
10
  # test "the truth" do