draper 0.12.1 → 0.17.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/.gitignore +5 -1
- data/.rspec +1 -0
- data/.travis.yml +3 -5
- data/CHANGELOG.markdown +51 -0
- data/Readme.markdown +32 -17
- data/draper.gemspec +6 -9
- data/lib/draper/active_model_support.rb +16 -13
- data/lib/draper/base.rb +54 -11
- data/lib/draper/decorated_enumerable_proxy.rb +13 -6
- data/lib/draper/railtie.rb +16 -15
- data/lib/draper/rspec_integration.rb +2 -33
- data/lib/draper/system.rb +3 -14
- data/lib/draper/test/minitest_integration.rb +26 -0
- data/lib/draper/test/rspec_integration.rb +28 -0
- data/lib/draper/test/view_context.rb +12 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +5 -9
- data/lib/draper.rb +3 -1
- data/lib/generators/decorator/templates/decorator.rb +1 -1
- data/lib/generators/test_unit/templates/decorator_test.rb +1 -1
- data/spec/draper/base_spec.rb +206 -88
- data/spec/draper/model_support_spec.rb +5 -5
- data/spec/draper/view_context_spec.rb +18 -7
- data/spec/generators/decorator/decorator_generator_spec.rb +4 -0
- data/spec/spec_helper.rb +12 -2
- data/spec/support/samples/application_controller.rb +10 -11
- data/spec/support/samples/application_helper.rb +3 -0
- data/spec/support/samples/decorator_with_allows.rb +0 -0
- data/spec/support/samples/decorator_with_application_helper.rb +4 -0
- data/spec/support/samples/decorator_with_denies_all.rb +3 -0
- data/spec/support/samples/decorator_with_special_methods.rb +13 -0
- data/spec/support/samples/sequel_product.rb +13 -0
- metadata +103 -159
- data/CHANGELOG.txt +0 -23
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Draper Changelog
|
|
2
|
+
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
* [Automatically prime `view_context`](https://github.com/jcasimir/draper/commit/057ab4e8)
|
|
6
|
+
* [Fixed bug where rspec eq matchers didn't work]((https://github.com/jcasimir/draper/commit/57617b)
|
|
7
|
+
* [Sequel ORM support](https://github.com/jcasimir/draper/commit/7d4942)
|
|
8
|
+
* Fixed issues with newer minitest
|
|
9
|
+
* [Changed the way the `view_context` gets set](https://github.com/jcasimir/draper/commit/0b03d9c)
|
|
10
|
+
|
|
11
|
+
## 0.15.0
|
|
12
|
+
|
|
13
|
+
* Proper minitest integration
|
|
14
|
+
* [We can properly decorate scoped associations](https://github.com/jcasimir/draper/issues/223)
|
|
15
|
+
* [Fixed awkward eager loading](https://github.com/jcasimir/draper/commit/7dc3510b)
|
|
16
|
+
|
|
17
|
+
## 0.14.0
|
|
18
|
+
|
|
19
|
+
* [Properly prime the view context in Rails Console](https://github.com/jcasimir/draper/commit/738074f)
|
|
20
|
+
* Make more gems development requirements only
|
|
21
|
+
|
|
22
|
+
## 0.13.0
|
|
23
|
+
|
|
24
|
+
* Upgraded all dependencies
|
|
25
|
+
* Dropped support for Rubies < 1.9.3
|
|
26
|
+
* `#to_model` has been renamed to `#wrapped_object`
|
|
27
|
+
* Allow proper overriding of special `ActiveModel` methods
|
|
28
|
+
|
|
29
|
+
## 0.12.3
|
|
30
|
+
|
|
31
|
+
* [Fix i18n issue](https://github.com/jcasimir/draper/issues/202)
|
|
32
|
+
|
|
33
|
+
## 0.12.2
|
|
34
|
+
|
|
35
|
+
* Fix bug with initializing ammeter
|
|
36
|
+
* Some gems are now development only in the gemspec
|
|
37
|
+
* Fix bug where generated models were still inheriting from `ApplicationDecorator`
|
|
38
|
+
|
|
39
|
+
## 0.12.0
|
|
40
|
+
|
|
41
|
+
* Added Changelog
|
|
42
|
+
* [Prevented double decoration](https://github.com/jcasimir/draper/issues/173)
|
|
43
|
+
* [`ActiveModel::Errors` support](https://github.com/jcasimir/draper/commit/19496f0c)
|
|
44
|
+
* [Fixed autoloading issue](https://github.com/jcasimir/draper/issues/188)
|
|
45
|
+
* [Re-did generators](https://github.com/jcasimir/draper/commit/9155e58f)
|
|
46
|
+
* [Added capybara integration](https://github.com/jcasimir/draper/commit/57c8678e)
|
|
47
|
+
* Fixed a few bugs with the `DecoratedEnumerableProxy`
|
|
48
|
+
|
|
49
|
+
## 0.11.1
|
|
50
|
+
|
|
51
|
+
* [Fixed regression, we don't want to introduce a hard dependency on Rails](https://github.com/jcasimir/draper/issues/107)
|
data/Readme.markdown
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Draper: View Models for Rails
|
|
2
2
|
|
|
3
3
|
[](http://travis-ci.org/jcasimir/draper)
|
|
4
|
-
[](https://gemnasium.com/jcasimir/draper)
|
|
5
4
|
|
|
6
5
|
## Quick Start
|
|
7
6
|
|
|
@@ -39,7 +38,7 @@ Ryan Bates has put together an excellent RailsCast on Draper based on the 0.8.0
|
|
|
39
38
|
|
|
40
39
|
## What's New
|
|
41
40
|
|
|
42
|
-
Check out the full commit history at
|
|
41
|
+
Check out the full commit history at http://github.com/jcasimir/draper/commits
|
|
43
42
|
|
|
44
43
|
In summary, you can now:
|
|
45
44
|
|
|
@@ -66,8 +65,9 @@ Why hate normal helpers? In Ruby/Rails we approach everything from an Object-Ori
|
|
|
66
65
|
A decorator wraps an object with presentation-related accessor methods. For instance, if you had an `Article` object, then the decorator could override `.published_at` to use formatted output like this:
|
|
67
66
|
|
|
68
67
|
```ruby
|
|
69
|
-
class ArticleDecorator <
|
|
68
|
+
class ArticleDecorator < Draper::Base
|
|
70
69
|
decorates :article
|
|
70
|
+
|
|
71
71
|
def published_at
|
|
72
72
|
date = h.content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
|
73
73
|
time = h.content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
|
@@ -89,6 +89,7 @@ When you use a decorator you have the power of a Ruby object but it's a part of
|
|
|
89
89
|
```ruby
|
|
90
90
|
class ArticleDecorator < ApplicationDecorator
|
|
91
91
|
decorates :article
|
|
92
|
+
|
|
92
93
|
ADMIN_VISIBLE_ATTRIBUTES = [:title, :body, :author, :status]
|
|
93
94
|
PUBLIC_VISIBLE_ATTRIBUTES = [:title, :body]
|
|
94
95
|
|
|
@@ -232,7 +233,7 @@ ArticleDecorator.decorate(Article.all) # Returns an enumeration proxy of Artic
|
|
|
232
233
|
```ruby
|
|
233
234
|
ArticleDecorator.find(1)
|
|
234
235
|
```
|
|
235
|
-
|
|
236
|
+
|
|
236
237
|
### In Your Views
|
|
237
238
|
|
|
238
239
|
Use the new methods in your views like any other model method (ex: `@article.published_at`):
|
|
@@ -241,25 +242,15 @@ Use the new methods in your views like any other model method (ex: `@article.pub
|
|
|
241
242
|
<h1><%= @article.title %> <%= @article.published_at %></h1>
|
|
242
243
|
```
|
|
243
244
|
|
|
244
|
-
### Using in Mailers
|
|
245
|
-
|
|
246
|
-
To use decorators in mailers that use helpers, you have to call `set_current_view_context` in your
|
|
247
|
-
ActionMailer class.
|
|
248
|
-
|
|
249
|
-
```ruby
|
|
250
|
-
class ArticleMailer < ActionMailer::Base
|
|
251
|
-
default 'init-draper' => Proc.new { set_current_view_context }
|
|
252
|
-
end
|
|
253
|
-
```
|
|
254
245
|
### Integration with RSpec
|
|
255
246
|
|
|
256
247
|
Using the provided generator, Draper will place specs for your new decorator in `spec/decorators/`.
|
|
257
248
|
|
|
258
|
-
By default, specs in `spec/decorators` will be tagged as `type => :decorator`. Any spec tagged as `decorator` will
|
|
249
|
+
By default, specs in `spec/decorators` will be tagged as `type => :decorator`. Any spec tagged as `decorator` will make helpers available to the decorator.
|
|
259
250
|
|
|
260
251
|
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.
|
|
261
252
|
|
|
262
|
-
Note: If you're using Spork, you need to `require 'draper/rspec_integration'` in your Spork.prefork block.
|
|
253
|
+
Note: If you're using Spork, you need to `require 'draper/test/rspec_integration'` in your Spork.prefork block.
|
|
263
254
|
|
|
264
255
|
## Possible Decoration Methods
|
|
265
256
|
|
|
@@ -289,7 +280,7 @@ First, follow the steps above to add the dependency and update your bundle.
|
|
|
289
280
|
Since we're talking about the `Article` model we'll create an `ArticleDecorator` class. You could do it by hand, but use the provided generator:
|
|
290
281
|
|
|
291
282
|
```
|
|
292
|
-
rails generate
|
|
283
|
+
rails generate decorator Article
|
|
293
284
|
```
|
|
294
285
|
|
|
295
286
|
Now open up the created `app/decorators/article_decorator.rb` and you'll find an `ArticleDecorator` class. Add this method:
|
|
@@ -357,6 +348,30 @@ Now when you call the association it will use a decorator.
|
|
|
357
348
|
<%= @article.author.fancy_name %>
|
|
358
349
|
```
|
|
359
350
|
|
|
351
|
+
### A note on Rails configuration
|
|
352
|
+
|
|
353
|
+
Draper loads your application's decorators when Rails start. This may lead to an issue with configuring I18n, whereby the settings you provide in `./config/application.rb` are ignored. This happens when you use the `I18n` constant at the class-level in one of the models that have a decorator, as in the following example:
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
# app/models/user.rb
|
|
357
|
+
class User < ActiveRecord::Base
|
|
358
|
+
validates :email, presence: { message: I18n.t('invalid_email') }
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# app/decorators/user_decorator.rb
|
|
362
|
+
class UserDecorator < ApplicationDecorator
|
|
363
|
+
decorates :user
|
|
364
|
+
end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Note how the `validates` line is executed when the `User` class is loaded, triggering the initialization of the I18n framework _before_ Rails had a chance to apply its configuration.
|
|
368
|
+
|
|
369
|
+
Using `I18n` directly in your model definition **is an antipattern**. The preferred solution would be to not override the `message` option in your `validates` macro, but provide the `activerecord.errors.models.attributes.user.email.presence` key in your translation files.
|
|
370
|
+
|
|
371
|
+
## Extension gems
|
|
372
|
+
|
|
373
|
+
For automatic decoration, check out [decorates_before_rendering](https://github.com/ohwillie/decorates_before_rendering).
|
|
374
|
+
|
|
360
375
|
## Contributing
|
|
361
376
|
|
|
362
377
|
1. Fork it.
|
data/draper.gemspec
CHANGED
|
@@ -16,15 +16,12 @@ Gem::Specification.new do |s|
|
|
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
17
17
|
s.require_paths = ["lib"]
|
|
18
18
|
|
|
19
|
-
s.add_dependency 'activesupport', '
|
|
20
|
-
s.add_dependency '
|
|
21
|
-
s.add_dependency 'rspec', '~> 2.0'
|
|
22
|
-
s.add_dependency 'activesupport', '~> 3.1.3'
|
|
23
|
-
s.add_dependency 'actionpack', "~> 3.1.3"
|
|
19
|
+
s.add_dependency 'activesupport', '~> 3.2'
|
|
20
|
+
s.add_dependency 'actionpack', '~> 3.2'
|
|
24
21
|
|
|
25
|
-
s.add_development_dependency 'ammeter', '
|
|
26
|
-
s.add_development_dependency '
|
|
27
|
-
s.add_development_dependency '
|
|
28
|
-
s.add_development_dependency 'launchy'
|
|
22
|
+
s.add_development_dependency 'ammeter', '0.2.2'
|
|
23
|
+
s.add_development_dependency 'rake', '~> 0.9.2'
|
|
24
|
+
s.add_development_dependency 'rspec', '~> 2.10'
|
|
29
25
|
s.add_development_dependency 'yard'
|
|
26
|
+
s.add_development_dependency 'minitest', '~> 3.0' if RUBY_PLATFORM == "java"
|
|
30
27
|
end
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
module Draper::ActiveModelSupport
|
|
2
2
|
module Proxies
|
|
3
|
-
def
|
|
3
|
+
def self.extended(base)
|
|
4
4
|
# These methods (as keys) will be created only if the correspondent
|
|
5
|
-
# model
|
|
6
|
-
proxies =
|
|
7
|
-
proxies[:to_param] = ActiveModel::Conversion if defined?(ActiveModel::Conversion)
|
|
8
|
-
proxies[:errors] = ActiveModel::Validations if defined?(ActiveModel::Validations)
|
|
9
|
-
proxies[:id] = ActiveRecord::Base if defined?(ActiveRecord::Base)
|
|
5
|
+
# model responds to the method
|
|
6
|
+
proxies = [:to_param, :errors, :id]
|
|
10
7
|
|
|
11
|
-
proxies.each do |method_name
|
|
12
|
-
if model.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
proxies.each do |method_name|
|
|
9
|
+
if base.model.respond_to?(method_name)
|
|
10
|
+
base.singleton_class.class_eval do
|
|
11
|
+
if !base.class.instance_methods.include?(method_name) || base.class.instance_method(method_name).owner === Draper::Base
|
|
12
|
+
define_method(method_name) do |*args, &block|
|
|
13
|
+
model.send(method_name, *args, &block)
|
|
14
|
+
end
|
|
18
15
|
end
|
|
19
16
|
end
|
|
20
17
|
end
|
|
21
18
|
end
|
|
19
|
+
|
|
20
|
+
base.class_eval do
|
|
21
|
+
def to_model
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
end
|
|
22
25
|
end
|
|
23
26
|
end
|
|
24
27
|
end
|
data/lib/draper/base.rb
CHANGED
|
@@ -11,8 +11,6 @@ module Draper
|
|
|
11
11
|
self.denied = DEFAULT_DENIED
|
|
12
12
|
self.allowed = DEFAULT_ALLOWED
|
|
13
13
|
|
|
14
|
-
include Draper::ActiveModelSupport::Proxies
|
|
15
|
-
|
|
16
14
|
# Initialize a new decorator instance by passing in
|
|
17
15
|
# an instance of the source class. Pass in an optional
|
|
18
16
|
# context inside the options hash is stored for later use.
|
|
@@ -24,7 +22,7 @@ module Draper
|
|
|
24
22
|
self.class.model_class = input.class if model_class.nil?
|
|
25
23
|
@model = input.kind_of?(Draper::Base) ? input.model : input
|
|
26
24
|
self.options = options
|
|
27
|
-
|
|
25
|
+
self.extend Draper::ActiveModelSupport::Proxies
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
# Proxies to the class specified by `decorates` to automatically
|
|
@@ -59,12 +57,16 @@ module Draper
|
|
|
59
57
|
# the assocation to be decorated when it is retrieved.
|
|
60
58
|
#
|
|
61
59
|
# @param [Symbol] name of association to decorate, like `:products`
|
|
62
|
-
# @option options [
|
|
60
|
+
# @option options [Hash] :with The decorator to decorate the association with
|
|
61
|
+
# :scope The scope to apply to the association
|
|
63
62
|
def self.decorates_association(association_symbol, options = {})
|
|
64
63
|
define_method(association_symbol) do
|
|
65
64
|
orig_association = model.send(association_symbol)
|
|
66
65
|
|
|
67
66
|
return orig_association if orig_association.nil?
|
|
67
|
+
return decorated_associations[association_symbol] if decorated_associations[association_symbol]
|
|
68
|
+
|
|
69
|
+
orig_association = orig_association.send(options[:scope]) if options[:scope]
|
|
68
70
|
|
|
69
71
|
return options[:with].decorate(orig_association) if options[:with]
|
|
70
72
|
|
|
@@ -78,7 +80,7 @@ module Draper
|
|
|
78
80
|
orig_association.class
|
|
79
81
|
end
|
|
80
82
|
|
|
81
|
-
"#{klass}Decorator".constantize.decorate(orig_association, options)
|
|
83
|
+
decorated_associations[association_symbol] = "#{klass}Decorator".constantize.decorate(orig_association, options)
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
86
|
|
|
@@ -87,7 +89,8 @@ module Draper
|
|
|
87
89
|
#
|
|
88
90
|
# @param [Symbols*] name of associations to decorate
|
|
89
91
|
def self.decorates_associations(*association_symbols)
|
|
90
|
-
association_symbols.
|
|
92
|
+
options = association_symbols.extract_options!
|
|
93
|
+
association_symbols.each{ |sym| decorates_association(sym, options) }
|
|
91
94
|
end
|
|
92
95
|
|
|
93
96
|
# Specifies a black list of methods which may *not* be proxied to
|
|
@@ -103,6 +106,16 @@ module Draper
|
|
|
103
106
|
self.denied += input_denied
|
|
104
107
|
end
|
|
105
108
|
|
|
109
|
+
# Specifies that all methods may *not* be proxied to
|
|
110
|
+
# to the wrapped object.
|
|
111
|
+
#
|
|
112
|
+
# Do not use `.allows` and `.denies` in combination with '.denies_all'
|
|
113
|
+
def self.denies_all
|
|
114
|
+
raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless ((self.allowed == DEFAULT_ALLOWED && self.denied == DEFAULT_DENIED) || (self.allowed != DEFAULT_ALLOWED && self.denied != DEFAULT_DENIED))
|
|
115
|
+
self.denied += [nil] # Add dummy value to denied to prevent calls to #allows. Hacky???
|
|
116
|
+
self.allowed += [nil] # Add dummy value to allowed to prevent calls to #denies
|
|
117
|
+
end
|
|
118
|
+
|
|
106
119
|
# Specifies a white list of methods which *may* be proxied to
|
|
107
120
|
# to the wrapped object. When `allows` is used, only the listed
|
|
108
121
|
# methods and methods defined in the decorator itself will be
|
|
@@ -135,7 +148,7 @@ module Draper
|
|
|
135
148
|
if input.instance_of?(self)
|
|
136
149
|
input.options = options unless options.empty?
|
|
137
150
|
return input
|
|
138
|
-
elsif input.respond_to?(:each)
|
|
151
|
+
elsif input.respond_to?(:each) && (!defined?(Sequel) || !input.is_a?(Sequel::Model))
|
|
139
152
|
Draper::DecoratedEnumerableProxy.new(input, self, options)
|
|
140
153
|
elsif options[:infer]
|
|
141
154
|
input.decorator(options)
|
|
@@ -160,12 +173,30 @@ module Draper
|
|
|
160
173
|
decorate(model_class.last, options)
|
|
161
174
|
end
|
|
162
175
|
|
|
176
|
+
# Some helpers are private, for example html_escape... as a workaround
|
|
177
|
+
# we are wrapping the helpers in a delegator that passes the methods
|
|
178
|
+
# along through a send, which will ignore private/public distinctions
|
|
179
|
+
class HelpersWrapper
|
|
180
|
+
def initialize(helpers)
|
|
181
|
+
@helpers = helpers
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def method_missing(method, *args, &block)
|
|
185
|
+
@helpers.send(method, *args, &block)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
#needed for tests
|
|
189
|
+
def ==(other)
|
|
190
|
+
other.instance_variable_get(:@helpers) == @helpers
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
163
194
|
# Access the helpers proxy to call built-in and user-defined
|
|
164
195
|
# Rails helpers. Aliased to `.h` for convenience.
|
|
165
196
|
#
|
|
166
197
|
# @return [Object] proxy
|
|
167
198
|
def helpers
|
|
168
|
-
self.class.helpers
|
|
199
|
+
HelpersWrapper.new self.class.helpers
|
|
169
200
|
end
|
|
170
201
|
alias :h :helpers
|
|
171
202
|
|
|
@@ -191,7 +222,7 @@ module Draper
|
|
|
191
222
|
# Fetch the original wrapped model.
|
|
192
223
|
#
|
|
193
224
|
# @return [Object] original_model
|
|
194
|
-
def
|
|
225
|
+
def wrapped_object
|
|
195
226
|
@model
|
|
196
227
|
end
|
|
197
228
|
|
|
@@ -211,12 +242,20 @@ module Draper
|
|
|
211
242
|
super || (allow?(method) && model.respond_to?(method, include_private))
|
|
212
243
|
end
|
|
213
244
|
|
|
245
|
+
# We always want to delegate present, in case we decorate a nil object.
|
|
246
|
+
#
|
|
247
|
+
# I don't like the idea of decorating a nil object, but we'll deal with
|
|
248
|
+
# that later.
|
|
249
|
+
def present?
|
|
250
|
+
model.present?
|
|
251
|
+
end
|
|
252
|
+
|
|
214
253
|
def method_missing(method, *args, &block)
|
|
215
254
|
super unless allow?(method)
|
|
216
255
|
|
|
217
256
|
if model.respond_to?(method)
|
|
218
|
-
self.class.send :define_method, method do |*args, &
|
|
219
|
-
model.send method, *args, &
|
|
257
|
+
self.class.send :define_method, method do |*args, &blokk|
|
|
258
|
+
model.send method, *args, &blokk
|
|
220
259
|
end
|
|
221
260
|
|
|
222
261
|
send method, *args, &block
|
|
@@ -265,5 +304,9 @@ module Draper
|
|
|
265
304
|
model.class.reflect_on_association(association)
|
|
266
305
|
end
|
|
267
306
|
end
|
|
307
|
+
|
|
308
|
+
def decorated_associations
|
|
309
|
+
@decorated_associations ||= {}
|
|
310
|
+
end
|
|
268
311
|
end
|
|
269
312
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
|
1
2
|
module Draper
|
|
2
3
|
class DecoratedEnumerableProxy
|
|
3
4
|
include Enumerable
|
|
@@ -13,6 +14,16 @@ module Draper
|
|
|
13
14
|
end
|
|
14
15
|
alias_method :to_ary, :decorated_collection
|
|
15
16
|
|
|
17
|
+
def find(ifnone_or_id = nil, &blk)
|
|
18
|
+
if block_given?
|
|
19
|
+
source.find(ifnone_or_id, &blk)
|
|
20
|
+
else
|
|
21
|
+
obj = decorated_collection.first
|
|
22
|
+
return nil if obj.blank?
|
|
23
|
+
obj.class.find(ifnone_or_id)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
16
27
|
def method_missing (method, *args, &block)
|
|
17
28
|
@wrapped_collection.send(method, *args, &block)
|
|
18
29
|
end
|
|
@@ -27,17 +38,13 @@ module Draper
|
|
|
27
38
|
alias :is_a? :kind_of?
|
|
28
39
|
|
|
29
40
|
def ==(other)
|
|
30
|
-
@wrapped_collection == other
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def [](index)
|
|
34
|
-
@klass.new(@wrapped_collection[index], @options)
|
|
41
|
+
@wrapped_collection == (other.respond_to?(:source) ? other.source : other)
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
def to_s
|
|
38
45
|
"#<DecoratedEnumerableProxy of #{@klass} for #{@wrapped_collection.inspect}>"
|
|
39
46
|
end
|
|
40
|
-
|
|
47
|
+
|
|
41
48
|
def context=(input)
|
|
42
49
|
self.map { |member| member.context = input }
|
|
43
50
|
end
|
data/lib/draper/railtie.rb
CHANGED
|
@@ -12,39 +12,40 @@ end
|
|
|
12
12
|
module Draper
|
|
13
13
|
class Railtie < Rails::Railtie
|
|
14
14
|
|
|
15
|
-
##
|
|
16
|
-
# Decorators are loaded in
|
|
17
|
-
# => at app boot in non-development environments
|
|
18
|
-
# => after each request in the development environment
|
|
19
|
-
#
|
|
20
|
-
# This is necessary because we might never explicitly reference
|
|
21
|
-
# Decorator constants.
|
|
22
|
-
#
|
|
23
|
-
config.to_prepare do
|
|
24
|
-
::Draper::System.load_app_local_decorators
|
|
25
|
-
end
|
|
26
|
-
|
|
27
15
|
##
|
|
28
16
|
# The `app/decorators` path is eager loaded
|
|
29
17
|
#
|
|
30
18
|
# This is the standard "Rails Way" to add paths from which constants
|
|
31
19
|
# can be loaded.
|
|
32
20
|
#
|
|
33
|
-
config.
|
|
21
|
+
config.after_initialize do |app|
|
|
34
22
|
app.config.paths.add 'app/decorators', :eager_load => true
|
|
35
23
|
end
|
|
36
24
|
|
|
37
25
|
initializer "draper.extend_action_controller_base" do |app|
|
|
38
26
|
ActiveSupport.on_load(:action_controller) do
|
|
39
|
-
Draper::System.setup(
|
|
27
|
+
Draper::System.setup(self)
|
|
40
28
|
end
|
|
41
29
|
end
|
|
42
30
|
|
|
43
31
|
initializer "draper.extend_action_mailer_base" do |app|
|
|
44
32
|
ActiveSupport.on_load(:action_mailer) do
|
|
45
|
-
Draper::System.setup(
|
|
33
|
+
Draper::System.setup(self)
|
|
46
34
|
end
|
|
47
35
|
end
|
|
48
36
|
|
|
37
|
+
initializer "draper.extend_active_record_base" do |app|
|
|
38
|
+
ActiveSupport.on_load(:active_record) do
|
|
39
|
+
self.send(:include, Draper::ModelSupport)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
console do
|
|
44
|
+
require 'action_controller/test_case'
|
|
45
|
+
ApplicationController.new.view_context
|
|
46
|
+
Draper::ViewContext.current.controller.request ||= ActionController::TestRequest.new
|
|
47
|
+
Draper::ViewContext.current.request ||= Draper::ViewContext.current.controller.request
|
|
48
|
+
Draper::ViewContext.current.params ||= {}
|
|
49
|
+
end
|
|
49
50
|
end
|
|
50
51
|
end
|
|
@@ -1,33 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
included { metadata[:type] = :decorator }
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
RSpec.configure do |config|
|
|
9
|
-
# Automatically tag specs in specs/decorators as type: :decorator
|
|
10
|
-
config.include Draper::DecoratorExampleGroup, :type => :decorator, :example_group => {
|
|
11
|
-
:file_path => /spec[\\\/]decorators/
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
# Specs tagged type: :decorator set the Draper view context
|
|
15
|
-
config.around do |example|
|
|
16
|
-
if :decorator == example.metadata[:type]
|
|
17
|
-
ApplicationController.new.set_current_view_context
|
|
18
|
-
Draper::ViewContext.current.controller.request ||= ActionController::TestRequest.new
|
|
19
|
-
Draper::ViewContext.current.request ||= Draper::ViewContext.current.controller.request
|
|
20
|
-
Draper::ViewContext.current.params ||= {}
|
|
21
|
-
end
|
|
22
|
-
example.call
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
if defined?(Capybara)
|
|
27
|
-
require 'capybara/rspec/matchers'
|
|
28
|
-
|
|
29
|
-
RSpec.configure do |config|
|
|
30
|
-
config.include Capybara::RSpecMatchers, :type => :decorator
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
1
|
+
warn 'DEPRECATION WARNING -- use `require "draper/test/rspec_integration"` instead of `require "draper/rspec_integration"`'
|
|
2
|
+
require 'draper/test/rspec_integration'
|
data/lib/draper/system.rb
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
module Draper
|
|
2
2
|
class System
|
|
3
|
-
def self.app_local_decorator_glob
|
|
4
|
-
'app/decorators/**/*_decorator.rb'
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def self.load_app_local_decorators
|
|
8
|
-
decorator_files = Dir[ "#{ Rails.root }/#{ app_local_decorator_glob }" ]
|
|
9
|
-
decorator_files.each { |d| require_dependency d }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
3
|
def self.setup(component)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
elsif component == :action_mailer
|
|
17
|
-
ActionMailer::Base.send(:include, Draper::ViewContextFilter)
|
|
4
|
+
component.class_eval do
|
|
5
|
+
include Draper::ViewContext
|
|
6
|
+
extend Draper::HelperSupport unless defined?(::ActionMailer) && self.is_a?(::ActionMailer::Base)
|
|
18
7
|
end
|
|
19
8
|
end
|
|
20
9
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'draper/test/view_context'
|
|
2
|
+
|
|
3
|
+
module MiniTest
|
|
4
|
+
class Spec
|
|
5
|
+
class Decorator < Spec
|
|
6
|
+
before { Draper::ViewContext.infect!(self) }
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class MiniTest::Unit::DecoratorTestCase < MiniTest::Unit::TestCase
|
|
12
|
+
if method_defined?(:before_setup)
|
|
13
|
+
# for minitext >= 2.11
|
|
14
|
+
def before_setup
|
|
15
|
+
super
|
|
16
|
+
Draper::ViewContext.infect!(self)
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
# for older minitest, like what ships w/Ruby 1.9
|
|
20
|
+
add_setup_hook { Draper::ViewContext.infect!(self) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
MiniTest::Spec.register_spec_type(MiniTest::Spec::Decorator) do |desc|
|
|
25
|
+
desc.superclass == Draper::Base
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'draper/test/view_context'
|
|
2
|
+
|
|
3
|
+
module Draper
|
|
4
|
+
module DecoratorExampleGroup
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
included { metadata[:type] = :decorator }
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
RSpec.configure do |config|
|
|
11
|
+
# Automatically tag specs in specs/decorators as type: :decorator
|
|
12
|
+
config.include Draper::DecoratorExampleGroup, :type => :decorator, :example_group => {
|
|
13
|
+
:file_path => /spec[\\\/]decorators/
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Specs tagged type: :decorator set the Draper view context
|
|
17
|
+
config.before :type => :decorator do
|
|
18
|
+
Draper::ViewContext.infect!(self)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if defined?(Capybara)
|
|
23
|
+
require 'capybara/rspec/matchers'
|
|
24
|
+
|
|
25
|
+
RSpec.configure do |config|
|
|
26
|
+
config.include Capybara::RSpecMatchers, :type => :decorator
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Draper
|
|
2
|
+
module ViewContext
|
|
3
|
+
def self.infect!(context)
|
|
4
|
+
context.instance_eval do
|
|
5
|
+
ApplicationController.new.view_context
|
|
6
|
+
Draper::ViewContext.current.controller.request ||= ActionController::TestRequest.new
|
|
7
|
+
Draper::ViewContext.current.request ||= Draper::ViewContext.current.controller.request
|
|
8
|
+
Draper::ViewContext.current.params ||= {}
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/draper/version.rb
CHANGED
data/lib/draper/view_context.rb
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
module Draper
|
|
2
2
|
module ViewContext
|
|
3
3
|
def self.current
|
|
4
|
-
Thread.current[:current_view_context]
|
|
4
|
+
Thread.current[:current_view_context] || ApplicationController.new.view_context
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def self.current=(input)
|
|
8
8
|
Thread.current[:current_view_context] = input
|
|
9
9
|
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
module ViewContextFilter
|
|
13
|
-
def set_current_view_context
|
|
14
|
-
Draper::ViewContext.current = self.view_context
|
|
15
|
-
end
|
|
16
10
|
|
|
17
|
-
def
|
|
18
|
-
|
|
11
|
+
def view_context
|
|
12
|
+
super.tap do |context|
|
|
13
|
+
Draper::ViewContext.current = context
|
|
14
|
+
end
|
|
19
15
|
end
|
|
20
16
|
end
|
|
21
17
|
end
|
data/lib/draper.rb
CHANGED
|
@@ -9,6 +9,8 @@ require 'draper/model_support'
|
|
|
9
9
|
require 'draper/helper_support'
|
|
10
10
|
require 'draper/view_context'
|
|
11
11
|
require 'draper/decorated_enumerable_proxy'
|
|
12
|
-
require 'draper/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
|
|
13
12
|
require 'draper/railtie' if defined?(Rails)
|
|
14
13
|
|
|
14
|
+
# Test Support
|
|
15
|
+
require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
|
|
16
|
+
require 'draper/test/minitest_integration' if defined?(MiniTest::Spec)
|