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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +55 -0
  3. data/CHANGELOG.md +41 -0
  4. data/Gemfile +11 -2
  5. data/README.md +35 -6
  6. data/bin/bundle +114 -0
  7. data/bin/rake +29 -0
  8. data/draper.gemspec +9 -8
  9. data/lib/draper/automatic_delegation.rb +4 -2
  10. data/lib/draper/collection_decorator.rb +4 -2
  11. data/lib/draper/configuration.rb +8 -0
  12. data/lib/draper/decoratable.rb +0 -1
  13. data/lib/draper/decorated_association.rb +0 -2
  14. data/lib/draper/decorator.rb +8 -6
  15. data/lib/draper/delegation.rb +1 -1
  16. data/lib/draper/finders.rb +0 -1
  17. data/lib/draper/helper_proxy.rb +2 -2
  18. data/lib/draper/lazy_helpers.rb +1 -3
  19. data/lib/draper/query_methods/load_strategy.rb +21 -0
  20. data/lib/draper/query_methods.rb +23 -0
  21. data/lib/draper/test_case.rb +3 -3
  22. data/lib/draper/undecorate.rb +1 -1
  23. data/lib/draper/version.rb +1 -1
  24. data/lib/draper/view_context/build_strategy.rb +1 -3
  25. data/lib/draper/view_helpers.rb +5 -5
  26. data/lib/draper.rb +3 -0
  27. data/spec/draper/collection_decorator_spec.rb +5 -6
  28. data/spec/draper/configuration_spec.rb +32 -8
  29. data/spec/draper/decoratable_spec.rb +1 -3
  30. data/spec/draper/decorated_association_spec.rb +0 -2
  31. data/spec/draper/decorator_spec.rb +33 -1
  32. data/spec/draper/draper_spec.rb +1 -0
  33. data/spec/draper/factory_spec.rb +0 -4
  34. data/spec/draper/query_methods/load_strategy_spec.rb +26 -0
  35. data/spec/draper/query_methods_spec.rb +70 -0
  36. data/spec/dummy/app/assets/config/manifest.js +3 -0
  37. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  38. data/spec/dummy/config/environments/development.rb +2 -0
  39. data/spec/dummy/config/environments/production.rb +2 -0
  40. data/spec/dummy/config/environments/test.rb +2 -0
  41. data/spec/dummy/config/storage.yml +7 -0
  42. data/spec/generators/controller/controller_generator_spec.rb +1 -0
  43. data/spec/generators/decorator/decorator_generator_spec.rb +1 -1
  44. data/spec/integration/integration_spec.rb +1 -0
  45. data/spec/spec_helper.rb +7 -0
  46. metadata +61 -20
  47. data/.travis.yml +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cc23ed0f81227aa958510ecb67676453d97b40cc
4
- data.tar.gz: ed8a159772bafb7df87cc5c767757e8b697f55b3
2
+ SHA256:
3
+ metadata.gz: 6b009279cf28737a6fcf4027fc26f3dc2a955741c7c5e5d2220c8dcb383490e3
4
+ data.tar.gz: 22455a3a7126650483af269d6a0e432a5a4465a59c762518ce78f40bcecb4286
5
5
  SHA512:
6
- metadata.gz: 466c609e53461c1e1f260f9e2bf6a0eb522238e5bdae8a2f37a88c1cd22d22665bf18293ba497f9f64d27faa0e12f0dbb9e3d906bae569f324c6124c02ad6b7e
7
- data.tar.gz: db229fe0cf5daf0acc2e52834c599a21e9ba85782458a8fd3a0520574e77f1857ec30c57c81dceb75561016170c941f8da3593d3a58fd395137c01ce57322cf0
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
- gem "sqlite3"
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
- gem "rails", "~> 5.0"
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
- [![TravisCI Build Status](https://travis-ci.org/drapergem/draper.svg?branch=master)](http://travis-ci.org/drapergem/draper)
3
+ [![Actions Status](https://github.com/drapergem/draper/workflows/CI/badge.svg?branch=master)](https://github.com/drapergem/draper/actions?query=workflow%3Aci+branch%3Amaster)
4
4
  [![Code Climate](https://codeclimate.com/github/drapergem/draper.svg)](https://codeclimate.com/github/drapergem/draper)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/0d40c43951d516bf6985/test_coverage)](https://codeclimate.com/github/drapergem/draper/test_coverage)
5
6
  [![Inline docs](http://inch-ci.org/github/drapergem/draper.svg?branch=master)](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 3.0.0, Draper is only compatible with Rails 5 / Ruby 2.2 and later. Add Draper to your Gemfile.
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 (this example uses RSpec's `stub` method):
472
+ preferred stubbing technique. This examples uses Rspec currently recommended API
473
+ available in RSpec 3.6+
461
474
 
462
475
  ```ruby
463
- helpers.stub(users_path: '/users')
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
- require File.join(__dir__, "lib", "draper", "version")
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', '~> 5.0'
21
- s.add_dependency 'actionpack', '~> 5.0'
22
- s.add_dependency 'request_store', '~> 1.0'
23
- s.add_dependency 'activemodel', '~> 5.0'
24
- s.add_dependency 'activemodel-serializers-xml', '~> 1.0'
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', '~> 0.10'
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
- alias_method :decorate, :new
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
- alias_method :decorated_with?, :instance_of?
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)
@@ -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
@@ -48,7 +48,6 @@ module Draper
48
48
  end
49
49
 
50
50
  module ClassMethods
51
-
52
51
  # Decorates a collection of objects. Used at the end of a scope chain.
53
52
  #
54
53
  # @example
@@ -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
@@ -12,7 +12,8 @@ module Draper
12
12
 
13
13
  # @return the object being decorated.
14
14
  attr_reader :object
15
- alias_method :model, :object
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
- alias_method :decorate, :new
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
- alias_method :is_a?, :kind_of?
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 && name.safe_constantize
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 && name.safe_constantize
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 && singular.pluralize
261
+ plural = singular&.pluralize
260
262
 
261
263
  "#{plural}Decorator" unless plural == singular
262
264
  end
@@ -7,7 +7,7 @@ module Draper
7
7
  # @return [void]
8
8
  def delegate(*methods)
9
9
  options = methods.extract_options!
10
- super *methods, options.reverse_merge(to: :object)
10
+ super(*methods, **options.reverse_merge(to: :object))
11
11
  end
12
12
  end
13
13
  end
@@ -3,7 +3,6 @@ module Draper
3
3
  # do not have to extend this module directly; it is extended by
4
4
  # {Decorator.decorates_finders}.
5
5
  module Finders
6
-
7
6
  def find(id, options = {})
8
7
  decorate(object_class.find(id), options)
9
8
  end
@@ -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
@@ -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