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