actionview 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +274 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +34 -0
  5. data/lib/action_view.rb +97 -0
  6. data/lib/action_view/base.rb +205 -0
  7. data/lib/action_view/buffers.rb +49 -0
  8. data/lib/action_view/context.rb +36 -0
  9. data/lib/action_view/dependency_tracker.rb +93 -0
  10. data/lib/action_view/digestor.rb +116 -0
  11. data/lib/action_view/flows.rb +76 -0
  12. data/lib/action_view/helpers.rb +64 -0
  13. data/lib/action_view/helpers/active_model_helper.rb +49 -0
  14. data/lib/action_view/helpers/asset_tag_helper.rb +322 -0
  15. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  16. data/lib/action_view/helpers/atom_feed_helper.rb +203 -0
  17. data/lib/action_view/helpers/cache_helper.rb +200 -0
  18. data/lib/action_view/helpers/capture_helper.rb +216 -0
  19. data/lib/action_view/helpers/controller_helper.rb +25 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +30 -0
  21. data/lib/action_view/helpers/date_helper.rb +1075 -0
  22. data/lib/action_view/helpers/debug_helper.rb +39 -0
  23. data/lib/action_view/helpers/form_helper.rb +1876 -0
  24. data/lib/action_view/helpers/form_options_helper.rb +843 -0
  25. data/lib/action_view/helpers/form_tag_helper.rb +746 -0
  26. data/lib/action_view/helpers/javascript_helper.rb +75 -0
  27. data/lib/action_view/helpers/number_helper.rb +425 -0
  28. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  29. data/lib/action_view/helpers/record_tag_helper.rb +108 -0
  30. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  31. data/lib/action_view/helpers/sanitize_helper.rb +256 -0
  32. data/lib/action_view/helpers/tag_helper.rb +176 -0
  33. data/lib/action_view/helpers/tags.rb +41 -0
  34. data/lib/action_view/helpers/tags/base.rb +148 -0
  35. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  36. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  37. data/lib/action_view/helpers/tags/collection_check_boxes.rb +44 -0
  38. data/lib/action_view/helpers/tags/collection_helpers.rb +85 -0
  39. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  40. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  41. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  42. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  43. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  44. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  45. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  46. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  47. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  48. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  50. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  51. data/lib/action_view/helpers/tags/label.rb +65 -0
  52. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  53. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  54. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  56. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  58. data/lib/action_view/helpers/tags/select.rb +41 -0
  59. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  61. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  62. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  65. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  66. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  67. data/lib/action_view/helpers/text_helper.rb +447 -0
  68. data/lib/action_view/helpers/translation_helper.rb +111 -0
  69. data/lib/action_view/helpers/url_helper.rb +625 -0
  70. data/lib/action_view/layouts.rb +426 -0
  71. data/lib/action_view/locale/en.yml +56 -0
  72. data/lib/action_view/log_subscriber.rb +44 -0
  73. data/lib/action_view/lookup_context.rb +249 -0
  74. data/lib/action_view/model_naming.rb +12 -0
  75. data/lib/action_view/path_set.rb +77 -0
  76. data/lib/action_view/railtie.rb +49 -0
  77. data/lib/action_view/record_identifier.rb +84 -0
  78. data/lib/action_view/renderer/abstract_renderer.rb +47 -0
  79. data/lib/action_view/renderer/partial_renderer.rb +492 -0
  80. data/lib/action_view/renderer/renderer.rb +50 -0
  81. data/lib/action_view/renderer/streaming_template_renderer.rb +103 -0
  82. data/lib/action_view/renderer/template_renderer.rb +96 -0
  83. data/lib/action_view/rendering.rb +145 -0
  84. data/lib/action_view/routing_url_for.rb +109 -0
  85. data/lib/action_view/tasks/dependencies.rake +17 -0
  86. data/lib/action_view/template.rb +340 -0
  87. data/lib/action_view/template/error.rb +141 -0
  88. data/lib/action_view/template/handlers.rb +53 -0
  89. data/lib/action_view/template/handlers/builder.rb +26 -0
  90. data/lib/action_view/template/handlers/erb.rb +145 -0
  91. data/lib/action_view/template/handlers/raw.rb +11 -0
  92. data/lib/action_view/template/resolver.rb +329 -0
  93. data/lib/action_view/template/text.rb +34 -0
  94. data/lib/action_view/template/types.rb +57 -0
  95. data/lib/action_view/test_case.rb +272 -0
  96. data/lib/action_view/testing/resolvers.rb +50 -0
  97. data/lib/action_view/vendor/html-scanner.rb +20 -0
  98. data/lib/action_view/vendor/html-scanner/html/document.rb +68 -0
  99. data/lib/action_view/vendor/html-scanner/html/node.rb +532 -0
  100. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +188 -0
  101. data/lib/action_view/vendor/html-scanner/html/selector.rb +830 -0
  102. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +107 -0
  103. data/lib/action_view/vendor/html-scanner/html/version.rb +11 -0
  104. data/lib/action_view/version.rb +11 -0
  105. data/lib/action_view/view_paths.rb +96 -0
  106. metadata +218 -0
@@ -0,0 +1,49 @@
1
+ require 'active_support/core_ext/string/output_safety'
2
+
3
+ module ActionView
4
+ class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
5
+ def initialize(*)
6
+ super
7
+ encode!
8
+ end
9
+
10
+ def <<(value)
11
+ return self if value.nil?
12
+ super(value.to_s)
13
+ end
14
+ alias :append= :<<
15
+
16
+ def safe_concat(value)
17
+ return self if value.nil?
18
+ super(value.to_s)
19
+ end
20
+ alias :safe_append= :safe_concat
21
+ end
22
+
23
+ class StreamingBuffer #:nodoc:
24
+ def initialize(block)
25
+ @block = block
26
+ end
27
+
28
+ def <<(value)
29
+ value = value.to_s
30
+ value = ERB::Util.h(value) unless value.html_safe?
31
+ @block.call(value)
32
+ end
33
+ alias :concat :<<
34
+ alias :append= :<<
35
+
36
+ def safe_concat(value)
37
+ @block.call(value.to_s)
38
+ end
39
+ alias :safe_append= :safe_concat
40
+
41
+ def html_safe?
42
+ true
43
+ end
44
+
45
+ def html_safe
46
+ self
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ module ActionView
2
+ module CompiledTemplates #:nodoc:
3
+ # holds compiled template code
4
+ end
5
+
6
+ # = Action View Context
7
+ #
8
+ # Action View contexts are supplied to Action Controller to render a template.
9
+ # The default Action View context is ActionView::Base.
10
+ #
11
+ # In order to work with ActionController, a Context must just include this module.
12
+ # The initialization of the variables used by the context (@output_buffer, @view_flow,
13
+ # and @virtual_path) is responsibility of the object that includes this module
14
+ # (although you can call _prepare_context defined below).
15
+ module Context
16
+ include CompiledTemplates
17
+ attr_accessor :output_buffer, :view_flow
18
+
19
+ # Prepares the context by setting the appropriate instance variables.
20
+ # :api: plugin
21
+ def _prepare_context
22
+ @view_flow = OutputFlow.new
23
+ @output_buffer = nil
24
+ @virtual_path = nil
25
+ end
26
+
27
+ # Encapsulates the interaction with the view flow so it
28
+ # returns the correct buffer on +yield+. This is usually
29
+ # overwritten by helpers to add more behavior.
30
+ # :api: plugin
31
+ def _layout_for(name=nil)
32
+ name ||= :layout
33
+ view_flow.get(name).html_safe
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,93 @@
1
+ require 'thread_safe'
2
+
3
+ module ActionView
4
+ class DependencyTracker
5
+ @trackers = ThreadSafe::Cache.new
6
+
7
+ def self.find_dependencies(name, template)
8
+ tracker = @trackers[template.handler]
9
+
10
+ if tracker.present?
11
+ tracker.call(name, template)
12
+ else
13
+ []
14
+ end
15
+ end
16
+
17
+ def self.register_tracker(extension, tracker)
18
+ handler = Template.handler_for_extension(extension)
19
+ @trackers[handler] = tracker
20
+ end
21
+
22
+ def self.remove_tracker(handler)
23
+ @trackers.delete(handler)
24
+ end
25
+
26
+ class ERBTracker
27
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
28
+
29
+ # Matches:
30
+ # render partial: "comments/comment", collection: commentable.comments
31
+ # render "comments/comments"
32
+ # render 'comments/comments'
33
+ # render('comments/comments')
34
+ #
35
+ # render(@topic) => render("topics/topic")
36
+ # render(topics) => render("topics/topic")
37
+ # render(message.topics) => render("topics/topic")
38
+ RENDER_DEPENDENCY = /
39
+ render\s* # render, followed by optional whitespace
40
+ \(? # start an optional parenthesis for the render call
41
+ (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
42
+ ([@a-z"'][@\w\/\."']+) # the template name itself -- 2nd capture
43
+ /x
44
+
45
+ def self.call(name, template)
46
+ new(name, template).dependencies
47
+ end
48
+
49
+ def initialize(name, template)
50
+ @name, @template = name, template
51
+ end
52
+
53
+ def dependencies
54
+ render_dependencies + explicit_dependencies
55
+ end
56
+
57
+ attr_reader :name, :template
58
+ private :name, :template
59
+
60
+ private
61
+
62
+ def source
63
+ template.source
64
+ end
65
+
66
+ def directory
67
+ name.split("/")[0..-2].join("/")
68
+ end
69
+
70
+ def render_dependencies
71
+ source.scan(RENDER_DEPENDENCY).
72
+ collect(&:second).uniq.
73
+
74
+ # render(@topic) => render("topics/topic")
75
+ # render(topics) => render("topics/topic")
76
+ # render(message.topics) => render("topics/topic")
77
+ collect { |name| name.sub(/\A@?([a-z_]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
78
+
79
+ # render("headline") => render("message/headline")
80
+ collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
81
+
82
+ # replace quotes from string renders
83
+ collect { |name| name.gsub(/["']/, "") }
84
+ end
85
+
86
+ def explicit_dependencies
87
+ source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
88
+ end
89
+ end
90
+
91
+ register_tracker :erb, ERBTracker
92
+ end
93
+ end
@@ -0,0 +1,116 @@
1
+ require 'thread_safe'
2
+ require 'action_view/dependency_tracker'
3
+ require 'monitor'
4
+
5
+ module ActionView
6
+ class Digestor
7
+ cattr_reader(:cache)
8
+ @@cache = ThreadSafe::Cache.new
9
+ @@digest_monitor = Monitor.new
10
+
11
+ class << self
12
+ def digest(name, format, finder, options = {})
13
+ details_key = finder.details_key.hash
14
+ dependencies = Array.wrap(options[:dependencies])
15
+ cache_key = ([name, details_key, format] + dependencies).join('.')
16
+
17
+ # this is a correctly done double-checked locking idiom
18
+ # (ThreadSafe::Cache's lookups have volatile semantics)
19
+ @@cache[cache_key] || @@digest_monitor.synchronize do
20
+ @@cache.fetch(cache_key) do # re-check under lock
21
+ compute_and_store_digest(cache_key, name, format, finder, options)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+ def compute_and_store_digest(cache_key, name, format, finder, options) # called under @@digest_monitor lock
28
+ klass = if options[:partial] || name.include?("/_")
29
+ # Prevent re-entry or else recursive templates will blow the stack.
30
+ # There is no need to worry about other threads seeing the +false+ value,
31
+ # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
32
+ pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
33
+ PartialDigestor
34
+ else
35
+ Digestor
36
+ end
37
+
38
+ digest = klass.new(name, format, finder, options).digest
39
+ # Store the actual digest if config.cache_template_loading is true
40
+ @@cache[cache_key] = stored_digest = digest if ActionView::Resolver.caching?
41
+ digest
42
+ ensure
43
+ # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
44
+ @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
45
+ end
46
+ end
47
+
48
+ attr_reader :name, :format, :finder, :options
49
+
50
+ def initialize(name, format, finder, options={})
51
+ @name, @format, @finder, @options = name, format, finder, options
52
+ end
53
+
54
+ def digest
55
+ Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
56
+ logger.try :info, "Cache digest for #{name}.#{format}: #{digest}"
57
+ end
58
+ rescue ActionView::MissingTemplate
59
+ logger.try :error, "Couldn't find template for digesting: #{name}.#{format}"
60
+ ''
61
+ end
62
+
63
+ def dependencies
64
+ DependencyTracker.find_dependencies(name, template)
65
+ rescue ActionView::MissingTemplate
66
+ [] # File doesn't exist, so no dependencies
67
+ end
68
+
69
+ def nested_dependencies
70
+ dependencies.collect do |dependency|
71
+ dependencies = PartialDigestor.new(dependency, format, finder).nested_dependencies
72
+ dependencies.any? ? { dependency => dependencies } : dependency
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def logger
79
+ ActionView::Base.logger
80
+ end
81
+
82
+ def logical_name
83
+ name.gsub(%r|/_|, "/")
84
+ end
85
+
86
+ def partial?
87
+ false
88
+ end
89
+
90
+ def template
91
+ @template ||= finder.find(logical_name, [], partial?, formats: [ format ])
92
+ end
93
+
94
+ def source
95
+ template.source
96
+ end
97
+
98
+ def dependency_digest
99
+ template_digests = dependencies.collect do |template_name|
100
+ Digestor.digest(template_name, format, finder, partial: true)
101
+ end
102
+
103
+ (template_digests + injected_dependencies).join("-")
104
+ end
105
+
106
+ def injected_dependencies
107
+ Array.wrap(options[:dependencies])
108
+ end
109
+ end
110
+
111
+ class PartialDigestor < Digestor # :nodoc:
112
+ def partial?
113
+ true
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,76 @@
1
+ require 'active_support/core_ext/string/output_safety'
2
+
3
+ module ActionView
4
+ class OutputFlow #:nodoc:
5
+ attr_reader :content
6
+
7
+ def initialize
8
+ @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
9
+ end
10
+
11
+ # Called by _layout_for to read stored values.
12
+ def get(key)
13
+ @content[key]
14
+ end
15
+
16
+ # Called by each renderer object to set the layout contents.
17
+ def set(key, value)
18
+ @content[key] = value
19
+ end
20
+
21
+ # Called by content_for
22
+ def append(key, value)
23
+ @content[key] << value
24
+ end
25
+ alias_method :append!, :append
26
+
27
+ end
28
+
29
+ class StreamingFlow < OutputFlow #:nodoc:
30
+ def initialize(view, fiber)
31
+ @view = view
32
+ @parent = nil
33
+ @child = view.output_buffer
34
+ @content = view.view_flow.content
35
+ @fiber = fiber
36
+ @root = Fiber.current.object_id
37
+ end
38
+
39
+ # Try to get stored content. If the content
40
+ # is not available and we are inside the layout
41
+ # fiber, we set that we are waiting for the given
42
+ # key and yield.
43
+ def get(key)
44
+ return super if @content.key?(key)
45
+
46
+ if inside_fiber?
47
+ view = @view
48
+
49
+ begin
50
+ @waiting_for = key
51
+ view.output_buffer, @parent = @child, view.output_buffer
52
+ Fiber.yield
53
+ ensure
54
+ @waiting_for = nil
55
+ view.output_buffer, @child = @parent, view.output_buffer
56
+ end
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ # Appends the contents for the given key. This is called
63
+ # by provides and resumes back to the fiber if it is
64
+ # the key it is waiting for.
65
+ def append!(key, value)
66
+ super
67
+ @fiber.resume if @waiting_for == key
68
+ end
69
+
70
+ private
71
+
72
+ def inside_fiber?
73
+ Fiber.current.object_id != @root
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,64 @@
1
+ require 'active_support/benchmarkable'
2
+
3
+ module ActionView #:nodoc:
4
+ module Helpers #:nodoc:
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :ActiveModelHelper
8
+ autoload :AssetTagHelper
9
+ autoload :AssetUrlHelper
10
+ autoload :AtomFeedHelper
11
+ autoload :CacheHelper
12
+ autoload :CaptureHelper
13
+ autoload :ControllerHelper
14
+ autoload :CsrfHelper
15
+ autoload :DateHelper
16
+ autoload :DebugHelper
17
+ autoload :FormHelper
18
+ autoload :FormOptionsHelper
19
+ autoload :FormTagHelper
20
+ autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
21
+ autoload :NumberHelper
22
+ autoload :OutputSafetyHelper
23
+ autoload :RecordTagHelper
24
+ autoload :RenderingHelper
25
+ autoload :SanitizeHelper
26
+ autoload :TagHelper
27
+ autoload :TextHelper
28
+ autoload :TranslationHelper
29
+ autoload :UrlHelper
30
+ autoload :Tags
31
+
32
+ def self.eager_load!
33
+ super
34
+ Tags.eager_load!
35
+ end
36
+
37
+ extend ActiveSupport::Concern
38
+
39
+ include ActiveSupport::Benchmarkable
40
+ include ActiveModelHelper
41
+ include AssetTagHelper
42
+ include AssetUrlHelper
43
+ include AtomFeedHelper
44
+ include CacheHelper
45
+ include CaptureHelper
46
+ include ControllerHelper
47
+ include CsrfHelper
48
+ include DateHelper
49
+ include DebugHelper
50
+ include FormHelper
51
+ include FormOptionsHelper
52
+ include FormTagHelper
53
+ include JavaScriptHelper
54
+ include NumberHelper
55
+ include OutputSafetyHelper
56
+ include RecordTagHelper
57
+ include RenderingHelper
58
+ include SanitizeHelper
59
+ include TagHelper
60
+ include TextHelper
61
+ include TranslationHelper
62
+ include UrlHelper
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'active_support/core_ext/enumerable'
3
+
4
+ module ActionView
5
+ # = Active Model Helpers
6
+ module Helpers
7
+ module ActiveModelHelper
8
+ end
9
+
10
+ module ActiveModelInstanceTag
11
+ def object
12
+ @active_model_object ||= begin
13
+ object = super
14
+ object.respond_to?(:to_model) ? object.to_model : object
15
+ end
16
+ end
17
+
18
+ def content_tag(*)
19
+ error_wrapping(super)
20
+ end
21
+
22
+ def tag(type, options, *)
23
+ tag_generate_errors?(options) ? error_wrapping(super) : super
24
+ end
25
+
26
+ def error_wrapping(html_tag)
27
+ if object_has_errors?
28
+ Base.field_error_proc.call(html_tag, self)
29
+ else
30
+ html_tag
31
+ end
32
+ end
33
+
34
+ def error_message
35
+ object.errors[@method_name]
36
+ end
37
+
38
+ private
39
+
40
+ def object_has_errors?
41
+ object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
42
+ end
43
+
44
+ def tag_generate_errors?(options)
45
+ options['type'] != 'hidden'
46
+ end
47
+ end
48
+ end
49
+ end