omg-actionview 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +316 -0
  8. data/lib/action_view/buffers.rb +165 -0
  9. data/lib/action_view/cache_expiry.rb +69 -0
  10. data/lib/action_view/context.rb +32 -0
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
  12. data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
  13. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  14. data/lib/action_view/dependency_tracker.rb +41 -0
  15. data/lib/action_view/deprecator.rb +7 -0
  16. data/lib/action_view/digestor.rb +130 -0
  17. data/lib/action_view/flows.rb +75 -0
  18. data/lib/action_view/gem_version.rb +17 -0
  19. data/lib/action_view/helpers/active_model_helper.rb +54 -0
  20. data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
  21. data/lib/action_view/helpers/asset_url_helper.rb +473 -0
  22. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  23. data/lib/action_view/helpers/cache_helper.rb +315 -0
  24. data/lib/action_view/helpers/capture_helper.rb +236 -0
  25. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  26. data/lib/action_view/helpers/controller_helper.rb +42 -0
  27. data/lib/action_view/helpers/csp_helper.rb +26 -0
  28. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  29. data/lib/action_view/helpers/date_helper.rb +1266 -0
  30. data/lib/action_view/helpers/debug_helper.rb +38 -0
  31. data/lib/action_view/helpers/form_helper.rb +2765 -0
  32. data/lib/action_view/helpers/form_options_helper.rb +927 -0
  33. data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
  34. data/lib/action_view/helpers/javascript_helper.rb +96 -0
  35. data/lib/action_view/helpers/number_helper.rb +165 -0
  36. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  37. data/lib/action_view/helpers/rendering_helper.rb +218 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +201 -0
  39. data/lib/action_view/helpers/tag_helper.rb +621 -0
  40. data/lib/action_view/helpers/tags/base.rb +138 -0
  41. data/lib/action_view/helpers/tags/check_box.rb +65 -0
  42. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  43. data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
  44. data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
  45. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  46. data/lib/action_view/helpers/tags/collection_select.rb +33 -0
  47. data/lib/action_view/helpers/tags/color_field.rb +26 -0
  48. data/lib/action_view/helpers/tags/date_field.rb +14 -0
  49. data/lib/action_view/helpers/tags/date_select.rb +75 -0
  50. data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
  51. data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
  52. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  53. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  54. data/lib/action_view/helpers/tags/file_field.rb +26 -0
  55. data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
  56. data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/label.rb +84 -0
  58. data/lib/action_view/helpers/tags/month_field.rb +14 -0
  59. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  60. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  61. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  62. data/lib/action_view/helpers/tags/radio_button.rb +32 -0
  63. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  64. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  65. data/lib/action_view/helpers/tags/select.rb +45 -0
  66. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  67. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  68. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  69. data/lib/action_view/helpers/tags/text_field.rb +33 -0
  70. data/lib/action_view/helpers/tags/time_field.rb +23 -0
  71. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  72. data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
  73. data/lib/action_view/helpers/tags/translator.rb +39 -0
  74. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  75. data/lib/action_view/helpers/tags/week_field.rb +14 -0
  76. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  77. data/lib/action_view/helpers/tags.rb +47 -0
  78. data/lib/action_view/helpers/text_helper.rb +568 -0
  79. data/lib/action_view/helpers/translation_helper.rb +161 -0
  80. data/lib/action_view/helpers/url_helper.rb +812 -0
  81. data/lib/action_view/helpers.rb +68 -0
  82. data/lib/action_view/layouts.rb +434 -0
  83. data/lib/action_view/locale/en.yml +56 -0
  84. data/lib/action_view/log_subscriber.rb +132 -0
  85. data/lib/action_view/lookup_context.rb +299 -0
  86. data/lib/action_view/model_naming.rb +14 -0
  87. data/lib/action_view/path_registry.rb +57 -0
  88. data/lib/action_view/path_set.rb +84 -0
  89. data/lib/action_view/railtie.rb +132 -0
  90. data/lib/action_view/record_identifier.rb +118 -0
  91. data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
  92. data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
  93. data/lib/action_view/render_parser.rb +40 -0
  94. data/lib/action_view/renderer/abstract_renderer.rb +186 -0
  95. data/lib/action_view/renderer/collection_renderer.rb +204 -0
  96. data/lib/action_view/renderer/object_renderer.rb +34 -0
  97. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +267 -0
  99. data/lib/action_view/renderer/renderer.rb +107 -0
  100. data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
  101. data/lib/action_view/renderer/template_renderer.rb +115 -0
  102. data/lib/action_view/rendering.rb +190 -0
  103. data/lib/action_view/routing_url_for.rb +149 -0
  104. data/lib/action_view/tasks/cache_digests.rake +25 -0
  105. data/lib/action_view/template/error.rb +264 -0
  106. data/lib/action_view/template/handlers/builder.rb +25 -0
  107. data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
  108. data/lib/action_view/template/handlers/erb.rb +157 -0
  109. data/lib/action_view/template/handlers/html.rb +11 -0
  110. data/lib/action_view/template/handlers/raw.rb +11 -0
  111. data/lib/action_view/template/handlers.rb +66 -0
  112. data/lib/action_view/template/html.rb +33 -0
  113. data/lib/action_view/template/inline.rb +22 -0
  114. data/lib/action_view/template/raw_file.rb +25 -0
  115. data/lib/action_view/template/renderable.rb +30 -0
  116. data/lib/action_view/template/resolver.rb +212 -0
  117. data/lib/action_view/template/sources/file.rb +17 -0
  118. data/lib/action_view/template/sources.rb +13 -0
  119. data/lib/action_view/template/text.rb +32 -0
  120. data/lib/action_view/template/types.rb +50 -0
  121. data/lib/action_view/template.rb +580 -0
  122. data/lib/action_view/template_details.rb +66 -0
  123. data/lib/action_view/template_path.rb +66 -0
  124. data/lib/action_view/test_case.rb +449 -0
  125. data/lib/action_view/testing/resolvers.rb +44 -0
  126. data/lib/action_view/unbound_template.rb +67 -0
  127. data/lib/action_view/version.rb +10 -0
  128. data/lib/action_view/view_paths.rb +117 -0
  129. data/lib/action_view.rb +104 -0
  130. metadata +275 -0
@@ -0,0 +1,473 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+
5
+ module ActionView
6
+ module Helpers # :nodoc:
7
+ # = Action View Asset URL \Helpers
8
+ #
9
+ # This module provides methods for generating asset paths and
10
+ # URLs.
11
+ #
12
+ # image_path("rails.png")
13
+ # # => "/assets/rails.png"
14
+ #
15
+ # image_url("rails.png")
16
+ # # => "http://www.example.com/assets/rails.png"
17
+ #
18
+ # === Using asset hosts
19
+ #
20
+ # By default, \Rails links to these assets on the current host in the public
21
+ # folder, but you can direct \Rails to link to assets from a dedicated asset
22
+ # server by setting <tt>ActionController::Base.asset_host</tt> in the application
23
+ # configuration, typically in <tt>config/environments/production.rb</tt>.
24
+ # For example, you'd define <tt>assets.example.com</tt> to be your asset
25
+ # host this way, inside the <tt>configure</tt> block of your environment-specific
26
+ # configuration files or <tt>config/application.rb</tt>:
27
+ #
28
+ # config.action_controller.asset_host = "assets.example.com"
29
+ #
30
+ # Helpers take that into account:
31
+ #
32
+ # image_tag("rails.png")
33
+ # # => <img src="http://assets.example.com/assets/rails.png" />
34
+ # stylesheet_link_tag("application")
35
+ # # => <link href="http://assets.example.com/assets/application.css" rel="stylesheet" />
36
+ #
37
+ # Browsers open a limited number of simultaneous connections to a single
38
+ # host. The exact number varies by browser and version. This limit may cause
39
+ # some asset downloads to wait for previous assets to finish before they can
40
+ # begin. You can use the <tt>%d</tt> wildcard in the +asset_host+ to
41
+ # distribute the requests over four hosts. For example,
42
+ # <tt>assets%d.example.com</tt> will spread the asset requests over
43
+ # "assets0.example.com", ..., "assets3.example.com".
44
+ #
45
+ # image_tag("rails.png")
46
+ # # => <img src="http://assets0.example.com/assets/rails.png" />
47
+ # stylesheet_link_tag("application")
48
+ # # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
49
+ #
50
+ # This may improve the asset loading performance of your application.
51
+ # It is also possible the combination of additional connection overhead
52
+ # (DNS, SSL) and the overall browser connection limits may result in this
53
+ # solution being slower. You should be sure to measure your actual
54
+ # performance across targeted browsers both before and after this change.
55
+ #
56
+ # To implement the corresponding hosts you can either set up four actual
57
+ # hosts or use wildcard DNS to CNAME the wildcard to a single asset host.
58
+ # You can read more about setting up your DNS CNAME records from your ISP.
59
+ #
60
+ # Note: This is purely a browser performance optimization and is not meant
61
+ # for server load balancing. See https://www.die.net/musings/page_load_time/
62
+ # for background and https://www.browserscope.org/?category=network for
63
+ # connection limit data.
64
+ #
65
+ # Alternatively, you can exert more control over the asset host by setting
66
+ # +asset_host+ to a proc like this:
67
+ #
68
+ # ActionController::Base.asset_host = Proc.new { |source|
69
+ # "http://assets#{OpenSSL::Digest::SHA256.hexdigest(source).to_i(16) % 2 + 1}.example.com"
70
+ # }
71
+ # image_tag("rails.png")
72
+ # # => <img src="http://assets1.example.com/assets/rails.png" />
73
+ # stylesheet_link_tag("application")
74
+ # # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
75
+ #
76
+ # The example above generates "http://assets1.example.com" and
77
+ # "http://assets2.example.com". This option is useful for example if
78
+ # you need fewer/more than four hosts, custom host names, etc.
79
+ #
80
+ # As you see the proc takes a +source+ parameter. That's a string with the
81
+ # absolute path of the asset, for example "/assets/rails.png".
82
+ #
83
+ # ActionController::Base.asset_host = Proc.new { |source|
84
+ # if source.end_with?('.css')
85
+ # "http://stylesheets.example.com"
86
+ # else
87
+ # "http://assets.example.com"
88
+ # end
89
+ # }
90
+ # image_tag("rails.png")
91
+ # # => <img src="http://assets.example.com/assets/rails.png" />
92
+ # stylesheet_link_tag("application")
93
+ # # => <link href="http://stylesheets.example.com/assets/application.css" rel="stylesheet" />
94
+ #
95
+ # Alternatively you may ask for a second parameter +request+. That one is
96
+ # particularly useful for serving assets from an SSL-protected page. The
97
+ # example proc below disables asset hosting for HTTPS connections, while
98
+ # still sending assets for plain HTTP requests from asset hosts. If you don't
99
+ # have SSL certificates for each of the asset hosts this technique allows you
100
+ # to avoid warnings in the client about mixed media.
101
+ # Note that the +request+ parameter might not be supplied, e.g. when the assets
102
+ # are precompiled with the command <tt>bin/rails assets:precompile</tt>. Make sure to use a
103
+ # +Proc+ instead of a lambda, since a +Proc+ allows missing parameters and sets them
104
+ # to +nil+.
105
+ #
106
+ # config.action_controller.asset_host = Proc.new { |source, request|
107
+ # if request && request.ssl?
108
+ # "#{request.protocol}#{request.host_with_port}"
109
+ # else
110
+ # "#{request.protocol}assets.example.com"
111
+ # end
112
+ # }
113
+ #
114
+ # You can also implement a custom asset host object that responds to +call+
115
+ # and takes either one or two parameters just like the proc.
116
+ #
117
+ # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
118
+ # "http://asset%d.example.com", "https://asset1.example.com"
119
+ # )
120
+ #
121
+ module AssetUrlHelper
122
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}i
123
+
124
+ # This is the entry point for all assets.
125
+ # When using an asset pipeline gem (e.g. propshaft or sprockets-rails), the
126
+ # behavior is "enhanced". You can bypass the asset pipeline by passing in
127
+ # <tt>skip_pipeline: true</tt> to the options.
128
+ #
129
+ # All other asset *_path helpers delegate through this method.
130
+ #
131
+ # === With the asset pipeline
132
+ #
133
+ # All options passed to +asset_path+ will be passed to +compute_asset_path+
134
+ # which is implemented by asset pipeline gems.
135
+ #
136
+ # asset_path("application.js") # => "/assets/application-60aa4fdc5cea14baf5400fba1abf4f2a46a5166bad4772b1effe341570f07de9.js"
137
+ # asset_path('application.js', host: 'example.com') # => "//example.com/assets/application.js"
138
+ # asset_path("application.js", host: 'example.com', protocol: 'https') # => "https://example.com/assets/application.js"
139
+ #
140
+ # === Without the asset pipeline (<tt>skip_pipeline: true</tt>)
141
+ #
142
+ # Accepts a <tt>type</tt> option that can specify the asset's extension. No error
143
+ # checking is done to verify the source passed into +asset_path+ is valid
144
+ # and that the file exists on disk.
145
+ #
146
+ # asset_path("application.js", skip_pipeline: true) # => "application.js"
147
+ # asset_path("filedoesnotexist.png", skip_pipeline: true) # => "filedoesnotexist.png"
148
+ # asset_path("application", type: :javascript, skip_pipeline: true) # => "/javascripts/application.js"
149
+ # asset_path("application", type: :stylesheet, skip_pipeline: true) # => "/stylesheets/application.css"
150
+ #
151
+ # === Options applying to all assets
152
+ #
153
+ # Below lists scenarios that apply to +asset_path+ whether or not you're
154
+ # using the asset pipeline.
155
+ #
156
+ # - All fully qualified URLs are returned immediately. This bypasses the
157
+ # asset pipeline and all other behavior described.
158
+ #
159
+ # asset_path("http://www.example.com/js/xmlhr.js") # => "http://www.example.com/js/xmlhr.js"
160
+ #
161
+ # - All assets that begin with a forward slash are assumed to be full
162
+ # URLs and will not be expanded. This will bypass the asset pipeline.
163
+ #
164
+ # asset_path("/foo.png") # => "/foo.png"
165
+ #
166
+ # - All blank strings will be returned immediately. This bypasses the
167
+ # asset pipeline and all other behavior described.
168
+ #
169
+ # asset_path("") # => ""
170
+ #
171
+ # - If <tt>config.relative_url_root</tt> is specified, all assets will have that
172
+ # root prepended.
173
+ #
174
+ # Rails.application.config.relative_url_root = "bar"
175
+ # asset_path("foo.js", skip_pipeline: true) # => "bar/foo.js"
176
+ #
177
+ # - A different asset host can be specified via <tt>config.action_controller.asset_host</tt>
178
+ # this is commonly used in conjunction with a CDN.
179
+ #
180
+ # Rails.application.config.action_controller.asset_host = "assets.example.com"
181
+ # asset_path("foo.js", skip_pipeline: true) # => "http://assets.example.com/foo.js"
182
+ #
183
+ # - An extension name can be specified manually with <tt>extname</tt>.
184
+ #
185
+ # asset_path("foo", skip_pipeline: true, extname: ".js") # => "/foo.js"
186
+ # asset_path("foo.css", skip_pipeline: true, extname: ".js") # => "/foo.css.js"
187
+ def asset_path(source, options = {})
188
+ raise ArgumentError, "nil is not a valid asset source" if source.nil?
189
+
190
+ source = source.to_s
191
+ return "" if source.blank?
192
+ return source if URI_REGEXP.match?(source)
193
+
194
+ tail, source = source[/([?#].+)$/], source.sub(/([?#].+)$/, "")
195
+
196
+ if extname = compute_asset_extname(source, options)
197
+ source = "#{source}#{extname}"
198
+ end
199
+
200
+ unless source.start_with?(?/)
201
+ if options[:skip_pipeline]
202
+ source = public_compute_asset_path(source, options)
203
+ else
204
+ source = compute_asset_path(source, options)
205
+ end
206
+ end
207
+
208
+ relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
209
+ if relative_url_root
210
+ source = File.join(relative_url_root, source) unless source.start_with?("#{relative_url_root}/")
211
+ end
212
+
213
+ if host = compute_asset_host(source, options)
214
+ source = File.join(host, source)
215
+ end
216
+
217
+ "#{source}#{tail}"
218
+ end
219
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with an asset_path named route
220
+
221
+ # Computes the full URL to an asset in the public directory. This
222
+ # will use +asset_path+ internally, so most of their behaviors
223
+ # will be the same. If +:host+ options is set, it overwrites global
224
+ # +config.action_controller.asset_host+ setting.
225
+ #
226
+ # All other options provided are forwarded to +asset_path+ call.
227
+ #
228
+ # asset_url "application.js" # => http://example.com/assets/application.js
229
+ # asset_url "application.js", host: "http://cdn.example.com" # => http://cdn.example.com/assets/application.js
230
+ #
231
+ def asset_url(source, options = {})
232
+ path_to_asset(source, options.merge(protocol: :request))
233
+ end
234
+ alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
235
+
236
+ ASSET_EXTENSIONS = {
237
+ javascript: ".js",
238
+ stylesheet: ".css"
239
+ }
240
+
241
+ # Compute extname to append to asset path. Returns +nil+ if
242
+ # nothing should be added.
243
+ def compute_asset_extname(source, options = {})
244
+ return if options[:extname] == false
245
+ extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
246
+ if extname && File.extname(source) != extname
247
+ extname
248
+ else
249
+ nil
250
+ end
251
+ end
252
+
253
+ # Maps asset types to public directory.
254
+ ASSET_PUBLIC_DIRECTORIES = {
255
+ audio: "/audios",
256
+ font: "/fonts",
257
+ image: "/images",
258
+ javascript: "/javascripts",
259
+ stylesheet: "/stylesheets",
260
+ video: "/videos"
261
+ }
262
+
263
+ # Computes asset path to public directory. Plugins and
264
+ # extensions can override this method to point to custom assets
265
+ # or generate digested paths or query strings.
266
+ def compute_asset_path(source, options = {})
267
+ dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
268
+ File.join(dir, source)
269
+ end
270
+ alias :public_compute_asset_path :compute_asset_path
271
+
272
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
273
+ # the host if no wildcard is set, the host interpolated with the
274
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
275
+ # or the value returned from invoking call on an object responding to call
276
+ # (proc or otherwise).
277
+ def compute_asset_host(source = "", options = {})
278
+ request = self.request if respond_to?(:request)
279
+ host = options[:host]
280
+ host ||= config.asset_host if defined? config.asset_host
281
+
282
+ if host
283
+ if host.respond_to?(:call)
284
+ arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
285
+ args = [source]
286
+ args << request if request && (arity > 1 || arity < 0)
287
+ host = host.call(*args)
288
+ elsif host.include?("%d")
289
+ host = host % (Zlib.crc32(source) % 4)
290
+ end
291
+ end
292
+
293
+ host ||= request.base_url if request && options[:protocol] == :request
294
+ return unless host
295
+
296
+ if URI_REGEXP.match?(host)
297
+ host
298
+ else
299
+ protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
300
+ case protocol
301
+ when :relative
302
+ "//#{host}"
303
+ when :request
304
+ "#{request.protocol}#{host}"
305
+ else
306
+ "#{protocol}://#{host}"
307
+ end
308
+ end
309
+ end
310
+
311
+ # Computes the path to a JavaScript asset in the public javascripts directory.
312
+ # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
313
+ # Full paths from the document root will be passed through.
314
+ # Used internally by +javascript_include_tag+ to build the script path.
315
+ #
316
+ # javascript_path "xmlhr" # => /assets/xmlhr.js
317
+ # javascript_path "dir/xmlhr.js" # => /assets/dir/xmlhr.js
318
+ # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
319
+ # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
320
+ # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
321
+ def javascript_path(source, options = {})
322
+ path_to_asset(source, { type: :javascript }.merge!(options))
323
+ end
324
+ alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
325
+
326
+ # Computes the full URL to a JavaScript asset in the public javascripts directory.
327
+ # This will use +javascript_path+ internally, so most of their behaviors will be the same.
328
+ # Since +javascript_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
329
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
330
+ #
331
+ # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/js/xmlhr.js
332
+ #
333
+ def javascript_url(source, options = {})
334
+ url_to_asset(source, { type: :javascript }.merge!(options))
335
+ end
336
+ alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
337
+
338
+ # Computes the path to a stylesheet asset in the public stylesheets directory.
339
+ # If the +source+ filename has no extension, .css will be appended (except for explicit URIs).
340
+ # Full paths from the document root will be passed through.
341
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
342
+ #
343
+ # stylesheet_path "style" # => /assets/style.css
344
+ # stylesheet_path "dir/style.css" # => /assets/dir/style.css
345
+ # stylesheet_path "/dir/style.css" # => /dir/style.css
346
+ # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
347
+ # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
348
+ def stylesheet_path(source, options = {})
349
+ path_to_asset(source, { type: :stylesheet }.merge!(options))
350
+ end
351
+ alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
352
+
353
+ # Computes the full URL to a stylesheet asset in the public stylesheets directory.
354
+ # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
355
+ # Since +stylesheet_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
356
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
357
+ #
358
+ # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/assets/css/style.css
359
+ #
360
+ def stylesheet_url(source, options = {})
361
+ url_to_asset(source, { type: :stylesheet }.merge!(options))
362
+ end
363
+ alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
364
+
365
+ # Computes the path to an image asset.
366
+ # Full paths from the document root will be passed through.
367
+ # Used internally by +image_tag+ to build the image path:
368
+ #
369
+ # image_path("edit") # => "/assets/edit"
370
+ # image_path("edit.png") # => "/assets/edit.png"
371
+ # image_path("icons/edit.png") # => "/assets/icons/edit.png"
372
+ # image_path("/icons/edit.png") # => "/icons/edit.png"
373
+ # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
374
+ #
375
+ # If you have images as application resources this method may conflict with their named routes.
376
+ # The alias +path_to_image+ is provided to avoid that. \Rails uses the alias internally, and
377
+ # plugin authors are encouraged to do so.
378
+ def image_path(source, options = {})
379
+ path_to_asset(source, { type: :image }.merge!(options))
380
+ end
381
+ alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
382
+
383
+ # Computes the full URL to an image asset.
384
+ # This will use +image_path+ internally, so most of their behaviors will be the same.
385
+ # Since +image_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
386
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
387
+ #
388
+ # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/assets/edit.png
389
+ #
390
+ def image_url(source, options = {})
391
+ url_to_asset(source, { type: :image }.merge!(options))
392
+ end
393
+ alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
394
+
395
+ # Computes the path to a video asset in the public videos directory.
396
+ # Full paths from the document root will be passed through.
397
+ # Used internally by +video_tag+ to build the video path.
398
+ #
399
+ # video_path("hd") # => /videos/hd
400
+ # video_path("hd.avi") # => /videos/hd.avi
401
+ # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
402
+ # video_path("/trailers/hd.avi") # => /trailers/hd.avi
403
+ # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
404
+ def video_path(source, options = {})
405
+ path_to_asset(source, { type: :video }.merge!(options))
406
+ end
407
+ alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
408
+
409
+ # Computes the full URL to a video asset in the public videos directory.
410
+ # This will use +video_path+ internally, so most of their behaviors will be the same.
411
+ # Since +video_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
412
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
413
+ #
414
+ # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/videos/hd.avi
415
+ #
416
+ def video_url(source, options = {})
417
+ url_to_asset(source, { type: :video }.merge!(options))
418
+ end
419
+ alias_method :url_to_video, :video_url # aliased to avoid conflicts with a video_url named route
420
+
421
+ # Computes the path to an audio asset in the public audios directory.
422
+ # Full paths from the document root will be passed through.
423
+ # Used internally by +audio_tag+ to build the audio path.
424
+ #
425
+ # audio_path("horse") # => /audios/horse
426
+ # audio_path("horse.wav") # => /audios/horse.wav
427
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
428
+ # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
429
+ # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
430
+ def audio_path(source, options = {})
431
+ path_to_asset(source, { type: :audio }.merge!(options))
432
+ end
433
+ alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
434
+
435
+ # Computes the full URL to an audio asset in the public audios directory.
436
+ # This will use +audio_path+ internally, so most of their behaviors will be the same.
437
+ # Since +audio_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
438
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
439
+ #
440
+ # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/audios/horse.wav
441
+ #
442
+ def audio_url(source, options = {})
443
+ url_to_asset(source, { type: :audio }.merge!(options))
444
+ end
445
+ alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
446
+
447
+ # Computes the path to a font asset.
448
+ # Full paths from the document root will be passed through.
449
+ #
450
+ # font_path("font") # => /fonts/font
451
+ # font_path("font.ttf") # => /fonts/font.ttf
452
+ # font_path("dir/font.ttf") # => /fonts/dir/font.ttf
453
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
454
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
455
+ def font_path(source, options = {})
456
+ path_to_asset(source, { type: :font }.merge!(options))
457
+ end
458
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with a font_path named route
459
+
460
+ # Computes the full URL to a font asset.
461
+ # This will use +font_path+ internally, so most of their behaviors will be the same.
462
+ # Since +font_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
463
+ # options is set, it overwrites global +config.action_controller.asset_host+ setting.
464
+ #
465
+ # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/fonts/font.ttf
466
+ #
467
+ def font_url(source, options = {})
468
+ url_to_asset(source, { type: :font }.merge!(options))
469
+ end
470
+ alias_method :url_to_font, :font_url # aliased to avoid conflicts with a font_url named route
471
+ end
472
+ end
473
+ end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module ActionView
6
+ module Helpers # :nodoc:
7
+ # = Action View Atom Feed \Helpers
8
+ module AtomFeedHelper
9
+ # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
10
+ # template languages).
11
+ #
12
+ # Full usage example:
13
+ #
14
+ # config/routes.rb:
15
+ # Rails.application.routes.draw do
16
+ # resources :posts
17
+ # root to: "posts#index"
18
+ # end
19
+ #
20
+ # app/controllers/posts_controller.rb:
21
+ # class PostsController < ApplicationController
22
+ # # GET /posts.html
23
+ # # GET /posts.atom
24
+ # def index
25
+ # @posts = Post.all
26
+ #
27
+ # respond_to do |format|
28
+ # format.html
29
+ # format.atom
30
+ # end
31
+ # end
32
+ # end
33
+ #
34
+ # app/views/posts/index.atom.builder:
35
+ # atom_feed do |feed|
36
+ # feed.title("My great blog!")
37
+ # feed.updated(@posts[0].created_at) if @posts.length > 0
38
+ #
39
+ # @posts.each do |post|
40
+ # feed.entry(post) do |entry|
41
+ # entry.title(post.title)
42
+ # entry.content(post.body, type: 'html')
43
+ #
44
+ # entry.author do |author|
45
+ # author.name("DHH")
46
+ # end
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # The options for atom_feed are:
52
+ #
53
+ # * <tt>:language</tt>: Defaults to "en-US".
54
+ # * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
55
+ # * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
56
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:localhost,2005:/posts", in this case.
57
+ # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
58
+ # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
59
+ # 2005 is used (as an "I don't care" value).
60
+ # * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
61
+ #
62
+ # Other namespaces can be added to the root element:
63
+ #
64
+ # app/views/posts/index.atom.builder:
65
+ # atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
66
+ # 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
67
+ # feed.title("My great blog!")
68
+ # feed.updated((@posts.first.created_at))
69
+ # feed.tag!('openSearch:totalResults', 10)
70
+ #
71
+ # @posts.each do |post|
72
+ # feed.entry(post) do |entry|
73
+ # entry.title(post.title)
74
+ # entry.content(post.body, type: 'html')
75
+ # entry.tag!('app:edited', Time.now)
76
+ #
77
+ # entry.author do |author|
78
+ # author.name("DHH")
79
+ # end
80
+ # end
81
+ # end
82
+ # end
83
+ #
84
+ # The Atom spec defines five elements (content rights title subtitle
85
+ # summary) which may directly contain XHTML content if type: 'xhtml'
86
+ # is specified as an attribute. If so, this helper will take care of
87
+ # the enclosing div and XHTML namespace declaration. Example usage:
88
+ #
89
+ # entry.summary type: 'xhtml' do |xhtml|
90
+ # xhtml.p pluralize(order.line_items.count, "line item")
91
+ # xhtml.p "Shipped to #{order.address}"
92
+ # xhtml.p "Paid by #{order.pay_type}"
93
+ # end
94
+ #
95
+ #
96
+ # <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield
97
+ # an +AtomBuilder+ instance.
98
+ def atom_feed(options = {}, &block)
99
+ if options[:schema_date]
100
+ options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
101
+ else
102
+ options[:schema_date] = "2005" # The Atom spec copyright date
103
+ end
104
+
105
+ xml = options.delete(:xml) || block.binding.local_variable_get(:xml)
106
+ xml.instruct!
107
+ if options[:instruct]
108
+ options[:instruct].each do |target, attrs|
109
+ if attrs.respond_to?(:keys)
110
+ xml.instruct!(target, attrs)
111
+ elsif attrs.respond_to?(:each)
112
+ attrs.each { |attr_group| xml.instruct!(target, attr_group) }
113
+ end
114
+ end
115
+ end
116
+
117
+ feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
118
+ feed_opts.merge!(options).select! { |k, _| k.start_with?("xml") }
119
+
120
+ xml.feed(feed_opts) do
121
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
122
+ xml.link(rel: "alternate", type: "text/html", href: options[:root_url] || (request.protocol + request.host_with_port))
123
+ xml.link(rel: "self", type: "application/atom+xml", href: options[:url] || request.url)
124
+
125
+ yield AtomFeedBuilder.new(xml, self, options)
126
+ end
127
+ end
128
+
129
+ class AtomBuilder # :nodoc:
130
+ XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
131
+
132
+ def initialize(xml)
133
+ @xml = xml
134
+ end
135
+
136
+ private
137
+ # Delegate to XML Builder, first wrapping the element in an XHTML
138
+ # namespaced div element if the method and arguments indicate
139
+ # that an xhtml_block? is desired.
140
+ def method_missing(method, *arguments, &block)
141
+ if xhtml_block?(method, arguments)
142
+ @xml.__send__(method, *arguments) do
143
+ @xml.div(xmlns: "http://www.w3.org/1999/xhtml") do |xhtml|
144
+ block.call(xhtml)
145
+ end
146
+ end
147
+ else
148
+ @xml.__send__(method, *arguments, &block)
149
+ end
150
+ end
151
+
152
+ # True if the method name matches one of the five elements defined
153
+ # in the Atom spec as potentially containing XHTML content and
154
+ # if type: 'xhtml' is, in fact, specified.
155
+ def xhtml_block?(method, arguments)
156
+ if XHTML_TAG_NAMES.include?(method.to_s)
157
+ last = arguments.last
158
+ last.is_a?(Hash) && last[:type].to_s == "xhtml"
159
+ end
160
+ end
161
+ end
162
+
163
+ class AtomFeedBuilder < AtomBuilder # :nodoc:
164
+ def initialize(xml, view, feed_options = {})
165
+ @xml, @view, @feed_options = xml, view, feed_options
166
+ end
167
+
168
+ # Accepts a Date or Time object and inserts it in the proper format. If +nil+ is passed, current time in UTC is used.
169
+ def updated(date_or_time = nil)
170
+ @xml.updated((date_or_time || Time.now.utc).xmlschema)
171
+ end
172
+
173
+ # Creates an entry tag for a specific record and prefills the id using class and id.
174
+ #
175
+ # Options:
176
+ #
177
+ # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
178
+ # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
179
+ # * <tt>:url</tt>: The URL for this entry or +false+ or +nil+ for not having a link tag. Defaults to the +polymorphic_url+ for the record.
180
+ # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
181
+ # * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
182
+ def entry(record, options = {})
183
+ @xml.entry do
184
+ @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
185
+
186
+ if options[:published] || (record.respond_to?(:created_at) && record.created_at)
187
+ @xml.published((options[:published] || record.created_at).xmlschema)
188
+ end
189
+
190
+ if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
191
+ @xml.updated((options[:updated] || record.updated_at).xmlschema)
192
+ end
193
+
194
+ type = options.fetch(:type, "text/html")
195
+
196
+ url = options.fetch(:url) { @view.polymorphic_url(record) }
197
+ @xml.link(rel: "alternate", type: type, href: url) if url
198
+
199
+ yield AtomBuilder.new(@xml)
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end