draper 0.9.5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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