actionpack 3.0.1 → 3.0.2
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 +7 -1
- data/lib/abstract_controller/base.rb +1 -0
- data/lib/abstract_controller/callbacks.rb +2 -0
- data/lib/abstract_controller/rendering.rb +4 -4
- data/lib/action_controller/base.rb +2 -0
- data/lib/action_controller/metal.rb +8 -3
- data/lib/action_controller/metal/head.rb +2 -4
- data/lib/action_controller/metal/http_authentication.rb +8 -10
- data/lib/action_controller/metal/mime_responds.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +4 -2
- data/lib/action_controller/metal/renderers.rb +1 -1
- data/lib/action_controller/metal/responder.rb +20 -0
- data/lib/action_controller/test_case.rb +7 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -11
- data/lib/action_dispatch/http/request.rb +19 -2
- data/lib/action_dispatch/http/response.rb +9 -10
- data/lib/action_dispatch/http/upload.rb +21 -29
- data/lib/action_dispatch/middleware/cookies.rb +11 -3
- data/lib/action_dispatch/railtie.rb +0 -5
- data/lib/action_dispatch/routing.rb +75 -22
- data/lib/action_dispatch/routing/mapper.rb +429 -60
- data/lib/action_dispatch/routing/polymorphic_routes.rb +1 -1
- data/lib/action_dispatch/routing/route.rb +2 -1
- data/lib/action_dispatch/routing/url_for.rb +4 -5
- data/lib/action_dispatch/testing/integration.rb +9 -7
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view/base.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +1 -1
- data/lib/action_view/helpers/capture_helper.rb +2 -1
- data/lib/action_view/helpers/date_helper.rb +2 -0
- data/lib/action_view/helpers/form_helper.rb +15 -12
- data/lib/action_view/helpers/form_options_helper.rb +5 -5
- data/lib/action_view/helpers/javascript_helper.rb +2 -1
- data/lib/action_view/helpers/number_helper.rb +23 -12
- data/lib/action_view/helpers/prototype_helper.rb +4 -4
- data/lib/action_view/helpers/text_helper.rb +18 -0
- data/lib/action_view/helpers/url_helper.rb +12 -10
- data/lib/action_view/render/partials.rb +52 -8
- data/lib/action_view/render/rendering.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +6 -1
- data/lib/action_view/test_case.rb +26 -10
- metadata +35 -12
data/CHANGELOG
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
*Rails 3.0.2 (November 15, 2010)*
|
2
|
+
|
3
|
+
* The helper number_to_currency accepts a new :negative_format option to be able to configure how to render negative amounts. [Don Wilson]
|
4
|
+
|
5
|
+
|
1
6
|
*Rails 3.0.1 (October 15, 2010)*
|
2
7
|
|
3
|
-
* No
|
8
|
+
* No changes.
|
9
|
+
|
4
10
|
|
5
11
|
*Rails 3.0.0 (August 29, 2010)*
|
6
12
|
|
@@ -83,6 +83,7 @@ module AbstractController
|
|
83
83
|
# for details on the allowed parameters.
|
84
84
|
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
|
85
85
|
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options}
|
86
|
+
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
86
87
|
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before_filter, name, options)
|
87
88
|
end # end
|
88
89
|
end # end
|
@@ -91,6 +92,7 @@ module AbstractController
|
|
91
92
|
# for details on the allowed parameters.
|
92
93
|
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
|
93
94
|
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
95
|
+
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
94
96
|
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
|
95
97
|
end # end
|
96
98
|
end # end
|
@@ -47,13 +47,13 @@ module AbstractController
|
|
47
47
|
@view_context_class ||= begin
|
48
48
|
controller = self
|
49
49
|
Class.new(ActionView::Base) do
|
50
|
+
if controller.respond_to?(:_routes)
|
51
|
+
include controller._routes.url_helpers
|
52
|
+
end
|
53
|
+
|
50
54
|
if controller.respond_to?(:_helpers)
|
51
55
|
include controller._helpers
|
52
56
|
|
53
|
-
if controller.respond_to?(:_routes)
|
54
|
-
include controller._routes.url_helpers
|
55
|
-
end
|
56
|
-
|
57
57
|
# TODO: Fix RJS to not require this
|
58
58
|
self.helpers = controller._helpers
|
59
59
|
end
|
@@ -148,6 +148,8 @@ module ActionController
|
|
148
148
|
#
|
149
149
|
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
|
150
150
|
#
|
151
|
+
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
|
152
|
+
#
|
151
153
|
# == Calling multiple redirects or renders
|
152
154
|
#
|
153
155
|
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
|
@@ -12,7 +12,7 @@ module ActionController
|
|
12
12
|
#
|
13
13
|
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
14
14
|
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
15
|
-
def initialize(klass, *args)
|
15
|
+
def initialize(klass, *args, &block)
|
16
16
|
options = args.extract_options!
|
17
17
|
@only = Array(options.delete(:only)).map(&:to_s)
|
18
18
|
@except = Array(options.delete(:except)).map(&:to_s)
|
@@ -112,6 +112,11 @@ module ActionController
|
|
112
112
|
headers["Location"] = url
|
113
113
|
end
|
114
114
|
|
115
|
+
# basic url_for that can be overridden for more robust functionality
|
116
|
+
def url_for(string)
|
117
|
+
string
|
118
|
+
end
|
119
|
+
|
115
120
|
def status
|
116
121
|
@_status
|
117
122
|
end
|
@@ -147,8 +152,8 @@ module ActionController
|
|
147
152
|
super
|
148
153
|
end
|
149
154
|
|
150
|
-
def self.use(*args)
|
151
|
-
middleware_stack.use(*args)
|
155
|
+
def self.use(*args, &block)
|
156
|
+
middleware_stack.use(*args, &block)
|
152
157
|
end
|
153
158
|
|
154
159
|
def self.middleware
|
@@ -2,8 +2,6 @@ module ActionController
|
|
2
2
|
module Head
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
include ActionController::UrlFor
|
6
|
-
|
7
5
|
# Return a response that has no content (merely headers). The options
|
8
6
|
# argument is interpreted to be a hash of header names and values.
|
9
7
|
# This allows you to easily return a response that consists only of
|
@@ -27,8 +25,8 @@ module ActionController
|
|
27
25
|
|
28
26
|
self.status = status
|
29
27
|
self.location = url_for(location) if location
|
30
|
-
self.content_type = Mime[formats.first]
|
28
|
+
self.content_type = Mime[formats.first] if formats
|
31
29
|
self.response_body = " "
|
32
30
|
end
|
33
31
|
end
|
34
|
-
end
|
32
|
+
end
|
@@ -95,12 +95,11 @@ module ActionController
|
|
95
95
|
# end
|
96
96
|
# end
|
97
97
|
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# other sites.
|
98
|
+
# === Notes
|
99
|
+
#
|
100
|
+
# The +authenticate_or_request_with_http_digest+ block must return the user's password
|
101
|
+
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
102
|
+
# credentials. Returning +nil+ will cause authentication to fail.
|
104
103
|
#
|
105
104
|
# On shared hosts, Apache sometimes doesn't pass authentication headers to
|
106
105
|
# FCGI instances. If your environment matches this description and you cannot
|
@@ -218,11 +217,10 @@ module ActionController
|
|
218
217
|
end
|
219
218
|
|
220
219
|
def decode_credentials(header)
|
221
|
-
header.to_s.gsub(/^Digest\s+/,'').split(',').
|
220
|
+
Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
222
221
|
key, value = pair.split('=', 2)
|
223
|
-
|
224
|
-
|
225
|
-
end
|
222
|
+
[key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')]
|
223
|
+
end]
|
226
224
|
end
|
227
225
|
|
228
226
|
def authentication_header(controller, realm)
|
@@ -227,7 +227,7 @@ module ActionController #:nodoc:
|
|
227
227
|
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
|
228
228
|
|
229
229
|
if response = retrieve_response_from_mimes(&block)
|
230
|
-
options = resources.extract_options!
|
230
|
+
options = resources.size == 1 ? {} : resources.extract_options!
|
231
231
|
options.merge!(:default_response => response)
|
232
232
|
(options.delete(:responder) || self.class.responder).call(self, resources, options)
|
233
233
|
end
|
@@ -38,6 +38,9 @@ module ActionController
|
|
38
38
|
# redirect_to :action=>'atom', :status => :moved_permanently
|
39
39
|
# redirect_to post_url(@post), :status => 301
|
40
40
|
# redirect_to :action=>'atom', :status => 302
|
41
|
+
#
|
42
|
+
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
|
43
|
+
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
41
44
|
#
|
42
45
|
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
|
43
46
|
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
|
@@ -48,8 +51,7 @@ module ActionController
|
|
48
51
|
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
|
49
52
|
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
|
50
53
|
#
|
51
|
-
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
52
|
-
# RedirectBackError will be raised. You may specify some fallback
|
54
|
+
# When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
|
53
55
|
# behavior for this case by rescuing RedirectBackError.
|
54
56
|
def redirect_to(options = {}, response_status = {}) #:doc:
|
55
57
|
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
|
@@ -71,7 +71,7 @@ module ActionController
|
|
71
71
|
end
|
72
72
|
|
73
73
|
add :json do |json, options|
|
74
|
-
json =
|
74
|
+
json = json.to_json(options) unless json.respond_to?(:to_str)
|
75
75
|
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
76
76
|
self.content_type ||= Mime::JSON
|
77
77
|
self.response_body = json
|
@@ -161,6 +161,8 @@ module ActionController #:nodoc:
|
|
161
161
|
display resource.errors, :status => :unprocessable_entity
|
162
162
|
elsif post?
|
163
163
|
display resource, :status => :created, :location => api_location
|
164
|
+
elsif has_empty_resource_definition?
|
165
|
+
display empty_resource, :status => :ok
|
164
166
|
else
|
165
167
|
head :ok
|
166
168
|
end
|
@@ -221,5 +223,23 @@ module ActionController #:nodoc:
|
|
221
223
|
def default_action
|
222
224
|
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
|
223
225
|
end
|
226
|
+
|
227
|
+
# Check whether resource needs a specific definition of empty resource to be valid
|
228
|
+
#
|
229
|
+
def has_empty_resource_definition?
|
230
|
+
respond_to?("empty_#{format}_resource")
|
231
|
+
end
|
232
|
+
|
233
|
+
# Delegate to proper empty resource method
|
234
|
+
#
|
235
|
+
def empty_resource
|
236
|
+
send("empty_#{format}_resource")
|
237
|
+
end
|
238
|
+
|
239
|
+
# Return a valid empty JSON resource
|
240
|
+
#
|
241
|
+
def empty_json_resource
|
242
|
+
"{}"
|
243
|
+
end
|
224
244
|
end
|
225
245
|
end
|
@@ -40,6 +40,13 @@ module ActionController
|
|
40
40
|
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
|
41
41
|
end
|
42
42
|
|
43
|
+
def process(*args)
|
44
|
+
@partials = Hash.new(0)
|
45
|
+
@templates = Hash.new(0)
|
46
|
+
@layouts = Hash.new(0)
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
43
50
|
# Asserts that the request was rendered with the appropriate template file or partials.
|
44
51
|
#
|
45
52
|
# ==== Examples
|
@@ -38,18 +38,14 @@ module HTML #:nodoc:
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def keys_to_strings(hash)
|
41
|
-
hash.keys.
|
42
|
-
h[k.to_s] = hash[k]
|
43
|
-
h
|
44
|
-
end
|
41
|
+
Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
|
45
42
|
end
|
46
43
|
|
47
44
|
def keys_to_symbols(hash)
|
48
|
-
hash.keys.
|
45
|
+
Hash[hash.keys.map do |k|
|
49
46
|
raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
|
50
|
-
|
51
|
-
|
52
|
-
end
|
47
|
+
[k.to_sym, hash[k]]
|
48
|
+
end]
|
53
49
|
end
|
54
50
|
end
|
55
51
|
|
@@ -77,9 +73,7 @@ module HTML #:nodoc:
|
|
77
73
|
|
78
74
|
# Return a textual representation of the node.
|
79
75
|
def to_s
|
80
|
-
|
81
|
-
@children.each { |child| s << child.to_s }
|
82
|
-
s
|
76
|
+
@children.join()
|
83
77
|
end
|
84
78
|
|
85
79
|
# Return false (subclasses must override this to provide specific matching
|
@@ -4,6 +4,7 @@ require 'strscan'
|
|
4
4
|
|
5
5
|
require 'active_support/core_ext/hash/indifferent_access'
|
6
6
|
require 'active_support/core_ext/string/access'
|
7
|
+
require 'active_support/inflector'
|
7
8
|
require 'action_dispatch/http/headers'
|
8
9
|
|
9
10
|
module ActionDispatch
|
@@ -44,8 +45,24 @@ module ActionDispatch
|
|
44
45
|
@env.key?(key)
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
# List of HTTP request methods from the following RFCs:
|
49
|
+
# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
|
50
|
+
# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
|
51
|
+
# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
|
52
|
+
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
|
53
|
+
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
|
54
|
+
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
|
55
|
+
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
|
56
|
+
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
|
57
|
+
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
|
58
|
+
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
|
59
|
+
RFC3648 = %w(ORDERPATCH)
|
60
|
+
RFC3744 = %w(ACL)
|
61
|
+
RFC5323 = %w(SEARCH)
|
62
|
+
RFC5789 = %w(PATCH)
|
63
|
+
|
64
|
+
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
|
65
|
+
HTTP_METHOD_LOOKUP = Hash.new { |h, m| h[m] = m.underscore.to_sym if HTTP_METHODS.include?(m) }
|
49
66
|
|
50
67
|
# Returns the HTTP \method that the application should see.
|
51
68
|
# In the case where the \method was overridden by a middleware
|
@@ -4,27 +4,26 @@ require 'active_support/core_ext/object/blank'
|
|
4
4
|
require 'active_support/core_ext/class/attribute_accessors'
|
5
5
|
|
6
6
|
module ActionDispatch # :nodoc:
|
7
|
-
# Represents an HTTP response generated by a controller action.
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# from integration tests). See CgiResponse and TestResponse, respectively.
|
7
|
+
# Represents an HTTP response generated by a controller action. Use it to
|
8
|
+
# retrieve the current state of the response, or customize the response. It can
|
9
|
+
# either represent a real HTTP response (i.e. one that is meant to be sent
|
10
|
+
# back to the web browser) or a TestResponse (i.e. one that is generated
|
11
|
+
# from integration tests).
|
13
12
|
#
|
14
|
-
# Response is mostly a Ruby on Rails framework
|
13
|
+
# \Response is mostly a Ruby on \Rails framework implementation detail, and
|
15
14
|
# should never be used directly in controllers. Controllers should use the
|
16
15
|
# methods defined in ActionController::Base instead. For example, if you want
|
17
16
|
# to set the HTTP response's content MIME type, then use
|
18
17
|
# ActionControllerBase#headers instead of Response#headers.
|
19
18
|
#
|
20
19
|
# Nevertheless, integration tests may want to inspect controller responses in
|
21
|
-
# more detail, and that's when Response can be useful for application
|
20
|
+
# more detail, and that's when \Response can be useful for application
|
22
21
|
# developers. Integration test methods such as
|
23
22
|
# ActionDispatch::Integration::Session#get and
|
24
23
|
# ActionDispatch::Integration::Session#post return objects of type
|
25
|
-
# TestResponse (which are of course also of type Response).
|
24
|
+
# TestResponse (which are of course also of type \Response).
|
26
25
|
#
|
27
|
-
# For example, the following demo integration
|
26
|
+
# For example, the following demo integration test prints the body of the
|
28
27
|
# controller response to the console:
|
29
28
|
#
|
30
29
|
# class DemoControllerTest < ActionDispatch::IntegrationTest
|
@@ -2,31 +2,27 @@ require 'active_support/core_ext/object/blank'
|
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Http
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class UploadedFile
|
6
|
+
attr_accessor :original_filename, :content_type, :tempfile, :headers
|
7
|
+
|
8
|
+
def initialize(hash)
|
9
|
+
@original_filename = hash[:filename]
|
10
|
+
@content_type = hash[:type]
|
11
|
+
@headers = hash[:head]
|
12
|
+
@tempfile = hash[:tempfile]
|
13
|
+
raise(ArgumentError, ':tempfile is required') unless @tempfile
|
11
14
|
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
$1
|
24
|
-
else
|
25
|
-
File.basename original_path
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
@original_filename
|
16
|
+
def read(*args)
|
17
|
+
@tempfile.read(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def rewind
|
21
|
+
@tempfile.rewind
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
@tempfile.size
|
30
26
|
end
|
31
27
|
end
|
32
28
|
|
@@ -35,11 +31,7 @@ module ActionDispatch
|
|
35
31
|
# file upload hash with UploadedFile objects
|
36
32
|
def normalize_parameters(value)
|
37
33
|
if Hash === value && value.has_key?(:tempfile)
|
38
|
-
|
39
|
-
upload.extend(UploadedFile)
|
40
|
-
upload.original_path = value[:filename]
|
41
|
-
upload.content_type = value[:type]
|
42
|
-
upload
|
34
|
+
UploadedFile.new(value)
|
43
35
|
else
|
44
36
|
super
|
45
37
|
end
|
@@ -47,4 +39,4 @@ module ActionDispatch
|
|
47
39
|
private :normalize_parameters
|
48
40
|
end
|
49
41
|
end
|
50
|
-
end
|
42
|
+
end
|
@@ -98,17 +98,19 @@ module ActionDispatch
|
|
98
98
|
def self.build(request)
|
99
99
|
secret = request.env[TOKEN_KEY]
|
100
100
|
host = request.host
|
101
|
+
secure = request.ssl?
|
101
102
|
|
102
|
-
new(secret, host).tap do |hash|
|
103
|
+
new(secret, host, secure).tap do |hash|
|
103
104
|
hash.update(request.cookies)
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
107
|
-
def initialize(secret = nil, host = nil)
|
108
|
+
def initialize(secret = nil, host = nil, secure = false)
|
108
109
|
@secret = secret
|
109
110
|
@set_cookies = {}
|
110
111
|
@delete_cookies = {}
|
111
112
|
@host = host
|
113
|
+
@secure = secure
|
112
114
|
|
113
115
|
super()
|
114
116
|
end
|
@@ -193,9 +195,15 @@ module ActionDispatch
|
|
193
195
|
end
|
194
196
|
|
195
197
|
def write(headers)
|
196
|
-
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) }
|
198
|
+
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
|
197
199
|
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
|
198
200
|
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def write_cookie?(cookie)
|
205
|
+
@secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
|
206
|
+
end
|
199
207
|
end
|
200
208
|
|
201
209
|
class PermanentCookieJar < CookieJar #:nodoc:
|