dekorator 1.0.0.pre.1 → 1.0.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +51 -0
  3. data/CHANGELOG.md +20 -4
  4. data/CODE_OF_CONDUCT.md +1 -1
  5. data/README.md +104 -38
  6. data/benchmarks/README.md +7 -0
  7. data/benchmarks/benchmark.rb +135 -0
  8. data/dekorator.gemspec +24 -11
  9. data/lib/dekorator.rb +67 -39
  10. data/lib/dekorator/rails/controller.rb +17 -0
  11. data/lib/dekorator/railtie.rb +9 -5
  12. data/lib/dekorator/version.rb +1 -1
  13. data/lib/generators/decorator_generator.rb +7 -0
  14. data/lib/generators/{decorator → dekorator/decorator}/USAGE +1 -1
  15. data/lib/generators/dekorator/decorator/decorator_generator.rb +17 -0
  16. data/lib/generators/{decorator → dekorator/decorator}/templates/decorator.rb +0 -0
  17. data/lib/generators/dekorator/{install_generator.rb → install/install_generator.rb} +8 -6
  18. data/lib/generators/rspec/decorator_generator.rb +2 -0
  19. data/lib/generators/test_unit/decorator_generator.rb +2 -0
  20. metadata +22 -36
  21. data/.editorconfig +0 -14
  22. data/.gitignore +0 -11
  23. data/.rspec +0 -3
  24. data/.rubocop.yml +0 -114
  25. data/.simplecov +0 -5
  26. data/.travis.yml +0 -37
  27. data/Appraisals +0 -17
  28. data/Gemfile +0 -22
  29. data/Gemfile.lock +0 -157
  30. data/bin/console +0 -16
  31. data/bin/setup +0 -8
  32. data/gemfiles/.bundle/config +0 -2
  33. data/gemfiles/rails_5.0.x.gemfile +0 -21
  34. data/gemfiles/rails_5.0.x.gemfile.lock +0 -207
  35. data/gemfiles/rails_5.1.x.gemfile +0 -21
  36. data/gemfiles/rails_5.1.x.gemfile.lock +0 -207
  37. data/gemfiles/rails_5.2.x.gemfile +0 -21
  38. data/gemfiles/rails_5.2.x.gemfile.lock +0 -207
  39. data/gemfiles/rails_6.0.x.gemfile +0 -21
  40. data/gemfiles/rails_6.0.x.gemfile.lock +0 -220
  41. data/lib/dekorator/decorators_helper.rb +0 -5
  42. data/lib/dekorator/rspec.rb +0 -0
  43. data/lib/generators/decorator/decorator_generator.rb +0 -11
  44. data/lib/generators/decorator/templates/decorator_test.rb +0 -1
@@ -1,23 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "dekorator/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "dekorator"
9
- spec.version = Dekorator::VERSION
10
- spec.authors = ["Nicolas Brousse"]
11
- spec.email = ["nicolas@pantographe.studio"]
12
-
13
- spec.summary = "An opinionated way of organizing model-view code in Ruby on Rails, based on decorators"
14
- spec.description = "An opinionated way of organizing model-view code in Ruby on Rails, based on decorators"
15
- spec.license = "MIT"
8
+ spec.name = "dekorator"
9
+ spec.version = Dekorator::VERSION
10
+ spec.authors = ["Pantographe"]
11
+ spec.email = ["oss@pantographe.studio"]
12
+
13
+ spec.summary = "An opinionated way of organizing model-view code in Ruby on Rails, based on decorators"
14
+ spec.description = "An opinionated way of organizing model-view code in Ruby on Rails, based on decorators"
15
+ spec.homepage = "http://komponent.io"
16
+ spec.license = "MIT"
17
+
18
+ spec.metadata = {
19
+ "homepage_uri" => "https://github.com/komposable/dekorator",
20
+ "changelog_uri" => "https://github.com/komposable/dekorator/blob/master/CHANGELOG.md",
21
+ "source_code_uri" => "https://github.com/komposable/dekorator",
22
+ "bug_tracker_uri" => "https://github.com/komposable/dekorator/issues",
23
+ }
16
24
 
17
25
  # Specify which files should be added to the gem when it is released.
18
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ `git ls-files -z`.split("\x0")
29
+ .reject do |f|
30
+ f.match(%r{^(test|spec|features|gemfiles|bin)/}) \
31
+ || %w[.editorconfig .gitignore .inch.yml .rspec .rubocop.yml .simplecov .travis.yml
32
+ .yardots Appraisals Gemfile Gemfile.lock].include?(f)
33
+ end
21
34
  end
22
35
 
23
36
  spec.require_paths = ["lib"]
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dekorator/version"
4
- require "active_support/core_ext/object/blank"
5
- require "active_support/core_ext/module/delegation"
4
+ require "delegate"
6
5
 
6
+ # :nodoc
7
7
  module Dekorator
8
+ # @api private
9
+ module Generators; end
10
+
8
11
  class DecoratorNotFound < ArgumentError; end
9
12
 
10
13
  # Base decorator.
@@ -19,11 +22,10 @@ module Dekorator
19
22
  #
20
23
  # @raise [DecoratorNotFound] if decorator is not found.
21
24
  def decorate(object_or_collection, with: nil)
22
- return object_or_collection if object_or_collection.blank?
23
-
24
- with = _guess_decorator(object_or_collection) if with.nil? && object_or_collection.present?
25
+ return object_or_collection if decorable_object?(object_or_collection)
25
26
 
26
- raise DecoratorNotFound, "Can't guess decorator for #{object_or_collection.class.name} object" if with.nil?
27
+ with ||= self if with != :__guess__ && self != Dekorator::Base
28
+ with = _guess_decorator(object_or_collection) if with.nil? || with == :__guess__
27
29
 
28
30
  object_or_collection = _decorate(object_or_collection, with: with)
29
31
 
@@ -40,82 +42,108 @@ module Dekorator
40
42
  # @option opts [Class] :with the decorator class to use. If empty a decorator will be guessed.
41
43
  #
42
44
  # @example Define an association to decorate
43
- # class UserDecorator < ApplicationDecorator
45
+ # class UserDecorator < Dekorator::Base
44
46
  # decorates_association :posts
45
47
  # end
46
48
  #
47
49
  # # A decorator could be precise
48
- # class UserDecorator < ApplicationDecorator
50
+ # class UserDecorator < Dekorator::Base
49
51
  # decorates_association :posts, PostDecorator
50
52
  # end
51
- def decorates_association(relation_name, with: nil)
53
+ def decorates_association(relation_name, with: :__guess__)
52
54
  relation_name = relation_name.to_sym
53
55
 
54
56
  define_method(relation_name) do
55
- association = __getobj__.public_send(relation_name)
56
-
57
- @decorated_associations[relation_name] ||= decorate(association, with: with)
57
+ @decorated_associations[relation_name] ||= decorate(__getobj__.public_send(relation_name), with: with)
58
58
  end
59
59
  end
60
60
 
61
+ # Guess and returns the decorated object class.
62
+ #
63
+ # @return [Class] the decorated object class.
61
64
  def base_class
62
- name.gsub("Decorator", "").safe_constantize
65
+ _safe_constantize(name.gsub("Decorator", ""))
63
66
  end
64
67
 
65
68
  private
66
69
 
67
- def _decorate(object_or_enumerable, with: nil)
68
- if defined?(ActiveRecord::Relation) && object_or_enumerable.is_a?(ActiveRecord::Relation)
69
- DecoratedEnumerableProxy.new(object_or_enumerable, with)
70
- elsif object_or_enumerable.is_a? Enumerable
71
- object_or_enumerable.map { |object| with.new(object) }
72
- else
70
+ def _decorate(object_or_enumerable, with:)
71
+ if !object_or_enumerable.is_a? Enumerable
73
72
  with.new(object_or_enumerable)
73
+ else
74
+ if defined?(ActiveRecord::Relation) && object_or_enumerable.is_a?(ActiveRecord::Relation)
75
+ Dekorator::DecoratedEnumerableProxy.new(with, object_or_enumerable)
76
+ else object_or_enumerable.is_a? Enumerable
77
+ object_or_enumerable.map { |object| _decorate(object, with: with) }
78
+ end
74
79
  end
75
80
  end
76
81
 
77
82
  def _guess_decorator(object_or_enumerable)
78
83
  object_or_enumerable = object_or_enumerable.first if object_or_enumerable.is_a? Enumerable
79
84
 
80
- "#{object_or_enumerable.class}Decorator".safe_constantize if object_or_enumerable.present?
85
+ _safe_constantize("#{object_or_enumerable.class}Decorator") \
86
+ || raise(DecoratorNotFound, "Can't guess decorator for #{object_or_enumerable.class.name} object")
81
87
  end
82
- end
83
88
 
84
- delegate :decorate, to: :class
89
+ def decorable_object?(object_or_collection)
90
+ (object_or_collection.respond_to?(:empty?) && object_or_collection.empty?) \
91
+ || !object_or_collection \
92
+ || object_or_collection.is_a?(Dekorator::Base) \
93
+ || (defined?(ActiveRecord::Relation) && object_or_collection.is_a?(Dekorator::DecoratedEnumerableProxy))
94
+ end
85
95
 
96
+ def _safe_constantize(class_name)
97
+ Object.const_get(class_name)
98
+ rescue NameError => _e
99
+ nil
100
+ end
101
+ end
102
+
103
+ # :nodoc
86
104
  def initialize(object)
87
105
  @decorated_associations = {}
88
106
 
89
107
  super(object)
90
108
  end
91
109
 
110
+ # :nodoc
111
+ def decorate(object_or_collection, with: :__guess__)
112
+ self.class.decorate(object_or_collection, with: with)
113
+ end
114
+
115
+ # Returns the decorated object.
116
+ #
117
+ # @return [Object] the decorated object.
92
118
  def object
93
119
  __getobj__
94
120
  end
121
+ end
95
122
 
96
- if defined?(ActiveRecord::Relation)
97
- class DecoratedEnumerableProxy < DelegateClass(ActiveRecord::Relation)
98
- include Enumerable
123
+ if defined?(ActiveRecord::Relation)
124
+ # DecoratedEnumerableProxy is strongly inspired from
125
+ # https://github.com/kiote/activeadmin-poro-decorator/blob/master/lib/activeadmin-poro-decorator.rb#L65
126
+ class DecoratedEnumerableProxy < DelegateClass(ActiveRecord::Relation)
127
+ include Enumerable
99
128
 
100
- delegate :as_json, :collect, :map, :each, :[], :all?, :include?,
101
- :first, :last, :shift, to: :decorated_collection
102
- delegate :each, to: :to_ary
129
+ delegate :as_json, :collect, :map, :each, :[], :all?, :include?,
130
+ :first, :last, :shift, to: :decorated_collection
131
+ delegate :each, to: :to_ary
103
132
 
104
- def initialize(collection, decorator_class)
105
- super(collection)
133
+ def initialize(decorator_class, collection)
134
+ super(collection)
106
135
 
107
- @decorator_class = decorator_class
108
- end
136
+ @decorator_class = decorator_class
137
+ end
109
138
 
110
- def wrapped_collection
111
- __getobj__
112
- end
139
+ def wrapped_collection
140
+ __getobj__
141
+ end
113
142
 
114
- def decorated_collection
115
- @decorated_collection ||= wrapped_collection.collect { |member| @decorator_class.decorate(member) }
116
- end
117
- alias to_ary decorated_collection
143
+ def decorated_collection
144
+ @decorated_collection ||= wrapped_collection.collect { |member| @decorator_class.new(member) }
118
145
  end
146
+ alias to_ary decorated_collection
119
147
  end
120
148
  end
121
149
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Dekorator
6
+ module Controller
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ helper_method :decorate
11
+ end
12
+
13
+ def decorate(object_or_collection, with: nil)
14
+ Dekorator::Base.decorate(object_or_collection, with: with)
15
+ end
16
+ end
17
+ end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dekorator/decorators_helper"
4
-
5
3
  module Dekorator
6
- class Railtie < Rails::Railtie
7
- initializer "decorators.helper" do |_app|
8
- ActionView::Base.send :include, DecoratorsHelper
4
+ require "dekorator/rails/controller"
5
+
6
+ class Railtie < ::Rails::Railtie
7
+ config.to_prepare do |_app|
8
+ ActionController::Base.include Dekorator::Controller
9
+ end
10
+
11
+ config.after_initialize do |app|
12
+ app.config.paths.add "app/decorators", eager_load: true
9
13
  end
10
14
  end
11
15
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dekorator
4
- VERSION = "1.0.0.pre.1".freeze
4
+ VERSION = "1.0.0".freeze
5
5
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
6
+ invoke "dekorator:decorator"
7
+ end
@@ -2,7 +2,7 @@ Description:
2
2
  Generate a fresh new decorator
3
3
 
4
4
  Example:
5
- `rails generate decorator User`
5
+ `rails generate dekorator:decorator User`
6
6
 
7
7
  User decorator.
8
8
  Decorator: app/decorators/user_decorator.rb
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Dekorator
6
+ module Generators
7
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def create_decorator
11
+ template "decorator.rb", File.join("app/decorators", class_path, "#{file_name}_decorator.rb")
12
+ end
13
+
14
+ hook_for :test_framework
15
+ end
16
+ end
17
+ end
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators"
4
+
3
5
  module Dekorator
4
6
  module Generators
5
- class InstallGenerator < Rails::Generators::Base
7
+ class InstallGenerator < ::Rails::Generators::Base
6
8
 
7
9
  def create_root_directory
8
- return if File.directory?(dekorator_root_directory)
9
-
10
10
  empty_directory(dekorator_root_directory)
11
+
12
+ concerns_directory = dekorator_root_directory.join("concerns")
13
+ empty_directory(concerns_directory)
14
+ create_file("#{concerns_directory}/.keep")
11
15
  end
12
16
 
13
17
  def create_application_decorator
14
- return if File.exist?(application_decorator_path)
15
-
16
18
  create_file application_decorator_path, <<-RUBY
17
19
  # frozen_string_literal: true
18
20
 
@@ -24,7 +26,7 @@ end
24
26
  protected
25
27
 
26
28
  def dekorator_root_directory
27
- Rails.root.join("app", "decorators")
29
+ Rails.root.join("app/decorators")
28
30
  end
29
31
 
30
32
  def application_decorator_path
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators"
4
+
3
5
  module Rspec
4
6
  module Generators
5
7
  class DecoratorGenerator < ::Rails::Generators::NamedBase
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators"
4
+
3
5
  module TestUnit
4
6
  module Generators
5
7
  class DecoratorGenerator < ::Rails::Generators::NamedBase
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dekorator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Nicolas Brousse
7
+ - Pantographe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-30 00:00:00.000000000 Z
11
+ date: 2019-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -115,55 +115,41 @@ dependencies:
115
115
  description: An opinionated way of organizing model-view code in Ruby on Rails, based
116
116
  on decorators
117
117
  email:
118
- - nicolas@pantographe.studio
118
+ - oss@pantographe.studio
119
119
  executables: []
120
120
  extensions: []
121
121
  extra_rdoc_files: []
122
122
  files:
123
- - ".editorconfig"
124
- - ".gitignore"
125
- - ".rspec"
126
- - ".rubocop.yml"
127
- - ".simplecov"
128
- - ".travis.yml"
129
- - Appraisals
123
+ - ".github/workflows/test.yml"
130
124
  - CHANGELOG.md
131
125
  - CODE_OF_CONDUCT.md
132
- - Gemfile
133
- - Gemfile.lock
134
126
  - LICENSE.txt
135
127
  - README.md
136
128
  - Rakefile
137
- - bin/console
138
- - bin/setup
129
+ - benchmarks/README.md
130
+ - benchmarks/benchmark.rb
139
131
  - dekorator.gemspec
140
- - gemfiles/.bundle/config
141
- - gemfiles/rails_5.0.x.gemfile
142
- - gemfiles/rails_5.0.x.gemfile.lock
143
- - gemfiles/rails_5.1.x.gemfile
144
- - gemfiles/rails_5.1.x.gemfile.lock
145
- - gemfiles/rails_5.2.x.gemfile
146
- - gemfiles/rails_5.2.x.gemfile.lock
147
- - gemfiles/rails_6.0.x.gemfile
148
- - gemfiles/rails_6.0.x.gemfile.lock
149
132
  - lib/dekorator.rb
150
- - lib/dekorator/decorators_helper.rb
133
+ - lib/dekorator/rails/controller.rb
151
134
  - lib/dekorator/railtie.rb
152
- - lib/dekorator/rspec.rb
153
135
  - lib/dekorator/version.rb
154
- - lib/generators/decorator/USAGE
155
- - lib/generators/decorator/decorator_generator.rb
156
- - lib/generators/decorator/templates/decorator.rb
157
- - lib/generators/decorator/templates/decorator_test.rb
158
- - lib/generators/dekorator/install_generator.rb
136
+ - lib/generators/decorator_generator.rb
137
+ - lib/generators/dekorator/decorator/USAGE
138
+ - lib/generators/dekorator/decorator/decorator_generator.rb
139
+ - lib/generators/dekorator/decorator/templates/decorator.rb
140
+ - lib/generators/dekorator/install/install_generator.rb
159
141
  - lib/generators/rspec/decorator_generator.rb
160
142
  - lib/generators/rspec/templates/decorator_spec.rb
161
143
  - lib/generators/test_unit/decorator_generator.rb
162
144
  - lib/generators/test_unit/templates/decorator_test.rb
163
- homepage:
145
+ homepage: http://komponent.io
164
146
  licenses:
165
147
  - MIT
166
- metadata: {}
148
+ metadata:
149
+ homepage_uri: https://github.com/komposable/dekorator
150
+ changelog_uri: https://github.com/komposable/dekorator/blob/master/CHANGELOG.md
151
+ source_code_uri: https://github.com/komposable/dekorator
152
+ bug_tracker_uri: https://github.com/komposable/dekorator/issues
167
153
  post_install_message:
168
154
  rdoc_options: []
169
155
  require_paths:
@@ -175,11 +161,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
175
161
  version: '2.3'
176
162
  required_rubygems_version: !ruby/object:Gem::Requirement
177
163
  requirements:
178
- - - ">"
164
+ - - ">="
179
165
  - !ruby/object:Gem::Version
180
- version: 1.3.1
166
+ version: '0'
181
167
  requirements: []
182
- rubygems_version: 3.0.1
168
+ rubygems_version: 3.0.6
183
169
  signing_key:
184
170
  specification_version: 4
185
171
  summary: An opinionated way of organizing model-view code in Ruby on Rails, based