draper 0.17.0 → 1.0.0.beta2
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/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.markdown +49 -0
- data/Gemfile +4 -0
- data/Rakefile +50 -31
- data/Readme.markdown +63 -92
- data/draper.gemspec +6 -4
- data/lib/draper/collection_decorator.rb +88 -0
- data/lib/draper/decoratable.rb +44 -0
- data/lib/draper/decorated_association.rb +55 -0
- data/lib/draper/decorator.rb +260 -0
- data/lib/draper/finders.rb +34 -0
- data/lib/draper/helper_proxy.rb +16 -0
- data/lib/draper/railtie.rb +17 -21
- data/lib/draper/security.rb +48 -0
- data/lib/draper/tasks/tu.rake +5 -0
- data/lib/draper/test/minitest_integration.rb +5 -24
- data/lib/draper/test/rspec_integration.rb +0 -6
- data/lib/draper/test/test_unit_integration.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +24 -6
- data/lib/draper/view_helpers.rb +36 -0
- data/lib/draper.rb +48 -7
- data/lib/generators/decorator/decorator_generator.rb +1 -1
- data/lib/generators/decorator/templates/decorator.rb +0 -1
- data/lib/generators/rspec/decorator_generator.rb +1 -1
- data/lib/generators/test_unit/decorator_generator.rb +1 -1
- data/lib/generators/test_unit/templates/decorator_test.rb +0 -3
- data/spec/draper/collection_decorator_spec.rb +240 -0
- data/spec/draper/decoratable_spec.rb +164 -0
- data/spec/draper/decorated_association_spec.rb +130 -0
- data/spec/draper/decorator_spec.rb +622 -0
- data/spec/draper/finders_spec.rb +145 -0
- data/spec/draper/helper_proxy_spec.rb +12 -0
- data/spec/draper/security_spec.rb +158 -0
- data/spec/draper/view_helpers_spec.rb +41 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/localized_urls.rb +5 -0
- data/spec/dummy/app/controllers/posts_controller.rb +17 -0
- data/spec/dummy/app/decorators/post_decorator.rb +29 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +9 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +11 -0
- data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
- data/spec/dummy/app/views/posts/_post.html.erb +19 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/config/application.rb +64 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/production.rb +55 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
- data/spec/dummy/db/schema.rb +21 -0
- data/spec/dummy/db/seeds.rb +2 -0
- data/spec/dummy/lib/tasks/spec.rake +5 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
- data/spec/dummy/spec/spec_helper.rb +9 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +25 -6
- data/spec/integration/integration_spec.rb +33 -0
- data/spec/minitest-rails/spec_type_spec.rb +63 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/spec/spec_helper.rb +23 -26
- data/spec/support/action_controller.rb +12 -0
- data/spec/support/active_model.rb +7 -0
- data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
- data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
- data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
- data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
- data/spec/support/decorators/product_decorator.rb +23 -0
- data/spec/support/decorators/products_decorator.rb +10 -0
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/dummy_app.rb +84 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/{samples → models}/namespaced_product.rb +1 -3
- data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
- data/spec/support/{samples → models}/product.rb +17 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- metadata +211 -102
- data/doc/ApplicationDecorator.html +0 -147
- data/doc/Draper/AllHelpers.html +0 -256
- data/doc/Draper/Base.html +0 -1222
- data/doc/Draper/DecoratorGenerator.html +0 -216
- data/doc/Draper/LazyHelpers.html +0 -174
- data/doc/Draper/ModelSupport.html +0 -164
- data/doc/Draper/System.html +0 -179
- data/doc/Draper.html +0 -123
- data/doc/_index.html +0 -172
- data/doc/class_list.html +0 -47
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -53
- data/doc/css/style.css +0 -320
- data/doc/file_list.html +0 -46
- data/doc/frames.html +0 -13
- data/doc/index.html +0 -172
- data/doc/js/app.js +0 -205
- data/doc/js/full_list.js +0 -150
- data/doc/js/jquery.js +0 -16
- data/doc/method_list.html +0 -174
- data/doc/top-level-namespace.html +0 -103
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -57
- data/lib/draper/model_support.rb +0 -17
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -10
- data/lib/draper/test/view_context.rb +0 -12
- data/spec/draper/base_spec.rb +0 -863
- data/spec/draper/helper_support_spec.rb +0 -18
- data/spec/draper/model_support_spec.rb +0 -48
- data/spec/draper/view_context_spec.rb +0 -30
- data/spec/support/samples/active_model.rb +0 -9
- data/spec/support/samples/application_controller.rb +0 -42
- data/spec/support/samples/application_helper.rb +0 -8
- data/spec/support/samples/decorator.rb +0 -5
- data/spec/support/samples/decorator_with_allows.rb +0 -3
- data/spec/support/samples/decorator_with_denies.rb +0 -3
- data/spec/support/samples/decorator_with_denies_all.rb +0 -3
- data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
- data/spec/support/samples/decorator_with_special_methods.rb +0 -13
- data/spec/support/samples/namespaced_product_decorator.rb +0 -7
- data/spec/support/samples/product_decorator.rb +0 -7
- data/spec/support/samples/sequel_product.rb +0 -13
- data/spec/support/samples/some_thing.rb +0 -2
- data/spec/support/samples/some_thing_decorator.rb +0 -3
- /data/{performance → spec/performance}/active_record.rb +0 -0
- /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- /data/{performance → spec/performance}/models.rb +0 -0
- /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- /data/spec/support/{samples → models}/widget.rb +0 -0
data/lib/draper.rb
CHANGED
|
@@ -1,16 +1,57 @@
|
|
|
1
1
|
require 'action_view'
|
|
2
2
|
|
|
3
3
|
require 'draper/version'
|
|
4
|
-
require 'draper/
|
|
5
|
-
require 'draper/
|
|
6
|
-
require 'draper/
|
|
4
|
+
require 'draper/view_helpers'
|
|
5
|
+
require 'draper/finders'
|
|
6
|
+
require 'draper/decorator'
|
|
7
|
+
require 'draper/helper_proxy'
|
|
7
8
|
require 'draper/lazy_helpers'
|
|
8
|
-
require 'draper/
|
|
9
|
+
require 'draper/decoratable'
|
|
10
|
+
require 'draper/decorated_association'
|
|
11
|
+
require 'draper/security'
|
|
9
12
|
require 'draper/helper_support'
|
|
10
13
|
require 'draper/view_context'
|
|
11
|
-
require 'draper/
|
|
14
|
+
require 'draper/collection_decorator'
|
|
12
15
|
require 'draper/railtie' if defined?(Rails)
|
|
13
16
|
|
|
14
17
|
# Test Support
|
|
15
|
-
require 'draper/test/rspec_integration'
|
|
16
|
-
require 'draper/test/minitest_integration'
|
|
18
|
+
require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
|
|
19
|
+
require 'draper/test/minitest_integration' if defined?(MiniTest::Rails)
|
|
20
|
+
require 'draper/test/test_unit_integration'
|
|
21
|
+
|
|
22
|
+
module Draper
|
|
23
|
+
def self.setup_action_controller(base)
|
|
24
|
+
base.class_eval do
|
|
25
|
+
include Draper::ViewContext
|
|
26
|
+
extend Draper::HelperSupport
|
|
27
|
+
before_filter ->(controller) {
|
|
28
|
+
Draper::ViewContext.current = nil
|
|
29
|
+
Draper::ViewContext.current_controller = controller
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.setup_action_mailer(base)
|
|
35
|
+
base.class_eval do
|
|
36
|
+
include Draper::ViewContext
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.setup_active_record(base)
|
|
41
|
+
base.class_eval do
|
|
42
|
+
include Draper::Decoratable
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class UninferrableDecoratorError < NameError
|
|
47
|
+
def initialize(klass)
|
|
48
|
+
super("Could not infer a decorator for #{klass}.")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class UninferrableSourceError < NameError
|
|
53
|
+
def initialize(klass)
|
|
54
|
+
super("Could not infer a source for #{klass}.")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -3,7 +3,7 @@ module Rspec
|
|
|
3
3
|
source_root File.expand_path('../templates', __FILE__)
|
|
4
4
|
|
|
5
5
|
def create_spec_file
|
|
6
|
-
template 'decorator_spec.rb', File.join('spec/decorators', "#{singular_name}_decorator_spec.rb")
|
|
6
|
+
template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -3,7 +3,7 @@ module TestUnit
|
|
|
3
3
|
source_root File.expand_path('../templates', __FILE__)
|
|
4
4
|
|
|
5
5
|
def create_test_file
|
|
6
|
-
template 'decorator_test.rb', File.join('test/decorators', "#{singular_name}_decorator_test.rb")
|
|
6
|
+
template 'decorator_test.rb', File.join('test/decorators', class_path, "#{singular_name}_decorator_test.rb")
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -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
|