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 CHANGED
@@ -6,4 +6,8 @@ pkg/*
6
6
  coverage.data
7
7
  coverage/*
8
8
  .yardoc
9
- tmp
9
+ tmp
10
+ vendor/bundle
11
+ *.swp
12
+ *.swo
13
+ *.DS_Store
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --color
2
+ --order rand
data/.travis.yml CHANGED
@@ -1,6 +1,4 @@
1
1
  rvm:
2
- - 1.8.7
3
- - 1.9.2
4
- - rbx
5
- - ree
6
- - jruby
2
+ - 1.9.3
3
+ - rbx-19mode
4
+ - jruby-19mode
@@ -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
  [![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)
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 https://github.com/jcasimir/draper/compare/ad94f54369deabd11315fc241c66b74a30adec0a...04bb00505b7832419fddba20e5068317610eb38d
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 < ApplicationDecorator
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 run `ApplicationController.new.set_current_view_context` which makes helpers available to the decorator.
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 draper:decorator Article
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', '>= 2.3.10'
20
- s.add_dependency 'rake'
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', '~> 0.2.2'
26
- s.add_development_dependency 'guard'
27
- s.add_development_dependency 'guard-rspec'
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 create_proxies
3
+ def self.extended(base)
4
4
  # These methods (as keys) will be created only if the correspondent
5
- # model descends from a specific class (as value)
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, dependency|
12
- if model.kind_of?(dependency) || dependency.nil?
13
- class << self
14
- self
15
- end.class_eval do
16
- self.send(:define_method, method_name) do |*args, &block|
17
- model.send(method_name, *args, &block)
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
- create_proxies
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 [Class] :with The decorator to decorate the association with
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.each{ |sym| decorates_association(sym) }
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 to_model
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, &block|
219
- model.send method, *args, &block
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
@@ -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.before_initialize do |app|
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(:action_controller)
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(:action_mailer)
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
- module Draper
2
- module DecoratorExampleGroup
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
- if component == :action_controller
14
- ActionController::Base.send(:include, Draper::ViewContextFilter)
15
- ActionController::Base.extend(Draper::HelperSupport)
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
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.12.1"
2
+ VERSION = "0.17.0"
3
3
  end
@@ -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 self.included(source)
18
- source.send(:before_filter, :set_current_view_context) if source.respond_to?(:before_filter)
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)