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