dekorator 1.0.0.pre.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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