draper 0.9.5 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
![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:
|
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:
|