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.
- data/Gemfile +2 -2
- data/Readme.markdown +19 -9
- data/lib/draper.rb +2 -1
- data/lib/draper/base.rb +70 -30
- data/lib/draper/decorated_enumerable_proxy.rb +13 -4
- data/lib/draper/model_support.rb +5 -5
- data/lib/draper/rspec_integration.rb +22 -0
- data/lib/draper/version.rb +1 -1
- data/lib/generators/draper/decorator/decorator_generator.rb +9 -7
- data/lib/generators/draper/decorator/templates/decorator.rb +5 -5
- data/lib/generators/draper/install/install_generator.rb +39 -0
- data/lib/generators/draper/{decorator → install}/templates/application_decorator.rb +6 -6
- data/lib/generators/draper/install/templates/application_decorator_spec.rb +4 -0
- data/lib/generators/{test_unit → draper/install}/templates/application_decorator_test.rb +0 -0
- data/lib/generators/rspec/decorator_generator.rb +0 -6
- data/lib/generators/test_unit/decorator_generator.rb +1 -7
- data/performance/active_record.rb +4 -0
- data/performance/bechmark.rb +55 -0
- data/performance/decorators.rb +47 -0
- data/performance/models.rb +20 -0
- data/spec/draper/base_spec.rb +162 -7
- data/spec/draper/model_support_spec.rb +10 -0
- data/spec/generators/draper/decorator/decorator_generator_spec.rb +0 -6
- data/spec/generators/draper/install/install_generator_spec.rb +46 -0
- data/spec/generators/rspec/decorator_generator_spec.rb +0 -6
- data/spec/generators/test_unit/decorator_generator_spec.rb +0 -6
- data/spec/spec_helper.rb +14 -1
- data/spec/support/samples/namespaced_product.rb +51 -0
- data/spec/support/samples/namespaced_product_decorator.rb +7 -0
- data/spec/support/samples/product.rb +12 -0
- data/spec/support/samples/specific_product_decorator.rb +4 -0
- data/spec/support/samples/widget.rb +2 -0
- data/spec/support/samples/widget_decorator.rb +2 -0
- metadata +26 -7
- 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.
|
6
|
-
gem 'actionpack', "~> 3.
|
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
|
-

|
3
|
+
[](http://travis-ci.org/jcasimir/draper)
|
4
|
+
[](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:
|
9
|
-
3.
|
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
|
-
|
13
|
-
|
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
|
-
|
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
|
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 [
|
22
|
-
def initialize(input,
|
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,
|
35
|
-
self.new(model_class.find(input),
|
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 [
|
95
|
-
def self.decorate(input,
|
96
|
-
input.respond_to?(:each)
|
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 [
|
133
|
+
# @param [Hash] options (optional)
|
102
134
|
# @return [Draper::DecoratedEnumerableProxy]
|
103
|
-
def self.all(
|
104
|
-
Draper::DecoratedEnumerableProxy.new(model_class.all, self,
|
135
|
+
def self.all(options = {})
|
136
|
+
Draper::DecoratedEnumerableProxy.new(model_class.all, self, options)
|
105
137
|
end
|
106
|
-
|
107
|
-
def self.first(
|
108
|
-
decorate(model_class.first,
|
138
|
+
|
139
|
+
def self.first(options = {})
|
140
|
+
decorate(model_class.first, options)
|
109
141
|
end
|
110
142
|
|
111
|
-
def self.last(
|
112
|
-
decorate(model_class.last,
|
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
|
-
|
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,
|
6
|
-
@wrapped_collection, @klass, @
|
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.
|
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.
|
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
|
data/lib/draper/model_support.rb
CHANGED
@@ -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(
|
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
|
+
|
data/lib/draper/version.rb
CHANGED
@@ -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
|
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
|
11
|
-
#
|
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
|
File without changes
|
@@ -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
|
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,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
|
data/spec/draper/base_spec.rb
CHANGED
@@ -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
|
-
|
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
|
@@ -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
|
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.
|
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:
|
12
|
+
date: 2012-01-16 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
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: *
|
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:
|