draper 3.0.1 → 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 +5 -5
- data/.github/workflows/ci.yml +55 -0
- data/CHANGELOG.md +41 -0
- data/Gemfile +11 -2
- data/README.md +35 -6
- data/bin/bundle +114 -0
- data/bin/rake +29 -0
- data/draper.gemspec +9 -8
- data/lib/draper/automatic_delegation.rb +4 -2
- data/lib/draper/collection_decorator.rb +4 -2
- data/lib/draper/configuration.rb +8 -0
- data/lib/draper/decoratable.rb +0 -1
- data/lib/draper/decorated_association.rb +0 -2
- data/lib/draper/decorator.rb +8 -6
- data/lib/draper/delegation.rb +1 -1
- data/lib/draper/finders.rb +0 -1
- data/lib/draper/helper_proxy.rb +2 -2
- data/lib/draper/lazy_helpers.rb +1 -3
- data/lib/draper/query_methods/load_strategy.rb +21 -0
- data/lib/draper/query_methods.rb +23 -0
- data/lib/draper/test_case.rb +3 -3
- data/lib/draper/undecorate.rb +1 -1
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context/build_strategy.rb +1 -3
- data/lib/draper/view_helpers.rb +5 -5
- data/lib/draper.rb +3 -0
- data/spec/draper/collection_decorator_spec.rb +5 -6
- data/spec/draper/configuration_spec.rb +32 -8
- data/spec/draper/decoratable_spec.rb +1 -3
- data/spec/draper/decorated_association_spec.rb +0 -2
- data/spec/draper/decorator_spec.rb +33 -1
- data/spec/draper/draper_spec.rb +1 -0
- data/spec/draper/factory_spec.rb +0 -4
- data/spec/draper/query_methods/load_strategy_spec.rb +26 -0
- data/spec/draper/query_methods_spec.rb +70 -0
- 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 +61 -20
- data/.travis.yml +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
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,46 @@
|
|
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
|
+
|
39
|
+
## 3.1.0
|
40
|
+
* Rails 6 support [#841](https://github.com/drapergem/draper/pull/841)
|
41
|
+
* Include ORM query methods in `CollectionDecorator` (e.g. `includes`) [#845](https://github.com/drapergem/draper/pull/845)
|
42
|
+
* Document the fix for view context leaking in specs [#847](https://github.com/drapergem/draper/pull/847)
|
43
|
+
|
3
44
|
## 3.0.1
|
4
45
|
* Let `decorator_class` infer anonymous class decorator from superclass [#820](https://github.com/drapergem/draper/pull/820)
|
5
46
|
* When inferring decorator fails, show original class instead of `ActiveRecord::Base` [#821](https://github.com/drapergem/draper/pull/821)
|
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'
|
@@ -326,6 +327,17 @@ your `ArticleDecorator` and they'll return decorated objects:
|
|
326
327
|
@article = ArticleDecorator.find(params[:id])
|
327
328
|
```
|
328
329
|
|
330
|
+
### Decorated Query Methods
|
331
|
+
By default, Draper will decorate all [QueryMethods](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html)
|
332
|
+
of ActiveRecord.
|
333
|
+
If you're using another ORM, in order to support it, you can tell Draper to use a custom strategy:
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
Draper.configure do |config|
|
337
|
+
config.default_query_methods_strategy = :mongoid
|
338
|
+
end
|
339
|
+
```
|
340
|
+
|
329
341
|
### When to Decorate Objects
|
330
342
|
|
331
343
|
Decorators are supposed to behave very much like the models they decorate, and
|
@@ -368,7 +380,7 @@ can continue to use the `@article` instance variable to manipulate the model -
|
|
368
380
|
for example, `@article.comments.build` to add a new blank comment for a form.
|
369
381
|
|
370
382
|
## Configuration
|
371
|
-
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
|
372
384
|
default functionality. For example, Draper assumes you have a base `ApplicationController`.
|
373
385
|
If your base controller is named something different (e.g. `BaseController`),
|
374
386
|
you can tell Draper to use it by adding the following to an initializer:
|
@@ -457,12 +469,29 @@ end
|
|
457
469
|
```
|
458
470
|
|
459
471
|
Then you can stub the specific route helper functions you need using your
|
460
|
-
preferred stubbing technique
|
472
|
+
preferred stubbing technique. This examples uses Rspec currently recommended API
|
473
|
+
available in RSpec 3.6+
|
461
474
|
|
462
475
|
```ruby
|
463
|
-
|
476
|
+
without_partial_double_verification do
|
477
|
+
allow(helpers).to receive(:users_path).and_return('/users')
|
478
|
+
end
|
479
|
+
```
|
480
|
+
|
481
|
+
### View context leakage
|
482
|
+
As mentioned before, Draper needs to build a view context to access helper methods. In MiniTest, the view context is
|
483
|
+
cleared during `before_setup` preventing any view context leakage. In RSpec, the view context is cleared before each
|
484
|
+
`decorator`, `controller`, and `mailer` spec. However, if you use decorators in other types of specs
|
485
|
+
(e.g. `job`), you may still experience the view context leaking from the previous spec. To solve this, add the
|
486
|
+
following to your `spec_helper` for each type of spec you are experiencing the leakage:
|
487
|
+
|
488
|
+
```ruby
|
489
|
+
config.before(:each, type: :type) { Draper::ViewContext.clear! }
|
464
490
|
```
|
465
491
|
|
492
|
+
_Note_: The `:type` above is just a placeholder. Replace `:type` with the type of spec you are experiencing
|
493
|
+
the leakage from.
|
494
|
+
|
466
495
|
## Advanced usage
|
467
496
|
|
468
497
|
### Shared Decorator Methods
|
@@ -620,7 +649,7 @@ you can include this module manually.
|
|
620
649
|
|
621
650
|
### Active Job Integration
|
622
651
|
|
623
|
-
[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
|
624
653
|
objects to background tasks directly and performs the necessary serialization and deserialization. In
|
625
654
|
order to do this, arguments to a background job must implement [Global ID](https://github.com/rails/globalid).
|
626
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,22 +12,23 @@ 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'
|
19
18
|
|
20
|
-
s.add_dependency 'activesupport', '
|
21
|
-
s.add_dependency 'actionpack', '
|
22
|
-
s.add_dependency 'request_store', '
|
23
|
-
s.add_dependency 'activemodel', '
|
24
|
-
s.add_dependency 'activemodel-serializers-xml', '
|
19
|
+
s.add_dependency 'activesupport', '>= 5.0'
|
20
|
+
s.add_dependency 'actionpack', '>= 5.0'
|
21
|
+
s.add_dependency 'request_store', '>= 1.0'
|
22
|
+
s.add_dependency 'activemodel', '>= 5.0'
|
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'
|
28
28
|
s.add_development_dependency 'rspec-rails'
|
29
29
|
s.add_development_dependency 'minitest-rails'
|
30
30
|
s.add_development_dependency 'capybara'
|
31
|
-
s.add_development_dependency 'active_model_serializers', '
|
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)
|
@@ -2,6 +2,7 @@ module Draper
|
|
2
2
|
class CollectionDecorator
|
3
3
|
include Enumerable
|
4
4
|
include Draper::ViewHelpers
|
5
|
+
include Draper::QueryMethods
|
5
6
|
extend Draper::Delegation
|
6
7
|
|
7
8
|
# @return the collection being decorated.
|
@@ -34,7 +35,7 @@ module Draper
|
|
34
35
|
end
|
35
36
|
|
36
37
|
class << self
|
37
|
-
|
38
|
+
alias :decorate :new
|
38
39
|
end
|
39
40
|
|
40
41
|
# @return [Array] the decorated items.
|
@@ -58,11 +59,12 @@ module Draper
|
|
58
59
|
true
|
59
60
|
end
|
60
61
|
|
61
|
-
|
62
|
+
alias :decorated_with? :instance_of?
|
62
63
|
|
63
64
|
def kind_of?(klass)
|
64
65
|
decorated_collection.kind_of?(klass) || super
|
65
66
|
end
|
67
|
+
|
66
68
|
alias_method :is_a?, :kind_of?
|
67
69
|
|
68
70
|
def replace(other)
|
data/lib/draper/configuration.rb
CHANGED
@@ -11,5 +11,13 @@ module Draper
|
|
11
11
|
def default_controller=(controller)
|
12
12
|
@@default_controller = controller
|
13
13
|
end
|
14
|
+
|
15
|
+
def default_query_methods_strategy
|
16
|
+
@@default_query_methods_strategy ||= :active_record
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_query_methods_strategy=(strategy)
|
20
|
+
@@default_query_methods_strategy = strategy
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
data/lib/draper/decoratable.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Draper
|
2
2
|
# @private
|
3
3
|
class DecoratedAssociation
|
4
|
-
|
5
4
|
def initialize(owner, association, options)
|
6
5
|
options.assert_valid_keys(:with, :scope, :context)
|
7
6
|
|
@@ -30,6 +29,5 @@ module Draper
|
|
30
29
|
|
31
30
|
@decorated = factory.decorate(associated, context_args: owner.context)
|
32
31
|
end
|
33
|
-
|
34
32
|
end
|
35
33
|
end
|
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/finders.rb
CHANGED
data/lib/draper/helper_proxy.rb
CHANGED
@@ -2,14 +2,13 @@ module Draper
|
|
2
2
|
# Provides access to helper methods - both Rails built-in helpers, and those
|
3
3
|
# defined in your application.
|
4
4
|
class HelperProxy
|
5
|
-
|
6
5
|
# @overload initialize(view_context)
|
7
6
|
def initialize(view_context)
|
8
7
|
@view_context = view_context
|
9
8
|
end
|
10
9
|
|
11
10
|
# Sends helper methods to the view context.
|
12
|
-
def method_missing(method, *args, &block)
|
11
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
13
12
|
self.class.define_proxy method
|
14
13
|
send(method, *args, &block)
|
15
14
|
end
|
@@ -32,6 +31,7 @@ module Draper
|
|
32
31
|
define_method name do |*args, &block|
|
33
32
|
view_context.send(name, *args, &block)
|
34
33
|
end
|
34
|
+
ruby2_keywords name
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/draper/lazy_helpers.rb
CHANGED
@@ -3,13 +3,11 @@ module Draper
|
|
3
3
|
# so that you can stop typing `h.` everywhere, at the cost of mixing in a
|
4
4
|
# bazillion methods.
|
5
5
|
module LazyHelpers
|
6
|
-
|
7
6
|
# Sends missing methods to the {HelperProxy}.
|
8
|
-
def method_missing(method, *args, &block)
|
7
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
9
8
|
helpers.send(method, *args, &block)
|
10
9
|
rescue NoMethodError
|
11
10
|
super
|
12
11
|
end
|
13
|
-
|
14
12
|
end
|
15
13
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Draper
|
2
|
+
module QueryMethods
|
3
|
+
module LoadStrategy
|
4
|
+
def self.new(name)
|
5
|
+
const_get(name.to_s.camelize).new
|
6
|
+
end
|
7
|
+
|
8
|
+
class ActiveRecord
|
9
|
+
def allowed?(method)
|
10
|
+
::ActiveRecord::Relation::VALUE_METHODS.include? method
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Mongoid
|
15
|
+
def allowed?(method)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'query_methods/load_strategy'
|
2
|
+
|
3
|
+
module Draper
|
4
|
+
module QueryMethods
|
5
|
+
# Proxies missing query methods to the source class if the strategy allows.
|
6
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
7
|
+
return super unless strategy.allowed? method
|
8
|
+
|
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
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Configures the strategy used to proxy the query methods, which defaults to `:active_record`.
|
19
|
+
def strategy
|
20
|
+
@strategy ||= LoadStrategy.new(Draper.default_query_methods_strategy)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|