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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 14f002118bfbc2127bf674fc7488990d80a5152c
4
- data.tar.gz: 6e40b90bb6ffc7ba584147a93041025456216f52
2
+ SHA256:
3
+ metadata.gz: f803e15d40a85700faf6ceb443bea2fabc225c2d3503e32c99dcb4e54e7f75e3
4
+ data.tar.gz: 1c15042cec49c02013f05ff50c91335d00d6acff63b6ae51ccee2ea77c27ec88
5
5
  SHA512:
6
- metadata.gz: 5bd8f1c1bdc0cfe4734755e9a56fd1d20cbea6205319ac20c308360b95b5c74303d3f16cda0002458c2b33795ad6690e2eb8401ed99d35db4c3068f5630e6936
7
- data.tar.gz: cf5c094dedd2637ea7f23bdd3b0f131b2a33f0cc98e9ff2148433960e8305c2b1732d7cf6005bd82247d9cc8b8a71897c61e5bcd5f9d044a641d970f53c7fa58
6
+ metadata.gz: b48a0acdcdce16cd8f3fda7b56c95209af5e833502ea2ea5f9244e9e78bcf4255c376c21f50310631852a928279283d070bed45f618578867eb1edd45f6ddcaa
7
+ data.tar.gz: 8b959e33b9c70791a47ee759a6e9c3c877672cda9bb3ba5dc1de0ea4587be5d16bbccd0d0e980fc9ecfaae47adad0f616b35dda7f3c902b05cba064fda21fcc5
data/.gitignore CHANGED
@@ -4,3 +4,5 @@ Gemfile.lock
4
4
  gemfiles/*.lock
5
5
  pkg/*
6
6
  log
7
+ tmp
8
+ .byebug_history
data/.travis.yml CHANGED
@@ -1,33 +1,100 @@
1
1
  language: ruby
2
2
 
3
- script: bundle exec rake test
4
-
5
- rvm:
6
- - 2.0.0
7
- - 2.1.10
8
- - 2.2.6
9
- - 2.3.3
10
- - ruby-head
11
- gemfile:
12
- - gemfiles/Gemfile-rails.3.2.x
13
- - gemfiles/Gemfile-rails.4.0.x
14
- - gemfiles/Gemfile-rails.4.1.x
15
- - gemfiles/Gemfile-rails.4.2.x
16
- - gemfiles/Gemfile-rails.5.0.x
17
-
18
- sudo: false
3
+ addons:
4
+ chrome: stable
5
+
6
+ before_install:
7
+ - sudo apt update -qq
8
+ - sudo apt install -y chromium-chromedriver
9
+ - gem i bundler -v '<2'
10
+
11
+ cache: bundler
12
+
13
+ # rvm:
14
+ # - 2.6.7
15
+ # - 2.5.9
16
+ # - 2.4.10
17
+ # - 2.3.8
18
+ # - 2.2.10
19
+ # - 2.1.10
20
+ # - 2.0.0
21
+ # gemfile:
22
+ # - gemfiles/Gemfile-rails.5.2.x
23
+ # - gemfiles/Gemfile-rails.5.1.x
24
+ # - gemfiles/Gemfile-rails.5.0.x
25
+ # - gemfiles/Gemfile-rails.4.2.x
19
26
 
20
27
  matrix:
21
- exclude:
22
- - rvm: ruby-head
23
- gemfile: gemfiles/Gemfile-rails.3.2.x
24
- - rvm: ruby-head
25
- gemfile: gemfiles/Gemfile-rails.4.0.x
26
- - rvm: ruby-head
27
- gemfile: gemfiles/Gemfile-rails.4.1.x
28
- - rvm: 2.0.0
28
+ include:
29
+ - rvm: 3.0.1
30
+ gemfile: gemfiles/Gemfile-rails.6.1.x
31
+ - rvm: 3.0.1
32
+ gemfile: gemfiles/Gemfile-rails.6.1.x
33
+ env: API=1
34
+
35
+ - rvm: 2.7.3
36
+ gemfile: gemfiles/Gemfile-rails.6.1.x
37
+ - rvm: 2.7.3
38
+ gemfile: gemfiles/Gemfile-rails.6.1.x
39
+ env: API=1
40
+ - rvm: 2.7.3
41
+ gemfile: gemfiles/Gemfile-rails.6.0.x
42
+ - rvm: 2.7.3
43
+ gemfile: gemfiles/Gemfile-rails.6.0.x
44
+ env: API=1
45
+
46
+ - rvm: 2.6.7
47
+ gemfile: gemfiles/Gemfile-rails.6.1.x
48
+ - rvm: 2.6.7
49
+ gemfile: gemfiles/Gemfile-rails.6.1.x
50
+ env: API=1
51
+ - rvm: 2.6.7
52
+ gemfile: gemfiles/Gemfile-rails.6.0.x
53
+ - rvm: 2.6.7
54
+ gemfile: gemfiles/Gemfile-rails.6.0.x
55
+ env: API=1
56
+ - rvm: 2.6.7
57
+ gemfile: gemfiles/Gemfile-rails.5.2.x
58
+ - rvm: 2.6.7
59
+ gemfile: gemfiles/Gemfile-rails.5.2.x
60
+ env: API=1
61
+ - rvm: 2.6.7
62
+ gemfile: gemfiles/Gemfile-rails.5.1.x
63
+ - rvm: 2.6.7
64
+ gemfile: gemfiles/Gemfile-rails.5.1.x
65
+ env: API=1
66
+ - rvm: 2.6.7
29
67
  gemfile: gemfiles/Gemfile-rails.5.0.x
30
- - rvm: 2.1.10
68
+ - rvm: 2.6.7
31
69
  gemfile: gemfiles/Gemfile-rails.5.0.x
70
+ env: API=1
71
+
72
+ - rvm: 2.5.9
73
+ gemfile: gemfiles/Gemfile-rails.5.2.x
74
+
75
+ - rvm: 2.4.10
76
+ gemfile: gemfiles/Gemfile-rails.5.2.x
77
+
78
+ - rvm: 2.3.8
79
+ gemfile: gemfiles/Gemfile-rails.5.2.x
80
+ - rvm: 2.3.8
81
+ gemfile: gemfiles/Gemfile-rails.4.2.x
82
+
83
+ - rvm: 2.2.10
84
+ gemfile: gemfiles/Gemfile-rails.5.2.x
85
+
86
+ - rvm: 2.1.10
87
+ gemfile: gemfiles/Gemfile-rails.4.2.x
88
+
89
+ - rvm: 2.0.0
90
+ gemfile: gemfiles/Gemfile-rails.4.2.x
91
+
92
+ - rvm: ruby-head
93
+ gemfile: gemfiles/Gemfile-rails.edge
94
+ - rvm: ruby-head
95
+ gemfile: gemfiles/Gemfile-rails.edge
96
+ env: API=1
97
+
32
98
  allow_failures:
33
99
  - rvm: ruby-head
100
+ - gemfile: gemfiles/Gemfile-rails.edge
data/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ ## 1.4.0
2
+
3
+ * Decorate non-nil objects where `nil?` returns true, namely, ActionText::RichText body [@jamesbrooks]
4
+
5
+ ## 1.3.4
6
+
7
+ * Support Rails 6.1 [@y-yagi]
8
+
9
+ ## 1.3.3
10
+
11
+ * Fixed Ruby 2.7 keyword arguments warning [@pocke]
12
+
13
+ ## 1.3.2
14
+
15
+ * Fixed NameError on ActionController::API controllers without jbuilder enhancement [@kamillle]
16
+
17
+ ## 1.3.1
18
+
19
+ * Switched back from Ruby's `const_get` to Active Support `constantize` for fetching decorator modules, due to inability to properly detect namespaced decorator [@sinsoku]
20
+
21
+ ## 1.3.0
22
+
23
+ * Switched from Active Support `constantize` to Ruby's `const_get` when fetching decorator modules
24
+
25
+ * Switched `config` from ActiveSupport::Configurable to a simple Struct
26
+
27
+ * Association decoration now propagates from AssociationRelation to spawned Relations (e.g. `@post.comments.order(:id).each`)
28
+
29
+ * Dropped support for Rails 3.2, 4.0, and 4.1
30
+
31
+
32
+ ## 1.2.0
33
+
34
+ * Decorate values in Hash recursively [@FumiyaShibusawa]
35
+
36
+
37
+ ## 1.1.1
38
+
39
+ * Improved ActionController::API support for Rails 5.0.x [@frodsan]
40
+
41
+ * Fixed "NameError: undefined local variable or method `view_context'" with ActionController::API or when rendering in controllers
42
+
43
+
44
+ ## 1.1.0
45
+
46
+ * ActionController::API support [@frodsan]
47
+
48
+ * `ActiveDecorator::Decorator.instance.decorate` now returns the decorated object when the receiver was already a decorated object (it used to return `nil`) [@velonica1997]
49
+
50
+ * Update decorator_spec.rb syntax to respect RSpec 3 style [@memoht]
51
+
52
+ * Fixed namespace for TestUnit generator with some refactorings [@yhirano55]
data/Gemfile CHANGED
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  source 'https://rubygems.org'
3
4
 
4
5
  gemspec
5
-
6
- gem 'rails'
7
- gem 'test-unit-rails'
8
- gem 'capybara'
9
- gem 'sqlite3'
10
- gem 'jbuilder'
data/README.md CHANGED
@@ -1,22 +1,23 @@
1
1
  # ActiveDecorator [![Build Status](https://travis-ci.org/amatsuda/active_decorator.svg?branch=master)](https://travis-ci.org/amatsuda/active_decorator) [![Code Climate](https://codeclimate.com/github/amatsuda/active_decorator/badges/gpa.svg)](https://codeclimate.com/github/amatsuda/active_decorator)
2
2
 
3
- A simple and Rubyish view helper for Rails 3, Rails 4 and Rails 5. Keep your helpers and views Object-Oriented!
3
+ A simple and Rubyish view helper for Rails 4, Rails 5, and Rails 6. Keep your helpers and views Object-Oriented!
4
4
 
5
5
 
6
6
  ## Features ##
7
7
 
8
8
  1. automatically mixes decorator module into corresponding model only when:
9
- 1. passing a model or collection of models or an instance of ActiveRecord::Relation from controllers to views
10
- 2. rendering partials with models (using `:collection` or `:object` or `:locals` explicitly or implicitly)
9
+ 1. passing a model or collection of models or an instance of ActiveRecord::Relation from controllers to views
10
+ 2. rendering partials with models (using `:collection` or `:object` or `:locals` explicitly or implicitly)
11
+ 3. fetching already decorated Active Record model object's association
11
12
  2. the decorator module runs in the model's context. So, you can directly call any attributes or methods in the decorator module
12
13
  3. since decorators are considered as sort of helpers, you can also call any ActionView's helper methods such as `content_tag` or `link_to`
13
14
 
14
15
 
15
16
  ## Supported versions ##
16
17
 
17
- * Ruby 2.0.0, 2.1.x, 2.2.x, 2.3.x, and 2.4 (trunk)
18
+ * Ruby 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x, 2.7.x, 3.0.x, and 3.1 (trunk)
18
19
 
19
- * Rails 3.2.x, 4.0.x, 4.1.x, 4.2.x, 5.0, and 5.1 (edge)
20
+ * Rails 4.2.x, 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 (edge)
20
21
 
21
22
 
22
23
  ## Supported ORMs ##
@@ -34,79 +35,97 @@ You can use the generator for doing this ( `% rails g decorator user` )
34
35
 
35
36
  ## Examples ##
36
37
 
38
+ ### Auto-decorating via `render` ###
39
+
40
+ * Model
37
41
  ```ruby
38
- # app/models/user.rb
39
- class User < ActiveRecord::Base
40
- # first_name:string last_name:string website:string
42
+ class Author < ActiveRecord::Base
43
+ # first_name:string last_name:string
41
44
  end
45
+ ```
42
46
 
43
- # app/decorators/user_decorator.rb
44
- module UserDecorator
45
- def full_name
46
- "#{first_name} #{last_name}"
47
- end
48
-
49
- def link
50
- link_to full_name, website
47
+ * Controller
48
+ ```ruby
49
+ class AuthorsController < ApplicationController
50
+ def show(id) # powered by action_args
51
+ @author = Author.find id
51
52
  end
52
53
  end
54
+ ```
53
55
 
54
- # app/controllers/users_controller.rb
55
- class UsersController < ApplicationController
56
- def index
57
- @users = User.all
56
+ * Decorator
57
+ ```ruby
58
+ module AuthorDecorator
59
+ def full_name
60
+ "#{first_name} #{last_name}"
58
61
  end
59
62
  end
60
63
  ```
64
+
65
+ * View
61
66
  ```erb
62
- # app/views/users/index.html.erb
63
- <% @users.each do |user| %>
64
- <%= user.link %><br>
65
- <% end %>
67
+ <%# @author here is auto-decorated in between the controller and the view %>
68
+ <p><%= @author.full_name %></p>
66
69
  ```
67
70
 
68
- ## Decorating associated objects ##
69
-
70
- ActiveDecorator *does not* automatically decorate associated objects. We recommend that you pass associated objects to `render` when decorated associated objects are needed.
71
+ ### Auto-decorating via AR model's associated objects ###
71
72
 
73
+ * Models
72
74
  ```ruby
73
- # app/models/blog_post.rb
74
- class BlogPost < ActiveRecord::Base
75
- # published_at:datetime
75
+ class Author < ActiveRecord::Base
76
+ # name:string
77
+ has_many :books
76
78
  end
77
79
 
78
- # app/models/user.rb
79
- class User < ActiveRecord::Base
80
- has_many :blog_posts
80
+ class Book < ActiveRecord::Base
81
+ # title:string url:string
82
+ belongs_to :author
81
83
  end
84
+ ```
82
85
 
83
- # app/decorators/blog_post_decorator.rb
84
- module BlogPostDecorator
85
- def published_date
86
- published_at.strftime("%Y.%m.%d")
86
+ * Controller
87
+ ```ruby
88
+ class AuthorsController < ApplicationController
89
+ def show(id)
90
+ @author = Author.find id
87
91
  end
88
92
  end
93
+ ```
89
94
 
90
- # app/controllers/users_controller.rb
91
- class UsersController < ApplicationController
92
- def index
93
- @users = User.all
95
+ * Decorator
96
+ ```ruby
97
+ module BookDecorator
98
+ def link
99
+ link_to title, url
94
100
  end
95
101
  end
96
102
  ```
97
103
 
104
+ * View
98
105
  ```erb
99
- # app/views/users/index.html.erb
100
- <% @users.each do |user| %>
101
- <%= render partial: "blog_post", locals: { blog_posts: user.blog_posts } %><br>
106
+ <p><%= @author.name %></p>
107
+ <ul>
108
+ <% @author.books.order(:id).each do |book| %>
109
+ <%# `book` here is auto-decorated because @author is a decorated instance %>
110
+ <li><%= book.link %></li>
102
111
  <% end %>
112
+ </ul>
113
+ ```
103
114
 
104
- # app/views/users/_blog_post.html.erb
105
- <% blog_posts.each do |blog_post| %>
106
- <%= blog_post.published_date %>
107
- <% end %>
115
+ ### Using ActiveDecorator outside of Action View ###
116
+
117
+ Sometimes you may want to use decorators outside of Action View, for example,
118
+ for background tasks for ActiveJob.
119
+ For such use case, ActiveDecorator module provides `run_with` method
120
+ that takes some kind of Action View and a block.
121
+
122
+ ```ruby
123
+ ActiveDecorator::ViewContext.run_with ApplicationController.new.view_context do
124
+ ## perform some heavy jobs here
125
+ end
108
126
  ```
109
127
 
128
+
110
129
  ## Testing
111
130
 
112
131
  You can test a decorator using your favorite test framework by decorating the model instance with
@@ -115,7 +134,7 @@ You can test a decorator using your favorite test framework by decorating the mo
115
134
  ActiveDecorator::Decorator.instance.decorate(model_instance)
116
135
  ```
117
136
 
118
- Considering an `Organization` model and it's simple decorator:
137
+ Considering an `Organization` model and its simple decorator:
119
138
 
120
139
  ```ruby
121
140
  module OrganizationDecorator
data/Rakefile CHANGED
@@ -8,7 +8,11 @@ require 'rake/testtask'
8
8
 
9
9
  Rake::TestTask.new do |t|
10
10
  t.libs << "test"
11
- t.pattern = 'test/**/*_test.rb'
11
+ if ENV['API']
12
+ t.pattern = 'test/**/*_api_test.rb'
13
+ else
14
+ t.test_files = Dir['test/**/*_test.rb'] - Dir['test/**/*_api_test.rb']
15
+ end
12
16
  t.warning = true
13
17
  t.verbose = true
14
18
  end
@@ -1,5 +1,5 @@
1
- # -*- encoding: utf-8 -*-
2
1
  # frozen_string_literal: true
2
+
3
3
  $:.push File.expand_path("../lib", __FILE__)
4
4
  require "active_decorator/version"
5
5
 
@@ -9,11 +9,22 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Akira Matsuda"]
10
10
  s.email = ["ronnie@dio.jp"]
11
11
  s.homepage = 'https://github.com/amatsuda/active_decorator'
12
+ s.license = 'MIT'
12
13
  s.summary = %q{A simple and Rubyish view helper for Rails}
13
14
  s.description = %q{A simple and Rubyish view helper for Rails}
14
15
 
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
18
19
  s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'activesupport'
22
+
23
+ s.add_development_dependency 'test-unit-rails'
24
+ s.add_development_dependency 'selenium-webdriver'
25
+ s.add_development_dependency 'puma'
26
+ s.add_development_dependency 'capybara'
27
+ s.add_development_dependency 'sqlite3'
28
+ s.add_development_dependency 'rake'
29
+ s.add_development_dependency 'byebug'
19
30
  end
@@ -1,9 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'active_decorator', path: '..'
3
+ gemspec path: '..'
4
4
 
5
5
  gem 'rails', '~> 4.2.0'
6
- gem 'test-unit-rails'
7
- gem 'capybara'
8
- gem 'sqlite3'
9
- gem 'jbuilder'
6
+ gem 'sqlite3', '< 1.4'
7
+ gem 'nokogiri', '~> 1.6.0'
8
+ gem 'jbuilder' unless ENV['API']
@@ -1,9 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'active_decorator', path: '..'
3
+ gemspec path: '..'
4
4
 
5
5
  gem 'rails', '~> 5.0.0'
6
- gem 'test-unit-rails'
7
- gem 'capybara'
8
- gem 'sqlite3'
9
- gem 'jbuilder'
6
+ gem 'sqlite3', '< 1.4'
7
+ gem 'jbuilder' unless ENV['API']
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 5.1.0'
6
+ gem 'jbuilder' unless ENV['API']
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', git: 'https://github.com/rails/rails', branch: '5-2-stable'
6
+ gem 'webdrivers', '< 4'
7
+ gem 'jbuilder' unless ENV['API']
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 6.0.0'
6
+ gem 'webdrivers'
7
+ gem 'jbuilder' unless ENV['API']
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 6.1.0'
6
+ gem 'webdrivers'
7
+ gem 'rexml'
8
+ gem 'jbuilder' unless ENV['API']
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ git_source(:github) do |repo_name|
6
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
7
+ "https://github.com/#{repo_name}.git"
8
+ end
9
+
10
+ github 'rails/rails' do
11
+ gem 'rails'
12
+ end
13
+
14
+ unless ENV['API']
15
+ github 'rails/jbuilder' do
16
+ gem 'jbuilder'
17
+ end
18
+ end
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveDecorator
3
- include ActiveSupport::Configurable
4
+ def self.config
5
+ @_config ||= Struct.new(:decorator_suffix).new
6
+ end
7
+
8
+ def self.configure
9
+ yield config
10
+ end
4
11
 
5
12
  config.decorator_suffix = 'Decorator'
6
13
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A no-op module to mark an AR model instance as a "decorated" object.
4
+ module ActiveDecorator::Decorated
5
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'singleton'
3
4
  require 'active_decorator/helpers'
5
+ require 'active_decorator/decorated'
4
6
 
5
7
  module ActiveDecorator
6
8
  class Decorator
@@ -10,31 +12,63 @@ module ActiveDecorator
10
12
  @@decorators = {}
11
13
  end
12
14
 
15
+ # Decorates the given object.
16
+ # Plus, performs special decoration for the classes below:
17
+ # Array: decorates its each element
18
+ # Hash: decorates its each value
19
+ # AR::Relation: decorates its each record lazily
20
+ # AR model: decorates its associations on the fly
21
+ #
22
+ # Always returns the object, regardless of whether decorated or not decorated.
23
+ #
24
+ # This method can be publicly called from anywhere by `ActiveDecorator::Decorator.instance.decorate(obj)`.
13
25
  def decorate(obj)
14
- return if defined?(Jbuilder) && Jbuilder === obj
15
- return if obj.nil?
26
+ return obj if defined?(Jbuilder) && (Jbuilder === obj)
16
27
 
17
- if obj.is_a?(Array)
28
+ case obj
29
+ when Array
18
30
  obj.each do |r|
19
31
  decorate r
20
32
  end
21
- elsif defined?(ActiveRecord) && obj.is_a?(ActiveRecord::Relation)
22
- # don't call each nor to_a immediately
23
- if obj.respond_to?(:records)
24
- # Rails 5.0
25
- obj.extend ActiveDecorator::RelationDecorator unless obj.is_a? ActiveDecorator::RelationDecorator
26
- else
27
- # Rails 3.x and 4.x
28
- obj.extend ActiveDecorator::RelationDecoratorLegacy unless obj.is_a? ActiveDecorator::RelationDecoratorLegacy
33
+ when Hash
34
+ obj.each_value do |v|
35
+ decorate v
29
36
  end
37
+ when nil, true, false
38
+ obj
30
39
  else
31
- d = decorator_for obj.class
32
- return obj unless d
33
- obj.extend d unless obj.is_a? d
40
+ if defined?(ActiveRecord) && obj.is_a?(ActiveRecord::Relation)
41
+ # don't call each nor to_a immediately
42
+ if obj.respond_to?(:records)
43
+ # Rails 5.0
44
+ obj.extend ActiveDecorator::RelationDecorator unless obj.is_a? ActiveDecorator::RelationDecorator
45
+ else
46
+ # Rails 3.x and 4.x
47
+ obj.extend ActiveDecorator::RelationDecoratorLegacy unless obj.is_a? ActiveDecorator::RelationDecoratorLegacy
48
+ end
49
+ else
50
+ if defined?(ActiveRecord) && obj.is_a?(ActiveRecord::Base) && !obj.is_a?(ActiveDecorator::Decorated)
51
+ obj.extend ActiveDecorator::Decorated
52
+ end
53
+
54
+ d = decorator_for obj.class
55
+ return obj unless d
56
+ obj.extend d unless obj.is_a? d
57
+ end
34
58
  end
59
+
60
+ obj
61
+ end
62
+
63
+ # Decorates AR model object's association only when the object was decorated.
64
+ # Returns the association instance.
65
+ def decorate_association(owner, target)
66
+ owner.is_a?(ActiveDecorator::Decorated) ? decorate(target) : target
35
67
  end
36
68
 
37
69
  private
70
+ # Returns a decorator module for the given class.
71
+ # Returns `nil` if no decorator module was found.
38
72
  def decorator_for(model_class)
39
73
  return @@decorators[model_class] if @@decorators.key? model_class
40
74
 
@@ -57,19 +91,17 @@ module ActiveDecorator
57
91
  end
58
92
  end
59
93
 
94
+ # For AR 3 and 4
60
95
  module RelationDecoratorLegacy
61
96
  def to_a
62
- super.tap do |arr|
63
- ActiveDecorator::Decorator.instance.decorate arr
64
- end
97
+ ActiveDecorator::Decorator.instance.decorate super
65
98
  end
66
99
  end
67
100
 
101
+ # For AR 5+
68
102
  module RelationDecorator
69
103
  def records
70
- super.tap do |arr|
71
- ActiveDecorator::Decorator.instance.decorate arr
72
- end
104
+ ActiveDecorator::Decorator.instance.decorate super
73
105
  end
74
106
  end
75
107
  end