draper 0.17.0 → 1.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.markdown +14 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +50 -31
  6. data/Readme.markdown +63 -92
  7. data/draper.gemspec +4 -2
  8. data/lib/draper/collection_decorator.rb +88 -0
  9. data/lib/draper/decoratable.rb +44 -0
  10. data/lib/draper/decorated_association.rb +55 -0
  11. data/lib/draper/decorator.rb +208 -0
  12. data/lib/draper/finders.rb +44 -0
  13. data/lib/draper/helper_proxy.rb +16 -0
  14. data/lib/draper/railtie.rb +17 -21
  15. data/lib/draper/security.rb +48 -0
  16. data/lib/draper/tasks/tu.rake +5 -0
  17. data/lib/draper/test/minitest_integration.rb +5 -24
  18. data/lib/draper/test/rspec_integration.rb +0 -6
  19. data/lib/draper/test/test_unit_integration.rb +9 -0
  20. data/lib/draper/version.rb +1 -1
  21. data/lib/draper/view_context.rb +24 -6
  22. data/lib/draper/view_helpers.rb +36 -0
  23. data/lib/draper.rb +42 -7
  24. data/lib/generators/decorator/decorator_generator.rb +1 -1
  25. data/lib/generators/decorator/templates/decorator.rb +0 -1
  26. data/lib/generators/rspec/decorator_generator.rb +1 -1
  27. data/lib/generators/test_unit/decorator_generator.rb +1 -1
  28. data/lib/generators/test_unit/templates/decorator_test.rb +0 -3
  29. data/spec/draper/collection_decorator_spec.rb +240 -0
  30. data/spec/draper/decoratable_spec.rb +164 -0
  31. data/spec/draper/decorated_association_spec.rb +130 -0
  32. data/spec/draper/decorator_spec.rb +497 -0
  33. data/spec/draper/finders_spec.rb +156 -0
  34. data/spec/draper/helper_proxy_spec.rb +12 -0
  35. data/spec/draper/security_spec.rb +158 -0
  36. data/spec/draper/view_helpers_spec.rb +41 -0
  37. data/spec/dummy/.rspec +2 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  41. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  42. data/spec/dummy/app/controllers/posts_controller.rb +17 -0
  43. data/spec/dummy/app/decorators/post_decorator.rb +25 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  46. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  47. data/spec/dummy/app/models/post.rb +3 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  49. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  50. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  51. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  52. data/spec/dummy/config/application.rb +64 -0
  53. data/spec/dummy/config/boot.rb +10 -0
  54. data/spec/dummy/config/database.yml +25 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +34 -0
  57. data/spec/dummy/config/environments/production.rb +55 -0
  58. data/spec/dummy/config/environments/test.rb +32 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/inflections.rb +15 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  63. data/spec/dummy/config/initializers/session_store.rb +8 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +5 -0
  66. data/spec/dummy/config/routes.rb +7 -0
  67. data/spec/dummy/config.ru +4 -0
  68. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  69. data/spec/dummy/db/schema.rb +21 -0
  70. data/spec/dummy/db/seeds.rb +2 -0
  71. data/spec/dummy/lib/tasks/spec.rake +5 -0
  72. data/spec/dummy/log/.gitkeep +0 -0
  73. data/spec/dummy/public/404.html +26 -0
  74. data/spec/dummy/public/422.html +26 -0
  75. data/spec/dummy/public/500.html +25 -0
  76. data/spec/dummy/public/favicon.ico +0 -0
  77. data/spec/dummy/script/rails +6 -0
  78. data/spec/dummy/spec/decorators/post_decorator_spec.rb +23 -0
  79. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  80. data/spec/dummy/spec/spec_helper.rb +9 -0
  81. data/spec/generators/decorator/decorator_generator_spec.rb +24 -5
  82. data/spec/integration/integration_spec.rb +33 -0
  83. data/spec/minitest-rails/spec_type_spec.rb +63 -0
  84. data/{performance → spec/performance}/decorators.rb +2 -4
  85. data/spec/spec_helper.rb +23 -26
  86. data/spec/support/action_controller.rb +12 -0
  87. data/spec/support/active_model.rb +7 -0
  88. data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
  89. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  90. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  91. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  92. data/spec/support/decorators/product_decorator.rb +19 -0
  93. data/spec/support/decorators/products_decorator.rb +10 -0
  94. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  95. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  96. data/spec/support/dummy_app.rb +84 -0
  97. data/spec/support/matchers/have_text.rb +50 -0
  98. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  99. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  100. data/spec/support/{samples → models}/product.rb +13 -2
  101. data/spec/support/models/some_thing.rb +5 -0
  102. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  103. metadata +207 -98
  104. data/doc/ApplicationDecorator.html +0 -147
  105. data/doc/Draper/AllHelpers.html +0 -256
  106. data/doc/Draper/Base.html +0 -1222
  107. data/doc/Draper/DecoratorGenerator.html +0 -216
  108. data/doc/Draper/LazyHelpers.html +0 -174
  109. data/doc/Draper/ModelSupport.html +0 -164
  110. data/doc/Draper/System.html +0 -179
  111. data/doc/Draper.html +0 -123
  112. data/doc/_index.html +0 -172
  113. data/doc/class_list.html +0 -47
  114. data/doc/css/common.css +0 -1
  115. data/doc/css/full_list.css +0 -53
  116. data/doc/css/style.css +0 -320
  117. data/doc/file_list.html +0 -46
  118. data/doc/frames.html +0 -13
  119. data/doc/index.html +0 -172
  120. data/doc/js/app.js +0 -205
  121. data/doc/js/full_list.js +0 -150
  122. data/doc/js/jquery.js +0 -16
  123. data/doc/method_list.html +0 -174
  124. data/doc/top-level-namespace.html +0 -103
  125. data/lib/draper/active_model_support.rb +0 -27
  126. data/lib/draper/base.rb +0 -312
  127. data/lib/draper/decorated_enumerable_proxy.rb +0 -57
  128. data/lib/draper/model_support.rb +0 -17
  129. data/lib/draper/rspec_integration.rb +0 -2
  130. data/lib/draper/system.rb +0 -10
  131. data/lib/draper/test/view_context.rb +0 -12
  132. data/spec/draper/base_spec.rb +0 -863
  133. data/spec/draper/helper_support_spec.rb +0 -18
  134. data/spec/draper/model_support_spec.rb +0 -48
  135. data/spec/draper/view_context_spec.rb +0 -30
  136. data/spec/support/samples/active_model.rb +0 -9
  137. data/spec/support/samples/application_controller.rb +0 -42
  138. data/spec/support/samples/application_helper.rb +0 -8
  139. data/spec/support/samples/decorator.rb +0 -5
  140. data/spec/support/samples/decorator_with_allows.rb +0 -3
  141. data/spec/support/samples/decorator_with_denies.rb +0 -3
  142. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  143. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  144. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  145. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  146. data/spec/support/samples/product_decorator.rb +0 -7
  147. data/spec/support/samples/sequel_product.rb +0 -13
  148. data/spec/support/samples/some_thing.rb +0 -2
  149. data/spec/support/samples/some_thing_decorator.rb +0 -3
  150. /data/{performance → spec/performance}/active_record.rb +0 -0
  151. /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  152. /data/{performance → spec/performance}/models.rb +0 -0
  153. /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  154. /data/spec/support/{samples → models}/widget.rb +0 -0
@@ -0,0 +1,208 @@
1
+ require 'active_support/core_ext/array/extract_options'
2
+
3
+ module Draper
4
+ class Decorator
5
+ include Draper::ViewHelpers
6
+ include ActiveModel::Serialization if defined?(ActiveModel::Serialization)
7
+
8
+ attr_accessor :source, :options
9
+
10
+ alias_method :model, :source
11
+ alias_method :to_source, :source
12
+
13
+ # Initialize a new decorator instance by passing in
14
+ # an instance of the source class. Pass in an optional
15
+ # context inside the options hash is stored for later use.
16
+ #
17
+ # A decorator cannot be applied to other instances of the
18
+ # same decorator and will instead result in a decorator
19
+ # with the same target as the original.
20
+ # You can, however, apply several decorators in a chain but
21
+ # you will get a warning if the same decorator appears at
22
+ # multiple places in the chain.
23
+ #
24
+ # @param [Object] source object to decorate
25
+ # @param [Hash] options (optional)
26
+ def initialize(source, options = {})
27
+ source.to_a if source.respond_to?(:to_a) # forces evaluation of a lazy query from AR
28
+ @source = source
29
+ @options = options
30
+ handle_multiple_decoration if source.is_a?(Draper::Decorator)
31
+ end
32
+
33
+ class << self
34
+ alias_method :decorate, :new
35
+ end
36
+
37
+ # Adds ActiveRecord finder methods to the decorator class. The
38
+ # methods return decorated models, so that you can use
39
+ # `ProductDecorator.find(id)` instead of
40
+ # `ProductDecorator.decorate(Product.find(id))`.
41
+ #
42
+ # If the `:for` option is not supplied, the model class will be
43
+ # inferred from the decorator class.
44
+ #
45
+ # @option options [Class, Symbol] :for The model class to find
46
+ def self.has_finders(options = {})
47
+ extend Draper::Finders
48
+ self.finder_class = options[:for] || name.chomp("Decorator")
49
+ end
50
+
51
+ # Typically called within a decorator definition, this method causes
52
+ # the assocation to be decorated when it is retrieved.
53
+ #
54
+ # @param [Symbol] association name of association to decorate, like `:products`
55
+ # @option options [Class] :with the decorator to apply to the association
56
+ # @option options [Symbol] :scope a scope to apply when fetching the association
57
+ def self.decorates_association(association, options = {})
58
+ define_method(association) do
59
+ decorated_associations[association] ||= Draper::DecoratedAssociation.new(source, association, options)
60
+ decorated_associations[association].call
61
+ end
62
+ end
63
+
64
+ # A convenience method for decorating multiple associations. Calls
65
+ # decorates_association on each of the given symbols.
66
+ #
67
+ # @param [Symbols*] associations name of associations to decorate
68
+ def self.decorates_associations(*associations)
69
+ options = associations.extract_options!
70
+ associations.each do |association|
71
+ decorates_association(association, options)
72
+ end
73
+ end
74
+
75
+ # Specifies a black list of methods which may *not* be proxied to
76
+ # the wrapped object.
77
+ #
78
+ # Do not use both `.allows` and `.denies` together, either write
79
+ # a whitelist with `.allows` or a blacklist with `.denies`
80
+ #
81
+ # @param [Symbols*] methods methods to deny like `:find, :find_by_name`
82
+ def self.denies(*methods)
83
+ security.denies(*methods)
84
+ end
85
+
86
+ # Specifies that all methods may *not* be proxied to the wrapped object.
87
+ #
88
+ # Do not use `.allows` and `.denies` in combination with '.denies_all'
89
+ def self.denies_all
90
+ security.denies_all
91
+ end
92
+
93
+ # Specifies a white list of methods which *may* be proxied to
94
+ # the wrapped object. When `allows` is used, only the listed
95
+ # methods and methods defined in the decorator itself will be
96
+ # available.
97
+ #
98
+ # Do not use both `.allows` and `.denies` together, either write
99
+ # a whitelist with `.allows` or a blacklist with `.denies`
100
+ #
101
+ # @param [Symbols*] methods methods to allow like `:find, :find_by_name`
102
+ def self.allows(*methods)
103
+ security.allows(*methods)
104
+ end
105
+
106
+ # Creates a new CollectionDecorator for the given collection.
107
+ #
108
+ # @param [Object] source collection to decorate
109
+ # @param [Hash] options passed to each item's decorator (except
110
+ # for the keys listed below)
111
+ # @option options [Class,Symbol] :with (self) the class used to decorate
112
+ # items, or `:infer` to call each item's `decorate` method instead
113
+ def self.decorate_collection(source, options = {})
114
+ Draper::CollectionDecorator.new(source, options.reverse_merge(with: self))
115
+ end
116
+
117
+ # Get the chain of decorators applied to the object.
118
+ #
119
+ # @return [Array] list of decorator classes
120
+ def applied_decorators
121
+ chain = source.respond_to?(:applied_decorators) ? source.applied_decorators : []
122
+ chain << self.class
123
+ end
124
+
125
+ # Checks if a given decorator has been applied.
126
+ #
127
+ # @param [Class] decorator_class
128
+ def decorated_with?(decorator_class)
129
+ applied_decorators.include?(decorator_class)
130
+ end
131
+
132
+ def decorated?
133
+ true
134
+ end
135
+
136
+ # Delegates == to the decorated models
137
+ #
138
+ # @return [Boolean] true if other's model == self's model
139
+ def ==(other)
140
+ source == (other.respond_to?(:source) ? other.source : other)
141
+ end
142
+
143
+ def kind_of?(klass)
144
+ super || source.kind_of?(klass)
145
+ end
146
+ alias_method :is_a?, :kind_of?
147
+
148
+ def respond_to?(method, include_private = false)
149
+ super || (allow?(method) && source.respond_to?(method, include_private))
150
+ end
151
+
152
+ # We always want to delegate present, in case we decorate a nil object.
153
+ #
154
+ # I don't like the idea of decorating a nil object, but we'll deal with
155
+ # that later.
156
+ def present?
157
+ source.present?
158
+ end
159
+
160
+ def method_missing(method, *args, &block)
161
+ if allow?(method) && source.respond_to?(method)
162
+ self.class.define_proxy(method)
163
+ send(method, *args, &block)
164
+ else
165
+ super
166
+ end
167
+ end
168
+
169
+ # For ActiveModel compatibilty
170
+ def to_model
171
+ self
172
+ end
173
+
174
+ # For ActiveModel compatibility
175
+ def to_param
176
+ source.to_param
177
+ end
178
+
179
+ private
180
+
181
+ def self.define_proxy(method)
182
+ define_method(method) do |*args, &block|
183
+ source.send(method, *args, &block)
184
+ end
185
+ end
186
+
187
+ def self.security
188
+ @security ||= Security.new
189
+ end
190
+
191
+ def allow?(method)
192
+ self.class.security.allow?(method)
193
+ end
194
+
195
+ def handle_multiple_decoration
196
+ if source.instance_of?(self.class)
197
+ self.options = source.options if options.empty?
198
+ self.source = source.source
199
+ elsif source.decorated_with?(self.class)
200
+ warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}"
201
+ end
202
+ end
203
+
204
+ def decorated_associations
205
+ @decorated_associations ||= {}
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,44 @@
1
+ module Draper
2
+ module Finders
3
+
4
+ attr_reader :finder_class
5
+ def finder_class=(klass)
6
+ @finder_class = klass.to_s.camelize.constantize
7
+ end
8
+
9
+ def find(id, options = {})
10
+ decorate(finder_class.find(id), options)
11
+ end
12
+
13
+ def all(options = {})
14
+ decorate_collection(finder_class.all, options)
15
+ end
16
+
17
+ def first(options = {})
18
+ decorate(finder_class.first, options)
19
+ end
20
+
21
+ def last(options = {})
22
+ decorate(finder_class.last, options)
23
+ end
24
+
25
+ def method_missing(method, *args, &block)
26
+ result = finder_class.send(method, *args, &block)
27
+ options = args.extract_options!
28
+
29
+ case method.to_s
30
+ when /^find_((last_)?by_|or_(initialize|create)_by_)/
31
+ decorate(result, options)
32
+ when /^find_all_by_/
33
+ decorate_collection(result, options)
34
+ else
35
+ result
36
+ end
37
+ end
38
+
39
+ def respond_to?(method, include_private = false)
40
+ super || finder_class.respond_to?(method)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ module Draper
2
+ class HelperProxy
3
+ # Some helpers are private, for example html_escape... as a workaround
4
+ # we are wrapping the helpers in a delegator that passes the methods
5
+ # along through a send, which will ignore private/public distinctions
6
+ def method_missing(method, *args, &block)
7
+ view_context.send(method, *args, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def view_context
13
+ Draper::ViewContext.current
14
+ end
15
+ end
16
+ end
@@ -3,8 +3,8 @@ require 'rails/railtie'
3
3
  module ActiveModel
4
4
  class Railtie < Rails::Railtie
5
5
  generators do |app|
6
- Rails::Generators.configure!(app.config.generators)
7
- require "generators/resource_override"
6
+ Rails::Generators.configure! app.config.generators
7
+ require 'generators/resource_override'
8
8
  end
9
9
  end
10
10
  end
@@ -12,40 +12,36 @@ end
12
12
  module Draper
13
13
  class Railtie < Rails::Railtie
14
14
 
15
- ##
16
- # The `app/decorators` path is eager loaded
17
- #
18
- # This is the standard "Rails Way" to add paths from which constants
19
- # can be loaded.
20
- #
21
15
  config.after_initialize do |app|
22
- app.config.paths.add 'app/decorators', :eager_load => true
16
+ app.config.paths.add 'app/decorators', eager_load: true
23
17
  end
24
18
 
25
- initializer "draper.extend_action_controller_base" do |app|
26
- ActiveSupport.on_load(:action_controller) do
27
- Draper::System.setup(self)
19
+ initializer "draper.setup_action_controller" do |app|
20
+ ActiveSupport.on_load :action_controller do
21
+ Draper.setup_action_controller self
28
22
  end
29
23
  end
30
24
 
31
- initializer "draper.extend_action_mailer_base" do |app|
32
- ActiveSupport.on_load(:action_mailer) do
33
- Draper::System.setup(self)
25
+ initializer "draper.setup_action_mailer" do |app|
26
+ ActiveSupport.on_load :action_mailer do
27
+ Draper.setup_action_mailer self
34
28
  end
35
29
  end
36
30
 
37
- initializer "draper.extend_active_record_base" do |app|
38
- ActiveSupport.on_load(:active_record) do
39
- self.send(:include, Draper::ModelSupport)
31
+ initializer "draper.setup_active_record" do |app|
32
+ ActiveSupport.on_load :active_record do
33
+ Draper.setup_active_record self
40
34
  end
41
35
  end
42
36
 
43
37
  console do
44
38
  require 'action_controller/test_case'
45
39
  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 ||= {}
40
+ Draper::ViewContext.build_view_context
41
+ end
42
+
43
+ rake_tasks do
44
+ Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
49
45
  end
50
46
  end
51
47
  end
@@ -0,0 +1,48 @@
1
+ module Draper
2
+ class Security
3
+ def initialize
4
+ @methods = []
5
+ end
6
+
7
+ def denies(*methods)
8
+ apply_strategy :denies
9
+ add_methods methods
10
+ end
11
+
12
+ def denies_all
13
+ apply_strategy :denies_all
14
+ end
15
+
16
+ def allows(*methods)
17
+ apply_strategy :allows
18
+ add_methods methods
19
+ end
20
+
21
+ def allow?(method)
22
+ case strategy
23
+ when :allows
24
+ methods.include?(method)
25
+ when :denies
26
+ !methods.include?(method)
27
+ when :denies_all
28
+ false
29
+ when nil
30
+ true
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :methods, :strategy
37
+
38
+ def apply_strategy(new_strategy)
39
+ raise ArgumentError, "Use only one of 'allows', 'denies', or 'denies_all'." if strategy && strategy != new_strategy
40
+ @strategy = new_strategy
41
+ end
42
+
43
+ def add_methods(new_methods)
44
+ raise ArgumentError, "Specify at least one method when using #{strategy}" if new_methods.empty?
45
+ @methods += new_methods.map(&:to_sym)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ if Rake::Task.task_defined?('test:run')
2
+ Rake::Task['test:run'].enhance do
3
+ Rake::Task['test:decorators'].invoke
4
+ end
5
+ end
@@ -1,26 +1,7 @@
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
1
+ class MiniTest::Rails::ActiveSupport::TestCase
2
+ # Use AS::TestCase for the base class when describing a decorator
3
+ register_spec_type(self) do |desc|
4
+ desc < Draper::Decorator if desc.is_a?(Class)
8
5
  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
6
+ register_spec_type(/Decorator( ?Test)?\z/i, self)
26
7
  end
@@ -1,5 +1,3 @@
1
- require 'draper/test/view_context'
2
-
3
1
  module Draper
4
2
  module DecoratorExampleGroup
5
3
  extend ActiveSupport::Concern
@@ -13,10 +11,6 @@ RSpec.configure do |config|
13
11
  :file_path => /spec[\\\/]decorators/
14
12
  }
15
13
 
16
- # Specs tagged type: :decorator set the Draper view context
17
- config.before :type => :decorator do
18
- Draper::ViewContext.infect!(self)
19
- end
20
14
  end
21
15
 
22
16
  if defined?(Capybara)
@@ -0,0 +1,9 @@
1
+ require "rake/testtask"
2
+ require "rails/test_unit/sub_test_task"
3
+
4
+ namespace :test do
5
+ Rails::SubTestTask.new(:decorators => "test:prepare") do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/decorators/**/*_test.rb"
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.17.0"
2
+ VERSION = "1.0.0.beta1"
3
3
  end
@@ -1,16 +1,34 @@
1
1
  module Draper
2
2
  module ViewContext
3
+ def view_context
4
+ super.tap do |context|
5
+ Draper::ViewContext.current = context
6
+ end
7
+ end
8
+
9
+ def self.current_controller
10
+ Thread.current[:current_controller] || ApplicationController.new
11
+ end
12
+
13
+ def self.current_controller=(controller)
14
+ Thread.current[:current_controller] = controller
15
+ end
16
+
3
17
  def self.current
4
- Thread.current[:current_view_context] || ApplicationController.new.view_context
18
+ Thread.current[:current_view_context] ||= build_view_context
5
19
  end
6
20
 
7
- def self.current=(input)
8
- Thread.current[:current_view_context] = input
21
+ def self.current=(context)
22
+ Thread.current[:current_view_context] = context
9
23
  end
10
24
 
11
- def view_context
12
- super.tap do |context|
13
- Draper::ViewContext.current = context
25
+ def self.build_view_context
26
+ current_controller.view_context.tap do |context|
27
+ if defined?(ActionController::TestRequest)
28
+ context.controller.request ||= ActionController::TestRequest.new
29
+ context.request ||= context.controller.request
30
+ context.params ||= {}
31
+ end
14
32
  end
15
33
  end
16
34
  end
@@ -0,0 +1,36 @@
1
+ module Draper
2
+ module ViewHelpers
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ # Access the helpers proxy to call built-in and user-defined
8
+ # Rails helpers from a class context.
9
+ #
10
+ # @return [HelperProxy] the helpers proxy
11
+ def helpers
12
+ @helpers ||= Draper::HelperProxy.new
13
+ end
14
+ alias_method :h, :helpers
15
+
16
+ end
17
+
18
+ # Access the helpers proxy to call built-in and user-defined
19
+ # Rails helpers. Aliased to `h` for convenience.
20
+ #
21
+ # @return [HelperProxy] the helpers proxy
22
+ def helpers
23
+ self.class.helpers
24
+ end
25
+ alias_method :h, :helpers
26
+
27
+ # Localize is something that's used quite often. Even though
28
+ # it's available through helpers, that's annoying. Aliased
29
+ # to `l` for convenience.
30
+ def localize(*args)
31
+ helpers.localize(*args)
32
+ end
33
+ alias_method :l, :localize
34
+
35
+ end
36
+ end
data/lib/draper.rb CHANGED
@@ -1,16 +1,51 @@
1
1
  require 'action_view'
2
2
 
3
3
  require 'draper/version'
4
- require 'draper/system'
5
- require 'draper/active_model_support'
6
- require 'draper/base'
4
+ require 'draper/view_helpers'
5
+ require 'draper/finders'
6
+ require 'draper/decorator'
7
+ require 'draper/helper_proxy'
7
8
  require 'draper/lazy_helpers'
8
- require 'draper/model_support'
9
+ require 'draper/decoratable'
10
+ require 'draper/decorated_association'
11
+ require 'draper/security'
9
12
  require 'draper/helper_support'
10
13
  require 'draper/view_context'
11
- require 'draper/decorated_enumerable_proxy'
14
+ require 'draper/collection_decorator'
12
15
  require 'draper/railtie' if defined?(Rails)
13
16
 
14
17
  # 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)
18
+ require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
19
+ require 'draper/test/minitest_integration' if defined?(MiniTest::Rails)
20
+ require 'draper/test/test_unit_integration'
21
+
22
+ module Draper
23
+ def self.setup_action_controller(base)
24
+ base.class_eval do
25
+ include Draper::ViewContext
26
+ extend Draper::HelperSupport
27
+ before_filter ->(controller) {
28
+ Draper::ViewContext.current = nil
29
+ Draper::ViewContext.current_controller = controller
30
+ }
31
+ end
32
+ end
33
+
34
+ def self.setup_action_mailer(base)
35
+ base.class_eval do
36
+ include Draper::ViewContext
37
+ end
38
+ end
39
+
40
+ def self.setup_active_record(base)
41
+ base.class_eval do
42
+ include Draper::Decoratable
43
+ end
44
+ end
45
+
46
+ class UninferrableDecoratorError < NameError
47
+ def initialize(klass)
48
+ super("Could not infer a decorator for #{klass}.")
49
+ end
50
+ end
51
+ end
@@ -20,7 +20,7 @@ module Rails
20
20
  elsif defined?(ApplicationDecorator)
21
21
  "ApplicationDecorator"
22
22
  else
23
- "Draper::Base"
23
+ "Draper::Decorator"
24
24
  end
25
25
  end
26
26
  end
@@ -1,6 +1,5 @@
1
1
  <% module_namespacing do -%>
2
2
  class <%= class_name %>Decorator < <%= parent_class_name %>
3
- decorates :<%= singular_name %>
4
3
 
5
4
  # Accessing Helpers
6
5
  # You can access any helper via a proxy
@@ -3,7 +3,7 @@ module Rspec
3
3
  source_root File.expand_path('../templates', __FILE__)
4
4
 
5
5
  def create_spec_file
6
- template 'decorator_spec.rb', File.join('spec/decorators', "#{singular_name}_decorator_spec.rb")
6
+ template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
7
7
  end
8
8
  end
9
9
  end
@@ -3,7 +3,7 @@ module TestUnit
3
3
  source_root File.expand_path('../templates', __FILE__)
4
4
 
5
5
  def create_test_file
6
- template 'decorator_test.rb', File.join('test/decorators', "#{singular_name}_decorator_test.rb")
6
+ template 'decorator_test.rb', File.join('test/decorators', class_path, "#{singular_name}_decorator_test.rb")
7
7
  end
8
8
  end
9
9
  end
@@ -1,7 +1,4 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class <%= class_name %>DecoratorTest < ActiveSupport::TestCase
4
- def setup
5
- ApplicationController.new.view_context
6
- end
7
4
  end