draper 0.10.0 → 0.11.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 +0 -1
- data/Gemfile +3 -3
- data/Rakefile +8 -8
- data/Readme.markdown +41 -22
- data/doc/css/full_list.css +2 -2
- data/doc/css/style.css +30 -30
- data/doc/js/app.js +10 -10
- data/doc/js/full_list.js +8 -8
- data/draper.gemspec +2 -2
- data/lib/draper.rb +1 -1
- data/lib/draper/base.rb +65 -22
- data/lib/draper/decorated_enumerable_proxy.rb +6 -1
- data/lib/draper/model_support.rb +2 -2
- data/lib/draper/railtie.rb +19 -0
- data/lib/draper/system.rb +7 -4
- data/lib/draper/version.rb +1 -1
- data/lib/generators/draper/decorator/decorator_generator.rb +26 -4
- data/lib/generators/draper/decorator/templates/decorator.rb +2 -2
- data/lib/generators/{rspec → draper/decorator}/templates/decorator_spec.rb +1 -1
- data/lib/generators/{test_unit → draper/decorator}/templates/decorator_test.rb +1 -1
- data/lib/generators/draper/install/install_generator.rb +6 -6
- data/lib/generators/rails/decorator_generator.rb +3 -3
- data/performance/bechmark.rb +5 -5
- data/performance/decorators.rb +4 -4
- data/performance/models.rb +2 -2
- data/spec/draper/base_spec.rb +176 -10
- data/spec/draper/helper_support_spec.rb +1 -1
- data/spec/draper/model_support_spec.rb +23 -14
- data/spec/draper/view_context_spec.rb +4 -4
- data/spec/generators/draper/decorator/decorator_generator_spec.rb +81 -1
- data/spec/generators/draper/install/install_generator_spec.rb +5 -5
- data/spec/spec_helper.rb +3 -0
- data/spec/support/samples/application_controller.rb +8 -11
- data/spec/support/samples/decorator_with_allows.rb +1 -1
- data/spec/support/samples/decorator_with_application_helper.rb +5 -5
- data/spec/support/samples/decorator_with_denies.rb +1 -1
- data/spec/support/samples/decorator_with_multiple_allows.rb +4 -0
- data/spec/support/samples/product.rb +28 -7
- data/spec/support/samples/some_thing.rb +2 -0
- data/spec/support/samples/some_thing_decorator.rb +3 -0
- metadata +62 -37
- data/lib/generators/rspec/decorator_generator.rb +0 -11
- data/lib/generators/test_unit/decorator_generator.rb +0 -11
- data/spec/generators/rspec/decorator_generator_spec.rb +0 -22
- data/spec/generators/test_unit/decorator_generator_spec.rb +0 -22
data/lib/draper.rb
CHANGED
data/lib/draper/base.rb
CHANGED
@@ -2,9 +2,10 @@ module Draper
|
|
2
2
|
class Base
|
3
3
|
require 'active_support/core_ext/class/attribute'
|
4
4
|
class_attribute :denied, :allowed, :model_class
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :model, :options
|
6
6
|
|
7
7
|
DEFAULT_DENIED = Object.new.methods << :method_missing
|
8
|
+
DEFAULT_ALLOWED = []
|
8
9
|
FORCED_PROXY = [:to_param, :id]
|
9
10
|
FORCED_PROXY.each do |method|
|
10
11
|
define_method method do |*args, &block|
|
@@ -12,6 +13,7 @@ module Draper
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
self.denied = DEFAULT_DENIED
|
16
|
+
self.allowed = DEFAULT_ALLOWED
|
15
17
|
|
16
18
|
# Initialize a new decorator instance by passing in
|
17
19
|
# an instance of the source class. Pass in an optional
|
@@ -23,7 +25,7 @@ module Draper
|
|
23
25
|
input.inspect # forces evaluation of a lazy query from AR
|
24
26
|
self.class.model_class = input.class if model_class.nil?
|
25
27
|
@model = input
|
26
|
-
self.
|
28
|
+
self.options = options
|
27
29
|
end
|
28
30
|
|
29
31
|
# Proxies to the class specified by `decorates` to automatically
|
@@ -48,7 +50,8 @@ module Draper
|
|
48
50
|
#
|
49
51
|
# @param [Symbol] class_name snakecase name of the decorated class, like `:product`
|
50
52
|
def self.decorates(input, options = {})
|
51
|
-
self.model_class = options[:class] || input.to_s.camelize
|
53
|
+
self.model_class = options[:class] || options[:class_name] || input.to_s.camelize
|
54
|
+
self.model_class = model_class.constantize if model_class.respond_to?(:constantize)
|
52
55
|
model_class.send :include, Draper::ModelSupport
|
53
56
|
define_method(input){ @model }
|
54
57
|
end
|
@@ -61,13 +64,21 @@ module Draper
|
|
61
64
|
def self.decorates_association(association_symbol, options = {})
|
62
65
|
define_method(association_symbol) do
|
63
66
|
orig_association = model.send(association_symbol)
|
64
|
-
|
65
|
-
if
|
66
|
-
|
67
|
+
|
68
|
+
return orig_association if orig_association.nil?
|
69
|
+
|
70
|
+
return options[:with].decorate(orig_association) if options[:with]
|
71
|
+
|
72
|
+
if options[:polymorphic]
|
73
|
+
klass = orig_association.class
|
74
|
+
elsif model.class.respond_to?(:reflect_on_association) && model.class.reflect_on_association(association_symbol)
|
75
|
+
klass = model.class.reflect_on_association(association_symbol).klass
|
76
|
+
elsif orig_association.respond_to?(:first)
|
77
|
+
klass = orig_association.first.class
|
67
78
|
else
|
68
|
-
|
69
|
-
"#{reflection.klass}Decorator".constantize.decorate(orig_association)
|
79
|
+
klass = orig_association.class
|
70
80
|
end
|
81
|
+
"#{klass}Decorator".constantize.decorate(orig_association, options)
|
71
82
|
end
|
72
83
|
end
|
73
84
|
|
@@ -88,7 +99,7 @@ module Draper
|
|
88
99
|
# @param [Symbols*] methods to deny like `:find, :find_by_name`
|
89
100
|
def self.denies(*input_denied)
|
90
101
|
raise ArgumentError, "Specify at least one method (as a symbol) to exclude when using denies" if input_denied.empty?
|
91
|
-
raise ArgumentError, "Use either 'allows' or 'denies', but not both."
|
102
|
+
raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless (self.allowed == DEFAULT_ALLOWED)
|
92
103
|
self.denied += input_denied
|
93
104
|
end
|
94
105
|
|
@@ -104,7 +115,7 @@ module Draper
|
|
104
115
|
def self.allows(*input_allows)
|
105
116
|
raise ArgumentError, "Specify at least one method (as a symbol) to allow when using allows" if input_allows.empty?
|
106
117
|
raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless (self.denied == DEFAULT_DENIED)
|
107
|
-
self.allowed
|
118
|
+
self.allowed += input_allows
|
108
119
|
end
|
109
120
|
|
110
121
|
# Initialize a new decorator instance by passing in
|
@@ -119,7 +130,10 @@ module Draper
|
|
119
130
|
# @param [Object] instance(s) to wrap
|
120
131
|
# @param [Hash] options (optional)
|
121
132
|
def self.decorate(input, options = {})
|
122
|
-
if input.
|
133
|
+
if input.instance_of?(self)
|
134
|
+
input.options = options unless options.empty?
|
135
|
+
return input
|
136
|
+
elsif input.respond_to?(:each)
|
123
137
|
Draper::DecoratedEnumerableProxy.new(input, self, options)
|
124
138
|
elsif options[:infer]
|
125
139
|
input.decorator(options)
|
@@ -145,7 +159,7 @@ module Draper
|
|
145
159
|
end
|
146
160
|
|
147
161
|
# Access the helpers proxy to call built-in and user-defined
|
148
|
-
# Rails helpers. Aliased to `.h` for
|
162
|
+
# Rails helpers. Aliased to `.h` for convenience.
|
149
163
|
#
|
150
164
|
# @return [Object] proxy
|
151
165
|
def helpers
|
@@ -153,6 +167,14 @@ module Draper
|
|
153
167
|
end
|
154
168
|
alias :h :helpers
|
155
169
|
|
170
|
+
# Localize is something that's used quite often. Even though
|
171
|
+
# it's available through helpers, that's annoying. Aliased
|
172
|
+
# to `.l` for convenience.
|
173
|
+
def localize(str)
|
174
|
+
self.class.helpers.localize(str)
|
175
|
+
end
|
176
|
+
alias :l :localize
|
177
|
+
|
156
178
|
# Access the helpers proxy to call built-in and user-defined
|
157
179
|
# Rails helpers from a class context.
|
158
180
|
#
|
@@ -188,31 +210,52 @@ module Draper
|
|
188
210
|
end
|
189
211
|
|
190
212
|
def method_missing(method, *args, &block)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
self.send(method, *args, &block)
|
197
|
-
rescue NoMethodError
|
198
|
-
super
|
213
|
+
super unless allow?(method)
|
214
|
+
|
215
|
+
if model.respond_to?(method)
|
216
|
+
self.class.send :define_method, method do |*args, &block|
|
217
|
+
model.send method, *args, &block
|
199
218
|
end
|
219
|
+
|
220
|
+
send method, *args, &block
|
200
221
|
else
|
201
222
|
super
|
202
223
|
end
|
224
|
+
|
225
|
+
rescue NoMethodError => no_method_error
|
226
|
+
super if no_method_error.name == method
|
227
|
+
raise no_method_error
|
203
228
|
end
|
204
229
|
|
205
230
|
def self.method_missing(method, *args, &block)
|
206
|
-
|
231
|
+
if method.to_s.match(/^find_by.*/)
|
232
|
+
self.decorate(model_class.send(method, *args, &block))
|
233
|
+
else
|
234
|
+
model_class.send(method, *args, &block)
|
235
|
+
end
|
207
236
|
end
|
208
237
|
|
209
238
|
def self.respond_to?(method, include_private = false)
|
210
239
|
super || model_class.respond_to?(method)
|
211
240
|
end
|
212
241
|
|
242
|
+
def context
|
243
|
+
options.fetch(:context, {})
|
244
|
+
end
|
245
|
+
|
246
|
+
def context=(input)
|
247
|
+
options[:context] = input
|
248
|
+
end
|
249
|
+
|
250
|
+
def source
|
251
|
+
model
|
252
|
+
end
|
253
|
+
alias_method :to_source, :model
|
254
|
+
|
213
255
|
private
|
256
|
+
|
214
257
|
def allow?(method)
|
215
|
-
(
|
258
|
+
(allowed.empty? || allowed.include?(method) || FORCED_PROXY.include?(method)) && !denied.include?(method)
|
216
259
|
end
|
217
260
|
end
|
218
261
|
end
|
@@ -21,7 +21,7 @@ module Draper
|
|
21
21
|
def respond_to?(method)
|
22
22
|
super || @wrapped_collection.respond_to?(method)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def kind_of?(klass)
|
26
26
|
super || @wrapped_collection.kind_of?(klass)
|
27
27
|
end
|
@@ -38,5 +38,10 @@ module Draper
|
|
38
38
|
def to_s
|
39
39
|
"#<DecoratedEnumerableProxy of #{@klass} for #{@wrapped_collection.inspect}>"
|
40
40
|
end
|
41
|
+
|
42
|
+
def source
|
43
|
+
@wrapped_collection
|
44
|
+
end
|
45
|
+
alias_method :to_source, :source
|
41
46
|
end
|
42
47
|
end
|
data/lib/draper/model_support.rb
CHANGED
@@ -8,8 +8,8 @@ module Draper::ModelSupport
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def decorate(options = {})
|
11
|
-
|
12
|
-
block_given? ? yield(
|
11
|
+
decorator_proxy = "#{model_name}Decorator".constantize.decorate(self.scoped, options)
|
12
|
+
block_given? ? yield(decorator_proxy) : decorator_proxy
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module Draper
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
|
6
|
+
initializer "draper.extend_action_controller_base" do |app|
|
7
|
+
ActiveSupport.on_load(:action_controller) do
|
8
|
+
Draper::System.setup(:action_controller)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer "draper.extend_action_mailer_base" do |app|
|
13
|
+
ActiveSupport.on_load(:action_mailer) do
|
14
|
+
Draper::System.setup(:action_mailer)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/draper/system.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
module Draper
|
2
2
|
class System
|
3
|
-
def self.setup
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
def self.setup(component)
|
4
|
+
if component == :action_controller
|
5
|
+
ActionController::Base.send(:include, Draper::ViewContextFilter)
|
6
|
+
ActionController::Base.extend(Draper::HelperSupport)
|
7
|
+
elsif component == :action_mailer
|
8
|
+
ActionMailer::Base.send(:include, Draper::ViewContextFilter)
|
9
|
+
end
|
7
10
|
end
|
8
11
|
end
|
9
12
|
end
|
data/lib/draper/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Draper
|
2
|
-
class DecoratorGenerator < Rails::Generators::
|
2
|
+
class DecoratorGenerator < Rails::Generators::Base
|
3
3
|
desc <<-DESC
|
4
4
|
Description:
|
5
5
|
Generate a decorator for the given model.
|
@@ -7,15 +7,37 @@ module Draper
|
|
7
7
|
generates: "app/decorators/article_decorator"
|
8
8
|
"spec/decorators/article_decorator_spec"
|
9
9
|
DESC
|
10
|
-
|
10
|
+
|
11
|
+
argument :resource_name, :type => :string
|
12
|
+
class_option "test-framework", :type => :string, :default => "rspec", :aliases => "-t", :desc => "Test framework to be invoked"
|
13
|
+
|
11
14
|
source_root File.expand_path('../templates', __FILE__)
|
12
15
|
|
13
16
|
DECORATORS_ROOT = 'app/decorators/'
|
14
17
|
|
15
18
|
def build_model_decorator
|
16
|
-
template 'decorator.rb', "#{DECORATORS_ROOT}#{
|
19
|
+
template 'decorator.rb', "#{DECORATORS_ROOT}#{resource_name.singularize}_decorator.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_decorator_tests
|
23
|
+
case options["test-framework"]
|
24
|
+
when "rspec"
|
25
|
+
build_decorator_spec
|
26
|
+
when "test_unit"
|
27
|
+
build_decorator_test
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def build_decorator_spec
|
33
|
+
empty_directory 'spec/decorators'
|
34
|
+
template 'decorator_spec.rb', File.join('spec/decorators', "#{resource_name.singularize}_decorator_spec.rb")
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_decorator_test
|
38
|
+
empty_directory 'test/decorators/'
|
39
|
+
template 'decorator_test.rb', File.join('test/decorators', "#{resource_name.singularize}_decorator_test.rb")
|
17
40
|
end
|
18
41
|
|
19
|
-
hook_for :test_framework
|
20
42
|
end
|
21
43
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
class <%=
|
2
|
-
decorates :<%=
|
1
|
+
class <%= resource_name.singularize.camelize %>Decorator < ApplicationDecorator
|
2
|
+
decorates :<%= resource_name.singularize.to_sym %>
|
3
3
|
|
4
4
|
# Accessing Helpers
|
5
5
|
# You can access any helper via a proxy
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module Draper
|
2
2
|
class InstallGenerator < Rails::Generators::Base
|
3
|
-
|
3
|
+
|
4
4
|
desc <<-DESC
|
5
5
|
Description:
|
6
6
|
Generate application and spec decorators in your application.
|
7
7
|
DESC
|
8
|
-
|
8
|
+
|
9
9
|
class_option "test-framework", :type => :string, :default => "rspec", :aliases => "-t", :desc => "Test framework to be invoked"
|
10
|
-
|
10
|
+
|
11
11
|
source_root File.expand_path('../templates', __FILE__)
|
12
12
|
|
13
13
|
def build_application_decorator
|
14
14
|
empty_directory 'app/decorators'
|
15
15
|
template 'application_decorator.rb', File.join('app/decorators', 'application_decorator.rb')
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def build_decorator_tests
|
19
19
|
case options["test-framework"]
|
20
20
|
when "rspec"
|
@@ -23,13 +23,13 @@ module Draper
|
|
23
23
|
build_application_decorator_test
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
private
|
28
28
|
def build_application_decorator_spec
|
29
29
|
empty_directory 'spec/decorators'
|
30
30
|
template 'application_decorator_spec.rb', File.join('spec/decorators', 'application_decorator_spec.rb')
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def build_application_decorator_test
|
34
34
|
empty_directory 'test/decorators/'
|
35
35
|
template 'application_decorator_test.rb', File.join('test/decorators', 'application_decorator_test.rb')
|
@@ -1,14 +1,14 @@
|
|
1
1
|
require File.expand_path('../../draper/decorator/decorator_generator.rb', __FILE__)
|
2
2
|
class Rails::DecoratorGenerator < Draper::DecoratorGenerator
|
3
|
-
|
3
|
+
|
4
4
|
source_root File.expand_path('../../draper/decorator/templates', __FILE__)
|
5
|
-
|
5
|
+
|
6
6
|
class_option :invoke_after_finished, :type => :string, :description => "Generator to invoke when finished"
|
7
7
|
|
8
8
|
def build_model_and_application_decorators
|
9
9
|
super
|
10
10
|
if self.options[:invoke_after_finished]
|
11
|
-
Rails::Generators.invoke(self.options[:invoke_after_finished], [@name, @_initializer.first[1..-1]])
|
11
|
+
Rails::Generators.invoke(self.options[:invoke_after_finished], [@name, @_initializer.first[1..-1]])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
data/performance/bechmark.rb
CHANGED
@@ -17,20 +17,20 @@ Benchmark.bm do |bm|
|
|
17
17
|
ProductDecorator.decorate(Product.new)
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
bm.report("#hello_world ") do
|
22
22
|
i.times do |n|
|
23
23
|
ProductDecorator.decorate(Product.new).hello_world
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
bm.report("#sample_class_method ") do
|
28
28
|
i.times do |n|
|
29
29
|
ProductDecorator.decorate(Product.new).class.sample_class_method
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
puts "\n[ Defining methods on method_missing first hit ]"
|
35
35
|
[ 1_000, 10_000, 100_000 ].each do |i|
|
36
36
|
puts "\n[ #{i} ]"
|
@@ -39,13 +39,13 @@ Benchmark.bm do |bm|
|
|
39
39
|
FastProductDecorator.decorate(FastProduct.new)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
bm.report("#hello_world ") do
|
44
44
|
i.times do |n|
|
45
45
|
FastProductDecorator.decorate(FastProduct.new).hello_world
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
bm.report("#sample_class_method ") do
|
50
50
|
i.times do |n|
|
51
51
|
FastProductDecorator.decorate(FastProduct.new).class.sample_class_method
|
data/performance/decorators.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require "./performance/models"
|
2
2
|
class ProductDecorator < Draper::Base
|
3
3
|
decorates :product
|
4
|
-
|
4
|
+
|
5
5
|
def awesome_title
|
6
6
|
"Awesome Title"
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
# Original #method_missing
|
10
10
|
def method_missing(method, *args, &block)
|
11
11
|
if allow?(method)
|
@@ -23,11 +23,11 @@ end
|
|
23
23
|
|
24
24
|
class FastProductDecorator < Draper::Base
|
25
25
|
decorates :product
|
26
|
-
|
26
|
+
|
27
27
|
def awesome_title
|
28
28
|
"Awesome Title"
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Modified #method_missing
|
32
32
|
def method_missing(method, *args, &block)
|
33
33
|
if allow?(method)
|