draper 1.0.0.beta6 → 1.0.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/.travis.yml +6 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +20 -0
- data/Gemfile +11 -0
- data/README.md +14 -17
- data/Rakefile +5 -3
- data/draper.gemspec +2 -2
- data/lib/draper.rb +2 -1
- data/lib/draper/automatic_delegation.rb +50 -0
- data/lib/draper/collection_decorator.rb +26 -7
- data/lib/draper/decoratable.rb +71 -32
- data/lib/draper/decorated_association.rb +11 -7
- data/lib/draper/decorator.rb +114 -148
- data/lib/draper/delegation.rb +13 -0
- data/lib/draper/finders.rb +9 -6
- data/lib/draper/helper_proxy.rb +4 -3
- data/lib/draper/lazy_helpers.rb +10 -6
- data/lib/draper/railtie.rb +5 -4
- data/lib/draper/tasks/test.rake +22 -0
- data/lib/draper/test/devise_helper.rb +34 -0
- data/lib/draper/test/minitest_integration.rb +2 -3
- data/lib/draper/test/rspec_integration.rb +4 -59
- data/lib/draper/test_case.rb +33 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_helpers.rb +4 -3
- data/lib/generators/decorator/templates/decorator.rb +7 -25
- data/lib/generators/mini_test/decorator_generator.rb +20 -0
- data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
- data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
- data/lib/generators/test_unit/templates/decorator_test.rb +1 -1
- data/spec/draper/collection_decorator_spec.rb +25 -3
- data/spec/draper/decorated_association_spec.rb +18 -7
- data/spec/draper/decorator_spec.rb +125 -165
- data/spec/draper/finders_spec.rb +0 -13
- data/spec/dummy/app/controllers/localized_urls.rb +1 -1
- data/spec/dummy/app/controllers/posts_controller.rb +3 -9
- data/spec/dummy/app/decorators/post_decorator.rb +4 -1
- data/spec/dummy/config/application.rb +3 -3
- data/spec/dummy/config/environments/development.rb +4 -4
- data/spec/dummy/config/environments/test.rb +2 -2
- data/spec/dummy/lib/tasks/test.rake +10 -0
- data/spec/dummy/mini_test/mini_test_integration_test.rb +46 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +2 -2
- data/spec/dummy/spec/decorators/rspec_integration_spec.rb +19 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +2 -2
- data/spec/dummy/spec/spec_helper.rb +0 -1
- data/spec/generators/decorator/decorator_generator_spec.rb +43 -2
- data/spec/integration/integration_spec.rb +2 -2
- data/spec/spec_helper.rb +17 -21
- data/spec/support/active_record.rb +0 -13
- data/spec/support/dummy_app.rb +4 -3
- metadata +26 -23
- data/lib/draper/security.rb +0 -48
- data/lib/draper/tasks/tu.rake +0 -5
- data/lib/draper/test/test_unit_integration.rb +0 -18
- data/spec/draper/security_spec.rb +0 -158
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/lib/tasks/spec.rake +0 -5
- data/spec/minitest-rails/spec_type_spec.rb +0 -63
data/lib/draper/helper_proxy.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Draper
|
2
|
+
# Provides access to helper methods - both Rails built-in helpers, and those
|
3
|
+
# defined in your application.
|
2
4
|
class HelperProxy
|
3
|
-
|
4
|
-
#
|
5
|
-
# along through a send, which will ignore private/public distinctions
|
5
|
+
|
6
|
+
# Sends helper methods to the view context.
|
6
7
|
def method_missing(method, *args, &block)
|
7
8
|
view_context.send(method, *args, &block)
|
8
9
|
end
|
data/lib/draper/lazy_helpers.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Draper
|
2
|
+
# Include this module in your decorators to get direct access to the helpers
|
3
|
+
# so that you can stop typing `h.` everywhere, at the cost of mixing in a
|
4
|
+
# bazillion methods.
|
2
5
|
module LazyHelpers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
|
7
|
+
# Sends missing methods to the {HelperProxy}.
|
8
|
+
def method_missing(method, *args, &block)
|
9
|
+
helpers.send(method, *args, &block)
|
10
|
+
rescue NoMethodError
|
11
|
+
super
|
9
12
|
end
|
13
|
+
|
10
14
|
end
|
11
15
|
end
|
data/lib/draper/railtie.rb
CHANGED
@@ -17,10 +17,11 @@ module Draper
|
|
17
17
|
config.after_initialize do |app|
|
18
18
|
app.config.paths.add 'app/decorators', eager_load: true
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
unless Rails.env.production?
|
21
|
+
require 'draper/test_case'
|
22
|
+
require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
|
23
|
+
require 'draper/test/minitest_integration' if defined?(MiniTest::Rails)
|
24
|
+
end
|
24
25
|
end
|
25
26
|
|
26
27
|
initializer "draper.setup_action_controller" do |app|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
test_task = if Rails.version.to_f < 3.2
|
4
|
+
require 'rails/test_unit/railtie'
|
5
|
+
Rake::TestTask
|
6
|
+
else
|
7
|
+
require 'rails/test_unit/sub_test_task'
|
8
|
+
Rails::SubTestTask
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :test do
|
12
|
+
test_task.new(:decorators => "test:prepare") do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.pattern = "test/decorators/**/*_test.rb"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if Rake::Task.task_defined?('test:run')
|
19
|
+
Rake::Task['test:run'].enhance do
|
20
|
+
Rake::Task['test:decorators'].invoke
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Draper
|
2
|
+
module DeviseHelper
|
3
|
+
def sign_in(user)
|
4
|
+
warden.stub :authenticate! => user
|
5
|
+
controller.stub :current_user => user
|
6
|
+
user
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def request
|
12
|
+
@request ||= ::ActionDispatch::TestRequest.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def controller
|
16
|
+
return @controller if @controller
|
17
|
+
@controller = ApplicationController.new
|
18
|
+
@controller.request = request
|
19
|
+
::Draper::ViewContext.current = @controller.view_context
|
20
|
+
@controller
|
21
|
+
end
|
22
|
+
|
23
|
+
# taken from Devise's helper but uses the request method instead of @request
|
24
|
+
# and we don't really need the rest of their helper
|
25
|
+
def warden
|
26
|
+
@warden ||= begin
|
27
|
+
manager = Warden::Manager.new(nil) do |config|
|
28
|
+
config.merge! Devise.warden_config
|
29
|
+
end
|
30
|
+
request.env['warden'] = Warden::Proxy.new(request.env, manager)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
|
-
class
|
2
|
-
# Use AS::TestCase for the base class when describing a decorator
|
1
|
+
class Draper::TestCase
|
3
2
|
register_spec_type(self) do |desc|
|
4
|
-
desc < Draper::Decorator if desc.is_a?(Class)
|
3
|
+
desc < Draper::Decorator || desc < Draper::CollectionDecorator if desc.is_a?(Class)
|
5
4
|
end
|
6
5
|
register_spec_type(/Decorator( ?Test)?\z/i, self)
|
7
6
|
end
|
@@ -1,67 +1,12 @@
|
|
1
1
|
module Draper
|
2
2
|
module DecoratorExampleGroup
|
3
|
+
include Draper::TestCase::Behavior
|
3
4
|
extend ActiveSupport::Concern
|
4
|
-
included { metadata[:type] = :decorator }
|
5
|
-
end
|
6
|
-
|
7
|
-
module DeviseHelper
|
8
|
-
def sign_in(user)
|
9
|
-
warden.stub :authenticate! => user
|
10
|
-
controller.stub :current_user => user
|
11
|
-
user
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
5
|
|
16
|
-
|
17
|
-
@request ||= ::ActionDispatch::TestRequest.new
|
18
|
-
end
|
19
|
-
|
20
|
-
def controller
|
21
|
-
return @controller if @controller
|
22
|
-
@controller = ApplicationController.new
|
23
|
-
@controller.request = request
|
24
|
-
::Draper::ViewContext.current = @controller.view_context
|
25
|
-
@controller
|
26
|
-
end
|
27
|
-
|
28
|
-
# taken from Devise's helper but uses the request method instead of @request
|
29
|
-
# and we don't really need the rest of their helper
|
30
|
-
def warden
|
31
|
-
@warden ||= begin
|
32
|
-
manager = Warden::Manager.new(nil) do |config|
|
33
|
-
config.merge! Devise.warden_config
|
34
|
-
end
|
35
|
-
request.env['warden'] = Warden::Proxy.new(request.env, manager)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
RSpec.configure do |config|
|
42
|
-
# Automatically tag specs in specs/decorators as type: :decorator
|
43
|
-
config.include Draper::DecoratorExampleGroup, :type => :decorator, :example_group => {
|
44
|
-
:file_path => /spec[\\\/]decorators/
|
45
|
-
}
|
46
|
-
|
47
|
-
if defined?(Devise)
|
48
|
-
config.include Draper::DeviseHelper, :type => :decorator
|
6
|
+
included { metadata[:type] = :decorator }
|
49
7
|
end
|
50
|
-
end
|
51
8
|
|
52
|
-
|
53
|
-
|
54
|
-
class Railtie < Rails::Railtie
|
55
|
-
config.after_initialize do |app|
|
56
|
-
if defined?(Capybara)
|
57
|
-
require 'capybara/rspec/matchers'
|
58
|
-
|
59
|
-
::RSpec.configure do |config|
|
60
|
-
config.include Capybara::RSpecMatchers, :type => :decorator
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator
|
65
11
|
end
|
66
12
|
end
|
67
|
-
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Draper
|
2
|
+
begin
|
3
|
+
require 'minitest/rails'
|
4
|
+
rescue LoadError
|
5
|
+
end
|
6
|
+
|
7
|
+
active_support_test_case = begin
|
8
|
+
require 'minitest/rails/active_support' # minitest-rails < 0.5
|
9
|
+
::MiniTest::Rails::ActiveSupport::TestCase
|
10
|
+
rescue LoadError
|
11
|
+
require 'active_support/test_case'
|
12
|
+
::ActiveSupport::TestCase
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestCase < active_support_test_case
|
16
|
+
module Behavior
|
17
|
+
if defined?(::Devise)
|
18
|
+
require 'draper/test/devise_helper'
|
19
|
+
include Draper::DeviseHelper
|
20
|
+
end
|
21
|
+
|
22
|
+
if defined?(::Capybara) && (defined?(::RSpec) || defined?(::MiniTest::Matchers))
|
23
|
+
require 'capybara/rspec/matchers'
|
24
|
+
include ::Capybara::RSpecMatchers
|
25
|
+
end
|
26
|
+
|
27
|
+
include Draper::ViewHelpers::ClassMethods
|
28
|
+
alias_method :helper, :helpers
|
29
|
+
end
|
30
|
+
|
31
|
+
include Behavior
|
32
|
+
end
|
33
|
+
end
|
data/lib/draper/version.rb
CHANGED
data/lib/draper/view_helpers.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Draper
|
2
|
+
# Provides the {#helpers} method used in {Decorator} and {CollectionDecorator}
|
3
|
+
# to call the Rails helpers.
|
2
4
|
module ViewHelpers
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
@@ -24,9 +26,8 @@ module Draper
|
|
24
26
|
end
|
25
27
|
alias_method :h, :helpers
|
26
28
|
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# to `l` for convenience.
|
29
|
+
# Alias for `helpers.localize`, since localize is something that's used
|
30
|
+
# quite often. Further aliased to `l` for convenience.
|
30
31
|
def localize(*args)
|
31
32
|
helpers.localize(*args)
|
32
33
|
end
|
@@ -4,34 +4,16 @@ class <%= class_name %>Decorator < <%= parent_class_name %>
|
|
4
4
|
<%- else -%>
|
5
5
|
class <%= class_name %>
|
6
6
|
<%- end -%>
|
7
|
+
delegate_all
|
7
8
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# Normal Usage: helpers.number_to_currency(2)
|
12
|
-
# Abbreviated : h.number_to_currency(2)
|
13
|
-
#
|
14
|
-
# Or, optionally enable "lazy helpers" by including this module:
|
15
|
-
# include Draper::LazyHelpers
|
16
|
-
# Then use the helpers with no proxy:
|
17
|
-
# number_to_currency(2)
|
18
|
-
|
19
|
-
# Defining an Interface
|
20
|
-
# Control access to the wrapped subject's methods using one of the following:
|
21
|
-
#
|
22
|
-
# To allow only the listed methods (whitelist):
|
23
|
-
# allows :method1, :method2
|
24
|
-
#
|
25
|
-
# To allow everything except the listed methods (blacklist):
|
26
|
-
# denies :method1, :method2
|
27
|
-
|
28
|
-
# Presentation Methods
|
29
|
-
# Define your own instance methods, even overriding accessors
|
30
|
-
# generated by ActiveRecord:
|
9
|
+
# Define presentation-specific methods here. Helpers are accessed through
|
10
|
+
# `helpers` (aka `h`). You can override attributes, for example:
|
31
11
|
#
|
32
12
|
# def created_at
|
33
|
-
#
|
34
|
-
#
|
13
|
+
# helpers.content_tag :span, class: 'time' do
|
14
|
+
# source.created_at.strftime("%a %m/%d/%y")
|
15
|
+
# end
|
35
16
|
# end
|
17
|
+
|
36
18
|
end
|
37
19
|
<% end -%>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'generators/mini_test'
|
2
|
+
|
3
|
+
module MiniTest
|
4
|
+
module Generators
|
5
|
+
class DecoratorGenerator < Base
|
6
|
+
def self.source_root
|
7
|
+
File.expand_path('../templates', __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
class_option :spec, :type => :boolean, :default => false, :desc => "Use MiniTest::Spec DSL"
|
11
|
+
|
12
|
+
check_class_collision suffix: "DecoratorTest"
|
13
|
+
|
14
|
+
def create_test_file
|
15
|
+
template_type = options[:spec] ? "spec" : "test"
|
16
|
+
template "decorator_#{template_type}.rb", File.join("test/decorators", class_path, "#{singular_name}_decorator_test.rb")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -76,7 +76,7 @@ describe Draper::CollectionDecorator do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
it "raises error on invalid options" do
|
79
|
-
expect { Draper::CollectionDecorator.new(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError,
|
79
|
+
expect { Draper::CollectionDecorator.new(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, /Unknown key/)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -127,6 +127,20 @@ describe Draper::CollectionDecorator do
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
describe ".delegate" do
|
131
|
+
subject { Class.new(Draper::CollectionDecorator) }
|
132
|
+
|
133
|
+
it "defaults the :to option to :source" do
|
134
|
+
Draper::CollectionDecorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :source)
|
135
|
+
subject.delegate :foo, :bar
|
136
|
+
end
|
137
|
+
|
138
|
+
it "does not overwrite the :to option if supplied" do
|
139
|
+
Draper::CollectionDecorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :baz)
|
140
|
+
subject.delegate :foo, :bar, to: :baz
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
130
144
|
describe "#find" do
|
131
145
|
context "with a block" do
|
132
146
|
it "decorates Enumerable#find" do
|
@@ -248,7 +262,7 @@ describe Draper::CollectionDecorator do
|
|
248
262
|
let(:options) { {with: ProductDecorator} }
|
249
263
|
|
250
264
|
it "returns a string representation of the CollectionDecorator" do
|
251
|
-
subject.to_s.should == '#<CollectionDecorator of ProductDecorator for ["a", "b", "c"]>'
|
265
|
+
subject.to_s.should == '#<Draper::CollectionDecorator of ProductDecorator for ["a", "b", "c"]>'
|
252
266
|
end
|
253
267
|
end
|
254
268
|
|
@@ -256,7 +270,15 @@ describe Draper::CollectionDecorator do
|
|
256
270
|
let(:options) { {} }
|
257
271
|
|
258
272
|
it "returns a string representation of the CollectionDecorator" do
|
259
|
-
subject.to_s.should == '#<CollectionDecorator of inferred decorators for ["a", "b", "c"]>'
|
273
|
+
subject.to_s.should == '#<Draper::CollectionDecorator of inferred decorators for ["a", "b", "c"]>'
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "for a custom subclass" do
|
278
|
+
subject { ProductsDecorator.new(source) }
|
279
|
+
|
280
|
+
it "uses the custom class name" do
|
281
|
+
subject.to_s.should =~ /ProductsDecorator/
|
260
282
|
end
|
261
283
|
end
|
262
284
|
end
|
@@ -15,7 +15,7 @@ describe Draper::DecoratedAssociation do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "raises error on invalid options" do
|
18
|
-
expect { Draper::DecoratedAssociation.new(owner, :association, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError,
|
18
|
+
expect { Draper::DecoratedAssociation.new(owner, :association, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, /Unknown key/)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -31,10 +31,10 @@ describe Draper::DecoratedAssociation do
|
|
31
31
|
|
32
32
|
context "for a singular association" do
|
33
33
|
let(:associated) { Product.new }
|
34
|
+
let(:decorator) { SpecificProductDecorator }
|
34
35
|
|
35
36
|
context "when :with option was given" do
|
36
37
|
let(:options) { {with: decorator} }
|
37
|
-
let(:decorator) { SpecificProductDecorator }
|
38
38
|
|
39
39
|
it "uses the specified decorator" do
|
40
40
|
decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated)
|
@@ -44,7 +44,8 @@ describe Draper::DecoratedAssociation do
|
|
44
44
|
|
45
45
|
context "when :with option was not given" do
|
46
46
|
it "infers the decorator" do
|
47
|
-
associated.
|
47
|
+
associated.stub(:decorator_class).and_return(decorator)
|
48
|
+
decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated)
|
48
49
|
decorated_association.call.should be :decorated
|
49
50
|
end
|
50
51
|
end
|
@@ -52,10 +53,10 @@ describe Draper::DecoratedAssociation do
|
|
52
53
|
|
53
54
|
context "for a collection association" do
|
54
55
|
let(:associated) { [Product.new, Widget.new] }
|
56
|
+
let(:collection_decorator) { ProductsDecorator }
|
55
57
|
|
56
58
|
context "when :with option is a collection decorator" do
|
57
59
|
let(:options) { {with: collection_decorator} }
|
58
|
-
let(:collection_decorator) { ProductsDecorator }
|
59
60
|
|
60
61
|
it "uses the specified decorator" do
|
61
62
|
collection_decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated_collection)
|
@@ -74,9 +75,19 @@ describe Draper::DecoratedAssociation do
|
|
74
75
|
end
|
75
76
|
|
76
77
|
context "when :with option was not given" do
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
context "when the collection is decoratable" do
|
79
|
+
it "infers the decorator" do
|
80
|
+
associated.stub(:decorator_class).and_return(collection_decorator)
|
81
|
+
collection_decorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated_collection)
|
82
|
+
decorated_association.call.should be :decorated_collection
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when the collection is not decoratable" do
|
87
|
+
it "uses a CollectionDecorator of inferred decorators" do
|
88
|
+
Draper::CollectionDecorator.should_receive(:decorate).with(associated, expected_options).and_return(:decorated_collection)
|
89
|
+
decorated_association.call.should be :decorated_collection
|
90
|
+
end
|
80
91
|
end
|
81
92
|
end
|
82
93
|
end
|