draper 1.0.0.beta4 → 1.0.0.beta5
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.
- data/CHANGELOG.markdown +9 -1
- data/Gemfile +7 -2
- data/draper.gemspec +3 -3
- data/lib/draper/collection_decorator.rb +31 -27
- data/lib/draper/decorated_association.rb +32 -32
- data/lib/draper/decorator.rb +7 -7
- data/lib/draper/version.rb +1 -1
- data/spec/draper/collection_decorator_spec.rb +62 -62
- data/spec/draper/decoratable_spec.rb +1 -1
- data/spec/draper/decorated_association_spec.rb +62 -244
- data/spec/draper/decorator_spec.rb +1 -10
- data/spec/draper/finders_spec.rb +2 -2
- metadata +22 -6
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Draper Changelog
|
2
2
|
|
3
|
+
## 1.0.0.beta5
|
4
|
+
|
5
|
+
* Change CollectionDecorator to freeze its collection [https://github.com/drapergem/draper/commit/04d779615c43580409083a71661489e1bbf91ad4](https://github.com/drapergem/draper/commit/04d779615c43580409083a71661489e1bbf91ad4)
|
6
|
+
|
7
|
+
* Bugfix on `CollectionDecorator#to_s` [https://github.com/drapergem/draper/commit/eefd7d09cac97d531b9235246378c3746d153f08](https://github.com/drapergem/draper/commit/eefd7d09cac97d531b9235246378c3746d153f08)
|
8
|
+
|
9
|
+
* Upgrade `request_store` dependency to take advantage of a bugfix [https://github.com/drapergem/draper/commit/9f17212fd1fb656ef1314327d60fe45e0acf60a2](https://github.com/drapergem/draper/commit/9f17212fd1fb656ef1314327d60fe45e0acf60a2)
|
10
|
+
|
3
11
|
## 1.0.0.beta4
|
4
12
|
|
5
13
|
* Fixed a race condition with capybara integration. [https://github.com/drapergem/draper/commit/e79464931e7b98c85ed5d78ed9ca38d51f43006e](https://github.com/drapergem/draper/commit/e79464931e7b98c85ed5d78ed9ca38d51f43006e)
|
@@ -44,7 +52,7 @@
|
|
44
52
|
Rails app in both development and production mode to help ensure that we
|
45
53
|
don't make changes that break Draper. [https://github.com/drapergem/draper/commit/90a4859085cab158658d23d77cd3108b6037e36f](https://github.com/drapergem/draper/commit/90a4859085cab158658d23d77cd3108b6037e36f)
|
46
54
|
* Add `#decorated?` method. This gives us a free RSpec matcher,
|
47
|
-
`
|
55
|
+
`be_decorated`. [https://github.com/drapergem/draper/commit/834a6fd1f24b5646c333a04a99fe9846a58965d6](https://github.com/drapergem/draper/commit/834a6fd1f24b5646c333a04a99fe9846a58965d6)
|
48
56
|
* `#decorates` is no longer needed inside your models, and should be removed.
|
49
57
|
Decorators automatically infer the class they decorate. [https://github.com/drapergem/draper/commit/e1214d97b62f2cab45227cc650029734160dcdfe](https://github.com/drapergem/draper/commit/e1214d97b62f2cab45227cc650029734160dcdfe)
|
50
58
|
* Decorators do not automatically come with 'finders' by default. If you'd like
|
data/Gemfile
CHANGED
@@ -2,6 +2,11 @@ source :rubygems
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
|
5
|
+
platforms :ruby do
|
6
|
+
gem "sqlite3"
|
7
|
+
end
|
6
8
|
|
7
|
-
|
9
|
+
platforms :jruby do
|
10
|
+
gem "minitest", ">= 3.0"
|
11
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 1.2.2.1"
|
12
|
+
end
|
data/draper.gemspec
CHANGED
@@ -18,13 +18,13 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_dependency 'activesupport', '>= 3.0'
|
20
20
|
s.add_dependency 'actionpack', '>= 3.0'
|
21
|
-
s.add_dependency 'request_store', '~> 1.0.
|
21
|
+
s.add_dependency 'request_store', '~> 1.0.3'
|
22
22
|
|
23
23
|
s.add_development_dependency 'ammeter'
|
24
24
|
s.add_development_dependency 'rake', '~> 0.9.2'
|
25
|
-
s.add_development_dependency 'rspec', '~> 2.
|
25
|
+
s.add_development_dependency 'rspec', '~> 2.12'
|
26
|
+
s.add_development_dependency 'rspec-mocks', '>= 2.12.1'
|
26
27
|
s.add_development_dependency 'yard'
|
27
28
|
s.add_development_dependency 'minitest-rails', '~> 0.2'
|
28
|
-
s.add_development_dependency 'minitest', '~> 3.0' if RUBY_PLATFORM == "java"
|
29
29
|
s.add_development_dependency 'capybara'
|
30
30
|
end
|
@@ -3,20 +3,21 @@ module Draper
|
|
3
3
|
include Enumerable
|
4
4
|
include ViewHelpers
|
5
5
|
|
6
|
-
|
6
|
+
attr_reader :source
|
7
7
|
alias_method :to_source, :source
|
8
8
|
|
9
|
-
|
9
|
+
attr_accessor :context
|
10
|
+
|
11
|
+
array_methods = Array.instance_methods - Object.instance_methods
|
12
|
+
delegate :as_json, *array_methods, to: :decorated_collection
|
10
13
|
|
11
14
|
# @param source collection to decorate
|
12
|
-
# @
|
13
|
-
# @option options [Class, Symbol] :with the class used to decorate
|
14
|
-
# items, or `:infer` to call each item's `decorate` method instead
|
15
|
+
# @option options [Class] :with the class used to decorate items
|
15
16
|
# @option options [Hash] :context context available to each item's decorator
|
16
17
|
def initialize(source, options = {})
|
17
18
|
options.assert_valid_keys(:with, :context)
|
18
|
-
@source = source
|
19
|
-
@decorator_class = options
|
19
|
+
@source = source.dup.freeze
|
20
|
+
@decorator_class = options[:with]
|
20
21
|
@context = options.fetch(:context, {})
|
21
22
|
end
|
22
23
|
|
@@ -25,7 +26,7 @@ module Draper
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def decorated_collection
|
28
|
-
@decorated_collection ||= source.
|
29
|
+
@decorated_collection ||= source.map{|item| decorate_item(item)}.freeze
|
29
30
|
end
|
30
31
|
|
31
32
|
def find(*args, &block)
|
@@ -36,25 +37,18 @@ module Draper
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
|
-
def method_missing(method, *args, &block)
|
40
|
-
source.send(method, *args, &block)
|
41
|
-
end
|
42
|
-
|
43
|
-
def respond_to?(method, include_private = false)
|
44
|
-
super || source.respond_to?(method, include_private)
|
45
|
-
end
|
46
|
-
|
47
|
-
def kind_of?(klass)
|
48
|
-
super || source.kind_of?(klass)
|
49
|
-
end
|
50
|
-
alias_method :is_a?, :kind_of?
|
51
|
-
|
52
40
|
def ==(other)
|
53
41
|
source == (other.respond_to?(:source) ? other.source : other)
|
54
42
|
end
|
55
43
|
|
56
44
|
def to_s
|
57
|
-
|
45
|
+
klass = begin
|
46
|
+
decorator_class
|
47
|
+
rescue Draper::UninferrableDecoratorError
|
48
|
+
"inferred decorators"
|
49
|
+
end
|
50
|
+
|
51
|
+
"#<CollectionDecorator of #{klass} for #{source.inspect}>"
|
58
52
|
end
|
59
53
|
|
60
54
|
def context=(value)
|
@@ -62,14 +56,14 @@ module Draper
|
|
62
56
|
each {|item| item.context = value } if @decorated_collection
|
63
57
|
end
|
64
58
|
|
59
|
+
def decorator_class
|
60
|
+
@decorator_class ||= self.class.inferred_decorator_class
|
61
|
+
end
|
62
|
+
|
65
63
|
protected
|
66
64
|
|
67
65
|
def decorate_item(item)
|
68
|
-
|
69
|
-
item.decorate(context: context)
|
70
|
-
else
|
71
|
-
decorator_class.decorate(item, context: context)
|
72
|
-
end
|
66
|
+
item_decorator.call(item, context: context)
|
73
67
|
end
|
74
68
|
|
75
69
|
def self.inferred_decorator_class
|
@@ -85,5 +79,15 @@ module Draper
|
|
85
79
|
def self.decorator_uninferrable
|
86
80
|
raise Draper::UninferrableDecoratorError.new(self)
|
87
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def item_decorator
|
86
|
+
@item_decorator ||= begin
|
87
|
+
decorator_class.method(:decorate)
|
88
|
+
rescue Draper::UninferrableDecoratorError
|
89
|
+
->(item, options) { item.decorate(options) }
|
90
|
+
end
|
91
|
+
end
|
88
92
|
end
|
89
93
|
end
|
@@ -1,70 +1,70 @@
|
|
1
1
|
module Draper
|
2
2
|
class DecoratedAssociation
|
3
3
|
|
4
|
-
|
4
|
+
def initialize(owner, association, options)
|
5
|
+
options.assert_valid_keys(:with, :scope, :context)
|
5
6
|
|
6
|
-
|
7
|
-
@base = base
|
7
|
+
@owner = owner
|
8
8
|
@association = association
|
9
|
-
|
10
|
-
@
|
9
|
+
|
10
|
+
@decorator_class = options[:with]
|
11
|
+
@scope = options[:scope]
|
12
|
+
@context = options.fetch(:context, owner.context)
|
11
13
|
end
|
12
14
|
|
13
15
|
def call
|
14
16
|
return undecorated if undecorated.nil?
|
15
|
-
|
17
|
+
decorated
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
+
def context
|
21
|
+
return @context.call(owner.context) if @context.respond_to?(:call)
|
22
|
+
@context
|
20
23
|
end
|
21
24
|
|
22
25
|
private
|
23
26
|
|
27
|
+
attr_reader :owner, :association, :decorator_class, :scope
|
28
|
+
|
29
|
+
def source
|
30
|
+
owner.source
|
31
|
+
end
|
32
|
+
|
24
33
|
def undecorated
|
25
34
|
@undecorated ||= begin
|
26
35
|
associated = source.send(association)
|
27
|
-
associated = associated.send(
|
36
|
+
associated = associated.send(scope) if scope
|
28
37
|
associated
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
32
|
-
def
|
33
|
-
@decorated ||=
|
34
|
-
end
|
35
|
-
|
36
|
-
def decorate_method
|
37
|
-
if collection? && decorator_class.respond_to?(:decorate_collection)
|
38
|
-
:decorate_collection
|
39
|
-
else
|
40
|
-
:decorate
|
41
|
-
end
|
41
|
+
def decorated
|
42
|
+
@decorated ||= decorator.call(undecorated, context: context)
|
42
43
|
end
|
43
44
|
|
44
45
|
def collection?
|
45
46
|
undecorated.respond_to?(:first)
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
-
return
|
49
|
+
def decorator
|
50
|
+
return collection_decorator if collection?
|
50
51
|
|
51
|
-
if
|
52
|
-
|
53
|
-
Draper::CollectionDecorator
|
52
|
+
if decorator_class
|
53
|
+
decorator_class.method(:decorate)
|
54
54
|
else
|
55
|
-
|
55
|
+
->(item, options) { item.decorate(options) }
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
60
|
-
|
59
|
+
def collection_decorator
|
60
|
+
klass = decorator_class || Draper::CollectionDecorator
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
if klass.respond_to?(:decorate_collection)
|
63
|
+
klass.method(:decorate_collection)
|
64
|
+
else
|
65
|
+
klass.method(:decorate)
|
66
66
|
end
|
67
|
-
dec_options
|
68
67
|
end
|
68
|
+
|
69
69
|
end
|
70
70
|
end
|
data/lib/draper/decorator.rb
CHANGED
@@ -5,11 +5,12 @@ module Draper
|
|
5
5
|
include Draper::ViewHelpers
|
6
6
|
include ActiveModel::Serialization if defined?(ActiveModel::Serialization)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
attr_reader :source
|
10
9
|
alias_method :model, :source
|
11
10
|
alias_method :to_source, :source
|
12
11
|
|
12
|
+
attr_accessor :context
|
13
|
+
|
13
14
|
# Initialize a new decorator instance by passing in
|
14
15
|
# an instance of the source class. Pass in an optional
|
15
16
|
# :context inside the options hash which is available
|
@@ -23,7 +24,6 @@ module Draper
|
|
23
24
|
# multiple places in the chain.
|
24
25
|
#
|
25
26
|
# @param [Object] source object to decorate
|
26
|
-
# @param [Hash] options (optional)
|
27
27
|
# @option options [Hash] :context context available to the decorator
|
28
28
|
def initialize(source, options = {})
|
29
29
|
options.assert_valid_keys(:context)
|
@@ -136,8 +136,8 @@ module Draper
|
|
136
136
|
# @param [Object] source collection to decorate
|
137
137
|
# @param [Hash] options passed to each item's decorator (except
|
138
138
|
# for the keys listed below)
|
139
|
-
# @option options [Class
|
140
|
-
# items
|
139
|
+
# @option options [Class] :with (self) the class used to decorate
|
140
|
+
# items
|
141
141
|
# @option options [Hash] :context context available to decorated items
|
142
142
|
def self.decorate_collection(source, options = {})
|
143
143
|
options.assert_valid_keys(:with, :context)
|
@@ -258,8 +258,8 @@ module Draper
|
|
258
258
|
|
259
259
|
def handle_multiple_decoration(options)
|
260
260
|
if source.instance_of?(self.class)
|
261
|
-
|
262
|
-
|
261
|
+
@context = source.context unless options.has_key?(:context)
|
262
|
+
@source = source.source
|
263
263
|
elsif source.decorated_with?(self.class)
|
264
264
|
warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}"
|
265
265
|
end
|
data/lib/draper/version.rb
CHANGED
@@ -16,6 +16,27 @@ describe Draper::CollectionDecorator do
|
|
16
16
|
subject.map{|item| item.source}.should == source
|
17
17
|
end
|
18
18
|
|
19
|
+
describe "#source" do
|
20
|
+
it "duplicates the source collection" do
|
21
|
+
subject.source.should == source
|
22
|
+
subject.source.should_not be source
|
23
|
+
end
|
24
|
+
|
25
|
+
it "is frozen" do
|
26
|
+
subject.source.should be_frozen
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is aliased to #to_source" do
|
30
|
+
subject.to_source.should == source
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#decorated_collection" do
|
35
|
+
it "is frozen" do
|
36
|
+
subject.decorated_collection.should be_frozen
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
19
40
|
context "with context" do
|
20
41
|
subject { Draper::CollectionDecorator.new(source, with: ProductDecorator, context: {some: 'context'}) }
|
21
42
|
|
@@ -79,55 +100,58 @@ describe Draper::CollectionDecorator do
|
|
79
100
|
expect { Draper::CollectionDecorator.new(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, 'Unknown key: foo')
|
80
101
|
end
|
81
102
|
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "item decoration" do
|
106
|
+
subject { subject_class.new(source, options) }
|
107
|
+
let(:decorator_classes) { subject.decorated_collection.map(&:class) }
|
108
|
+
let(:source) { [Product.new, Widget.new] }
|
109
|
+
|
110
|
+
context "when the :with option was given" do
|
111
|
+
let(:options) { {with: SpecificProductDecorator} }
|
82
112
|
|
83
|
-
context "when the :with option is given" do
|
84
113
|
context "and the decorator can't be inferred from the class" do
|
85
|
-
|
114
|
+
let(:subject_class) { Draper::CollectionDecorator }
|
86
115
|
|
87
116
|
it "uses the :with option" do
|
88
|
-
|
117
|
+
decorator_classes.should == [SpecificProductDecorator, SpecificProductDecorator]
|
89
118
|
end
|
90
119
|
end
|
91
120
|
|
92
121
|
context "and the decorator is inferrable from the class" do
|
93
|
-
|
122
|
+
let(:subject_class) { ProductsDecorator }
|
94
123
|
|
95
124
|
it "uses the :with option" do
|
96
|
-
|
125
|
+
decorator_classes.should == [SpecificProductDecorator, SpecificProductDecorator]
|
97
126
|
end
|
98
127
|
end
|
99
128
|
end
|
100
129
|
|
101
|
-
context "when the :with option
|
130
|
+
context "when the :with option was not given" do
|
131
|
+
let(:options) { {} }
|
132
|
+
|
102
133
|
context "and the decorator can't be inferred from the class" do
|
103
|
-
|
104
|
-
|
134
|
+
let(:subject_class) { Draper::CollectionDecorator }
|
135
|
+
|
136
|
+
it "infers the decorator from each item" do
|
137
|
+
decorator_classes.should == [ProductDecorator, WidgetDecorator]
|
105
138
|
end
|
106
139
|
end
|
107
140
|
|
108
141
|
context "and the decorator is inferrable from the class" do
|
109
|
-
|
142
|
+
let(:subject_class) { ProductsDecorator}
|
110
143
|
|
111
144
|
it "infers the decorator" do
|
112
|
-
|
145
|
+
decorator_classes.should == [ProductDecorator, ProductDecorator]
|
113
146
|
end
|
114
147
|
end
|
115
148
|
end
|
116
149
|
end
|
117
150
|
|
118
|
-
describe "#source" do
|
119
|
-
it "returns the source collection" do
|
120
|
-
subject.source.should be source
|
121
|
-
end
|
122
|
-
|
123
|
-
it "is aliased to #to_source" do
|
124
|
-
subject.to_source.should be source
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
151
|
describe "#find" do
|
129
152
|
context "with a block" do
|
130
153
|
it "decorates Enumerable#find" do
|
154
|
+
subject.stub decorated_collection: []
|
131
155
|
subject.decorated_collection.should_receive(:find)
|
132
156
|
subject.find {|p| p.title == "title" }
|
133
157
|
end
|
@@ -184,36 +208,15 @@ describe Draper::CollectionDecorator do
|
|
184
208
|
describe "#to_ary" do
|
185
209
|
# required for `render @collection` in Rails
|
186
210
|
it "delegates to the decorated collection" do
|
187
|
-
subject.decorated_collection
|
211
|
+
subject.stub decorated_collection: double(to_ary: :an_array)
|
188
212
|
subject.to_ary.should == :an_array
|
189
213
|
end
|
190
214
|
end
|
191
215
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
it "returns true for the wrapped collection's methods" do
|
198
|
-
source.stub(:respond_to?).with(:whatever, true).and_return(true)
|
199
|
-
subject.respond_to?(:whatever, true).should be_true
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
context "Array methods" do
|
204
|
-
describe "#include?" do
|
205
|
-
it "delegates to the decorated collection" do
|
206
|
-
subject.decorated_collection.should_receive(:include?).with(:something).and_return(true)
|
207
|
-
subject.should include :something
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
describe "#[]" do
|
212
|
-
it "delegates to the decorated collection" do
|
213
|
-
subject.decorated_collection.should_receive(:[]).with(42).and_return(:something)
|
214
|
-
subject[42].should == :something
|
215
|
-
end
|
216
|
-
end
|
216
|
+
it "delegates array methods to the decorated collection" do
|
217
|
+
subject.stub decorated_collection: []
|
218
|
+
subject.decorated_collection.should_receive(:[]).with(42).and_return(:the_answer)
|
219
|
+
subject[42].should == :the_answer
|
217
220
|
end
|
218
221
|
|
219
222
|
describe "#==" do
|
@@ -250,27 +253,24 @@ describe Draper::CollectionDecorator do
|
|
250
253
|
end
|
251
254
|
end
|
252
255
|
|
253
|
-
|
254
|
-
subject.
|
255
|
-
|
256
|
-
end
|
256
|
+
describe "#to_s" do
|
257
|
+
subject { Draper::CollectionDecorator.new(source, options) }
|
258
|
+
let(:source) { ["a", "b", "c"] }
|
257
259
|
|
258
|
-
|
259
|
-
|
260
|
-
subject.is_a?(subject.class).should be_true
|
261
|
-
end
|
260
|
+
context "when :with option was given" do
|
261
|
+
let(:options) { {with: ProductDecorator} }
|
262
262
|
|
263
|
-
|
264
|
-
|
265
|
-
class << source
|
266
|
-
def page_number
|
267
|
-
42
|
268
|
-
end
|
263
|
+
it "returns a string representation of the CollectionDecorator" do
|
264
|
+
subject.to_s.should == '#<CollectionDecorator of ProductDecorator for ["a", "b", "c"]>'
|
269
265
|
end
|
270
266
|
end
|
271
267
|
|
272
|
-
|
273
|
-
|
268
|
+
context "when :with option was not given" do
|
269
|
+
let(:options) { {} }
|
270
|
+
|
271
|
+
it "returns a string representation of the CollectionDecorator" do
|
272
|
+
subject.to_s.should == '#<CollectionDecorator of inferred decorators for ["a", "b", "c"]>'
|
273
|
+
end
|
274
274
|
end
|
275
275
|
end
|
276
276
|
|
@@ -150,7 +150,7 @@ describe Draper::Decoratable do
|
|
150
150
|
|
151
151
|
decorator.should be_a Draper::CollectionDecorator
|
152
152
|
decorator.decorator_class.should be WidgetDecorator
|
153
|
-
decorator.source.should
|
153
|
+
decorator.source.should == Product.scoped
|
154
154
|
end
|
155
155
|
|
156
156
|
it "accepts context" do
|
@@ -1,313 +1,131 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Draper::DecoratedAssociation do
|
4
|
-
let(:decorated_association) { Draper::DecoratedAssociation.new(
|
4
|
+
let(:decorated_association) { Draper::DecoratedAssociation.new(owner, :association, options) }
|
5
5
|
let(:source) { Product.new }
|
6
|
-
let(:
|
6
|
+
let(:owner) { source.decorate }
|
7
7
|
let(:options) { {} }
|
8
8
|
|
9
9
|
describe "#initialize" do
|
10
10
|
describe "options validation" do
|
11
|
-
let(:association) { :similar_products }
|
12
11
|
let(:valid_options) { {with: ProductDecorator, scope: :foo, context: {}} }
|
13
12
|
|
14
13
|
it "does not raise error on valid options" do
|
15
|
-
expect { Draper::DecoratedAssociation.new(
|
14
|
+
expect { Draper::DecoratedAssociation.new(owner, :association, valid_options) }.to_not raise_error
|
16
15
|
end
|
17
16
|
|
18
17
|
it "raises error on invalid options" do
|
19
|
-
expect { Draper::DecoratedAssociation.new(
|
18
|
+
expect { Draper::DecoratedAssociation.new(owner, :association, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, 'Unknown key: foo')
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
|
-
describe "#base" do
|
25
|
-
subject { decorated_association.base }
|
26
|
-
let(:association) { :similar_products }
|
27
|
-
|
28
|
-
it "returns the base decorator" do
|
29
|
-
should be base
|
30
|
-
end
|
31
|
-
|
32
|
-
it "returns a Decorator" do
|
33
|
-
subject.class.should == ProductDecorator
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "#source" do
|
38
|
-
subject { decorated_association.source }
|
39
|
-
let(:association) { :similar_products }
|
40
|
-
|
41
|
-
it "returns the base decorator's source" do
|
42
|
-
should be base.source
|
43
|
-
end
|
44
|
-
|
45
|
-
it "returns a Model" do
|
46
|
-
subject.class.should == Product
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
23
|
describe "#call" do
|
51
|
-
|
52
|
-
|
53
|
-
context "for an ActiveModel collection association" do
|
54
|
-
let(:association) { :similar_products }
|
55
|
-
|
56
|
-
context "when the association is not empty" do
|
57
|
-
it "decorates the collection" do
|
58
|
-
subject.should be_a Draper::CollectionDecorator
|
59
|
-
end
|
60
|
-
|
61
|
-
it "infers the decorator" do
|
62
|
-
subject.decorator_class.should be :infer
|
63
|
-
end
|
64
|
-
end
|
24
|
+
let(:context) { {foo: "bar"} }
|
25
|
+
let(:expected_options) { {context: context} }
|
65
26
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
subject.should be_a Draper::CollectionDecorator
|
70
|
-
subject.should be_empty
|
71
|
-
subject.first.should be_nil
|
72
|
-
end
|
73
|
-
end
|
27
|
+
before do
|
28
|
+
source.stub association: associated
|
29
|
+
decorated_association.stub context: context
|
74
30
|
end
|
75
31
|
|
76
|
-
context "for
|
77
|
-
let(:
|
32
|
+
context "for a singular association" do
|
33
|
+
let(:associated) { Product.new }
|
78
34
|
|
79
|
-
context "when
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
it "infers the decorator" do
|
85
|
-
subject.decorator_class.should be :infer
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
context "when the association is empty" do
|
90
|
-
it "returns an empty collection decorator" do
|
91
|
-
source.stub(:poro_similar_products).and_return([])
|
92
|
-
subject.should be_a Draper::CollectionDecorator
|
93
|
-
subject.should be_empty
|
94
|
-
subject.first.should be_nil
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
context "for an ActiveModel singular association" do
|
100
|
-
let(:association) { :previous_version }
|
35
|
+
context "when :with option was given" do
|
36
|
+
let(:options) { {with: decorator} }
|
37
|
+
let(:decorator) { SpecificProductDecorator }
|
101
38
|
|
102
|
-
|
103
|
-
|
104
|
-
|
39
|
+
it "uses the specified decorator" do
|
40
|
+
decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated)
|
41
|
+
decorated_association.call.should be :decorated
|
105
42
|
end
|
106
43
|
end
|
107
44
|
|
108
|
-
context "when
|
109
|
-
it "
|
110
|
-
|
111
|
-
|
45
|
+
context "when :with option was not given" do
|
46
|
+
it "infers the decorator" do
|
47
|
+
associated.should_receive(:decorate).with(expected_options).and_return(:decorated)
|
48
|
+
decorated_association.call.should be :decorated
|
112
49
|
end
|
113
50
|
end
|
114
51
|
end
|
115
52
|
|
116
|
-
context "for a
|
117
|
-
let(:
|
53
|
+
context "for a collection association" do
|
54
|
+
let(:associated) { [Product.new, Widget.new] }
|
118
55
|
|
119
|
-
context "when
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
56
|
+
context "when :with option is a collection decorator" do
|
57
|
+
let(:options) { {with: collection_decorator} }
|
58
|
+
let(:collection_decorator) { ProductsDecorator }
|
124
59
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
subject.should be_nil
|
60
|
+
it "uses the specified decorator" do
|
61
|
+
collection_decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated_collection)
|
62
|
+
decorated_association.call.should be :decorated_collection
|
129
63
|
end
|
130
64
|
end
|
131
|
-
end
|
132
|
-
|
133
|
-
context "when a decorator is specified" do
|
134
|
-
let(:options) { {with: SpecificProductDecorator} }
|
135
65
|
|
136
|
-
context "
|
137
|
-
let(:
|
66
|
+
context "when :with option is a singular decorator" do
|
67
|
+
let(:options) { {with: decorator} }
|
68
|
+
let(:decorator) { SpecificProductDecorator }
|
138
69
|
|
139
|
-
it "
|
140
|
-
|
70
|
+
it "uses a CollectionDecorator of the specified decorator" do
|
71
|
+
decorator.should_receive(:decorate_collection).with(associated, expected_options).and_return(:decorated_collection)
|
72
|
+
decorated_association.call.should be :decorated_collection
|
141
73
|
end
|
142
74
|
end
|
143
75
|
|
144
|
-
context "
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
subject.should be_a Draper::CollectionDecorator
|
149
|
-
subject.decorator_class.should be SpecificProductDecorator
|
76
|
+
context "when :with option was not given" do
|
77
|
+
it "uses a CollectionDecorator of inferred decorators" do
|
78
|
+
Draper::CollectionDecorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated_collection)
|
79
|
+
decorated_association.call.should be :decorated_collection
|
150
80
|
end
|
151
81
|
end
|
152
82
|
end
|
153
83
|
|
154
|
-
context "when a collection decorator is specified" do
|
155
|
-
let(:association) { :similar_products }
|
156
|
-
let(:options) { {with: ProductsDecorator} }
|
157
|
-
|
158
|
-
it "decorates with the specified decorator" do
|
159
|
-
subject.should be_a ProductsDecorator
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
84
|
context "with a scope" do
|
164
|
-
let(:
|
85
|
+
let(:associated) { [] }
|
165
86
|
let(:options) { {scope: :foo} }
|
166
87
|
|
167
88
|
it "applies the scope before decoration" do
|
168
|
-
scoped = [
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context "base has context" do
|
175
|
-
let(:association) { :similar_products }
|
176
|
-
let(:base) { source.decorate(context: {some: 'context'}) }
|
177
|
-
|
178
|
-
context "when no context is specified" do
|
179
|
-
it "it should inherit context from base" do
|
180
|
-
subject.context.should == {some: 'context'}
|
181
|
-
end
|
182
|
-
|
183
|
-
it "it should share context hash with base" do
|
184
|
-
subject.context.should be base.context
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
context "when static context is specified" do
|
189
|
-
let(:options) { {context: {other: 'context'}} }
|
190
|
-
|
191
|
-
it "it should get context from static option" do
|
192
|
-
subject.context.should == {other: 'context'}
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
context "when lambda context is specified" do
|
197
|
-
let(:options) { {context: lambda {|context| context.merge(other: 'protext')}} }
|
198
|
-
|
199
|
-
it "it should get generated context" do
|
200
|
-
subject.context.should == {some: 'context', other: 'protext'}
|
201
|
-
end
|
89
|
+
scoped = [:scoped]
|
90
|
+
associated.should_receive(:foo).and_return(scoped)
|
91
|
+
decorated_association.call.source.should == scoped
|
202
92
|
end
|
203
93
|
end
|
204
94
|
end
|
205
95
|
|
206
|
-
describe "#
|
207
|
-
|
208
|
-
|
209
|
-
context "collection association" do
|
210
|
-
let(:association) { :similar_products }
|
96
|
+
describe "#context" do
|
97
|
+
before { owner.stub context: :owner_context }
|
211
98
|
|
212
|
-
|
213
|
-
|
214
|
-
should == {with: :infer, context: {}}
|
215
|
-
end
|
99
|
+
context "when :context option was given" do
|
100
|
+
let(:options) { {context: context} }
|
216
101
|
|
217
|
-
|
218
|
-
|
219
|
-
subject
|
220
|
-
decorated_association.send(:options).should == {with: :infer}
|
221
|
-
end
|
222
|
-
end
|
102
|
+
context "and is callable" do
|
103
|
+
let(:context) { ->(*){ :dynamic_context } }
|
223
104
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
should == {with: ProductDecorator, context: {}}
|
105
|
+
it "calls it with the owner's context" do
|
106
|
+
context.should_receive(:call).with(:owner_context)
|
107
|
+
decorated_association.context
|
228
108
|
end
|
229
|
-
end
|
230
109
|
|
231
|
-
|
232
|
-
|
233
|
-
it "should strip scope: from options" do
|
234
|
-
decorated_association.send(:options).should == options
|
235
|
-
should == {with: :infer, context: {}}
|
110
|
+
it "returns the lambda's return value" do
|
111
|
+
decorated_association.context.should be :dynamic_context
|
236
112
|
end
|
237
113
|
end
|
238
114
|
|
239
|
-
context "
|
240
|
-
let(:
|
115
|
+
context "and is not callable" do
|
116
|
+
let(:context) { :static_context }
|
241
117
|
|
242
|
-
|
243
|
-
|
244
|
-
should == {with: :infer, context: {some: 'context'}}
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
context "option context: {other: 'context'}" do
|
249
|
-
let(:options) { {context: {other: 'context'}} }
|
250
|
-
it "should return specified context" do
|
251
|
-
should == {with: :infer, context: {other: 'context'}}
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
context "option context: lambda" do
|
256
|
-
let(:options) { {context: lambda {|context| context.merge(other: 'protext')}} }
|
257
|
-
it "should return specified context" do
|
258
|
-
should == {with: :infer, context: {some: 'context', other: 'protext'}}
|
259
|
-
end
|
118
|
+
it "returns the specified value" do
|
119
|
+
decorated_association.context.should be :static_context
|
260
120
|
end
|
261
121
|
end
|
262
122
|
end
|
263
123
|
|
264
|
-
context "
|
265
|
-
|
266
|
-
|
267
|
-
context "no options" do
|
268
|
-
it "should return default options" do
|
269
|
-
should == {context: {}}
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
context "option with: ProductDecorator" do
|
274
|
-
let(:options) { {with: ProductDecorator} }
|
275
|
-
it "should strip with: from options" do
|
276
|
-
should == {context: {}}
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
context "option scope: :decorate" do
|
281
|
-
let(:options) { {scope: :decorate} }
|
282
|
-
it "should strip scope: from options" do
|
283
|
-
decorated_association.send(:options).should == options
|
284
|
-
should == {context: {}}
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
context "base has context" do
|
289
|
-
let(:base) { source.decorate(context: {some: 'context'}) }
|
290
|
-
|
291
|
-
context "no options" do
|
292
|
-
it "should return context from base" do
|
293
|
-
should == {context: {some: 'context'}}
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
context "option context: {other: 'context'}" do
|
298
|
-
let(:options) { {context: {other: 'context'}} }
|
299
|
-
it "should return specified context" do
|
300
|
-
should == {context: {other: 'context'}}
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
context "option context: lambda" do
|
305
|
-
let(:options) { {context: lambda {|context| context.merge(other: 'protext')}} }
|
306
|
-
it "should return specified context" do
|
307
|
-
should == {context: {some: 'context', other: 'protext'}}
|
308
|
-
end
|
309
|
-
end
|
124
|
+
context "when :context option was not given" do
|
125
|
+
it "returns the owner's context" do
|
126
|
+
decorated_association.context.should be :owner_context
|
310
127
|
end
|
311
128
|
end
|
312
129
|
end
|
130
|
+
|
313
131
|
end
|
@@ -94,22 +94,13 @@ describe Draper::Decorator do
|
|
94
94
|
|
95
95
|
it "returns a collection decorator" do
|
96
96
|
subject.should be_a Draper::CollectionDecorator
|
97
|
-
subject.source.should
|
97
|
+
subject.source.should == source
|
98
98
|
end
|
99
99
|
|
100
100
|
it "uses itself as the item decorator by default" do
|
101
101
|
subject.each {|item| item.should be_a ProductDecorator}
|
102
102
|
end
|
103
103
|
|
104
|
-
context "when given :with => :infer" do
|
105
|
-
subject { ProductDecorator.decorate_collection(source, with: :infer) }
|
106
|
-
|
107
|
-
it "infers the item decorators" do
|
108
|
-
subject.first.should be_a ProductDecorator
|
109
|
-
subject.last.should be_a WidgetDecorator
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
104
|
context "with context" do
|
114
105
|
subject { ProductDecorator.decorate_collection(source, with: :infer, context: {some: 'context'}) }
|
115
106
|
|
data/spec/draper/finders_spec.rb
CHANGED
@@ -64,7 +64,7 @@ describe Draper::Finders do
|
|
64
64
|
|
65
65
|
describe ".find_all_by_" do
|
66
66
|
it "proxies to the model class" do
|
67
|
-
Product.should_receive(:find_all_by_name_and_size).with("apples", "large")
|
67
|
+
Product.should_receive(:find_all_by_name_and_size).with("apples", "large").and_return([])
|
68
68
|
ProductDecorator.find_all_by_name_and_size("apples", "large")
|
69
69
|
end
|
70
70
|
|
@@ -73,7 +73,7 @@ describe Draper::Finders do
|
|
73
73
|
Product.stub(:find_all_by_name).and_return(found)
|
74
74
|
decorator = ProductDecorator.find_all_by_name("apples")
|
75
75
|
decorator.should be_a Draper::CollectionDecorator
|
76
|
-
decorator.source.should
|
76
|
+
decorator.source.should == found
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: draper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta5
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-12-
|
13
|
+
date: 2012-12-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -51,7 +51,7 @@ dependencies:
|
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0.
|
54
|
+
version: 1.0.3
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
57
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -59,7 +59,7 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - ~>
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 1.0.
|
62
|
+
version: 1.0.3
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
64
|
name: ammeter
|
65
65
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,7 +99,7 @@ dependencies:
|
|
99
99
|
requirements:
|
100
100
|
- - ~>
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '2.
|
102
|
+
version: '2.12'
|
103
103
|
type: :development
|
104
104
|
prerelease: false
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -107,7 +107,23 @@ dependencies:
|
|
107
107
|
requirements:
|
108
108
|
- - ~>
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '2.
|
110
|
+
version: '2.12'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec-mocks
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 2.12.1
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 2.12.1
|
111
127
|
- !ruby/object:Gem::Dependency
|
112
128
|
name: yard
|
113
129
|
requirement: !ruby/object:Gem::Requirement
|