draper 0.11.1 → 0.12.0
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.txt +23 -0
- data/Gemfile +4 -0
- data/Readme.markdown +32 -8
- data/draper.gemspec +1 -0
- data/lib/draper.rb +1 -0
- data/lib/draper/active_model_support.rb +24 -0
- data/lib/draper/base.rb +30 -22
- data/lib/draper/decorated_enumerable_proxy.rb +12 -9
- data/lib/draper/helper_support.rb +1 -1
- data/lib/draper/lazy_helpers.rb +1 -1
- data/lib/draper/model_support.rb +2 -4
- data/lib/draper/railtie.rb +31 -0
- data/lib/draper/rspec_integration.rb +22 -11
- data/lib/draper/system.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/generators/decorator/decorator_generator.rb +28 -0
- data/lib/generators/{draper/decorator → decorator}/templates/decorator.rb +5 -3
- data/lib/generators/resource_override.rb +12 -0
- data/lib/generators/rspec/decorator_generator.rb +9 -0
- data/lib/generators/rspec/templates/decorator_spec.rb +4 -0
- data/lib/generators/test_unit/decorator_generator.rb +9 -0
- data/lib/generators/test_unit/templates/decorator_test.rb +7 -0
- data/performance/active_record.rb +1 -1
- data/performance/models.rb +1 -1
- data/spec/draper/base_spec.rb +74 -15
- data/spec/draper/helper_support_spec.rb +4 -4
- data/spec/draper/model_support_spec.rb +1 -1
- data/spec/draper/view_context_spec.rb +2 -7
- data/spec/generators/decorator/decorator_generator_spec.rb +52 -0
- data/spec/spec_helper.rb +21 -17
- data/spec/support/samples/active_model.rb +9 -0
- data/spec/support/samples/active_record.rb +8 -0
- data/spec/support/samples/application_helper.rb +1 -1
- data/spec/support/samples/decorator.rb +1 -1
- data/spec/support/samples/decorator_with_allows.rb +1 -1
- data/spec/support/samples/non_active_model_product.rb +2 -0
- data/spec/support/samples/specific_product_decorator.rb +1 -1
- metadata +30 -18
- data/lib/generators/draper/decorator/USAGE +0 -7
- data/lib/generators/draper/decorator/decorator_generator.rb +0 -43
- data/lib/generators/draper/decorator/templates/decorator_spec.rb +0 -5
- data/lib/generators/draper/decorator/templates/decorator_test.rb +0 -12
- data/lib/generators/draper/install/install_generator.rb +0 -39
- data/lib/generators/draper/install/templates/application_decorator.rb +0 -28
- data/lib/generators/draper/install/templates/application_decorator_spec.rb +0 -4
- data/lib/generators/draper/install/templates/application_decorator_test.rb +0 -11
- data/lib/generators/rails/decorator_generator.rb +0 -15
- data/spec/generators/draper/decorator/decorator_generator_spec.rb +0 -102
- data/spec/generators/draper/install/install_generator_spec.rb +0 -46
data/lib/draper/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Rails
|
2
|
+
module Generators
|
3
|
+
class DecoratorGenerator < NamedBase
|
4
|
+
source_root File.expand_path("../templates", __FILE__)
|
5
|
+
check_class_collision :suffix => "Decorator"
|
6
|
+
|
7
|
+
class_option :parent, :type => :string, :desc => "The parent class for the generated decorator"
|
8
|
+
|
9
|
+
def create_decorator_file
|
10
|
+
template 'decorator.rb', File.join('app/decorators', class_path, "#{file_name}_decorator.rb")
|
11
|
+
end
|
12
|
+
|
13
|
+
hook_for :test_framework
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parent_class_name
|
18
|
+
if options[:parent]
|
19
|
+
options[:parent]
|
20
|
+
elsif defined?(:ApplicationDecorator)
|
21
|
+
"ApplicationDecorator"
|
22
|
+
else
|
23
|
+
"Draper::Base"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
class <%= class_name %>Decorator < ApplicationDecorator
|
3
|
+
decorates :<%= singular_name %>
|
3
4
|
|
4
5
|
# Accessing Helpers
|
5
6
|
# You can access any helper via a proxy
|
@@ -29,4 +30,5 @@ class <%= resource_name.singularize.camelize %>Decorator < ApplicationDecorator
|
|
29
30
|
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
|
30
31
|
# :class => 'timestamp'
|
31
32
|
# end
|
32
|
-
end
|
33
|
+
end
|
34
|
+
<% end -%>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Rspec
|
2
|
+
class DecoratorGenerator < ::Rails::Generators::NamedBase
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
|
5
|
+
def create_spec_file
|
6
|
+
template 'decorator_spec.rb', File.join('spec/decorators', "#{singular_name}_decorator_spec.rb")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module TestUnit
|
2
|
+
class DecoratorGenerator < ::Rails::Generators::NamedBase
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
|
5
|
+
def create_test_file
|
6
|
+
template 'decorator_test.rb', File.join('test/decorators', "#{singular_name}_decorator_test.rb")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
data/performance/models.rb
CHANGED
data/spec/draper/base_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe Draper::Base do
|
|
4
4
|
before(:each){ ApplicationController.new.set_current_view_context }
|
5
5
|
subject{ Decorator.new(source) }
|
6
6
|
let(:source){ Product.new }
|
7
|
+
let(:non_active_model_source){ NonActiveModelProduct.new }
|
7
8
|
|
8
9
|
context("proxying class methods") do
|
9
10
|
it "should pass missing class method calls on to the wrapped class" do
|
@@ -14,7 +15,7 @@ describe Draper::Base do
|
|
14
15
|
subject.class.should respond_to(:sample_class_method)
|
15
16
|
end
|
16
17
|
|
17
|
-
it "should still respond_to
|
18
|
+
it "should still respond_to its own class methods" do
|
18
19
|
subject.class.should respond_to(:own_class_method)
|
19
20
|
end
|
20
21
|
end
|
@@ -44,6 +45,11 @@ describe Draper::Base do
|
|
44
45
|
ProductDecorator.new(source).model_class.should == Product
|
45
46
|
end
|
46
47
|
|
48
|
+
it "returns decorator if it's decorated model already" do
|
49
|
+
product_decorator = ProductDecorator.new(source)
|
50
|
+
ProductDecorator.new(product_decorator).model.should be_instance_of Product
|
51
|
+
end
|
52
|
+
|
47
53
|
it "should handle plural-like words properly'" do
|
48
54
|
class Business; end
|
49
55
|
expect do
|
@@ -190,7 +196,7 @@ describe Draper::Base do
|
|
190
196
|
end
|
191
197
|
end
|
192
198
|
|
193
|
-
|
199
|
+
describe "method selection" do
|
194
200
|
it "echos the methods of the wrapped class except default exclusions" do
|
195
201
|
source.methods.each do |method|
|
196
202
|
unless Draper::Base::DEFAULT_DENIED.include?(method)
|
@@ -203,27 +209,48 @@ describe Draper::Base do
|
|
203
209
|
DecoratorWithApplicationHelper.new(source).length.should == "overridden"
|
204
210
|
end
|
205
211
|
|
206
|
-
it "should always proxy to_param" do
|
207
|
-
source.send :class_eval, "def to_param; 1; end"
|
208
|
-
Draper::Base.new(source).to_param.should == 1
|
209
|
-
end
|
210
|
-
|
211
|
-
it "should always proxy id" do
|
212
|
-
source.send :class_eval, "def id; 123456789; end"
|
213
|
-
Draper::Base.new(source).id.should == 123456789
|
214
|
-
end
|
215
|
-
|
216
212
|
it "should not copy the .class, .inspect, or other existing methods" do
|
217
213
|
source.class.should_not == subject.class
|
218
214
|
source.inspect.should_not == subject.inspect
|
219
215
|
source.to_s.should_not == subject.to_s
|
220
216
|
end
|
217
|
+
|
218
|
+
context "when an ActiveModel descendant" do
|
219
|
+
it "should always proxy to_param" do
|
220
|
+
source.stub(:to_param).and_return(1)
|
221
|
+
Draper::Base.new(source).to_param.should == 1
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should always proxy id" do
|
225
|
+
source.stub(:id).and_return(123456789)
|
226
|
+
Draper::Base.new(source).id.should == 123456789
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should always proxy errors" do
|
230
|
+
Draper::Base.new(source).errors.should be_an_instance_of ActiveModel::Errors
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "when not an ActiveModel descendant" do
|
235
|
+
it "does not proxy to_param" do
|
236
|
+
non_active_model_source.stub(:to_param).and_return(1)
|
237
|
+
Draper::Base.new(non_active_model_source).to_param.should_not == 1
|
238
|
+
end
|
239
|
+
|
240
|
+
it "does not proxy errors" do
|
241
|
+
Draper::Base.new(non_active_model_source).should_not respond_to :errors
|
242
|
+
end
|
243
|
+
end
|
221
244
|
end
|
222
245
|
|
223
246
|
context 'the decorated model' do
|
224
247
|
it 'receives the mixin' do
|
225
248
|
source.class.ancestors.include?(Draper::ModelSupport)
|
226
249
|
end
|
250
|
+
|
251
|
+
it 'includes ActiveModel support' do
|
252
|
+
source.class.ancestors.include?(Draper::ActiveModelSupport)
|
253
|
+
end
|
227
254
|
end
|
228
255
|
|
229
256
|
it "should wrap source methods so they still accept blocks" do
|
@@ -264,13 +291,32 @@ describe Draper::Base do
|
|
264
291
|
ProductDecorator.find_by_name_and_size("apples", "large")
|
265
292
|
end
|
266
293
|
|
294
|
+
it "runs find_all_by_(x) finders" do
|
295
|
+
Product.should_receive(:find_all_by_name_and_size)
|
296
|
+
ProductDecorator.find_all_by_name_and_size("apples", "large")
|
297
|
+
end
|
298
|
+
|
299
|
+
it "runs find_last_by_(x) finders" do
|
300
|
+
Product.should_receive(:find_last_by_name_and_size)
|
301
|
+
ProductDecorator.find_last_by_name_and_size("apples", "large")
|
302
|
+
end
|
303
|
+
|
304
|
+
it "runs find_or_initialize_by_(x) finders" do
|
305
|
+
Product.should_receive(:find_or_initialize_by_name_and_size)
|
306
|
+
ProductDecorator.find_or_initialize_by_name_and_size("apples", "large")
|
307
|
+
end
|
308
|
+
|
309
|
+
it "runs find_or_create_by_(x) finders" do
|
310
|
+
Product.should_receive(:find_or_create_by_name_and_size)
|
311
|
+
ProductDecorator.find_or_create_by_name_and_size("apples", "large")
|
312
|
+
end
|
313
|
+
|
267
314
|
it "accepts an options hash" do
|
268
315
|
Product.should_receive(:find_by_name_and_size).with("apples", "large", {:role => :admin})
|
269
316
|
ProductDecorator.find_by_name_and_size("apples", "large", {:role => :admin})
|
270
317
|
end
|
271
318
|
|
272
319
|
it "uses the options hash in the decorator instantiation" do
|
273
|
-
pending "Figure out an implementation that supports multiple args (find_by_name_and_count_and_size) plus an options hash"
|
274
320
|
Product.should_receive(:find_by_name_and_size).with("apples", "large", {:role => :admin})
|
275
321
|
pd = ProductDecorator.find_by_name_and_size("apples", "large", {:role => :admin})
|
276
322
|
pd.context[:role].should == :admin
|
@@ -289,6 +335,11 @@ describe Draper::Base do
|
|
289
335
|
it "returns a collection of wrapped objects" do
|
290
336
|
subject.each{ |decorated| decorated.should be_instance_of(Draper::Base) }
|
291
337
|
end
|
338
|
+
|
339
|
+
it 'should accepted and store a context for a collection' do
|
340
|
+
subject.context = :admin
|
341
|
+
subject.each { |decorated| decorated.context.should == :admin }
|
342
|
+
end
|
292
343
|
end
|
293
344
|
|
294
345
|
context "when given a single source object" do
|
@@ -395,6 +446,14 @@ describe Draper::Base do
|
|
395
446
|
end
|
396
447
|
end
|
397
448
|
|
449
|
+
context ".respond_to?" do
|
450
|
+
it "should delegate respond_to? to the decorated model" do
|
451
|
+
other = Draper::Base.new(source)
|
452
|
+
source.should_receive(:respond_to?).with(:whatever, true)
|
453
|
+
subject.respond_to?(:whatever, true)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
398
457
|
context 'position accessors' do
|
399
458
|
[:first, :last].each do |method|
|
400
459
|
context "##{method}" do
|
@@ -441,8 +500,8 @@ describe Draper::Base do
|
|
441
500
|
|
442
501
|
it "should delegate respond_to? to the wrapped collection" do
|
443
502
|
decorator = ProductDecorator.decorate(paged_array)
|
444
|
-
paged_array.should_receive(:respond_to?).with(:whatever)
|
445
|
-
decorator.respond_to?(:whatever)
|
503
|
+
paged_array.should_receive(:respond_to?).with(:whatever, true)
|
504
|
+
decorator.respond_to?(:whatever, true)
|
446
505
|
end
|
447
506
|
|
448
507
|
it "should return blank for a decorated empty collection" do
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Draper::HelperSupport do
|
4
|
-
|
4
|
+
let(:product){ Product.new }
|
5
5
|
|
6
6
|
context '#decorate' do
|
7
7
|
it 'renders a block' do
|
8
|
-
output = ApplicationController.decorate(
|
9
|
-
output.should ==
|
8
|
+
output = ApplicationController.decorate(product){|p| p.model.object_id }
|
9
|
+
output.should == product.object_id
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'uses #capture so Rails only renders the content once' do
|
13
|
-
ApplicationController.decorate(
|
13
|
+
ApplicationController.decorate(product){|p| p.model.object_id }
|
14
14
|
ApplicationController.capture_triggered.should be
|
15
15
|
end
|
16
16
|
end
|
@@ -1,13 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Draper::ViewContext do
|
4
|
-
let
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
let (:app_controller_instance) do
|
9
|
-
app_controller.new
|
10
|
-
end
|
4
|
+
let(:app_controller) { ApplicationController }
|
5
|
+
let(:app_controller_instance) { app_controller.new }
|
11
6
|
|
12
7
|
it "implements #set_current_view_context" do
|
13
8
|
app_controller_instance.should respond_to(:set_current_view_context)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Generators are not automatically loaded by Rails
|
4
|
+
require 'generators/decorator/decorator_generator'
|
5
|
+
|
6
|
+
describe Rails::Generators::DecoratorGenerator do
|
7
|
+
# Tell the generator where to put its output (what it thinks of as Rails.root)
|
8
|
+
destination File.expand_path("../../../../../tmp", __FILE__)
|
9
|
+
|
10
|
+
before { prepare_destination }
|
11
|
+
|
12
|
+
context 'decorator context' do
|
13
|
+
before { run_generator ["YourModel"] }
|
14
|
+
|
15
|
+
describe 'app/decorators/your_model_decorator.rb' do
|
16
|
+
subject { file('app/decorators/your_model_decorator.rb') }
|
17
|
+
it { should exist }
|
18
|
+
it { should contain "class YourModelDecorator < ApplicationDecorator" }
|
19
|
+
it { should contain "decorates :your_model" }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'decorator name' do
|
24
|
+
before { run_generator ["YourModel", '-t=rspec'] }
|
25
|
+
|
26
|
+
describe 'spec/decorators/your_model_decorator_spec.rb' do
|
27
|
+
subject { file('spec/decorators/your_model_decorator_spec.rb') }
|
28
|
+
it { should exist }
|
29
|
+
it { should contain "describe YourModelDecorator" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'using rspec' do
|
34
|
+
before { run_generator ["YourModel", "-t=rspec"] }
|
35
|
+
|
36
|
+
describe 'spec/decorators/your_model_decorator_spec.rb' do
|
37
|
+
subject { file('spec/decorators/your_model_decorator_spec.rb') }
|
38
|
+
it { should exist }
|
39
|
+
it { should contain "describe YourModelDecorator" }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'using rspec' do
|
44
|
+
before { run_generator ["YourModel", "-t=test_unit"] }
|
45
|
+
|
46
|
+
describe 'test/decorators/YourModel_decorator_test.rb' do
|
47
|
+
subject { file('test/decorators/your_model_decorator_test.rb') }
|
48
|
+
it { should exist }
|
49
|
+
it { should contain "class YourModelDecoratorTest < ActiveSupport::TestCase" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler/setup'
|
3
|
+
require 'rails'
|
4
|
+
|
3
5
|
Bundler.require
|
4
6
|
|
5
|
-
require './spec/support/samples/
|
6
|
-
require './spec/support/samples/
|
7
|
-
require './spec/support/samples/
|
8
|
-
require './spec/support/samples/
|
9
|
-
require './spec/support/samples/
|
10
|
-
require './spec/support/samples/
|
11
|
-
require './spec/support/samples/
|
12
|
-
require './spec/support/samples/
|
13
|
-
require './spec/support/samples/
|
14
|
-
require './spec/support/samples/
|
15
|
-
require './spec/support/samples/
|
16
|
-
require './spec/support/samples/
|
17
|
-
require './spec/support/samples/
|
18
|
-
require './spec/support/samples/
|
19
|
-
require './spec/support/samples/
|
20
|
-
require './spec/support/samples/
|
21
|
-
require './spec/support/samples/
|
7
|
+
require './spec/support/samples/active_model'
|
8
|
+
require './spec/support/samples/active_record'
|
9
|
+
require './spec/support/samples/application_controller'
|
10
|
+
require './spec/support/samples/application_helper'
|
11
|
+
require './spec/support/samples/decorator'
|
12
|
+
require './spec/support/samples/decorator_with_allows'
|
13
|
+
require './spec/support/samples/decorator_with_multiple_allows'
|
14
|
+
require './spec/support/samples/decorator_with_application_helper'
|
15
|
+
require './spec/support/samples/decorator_with_denies'
|
16
|
+
require './spec/support/samples/namespaced_product'
|
17
|
+
require './spec/support/samples/namespaced_product_decorator'
|
18
|
+
require './spec/support/samples/non_active_model_product'
|
19
|
+
require './spec/support/samples/product'
|
20
|
+
require './spec/support/samples/product_decorator'
|
21
|
+
require './spec/support/samples/specific_product_decorator'
|
22
|
+
require './spec/support/samples/some_thing'
|
23
|
+
require './spec/support/samples/some_thing_decorator'
|
24
|
+
require './spec/support/samples/widget'
|
25
|
+
require './spec/support/samples/widget_decorator'
|