draper 0.9.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/Gemfile +2 -2
  2. data/Readme.markdown +19 -9
  3. data/lib/draper.rb +2 -1
  4. data/lib/draper/base.rb +70 -30
  5. data/lib/draper/decorated_enumerable_proxy.rb +13 -4
  6. data/lib/draper/model_support.rb +5 -5
  7. data/lib/draper/rspec_integration.rb +22 -0
  8. data/lib/draper/version.rb +1 -1
  9. data/lib/generators/draper/decorator/decorator_generator.rb +9 -7
  10. data/lib/generators/draper/decorator/templates/decorator.rb +5 -5
  11. data/lib/generators/draper/install/install_generator.rb +39 -0
  12. data/lib/generators/draper/{decorator → install}/templates/application_decorator.rb +6 -6
  13. data/lib/generators/draper/install/templates/application_decorator_spec.rb +4 -0
  14. data/lib/generators/{test_unit → draper/install}/templates/application_decorator_test.rb +0 -0
  15. data/lib/generators/rspec/decorator_generator.rb +0 -6
  16. data/lib/generators/test_unit/decorator_generator.rb +1 -7
  17. data/performance/active_record.rb +4 -0
  18. data/performance/bechmark.rb +55 -0
  19. data/performance/decorators.rb +47 -0
  20. data/performance/models.rb +20 -0
  21. data/spec/draper/base_spec.rb +162 -7
  22. data/spec/draper/model_support_spec.rb +10 -0
  23. data/spec/generators/draper/decorator/decorator_generator_spec.rb +0 -6
  24. data/spec/generators/draper/install/install_generator_spec.rb +46 -0
  25. data/spec/generators/rspec/decorator_generator_spec.rb +0 -6
  26. data/spec/generators/test_unit/decorator_generator_spec.rb +0 -6
  27. data/spec/spec_helper.rb +14 -1
  28. data/spec/support/samples/namespaced_product.rb +51 -0
  29. data/spec/support/samples/namespaced_product_decorator.rb +7 -0
  30. data/spec/support/samples/product.rb +12 -0
  31. data/spec/support/samples/specific_product_decorator.rb +4 -0
  32. data/spec/support/samples/widget.rb +2 -0
  33. data/spec/support/samples/widget_decorator.rb +2 -0
  34. metadata +26 -7
  35. data/lib/generators/rspec/templates/application_decorator_spec.rb +0 -5
data/Gemfile CHANGED
@@ -2,8 +2,8 @@ source :rubygems
2
2
 
3
3
  gem 'rake'
4
4
  gem 'rspec', '~> 2.0'
5
- gem 'activesupport', '~> 3.0.10'
6
- gem 'actionpack', "~> 3.0.10", :require => 'action_view'
5
+ gem 'activesupport', '~> 3.1.3'
6
+ gem 'actionpack', "~> 3.1.3", :require => 'action_view'
7
7
  gem 'ammeter', '~> 0.1.3', :require => 'ammeter/init'
8
8
  gem 'guard'
9
9
  gem 'guard-rspec'
data/Readme.markdown CHANGED
@@ -1,23 +1,25 @@
1
1
  # Draper: View Models for Rails
2
2
 
3
- ![TravisCI Build Status](https://secure.travis-ci.org/jcasimir/draper.png)
3
+ [![TravisCI Build Status](https://secure.travis-ci.org/jcasimir/draper.png)](http://travis-ci.org/jcasimir/draper)
4
+ [![Gemnasium Build Status](https://gemnasium.com/jcasimir/draper.png)](https://gemnasium.com/jcasimir/draper)
4
5
 
5
6
  ## Quick Start
6
7
 
7
8
  1. Add `gem 'draper'` to your `Gemfile` and `bundle`
8
- 2. Run `rails g draper:decorator YourModel`
9
- 3. Edit `app/decorators/[your_model]_decorator.rb` using:
9
+ 2. Run `rails g draper:install` to create the directory and `ApplicationDecorator`
10
+ 3. Run `rails g draper:decorator YourModel`
11
+ 4. Edit `app/decorators/[your_model]_decorator.rb` using:
10
12
  1. `h` to proxy to Rails/application helpers like `h.current_user`
11
13
  2. `[your_model]` to access the wrapped object like `article.created_at`
12
- 4. Put common decorations in `app/decorators/application.rb`
13
- 5. Wrap models in your controller with the decorator using:
14
+ 5. Put common decorations in `app/decorators/application.rb`
15
+ 6. Wrap models in your controller with the decorator using:
14
16
  1. `.find` automatic lookup & wrap
15
17
  ex: `ArticleDecorator.find(1)`
16
18
  2. `.decorate` method with single object or collection,
17
19
  ex: `ArticleDecorator.decorate(Article.all)`
18
20
  3. `.new` method with single object
19
21
  ex: `ArticleDecorator.new(Article.first)`
20
- 6. Output the instance methods in your view templates
22
+ 7. Output the instance methods in your view templates
21
23
  ex: `@article_decorator.created_at`
22
24
 
23
25
  ## Watch the RailsCast
@@ -209,7 +211,7 @@ When writing your controller actions, you have three options:
209
211
  * Call `.new` and pass in the object to be wrapped
210
212
 
211
213
  ```ruby
212
- ArticleDecorator.new(Article.find(params[:id]))`
214
+ ArticleDecorator.new(Article.find(params[:id]))
213
215
  ```
214
216
 
215
217
  * Call `.decorate` and pass in an object or collection of objects to be wrapped:
@@ -219,7 +221,7 @@ ArticleDecorator.decorate(Article.first) # Returns one instance of ArticleDecora
219
221
  ArticleDecorator.decorate(Article.all) # Returns an enumeration proxy of ArticleDecorator instances
220
222
  ```
221
223
 
222
- * Call `.find` to do automatically do a lookup on the `decorates` class:
224
+ * Call `.find` to automatically do a lookup on the `decorates` class:
223
225
 
224
226
  ```ruby
225
227
  ArticleDecorator.find(1)
@@ -247,6 +249,15 @@ class ActicleMailer < ActionMailer::Base
247
249
  end
248
250
  end
249
251
  ```
252
+ ### Integration with RSpec
253
+
254
+ Using the provided generator, Draper will place specs for your new decorator in `spec/decorators/`.
255
+
256
+ By default, specs in `spec/decorators` will be tagged as `type => :decorator`. Any spec tagged as `decorator` will run `ApplicationController.new.set_current_view_context` which makes helpers available to the decorator.
257
+
258
+ If your decorator specs live somewhere else, which they shouldn't, make sure to tag them with `type => :decorator`. If you don't tag them, Draper's helpers won't be available to your decorator while testing.
259
+
260
+ Note: If you're using Spork, you need to `require 'draper/rspec_integration'` in your Spork.prefork block.
250
261
 
251
262
  ## Possible Decoration Methods
252
263
 
@@ -318,7 +329,6 @@ class ArticleDecorator < ApplicationDecorator
318
329
  end
319
330
  end
320
331
  ```
321
-
322
332
  ## Issues / Pending
323
333
 
324
334
  * Documentation
data/lib/draper.rb CHANGED
@@ -6,5 +6,6 @@ require 'draper/model_support'
6
6
  require 'draper/helper_support'
7
7
  require 'draper/view_context'
8
8
  require 'draper/decorated_enumerable_proxy'
9
+ require 'draper/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
9
10
 
10
- Draper::System.setup
11
+ Draper::System.setup
data/lib/draper/base.rb CHANGED
@@ -15,15 +15,15 @@ module Draper
15
15
 
16
16
  # Initialize a new decorator instance by passing in
17
17
  # an instance of the source class. Pass in an optional
18
- # context is stored for later use.
18
+ # context inside the options hash is stored for later use.
19
19
  #
20
20
  # @param [Object] instance to wrap
21
- # @param [Object] context (optional)
22
- def initialize(input, context = {})
21
+ # @param [Hash] options (optional)
22
+ def initialize(input, options = {})
23
23
  input.inspect # forces evaluation of a lazy query from AR
24
24
  self.class.model_class = input.class if model_class.nil?
25
25
  @model = input
26
- self.context = context
26
+ self.context = options.fetch(:context, {})
27
27
  end
28
28
 
29
29
  # Proxies to the class specified by `decorates` to automatically
@@ -31,8 +31,8 @@ module Draper
31
31
  #
32
32
  # @param [Symbol or String] id to lookup
33
33
  # @return [Object] instance of this decorator class
34
- def self.find(input, context = {})
35
- self.new(model_class.find(input), context)
34
+ def self.find(input, options = {})
35
+ self.new(model_class.find(input), options)
36
36
  end
37
37
 
38
38
  # Typically called within a decorator definition, this method
@@ -43,16 +43,42 @@ module Draper
43
43
  # But they don't have to match in name, so a `EmployeeDecorator`
44
44
  # class could call `decorates :person` to wrap instances of `Person`
45
45
  #
46
- # This is primarilly set so the `.find` method knows which class
46
+ # This is primarilly set so the `.find` method knows which class
47
47
  # to query.
48
48
  #
49
49
  # @param [Symbol] class_name snakecase name of the decorated class, like `:product`
50
- def self.decorates(input)
51
- self.model_class = input.to_s.camelize.constantize
50
+ def self.decorates(input, options = {})
51
+ self.model_class = options[:class] || input.to_s.camelize.constantize
52
52
  model_class.send :include, Draper::ModelSupport
53
53
  define_method(input){ @model }
54
54
  end
55
55
 
56
+ # Typically called within a decorator definition, this method causes
57
+ # the assocation to be decorated when it is retrieved.
58
+ #
59
+ # @param [Symbol] name of association to decorate, like `:products`
60
+ # @option opts [Class] :with The decorator to decorate the association with
61
+ def self.decorates_association(association_symbol, options = {})
62
+ define_method(association_symbol) do
63
+ orig_association = model.send(association_symbol)
64
+ return orig_association if orig_association.nil?
65
+ if options[:with]
66
+ options[:with].decorate(orig_association)
67
+ else
68
+ reflection = model.class.reflect_on_association(association_symbol)
69
+ "#{reflection.klass}Decorator".constantize.decorate(orig_association)
70
+ end
71
+ end
72
+ end
73
+
74
+ # A convenience method for decorating multiple associations. Calls
75
+ # decorates_association on each of the given symbols.
76
+ #
77
+ # @param [Symbols*] name of associations to decorate
78
+ def self.decorates_associations(*association_symbols)
79
+ association_symbols.each{ |sym| decorates_association(sym) }
80
+ end
81
+
56
82
  # Specifies a black list of methods which may *not* be proxied to
57
83
  # to the wrapped object.
58
84
  #
@@ -83,7 +109,7 @@ module Draper
83
109
 
84
110
  # Initialize a new decorator instance by passing in
85
111
  # an instance of the source class. Pass in an optional
86
- # context is stored for later use.
112
+ # context into the options hash is stored for later use.
87
113
  #
88
114
  # When passing in a single object, using `.decorate` is
89
115
  # identical to calling `.new`. However, `.decorate` can
@@ -91,31 +117,37 @@ module Draper
91
117
  # individually decorated objects.
92
118
  #
93
119
  # @param [Object] instance(s) to wrap
94
- # @param [Object] context (optional)
95
- def self.decorate(input, context = {})
96
- input.respond_to?(:each) ? Draper::DecoratedEnumerableProxy.new(input, self, context) : new(input, context)
120
+ # @param [Hash] options (optional)
121
+ def self.decorate(input, options = {})
122
+ if input.respond_to?(:each)
123
+ Draper::DecoratedEnumerableProxy.new(input, self, options)
124
+ elsif options[:infer]
125
+ input.decorator(options)
126
+ else
127
+ new(input, options)
128
+ end
97
129
  end
98
-
130
+
99
131
  # Fetch all instances of the decorated class and decorate them.
100
132
  #
101
- # @param [Object] context (optional)
133
+ # @param [Hash] options (optional)
102
134
  # @return [Draper::DecoratedEnumerableProxy]
103
- def self.all(context = {})
104
- Draper::DecoratedEnumerableProxy.new(model_class.all, self, context)
135
+ def self.all(options = {})
136
+ Draper::DecoratedEnumerableProxy.new(model_class.all, self, options)
105
137
  end
106
-
107
- def self.first(context = {})
108
- decorate(model_class.first, context)
138
+
139
+ def self.first(options = {})
140
+ decorate(model_class.first, options)
109
141
  end
110
142
 
111
- def self.last(context = {})
112
- decorate(model_class.last, context)
143
+ def self.last(options = {})
144
+ decorate(model_class.last, options)
113
145
  end
114
146
 
115
147
  # Access the helpers proxy to call built-in and user-defined
116
148
  # Rails helpers. Aliased to `.h` for convinience.
117
149
  #
118
- # @return [Object] proxy
150
+ # @return [Object] proxy
119
151
  def helpers
120
152
  self.class.helpers
121
153
  end
@@ -124,13 +156,13 @@ module Draper
124
156
  # Access the helpers proxy to call built-in and user-defined
125
157
  # Rails helpers from a class context.
126
158
  #
127
- # @return [Object] proxy
159
+ # @return [Object] proxy
128
160
  class << self
129
161
  def helpers
130
162
  Draper::ViewContext.current
131
163
  end
132
164
  alias :h :helpers
133
- end
165
+ end
134
166
 
135
167
  # Fetch the original wrapped model.
136
168
  #
@@ -141,11 +173,16 @@ module Draper
141
173
 
142
174
  # Delegates == to the decorated models
143
175
  #
144
- # @return [Boolean] true if other's model == self's model
176
+ # @return [Boolean] true if other's model == self's model
145
177
  def ==(other)
146
178
  @model == (other.respond_to?(:model) ? other.model : other)
147
179
  end
148
180
 
181
+ def kind_of?(klass)
182
+ super || model.kind_of?(klass)
183
+ end
184
+ alias :is_a? :kind_of?
185
+
149
186
  def respond_to?(method, include_private = false)
150
187
  super || (allow?(method) && model.respond_to?(method))
151
188
  end
@@ -153,7 +190,10 @@ module Draper
153
190
  def method_missing(method, *args, &block)
154
191
  if allow?(method)
155
192
  begin
156
- model.send(method, *args, &block)
193
+ self.class.send :define_method, method do |*args, &block|
194
+ model.send(method, *args, &block)
195
+ end
196
+ self.send(method, *args, &block)
157
197
  rescue NoMethodError
158
198
  super
159
199
  end
@@ -161,11 +201,11 @@ module Draper
161
201
  super
162
202
  end
163
203
  end
164
-
204
+
165
205
  def self.method_missing(method, *args, &block)
166
206
  model_class.send(method, *args, &block)
167
207
  end
168
-
208
+
169
209
  def self.respond_to?(method, include_private = false)
170
210
  super || model_class.respond_to?(method)
171
211
  end
@@ -173,6 +213,6 @@ module Draper
173
213
  private
174
214
  def allow?(method)
175
215
  (!allowed? || allowed.include?(method) || FORCED_PROXY.include?(method)) && !denied.include?(method)
176
- end
216
+ end
177
217
  end
178
218
  end
@@ -2,16 +2,16 @@ module Draper
2
2
  class DecoratedEnumerableProxy
3
3
  include Enumerable
4
4
 
5
- def initialize(collection, klass, context)
6
- @wrapped_collection, @klass, @context = collection, klass, context
5
+ def initialize(collection, klass, options = {})
6
+ @wrapped_collection, @klass, @options = collection, klass, options
7
7
  end
8
8
 
9
9
  def each(&block)
10
- @wrapped_collection.each { |member| block.call(@klass.new(member, @context)) }
10
+ @wrapped_collection.each { |member| block.call(@klass.decorate(member, @options)) }
11
11
  end
12
12
 
13
13
  def to_ary
14
- @wrapped_collection.map { |member| @klass.new(member, @context) }
14
+ @wrapped_collection.map { |member| @klass.decorate(member, @options) }
15
15
  end
16
16
 
17
17
  def method_missing (method, *args, &block)
@@ -21,11 +21,20 @@ module Draper
21
21
  def respond_to?(method)
22
22
  super || @wrapped_collection.respond_to?(method)
23
23
  end
24
+
25
+ def kind_of?(klass)
26
+ super || @wrapped_collection.kind_of?(klass)
27
+ end
28
+ alias :is_a? :kind_of?
24
29
 
25
30
  def ==(other)
26
31
  @wrapped_collection == other
27
32
  end
28
33
 
34
+ def [](index)
35
+ @klass.new(@wrapped_collection[index], @options)
36
+ end
37
+
29
38
  def to_s
30
39
  "#<DecoratedEnumerableProxy of #{@klass} for #{@wrapped_collection.inspect}>"
31
40
  end
@@ -1,14 +1,14 @@
1
1
  module Draper::ModelSupport
2
- def decorator
3
- @decorator ||= "#{self.class.name}Decorator".constantize.decorate(self)
2
+ def decorator(options = {})
3
+ @decorator ||= "#{self.class.name}Decorator".constantize.decorate(self, options.merge(:infer => false))
4
4
  block_given? ? yield(@decorator) : @decorator
5
5
  end
6
-
6
+
7
7
  alias :decorate :decorator
8
8
 
9
9
  module ClassMethods
10
- def decorate(context = {})
11
- @decorator_proxy ||= "#{model_name}Decorator".constantize.decorate(self.scoped)
10
+ def decorate(options = {})
11
+ @decorator_proxy ||= "#{model_name}Decorator".constantize.decorate(self.scoped, options)
12
12
  block_given? ? yield(@decorator_proxy) : @decorator_proxy
13
13
  end
14
14
  end
@@ -0,0 +1,22 @@
1
+ module Draper
2
+ module DecoratorExampleGroup
3
+ extend ActiveSupport::Concern
4
+ included { metadata[:type] = :decorator }
5
+ end
6
+
7
+ RSpec.configure do |config|
8
+ # Automatically tag specs in specs/decorators as type: :decorator
9
+ config.include Draper::DecoratorExampleGroup, :type => :decorator, :example_group => {
10
+ :file_path => /spec[\\\/]decorators/
11
+ }
12
+
13
+ # Specs tagged type: :decorator set the Draper view context
14
+ config.around do |example|
15
+ if :decorator == example.metadata[:type]
16
+ ApplicationController.new.set_current_view_context
17
+ end
18
+ example.call
19
+ end
20
+ end
21
+ end
22
+
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.9.5"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -1,16 +1,18 @@
1
1
  module Draper
2
2
  class DecoratorGenerator < Rails::Generators::NamedBase
3
+ desc <<-DESC
4
+ Description:
5
+ Generate a decorator for the given model.
6
+ Example: rails g draper:decorator Article
7
+ generates: "app/decorators/article_decorator"
8
+ "spec/decorators/article_decorator_spec"
9
+ DESC
10
+
3
11
  source_root File.expand_path('../templates', __FILE__)
4
12
 
5
13
  DECORATORS_ROOT = 'app/decorators/'
6
- APPLICATION_DECORATOR = 'application_decorator.rb'
7
- APPLICATION_DECORATOR_PATH = DECORATORS_ROOT + APPLICATION_DECORATOR
8
14
 
9
- def build_model_and_application_decorators
10
- empty_directory "app/decorators"
11
- unless File.exists?(APPLICATION_DECORATOR_PATH)
12
- template APPLICATION_DECORATOR, APPLICATION_DECORATOR_PATH
13
- end
15
+ def build_model_decorator
14
16
  template 'decorator.rb', "#{DECORATORS_ROOT}#{singular_name}_decorator.rb"
15
17
  end
16
18
 
@@ -6,9 +6,9 @@ class <%= singular_name.camelize %>Decorator < ApplicationDecorator
6
6
  #
7
7
  # Normal Usage: helpers.number_to_currency(2)
8
8
  # Abbreviated : h.number_to_currency(2)
9
- #
10
- # Or, optionally enable "lazy helpers" by calling this method:
11
- # lazy_helpers
9
+ #
10
+ # Or, optionally enable "lazy helpers" by including this module:
11
+ # include Draper::LazyHelpers
12
12
  # Then use the helpers with no proxy:
13
13
  # number_to_currency(2)
14
14
 
@@ -24,9 +24,9 @@ class <%= singular_name.camelize %>Decorator < ApplicationDecorator
24
24
  # Presentation Methods
25
25
  # Define your own instance methods, even overriding accessors
26
26
  # generated by ActiveRecord:
27
- #
27
+ #
28
28
  # def created_at
29
- # h.content_tag :span, time.strftime("%a %m/%d/%y"),
29
+ # h.content_tag :span, time.strftime("%a %m/%d/%y"),
30
30
  # :class => 'timestamp'
31
31
  # end
32
32
  end
@@ -0,0 +1,39 @@
1
+ module Draper
2
+ class InstallGenerator < Rails::Generators::Base
3
+
4
+ desc <<-DESC
5
+ Description:
6
+ Generate application and spec decorators in your application.
7
+ DESC
8
+
9
+ class_option "test-framework", :type => :string, :default => "rspec", :aliases => "-t", :desc => "Test framework to be invoked"
10
+
11
+ source_root File.expand_path('../templates', __FILE__)
12
+
13
+ def build_application_decorator
14
+ empty_directory 'app/decorators'
15
+ template 'application_decorator.rb', File.join('app/decorators', 'application_decorator.rb')
16
+ end
17
+
18
+ def build_decorator_tests
19
+ case options["test-framework"]
20
+ when "rspec"
21
+ build_application_decorator_spec
22
+ when "test_unit"
23
+ build_application_decorator_test
24
+ end
25
+ end
26
+
27
+ private
28
+ def build_application_decorator_spec
29
+ empty_directory 'spec/decorators'
30
+ template 'application_decorator_spec.rb', File.join('spec/decorators', 'application_decorator_spec.rb')
31
+ end
32
+
33
+ def build_application_decorator_test
34
+ empty_directory 'test/decorators/'
35
+ template 'application_decorator_test.rb', File.join('test/decorators', 'application_decorator_test.rb')
36
+ end
37
+
38
+ end
39
+ end
@@ -4,24 +4,24 @@ class ApplicationDecorator < Draper::Base
4
4
  # ex: number_to_currency(model.price)
5
5
  # CON: Add a bazillion methods into your decorator's namespace
6
6
  # and probably sacrifice performance/memory
7
- #
7
+ #
8
8
  # Enable them by uncommenting this line:
9
9
  # lazy_helpers
10
10
 
11
11
  # Shared Decorations
12
12
  # Consider defining shared methods common to all your models.
13
- #
13
+ #
14
14
  # Example: standardize the formatting of timestamps
15
15
  #
16
16
  # def formatted_timestamp(time)
17
- # h.content_tag :span, time.strftime("%a %m/%d/%y"),
18
- # :class => 'timestamp'
17
+ # h.content_tag :span, time.strftime("%a %m/%d/%y"),
18
+ # :class => 'timestamp'
19
19
  # end
20
- #
20
+ #
21
21
  # def created_at
22
22
  # formatted_timestamp(model.created_at)
23
23
  # end
24
- #
24
+ #
25
25
  # def updated_at
26
26
  # formatted_timestamp(model.updated_at)
27
27
  # end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationDecorator do
4
+ end
@@ -3,14 +3,8 @@ module Rspec
3
3
  source_root File.expand_path('../templates', __FILE__)
4
4
 
5
5
  SPEC_ROOT = 'spec/decorators/'
6
- APPLICATION_DECORATOR_SPEC = 'application_decorator_spec.rb'
7
- APPLICATION_DECORATOR_SPEC_PATH = SPEC_ROOT + APPLICATION_DECORATOR_SPEC
8
6
 
9
7
  def build_model_and_application_decorator_specs
10
- empty_directory SPEC_ROOT
11
- unless File.exists?(APPLICATION_DECORATOR_SPEC_PATH)
12
- template APPLICATION_DECORATOR_SPEC, APPLICATION_DECORATOR_SPEC_PATH
13
- end
14
8
  template 'decorator_spec.rb', "#{SPEC_ROOT}#{singular_name}_decorator_spec.rb"
15
9
  end
16
10
  end
@@ -3,14 +3,8 @@ module TestUnit
3
3
  source_root File.expand_path('../templates', __FILE__)
4
4
 
5
5
  TEST_ROOT = 'test/decorators/'
6
- APPLICATION_DECORATOR_TEST = 'application_decorator_test.rb'
7
- APPLICATION_DECORATOR_TEST_PATH = TEST_ROOT + APPLICATION_DECORATOR_TEST
8
6
 
9
- def build_model_and_application_decorator_tests
10
- empty_directory TEST_ROOT
11
- unless File.exists?(APPLICATION_DECORATOR_TEST_PATH)
12
- template APPLICATION_DECORATOR_TEST, APPLICATION_DECORATOR_TEST_PATH
13
- end
7
+ def build_model_decorator_tests
14
8
  template 'decorator_test.rb', "#{TEST_ROOT}#{singular_name}_decorator_test.rb"
15
9
  end
16
10
  end
@@ -0,0 +1,4 @@
1
+ module ActiveRecord
2
+ class Base
3
+ end
4
+ end
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
4
+ Bundler.require(:default) if defined?(Bundler)
5
+
6
+ require "benchmark"
7
+ require "draper"
8
+ require "./performance/models"
9
+ require "./performance/decorators"
10
+
11
+ Benchmark.bm do |bm|
12
+ puts "\n[ Exclusivelly using #method_missing for model delegation ]"
13
+ [ 1_000, 10_000, 100_000 ].each do |i|
14
+ puts "\n[ #{i} ]"
15
+ bm.report("#new ") do
16
+ i.times do |n|
17
+ ProductDecorator.decorate(Product.new)
18
+ end
19
+ end
20
+
21
+ bm.report("#hello_world ") do
22
+ i.times do |n|
23
+ ProductDecorator.decorate(Product.new).hello_world
24
+ end
25
+ end
26
+
27
+ bm.report("#sample_class_method ") do
28
+ i.times do |n|
29
+ ProductDecorator.decorate(Product.new).class.sample_class_method
30
+ end
31
+ end
32
+ end
33
+
34
+ puts "\n[ Defining methods on method_missing first hit ]"
35
+ [ 1_000, 10_000, 100_000 ].each do |i|
36
+ puts "\n[ #{i} ]"
37
+ bm.report("#new ") do
38
+ i.times do |n|
39
+ FastProductDecorator.decorate(FastProduct.new)
40
+ end
41
+ end
42
+
43
+ bm.report("#hello_world ") do
44
+ i.times do |n|
45
+ FastProductDecorator.decorate(FastProduct.new).hello_world
46
+ end
47
+ end
48
+
49
+ bm.report("#sample_class_method ") do
50
+ i.times do |n|
51
+ FastProductDecorator.decorate(FastProduct.new).class.sample_class_method
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ require "./performance/models"
2
+ class ProductDecorator < Draper::Base
3
+ decorates :product
4
+
5
+ def awesome_title
6
+ "Awesome Title"
7
+ end
8
+
9
+ # Original #method_missing
10
+ def method_missing(method, *args, &block)
11
+ if allow?(method)
12
+ begin
13
+ model.send(method, *args, &block)
14
+ rescue NoMethodError
15
+ super
16
+ end
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ class FastProductDecorator < Draper::Base
25
+ decorates :product
26
+
27
+ def awesome_title
28
+ "Awesome Title"
29
+ end
30
+
31
+ # Modified #method_missing
32
+ def method_missing(method, *args, &block)
33
+ if allow?(method)
34
+ begin
35
+ self.class.send :define_method, method do |*args, &block|
36
+ model.send(method, *args, &block)
37
+ end
38
+ self.send(method, *args, &block)
39
+ rescue NoMethodError
40
+ super
41
+ end
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,20 @@
1
+ require "./performance/active_record"
2
+ class Product < ActiveRecord::Base
3
+ def self.sample_class_method
4
+ "sample class method"
5
+ end
6
+
7
+ def hello_world
8
+ "Hello, World"
9
+ end
10
+ end
11
+
12
+ class FastProduct < ActiveRecord::Base
13
+ def self.sample_class_method
14
+ "sample class method"
15
+ end
16
+
17
+ def hello_world
18
+ "Hello, World"
19
+ end
20
+ end
@@ -58,6 +58,58 @@ describe Draper::Base do
58
58
  pd = ProductDecorator.new(source)
59
59
  pd.send(:product).should == source
60
60
  end
61
+
62
+ context("namespaced model supporting") do
63
+ let(:source){ Namespace::Product.new }
64
+
65
+ it "sets the model class for the decorator" do
66
+ decorator = Namespace::ProductDecorator.new(source)
67
+ decorator.model_class.should == Namespace::Product
68
+ end
69
+
70
+ it "creates a named accessor for the wrapped model" do
71
+ pd = Namespace::ProductDecorator.new(source)
72
+ pd.send(:product).should == source
73
+ end
74
+ end
75
+ end
76
+
77
+ context(".decorates_association") do
78
+ context "for collection associations" do
79
+ before(:each){ subject.class_eval{ decorates_association :similar_products } }
80
+ it "causes the association's method to return a collection of wrapped objects" do
81
+ subject.similar_products.each{ |decorated| decorated.should be_instance_of(ProductDecorator) }
82
+ end
83
+ end
84
+
85
+ context "for a singular association" do
86
+ before(:each){ subject.class_eval{ decorates_association :previous_version } }
87
+ it "causes the association's method to return a single wrapped object if the association is singular" do
88
+ subject.previous_version.should be_instance_of(ProductDecorator)
89
+ end
90
+
91
+ it "causes the association's method to return nil if the association is nil" do
92
+ source.stub(:previous_version){ nil }
93
+ subject.previous_version.should be_nil
94
+ end
95
+ end
96
+
97
+ context "with a specific decorator specified" do
98
+ before(:each){ subject.class_eval{ decorates_association :previous_version, :with => SpecificProductDecorator } }
99
+ it "causes the association to be decorated with the specified association" do
100
+ subject.previous_version.should be_instance_of(SpecificProductDecorator)
101
+ end
102
+ end
103
+ end
104
+
105
+ context('.decorates_associations') do
106
+ subject { Decorator }
107
+ it "decorates each of the associations" do
108
+ subject.should_receive(:decorates_association).with(:similar_products)
109
+ subject.should_receive(:decorates_association).with(:previous_version)
110
+
111
+ subject.decorates_associations :similar_products, :previous_version
112
+ end
61
113
  end
62
114
 
63
115
  context(".model / .to_model") do
@@ -121,7 +173,7 @@ describe Draper::Base do
121
173
  end
122
174
 
123
175
  it "should accept and store a context" do
124
- pd = ProductDecorator.find(1, :admin)
176
+ pd = ProductDecorator.find(1, :context => :admin)
125
177
  pd.context.should == :admin
126
178
  end
127
179
  end
@@ -150,7 +202,7 @@ describe Draper::Base do
150
202
  context "with a context" do
151
203
  let(:context) {{ :some => 'data' }}
152
204
 
153
- subject { Draper::Base.decorate(source, context) }
205
+ subject { Draper::Base.decorate(source, :context => context) }
154
206
 
155
207
  context "when given a collection of source objects" do
156
208
  let(:source) { [Product.new, Product.new] }
@@ -167,6 +219,47 @@ describe Draper::Base do
167
219
  end
168
220
  end
169
221
 
222
+ context "does not infer collections by default" do
223
+ subject { Draper::Base.decorate(source).to_ary }
224
+
225
+ let(:source) { [Product.new, Widget.new] }
226
+
227
+ it "returns a collection of wrapped objects all with the same decorator" do
228
+ subject.first.class.name.should eql 'Draper::Base'
229
+ subject.last.class.name.should eql 'Draper::Base'
230
+ end
231
+ end
232
+
233
+ context "does not infer single items by default" do
234
+ subject { Draper::Base.decorate(source) }
235
+
236
+ let(:source) { Product.new }
237
+
238
+ it "returns a decorator of the type explicity used in the call" do
239
+ subject.class.should eql Draper::Base
240
+ end
241
+ end
242
+
243
+ context "returns a collection containing only the explicit decorator used in the call" do
244
+ subject { Draper::Base.decorate(source, :infer => true).to_ary }
245
+
246
+ let(:source) { [Product.new, Widget.new] }
247
+
248
+ it "returns a mixed collection of wrapped objects" do
249
+ subject.first.class.should eql ProductDecorator
250
+ subject.last.class.should eql WidgetDecorator
251
+ end
252
+ end
253
+
254
+ context "when given a single object" do
255
+ subject { Draper::Base.decorate(source, :infer => true) }
256
+
257
+ let(:source) { Product.new }
258
+
259
+ it "can also infer its decorator" do
260
+ subject.class.should eql ProductDecorator
261
+ end
262
+ end
170
263
  end
171
264
 
172
265
  context('.==') do
@@ -175,7 +268,7 @@ describe Draper::Base do
175
268
  subject.should == other
176
269
  end
177
270
  end
178
-
271
+
179
272
  context 'position accessors' do
180
273
  [:first, :last].each do |method|
181
274
  context "##{method}" do
@@ -188,9 +281,9 @@ describe Draper::Base do
188
281
  end
189
282
 
190
283
  it "should accept an optional context" do
191
- ProductDecorator.send(method, :admin).context.should == :admin
284
+ ProductDecorator.send(method, :context => :admin).context.should == :admin
192
285
  end
193
- end
286
+ end
194
287
  end
195
288
  end
196
289
 
@@ -255,13 +348,38 @@ describe Draper::Base do
255
348
  subject_one.should_not == subject_two
256
349
  end
257
350
 
351
+ it "should allow decorated access by index" do
352
+ subject = ProductDecorator.decorate(paged_array)
353
+ subject[0].should be_instance_of ProductDecorator
354
+ end
355
+
356
+ context "pretends to be of kind of wrapped collection class" do
357
+ subject { ProductDecorator.decorate(paged_array) }
358
+
359
+ it "#kind_of? DecoratedEnumerableProxy" do
360
+ subject.should be_kind_of Draper::DecoratedEnumerableProxy
361
+ end
362
+
363
+ it "#is_a? DecoratedEnumerableProxy" do
364
+ subject.is_a?(Draper::DecoratedEnumerableProxy).should be_true
365
+ end
366
+
367
+ it "#kind_of? Array" do
368
+ subject.should be_kind_of Array
369
+ end
370
+
371
+ it "#is_a? Array" do
372
+ subject.is_a?(Array).should be_true
373
+ end
374
+ end
375
+
258
376
  context '#all' do
259
377
  it "should return a decorated collection" do
260
378
  ProductDecorator.all.first.should be_instance_of ProductDecorator
261
379
  end
262
-
380
+
263
381
  it "should accept a context" do
264
- collection = ProductDecorator.all(:admin)
382
+ collection = ProductDecorator.all(:context => :admin)
265
383
  collection.first.context.should == :admin
266
384
  end
267
385
  end
@@ -362,4 +480,41 @@ describe Draper::Base do
362
480
  decorator.sample_truncate.should == "Once..."
363
481
  end
364
482
  end
483
+
484
+ describe "#method_missing" do
485
+ context "when #hello_world is called for the first time" do
486
+ it "hits method missing" do
487
+ subject.should_receive(:method_missing)
488
+ subject.hello_world
489
+ end
490
+ end
491
+
492
+ context "when #hello_world is called again" do
493
+ before { subject.hello_world }
494
+ it "proxies method directly after first hit" do
495
+ subject.should_not_receive(:method_missing)
496
+ subject.hello_world
497
+ end
498
+ end
499
+ end
500
+
501
+ describe "#kind_of?" do
502
+ context "pretends to be of kind of model class" do
503
+ it "#kind_of? decorator class" do
504
+ subject.should be_kind_of subject.class
505
+ end
506
+
507
+ it "#is_a? decorator class" do
508
+ subject.is_a?(subject.class).should be_true
509
+ end
510
+
511
+ it "#kind_of? source class" do
512
+ subject.should be_kind_of source.class
513
+ end
514
+
515
+ it "#is_a? source class" do
516
+ subject.is_a?(source.class).should be_true
517
+ end
518
+ end
519
+ end
365
520
  end
@@ -26,4 +26,14 @@ describe Draper::ModelSupport do
26
26
  subject.decorate.to_ary[0].model.should be_a(Product)
27
27
  end
28
28
  end
29
+
30
+ describe '#decorate - decorate collections of namespaced AR objects' do
31
+ subject { Namespace::Product.limit }
32
+ its(:decorate) { should be_kind_of(Draper::DecoratedEnumerableProxy) }
33
+
34
+ it "should decorate the collection" do
35
+ subject.decorate.size.should == 1
36
+ subject.decorate.to_ary[0].model.should be_a(Namespace::Product)
37
+ end
38
+ end
29
39
  end
@@ -12,12 +12,6 @@ describe Draper::DecoratorGenerator do
12
12
  describe 'no arguments' do
13
13
  before { run_generator %w(products) }
14
14
 
15
- describe 'app/decorators/application_decorator.rb' do
16
- subject { file('app/decorators/application_decorator.rb') }
17
- it { should exist }
18
- it { should contain "class ApplicationDecorator < Draper::Base" }
19
- end
20
-
21
15
  describe 'app/decorators/products_decorator.rb' do
22
16
  subject { file('app/decorators/products_decorator.rb') }
23
17
  it { should exist }
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ # Generators are not automatically loaded by Rails
4
+ require 'generators/draper/install/install_generator'
5
+
6
+ describe Draper::InstallGenerator do
7
+ # Tell the generator where to put its output (what it thinks of as Rails.root)
8
+ destination File.expand_path("../../../../../tmp", __FILE__)
9
+
10
+ before { prepare_destination }
11
+
12
+ context 'using rspec' do
13
+ before { run_generator }
14
+
15
+ shared_examples_for "ApplicationDecoratorGenerator" do
16
+ describe 'app/decorators/application_decorator.rb' do
17
+ subject { file('app/decorators/application_decorator.rb') }
18
+ it { should exist }
19
+ it { should contain "class ApplicationDecorator < Draper::Base" }
20
+ end
21
+ end
22
+
23
+ describe 'spec/decorators/application_decorator_spec.rb' do
24
+ subject { file('spec/decorators/application_decorator_spec.rb') }
25
+ it { should exist }
26
+ it { should contain "describe ApplicationDecorator do" }
27
+ end
28
+ end
29
+
30
+ context "using test_unit" do
31
+ before { run_generator ["", "-t=test_unit"] }
32
+
33
+ it_should_behave_like "ApplicationDecoratorGenerator"
34
+
35
+ describe 'spec/decorators/application_decorator_spec.rb' do
36
+ subject { file('spec/decorators/application_decorator_spec.rb') }
37
+ it { should_not exist }
38
+ end
39
+
40
+ describe 'spec/decorators/application_decorator_test.rb' do
41
+ subject { file('test/decorators/application_decorator_test.rb') }
42
+ it { should exist }
43
+ end
44
+ end
45
+
46
+ end
@@ -12,12 +12,6 @@ describe Rspec::DecoratorGenerator do
12
12
  describe 'no arguments' do
13
13
  before { run_generator %w(products) }
14
14
 
15
- describe 'spec/decorators/application_decorator_spec.rb' do
16
- subject { file('spec/decorators/application_decorator_spec.rb') }
17
- it { should exist }
18
- it { should contain "describe ApplicationDecorator do" }
19
- end
20
-
21
15
  describe 'spec/decorators/products_decorator_spec.rb' do
22
16
  subject { file('spec/decorators/products_decorator_spec.rb') }
23
17
  it { should exist }
@@ -12,12 +12,6 @@ describe TestUnit::DecoratorGenerator do
12
12
  describe 'no arguments' do
13
13
  before { run_generator %w(products) }
14
14
 
15
- describe 'test/decorators/application_decorator_test.rb' do
16
- subject { file('test/decorators/application_decorator_test.rb') }
17
- it { should exist }
18
- it { should contain "class ApplicationDecoratorTest < ActiveSupport::TestCase" }
19
- end
20
-
21
15
  describe 'test/decorators/products_decorator_test.rb' do
22
16
  subject { file('test/decorators/products_decorator_test.rb') }
23
17
  it { should exist }
data/spec/spec_helper.rb CHANGED
@@ -2,4 +2,17 @@ require 'rubygems'
2
2
  require 'bundler/setup'
3
3
  Bundler.require
4
4
 
5
- Dir['./spec/support/**/*.rb'].each {|file| require file }
5
+ require './spec/support/samples/active_record.rb'
6
+ require './spec/support/samples/application_controller.rb'
7
+ require './spec/support/samples/application_helper.rb'
8
+ require './spec/support/samples/decorator.rb'
9
+ require './spec/support/samples/decorator_with_allows.rb'
10
+ require './spec/support/samples/decorator_with_application_helper.rb'
11
+ require './spec/support/samples/decorator_with_denies.rb'
12
+ require './spec/support/samples/namespaced_product.rb'
13
+ require './spec/support/samples/namespaced_product_decorator.rb'
14
+ require './spec/support/samples/product.rb'
15
+ require './spec/support/samples/product_decorator.rb'
16
+ require './spec/support/samples/specific_product_decorator.rb'
17
+ require './spec/support/samples/widget.rb'
18
+ require './spec/support/samples/widget_decorator.rb'
@@ -0,0 +1,51 @@
1
+ require './spec/support/samples/product'
2
+
3
+ module Namespace
4
+ class Product < ActiveRecord::Base
5
+ include Draper::ModelSupport
6
+
7
+ def self.first
8
+ @@first ||= Namespace::Product.new
9
+ end
10
+
11
+ def self.last
12
+ @@last ||= Namespace::Product.new
13
+ end
14
+
15
+ def self.all
16
+ [Namespace::Product.new, Namespace::Product.new]
17
+ end
18
+
19
+ def self.scoped
20
+ [Namespace::Product.new]
21
+ end
22
+
23
+ def self.model_name
24
+ "Namespace::Product"
25
+ end
26
+
27
+ def self.find(id)
28
+ return Namespace::Product.new
29
+ end
30
+
31
+ def self.sample_class_method
32
+ "sample class method"
33
+ end
34
+
35
+ def hello_world
36
+ "Hello, World"
37
+ end
38
+
39
+ def goodnight_moon
40
+ "Goodnight, Moon"
41
+ end
42
+
43
+ def title
44
+ "Sample Title"
45
+ end
46
+
47
+ def block
48
+ yield
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ require './spec/support/samples/namespaced_product'
2
+
3
+ module Namespace
4
+ class ProductDecorator < Draper::Base
5
+ decorates :product, :class => Namespace::Product
6
+ end
7
+ end
@@ -44,4 +44,16 @@ class Product < ActiveRecord::Base
44
44
  def block
45
45
  yield
46
46
  end
47
+
48
+ def self.reflect_on_association(association_symbol)
49
+ OpenStruct.new(:klass => self)
50
+ end
51
+
52
+ def similar_products
53
+ [Product.new, Product.new]
54
+ end
55
+
56
+ def previous_version
57
+ Product.new
58
+ end
47
59
  end
@@ -0,0 +1,4 @@
1
+ require './spec/support/samples/product_decorator'
2
+
3
+ class SpecificProductDecorator < ProductDecorator
4
+ end
@@ -0,0 +1,2 @@
1
+ class Widget < Product
2
+ end
@@ -0,0 +1,2 @@
1
+ class WidgetDecorator < ProductDecorator
2
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.10.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-29 00:00:00.000000000Z
12
+ date: 2012-01-16 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70218782393040 !ruby/object:Gem::Requirement
16
+ requirement: &70130732077940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 2.3.10
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70218782393040
24
+ version_requirements: *70130732077940
25
25
  description: Draper implements a decorator or presenter pattern for Rails applications.
26
26
  email:
27
27
  - jeff@casimircreative.com
@@ -65,25 +65,32 @@ files:
65
65
  - lib/draper/helper_support.rb
66
66
  - lib/draper/lazy_helpers.rb
67
67
  - lib/draper/model_support.rb
68
+ - lib/draper/rspec_integration.rb
68
69
  - lib/draper/system.rb
69
70
  - lib/draper/version.rb
70
71
  - lib/draper/view_context.rb
71
72
  - lib/generators/draper/decorator/USAGE
72
73
  - lib/generators/draper/decorator/decorator_generator.rb
73
- - lib/generators/draper/decorator/templates/application_decorator.rb
74
74
  - lib/generators/draper/decorator/templates/decorator.rb
75
+ - lib/generators/draper/install/install_generator.rb
76
+ - lib/generators/draper/install/templates/application_decorator.rb
77
+ - lib/generators/draper/install/templates/application_decorator_spec.rb
78
+ - lib/generators/draper/install/templates/application_decorator_test.rb
75
79
  - lib/generators/rails/decorator_generator.rb
76
80
  - lib/generators/rspec/decorator_generator.rb
77
- - lib/generators/rspec/templates/application_decorator_spec.rb
78
81
  - lib/generators/rspec/templates/decorator_spec.rb
79
82
  - lib/generators/test_unit/decorator_generator.rb
80
- - lib/generators/test_unit/templates/application_decorator_test.rb
81
83
  - lib/generators/test_unit/templates/decorator_test.rb
84
+ - performance/active_record.rb
85
+ - performance/bechmark.rb
86
+ - performance/decorators.rb
87
+ - performance/models.rb
82
88
  - spec/draper/base_spec.rb
83
89
  - spec/draper/helper_support_spec.rb
84
90
  - spec/draper/model_support_spec.rb
85
91
  - spec/draper/view_context_spec.rb
86
92
  - spec/generators/draper/decorator/decorator_generator_spec.rb
93
+ - spec/generators/draper/install/install_generator_spec.rb
87
94
  - spec/generators/rspec/decorator_generator_spec.rb
88
95
  - spec/generators/test_unit/decorator_generator_spec.rb
89
96
  - spec/spec_helper.rb
@@ -94,8 +101,13 @@ files:
94
101
  - spec/support/samples/decorator_with_allows.rb
95
102
  - spec/support/samples/decorator_with_application_helper.rb
96
103
  - spec/support/samples/decorator_with_denies.rb
104
+ - spec/support/samples/namespaced_product.rb
105
+ - spec/support/samples/namespaced_product_decorator.rb
97
106
  - spec/support/samples/product.rb
98
107
  - spec/support/samples/product_decorator.rb
108
+ - spec/support/samples/specific_product_decorator.rb
109
+ - spec/support/samples/widget.rb
110
+ - spec/support/samples/widget_decorator.rb
99
111
  homepage: http://github.com/jcasimir/draper
100
112
  licenses: []
101
113
  post_install_message:
@@ -126,6 +138,7 @@ test_files:
126
138
  - spec/draper/model_support_spec.rb
127
139
  - spec/draper/view_context_spec.rb
128
140
  - spec/generators/draper/decorator/decorator_generator_spec.rb
141
+ - spec/generators/draper/install/install_generator_spec.rb
129
142
  - spec/generators/rspec/decorator_generator_spec.rb
130
143
  - spec/generators/test_unit/decorator_generator_spec.rb
131
144
  - spec/spec_helper.rb
@@ -136,5 +149,11 @@ test_files:
136
149
  - spec/support/samples/decorator_with_allows.rb
137
150
  - spec/support/samples/decorator_with_application_helper.rb
138
151
  - spec/support/samples/decorator_with_denies.rb
152
+ - spec/support/samples/namespaced_product.rb
153
+ - spec/support/samples/namespaced_product_decorator.rb
139
154
  - spec/support/samples/product.rb
140
155
  - spec/support/samples/product_decorator.rb
156
+ - spec/support/samples/specific_product_decorator.rb
157
+ - spec/support/samples/widget.rb
158
+ - spec/support/samples/widget_decorator.rb
159
+ has_rdoc:
@@ -1,5 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ApplicationDecorator do
4
- before { ApplicationController.new.set_current_view_context }
5
- end