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
data/lib/draper/test_case.rb
CHANGED
@@ -3,9 +3,9 @@ module Draper
|
|
3
3
|
|
4
4
|
class TestCase < ::ActiveSupport::TestCase
|
5
5
|
module ViewContextTeardown
|
6
|
-
def
|
7
|
-
super
|
6
|
+
def before_setup
|
8
7
|
Draper::ViewContext.clear!
|
8
|
+
super
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -21,7 +21,7 @@ module Draper
|
|
21
21
|
end
|
22
22
|
|
23
23
|
include Draper::ViewHelpers::ClassMethods
|
24
|
-
|
24
|
+
alias :helper :helpers
|
25
25
|
end
|
26
26
|
|
27
27
|
include Behavior
|
data/lib/draper/undecorate.rb
CHANGED
data/lib/draper/version.rb
CHANGED
@@ -2,7 +2,6 @@ module Draper
|
|
2
2
|
module ViewContext
|
3
3
|
# @private
|
4
4
|
module BuildStrategy
|
5
|
-
|
6
5
|
def self.new(name, &block)
|
7
6
|
const_get(name.to_s.camelize).new(&block)
|
8
7
|
end
|
@@ -13,7 +12,7 @@ module Draper
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def call
|
16
|
-
view_context_class.new
|
15
|
+
view_context_class.respond_to?(:empty) ? view_context_class.empty : view_context_class.new
|
17
16
|
end
|
18
17
|
|
19
18
|
private
|
@@ -51,7 +50,6 @@ module Draper
|
|
51
50
|
ActionController::TestRequest.method(:create).parameters.first == [:req, :controller_class]
|
52
51
|
end
|
53
52
|
end
|
54
|
-
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
data/lib/draper/view_helpers.rb
CHANGED
@@ -5,7 +5,6 @@ module Draper
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
module ClassMethods
|
8
|
-
|
9
8
|
# Access the helpers proxy to call built-in and user-defined
|
10
9
|
# Rails helpers from a class context.
|
11
10
|
#
|
@@ -13,8 +12,8 @@ module Draper
|
|
13
12
|
def helpers
|
14
13
|
Draper::ViewContext.current
|
15
14
|
end
|
16
|
-
alias_method :h, :helpers
|
17
15
|
|
16
|
+
alias :h :helpers
|
18
17
|
end
|
19
18
|
|
20
19
|
# Access the helpers proxy to call built-in and user-defined
|
@@ -24,14 +23,15 @@ module Draper
|
|
24
23
|
def helpers
|
25
24
|
Draper::ViewContext.current
|
26
25
|
end
|
27
|
-
|
26
|
+
|
27
|
+
alias :h :helpers
|
28
28
|
|
29
29
|
# Alias for `helpers.localize`, since localize is something that's used
|
30
30
|
# quite often. Further aliased to `l` for convenience.
|
31
|
-
def localize(*args)
|
31
|
+
ruby2_keywords def localize(*args)
|
32
32
|
helpers.localize(*args)
|
33
33
|
end
|
34
|
-
alias_method :l, :localize
|
35
34
|
|
35
|
+
alias :l :localize
|
36
36
|
end
|
37
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'
|
@@ -23,6 +25,7 @@ require 'draper/factory'
|
|
23
25
|
require 'draper/decorated_association'
|
24
26
|
require 'draper/helper_support'
|
25
27
|
require 'draper/view_context'
|
28
|
+
require 'draper/query_methods'
|
26
29
|
require 'draper/collection_decorator'
|
27
30
|
require 'draper/undecorate'
|
28
31
|
require 'draper/decorates_assigned'
|
@@ -7,7 +7,6 @@ module Draper
|
|
7
7
|
|
8
8
|
describe "#initialize" do
|
9
9
|
describe "options validation" do
|
10
|
-
|
11
10
|
it "does not raise error on valid options" do
|
12
11
|
valid_options = {with: Decorator, context: {}}
|
13
12
|
expect{CollectionDecorator.new([], valid_options)}.not_to raise_error
|
@@ -61,10 +60,11 @@ module Draper
|
|
61
60
|
|
62
61
|
context "when the collection has not yet been decorated" do
|
63
62
|
it "does not trigger decoration" do
|
64
|
-
|
63
|
+
decorated = CollectionDecorator.new([]).tap(&:to_a)
|
64
|
+
undecorated = CollectionDecorator.new([])
|
65
65
|
|
66
|
-
expect(
|
67
|
-
|
66
|
+
expect(decorated.instance_variable_defined?(:@decorated_collection)).to be_truthy
|
67
|
+
expect(undecorated.instance_variable_defined?(:@decorated_collection)).to be_falsy
|
68
68
|
end
|
69
69
|
|
70
70
|
it "sets context after decoration is triggered" do
|
@@ -218,7 +218,7 @@ module Draper
|
|
218
218
|
it "uses the custom class name" do
|
219
219
|
decorator = ProductsDecorator.new([])
|
220
220
|
|
221
|
-
expect(decorator.to_s).to match
|
221
|
+
expect(decorator.to_s).to match(/ProductsDecorator/)
|
222
222
|
end
|
223
223
|
end
|
224
224
|
end
|
@@ -287,6 +287,5 @@ module Draper
|
|
287
287
|
expect(decorator.replace([:foo, :bar])).to be decorator
|
288
288
|
end
|
289
289
|
end
|
290
|
-
|
291
290
|
end
|
292
291
|
end
|
@@ -6,20 +6,44 @@ module Draper
|
|
6
6
|
Draper.configure { |config| expect(config).to be Draper }
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
describe '#default_controller' do
|
10
|
+
it 'defaults default_controller to ApplicationController' do
|
11
|
+
expect(Draper.default_controller).to be ApplicationController
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'allows customizing default_controller through configure' do
|
15
|
+
default = Draper.default_controller
|
16
|
+
|
17
|
+
Draper.configure do |config|
|
18
|
+
config.default_controller = CustomController
|
19
|
+
end
|
20
|
+
|
21
|
+
expect(Draper.default_controller).to be CustomController
|
22
|
+
|
23
|
+
Draper.default_controller = default
|
24
|
+
end
|
11
25
|
end
|
12
26
|
|
13
|
-
|
14
|
-
default
|
27
|
+
describe '#default_query_methods_strategy' do
|
28
|
+
let!(:default) { Draper.default_query_methods_strategy }
|
29
|
+
|
30
|
+
subject { Draper.default_query_methods_strategy }
|
15
31
|
|
16
|
-
|
17
|
-
|
32
|
+
context 'when there is no custom strategy' do
|
33
|
+
it { is_expected.to eq(:active_record) }
|
18
34
|
end
|
19
35
|
|
20
|
-
|
36
|
+
context 'when using a custom strategy' do
|
37
|
+
before do
|
38
|
+
Draper.configure do |config|
|
39
|
+
config.default_query_methods_strategy = :mongoid
|
40
|
+
end
|
41
|
+
end
|
21
42
|
|
22
|
-
|
43
|
+
after { Draper.default_query_methods_strategy = default }
|
44
|
+
|
45
|
+
it { is_expected.to eq(:mongoid) }
|
46
|
+
end
|
23
47
|
end
|
24
48
|
end
|
25
49
|
end
|
@@ -3,7 +3,6 @@ require 'support/shared_examples/decoratable_equality'
|
|
3
3
|
|
4
4
|
module Draper
|
5
5
|
describe Decoratable do
|
6
|
-
|
7
6
|
describe "#decorate" do
|
8
7
|
it "returns a decorator for self" do
|
9
8
|
product = Product.new
|
@@ -11,7 +10,7 @@ module Draper
|
|
11
10
|
|
12
11
|
expect(decorator).to be_a ProductDecorator
|
13
12
|
expect(decorator.object).to be product
|
14
|
-
|
13
|
+
end
|
15
14
|
|
16
15
|
it "accepts context" do
|
17
16
|
context = {some: "context"}
|
@@ -232,6 +231,5 @@ module Draper
|
|
232
231
|
end
|
233
232
|
end
|
234
233
|
end
|
235
|
-
|
236
234
|
end
|
237
235
|
end
|
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Draper
|
4
4
|
describe DecoratedAssociation do
|
5
|
-
|
6
5
|
describe "#initialize" do
|
7
6
|
it "accepts valid options" do
|
8
7
|
valid_options = {with: Decorator, scope: :foo, context: {}}
|
@@ -79,6 +78,5 @@ module Draper
|
|
79
78
|
end
|
80
79
|
end
|
81
80
|
end
|
82
|
-
|
83
81
|
end
|
84
82
|
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
data/spec/draper/factory_spec.rb
CHANGED
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Draper
|
4
4
|
describe Factory do
|
5
|
-
|
6
5
|
describe "#initialize" do
|
7
6
|
it "accepts valid options" do
|
8
7
|
valid_options = {with: Decorator, context: {foo: "bar"}}
|
@@ -88,11 +87,9 @@ module Draper
|
|
88
87
|
end
|
89
88
|
end
|
90
89
|
end
|
91
|
-
|
92
90
|
end
|
93
91
|
|
94
92
|
describe Factory::Worker do
|
95
|
-
|
96
93
|
describe "#call" do
|
97
94
|
it "calls the decorator method" do
|
98
95
|
object = double
|
@@ -246,6 +243,5 @@ module Draper
|
|
246
243
|
end
|
247
244
|
end
|
248
245
|
end
|
249
|
-
|
250
246
|
end
|
251
247
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
module Draper
|
5
|
+
module QueryMethods
|
6
|
+
describe LoadStrategy do
|
7
|
+
describe '#new' do
|
8
|
+
subject { described_class.new(:active_record) }
|
9
|
+
|
10
|
+
it { is_expected.to be_an_instance_of(LoadStrategy::ActiveRecord) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe LoadStrategy::ActiveRecord do
|
15
|
+
describe '#allowed?' do
|
16
|
+
it 'checks whether or not ActiveRecord::Relation::VALUE_METHODS has the given method' do
|
17
|
+
allow(::ActiveRecord::Relation::VALUE_METHODS).to receive(:include?)
|
18
|
+
|
19
|
+
described_class.new.allowed? :foo
|
20
|
+
|
21
|
+
expect(::ActiveRecord::Relation::VALUE_METHODS).to have_received(:include?).with(:foo)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../dummy/app/decorators/post_decorator'
|
3
|
+
|
4
|
+
Post = Struct.new(:id) { }
|
5
|
+
|
6
|
+
module Draper
|
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
|
+
|
12
|
+
describe '#method_missing' do
|
13
|
+
let(:collection) { [ Post.new, Post.new ] }
|
14
|
+
let(:collection_context) { { user: 'foo' } }
|
15
|
+
let(:collection_decorator) { PostDecorator.decorate_collection(collection, context: collection_context) }
|
16
|
+
|
17
|
+
context 'when strategy allows collection to call the method' do
|
18
|
+
let(:results) { spy(:results) }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(true)
|
22
|
+
allow(collection).to receive(:send).with(:some_query_method).and_return(results)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'calls the method on the collection and decorate it results' do
|
26
|
+
collection_decorator.some_query_method
|
27
|
+
|
28
|
+
expect(results).to have_received(:decorate)
|
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
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when strategy does not allow collection to call the method' do
|
39
|
+
before { allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(false) }
|
40
|
+
|
41
|
+
it 'raises NoMethodError' do
|
42
|
+
expect { collection_decorator.some_query_method }.to raise_exception(NoMethodError)
|
43
|
+
end
|
44
|
+
end
|
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
|
69
|
+
end
|
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
|
)
|