omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +691 -0
  65. data/lib/action_controller.rb +80 -0
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController
6
+ # # Action Controller Etag With Template Digest
7
+ #
8
+ # When our views change, they should bubble up into HTTP cache freshness and
9
+ # bust browser caches. So the template digest for the current action is
10
+ # automatically included in the ETag.
11
+ #
12
+ # Enabled by default for apps that use Action View. Disable by setting
13
+ #
14
+ # config.action_controller.etag_with_template_digest = false
15
+ #
16
+ # Override the template to digest by passing `:template` to `fresh_when` and
17
+ # `stale?` calls. For example:
18
+ #
19
+ # # We're going to render widgets/show, not posts/show
20
+ # fresh_when @post, template: 'widgets/show'
21
+ #
22
+ # # We're not going to render a template, so omit it from the ETag.
23
+ # fresh_when @post, template: false
24
+ #
25
+ module EtagWithTemplateDigest
26
+ extend ActiveSupport::Concern
27
+
28
+ include ActionController::ConditionalGet
29
+
30
+ included do
31
+ class_attribute :etag_with_template_digest, default: true
32
+
33
+ etag do |options|
34
+ determine_template_etag(options) if etag_with_template_digest
35
+ end
36
+ end
37
+
38
+ private
39
+ def determine_template_etag(options)
40
+ if template = pick_template_for_etag(options)
41
+ lookup_and_digest_template(template)
42
+ end
43
+ end
44
+
45
+ # Pick the template digest to include in the ETag. If the `:template` option is
46
+ # present, use the named template. If `:template` is `nil` or absent, use the
47
+ # default controller/action template. If `:template` is false, omit the template
48
+ # digest from the ETag.
49
+ def pick_template_for_etag(options)
50
+ unless options[:template] == false
51
+ options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path
52
+ end
53
+ end
54
+
55
+ def lookup_and_digest_template(template)
56
+ ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController
6
+ class ActionControllerError < StandardError # :nodoc:
7
+ end
8
+
9
+ class BadRequest < ActionControllerError # :nodoc:
10
+ def initialize(msg = nil)
11
+ super(msg)
12
+ set_backtrace $!.backtrace if $!
13
+ end
14
+ end
15
+
16
+ class RenderError < ActionControllerError # :nodoc:
17
+ end
18
+
19
+ class RoutingError < ActionControllerError # :nodoc:
20
+ attr_reader :failures
21
+ def initialize(message, failures = [])
22
+ super(message)
23
+ @failures = failures
24
+ end
25
+ end
26
+
27
+ class UrlGenerationError < ActionControllerError # :nodoc:
28
+ attr_reader :routes, :route_name, :method_name
29
+
30
+ def initialize(message, routes = nil, route_name = nil, method_name = nil)
31
+ @routes = routes
32
+ @route_name = route_name
33
+ @method_name = method_name
34
+
35
+ super(message)
36
+ end
37
+
38
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
39
+ include DidYouMean::Correctable
40
+
41
+ def corrections
42
+ @corrections ||= begin
43
+ maybe_these = routes&.named_routes&.helper_names&.grep(/#{route_name}/) || []
44
+ maybe_these -= [method_name.to_s] # remove exact match
45
+
46
+ DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(route_name)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ class MethodNotAllowed < ActionControllerError # :nodoc:
53
+ def initialize(*allowed_methods)
54
+ super("Only #{allowed_methods.to_sentence} requests are allowed.")
55
+ end
56
+ end
57
+
58
+ class NotImplemented < MethodNotAllowed # :nodoc:
59
+ end
60
+
61
+ class MissingFile < ActionControllerError # :nodoc:
62
+ end
63
+
64
+ class SessionOverflowError < ActionControllerError # :nodoc:
65
+ DEFAULT_MESSAGE = "Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data."
66
+
67
+ def initialize(message = nil)
68
+ super(message || DEFAULT_MESSAGE)
69
+ end
70
+ end
71
+
72
+ class UnknownHttpMethod < ActionControllerError # :nodoc:
73
+ end
74
+
75
+ class UnknownFormat < ActionControllerError # :nodoc:
76
+ end
77
+
78
+ # Raised when a nested respond_to is triggered and the content types of each are
79
+ # incompatible. For example:
80
+ #
81
+ # respond_to do |outer_type|
82
+ # outer_type.js do
83
+ # respond_to do |inner_type|
84
+ # inner_type.html { render body: "HTML" }
85
+ # end
86
+ # end
87
+ # end
88
+ class RespondToMismatchError < ActionControllerError
89
+ DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
90
+
91
+ def initialize(message = nil)
92
+ super(message || DEFAULT_MESSAGE)
93
+ end
94
+ end
95
+
96
+ class MissingExactTemplate < UnknownFormat # :nodoc:
97
+ attr_reader :controller, :action_name
98
+
99
+ def initialize(message, controller, action_name)
100
+ @controller = controller
101
+ @action_name = action_name
102
+
103
+ super(message)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController # :nodoc:
6
+ module Flash
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_flash_types, instance_accessor: false, default: []
11
+
12
+ delegate :flash, to: :request
13
+ add_flash_types(:alert, :notice)
14
+ end
15
+
16
+ module ClassMethods
17
+ # Creates new flash types. You can pass as many types as you want to create
18
+ # flash types other than the default `alert` and `notice` in your controllers
19
+ # and views. For instance:
20
+ #
21
+ # # in application_controller.rb
22
+ # class ApplicationController < ActionController::Base
23
+ # add_flash_types :warning
24
+ # end
25
+ #
26
+ # # in your controller
27
+ # redirect_to user_path(@user), warning: "Incomplete profile"
28
+ #
29
+ # # in your view
30
+ # <%= warning %>
31
+ #
32
+ # This method will automatically define a new method for each of the given
33
+ # names, and it will be available in your views.
34
+ def add_flash_types(*types)
35
+ types.each do |type|
36
+ next if _flash_types.include?(type)
37
+
38
+ define_method(type) do
39
+ request.flash[type]
40
+ end
41
+ helper_method(type) if respond_to?(:helper_method)
42
+
43
+ self._flash_types += [type]
44
+ end
45
+ end
46
+
47
+ def action_methods # :nodoc:
48
+ @action_methods ||= super - _flash_types.map(&:to_s).to_set
49
+ end
50
+ end
51
+
52
+ private
53
+ def redirect_to(options = {}, response_options_and_flash = {}) # :doc:
54
+ self.class._flash_types.each do |flash_type|
55
+ if type = response_options_and_flash.delete(flash_type)
56
+ flash[flash_type] = type
57
+ end
58
+ end
59
+
60
+ if other_flashes = response_options_and_flash.delete(:flash)
61
+ flash.update(other_flashes)
62
+ end
63
+
64
+ super(options, response_options_and_flash)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController
6
+ module Head
7
+ # Returns a response that has no content (merely headers). The options argument
8
+ # is interpreted to be a hash of header names and values. This allows you to
9
+ # easily return a response that consists only of significant headers:
10
+ #
11
+ # head :created, location: person_path(@person)
12
+ #
13
+ # head :created, location: @person
14
+ #
15
+ # It can also be used to return exceptional conditions:
16
+ #
17
+ # return head(:method_not_allowed) unless request.post?
18
+ # return head(:bad_request) unless valid_request?
19
+ # render
20
+ #
21
+ # See `Rack::Utils::SYMBOL_TO_STATUS_CODE` for a full list of valid `status`
22
+ # symbols.
23
+ def head(status, options = nil)
24
+ if status.is_a?(Hash)
25
+ raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
26
+ end
27
+
28
+ status ||= :ok
29
+
30
+ if options
31
+ location = options.delete(:location)
32
+ content_type = options.delete(:content_type)
33
+
34
+ options.each do |key, value|
35
+ headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
36
+ end
37
+ end
38
+
39
+ self.status = status
40
+ self.location = url_for(location) if location
41
+
42
+ if include_content?(response_code)
43
+ unless self.media_type
44
+ self.content_type = content_type || ((f = formats) && Mime[f.first]) || Mime[:html]
45
+ end
46
+
47
+ response.charset = false
48
+ end
49
+
50
+ self.response_body = ""
51
+
52
+ true
53
+ end
54
+
55
+ private
56
+ def include_content?(status)
57
+ case status
58
+ when 100..199
59
+ false
60
+ when 204, 205, 304
61
+ false
62
+ else
63
+ true
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController
6
+ # # Action Controller Helpers
7
+ #
8
+ # The Rails framework provides a large number of helpers for working with
9
+ # assets, dates, forms, numbers and model objects, to name a few. These helpers
10
+ # are available to all templates by default.
11
+ #
12
+ # In addition to using the standard template helpers provided, creating custom
13
+ # helpers to extract complicated logic or reusable functionality is strongly
14
+ # encouraged. By default, each controller will include all helpers. These
15
+ # helpers are only accessible on the controller through `#helpers`
16
+ #
17
+ # In previous versions of Rails the controller will include a helper which
18
+ # matches the name of the controller, e.g., `MyController` will automatically
19
+ # include `MyHelper`. You can revert to the old behavior with the following:
20
+ #
21
+ # # config/application.rb
22
+ # class Application < Rails::Application
23
+ # config.action_controller.include_all_helpers = false
24
+ # end
25
+ #
26
+ # Additional helpers can be specified using the `helper` class method in
27
+ # ActionController::Base or any controller which inherits from it.
28
+ #
29
+ # The `to_s` method from the Time class can be wrapped in a helper method to
30
+ # display a custom message if a Time object is blank:
31
+ #
32
+ # module FormattedTimeHelper
33
+ # def format_time(time, format=:long, blank_message="&nbsp;")
34
+ # time.blank? ? blank_message : time.to_fs(format)
35
+ # end
36
+ # end
37
+ #
38
+ # FormattedTimeHelper can now be included in a controller, using the `helper`
39
+ # class method:
40
+ #
41
+ # class EventsController < ActionController::Base
42
+ # helper FormattedTimeHelper
43
+ # def index
44
+ # @events = Event.all
45
+ # end
46
+ # end
47
+ #
48
+ # Then, in any view rendered by `EventsController`, the `format_time` method can
49
+ # be called:
50
+ #
51
+ # <% @events.each do |event| -%>
52
+ # <p>
53
+ # <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
54
+ # </p>
55
+ # <% end -%>
56
+ #
57
+ # Finally, assuming we have two event instances, one which has a time and one
58
+ # which does not, the output might look like this:
59
+ #
60
+ # 23 Aug 11:30 | Carolina Railhawks Soccer Match
61
+ # N/A | Carolina Railhawks Training Workshop
62
+ #
63
+ module Helpers
64
+ extend ActiveSupport::Concern
65
+
66
+ class << self; attr_accessor :helpers_path; end
67
+ include AbstractController::Helpers
68
+
69
+ included do
70
+ class_attribute :helpers_path, default: []
71
+ class_attribute :include_all_helpers, default: true
72
+ end
73
+
74
+ module ClassMethods
75
+ # Declares helper accessors for controller attributes. For example, the
76
+ # following adds new `name` and `name=` instance methods to a controller and
77
+ # makes them available to the view:
78
+ # attr_accessor :name
79
+ # helper_attr :name
80
+ #
81
+ # #### Parameters
82
+ # * `attrs` - Names of attributes to be converted into helpers.
83
+ #
84
+ def helper_attr(*attrs)
85
+ attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
86
+ end
87
+
88
+ # Provides a proxy to access helper methods from outside the view.
89
+ #
90
+ # Note that the proxy is rendered under a different view context. This may cause
91
+ # incorrect behavior with capture methods. Consider using
92
+ # [helper](rdoc-ref:AbstractController::Helpers::ClassMethods#helper) instead
93
+ # when using `capture`.
94
+ def helpers
95
+ @helper_proxy ||= begin
96
+ proxy = ActionView::Base.empty
97
+ proxy.config = config.inheritable_copy
98
+ proxy.extend(_helpers)
99
+ end
100
+ end
101
+
102
+ # Override modules_for_helpers to accept `:all` as argument, which loads all
103
+ # helpers in helpers_path.
104
+ #
105
+ # #### Parameters
106
+ # * `args` - A list of helpers
107
+ #
108
+ #
109
+ # #### Returns
110
+ # * `array` - A normalized list of modules for the list of helpers provided.
111
+ #
112
+ def modules_for_helpers(args)
113
+ args += all_application_helpers if args.delete(:all)
114
+ super(args)
115
+ end
116
+
117
+ private
118
+ # Extract helper names from files in `app/helpers/***/**_helper.rb`
119
+ def all_application_helpers
120
+ all_helpers_from_path(helpers_path)
121
+ end
122
+ end
123
+
124
+ # Provides a proxy to access helper methods from outside the view.
125
+ def helpers
126
+ @_helper_proxy ||= view_context
127
+ end
128
+ end
129
+ end