draper 0.17.0 → 1.0.0.beta2

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.
Files changed (154) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.markdown +49 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +50 -31
  6. data/Readme.markdown +63 -92
  7. data/draper.gemspec +6 -4
  8. data/lib/draper/collection_decorator.rb +88 -0
  9. data/lib/draper/decoratable.rb +44 -0
  10. data/lib/draper/decorated_association.rb +55 -0
  11. data/lib/draper/decorator.rb +260 -0
  12. data/lib/draper/finders.rb +34 -0
  13. data/lib/draper/helper_proxy.rb +16 -0
  14. data/lib/draper/railtie.rb +17 -21
  15. data/lib/draper/security.rb +48 -0
  16. data/lib/draper/tasks/tu.rake +5 -0
  17. data/lib/draper/test/minitest_integration.rb +5 -24
  18. data/lib/draper/test/rspec_integration.rb +0 -6
  19. data/lib/draper/test/test_unit_integration.rb +9 -0
  20. data/lib/draper/version.rb +1 -1
  21. data/lib/draper/view_context.rb +24 -6
  22. data/lib/draper/view_helpers.rb +36 -0
  23. data/lib/draper.rb +48 -7
  24. data/lib/generators/decorator/decorator_generator.rb +1 -1
  25. data/lib/generators/decorator/templates/decorator.rb +0 -1
  26. data/lib/generators/rspec/decorator_generator.rb +1 -1
  27. data/lib/generators/test_unit/decorator_generator.rb +1 -1
  28. data/lib/generators/test_unit/templates/decorator_test.rb +0 -3
  29. data/spec/draper/collection_decorator_spec.rb +240 -0
  30. data/spec/draper/decoratable_spec.rb +164 -0
  31. data/spec/draper/decorated_association_spec.rb +130 -0
  32. data/spec/draper/decorator_spec.rb +622 -0
  33. data/spec/draper/finders_spec.rb +145 -0
  34. data/spec/draper/helper_proxy_spec.rb +12 -0
  35. data/spec/draper/security_spec.rb +158 -0
  36. data/spec/draper/view_helpers_spec.rb +41 -0
  37. data/spec/dummy/.rspec +2 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  41. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  42. data/spec/dummy/app/controllers/posts_controller.rb +17 -0
  43. data/spec/dummy/app/decorators/post_decorator.rb +29 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  46. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  47. data/spec/dummy/app/models/post.rb +3 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  49. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  50. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  51. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  52. data/spec/dummy/config/application.rb +64 -0
  53. data/spec/dummy/config/boot.rb +10 -0
  54. data/spec/dummy/config/database.yml +25 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +34 -0
  57. data/spec/dummy/config/environments/production.rb +55 -0
  58. data/spec/dummy/config/environments/test.rb +32 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/inflections.rb +15 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  63. data/spec/dummy/config/initializers/session_store.rb +8 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +5 -0
  66. data/spec/dummy/config/routes.rb +7 -0
  67. data/spec/dummy/config.ru +4 -0
  68. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  69. data/spec/dummy/db/schema.rb +21 -0
  70. data/spec/dummy/db/seeds.rb +2 -0
  71. data/spec/dummy/lib/tasks/spec.rake +5 -0
  72. data/spec/dummy/log/.gitkeep +0 -0
  73. data/spec/dummy/public/404.html +26 -0
  74. data/spec/dummy/public/422.html +26 -0
  75. data/spec/dummy/public/500.html +25 -0
  76. data/spec/dummy/public/favicon.ico +0 -0
  77. data/spec/dummy/script/rails +6 -0
  78. data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -0
  79. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  80. data/spec/dummy/spec/spec_helper.rb +9 -0
  81. data/spec/generators/decorator/decorator_generator_spec.rb +25 -6
  82. data/spec/integration/integration_spec.rb +33 -0
  83. data/spec/minitest-rails/spec_type_spec.rb +63 -0
  84. data/{performance → spec/performance}/decorators.rb +2 -4
  85. data/spec/spec_helper.rb +23 -26
  86. data/spec/support/action_controller.rb +12 -0
  87. data/spec/support/active_model.rb +7 -0
  88. data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
  89. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  90. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  91. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  92. data/spec/support/decorators/product_decorator.rb +23 -0
  93. data/spec/support/decorators/products_decorator.rb +10 -0
  94. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  95. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  96. data/spec/support/dummy_app.rb +84 -0
  97. data/spec/support/matchers/have_text.rb +50 -0
  98. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  99. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  100. data/spec/support/{samples → models}/product.rb +17 -2
  101. data/spec/support/models/some_thing.rb +5 -0
  102. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  103. metadata +211 -102
  104. data/doc/ApplicationDecorator.html +0 -147
  105. data/doc/Draper/AllHelpers.html +0 -256
  106. data/doc/Draper/Base.html +0 -1222
  107. data/doc/Draper/DecoratorGenerator.html +0 -216
  108. data/doc/Draper/LazyHelpers.html +0 -174
  109. data/doc/Draper/ModelSupport.html +0 -164
  110. data/doc/Draper/System.html +0 -179
  111. data/doc/Draper.html +0 -123
  112. data/doc/_index.html +0 -172
  113. data/doc/class_list.html +0 -47
  114. data/doc/css/common.css +0 -1
  115. data/doc/css/full_list.css +0 -53
  116. data/doc/css/style.css +0 -320
  117. data/doc/file_list.html +0 -46
  118. data/doc/frames.html +0 -13
  119. data/doc/index.html +0 -172
  120. data/doc/js/app.js +0 -205
  121. data/doc/js/full_list.js +0 -150
  122. data/doc/js/jquery.js +0 -16
  123. data/doc/method_list.html +0 -174
  124. data/doc/top-level-namespace.html +0 -103
  125. data/lib/draper/active_model_support.rb +0 -27
  126. data/lib/draper/base.rb +0 -312
  127. data/lib/draper/decorated_enumerable_proxy.rb +0 -57
  128. data/lib/draper/model_support.rb +0 -17
  129. data/lib/draper/rspec_integration.rb +0 -2
  130. data/lib/draper/system.rb +0 -10
  131. data/lib/draper/test/view_context.rb +0 -12
  132. data/spec/draper/base_spec.rb +0 -863
  133. data/spec/draper/helper_support_spec.rb +0 -18
  134. data/spec/draper/model_support_spec.rb +0 -48
  135. data/spec/draper/view_context_spec.rb +0 -30
  136. data/spec/support/samples/active_model.rb +0 -9
  137. data/spec/support/samples/application_controller.rb +0 -42
  138. data/spec/support/samples/application_helper.rb +0 -8
  139. data/spec/support/samples/decorator.rb +0 -5
  140. data/spec/support/samples/decorator_with_allows.rb +0 -3
  141. data/spec/support/samples/decorator_with_denies.rb +0 -3
  142. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  143. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  144. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  145. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  146. data/spec/support/samples/product_decorator.rb +0 -7
  147. data/spec/support/samples/sequel_product.rb +0 -13
  148. data/spec/support/samples/some_thing.rb +0 -2
  149. data/spec/support/samples/some_thing_decorator.rb +0 -3
  150. /data/{performance → spec/performance}/active_record.rb +0 -0
  151. /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  152. /data/{performance → spec/performance}/models.rb +0 -0
  153. /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  154. /data/spec/support/{samples → models}/widget.rb +0 -0
@@ -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,260 @@
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
+ # Specify the class that this class decorates.
38
+ #
39
+ # @param [String, Symbol, Class] Class or name of class to decorate.
40
+ def self.decorates(klass)
41
+ @source_class = klass.to_s.classify.constantize
42
+ end
43
+
44
+ # @return [Class] The source class corresponding to this
45
+ # decorator class
46
+ def self.source_class
47
+ @source_class ||= inferred_source_class
48
+ end
49
+
50
+ # Checks whether this decorator class has a corresponding
51
+ # source class
52
+ def self.source_class?
53
+ source_class
54
+ rescue Draper::UninferrableSourceError
55
+ false
56
+ end
57
+
58
+ # Automatically decorates ActiveRecord finder methods, so that
59
+ # you can use `ProductDecorator.find(id)` instead of
60
+ # `ProductDecorator.decorate(Product.find(id))`.
61
+ #
62
+ # The model class to be found is defined by `decorates` or
63
+ # inferred from the decorator class name.
64
+ #
65
+ def self.decorates_finders
66
+ extend Draper::Finders
67
+ end
68
+
69
+ # Typically called within a decorator definition, this method causes
70
+ # the assocation to be decorated when it is retrieved.
71
+ #
72
+ # @param [Symbol] association name of association to decorate, like `:products`
73
+ # @option options [Class] :with the decorator to apply to the association
74
+ # @option options [Symbol] :scope a scope to apply when fetching the association
75
+ def self.decorates_association(association, options = {})
76
+ define_method(association) do
77
+ decorated_associations[association] ||= Draper::DecoratedAssociation.new(source, association, options)
78
+ decorated_associations[association].call
79
+ end
80
+ end
81
+
82
+ # A convenience method for decorating multiple associations. Calls
83
+ # decorates_association on each of the given symbols.
84
+ #
85
+ # @param [Symbols*] associations name of associations to decorate
86
+ def self.decorates_associations(*associations)
87
+ options = associations.extract_options!
88
+ associations.each do |association|
89
+ decorates_association(association, options)
90
+ end
91
+ end
92
+
93
+ # Specifies a black list of methods which may *not* be proxied to
94
+ # the wrapped object.
95
+ #
96
+ # Do not use both `.allows` and `.denies` together, either write
97
+ # a whitelist with `.allows` or a blacklist with `.denies`
98
+ #
99
+ # @param [Symbols*] methods methods to deny like `:find, :find_by_name`
100
+ def self.denies(*methods)
101
+ security.denies(*methods)
102
+ end
103
+
104
+ # Specifies that all methods may *not* be proxied to the wrapped object.
105
+ #
106
+ # Do not use `.allows` and `.denies` in combination with '.denies_all'
107
+ def self.denies_all
108
+ security.denies_all
109
+ end
110
+
111
+ # Specifies a white list of methods which *may* be proxied to
112
+ # the wrapped object. When `allows` is used, only the listed
113
+ # methods and methods defined in the decorator itself will be
114
+ # available.
115
+ #
116
+ # Do not use both `.allows` and `.denies` together, either write
117
+ # a whitelist with `.allows` or a blacklist with `.denies`
118
+ #
119
+ # @param [Symbols*] methods methods to allow like `:find, :find_by_name`
120
+ def self.allows(*methods)
121
+ security.allows(*methods)
122
+ end
123
+
124
+ # Creates a new CollectionDecorator for the given collection.
125
+ #
126
+ # @param [Object] source collection to decorate
127
+ # @param [Hash] options passed to each item's decorator (except
128
+ # for the keys listed below)
129
+ # @option options [Class,Symbol] :with (self) the class used to decorate
130
+ # items, or `:infer` to call each item's `decorate` method instead
131
+ def self.decorate_collection(source, options = {})
132
+ Draper::CollectionDecorator.new(source, options.reverse_merge(with: self))
133
+ end
134
+
135
+ # Get the chain of decorators applied to the object.
136
+ #
137
+ # @return [Array] list of decorator classes
138
+ def applied_decorators
139
+ chain = source.respond_to?(:applied_decorators) ? source.applied_decorators : []
140
+ chain << self.class
141
+ end
142
+
143
+ # Checks if a given decorator has been applied.
144
+ #
145
+ # @param [Class] decorator_class
146
+ def decorated_with?(decorator_class)
147
+ applied_decorators.include?(decorator_class)
148
+ end
149
+
150
+ def decorated?
151
+ true
152
+ end
153
+
154
+ # Delegates == to the decorated models
155
+ #
156
+ # @return [Boolean] true if other's model == self's model
157
+ def ==(other)
158
+ source == (other.respond_to?(:source) ? other.source : other)
159
+ end
160
+
161
+ def kind_of?(klass)
162
+ super || source.kind_of?(klass)
163
+ end
164
+ alias_method :is_a?, :kind_of?
165
+
166
+ # We always want to delegate present, in case we decorate a nil object.
167
+ #
168
+ # I don't like the idea of decorating a nil object, but we'll deal with
169
+ # that later.
170
+ def present?
171
+ source.present?
172
+ end
173
+
174
+ # For ActiveModel compatibilty
175
+ def to_model
176
+ self
177
+ end
178
+
179
+ # For ActiveModel compatibility
180
+ def to_param
181
+ source.to_param
182
+ end
183
+
184
+ def method_missing(method, *args, &block)
185
+ if delegatable_method?(method)
186
+ self.class.define_proxy(method)
187
+ send(method, *args, &block)
188
+ else
189
+ super
190
+ end
191
+ end
192
+
193
+ def respond_to?(method, include_private = false)
194
+ super || delegatable_method?(method)
195
+ end
196
+
197
+ def self.method_missing(method, *args, &block)
198
+ if delegatable_method?(method)
199
+ source_class.send(method, *args, &block)
200
+ else
201
+ super
202
+ end
203
+ end
204
+
205
+ def self.respond_to?(method, include_private = false)
206
+ super || delegatable_method?(method)
207
+ end
208
+
209
+ private
210
+
211
+ def delegatable_method?(method)
212
+ allow?(method) && source.respond_to?(method)
213
+ end
214
+
215
+ def self.delegatable_method?(method)
216
+ source_class? && source_class.respond_to?(method)
217
+ end
218
+
219
+ def self.inferred_source_class
220
+ uninferrable_source if name.nil? || name.demodulize !~ /.+Decorator$/
221
+
222
+ begin
223
+ name.chomp("Decorator").constantize
224
+ rescue NameError
225
+ uninferrable_source
226
+ end
227
+ end
228
+
229
+ def self.uninferrable_source
230
+ raise Draper::UninferrableSourceError.new(self)
231
+ end
232
+
233
+ def self.define_proxy(method)
234
+ define_method(method) do |*args, &block|
235
+ source.send(method, *args, &block)
236
+ end
237
+ end
238
+
239
+ def self.security
240
+ @security ||= Security.new
241
+ end
242
+
243
+ def allow?(method)
244
+ self.class.security.allow?(method)
245
+ end
246
+
247
+ def handle_multiple_decoration
248
+ if source.instance_of?(self.class)
249
+ self.options = source.options if options.empty?
250
+ self.source = source.source
251
+ elsif source.decorated_with?(self.class)
252
+ warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}"
253
+ end
254
+ end
255
+
256
+ def decorated_associations
257
+ @decorated_associations ||= {}
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,34 @@
1
+ module Draper
2
+ module Finders
3
+
4
+ def find(id, options = {})
5
+ decorate(source_class.find(id), options)
6
+ end
7
+
8
+ def all(options = {})
9
+ decorate_collection(source_class.all, options)
10
+ end
11
+
12
+ def first(options = {})
13
+ decorate(source_class.first, options)
14
+ end
15
+
16
+ def last(options = {})
17
+ decorate(source_class.last, options)
18
+ end
19
+
20
+ def method_missing(method, *args, &block)
21
+ result = super
22
+ options = args.extract_options!
23
+
24
+ case method.to_s
25
+ when /^find_((last_)?by_|or_(initialize|create)_by_)/
26
+ decorate(result, options)
27
+ when /^find_all_by_/
28
+ decorate_collection(result, options)
29
+ else
30
+ result
31
+ end
32
+ end
33
+ end
34
+ 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
@@ -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!(app.config.generators)
7
- require "generators/resource_override"
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', :eager_load => true
16
+ app.config.paths.add 'app/decorators', eager_load: true
23
17
  end
24
18
 
25
- initializer "draper.extend_action_controller_base" do |app|
26
- ActiveSupport.on_load(:action_controller) do
27
- Draper::System.setup(self)
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.extend_action_mailer_base" do |app|
32
- ActiveSupport.on_load(:action_mailer) do
33
- Draper::System.setup(self)
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.extend_active_record_base" do |app|
38
- ActiveSupport.on_load(:active_record) do
39
- self.send(:include, Draper::ModelSupport)
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.current.controller.request ||= ActionController::TestRequest.new
47
- Draper::ViewContext.current.request ||= Draper::ViewContext.current.controller.request
48
- Draper::ViewContext.current.params ||= {}
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
@@ -0,0 +1,5 @@
1
+ if Rake::Task.task_defined?('test:run')
2
+ Rake::Task['test:run'].enhance do
3
+ Rake::Task['test:decorators'].invoke
4
+ end
5
+ end
@@ -1,26 +1,7 @@
1
- require 'draper/test/view_context'
2
-
3
- module MiniTest
4
- class Spec
5
- class Decorator < Spec
6
- before { Draper::ViewContext.infect!(self) }
7
- end
1
+ class MiniTest::Rails::ActiveSupport::TestCase
2
+ # Use AS::TestCase for the base class when describing a decorator
3
+ register_spec_type(self) do |desc|
4
+ desc < Draper::Decorator if desc.is_a?(Class)
8
5
  end
9
- end
10
-
11
- class MiniTest::Unit::DecoratorTestCase < MiniTest::Unit::TestCase
12
- if method_defined?(:before_setup)
13
- # for minitext >= 2.11
14
- def before_setup
15
- super
16
- Draper::ViewContext.infect!(self)
17
- end
18
- else
19
- # for older minitest, like what ships w/Ruby 1.9
20
- add_setup_hook { Draper::ViewContext.infect!(self) }
21
- end
22
- end
23
-
24
- MiniTest::Spec.register_spec_type(MiniTest::Spec::Decorator) do |desc|
25
- desc.superclass == Draper::Base
6
+ register_spec_type(/Decorator( ?Test)?\z/i, self)
26
7
  end
@@ -1,5 +1,3 @@
1
- require 'draper/test/view_context'
2
-
3
1
  module Draper
4
2
  module DecoratorExampleGroup
5
3
  extend ActiveSupport::Concern
@@ -13,10 +11,6 @@ RSpec.configure do |config|
13
11
  :file_path => /spec[\\\/]decorators/
14
12
  }
15
13
 
16
- # Specs tagged type: :decorator set the Draper view context
17
- config.before :type => :decorator do
18
- Draper::ViewContext.infect!(self)
19
- end
20
14
  end
21
15
 
22
16
  if defined?(Capybara)
@@ -0,0 +1,9 @@
1
+ require "rake/testtask"
2
+ require "rails/test_unit/sub_test_task"
3
+
4
+ namespace :test do
5
+ Rails::SubTestTask.new(:decorators => "test:prepare") do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/decorators/**/*_test.rb"
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.17.0"
2
+ VERSION = "1.0.0.beta2"
3
3
  end
@@ -1,16 +1,34 @@
1
1
  module Draper
2
2
  module ViewContext
3
+ def view_context
4
+ super.tap do |context|
5
+ Draper::ViewContext.current = context
6
+ end
7
+ end
8
+
9
+ def self.current_controller
10
+ Thread.current[:current_controller] || ApplicationController.new
11
+ end
12
+
13
+ def self.current_controller=(controller)
14
+ Thread.current[:current_controller] = controller
15
+ end
16
+
3
17
  def self.current
4
- Thread.current[:current_view_context] || ApplicationController.new.view_context
18
+ Thread.current[:current_view_context] ||= build_view_context
5
19
  end
6
20
 
7
- def self.current=(input)
8
- Thread.current[:current_view_context] = input
21
+ def self.current=(context)
22
+ Thread.current[:current_view_context] = context
9
23
  end
10
24
 
11
- def view_context
12
- super.tap do |context|
13
- Draper::ViewContext.current = context
25
+ def self.build_view_context
26
+ current_controller.view_context.tap do |context|
27
+ if defined?(ActionController::TestRequest)
28
+ context.controller.request ||= ActionController::TestRequest.new
29
+ context.request ||= context.controller.request
30
+ context.params ||= {}
31
+ end
14
32
  end
15
33
  end
16
34
  end
@@ -0,0 +1,36 @@
1
+ module Draper
2
+ module ViewHelpers
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ # Access the helpers proxy to call built-in and user-defined
8
+ # Rails helpers from a class context.
9
+ #
10
+ # @return [HelperProxy] the helpers proxy
11
+ def helpers
12
+ @helpers ||= Draper::HelperProxy.new
13
+ end
14
+ alias_method :h, :helpers
15
+
16
+ end
17
+
18
+ # Access the helpers proxy to call built-in and user-defined
19
+ # Rails helpers. Aliased to `h` for convenience.
20
+ #
21
+ # @return [HelperProxy] the helpers proxy
22
+ def helpers
23
+ self.class.helpers
24
+ end
25
+ alias_method :h, :helpers
26
+
27
+ # Localize is something that's used quite often. Even though
28
+ # it's available through helpers, that's annoying. Aliased
29
+ # to `l` for convenience.
30
+ def localize(*args)
31
+ helpers.localize(*args)
32
+ end
33
+ alias_method :l, :localize
34
+
35
+ end
36
+ end