draper 0.18.0 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.markdown +8 -0
- data/Gemfile +4 -0
- data/Rakefile +50 -31
- data/Readme.markdown +42 -48
- data/draper.gemspec +2 -1
- data/lib/draper.rb +42 -7
- 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 +208 -0
- data/lib/draper/finders.rb +44 -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 +1 -1
- data/lib/draper/test/test_unit_integration.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +6 -13
- data/lib/draper/view_helpers.rb +36 -0
- data/lib/generators/decorator/decorator_generator.rb +1 -1
- data/lib/generators/decorator/templates/decorator.rb +0 -1
- 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 +497 -0
- data/spec/draper/finders_spec.rb +156 -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 +25 -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.ru +4 -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/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/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 +23 -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 +3 -4
- data/spec/integration/integration_spec.rb +33 -0
- data/{performance → spec/performance}/active_record.rb +0 -0
- data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/{performance → spec/performance}/models.rb +0 -0
- data/spec/spec_helper.rb +20 -41
- 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 +19 -0
- data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- 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 +13 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- data/spec/support/{samples → models}/widget.rb +0 -0
- metadata +185 -68
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -90
- data/lib/draper/model_support.rb +0 -25
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -18
- data/spec/draper/base_spec.rb +0 -873
- data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
- data/spec/draper/model_support_spec.rb +0 -48
- 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/enumerable_proxy.rb +0 -3
- 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
@@ -0,0 +1,44 @@
|
|
1
|
+
module Draper::Decoratable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def decorate(options = {})
|
5
|
+
decorator_class.decorate(self, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def decorator_class
|
9
|
+
self.class.decorator_class
|
10
|
+
end
|
11
|
+
|
12
|
+
def applied_decorators
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def decorated_with?(decorator_class)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def decorated?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def ===(other)
|
25
|
+
super || (other.respond_to?(:source) && super(other.source))
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def decorate(options = {})
|
30
|
+
decorator_class.decorate_collection(self.scoped, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def decorator_class
|
34
|
+
prefix = respond_to?(:model_name) ? model_name : name
|
35
|
+
"#{prefix}Decorator".constantize
|
36
|
+
rescue NameError
|
37
|
+
raise Draper::UninferrableDecoratorError.new(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def ===(other)
|
41
|
+
super || (other.respond_to?(:source) && super(other.source))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Draper
|
2
|
+
class DecoratedAssociation
|
3
|
+
|
4
|
+
attr_reader :source, :association, :options
|
5
|
+
|
6
|
+
def initialize(source, association, options)
|
7
|
+
@source = source
|
8
|
+
@association = association
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
return undecorated if undecorated.nil? || undecorated == []
|
14
|
+
decorate
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def undecorated
|
20
|
+
@undecorated ||= begin
|
21
|
+
associated = source.send(association)
|
22
|
+
associated = associated.send(options[:scope]) if options[:scope]
|
23
|
+
associated
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def decorate
|
28
|
+
@decorated ||= decorator_class.send(decorate_method, undecorated, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def decorate_method
|
32
|
+
if collection? && decorator_class.respond_to?(:decorate_collection)
|
33
|
+
:decorate_collection
|
34
|
+
else
|
35
|
+
:decorate
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def collection?
|
40
|
+
undecorated.respond_to?(:first)
|
41
|
+
end
|
42
|
+
|
43
|
+
def decorator_class
|
44
|
+
return options[:with] if options[:with]
|
45
|
+
|
46
|
+
if collection?
|
47
|
+
options[:with] = :infer
|
48
|
+
Draper::CollectionDecorator
|
49
|
+
else
|
50
|
+
undecorated.decorator_class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'active_support/core_ext/array/extract_options'
|
2
|
+
|
3
|
+
module Draper
|
4
|
+
class Decorator
|
5
|
+
include Draper::ViewHelpers
|
6
|
+
include ActiveModel::Serialization if defined?(ActiveModel::Serialization)
|
7
|
+
|
8
|
+
attr_accessor :source, :options
|
9
|
+
|
10
|
+
alias_method :model, :source
|
11
|
+
alias_method :to_source, :source
|
12
|
+
|
13
|
+
# Initialize a new decorator instance by passing in
|
14
|
+
# an instance of the source class. Pass in an optional
|
15
|
+
# context inside the options hash is stored for later use.
|
16
|
+
#
|
17
|
+
# A decorator cannot be applied to other instances of the
|
18
|
+
# same decorator and will instead result in a decorator
|
19
|
+
# with the same target as the original.
|
20
|
+
# You can, however, apply several decorators in a chain but
|
21
|
+
# you will get a warning if the same decorator appears at
|
22
|
+
# multiple places in the chain.
|
23
|
+
#
|
24
|
+
# @param [Object] source object to decorate
|
25
|
+
# @param [Hash] options (optional)
|
26
|
+
def initialize(source, options = {})
|
27
|
+
source.to_a if source.respond_to?(:to_a) # forces evaluation of a lazy query from AR
|
28
|
+
@source = source
|
29
|
+
@options = options
|
30
|
+
handle_multiple_decoration if source.is_a?(Draper::Decorator)
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
alias_method :decorate, :new
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds ActiveRecord finder methods to the decorator class. The
|
38
|
+
# methods return decorated models, so that you can use
|
39
|
+
# `ProductDecorator.find(id)` instead of
|
40
|
+
# `ProductDecorator.decorate(Product.find(id))`.
|
41
|
+
#
|
42
|
+
# If the `:for` option is not supplied, the model class will be
|
43
|
+
# inferred from the decorator class.
|
44
|
+
#
|
45
|
+
# @option options [Class, Symbol] :for The model class to find
|
46
|
+
def self.has_finders(options = {})
|
47
|
+
extend Draper::Finders
|
48
|
+
self.finder_class = options[:for] || name.chomp("Decorator")
|
49
|
+
end
|
50
|
+
|
51
|
+
# Typically called within a decorator definition, this method causes
|
52
|
+
# the assocation to be decorated when it is retrieved.
|
53
|
+
#
|
54
|
+
# @param [Symbol] association name of association to decorate, like `:products`
|
55
|
+
# @option options [Class] :with the decorator to apply to the association
|
56
|
+
# @option options [Symbol] :scope a scope to apply when fetching the association
|
57
|
+
def self.decorates_association(association, options = {})
|
58
|
+
define_method(association) do
|
59
|
+
decorated_associations[association] ||= Draper::DecoratedAssociation.new(source, association, options)
|
60
|
+
decorated_associations[association].call
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# A convenience method for decorating multiple associations. Calls
|
65
|
+
# decorates_association on each of the given symbols.
|
66
|
+
#
|
67
|
+
# @param [Symbols*] associations name of associations to decorate
|
68
|
+
def self.decorates_associations(*associations)
|
69
|
+
options = associations.extract_options!
|
70
|
+
associations.each do |association|
|
71
|
+
decorates_association(association, options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Specifies a black list of methods which may *not* be proxied to
|
76
|
+
# the wrapped object.
|
77
|
+
#
|
78
|
+
# Do not use both `.allows` and `.denies` together, either write
|
79
|
+
# a whitelist with `.allows` or a blacklist with `.denies`
|
80
|
+
#
|
81
|
+
# @param [Symbols*] methods methods to deny like `:find, :find_by_name`
|
82
|
+
def self.denies(*methods)
|
83
|
+
security.denies(*methods)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Specifies that all methods may *not* be proxied to the wrapped object.
|
87
|
+
#
|
88
|
+
# Do not use `.allows` and `.denies` in combination with '.denies_all'
|
89
|
+
def self.denies_all
|
90
|
+
security.denies_all
|
91
|
+
end
|
92
|
+
|
93
|
+
# Specifies a white list of methods which *may* be proxied to
|
94
|
+
# the wrapped object. When `allows` is used, only the listed
|
95
|
+
# methods and methods defined in the decorator itself will be
|
96
|
+
# available.
|
97
|
+
#
|
98
|
+
# Do not use both `.allows` and `.denies` together, either write
|
99
|
+
# a whitelist with `.allows` or a blacklist with `.denies`
|
100
|
+
#
|
101
|
+
# @param [Symbols*] methods methods to allow like `:find, :find_by_name`
|
102
|
+
def self.allows(*methods)
|
103
|
+
security.allows(*methods)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Creates a new CollectionDecorator for the given collection.
|
107
|
+
#
|
108
|
+
# @param [Object] source collection to decorate
|
109
|
+
# @param [Hash] options passed to each item's decorator (except
|
110
|
+
# for the keys listed below)
|
111
|
+
# @option options [Class,Symbol] :with (self) the class used to decorate
|
112
|
+
# items, or `:infer` to call each item's `decorate` method instead
|
113
|
+
def self.decorate_collection(source, options = {})
|
114
|
+
Draper::CollectionDecorator.new(source, options.reverse_merge(with: self))
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get the chain of decorators applied to the object.
|
118
|
+
#
|
119
|
+
# @return [Array] list of decorator classes
|
120
|
+
def applied_decorators
|
121
|
+
chain = source.respond_to?(:applied_decorators) ? source.applied_decorators : []
|
122
|
+
chain << self.class
|
123
|
+
end
|
124
|
+
|
125
|
+
# Checks if a given decorator has been applied.
|
126
|
+
#
|
127
|
+
# @param [Class] decorator_class
|
128
|
+
def decorated_with?(decorator_class)
|
129
|
+
applied_decorators.include?(decorator_class)
|
130
|
+
end
|
131
|
+
|
132
|
+
def decorated?
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
# Delegates == to the decorated models
|
137
|
+
#
|
138
|
+
# @return [Boolean] true if other's model == self's model
|
139
|
+
def ==(other)
|
140
|
+
source == (other.respond_to?(:source) ? other.source : other)
|
141
|
+
end
|
142
|
+
|
143
|
+
def kind_of?(klass)
|
144
|
+
super || source.kind_of?(klass)
|
145
|
+
end
|
146
|
+
alias_method :is_a?, :kind_of?
|
147
|
+
|
148
|
+
def respond_to?(method, include_private = false)
|
149
|
+
super || (allow?(method) && source.respond_to?(method, include_private))
|
150
|
+
end
|
151
|
+
|
152
|
+
# We always want to delegate present, in case we decorate a nil object.
|
153
|
+
#
|
154
|
+
# I don't like the idea of decorating a nil object, but we'll deal with
|
155
|
+
# that later.
|
156
|
+
def present?
|
157
|
+
source.present?
|
158
|
+
end
|
159
|
+
|
160
|
+
def method_missing(method, *args, &block)
|
161
|
+
if allow?(method) && source.respond_to?(method)
|
162
|
+
self.class.define_proxy(method)
|
163
|
+
send(method, *args, &block)
|
164
|
+
else
|
165
|
+
super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# For ActiveModel compatibilty
|
170
|
+
def to_model
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
# For ActiveModel compatibility
|
175
|
+
def to_param
|
176
|
+
source.to_param
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def self.define_proxy(method)
|
182
|
+
define_method(method) do |*args, &block|
|
183
|
+
source.send(method, *args, &block)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.security
|
188
|
+
@security ||= Security.new
|
189
|
+
end
|
190
|
+
|
191
|
+
def allow?(method)
|
192
|
+
self.class.security.allow?(method)
|
193
|
+
end
|
194
|
+
|
195
|
+
def handle_multiple_decoration
|
196
|
+
if source.instance_of?(self.class)
|
197
|
+
self.options = source.options if options.empty?
|
198
|
+
self.source = source.source
|
199
|
+
elsif source.decorated_with?(self.class)
|
200
|
+
warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def decorated_associations
|
205
|
+
@decorated_associations ||= {}
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Draper
|
2
|
+
module Finders
|
3
|
+
|
4
|
+
attr_reader :finder_class
|
5
|
+
def finder_class=(klass)
|
6
|
+
@finder_class = klass.to_s.camelize.constantize
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(id, options = {})
|
10
|
+
decorate(finder_class.find(id), options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def all(options = {})
|
14
|
+
decorate_collection(finder_class.all, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def first(options = {})
|
18
|
+
decorate(finder_class.first, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def last(options = {})
|
22
|
+
decorate(finder_class.last, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method, *args, &block)
|
26
|
+
result = finder_class.send(method, *args, &block)
|
27
|
+
options = args.extract_options!
|
28
|
+
|
29
|
+
case method.to_s
|
30
|
+
when /^find_((last_)?by_|or_(initialize|create)_by_)/
|
31
|
+
decorate(result, options)
|
32
|
+
when /^find_all_by_/
|
33
|
+
decorate_collection(result, options)
|
34
|
+
else
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to?(method, include_private = false)
|
40
|
+
super || finder_class.respond_to?(method)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Draper
|
2
|
+
class HelperProxy
|
3
|
+
# Some helpers are private, for example html_escape... as a workaround
|
4
|
+
# we are wrapping the helpers in a delegator that passes the methods
|
5
|
+
# along through a send, which will ignore private/public distinctions
|
6
|
+
def method_missing(method, *args, &block)
|
7
|
+
view_context.send(method, *args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def view_context
|
13
|
+
Draper::ViewContext.current
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/draper/railtie.rb
CHANGED
@@ -3,8 +3,8 @@ require 'rails/railtie'
|
|
3
3
|
module ActiveModel
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
generators do |app|
|
6
|
-
Rails::Generators.configure!
|
7
|
-
require
|
6
|
+
Rails::Generators.configure! app.config.generators
|
7
|
+
require 'generators/resource_override'
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -12,40 +12,36 @@ end
|
|
12
12
|
module Draper
|
13
13
|
class Railtie < Rails::Railtie
|
14
14
|
|
15
|
-
##
|
16
|
-
# The `app/decorators` path is eager loaded
|
17
|
-
#
|
18
|
-
# This is the standard "Rails Way" to add paths from which constants
|
19
|
-
# can be loaded.
|
20
|
-
#
|
21
15
|
config.after_initialize do |app|
|
22
|
-
app.config.paths.add 'app/decorators', :
|
16
|
+
app.config.paths.add 'app/decorators', eager_load: true
|
23
17
|
end
|
24
18
|
|
25
|
-
initializer "draper.
|
26
|
-
ActiveSupport.on_load
|
27
|
-
Draper
|
19
|
+
initializer "draper.setup_action_controller" do |app|
|
20
|
+
ActiveSupport.on_load :action_controller do
|
21
|
+
Draper.setup_action_controller self
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
initializer "draper.
|
32
|
-
ActiveSupport.on_load
|
33
|
-
Draper
|
25
|
+
initializer "draper.setup_action_mailer" do |app|
|
26
|
+
ActiveSupport.on_load :action_mailer do
|
27
|
+
Draper.setup_action_mailer self
|
34
28
|
end
|
35
29
|
end
|
36
30
|
|
37
|
-
initializer "draper.
|
38
|
-
ActiveSupport.on_load
|
39
|
-
|
31
|
+
initializer "draper.setup_active_record" do |app|
|
32
|
+
ActiveSupport.on_load :active_record do
|
33
|
+
Draper.setup_active_record self
|
40
34
|
end
|
41
35
|
end
|
42
36
|
|
43
37
|
console do
|
44
38
|
require 'action_controller/test_case'
|
45
39
|
ApplicationController.new.view_context
|
46
|
-
Draper::ViewContext.
|
47
|
-
|
48
|
-
|
40
|
+
Draper::ViewContext.build_view_context
|
41
|
+
end
|
42
|
+
|
43
|
+
rake_tasks do
|
44
|
+
Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
|
49
45
|
end
|
50
46
|
end
|
51
47
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Draper
|
2
|
+
class Security
|
3
|
+
def initialize
|
4
|
+
@methods = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def denies(*methods)
|
8
|
+
apply_strategy :denies
|
9
|
+
add_methods methods
|
10
|
+
end
|
11
|
+
|
12
|
+
def denies_all
|
13
|
+
apply_strategy :denies_all
|
14
|
+
end
|
15
|
+
|
16
|
+
def allows(*methods)
|
17
|
+
apply_strategy :allows
|
18
|
+
add_methods methods
|
19
|
+
end
|
20
|
+
|
21
|
+
def allow?(method)
|
22
|
+
case strategy
|
23
|
+
when :allows
|
24
|
+
methods.include?(method)
|
25
|
+
when :denies
|
26
|
+
!methods.include?(method)
|
27
|
+
when :denies_all
|
28
|
+
false
|
29
|
+
when nil
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :methods, :strategy
|
37
|
+
|
38
|
+
def apply_strategy(new_strategy)
|
39
|
+
raise ArgumentError, "Use only one of 'allows', 'denies', or 'denies_all'." if strategy && strategy != new_strategy
|
40
|
+
@strategy = new_strategy
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_methods(new_methods)
|
44
|
+
raise ArgumentError, "Specify at least one method when using #{strategy}" if new_methods.empty?
|
45
|
+
@methods += new_methods.map(&:to_sym)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|