actionpack 2.0.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +149 -7
- data/MIT-LICENSE +1 -1
- data/README +1 -1
- data/Rakefile +5 -6
- data/lib/action_controller.rb +2 -2
- data/lib/action_controller/assertions/model_assertions.rb +2 -1
- data/lib/action_controller/assertions/response_assertions.rb +4 -2
- data/lib/action_controller/assertions/routing_assertions.rb +3 -3
- data/lib/action_controller/assertions/selector_assertions.rb +30 -27
- data/lib/action_controller/assertions/tag_assertions.rb +3 -3
- data/lib/action_controller/base.rb +103 -129
- data/lib/action_controller/benchmarking.rb +3 -3
- data/lib/action_controller/caching.rb +41 -652
- data/lib/action_controller/caching/actions.rb +144 -0
- data/lib/action_controller/caching/fragments.rb +138 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sql_cache.rb +18 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/cgi_ext/cookie.rb +27 -23
- data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
- data/lib/action_controller/cgi_process.rb +6 -4
- data/lib/action_controller/components.rb +7 -6
- data/lib/action_controller/cookies.rb +31 -19
- data/lib/action_controller/dispatcher.rb +51 -84
- data/lib/action_controller/filters.rb +295 -421
- data/lib/action_controller/flash.rb +1 -6
- data/lib/action_controller/headers.rb +31 -0
- data/lib/action_controller/helpers.rb +26 -9
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +65 -13
- data/lib/action_controller/layout.rb +24 -39
- data/lib/action_controller/mime_responds.rb +7 -3
- data/lib/action_controller/mime_type.rb +25 -9
- data/lib/action_controller/mime_types.rb +1 -1
- data/lib/action_controller/polymorphic_routes.rb +32 -17
- data/lib/action_controller/record_identifier.rb +10 -4
- data/lib/action_controller/request.rb +46 -30
- data/lib/action_controller/request_forgery_protection.rb +10 -9
- data/lib/action_controller/request_profiler.rb +29 -8
- data/lib/action_controller/rescue.rb +24 -24
- data/lib/action_controller/resources.rb +66 -23
- data/lib/action_controller/response.rb +2 -2
- data/lib/action_controller/routing.rb +113 -1229
- data/lib/action_controller/routing/builder.rb +204 -0
- data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
- data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
- data/lib/action_controller/routing/route.rb +240 -0
- data/lib/action_controller/routing/route_set.rb +435 -0
- data/lib/action_controller/routing/routing_ext.rb +46 -0
- data/lib/action_controller/routing/segments.rb +283 -0
- data/lib/action_controller/session/active_record_store.rb +13 -8
- data/lib/action_controller/session/cookie_store.rb +20 -17
- data/lib/action_controller/session_management.rb +10 -3
- data/lib/action_controller/streaming.rb +45 -31
- data/lib/action_controller/test_case.rb +33 -23
- data/lib/action_controller/test_process.rb +39 -35
- data/lib/action_controller/url_rewriter.rb +18 -12
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view.rb +11 -3
- data/lib/action_view/base.rb +73 -390
- data/lib/action_view/helpers/active_record_helper.rb +83 -62
- data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
- data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
- data/lib/action_view/helpers/benchmark_helper.rb +5 -3
- data/lib/action_view/helpers/cache_helper.rb +3 -2
- data/lib/action_view/helpers/capture_helper.rb +1 -2
- data/lib/action_view/helpers/date_helper.rb +104 -82
- data/lib/action_view/helpers/form_helper.rb +148 -75
- data/lib/action_view/helpers/form_options_helper.rb +44 -23
- data/lib/action_view/helpers/form_tag_helper.rb +22 -13
- data/lib/action_view/helpers/javascripts/controls.js +1 -1
- data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
- data/lib/action_view/helpers/javascripts/effects.js +1 -1
- data/lib/action_view/helpers/number_helper.rb +10 -3
- data/lib/action_view/helpers/prototype_helper.rb +61 -29
- data/lib/action_view/helpers/record_tag_helper.rb +3 -3
- data/lib/action_view/helpers/sanitize_helper.rb +23 -17
- data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
- data/lib/action_view/helpers/text_helper.rb +153 -125
- data/lib/action_view/helpers/url_helper.rb +83 -28
- data/lib/action_view/inline_template.rb +20 -0
- data/lib/action_view/partial_template.rb +70 -0
- data/lib/action_view/partials.rb +31 -73
- data/lib/action_view/template.rb +127 -0
- data/lib/action_view/template_error.rb +8 -7
- data/lib/action_view/template_finder.rb +177 -0
- data/lib/action_view/template_handler.rb +18 -1
- data/lib/action_view/template_handlers/builder.rb +10 -2
- data/lib/action_view/template_handlers/compilable.rb +128 -0
- data/lib/action_view/template_handlers/erb.rb +37 -2
- data/lib/action_view/template_handlers/rjs.rb +14 -1
- data/lib/action_view/test_case.rb +58 -0
- data/test/abstract_unit.rb +1 -1
- data/test/active_record_unit.rb +3 -6
- data/test/activerecord/active_record_store_test.rb +1 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +16 -19
- data/test/controller/addresses_render_test.rb +1 -1
- data/test/controller/assert_select_test.rb +13 -6
- data/test/controller/base_test.rb +48 -2
- data/test/controller/benchmark_test.rb +1 -2
- data/test/controller/caching_test.rb +282 -21
- data/test/controller/capture_test.rb +1 -1
- data/test/controller/cgi_test.rb +1 -1
- data/test/controller/components_test.rb +1 -1
- data/test/controller/content_type_test.rb +2 -2
- data/test/controller/cookie_test.rb +13 -2
- data/test/controller/custom_handler_test.rb +14 -10
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
- data/test/controller/dispatcher_test.rb +31 -49
- data/test/controller/fake_controllers.rb +17 -0
- data/test/controller/fake_models.rb +6 -0
- data/test/controller/filter_params_test.rb +14 -8
- data/test/controller/filters_test.rb +44 -16
- data/test/controller/flash_test.rb +2 -2
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +19 -15
- data/test/controller/html-scanner/document_test.rb +1 -2
- data/test/controller/html-scanner/node_test.rb +1 -2
- data/test/controller/html-scanner/sanitizer_test.rb +8 -5
- data/test/controller/html-scanner/tag_node_test.rb +1 -2
- data/test/controller/html-scanner/text_node_test.rb +2 -3
- data/test/controller/html-scanner/tokenizer_test.rb +8 -2
- data/test/controller/http_authentication_test.rb +1 -1
- data/test/controller/integration_test.rb +14 -16
- data/test/controller/integration_upload_test.rb +43 -0
- data/test/controller/layout_test.rb +26 -6
- data/test/controller/mime_responds_test.rb +39 -7
- data/test/controller/mime_type_test.rb +29 -5
- data/test/controller/new_render_test.rb +105 -34
- data/test/controller/polymorphic_routes_test.rb +32 -20
- data/test/controller/record_identifier_test.rb +38 -2
- data/test/controller/redirect_test.rb +21 -1
- data/test/controller/render_test.rb +59 -15
- data/test/controller/request_forgery_protection_test.rb +92 -5
- data/test/controller/request_test.rb +64 -6
- data/test/controller/rescue_test.rb +22 -6
- data/test/controller/resources_test.rb +102 -14
- data/test/controller/routing_test.rb +231 -19
- data/test/controller/selector_test.rb +2 -2
- data/test/controller/send_file_test.rb +14 -3
- data/test/controller/session/cookie_store_test.rb +16 -4
- data/test/controller/session/mem_cache_store_test.rb +3 -4
- data/test/controller/session_fixation_test.rb +1 -1
- data/test/controller/session_management_test.rb +23 -1
- data/test/controller/test_test.rb +39 -18
- data/test/controller/url_rewriter_test.rb +35 -1
- data/test/controller/verification_test.rb +1 -1
- data/test/controller/view_paths_test.rb +15 -12
- data/test/controller/webservice_test.rb +48 -3
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/company.rb +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +6 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/controls.js +1 -0
- data/test/fixtures/public/javascripts/dragdrop.js +1 -0
- data/test/fixtures/public/javascripts/effects.js +1 -0
- data/test/fixtures/public/javascripts/prototype.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/reply.rb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/template/active_record_helper_test.rb +25 -8
- data/test/template/asset_tag_helper_test.rb +100 -17
- data/test/template/atom_feed_helper_test.rb +29 -1
- data/test/template/benchmark_helper_test.rb +10 -22
- data/test/template/date_helper_test.rb +455 -153
- data/test/template/erb_util_test.rb +10 -42
- data/test/template/form_helper_test.rb +192 -66
- data/test/template/form_options_helper_test.rb +19 -8
- data/test/template/form_tag_helper_test.rb +11 -8
- data/test/template/javascript_helper_test.rb +3 -9
- data/test/template/number_helper_test.rb +6 -3
- data/test/template/prototype_helper_test.rb +27 -40
- data/test/template/record_tag_helper_test.rb +54 -0
- data/test/template/sanitize_helper_test.rb +5 -6
- data/test/template/scriptaculous_helper_test.rb +7 -13
- data/test/template/tag_helper_test.rb +3 -6
- data/test/template/template_finder_test.rb +73 -0
- data/test/template/template_object_test.rb +95 -0
- data/test/template/test_test.rb +56 -0
- data/test/template/text_helper_test.rb +46 -33
- data/test/template/url_helper_test.rb +8 -10
- metadata +65 -12
- data/lib/action_view/compiled_templates.rb +0 -69
- data/test/action_view_test.rb +0 -44
- data/test/activerecord/fixtures_test.rb +0 -24
- data/test/controller/fragment_store_setting_test.rb +0 -47
- data/test/template/compiled_templates_test.rb +0 -197
- data/test/template/deprecate_ivars_test.rb +0 -51
@@ -8,47 +8,55 @@ module ActionView
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module Helpers
|
11
|
-
# The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the form
|
11
|
+
# The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
|
12
12
|
# method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
|
13
13
|
# is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
|
14
|
-
# In that case, it's better to use the input method and the specialized form methods in link:classes/ActionView/Helpers/FormHelper.html
|
14
|
+
# In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
|
15
15
|
module ActiveRecordHelper
|
16
|
-
# Returns a default input tag for the type of object returned by the method. For example,
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
|
17
|
+
# has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
|
18
|
+
#
|
19
|
+
# input("post", "title")
|
20
|
+
# # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
20
21
|
def input(record_name, method, options = {})
|
21
22
|
InstanceTag.new(record_name, method, self).to_tag(options)
|
22
23
|
end
|
23
24
|
|
24
|
-
# Returns an entire form with all needed input tags for a specified Active Record object. For example,
|
25
|
-
#
|
25
|
+
# Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
|
26
|
+
# has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
|
27
|
+
#
|
26
28
|
# form("post")
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# </
|
33
|
-
# <
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
29
|
+
#
|
30
|
+
# would yield a form like the following (modulus formatting):
|
31
|
+
#
|
32
|
+
# <form action='/posts/create' method='post'>
|
33
|
+
# <p>
|
34
|
+
# <label for="post_title">Title</label><br />
|
35
|
+
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
36
|
+
# </p>
|
37
|
+
# <p>
|
38
|
+
# <label for="post_body">Body</label><br />
|
39
|
+
# <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
|
40
|
+
# </p>
|
41
|
+
# <input name="commit" type="submit" value="Create" />
|
42
|
+
# </form>
|
40
43
|
#
|
41
44
|
# It's possible to specialize the form builder by using a different action name and by supplying another
|
42
|
-
# block renderer. For example,
|
45
|
+
# block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
|
43
46
|
#
|
44
|
-
# form("entry",
|
45
|
-
#
|
47
|
+
# form("entry",
|
48
|
+
# :action => "sign",
|
49
|
+
# :input_block => Proc.new { |record, column|
|
50
|
+
# "#{column.human_name}: #{input(record, column.name)}<br />"
|
51
|
+
# })
|
46
52
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
53
|
+
# would yield a form like the following (modulus formatting):
|
54
|
+
#
|
55
|
+
# <form action="/entries/sign" method="post">
|
56
|
+
# Message:
|
57
|
+
# <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
|
58
|
+
# <input name="commit" type="submit" value="Sign" />
|
59
|
+
# </form>
|
52
60
|
#
|
53
61
|
# It's also possible to add additional content to the form by giving it a block, such as:
|
54
62
|
#
|
@@ -56,6 +64,14 @@ module ActionView
|
|
56
64
|
# form << content_tag("b", "Department")
|
57
65
|
# form << collection_select("department", "id", @departments, "id", "name")
|
58
66
|
# end
|
67
|
+
#
|
68
|
+
# The following options are available:
|
69
|
+
#
|
70
|
+
# * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
|
71
|
+
# * <tt>:input_block</tt> - Specialize the output using a different block, see above.
|
72
|
+
# * <tt>:method</tt> - The method used when submitting the form (default: +post+).
|
73
|
+
# * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
|
74
|
+
# * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
|
59
75
|
def form(record_name, options = {})
|
60
76
|
record = instance_variable_get("@#{record_name}")
|
61
77
|
|
@@ -65,29 +81,27 @@ module ActionView
|
|
65
81
|
|
66
82
|
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
|
67
83
|
|
68
|
-
contents = ''
|
84
|
+
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
|
69
85
|
contents << hidden_field(record_name, :id) unless record.new_record?
|
70
86
|
contents << all_input_tags(record, record_name, options)
|
71
87
|
yield contents if block_given?
|
72
88
|
contents << submit_tag(submit_value)
|
73
|
-
|
74
|
-
content_tag('form', contents, :action => action, :method => 'post', :enctype => options[:multipart] ? 'multipart/form-data': nil)
|
89
|
+
contents << '</form>'
|
75
90
|
end
|
76
91
|
|
77
92
|
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
|
78
93
|
# This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a +prepend_text+ and/or +append_text+
|
79
94
|
# (to properly explain the error), and a +css_class+ to style it accordingly. +object+ should either be the name of an instance variable or
|
80
|
-
# the actual object. As an example, let's say you have a model
|
81
|
-
# +post+ that has an error message on the +title+ attribute:
|
95
|
+
# the actual object. As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
|
82
96
|
#
|
83
|
-
# <%= error_message_on "post", "title" %>
|
84
|
-
#
|
97
|
+
# <%= error_message_on "post", "title" %>
|
98
|
+
# # => <div class="formError">can't be empty</div>
|
85
99
|
#
|
86
|
-
# <%= error_message_on @post, "title" %>
|
87
|
-
#
|
100
|
+
# <%= error_message_on @post, "title" %>
|
101
|
+
# # => <div class="formError">can't be empty</div>
|
88
102
|
#
|
89
|
-
# <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %>
|
90
|
-
#
|
103
|
+
# <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %>
|
104
|
+
# # => <div class="inputError">Title simply can't be empty (or it won't work).</div>
|
91
105
|
def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
|
92
106
|
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
|
93
107
|
(errors = obj.errors.on(method))
|
@@ -103,30 +117,37 @@ module ActionView
|
|
103
117
|
#
|
104
118
|
# This <tt>DIV</tt> can be tailored by the following options:
|
105
119
|
#
|
106
|
-
# * <tt
|
107
|
-
# * <tt
|
108
|
-
# * <tt
|
109
|
-
# * <tt
|
110
|
-
#
|
111
|
-
# * <tt
|
112
|
-
#
|
120
|
+
# * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
|
121
|
+
# * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
|
122
|
+
# * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
|
123
|
+
# * <tt>:object</tt> - The object (or array of objects) for which to display errors,
|
124
|
+
# if you need to escape the instance variable convention.
|
125
|
+
# * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
|
126
|
+
# If <tt>:object_name</tt> is not set, the name of the first object will be used.
|
127
|
+
# * <tt>:header_message</tt> - The message in the header of the error div. Pass +nil+
|
128
|
+
# or an empty string to avoid the header message altogether. (Default: "X errors
|
129
|
+
# prohibited this object from being saved").
|
130
|
+
# * <tt>:message</tt> - The explanation message after the header message and before
|
131
|
+
# the error list. Pass +nil+ or an empty string to avoid the explanation message
|
132
|
+
# altogether. (Default: "There were problems with the following fields:").
|
113
133
|
#
|
114
|
-
# To specify the display for one object, you simply provide its name as a parameter.
|
134
|
+
# To specify the display for one object, you simply provide its name as a parameter.
|
135
|
+
# For example, for the <tt>@user</tt> model:
|
115
136
|
#
|
116
137
|
# error_messages_for 'user'
|
117
138
|
#
|
118
|
-
# To specify more than one object, you simply list them; optionally, you can add an extra
|
119
|
-
# will be the name used in the header message
|
139
|
+
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
|
140
|
+
# will be the name used in the header message:
|
120
141
|
#
|
121
142
|
# error_messages_for 'user_common', 'user', :object_name => 'user'
|
122
143
|
#
|
123
|
-
# If the objects cannot be located as instance variables, you can add an extra
|
124
|
-
# object (or array of objects to use)
|
144
|
+
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter which gives the actual
|
145
|
+
# object (or array of objects to use):
|
125
146
|
#
|
126
147
|
# error_messages_for 'user', :object => @question.user
|
127
148
|
#
|
128
149
|
# NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
|
129
|
-
# you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors
|
150
|
+
# you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
|
130
151
|
# instance yourself and set it up. View the source of this method to see how easy it is.
|
131
152
|
def error_messages_for(*params)
|
132
153
|
options = params.extract_options!.symbolize_keys
|
@@ -213,29 +234,29 @@ module ActionView
|
|
213
234
|
end
|
214
235
|
|
215
236
|
alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag
|
216
|
-
def to_date_select_tag(options = {})
|
237
|
+
def to_date_select_tag(options = {}, html_options = {})
|
217
238
|
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
218
|
-
error_wrapping(to_date_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
|
239
|
+
error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
|
219
240
|
else
|
220
|
-
to_date_select_tag_without_error_wrapping(options)
|
241
|
+
to_date_select_tag_without_error_wrapping(options, html_options)
|
221
242
|
end
|
222
243
|
end
|
223
244
|
|
224
245
|
alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag
|
225
|
-
def to_datetime_select_tag(options = {})
|
246
|
+
def to_datetime_select_tag(options = {}, html_options = {})
|
226
247
|
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
227
|
-
error_wrapping(to_datetime_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
|
248
|
+
error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
|
228
249
|
else
|
229
|
-
to_datetime_select_tag_without_error_wrapping(options)
|
250
|
+
to_datetime_select_tag_without_error_wrapping(options, html_options)
|
230
251
|
end
|
231
252
|
end
|
232
253
|
|
233
254
|
alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag
|
234
|
-
def to_time_select_tag(options = {})
|
255
|
+
def to_time_select_tag(options = {}, html_options = {})
|
235
256
|
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
236
|
-
error_wrapping(to_time_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
|
257
|
+
error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
|
237
258
|
else
|
238
|
-
to_time_select_tag_without_error_wrapping(options)
|
259
|
+
to_time_select_tag_without_error_wrapping(options, html_options)
|
239
260
|
end
|
240
261
|
end
|
241
262
|
|
@@ -11,24 +11,24 @@ module ActionView
|
|
11
11
|
# === Using asset hosts
|
12
12
|
# By default, Rails links to these assets on the current host in the public
|
13
13
|
# folder, but you can direct Rails to link to assets from a dedicated assets server by
|
14
|
-
# setting ActionController::Base.asset_host in your environment.rb
|
15
|
-
# let's say your asset host is assets.example.com
|
14
|
+
# setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>. For example,
|
15
|
+
# let's say your asset host is <tt>assets.example.com</tt>.
|
16
16
|
#
|
17
17
|
# ActionController::Base.asset_host = "assets.example.com"
|
18
18
|
# image_tag("rails.png")
|
19
19
|
# => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
|
20
|
-
#
|
20
|
+
# stylesheet_link_tag("application")
|
21
21
|
# => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
|
22
22
|
#
|
23
23
|
# This is useful since browsers typically open at most two connections to a single host,
|
24
24
|
# which means your assets often wait in single file for their turn to load. You can
|
25
|
-
# alleviate this by using a
|
26
|
-
# to automatically distribute asset requests among four hosts (e.g., assets0.example.com through assets3.example.com)
|
25
|
+
# alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
|
26
|
+
# to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com")
|
27
27
|
# so browsers will open eight connections rather than two.
|
28
28
|
#
|
29
29
|
# image_tag("rails.png")
|
30
30
|
# => <img src="http://assets0.example.com/images/rails.png" alt="Rails" />
|
31
|
-
#
|
31
|
+
# stylesheet_link_tag("application")
|
32
32
|
# => <link href="http://assets3.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
|
33
33
|
#
|
34
34
|
# To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
|
@@ -47,11 +47,13 @@ module ActionView
|
|
47
47
|
# ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" }
|
48
48
|
# image_tag("rails.png")
|
49
49
|
# => <img src="http://assets2.example.com/images/rails.png" alt="Rails" />
|
50
|
-
#
|
50
|
+
# stylesheet_link_tag("application")
|
51
51
|
# => <link href="http://assets1.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
|
52
52
|
#
|
53
|
-
# The proc takes a
|
54
|
-
#
|
53
|
+
# The proc takes a <tt>source</tt> parameter (which is the path of the source asset) and an optional
|
54
|
+
# <tt>request</tt> parameter (which is an entire instance of an <tt>ActionController::AbstractRequest</tt>
|
55
|
+
# subclass). This can be used to generate a particular asset host depending on the asset path and the particular
|
56
|
+
# request.
|
55
57
|
#
|
56
58
|
# ActionController::Base.asset_host = Proc.new { |source|
|
57
59
|
# if source.starts_with?('/images')
|
@@ -62,9 +64,22 @@ module ActionView
|
|
62
64
|
# }
|
63
65
|
# image_tag("rails.png")
|
64
66
|
# => <img src="http://images.example.com/images/rails.png" alt="Rails" />
|
65
|
-
#
|
67
|
+
# stylesheet_link_tag("application")
|
66
68
|
# => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
|
67
69
|
#
|
70
|
+
# The optional <tt>request</tt> parameter to the proc is useful in particular for serving assets from an
|
71
|
+
# SSL-protected page. The example proc below disables asset hosting for HTTPS connections, while still sending
|
72
|
+
# assets for plain HTTP requests from asset hosts. This is useful for avoiding mixed media warnings when serving
|
73
|
+
# non-HTTP assets from HTTPS web pages when you don't have an SSL certificate for each of the asset hosts.
|
74
|
+
#
|
75
|
+
# ActionController::Base.asset_host = Proc.new { |source, request|
|
76
|
+
# if request.ssl?
|
77
|
+
# "#{request.protocol}#{request.host_with_port}"
|
78
|
+
# else
|
79
|
+
# "#{request.protocol}assets.example.com"
|
80
|
+
# end
|
81
|
+
# }
|
82
|
+
#
|
68
83
|
# === Using asset timestamps
|
69
84
|
#
|
70
85
|
# By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the
|
@@ -86,7 +101,7 @@ module ActionView
|
|
86
101
|
# something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being
|
87
102
|
# requested over and over).
|
88
103
|
module AssetTagHelper
|
89
|
-
ASSETS_DIR = defined?(
|
104
|
+
ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public"
|
90
105
|
JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts"
|
91
106
|
STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets"
|
92
107
|
|
@@ -140,7 +155,8 @@ module ActionView
|
|
140
155
|
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
|
141
156
|
|
142
157
|
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
|
143
|
-
@@
|
158
|
+
@@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
|
159
|
+
@@stylesheet_expansions = {}
|
144
160
|
|
145
161
|
# Returns an html script tag for each of the +sources+ provided. You
|
146
162
|
# can pass in the filename (.js extension is optional) of javascript files
|
@@ -148,7 +164,7 @@ module ActionView
|
|
148
164
|
# current page or you can pass the full path relative to your document
|
149
165
|
# root. To include the Prototype and Scriptaculous javascript libraries in
|
150
166
|
# your application, pass <tt>:defaults</tt> as the source. When using
|
151
|
-
#
|
167
|
+
# <tt>:defaults</tt>, if an application.js file exists in your public
|
152
168
|
# javascripts directory, it will be included as well. You can modify the
|
153
169
|
# html attributes of the script tag by passing a hash as the last argument.
|
154
170
|
#
|
@@ -234,22 +250,52 @@ module ActionView
|
|
234
250
|
end
|
235
251
|
end
|
236
252
|
|
253
|
+
# Register one or more javascript files to be included when <tt>symbol</tt>
|
254
|
+
# is passed to <tt>javascript_include_tag</tt>. This method is typically intended
|
255
|
+
# to be called from plugin initialization to register javascript files
|
256
|
+
# that the plugin installed in <tt>public/javascripts</tt>.
|
257
|
+
#
|
258
|
+
# ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
|
259
|
+
#
|
260
|
+
# javascript_include_tag :monkey # =>
|
261
|
+
# <script type="text/javascript" src="/javascripts/head.js"></script>
|
262
|
+
# <script type="text/javascript" src="/javascripts/body.js"></script>
|
263
|
+
# <script type="text/javascript" src="/javascripts/tail.js"></script>
|
264
|
+
def self.register_javascript_expansion(expansions)
|
265
|
+
@@javascript_expansions.merge!(expansions)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Register one or more stylesheet files to be included when <tt>symbol</tt>
|
269
|
+
# is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
|
270
|
+
# to be called from plugin initialization to register stylesheet files
|
271
|
+
# that the plugin installed in <tt>public/stylesheets</tt>.
|
272
|
+
#
|
273
|
+
# ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
|
274
|
+
#
|
275
|
+
# stylesheet_link_tag :monkey # =>
|
276
|
+
# <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />
|
277
|
+
# <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
|
278
|
+
# <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
|
279
|
+
def self.register_stylesheet_expansion(expansions)
|
280
|
+
@@stylesheet_expansions.merge!(expansions)
|
281
|
+
end
|
282
|
+
|
237
283
|
# Register one or more additional JavaScript files to be included when
|
238
284
|
# <tt>javascript_include_tag :defaults</tt> is called. This method is
|
239
285
|
# typically intended to be called from plugin initialization to register additional
|
240
286
|
# .js files that the plugin installed in <tt>public/javascripts</tt>.
|
241
287
|
def self.register_javascript_include_default(*sources)
|
242
|
-
@@
|
288
|
+
@@javascript_expansions[:defaults].concat(sources)
|
243
289
|
end
|
244
290
|
|
245
291
|
def self.reset_javascript_include_default #:nodoc:
|
246
|
-
@@
|
292
|
+
@@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
|
247
293
|
end
|
248
294
|
|
249
295
|
# Computes the path to a stylesheet asset in the public stylesheets directory.
|
250
|
-
# If the +source+ filename has no extension,
|
296
|
+
# If the +source+ filename has no extension, <tt>.css</tt> will be appended.
|
251
297
|
# Full paths from the document root will be passed through.
|
252
|
-
# Used internally by stylesheet_link_tag to build the stylesheet path.
|
298
|
+
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
|
253
299
|
#
|
254
300
|
# ==== Examples
|
255
301
|
# stylesheet_path "style" # => /stylesheets/style.css
|
@@ -263,7 +309,7 @@ module ActionView
|
|
263
309
|
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
|
264
310
|
|
265
311
|
# Returns a stylesheet link tag for the sources specified as arguments. If
|
266
|
-
# you don't specify an extension,
|
312
|
+
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
|
267
313
|
# You can modify the link attributes by passing a hash as the last argument.
|
268
314
|
#
|
269
315
|
# ==== Examples
|
@@ -286,7 +332,7 @@ module ActionView
|
|
286
332
|
# <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
|
287
333
|
# <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
|
288
334
|
#
|
289
|
-
# You can also include all styles in the stylesheet directory using
|
335
|
+
# You can also include all styles in the stylesheet directory using <tt>:all</tt> as the source:
|
290
336
|
#
|
291
337
|
# stylesheet_link_tag :all # =>
|
292
338
|
# <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
|
@@ -333,7 +379,7 @@ module ActionView
|
|
333
379
|
|
334
380
|
# Computes the path to an image asset in the public images directory.
|
335
381
|
# Full paths from the document root will be passed through.
|
336
|
-
# Used internally by image_tag to build the image path.
|
382
|
+
# Used internally by +image_tag+ to build the image path.
|
337
383
|
#
|
338
384
|
# ==== Examples
|
339
385
|
# image_path("edit") # => /images/edit
|
@@ -383,7 +429,7 @@ module ActionView
|
|
383
429
|
options.symbolize_keys!
|
384
430
|
|
385
431
|
options[:src] = path_to_image(source)
|
386
|
-
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
|
432
|
+
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.to_s.capitalize
|
387
433
|
|
388
434
|
if size = options.delete(:size)
|
389
435
|
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
|
@@ -408,8 +454,8 @@ module ActionView
|
|
408
454
|
end
|
409
455
|
end
|
410
456
|
|
411
|
-
# Add the
|
412
|
-
# Prefix with
|
457
|
+
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
|
458
|
+
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
|
413
459
|
# roots. Rewrite the asset path for cache-busting asset ids. Include
|
414
460
|
# asset host, if configured, with the correct request protocol.
|
415
461
|
def compute_public_path(source, dir, ext = nil, include_host = true)
|
@@ -428,7 +474,7 @@ module ActionView
|
|
428
474
|
|
429
475
|
ActionView::Base.computed_public_paths[cache_key] ||=
|
430
476
|
begin
|
431
|
-
source += ".#{ext}" if File.extname(source).blank?
|
477
|
+
source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))
|
432
478
|
|
433
479
|
if source =~ %r{^[-a-z]+://}
|
434
480
|
source
|
@@ -456,16 +502,21 @@ module ActionView
|
|
456
502
|
end
|
457
503
|
end
|
458
504
|
|
459
|
-
# Pick an asset host for this source. Returns nil if no host is set,
|
505
|
+
# Pick an asset host for this source. Returns +nil+ if no host is set,
|
460
506
|
# the host if no wildcard is set, the host interpolated with the
|
461
|
-
# numbers 0-3 if it contains
|
507
|
+
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
|
462
508
|
# or the value returned from invoking the proc if it's a proc.
|
463
509
|
def compute_asset_host(source)
|
464
510
|
if host = ActionController::Base.asset_host
|
465
511
|
if host.is_a?(Proc)
|
466
|
-
host.
|
512
|
+
case host.arity
|
513
|
+
when 2
|
514
|
+
host.call(source, @controller.request)
|
515
|
+
else
|
516
|
+
host.call(source)
|
517
|
+
end
|
467
518
|
else
|
468
|
-
host % (source.hash % 4)
|
519
|
+
(host =~ /%d/) ? host % (source.hash % 4) : host
|
469
520
|
end
|
470
521
|
end
|
471
522
|
end
|
@@ -514,28 +565,34 @@ module ActionView
|
|
514
565
|
end
|
515
566
|
|
516
567
|
def expand_javascript_sources(sources)
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
sources.delete(:defaults)
|
528
|
-
sources << "application" if file_exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
|
568
|
+
if sources.include?(:all)
|
569
|
+
all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
|
570
|
+
@@all_javascript_sources ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
|
571
|
+
else
|
572
|
+
expanded_sources = sources.collect do |source|
|
573
|
+
determine_source(source, @@javascript_expansions)
|
574
|
+
end.flatten
|
575
|
+
expanded_sources << "application" if sources.include?(:defaults) && file_exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
|
576
|
+
expanded_sources
|
529
577
|
end
|
530
|
-
|
531
|
-
sources
|
532
578
|
end
|
533
579
|
|
534
580
|
def expand_stylesheet_sources(sources)
|
535
581
|
if sources.first == :all
|
536
|
-
@@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).
|
582
|
+
@@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
|
537
583
|
else
|
538
|
-
sources
|
584
|
+
sources.collect do |source|
|
585
|
+
determine_source(source, @@stylesheet_expansions)
|
586
|
+
end.flatten
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
def determine_source(source, collection)
|
591
|
+
case source
|
592
|
+
when Symbol
|
593
|
+
collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
|
594
|
+
else
|
595
|
+
source
|
539
596
|
end
|
540
597
|
end
|
541
598
|
|