draper 0.17.0 → 1.0.0.beta1

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 (154) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.markdown +14 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +50 -31
  6. data/Readme.markdown +63 -92
  7. data/draper.gemspec +4 -2
  8. data/lib/draper/collection_decorator.rb +88 -0
  9. data/lib/draper/decoratable.rb +44 -0
  10. data/lib/draper/decorated_association.rb +55 -0
  11. data/lib/draper/decorator.rb +208 -0
  12. data/lib/draper/finders.rb +44 -0
  13. data/lib/draper/helper_proxy.rb +16 -0
  14. data/lib/draper/railtie.rb +17 -21
  15. data/lib/draper/security.rb +48 -0
  16. data/lib/draper/tasks/tu.rake +5 -0
  17. data/lib/draper/test/minitest_integration.rb +5 -24
  18. data/lib/draper/test/rspec_integration.rb +0 -6
  19. data/lib/draper/test/test_unit_integration.rb +9 -0
  20. data/lib/draper/version.rb +1 -1
  21. data/lib/draper/view_context.rb +24 -6
  22. data/lib/draper/view_helpers.rb +36 -0
  23. data/lib/draper.rb +42 -7
  24. data/lib/generators/decorator/decorator_generator.rb +1 -1
  25. data/lib/generators/decorator/templates/decorator.rb +0 -1
  26. data/lib/generators/rspec/decorator_generator.rb +1 -1
  27. data/lib/generators/test_unit/decorator_generator.rb +1 -1
  28. data/lib/generators/test_unit/templates/decorator_test.rb +0 -3
  29. data/spec/draper/collection_decorator_spec.rb +240 -0
  30. data/spec/draper/decoratable_spec.rb +164 -0
  31. data/spec/draper/decorated_association_spec.rb +130 -0
  32. data/spec/draper/decorator_spec.rb +497 -0
  33. data/spec/draper/finders_spec.rb +156 -0
  34. data/spec/draper/helper_proxy_spec.rb +12 -0
  35. data/spec/draper/security_spec.rb +158 -0
  36. data/spec/draper/view_helpers_spec.rb +41 -0
  37. data/spec/dummy/.rspec +2 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  41. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  42. data/spec/dummy/app/controllers/posts_controller.rb +17 -0
  43. data/spec/dummy/app/decorators/post_decorator.rb +25 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  46. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  47. data/spec/dummy/app/models/post.rb +3 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  49. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  50. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  51. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  52. data/spec/dummy/config/application.rb +64 -0
  53. data/spec/dummy/config/boot.rb +10 -0
  54. data/spec/dummy/config/database.yml +25 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +34 -0
  57. data/spec/dummy/config/environments/production.rb +55 -0
  58. data/spec/dummy/config/environments/test.rb +32 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/inflections.rb +15 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  63. data/spec/dummy/config/initializers/session_store.rb +8 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +5 -0
  66. data/spec/dummy/config/routes.rb +7 -0
  67. data/spec/dummy/config.ru +4 -0
  68. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  69. data/spec/dummy/db/schema.rb +21 -0
  70. data/spec/dummy/db/seeds.rb +2 -0
  71. data/spec/dummy/lib/tasks/spec.rake +5 -0
  72. data/spec/dummy/log/.gitkeep +0 -0
  73. data/spec/dummy/public/404.html +26 -0
  74. data/spec/dummy/public/422.html +26 -0
  75. data/spec/dummy/public/500.html +25 -0
  76. data/spec/dummy/public/favicon.ico +0 -0
  77. data/spec/dummy/script/rails +6 -0
  78. data/spec/dummy/spec/decorators/post_decorator_spec.rb +23 -0
  79. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  80. data/spec/dummy/spec/spec_helper.rb +9 -0
  81. data/spec/generators/decorator/decorator_generator_spec.rb +24 -5
  82. data/spec/integration/integration_spec.rb +33 -0
  83. data/spec/minitest-rails/spec_type_spec.rb +63 -0
  84. data/{performance → spec/performance}/decorators.rb +2 -4
  85. data/spec/spec_helper.rb +23 -26
  86. data/spec/support/action_controller.rb +12 -0
  87. data/spec/support/active_model.rb +7 -0
  88. data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
  89. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  90. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  91. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  92. data/spec/support/decorators/product_decorator.rb +19 -0
  93. data/spec/support/decorators/products_decorator.rb +10 -0
  94. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  95. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  96. data/spec/support/dummy_app.rb +84 -0
  97. data/spec/support/matchers/have_text.rb +50 -0
  98. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  99. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  100. data/spec/support/{samples → models}/product.rb +13 -2
  101. data/spec/support/models/some_thing.rb +5 -0
  102. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  103. metadata +207 -98
  104. data/doc/ApplicationDecorator.html +0 -147
  105. data/doc/Draper/AllHelpers.html +0 -256
  106. data/doc/Draper/Base.html +0 -1222
  107. data/doc/Draper/DecoratorGenerator.html +0 -216
  108. data/doc/Draper/LazyHelpers.html +0 -174
  109. data/doc/Draper/ModelSupport.html +0 -164
  110. data/doc/Draper/System.html +0 -179
  111. data/doc/Draper.html +0 -123
  112. data/doc/_index.html +0 -172
  113. data/doc/class_list.html +0 -47
  114. data/doc/css/common.css +0 -1
  115. data/doc/css/full_list.css +0 -53
  116. data/doc/css/style.css +0 -320
  117. data/doc/file_list.html +0 -46
  118. data/doc/frames.html +0 -13
  119. data/doc/index.html +0 -172
  120. data/doc/js/app.js +0 -205
  121. data/doc/js/full_list.js +0 -150
  122. data/doc/js/jquery.js +0 -16
  123. data/doc/method_list.html +0 -174
  124. data/doc/top-level-namespace.html +0 -103
  125. data/lib/draper/active_model_support.rb +0 -27
  126. data/lib/draper/base.rb +0 -312
  127. data/lib/draper/decorated_enumerable_proxy.rb +0 -57
  128. data/lib/draper/model_support.rb +0 -17
  129. data/lib/draper/rspec_integration.rb +0 -2
  130. data/lib/draper/system.rb +0 -10
  131. data/lib/draper/test/view_context.rb +0 -12
  132. data/spec/draper/base_spec.rb +0 -863
  133. data/spec/draper/helper_support_spec.rb +0 -18
  134. data/spec/draper/model_support_spec.rb +0 -48
  135. data/spec/draper/view_context_spec.rb +0 -30
  136. data/spec/support/samples/active_model.rb +0 -9
  137. data/spec/support/samples/application_controller.rb +0 -42
  138. data/spec/support/samples/application_helper.rb +0 -8
  139. data/spec/support/samples/decorator.rb +0 -5
  140. data/spec/support/samples/decorator_with_allows.rb +0 -3
  141. data/spec/support/samples/decorator_with_denies.rb +0 -3
  142. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  143. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  144. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  145. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  146. data/spec/support/samples/product_decorator.rb +0 -7
  147. data/spec/support/samples/sequel_product.rb +0 -13
  148. data/spec/support/samples/some_thing.rb +0 -2
  149. data/spec/support/samples/some_thing_decorator.rb +0 -3
  150. /data/{performance → spec/performance}/active_record.rb +0 -0
  151. /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  152. /data/{performance → spec/performance}/models.rb +0 -0
  153. /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  154. /data/spec/support/{samples → models}/widget.rb +0 -0
@@ -0,0 +1,240 @@
1
+ require 'spec_helper'
2
+
3
+ describe Draper::CollectionDecorator do
4
+ before { ApplicationController.new.view_context }
5
+ subject { Draper::CollectionDecorator.new(source, with: ProductDecorator) }
6
+ let(:source){ [Product.new, Product.new] }
7
+ let(:non_active_model_source){ NonActiveModelProduct.new }
8
+
9
+ it "decorates a collection's items" do
10
+ subject.each do |item|
11
+ item.should be_decorated_with ProductDecorator
12
+ end
13
+ end
14
+
15
+ it "sets the decorated items' source models" do
16
+ subject.map{|item| item.source}.should == source
17
+ end
18
+
19
+ context "with options" do
20
+ subject { Draper::CollectionDecorator.new(source, with: ProductDecorator, some: "options") }
21
+
22
+ its(:options) { should == {some: "options"} }
23
+
24
+ it "passes options to the individual decorators" do
25
+ subject.each do |item|
26
+ item.options.should == {some: "options"}
27
+ end
28
+ end
29
+
30
+ describe "#options=" do
31
+ it "updates the options on the collection decorator" do
32
+ subject.options = {other: "options"}
33
+ subject.options.should == {other: "options"}
34
+ end
35
+
36
+ it "updates the options on the individual decorators" do
37
+ subject.options = {other: "options"}
38
+ subject.each do |item|
39
+ item.options.should == {other: "options"}
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#initialize" do
46
+ context "when the :with option is given" do
47
+ context "and the decorator can't be inferred from the class" do
48
+ subject { Draper::CollectionDecorator.new(source, with: ProductDecorator) }
49
+
50
+ it "uses the :with option" do
51
+ subject.decorator_class.should be ProductDecorator
52
+ end
53
+ end
54
+
55
+ context "and the decorator is inferrable from the class" do
56
+ subject { ProductsDecorator.new(source, with: SpecificProductDecorator) }
57
+
58
+ it "uses the :with option" do
59
+ subject.decorator_class.should be SpecificProductDecorator
60
+ end
61
+ end
62
+ end
63
+
64
+ context "when the :with option is not given" do
65
+ context "and the decorator can't be inferred from the class" do
66
+ it "raises an UninferrableDecoratorError" do
67
+ expect{Draper::CollectionDecorator.new(source)}.to raise_error Draper::UninferrableDecoratorError
68
+ end
69
+ end
70
+
71
+ context "and the decorator is inferrable from the class" do
72
+ subject { ProductsDecorator.new(source) }
73
+
74
+ it "infers the decorator" do
75
+ subject.decorator_class.should be ProductDecorator
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#source" do
82
+ it "returns the source collection" do
83
+ subject.source.should be source
84
+ end
85
+
86
+ it "is aliased to #to_source" do
87
+ subject.to_source.should be source
88
+ end
89
+ end
90
+
91
+ describe "#find" do
92
+ context "with a block" do
93
+ it "decorates Enumerable#find" do
94
+ subject.decorated_collection.should_receive(:find)
95
+ subject.find {|p| p.title == "title" }
96
+ end
97
+ end
98
+
99
+ context "without a block" do
100
+ it "decorates Model.find" do
101
+ source.should_not_receive(:find)
102
+ Product.should_receive(:find).with(1).and_return(:product)
103
+ subject.find(1).should == ProductDecorator.new(:product)
104
+ end
105
+ end
106
+ end
107
+
108
+ describe "#helpers" do
109
+ it "returns a HelperProxy" do
110
+ subject.helpers.should be_a Draper::HelperProxy
111
+ end
112
+
113
+ it "is aliased to #h" do
114
+ subject.h.should be subject.helpers
115
+ end
116
+
117
+ it "initializes the wrapper only once" do
118
+ helper_proxy = subject.helpers
119
+ helper_proxy.stub(:test_method) { "test_method" }
120
+ subject.helpers.test_method.should == "test_method"
121
+ subject.helpers.test_method.should == "test_method"
122
+ end
123
+ end
124
+
125
+ describe "#localize" do
126
+ before { subject.helpers.should_receive(:localize).with(:an_object, {some: "options"}) }
127
+
128
+ it "delegates to helpers" do
129
+ subject.localize(:an_object, some: "options")
130
+ end
131
+
132
+ it "is aliased to #l" do
133
+ subject.l(:an_object, some: "options")
134
+ end
135
+ end
136
+
137
+ describe ".helpers" do
138
+ it "returns a HelperProxy" do
139
+ subject.class.helpers.should be_a Draper::HelperProxy
140
+ end
141
+
142
+ it "is aliased to .h" do
143
+ subject.class.h.should be subject.class.helpers
144
+ end
145
+ end
146
+
147
+ describe "#to_ary" do
148
+ # required for `render @collection` in Rails
149
+ it "delegates to the decorated collection" do
150
+ subject.decorated_collection.should_receive(:to_ary).and_return(:an_array)
151
+ subject.to_ary.should == :an_array
152
+ end
153
+ end
154
+
155
+ describe "#respond_to?" do
156
+ it "returns true for its own methods" do
157
+ subject.should respond_to :decorated_collection
158
+ end
159
+
160
+ it "returns true for the wrapped collection's methods" do
161
+ source.stub(:respond_to?).with(:whatever, true).and_return(true)
162
+ subject.respond_to?(:whatever, true).should be_true
163
+ end
164
+ end
165
+
166
+ context "Array methods" do
167
+ describe "#include?" do
168
+ it "delegates to the decorated collection" do
169
+ subject.decorated_collection.should_receive(:include?).with(:something).and_return(true)
170
+ subject.should include :something
171
+ end
172
+ end
173
+
174
+ describe "#[]" do
175
+ it "delegates to the decorated collection" do
176
+ subject.decorated_collection.should_receive(:[]).with(42).and_return(:something)
177
+ subject[42].should == :something
178
+ end
179
+ end
180
+ end
181
+
182
+ describe "#==" do
183
+ context "when comparing to a collection decorator with the same source" do
184
+ it "returns true" do
185
+ a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
186
+ b = ProductsDecorator.new(source)
187
+ a.should == b
188
+ end
189
+ end
190
+
191
+ context "when comparing to a collection decorator with a different source" do
192
+ it "returns false" do
193
+ a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
194
+ b = ProductsDecorator.new([Product.new])
195
+ a.should_not == b
196
+ end
197
+ end
198
+
199
+ context "when comparing to a collection of the same items" do
200
+ it "returns true" do
201
+ a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
202
+ b = source.dup
203
+ a.should == b
204
+ end
205
+ end
206
+
207
+ context "when comparing to a collection of different items" do
208
+ it "returns true" do
209
+ a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
210
+ b = [Product.new]
211
+ a.should_not == b
212
+ end
213
+ end
214
+ end
215
+
216
+ it "pretends to be the source class" do
217
+ subject.kind_of?(source.class).should be_true
218
+ subject.is_a?(source.class).should be_true
219
+ end
220
+
221
+ it "is still its own class" do
222
+ subject.kind_of?(subject.class).should be_true
223
+ subject.is_a?(subject.class).should be_true
224
+ end
225
+
226
+ describe "#method_missing" do
227
+ before do
228
+ class << source
229
+ def page_number
230
+ 42
231
+ end
232
+ end
233
+ end
234
+
235
+ it "proxies unknown methods to the source collection" do
236
+ subject.page_number.should == 42
237
+ end
238
+ end
239
+
240
+ end
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ describe Draper::Decoratable do
4
+ subject { Product.new }
5
+
6
+ describe "#decorate" do
7
+ it "returns a decorator for self" do
8
+ subject.decorate.should be_a ProductDecorator
9
+ subject.decorate.source.should be subject
10
+ end
11
+
12
+ it "accepts options" do
13
+ decorator = subject.decorate(some: "options")
14
+ decorator.options.should == {some: "options"}
15
+ end
16
+
17
+ it "is not memoized" do
18
+ subject.decorate.should_not be subject.decorate
19
+ end
20
+ end
21
+
22
+ describe "#applied_decorators" do
23
+ it "returns an empty list" do
24
+ subject.applied_decorators.should be_empty
25
+ end
26
+ end
27
+
28
+ describe "#decorated_with?" do
29
+ it "returns false" do
30
+ subject.should_not be_decorated_with ProductDecorator
31
+ end
32
+ end
33
+
34
+ describe "#decorated?" do
35
+ it "returns false" do
36
+ subject.should_not be_decorated
37
+ end
38
+ end
39
+
40
+ describe "#decorator_class" do
41
+ it "delegates to .decorator_class" do
42
+ Product.stub(:decorator_class).and_return(WidgetDecorator)
43
+ product = Product.new
44
+ product.decorator_class.should be WidgetDecorator
45
+ end
46
+ end
47
+
48
+ describe "#===" do
49
+ context "with itself" do
50
+ it "returns true" do
51
+ (subject === subject).should be_true
52
+ end
53
+ end
54
+
55
+ context "with another instance" do
56
+ it "returns false" do
57
+ (subject === Product.new).should be_false
58
+ end
59
+ end
60
+
61
+ context "with a decorated version of itself" do
62
+ it "returns true" do
63
+ decorator = double(source: subject)
64
+ (subject === decorator).should be_true
65
+ end
66
+ end
67
+
68
+ context "with a decorated other instance" do
69
+ it "returns false" do
70
+ decorator = double(source: Product.new)
71
+ (subject === decorator).should be_false
72
+ end
73
+ end
74
+ end
75
+
76
+ describe ".====" do
77
+ context "with an instance" do
78
+ it "returns true" do
79
+ (Product === Product.new).should be_true
80
+ end
81
+ end
82
+
83
+ context "with a derived instance" do
84
+ it "returns true" do
85
+ (Product === Widget.new).should be_true
86
+ end
87
+ end
88
+
89
+ context "with an unrelated instance" do
90
+ it "returns false" do
91
+ (Product === Object.new).should be_false
92
+ end
93
+ end
94
+
95
+ context "with a decorated instance" do
96
+ it "returns true" do
97
+ decorator = double(source: Product.new)
98
+ (Product === decorator).should be_true
99
+ end
100
+ end
101
+
102
+ context "with a decorated derived instance" do
103
+ it "returns true" do
104
+ decorator = double(source: Widget.new)
105
+ (Product === decorator).should be_true
106
+ end
107
+ end
108
+
109
+ context "with a decorated unrelated instance" do
110
+ it "returns false" do
111
+ decorator = double(source: Object.new)
112
+ (Product === decorator).should be_false
113
+ end
114
+ end
115
+ end
116
+
117
+ describe ".decorate" do
118
+ it "returns a collection decorator" do
119
+ Product.stub(:scoped).and_return([Product.new])
120
+ Product.stub(:decorator_class).and_return(WidgetDecorator)
121
+ decorator = Product.decorate
122
+
123
+ decorator.should be_a Draper::CollectionDecorator
124
+ decorator.decorator_class.should be WidgetDecorator
125
+ decorator.source.should be Product.scoped
126
+ end
127
+
128
+ it "accepts options" do
129
+ decorator = Product.decorate(some: "options")
130
+ decorator.options.should == {some: "options"}
131
+ end
132
+
133
+ it "is not memoized" do
134
+ Product.decorate.should_not be Product.decorate
135
+ end
136
+ end
137
+
138
+ describe ".decorator_class" do
139
+ context "for non-ActiveModel classes" do
140
+ it "infers the decorator from the class" do
141
+ NonActiveModelProduct.decorator_class.should be NonActiveModelProductDecorator
142
+ end
143
+ end
144
+
145
+ context "for ActiveModel classes" do
146
+ it "infers the decorator from the model name" do
147
+ Product.stub(:model_name).and_return("Widget")
148
+ Product.decorator_class.should be WidgetDecorator
149
+ end
150
+ end
151
+
152
+ context "for namespaced ActiveModel classes" do
153
+ it "infers the decorator from the model name" do
154
+ Namespace::Product.decorator_class.should be Namespace::ProductDecorator
155
+ end
156
+ end
157
+
158
+ context "when the decorator can't be inferred" do
159
+ it "throws an UninferrableDecoratorError" do
160
+ expect{UninferrableDecoratorModel.decorator_class}.to raise_error Draper::UninferrableDecoratorError
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Draper::DecoratedAssociation do
4
+ let(:decorated_association) { Draper::DecoratedAssociation.new(source, association, options) }
5
+ let(:source) { Product.new }
6
+ let(:options) { {} }
7
+
8
+ describe "#call" do
9
+ subject { decorated_association.call }
10
+
11
+ context "for an ActiveModel collection association" do
12
+ let(:association) { :similar_products }
13
+
14
+ context "when the association is not empty" do
15
+ it "decorates the collection" do
16
+ subject.should be_a Draper::CollectionDecorator
17
+ end
18
+
19
+ it "infers the decorator" do
20
+ subject.decorator_class.should be :infer
21
+ end
22
+ end
23
+
24
+ context "when the association is empty" do
25
+ it "doesn't decorate the collection" do
26
+ source.stub(:similar_products).and_return([])
27
+ subject.should_not be_a Draper::CollectionDecorator
28
+ subject.should be_empty
29
+ end
30
+ end
31
+ end
32
+
33
+ context "for non-ActiveModel collection associations" do
34
+ let(:association) { :poro_similar_products }
35
+
36
+ context "when the association is not empty" do
37
+ it "decorates the collection" do
38
+ subject.should be_a Draper::CollectionDecorator
39
+ end
40
+
41
+ it "infers the decorator" do
42
+ subject.decorator_class.should be :infer
43
+ end
44
+ end
45
+
46
+ context "when the association is empty" do
47
+ it "doesn't decorate the collection" do
48
+ source.stub(:poro_similar_products).and_return([])
49
+ subject.should_not be_a Draper::CollectionDecorator
50
+ subject.should be_empty
51
+ end
52
+ end
53
+ end
54
+
55
+ context "for an ActiveModel singular association" do
56
+ let(:association) { :previous_version }
57
+
58
+ context "when the association is present" do
59
+ it "decorates the association" do
60
+ subject.should be_decorated_with ProductDecorator
61
+ end
62
+ end
63
+
64
+ context "when the association is absent" do
65
+ it "doesn't decorate the association" do
66
+ source.stub(:previous_version).and_return(nil)
67
+ subject.should be_nil
68
+ end
69
+ end
70
+ end
71
+
72
+ context "for a non-ActiveModel singular association" do
73
+ let(:association) { :poro_previous_version }
74
+
75
+ context "when the association is present" do
76
+ it "decorates the association" do
77
+ subject.should be_decorated_with ProductDecorator
78
+ end
79
+ end
80
+
81
+ context "when the association is absent" do
82
+ it "doesn't decorate the association" do
83
+ source.stub(:poro_previous_version).and_return(nil)
84
+ subject.should be_nil
85
+ end
86
+ end
87
+ end
88
+
89
+ context "when a decorator is specified" do
90
+ let(:options) { {with: SpecificProductDecorator} }
91
+
92
+ context "for a singular association" do
93
+ let(:association) { :previous_version }
94
+
95
+ it "decorates with the specified decorator" do
96
+ subject.should be_decorated_with SpecificProductDecorator
97
+ end
98
+ end
99
+
100
+ context "for a collection association" do
101
+ let(:association) { :similar_products}
102
+
103
+ it "decorates with a collection of the specifed decorators" do
104
+ subject.should be_a Draper::CollectionDecorator
105
+ subject.decorator_class.should be SpecificProductDecorator
106
+ end
107
+ end
108
+ end
109
+
110
+ context "when a collection decorator is specified" do
111
+ let(:association) { :similar_products }
112
+ let(:options) { {with: ProductsDecorator} }
113
+
114
+ it "decorates with the specified decorator" do
115
+ subject.should be_a ProductsDecorator
116
+ end
117
+ end
118
+
119
+ context "with a scope" do
120
+ let(:association) { :thing }
121
+ let(:options) { {scope: :foo} }
122
+
123
+ it "applies the scope before decoration" do
124
+ scoped = [SomeThing.new]
125
+ SomeThing.any_instance.should_receive(:foo).and_return(scoped)
126
+ subject.source.should be scoped
127
+ end
128
+ end
129
+ end
130
+ end