draper 3.1.0 → 4.0.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +55 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +11 -2
- data/README.md +10 -6
- data/bin/bundle +114 -0
- data/bin/rake +29 -0
- data/draper.gemspec +3 -2
- data/lib/draper/automatic_delegation.rb +4 -2
- data/lib/draper/collection_decorator.rb +3 -2
- data/lib/draper/decorator.rb +8 -6
- data/lib/draper/delegation.rb +1 -1
- data/lib/draper/helper_proxy.rb +2 -1
- data/lib/draper/lazy_helpers.rb +1 -1
- data/lib/draper/query_methods.rb +6 -2
- data/lib/draper/test_case.rb +1 -1
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context/build_strategy.rb +1 -1
- data/lib/draper/view_helpers.rb +7 -4
- data/lib/draper.rb +2 -0
- data/spec/draper/collection_decorator_spec.rb +5 -4
- data/spec/draper/decorator_spec.rb +33 -1
- data/spec/draper/draper_spec.rb +1 -0
- data/spec/draper/query_methods_spec.rb +35 -4
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +2 -0
- data/spec/dummy/config/storage.yml +7 -0
- data/spec/generators/controller/controller_generator_spec.rb +1 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +1 -1
- data/spec/integration/integration_spec.rb +1 -0
- data/spec/spec_helper.rb +7 -0
- metadata +43 -7
- data/.travis.yml +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b009279cf28737a6fcf4027fc26f3dc2a955741c7c5e5d2220c8dcb383490e3
|
4
|
+
data.tar.gz: 22455a3a7126650483af269d6a0e432a5a4465a59c762518ce78f40bcecb4286
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fd6c0b87227c23320e9ee211b4b2001c81cbf76d103a12e5076f427954febdb2d0366d5182f16b27f5155717ad878f08671098f8b1f9986c137ec053e1637bf
|
7
|
+
data.tar.gz: 260d4eccd57c623c966422cd256ab1485e1ce4bbaea05d2c6f615cdb644ab0147f00e8917b8165eba78651f279b66cde0a13da38b1c5cc9dc6a0e49822db2361
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
name: CI
|
3
|
+
|
4
|
+
on:
|
5
|
+
- push
|
6
|
+
- pull_request
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
rspec:
|
10
|
+
runs-on: ubuntu-20.04
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
ruby:
|
15
|
+
- '3.0'
|
16
|
+
- '2.7'
|
17
|
+
- '2.6'
|
18
|
+
- '2.5'
|
19
|
+
- '2.4'
|
20
|
+
|
21
|
+
services:
|
22
|
+
mongodb:
|
23
|
+
image: mongo
|
24
|
+
ports:
|
25
|
+
- 27017:27017
|
26
|
+
|
27
|
+
steps:
|
28
|
+
- name: Checkout
|
29
|
+
uses: actions/checkout@v2
|
30
|
+
|
31
|
+
- name: Setup Ruby
|
32
|
+
uses: ruby/setup-ruby@v1
|
33
|
+
with:
|
34
|
+
ruby-version: ${{ matrix.ruby }}
|
35
|
+
|
36
|
+
- name: Setup Ruby cache
|
37
|
+
uses: actions/cache@v2
|
38
|
+
with:
|
39
|
+
path: vendor/bundle
|
40
|
+
key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
|
41
|
+
restore-keys: |
|
42
|
+
${{ runner.os }}-gems-${{ matrix.ruby }}-
|
43
|
+
|
44
|
+
- name: Bundle
|
45
|
+
run: |
|
46
|
+
gem install bundler
|
47
|
+
bundle config path vendor/bundle
|
48
|
+
bundle install --jobs 4 --retry 3
|
49
|
+
|
50
|
+
- name: RSpec & publish code coverage
|
51
|
+
uses: paambaati/codeclimate-action@v2.7.5
|
52
|
+
env:
|
53
|
+
CC_TEST_REPORTER_ID: b7ba588af2a540fa96c267b3655a2afe31ea29976dc25905a668dd28d5e88915
|
54
|
+
with:
|
55
|
+
coverageCommand: bin/rake
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,41 @@
|
|
1
1
|
# Draper Changelog
|
2
2
|
|
3
|
+
## 4.0.2 - 2021-05-27
|
4
|
+
|
5
|
+
### Fixes
|
6
|
+
* Fix kwargs usage for Ruby 3 compatibility [#885](https://github.com/drapergem/draper/pull/885)
|
7
|
+
* Fix ruby warnings for "ambiguous first argument" [#881](https://github.com/drapergem/draper/pull/881)
|
8
|
+
* Fix rake warnings in CI [#897](https://github.com/drapergem/draper/pull/897)
|
9
|
+
* Fix decoration spec [#895](https://github.com/drapergem/draper/pull/895)
|
10
|
+
|
11
|
+
### Other Changes
|
12
|
+
* Migration from Travis CI to GitHub Actions [#893](https://github.com/drapergem/draper/pull/893), [#896](https://github.com/drapergem/draper/pull/896), [#903](https://github.com/drapergem/draper/pull/903)
|
13
|
+
|
14
|
+
## 4.0.1 - 2020-03-25
|
15
|
+
|
16
|
+
### Fixes
|
17
|
+
* Check only object's private methods when preventing delegation [#875](https://github.com/drapergem/draper/pull/875)
|
18
|
+
|
19
|
+
### Other Changes
|
20
|
+
* Use `alias` over `alias_method` [#877](https://github.com/drapergem/draper/pull/877)
|
21
|
+
|
22
|
+
## 4.0.0 - 2020-02-05
|
23
|
+
|
24
|
+
### Breaking Changes
|
25
|
+
* Drop support for Ruby < 2.4 [#852](https://github.com/drapergem/draper/pull/852), [#872](https://github.com/drapergem/draper/pull/872)
|
26
|
+
* Don't delegate public methods overridden by a private method in the decorator [#849](https://github.com/drapergem/draper/pull/849)
|
27
|
+
|
28
|
+
### Fixes
|
29
|
+
* Add preservation of decorator options on QueryMethods [#868](https://github.com/drapergem/draper/pull/868)
|
30
|
+
* Add `#respond_to_missing?` to `CollectionDecorator` so it correctly responds to ORM methods [#850](https://github.com/drapergem/draper/pull/850)
|
31
|
+
* Fix deprecation warning with the new Rails 6 `ActionView::Base` constructor [#866](https://github.com/drapergem/draper/pull/866)
|
32
|
+
* Fix deprecation warning with Ruby 2.7 [#870](https://github.com/drapergem/draper/pull/870)
|
33
|
+
|
34
|
+
### Other Changes
|
35
|
+
* Add SimpleCov for code coverage analysis [#851](https://github.com/drapergem/draper/pull/851)
|
36
|
+
* Update RSpec syntax in the README [#855](https://github.com/drapergem/draper/pull/855)
|
37
|
+
* Update continuous integration configuration [#861](https://github.com/drapergem/draper/pull/861), [#862](https://github.com/drapergem/draper/pull/862), [#863](https://github.com/drapergem/draper/pull/863), [#872](https://github.com/drapergem/draper/pull/872)
|
38
|
+
|
3
39
|
## 3.1.0
|
4
40
|
* Rails 6 support [#841](https://github.com/drapergem/draper/pull/841)
|
5
41
|
* Include ORM query methods in `CollectionDecorator` (e.g. `includes`) [#845](https://github.com/drapergem/draper/pull/845)
|
data/Gemfile
CHANGED
@@ -3,7 +3,11 @@ source "https://rubygems.org"
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
platforms :ruby do
|
6
|
-
|
6
|
+
if RUBY_VERSION >= "2.5.0"
|
7
|
+
gem 'sqlite3'
|
8
|
+
else
|
9
|
+
gem 'sqlite3', '~> 1.3.6'
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
platforms :jruby do
|
@@ -11,5 +15,10 @@ platforms :jruby do
|
|
11
15
|
gem "activerecord-jdbcsqlite3-adapter"
|
12
16
|
end
|
13
17
|
|
14
|
-
|
18
|
+
if RUBY_VERSION >= "2.5.0"
|
19
|
+
gem "rails", "~> 6.0"
|
20
|
+
gem 'webrick'
|
21
|
+
else
|
22
|
+
gem "rails", "~> 5.0"
|
23
|
+
end
|
15
24
|
gem "mongoid", github: "mongodb/mongoid"
|
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# Draper: View Models for Rails
|
2
2
|
|
3
|
-
[](https://github.com/drapergem/draper/actions?query=workflow%3Aci+branch%3Amaster)
|
4
4
|
[](https://codeclimate.com/github/drapergem/draper)
|
5
|
+
[](https://codeclimate.com/github/drapergem/draper/test_coverage)
|
5
6
|
[](http://inch-ci.org/github/drapergem/draper)
|
6
7
|
|
7
8
|
Draper adds an object-oriented layer of presentation logic to your Rails
|
@@ -107,7 +108,7 @@ Decorators are the ideal place to:
|
|
107
108
|
|
108
109
|
## Installation
|
109
110
|
|
110
|
-
As of version
|
111
|
+
As of version 4.0.0, Draper only officially supports Rails 5.2 / Ruby 2.4 and later. Add Draper to your Gemfile.
|
111
112
|
|
112
113
|
```ruby
|
113
114
|
gem 'draper'
|
@@ -379,7 +380,7 @@ can continue to use the `@article` instance variable to manipulate the model -
|
|
379
380
|
for example, `@article.comments.build` to add a new blank comment for a form.
|
380
381
|
|
381
382
|
## Configuration
|
382
|
-
Draper works out the box well, but also provides a hook for you to configure its
|
383
|
+
Draper works out the box well, but also provides a hook for you to configure its
|
383
384
|
default functionality. For example, Draper assumes you have a base `ApplicationController`.
|
384
385
|
If your base controller is named something different (e.g. `BaseController`),
|
385
386
|
you can tell Draper to use it by adding the following to an initializer:
|
@@ -468,10 +469,13 @@ end
|
|
468
469
|
```
|
469
470
|
|
470
471
|
Then you can stub the specific route helper functions you need using your
|
471
|
-
preferred stubbing technique
|
472
|
+
preferred stubbing technique. This examples uses Rspec currently recommended API
|
473
|
+
available in RSpec 3.6+
|
472
474
|
|
473
475
|
```ruby
|
474
|
-
|
476
|
+
without_partial_double_verification do
|
477
|
+
allow(helpers).to receive(:users_path).and_return('/users')
|
478
|
+
end
|
475
479
|
```
|
476
480
|
|
477
481
|
### View context leakage
|
@@ -645,7 +649,7 @@ you can include this module manually.
|
|
645
649
|
|
646
650
|
### Active Job Integration
|
647
651
|
|
648
|
-
[Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass ActiveRecord
|
652
|
+
[Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass ActiveRecord
|
649
653
|
objects to background tasks directly and performs the necessary serialization and deserialization. In
|
650
654
|
order to do this, arguments to a background job must implement [Global ID](https://github.com/rails/globalid).
|
651
655
|
Decorated objects implement Global ID by delegating to the object they are decorating. This means
|
data/bin/bundle
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||=
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version
|
67
|
+
end
|
68
|
+
|
69
|
+
def bundler_requirement
|
70
|
+
return "#{Gem::Requirement.default}.a" unless bundler_version
|
71
|
+
|
72
|
+
bundler_gem_version = Gem::Version.new(bundler_version)
|
73
|
+
|
74
|
+
requirement = bundler_gem_version.approximate_recommendation
|
75
|
+
|
76
|
+
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
|
77
|
+
|
78
|
+
requirement += ".a" if bundler_gem_version.prerelease?
|
79
|
+
|
80
|
+
requirement
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_bundler!
|
84
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
85
|
+
|
86
|
+
activate_bundler
|
87
|
+
end
|
88
|
+
|
89
|
+
def activate_bundler
|
90
|
+
gem_error = activation_error_handling do
|
91
|
+
gem "bundler", bundler_requirement
|
92
|
+
end
|
93
|
+
return if gem_error.nil?
|
94
|
+
require_error = activation_error_handling do
|
95
|
+
require "bundler/version"
|
96
|
+
end
|
97
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
98
|
+
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
99
|
+
exit 42
|
100
|
+
end
|
101
|
+
|
102
|
+
def activation_error_handling
|
103
|
+
yield
|
104
|
+
nil
|
105
|
+
rescue StandardError, LoadError => e
|
106
|
+
e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
m.load_bundler!
|
111
|
+
|
112
|
+
if m.invoked_as_script?
|
113
|
+
load Gem.bin_path("bundler", "bundle")
|
114
|
+
end
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/draper.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'lib/draper/version'
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "draper"
|
@@ -12,7 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
15
|
s.require_paths = ["lib"]
|
17
16
|
|
18
17
|
s.required_ruby_version = '>= 2.2.2'
|
@@ -22,6 +21,7 @@ Gem::Specification.new do |s|
|
|
22
21
|
s.add_dependency 'request_store', '>= 1.0'
|
23
22
|
s.add_dependency 'activemodel', '>= 5.0'
|
24
23
|
s.add_dependency 'activemodel-serializers-xml', '>= 1.0'
|
24
|
+
s.add_dependency 'ruby2_keywords'
|
25
25
|
|
26
26
|
s.add_development_dependency 'ammeter'
|
27
27
|
s.add_development_dependency 'rake'
|
@@ -30,4 +30,5 @@ Gem::Specification.new do |s|
|
|
30
30
|
s.add_development_dependency 'capybara'
|
31
31
|
s.add_development_dependency 'active_model_serializers', '>= 0.10'
|
32
32
|
s.add_development_dependency 'rubocop'
|
33
|
+
s.add_development_dependency 'simplecov', '0.17.1'
|
33
34
|
end
|
@@ -6,7 +6,7 @@ module Draper
|
|
6
6
|
# method calls to `object` as well. Calling `super` will first try to call the method on
|
7
7
|
# the parent decorator class. If no method exists on the parent class, it will then try
|
8
8
|
# to call the method on the `object`.
|
9
|
-
def method_missing(method, *args, &block)
|
9
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
10
10
|
return super unless delegatable?(method)
|
11
11
|
|
12
12
|
object.send(method, *args, &block)
|
@@ -20,12 +20,14 @@ module Draper
|
|
20
20
|
|
21
21
|
# @private
|
22
22
|
def delegatable?(method)
|
23
|
+
return if private_methods(false).include?(method)
|
24
|
+
|
23
25
|
object.respond_to?(method)
|
24
26
|
end
|
25
27
|
|
26
28
|
module ClassMethods
|
27
29
|
# Proxies missing class methods to the source class.
|
28
|
-
def method_missing(method, *args, &block)
|
30
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
29
31
|
return super unless delegatable?(method)
|
30
32
|
|
31
33
|
object_class.send(method, *args, &block)
|
@@ -35,7 +35,7 @@ module Draper
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class << self
|
38
|
-
|
38
|
+
alias :decorate :new
|
39
39
|
end
|
40
40
|
|
41
41
|
# @return [Array] the decorated items.
|
@@ -59,11 +59,12 @@ module Draper
|
|
59
59
|
true
|
60
60
|
end
|
61
61
|
|
62
|
-
|
62
|
+
alias :decorated_with? :instance_of?
|
63
63
|
|
64
64
|
def kind_of?(klass)
|
65
65
|
decorated_collection.kind_of?(klass) || super
|
66
66
|
end
|
67
|
+
|
67
68
|
alias_method :is_a?, :kind_of?
|
68
69
|
|
69
70
|
def replace(other)
|
data/lib/draper/decorator.rb
CHANGED
@@ -12,7 +12,8 @@ module Draper
|
|
12
12
|
|
13
13
|
# @return the object being decorated.
|
14
14
|
attr_reader :object
|
15
|
-
|
15
|
+
|
16
|
+
alias :model :object
|
16
17
|
|
17
18
|
# @return [Hash] extra data to be used in user-defined methods.
|
18
19
|
attr_accessor :context
|
@@ -36,7 +37,7 @@ module Draper
|
|
36
37
|
end
|
37
38
|
|
38
39
|
class << self
|
39
|
-
|
40
|
+
alias :decorate :new
|
40
41
|
end
|
41
42
|
|
42
43
|
# Automatically delegates instance methods to the source object. Class
|
@@ -190,7 +191,8 @@ module Draper
|
|
190
191
|
def kind_of?(klass)
|
191
192
|
super || object.kind_of?(klass)
|
192
193
|
end
|
193
|
-
|
194
|
+
|
195
|
+
alias :is_a? :kind_of?
|
194
196
|
|
195
197
|
# Checks if `self.instance_of?(klass)` or `object.instance_of?(klass)`
|
196
198
|
#
|
@@ -225,7 +227,7 @@ module Draper
|
|
225
227
|
# @return [Class] the class created by {decorate_collection}.
|
226
228
|
def self.collection_decorator_class
|
227
229
|
name = collection_decorator_name
|
228
|
-
name_constant = name
|
230
|
+
name_constant = name&.safe_constantize
|
229
231
|
|
230
232
|
name_constant || Draper::CollectionDecorator
|
231
233
|
end
|
@@ -248,7 +250,7 @@ module Draper
|
|
248
250
|
|
249
251
|
def self.inferred_object_class
|
250
252
|
name = object_class_name
|
251
|
-
name_constant = name
|
253
|
+
name_constant = name&.safe_constantize
|
252
254
|
return name_constant unless name_constant.nil?
|
253
255
|
|
254
256
|
raise Draper::UninferrableObjectError.new(self)
|
@@ -256,7 +258,7 @@ module Draper
|
|
256
258
|
|
257
259
|
def self.collection_decorator_name
|
258
260
|
singular = object_class_name
|
259
|
-
plural = singular
|
261
|
+
plural = singular&.pluralize
|
260
262
|
|
261
263
|
"#{plural}Decorator" unless plural == singular
|
262
264
|
end
|
data/lib/draper/delegation.rb
CHANGED
data/lib/draper/helper_proxy.rb
CHANGED
@@ -8,7 +8,7 @@ module Draper
|
|
8
8
|
end
|
9
9
|
|
10
10
|
# Sends helper methods to the view context.
|
11
|
-
def method_missing(method, *args, &block)
|
11
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
12
12
|
self.class.define_proxy method
|
13
13
|
send(method, *args, &block)
|
14
14
|
end
|
@@ -31,6 +31,7 @@ module Draper
|
|
31
31
|
define_method name do |*args, &block|
|
32
32
|
view_context.send(name, *args, &block)
|
33
33
|
end
|
34
|
+
ruby2_keywords name
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
data/lib/draper/lazy_helpers.rb
CHANGED
@@ -4,7 +4,7 @@ module Draper
|
|
4
4
|
# bazillion methods.
|
5
5
|
module LazyHelpers
|
6
6
|
# Sends missing methods to the {HelperProxy}.
|
7
|
-
def method_missing(method, *args, &block)
|
7
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
8
8
|
helpers.send(method, *args, &block)
|
9
9
|
rescue NoMethodError
|
10
10
|
super
|
data/lib/draper/query_methods.rb
CHANGED
@@ -3,10 +3,14 @@ require_relative 'query_methods/load_strategy'
|
|
3
3
|
module Draper
|
4
4
|
module QueryMethods
|
5
5
|
# Proxies missing query methods to the source class if the strategy allows.
|
6
|
-
def method_missing(method, *args, &block)
|
6
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
7
7
|
return super unless strategy.allowed? method
|
8
8
|
|
9
|
-
object.send(method, *args, &block).decorate
|
9
|
+
object.send(method, *args, &block).decorate(with: decorator_class, context: context)
|
10
|
+
end
|
11
|
+
|
12
|
+
def respond_to_missing?(method, include_private = false)
|
13
|
+
strategy.allowed?(method) || super
|
10
14
|
end
|
11
15
|
|
12
16
|
private
|
data/lib/draper/test_case.rb
CHANGED
data/lib/draper/version.rb
CHANGED
data/lib/draper/view_helpers.rb
CHANGED
@@ -12,7 +12,8 @@ module Draper
|
|
12
12
|
def helpers
|
13
13
|
Draper::ViewContext.current
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
|
+
alias :h :helpers
|
16
17
|
end
|
17
18
|
|
18
19
|
# Access the helpers proxy to call built-in and user-defined
|
@@ -22,13 +23,15 @@ module Draper
|
|
22
23
|
def helpers
|
23
24
|
Draper::ViewContext.current
|
24
25
|
end
|
25
|
-
|
26
|
+
|
27
|
+
alias :h :helpers
|
26
28
|
|
27
29
|
# Alias for `helpers.localize`, since localize is something that's used
|
28
30
|
# quite often. Further aliased to `l` for convenience.
|
29
|
-
def localize(*args)
|
31
|
+
ruby2_keywords def localize(*args)
|
30
32
|
helpers.localize(*args)
|
31
33
|
end
|
32
|
-
|
34
|
+
|
35
|
+
alias :l :localize
|
33
36
|
end
|
34
37
|
end
|
data/lib/draper.rb
CHANGED
@@ -8,6 +8,8 @@ require 'active_support/core_ext/hash/keys'
|
|
8
8
|
require 'active_support/core_ext/hash/reverse_merge'
|
9
9
|
require 'active_support/core_ext/name_error'
|
10
10
|
|
11
|
+
require 'ruby2_keywords'
|
12
|
+
|
11
13
|
require 'draper/version'
|
12
14
|
require 'draper/configuration'
|
13
15
|
require 'draper/view_helpers'
|
@@ -60,10 +60,11 @@ module Draper
|
|
60
60
|
|
61
61
|
context "when the collection has not yet been decorated" do
|
62
62
|
it "does not trigger decoration" do
|
63
|
-
|
63
|
+
decorated = CollectionDecorator.new([]).tap(&:to_a)
|
64
|
+
undecorated = CollectionDecorator.new([])
|
64
65
|
|
65
|
-
expect(
|
66
|
-
|
66
|
+
expect(decorated.instance_variable_defined?(:@decorated_collection)).to be_truthy
|
67
|
+
expect(undecorated.instance_variable_defined?(:@decorated_collection)).to be_falsy
|
67
68
|
end
|
68
69
|
|
69
70
|
it "sets context after decoration is triggered" do
|
@@ -217,7 +218,7 @@ module Draper
|
|
217
218
|
it "uses the custom class name" do
|
218
219
|
decorator = ProductsDecorator.new([])
|
219
220
|
|
220
|
-
expect(decorator.to_s).to match
|
221
|
+
expect(decorator.to_s).to match(/ProductsDecorator/)
|
221
222
|
end
|
222
223
|
end
|
223
224
|
end
|
@@ -439,7 +439,7 @@ module Draper
|
|
439
439
|
it "returns a detailed description of the decorator" do
|
440
440
|
decorator = ProductDecorator.new(double)
|
441
441
|
|
442
|
-
expect(decorator.inspect).to match
|
442
|
+
expect(decorator.inspect).to match(/#<ProductDecorator:0x\h+ .+>/)
|
443
443
|
end
|
444
444
|
|
445
445
|
it "includes the object" do
|
@@ -664,6 +664,38 @@ module Draper
|
|
664
664
|
expect{decorator.hello_world}.to raise_error NoMethodError
|
665
665
|
expect(decorator.methods).not_to include :hello_world
|
666
666
|
end
|
667
|
+
|
668
|
+
context 'when decorator overrides a public method defined on the object with a private' do
|
669
|
+
let(:decorator_class) do
|
670
|
+
Class.new(Decorator) do
|
671
|
+
private
|
672
|
+
|
673
|
+
def hello_world
|
674
|
+
'hello world'
|
675
|
+
end
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
let(:object) { Class.new { def hello_world; end }.new }
|
680
|
+
|
681
|
+
it 'does not delegate the public method defined on the object' do
|
682
|
+
decorator = decorator_class.new(object)
|
683
|
+
|
684
|
+
expect{ decorator.hello_world }.to raise_error NoMethodError
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
context 'when delegated method has the same name as private method defined on another object' do
|
689
|
+
let(:decorator_class) { Class.new(Decorator) }
|
690
|
+
let(:object) { Class.new { def print; end }.new }
|
691
|
+
|
692
|
+
it 'delegates the public method defined on the object' do
|
693
|
+
decorator = decorator_class.new(object)
|
694
|
+
|
695
|
+
# `print` private method is defined on `Object`
|
696
|
+
expect{ decorator.print }.not_to raise_error
|
697
|
+
end
|
698
|
+
end
|
667
699
|
end
|
668
700
|
|
669
701
|
context ".method_missing" do
|
data/spec/draper/draper_spec.rb
CHANGED
@@ -5,12 +5,14 @@ Post = Struct.new(:id) { }
|
|
5
5
|
|
6
6
|
module Draper
|
7
7
|
describe QueryMethods do
|
8
|
+
let(:fake_strategy) { instance_double(QueryMethods::LoadStrategy::ActiveRecord) }
|
9
|
+
|
10
|
+
before { allow(QueryMethods::LoadStrategy).to receive(:new).and_return(fake_strategy) }
|
11
|
+
|
8
12
|
describe '#method_missing' do
|
9
13
|
let(:collection) { [ Post.new, Post.new ] }
|
10
|
-
let(:
|
11
|
-
let(:
|
12
|
-
|
13
|
-
before { allow(QueryMethods::LoadStrategy).to receive(:new).and_return(fake_strategy) }
|
14
|
+
let(:collection_context) { { user: 'foo' } }
|
15
|
+
let(:collection_decorator) { PostDecorator.decorate_collection(collection, context: collection_context) }
|
14
16
|
|
15
17
|
context 'when strategy allows collection to call the method' do
|
16
18
|
let(:results) { spy(:results) }
|
@@ -25,6 +27,12 @@ module Draper
|
|
25
27
|
|
26
28
|
expect(results).to have_received(:decorate)
|
27
29
|
end
|
30
|
+
|
31
|
+
it 'calls the method on the collection and keeps the decoration options' do
|
32
|
+
collection_decorator.some_query_method
|
33
|
+
|
34
|
+
expect(results).to have_received(:decorate).with({ context: collection_context, with: PostDecorator })
|
35
|
+
end
|
28
36
|
end
|
29
37
|
|
30
38
|
context 'when strategy does not allow collection to call the method' do
|
@@ -35,5 +43,28 @@ module Draper
|
|
35
43
|
end
|
36
44
|
end
|
37
45
|
end
|
46
|
+
|
47
|
+
describe "#respond_to?" do
|
48
|
+
let(:collection) { [ Post.new, Post.new ] }
|
49
|
+
let(:collection_decorator) { PostDecorator.decorate_collection(collection) }
|
50
|
+
|
51
|
+
subject { collection_decorator.respond_to?(:some_query_method) }
|
52
|
+
|
53
|
+
context 'when strategy allows collection to call the method' do
|
54
|
+
before do
|
55
|
+
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it { is_expected.to eq(true) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when strategy does not allow collection to call the method' do
|
62
|
+
before do
|
63
|
+
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(false)
|
64
|
+
end
|
65
|
+
|
66
|
+
it { is_expected.to eq(false) }
|
67
|
+
end
|
68
|
+
end
|
38
69
|
end
|
39
70
|
end
|
@@ -3,6 +3,7 @@ require 'dummy/config/environment'
|
|
3
3
|
require 'ammeter/init'
|
4
4
|
require 'generators/controller_override'
|
5
5
|
require 'generators/rails/decorator_generator'
|
6
|
+
SimpleCov.command_name 'test:generator'
|
6
7
|
|
7
8
|
describe Rails::Generators::ControllerGenerator do
|
8
9
|
destination File.expand_path("../tmp", __FILE__)
|
@@ -40,7 +40,7 @@ describe Rails::Generators::DecoratorGenerator do
|
|
40
40
|
|
41
41
|
context "with an ApplicationDecorator" do
|
42
42
|
before do
|
43
|
-
allow_any_instance_of(Object).to receive(:require)
|
43
|
+
allow_any_instance_of(Object).to receive(:require).and_call_original
|
44
44
|
allow_any_instance_of(Object).to receive(:require).with("application_decorator").and_return(
|
45
45
|
stub_const "ApplicationDecorator", Class.new
|
46
46
|
)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: draper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Casimir
|
8
8
|
- Steve Klabnik
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -81,6 +81,20 @@ dependencies:
|
|
81
81
|
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '1.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: ruby2_keywords
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
84
98
|
- !ruby/object:Gem::Dependency
|
85
99
|
name: ammeter
|
86
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,6 +193,20 @@ dependencies:
|
|
179
193
|
- - ">="
|
180
194
|
- !ruby/object:Gem::Version
|
181
195
|
version: '0'
|
196
|
+
- !ruby/object:Gem::Dependency
|
197
|
+
name: simplecov
|
198
|
+
requirement: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - '='
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: 0.17.1
|
203
|
+
type: :development
|
204
|
+
prerelease: false
|
205
|
+
version_requirements: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - '='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: 0.17.1
|
182
210
|
description: Draper adds an object-oriented layer of presentation logic to your Rails
|
183
211
|
apps.
|
184
212
|
email:
|
@@ -190,10 +218,10 @@ extra_rdoc_files: []
|
|
190
218
|
files:
|
191
219
|
- ".codeclimate.yml"
|
192
220
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
221
|
+
- ".github/workflows/ci.yml"
|
193
222
|
- ".gitignore"
|
194
223
|
- ".rspec"
|
195
224
|
- ".rubocop.yml"
|
196
|
-
- ".travis.yml"
|
197
225
|
- ".yardopts"
|
198
226
|
- CHANGELOG.md
|
199
227
|
- CONTRIBUTING.md
|
@@ -202,6 +230,8 @@ files:
|
|
202
230
|
- LICENSE
|
203
231
|
- README.md
|
204
232
|
- Rakefile
|
233
|
+
- bin/bundle
|
234
|
+
- bin/rake
|
205
235
|
- draper.gemspec
|
206
236
|
- lib/draper.rb
|
207
237
|
- lib/draper/automatic_delegation.rb
|
@@ -266,6 +296,8 @@ files:
|
|
266
296
|
- spec/draper/view_helpers_spec.rb
|
267
297
|
- spec/dummy/.rspec
|
268
298
|
- spec/dummy/Rakefile
|
299
|
+
- spec/dummy/app/assets/config/manifest.js
|
300
|
+
- spec/dummy/app/controllers/application_controller.rb
|
269
301
|
- spec/dummy/app/controllers/base_controller.rb
|
270
302
|
- spec/dummy/app/controllers/localized_urls.rb
|
271
303
|
- spec/dummy/app/controllers/posts_controller.rb
|
@@ -303,6 +335,7 @@ files:
|
|
303
335
|
- spec/dummy/config/locales/en.yml
|
304
336
|
- spec/dummy/config/mongoid.yml
|
305
337
|
- spec/dummy/config/routes.rb
|
338
|
+
- spec/dummy/config/storage.yml
|
306
339
|
- spec/dummy/db/migrate/20121019115657_create_posts.rb
|
307
340
|
- spec/dummy/db/schema.rb
|
308
341
|
- spec/dummy/db/seeds.rb
|
@@ -353,7 +386,7 @@ homepage: http://github.com/drapergem/draper
|
|
353
386
|
licenses:
|
354
387
|
- MIT
|
355
388
|
metadata: {}
|
356
|
-
post_install_message:
|
389
|
+
post_install_message:
|
357
390
|
rdoc_options: []
|
358
391
|
require_paths:
|
359
392
|
- lib
|
@@ -368,8 +401,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
368
401
|
- !ruby/object:Gem::Version
|
369
402
|
version: '0'
|
370
403
|
requirements: []
|
371
|
-
rubygems_version: 3.0.
|
372
|
-
signing_key:
|
404
|
+
rubygems_version: 3.0.8
|
405
|
+
signing_key:
|
373
406
|
specification_version: 4
|
374
407
|
summary: View Models for Rails
|
375
408
|
test_files:
|
@@ -394,6 +427,8 @@ test_files:
|
|
394
427
|
- spec/draper/view_helpers_spec.rb
|
395
428
|
- spec/dummy/.rspec
|
396
429
|
- spec/dummy/Rakefile
|
430
|
+
- spec/dummy/app/assets/config/manifest.js
|
431
|
+
- spec/dummy/app/controllers/application_controller.rb
|
397
432
|
- spec/dummy/app/controllers/base_controller.rb
|
398
433
|
- spec/dummy/app/controllers/localized_urls.rb
|
399
434
|
- spec/dummy/app/controllers/posts_controller.rb
|
@@ -431,6 +466,7 @@ test_files:
|
|
431
466
|
- spec/dummy/config/locales/en.yml
|
432
467
|
- spec/dummy/config/mongoid.yml
|
433
468
|
- spec/dummy/config/routes.rb
|
469
|
+
- spec/dummy/config/storage.yml
|
434
470
|
- spec/dummy/db/migrate/20121019115657_create_posts.rb
|
435
471
|
- spec/dummy/db/schema.rb
|
436
472
|
- spec/dummy/db/seeds.rb
|