draper 0.18.0 → 1.0.0.beta1
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/.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
|