merb-core 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +61 -11
- data/bin/merb +5 -1
- data/lib/merb-core.rb +202 -25
- data/lib/merb-core/autoload.rb +19 -17
- data/lib/merb-core/bootloader.rb +84 -71
- data/lib/merb-core/config.rb +19 -14
- data/lib/merb-core/controller/abstract_controller.rb +16 -17
- data/lib/merb-core/controller/exceptions.rb +115 -70
- data/lib/merb-core/controller/merb_controller.rb +62 -38
- data/lib/merb-core/controller/mime.rb +1 -1
- data/lib/merb-core/controller/mixins/authentication.rb +87 -0
- data/lib/merb-core/controller/mixins/controller.rb +16 -15
- data/lib/merb-core/controller/mixins/render.rb +113 -19
- data/lib/merb-core/controller/mixins/responder.rb +8 -2
- data/lib/merb-core/controller/template.rb +1 -1
- data/lib/merb-core/core_ext.rb +1 -0
- data/lib/merb-core/core_ext/class.rb +113 -6
- data/lib/merb-core/core_ext/hash.rb +43 -39
- data/lib/merb-core/core_ext/kernel.rb +75 -38
- data/lib/merb-core/core_ext/mash.rb +4 -4
- data/lib/merb-core/core_ext/object.rb +18 -7
- data/lib/merb-core/core_ext/set.rb +9 -4
- data/lib/merb-core/core_ext/string.rb +29 -9
- data/lib/merb-core/core_ext/time.rb +13 -0
- data/lib/merb-core/dispatch/cookies.rb +1 -2
- data/lib/merb-core/dispatch/dispatcher.rb +18 -10
- data/lib/merb-core/dispatch/exceptions.html.erb +1 -1
- data/lib/merb-core/dispatch/request.rb +3 -0
- data/lib/merb-core/dispatch/router.rb +10 -7
- data/lib/merb-core/dispatch/router/behavior.rb +36 -27
- data/lib/merb-core/dispatch/router/route.rb +7 -2
- data/lib/merb-core/dispatch/session/cookie.rb +4 -4
- data/lib/merb-core/dispatch/session/memcached.rb +17 -5
- data/lib/merb-core/logger.rb +2 -2
- data/lib/merb-core/plugins.rb +16 -4
- data/lib/merb-core/rack/adapter/ebb.rb +4 -1
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +1 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +1 -0
- data/lib/merb-core/rack/adapter/runner.rb +1 -0
- data/lib/merb-core/rack/adapter/thin.rb +3 -1
- data/lib/merb-core/rack/adapter/webrick.rb +1 -0
- data/lib/merb-core/rack/application.rb +17 -1
- data/lib/merb-core/server.rb +78 -28
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -3
- data/lib/merb-core/test/helpers/request_helper.rb +81 -27
- data/lib/merb-core/test/helpers/view_helper.rb +1 -1
- data/lib/merb-core/test/matchers/controller_matchers.rb +55 -5
- data/lib/merb-core/test/matchers/route_matchers.rb +8 -17
- data/lib/merb-core/test/matchers/view_matchers.rb +53 -11
- data/lib/merb-core/test/run_specs.rb +22 -14
- data/lib/merb-core/test/tasks/spectasks.rb +54 -33
- data/lib/merb-core/vendor/facets/inflect.rb +91 -2
- data/lib/merb-core/version.rb +2 -2
- data/spec/private/config/config_spec.rb +54 -26
- data/spec/private/core_ext/class_spec.rb +22 -0
- data/spec/private/core_ext/hash_spec.rb +70 -54
- data/spec/private/core_ext/kernel_spec.rb +149 -14
- data/spec/private/core_ext/object_spec.rb +92 -10
- data/spec/private/core_ext/string_spec.rb +162 -4
- data/spec/private/core_ext/time_spec.rb +16 -0
- data/spec/private/dispatch/bootloader_spec.rb +24 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +1 -1
- data/spec/private/dispatch/fixture/config/black_hole.rb +12 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +138 -0
- data/spec/private/plugins/plugin_spec.rb +79 -8
- data/spec/private/rack/application_spec.rb +1 -1
- data/spec/public/abstract_controller/controllers/filters.rb +26 -0
- data/spec/public/abstract_controller/controllers/helpers.rb +2 -2
- data/spec/public/abstract_controller/controllers/partial.rb +2 -2
- data/spec/public/abstract_controller/controllers/render.rb +16 -4
- data/spec/public/abstract_controller/filter_spec.rb +8 -0
- data/spec/public/abstract_controller/render_spec.rb +12 -0
- data/spec/public/controller/authentication_spec.rb +103 -0
- data/spec/public/controller/base_spec.rb +4 -3
- data/spec/public/controller/controllers/authentication.rb +47 -0
- data/spec/public/controller/controllers/base.rb +1 -0
- data/spec/public/controller/controllers/display.rb +30 -0
- data/spec/public/controller/controllers/views/layout/custom_arg.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template_argument/index.html.erb +1 -0
- data/spec/public/controller/display_spec.rb +17 -0
- data/spec/public/controller/spec_helper.rb +1 -0
- data/spec/public/controller/url_spec.rb +25 -7
- data/spec/public/core/merb_core_spec.rb +34 -0
- data/spec/public/directory_structure/directory/app/controllers/custom.rb +2 -2
- data/spec/public/directory_structure/directory/log/merb_test.log +48 -0
- data/spec/public/logger/logger_spec.rb +10 -4
- data/spec/public/reloading/directory/app/controllers/reload.rb +1 -1
- data/spec/public/reloading/directory/log/merb_test.log +13 -0
- data/spec/public/reloading/reload_spec.rb +23 -22
- data/spec/public/request/request_spec.rb +2 -0
- data/spec/public/router/nested_resources_spec.rb +7 -0
- data/spec/public/router/resources_spec.rb +46 -1
- data/spec/public/router/special_spec.rb +5 -1
- data/spec/public/test/controller_matchers_spec.rb +25 -1
- data/spec/public/test/controllers/spec_helper_controller.rb +8 -0
- data/spec/public/test/request_helper_spec.rb +52 -1
- data/spec/public/test/route_matchers_spec.rb +27 -25
- data/spec/public/test/view_helper_spec.rb +1 -1
- data/spec/public/test/view_matchers_spec.rb +148 -72
- metadata +23 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'enumerator'
|
|
2
2
|
require 'merb-core/controller/mime'
|
|
3
|
+
require "merb-core/vendor/facets/dictionary"
|
|
3
4
|
module Merb
|
|
4
5
|
# The ResponderMixin adds methods that help you manage what
|
|
5
6
|
# formats your controllers have available, determine what format(s)
|
|
@@ -96,7 +97,7 @@ module Merb
|
|
|
96
97
|
# and none of the provides methods can be used.
|
|
97
98
|
module ResponderMixin
|
|
98
99
|
|
|
99
|
-
TYPES =
|
|
100
|
+
TYPES = Dictionary.new
|
|
100
101
|
|
|
101
102
|
class ContentTypeAlreadySet < StandardError; end
|
|
102
103
|
|
|
@@ -294,7 +295,12 @@ module Merb
|
|
|
294
295
|
specifics = accepts & _provided_formats
|
|
295
296
|
return specifics.first unless specifics.length == 0
|
|
296
297
|
return _provided_formats.first if accepts.include? :all
|
|
297
|
-
|
|
298
|
+
message = "A format (%s) that isn't provided (%s) has been requested. "
|
|
299
|
+
message += "Make sure the action provides the format, and be "
|
|
300
|
+
message += "careful of before filters which won't recognize "
|
|
301
|
+
message += "formats provided within actions."
|
|
302
|
+
raise Merb::ControllerExceptions::NotAcceptable,
|
|
303
|
+
(message % [accepts.join(', '), _provided_formats.join(', ')])
|
|
298
304
|
end
|
|
299
305
|
|
|
300
306
|
# Returns the output format for this request, based on the
|
|
@@ -67,7 +67,7 @@ module Merb::Template
|
|
|
67
67
|
# The module to put the compiled method into. Defaults to
|
|
68
68
|
# Merb::InlineTemplates
|
|
69
69
|
#
|
|
70
|
-
# ====
|
|
70
|
+
# ==== Notes
|
|
71
71
|
# Even though this method supports inlining into any module, the method
|
|
72
72
|
# must be available to instances of AbstractController that will use it.
|
|
73
73
|
#---
|
data/lib/merb-core/core_ext.rb
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
# example, an array without those additions being shared with either their
|
|
5
5
|
# parent, siblings, or children, which is unlike the regular class-level
|
|
6
6
|
# attributes that are shared across the entire hierarchy.
|
|
7
|
-
class Class
|
|
8
|
-
|
|
7
|
+
class Class
|
|
8
|
+
# Defines class-level and instance-level attribute reader.
|
|
9
|
+
#
|
|
10
|
+
# ==== Parameters
|
|
11
|
+
# *syms<Array>:: Array of attributes to define reader for.
|
|
9
12
|
def cattr_reader(*syms)
|
|
10
13
|
syms.flatten.each do |sym|
|
|
11
14
|
next if sym.is_a?(Hash)
|
|
@@ -25,6 +28,13 @@ class Class # :nodoc:
|
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
# Defines class-level (and optionally instance-level) attribute writer.
|
|
32
|
+
#
|
|
33
|
+
# ==== Parameters
|
|
34
|
+
# *syms<Array>:: Array of attributes to define writer for.
|
|
35
|
+
#
|
|
36
|
+
# ==== Options
|
|
37
|
+
# :instance_writer<Boolean>:: if true, instance-level attribute writer is defined.
|
|
28
38
|
def cattr_writer(*syms)
|
|
29
39
|
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
|
30
40
|
syms.flatten.each do |sym|
|
|
@@ -47,11 +57,23 @@ class Class # :nodoc:
|
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
|
|
60
|
+
# Defines class-level (and optionally instance-level) attribute accessor.
|
|
61
|
+
#
|
|
62
|
+
# ==== Parameters
|
|
63
|
+
# *syms<Array>:: Array of attributes to define accessor for.
|
|
64
|
+
#
|
|
65
|
+
# ==== Options
|
|
66
|
+
# :instance_writer<Boolean>:: if true, instance-level attribute writer is defined.
|
|
50
67
|
def cattr_accessor(*syms)
|
|
51
68
|
cattr_reader(*syms)
|
|
52
69
|
cattr_writer(*syms)
|
|
53
70
|
end
|
|
54
71
|
|
|
72
|
+
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
|
73
|
+
# each subclass has a copy of parent's attribute.
|
|
74
|
+
#
|
|
75
|
+
# ==== Parameters
|
|
76
|
+
# *syms<Array>:: Array of attributes to define inheritable reader for.
|
|
55
77
|
def class_inheritable_reader(*syms)
|
|
56
78
|
syms.each do |sym|
|
|
57
79
|
next if sym.is_a?(Hash)
|
|
@@ -68,6 +90,14 @@ class Class # :nodoc:
|
|
|
68
90
|
end
|
|
69
91
|
end
|
|
70
92
|
|
|
93
|
+
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
|
94
|
+
# each subclass has a copy of parent's attribute.
|
|
95
|
+
#
|
|
96
|
+
# ==== Parameters
|
|
97
|
+
# *syms<Array>:: Array of attributes to define inheritable writer for.
|
|
98
|
+
#
|
|
99
|
+
# ==== Options
|
|
100
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable attribute writer is defined.
|
|
71
101
|
def class_inheritable_writer(*syms)
|
|
72
102
|
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
|
73
103
|
syms.each do |sym|
|
|
@@ -87,6 +117,15 @@ class Class # :nodoc:
|
|
|
87
117
|
end
|
|
88
118
|
end
|
|
89
119
|
|
|
120
|
+
# Defines class-level inheritable array writer. Arrays are available to subclasses,
|
|
121
|
+
# each subclass has a copy of parent's array. Difference between other inheritable
|
|
122
|
+
# attributes is that array is recreated every time it is written.
|
|
123
|
+
#
|
|
124
|
+
# ==== Parameters
|
|
125
|
+
# *syms<Array>:: Array of array attribute names to define inheritable writer for.
|
|
126
|
+
#
|
|
127
|
+
# ==== Options
|
|
128
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable array attribute writer is defined.
|
|
90
129
|
def class_inheritable_array_writer(*syms)
|
|
91
130
|
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
|
92
131
|
syms.each do |sym|
|
|
@@ -106,6 +145,15 @@ class Class # :nodoc:
|
|
|
106
145
|
end
|
|
107
146
|
end
|
|
108
147
|
|
|
148
|
+
# Defines class-level inheritable hash writer. Hashs are available to subclasses,
|
|
149
|
+
# each subclass has a copy of parent's hash. Difference between other inheritable
|
|
150
|
+
# attributes is that hash is recreated every time it is written.
|
|
151
|
+
#
|
|
152
|
+
# ==== Parameters
|
|
153
|
+
# *syms<Array>:: Array of hash attribute names to define inheritable writer for.
|
|
154
|
+
#
|
|
155
|
+
# ==== Options
|
|
156
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable hash attribute writer is defined.
|
|
109
157
|
def class_inheritable_hash_writer(*syms)
|
|
110
158
|
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
|
111
159
|
syms.each do |sym|
|
|
@@ -125,25 +173,62 @@ class Class # :nodoc:
|
|
|
125
173
|
end
|
|
126
174
|
end
|
|
127
175
|
|
|
176
|
+
# Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
|
|
177
|
+
# each subclass has a copy of parent's attribute.
|
|
178
|
+
#
|
|
179
|
+
# ==== Parameters
|
|
180
|
+
# *syms<Array>:: Array of attributes to define inheritable accessor for.
|
|
181
|
+
#
|
|
182
|
+
# ==== Options
|
|
183
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable attribute writer is defined.
|
|
128
184
|
def class_inheritable_accessor(*syms)
|
|
129
185
|
class_inheritable_reader(*syms)
|
|
130
186
|
class_inheritable_writer(*syms)
|
|
131
187
|
end
|
|
132
188
|
|
|
189
|
+
# Defines class-level inheritable array accessor. Arrays are available to subclasses,
|
|
190
|
+
# each subclass has a copy of parent's array. Difference between other inheritable
|
|
191
|
+
# attributes is that array is recreated every time it is written.
|
|
192
|
+
#
|
|
193
|
+
# ==== Parameters
|
|
194
|
+
# *syms<Array>:: Array of array attribute names to define inheritable accessor for.
|
|
195
|
+
#
|
|
196
|
+
# ==== Options
|
|
197
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable array attribute writer is defined.
|
|
133
198
|
def class_inheritable_array(*syms)
|
|
134
199
|
class_inheritable_reader(*syms)
|
|
135
200
|
class_inheritable_array_writer(*syms)
|
|
136
201
|
end
|
|
137
202
|
|
|
203
|
+
# Defines class-level inheritable hash accessor. Hashs are available to subclasses,
|
|
204
|
+
# each subclass has a copy of parent's hash. Difference between other inheritable
|
|
205
|
+
# attributes is that hash is recreated every time it is written.
|
|
206
|
+
#
|
|
207
|
+
# ==== Parameters
|
|
208
|
+
# *syms<Array>:: Array of hash attribute names to define inheritable accessor for.
|
|
209
|
+
#
|
|
210
|
+
# ==== Options
|
|
211
|
+
# :instance_writer<Boolean>:: if true, instance-level inheritable hash attribute writer is defined.
|
|
138
212
|
def class_inheritable_hash(*syms)
|
|
139
213
|
class_inheritable_reader(*syms)
|
|
140
214
|
class_inheritable_hash_writer(*syms)
|
|
141
215
|
end
|
|
142
216
|
|
|
217
|
+
# ==== Returns
|
|
218
|
+
# <Hash>:: inheritable attributes hash or it's default value, new frozen Hash.
|
|
143
219
|
def inheritable_attributes
|
|
144
220
|
@inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
|
|
145
221
|
end
|
|
146
222
|
|
|
223
|
+
# Sets the attribute which copy is available to subclasses.
|
|
224
|
+
#
|
|
225
|
+
# ==== Parameters
|
|
226
|
+
# key<~to_s, String, Symbol>:: inheritable attribute name
|
|
227
|
+
# value<Anything but Array or Hash>:: value of inheritable attribute
|
|
228
|
+
#
|
|
229
|
+
# ==== Notes
|
|
230
|
+
# If inheritable attributes storage has it's default value,
|
|
231
|
+
# a new frozen hash, it is set to new Hash that is not frozen.
|
|
147
232
|
def write_inheritable_attribute(key, value)
|
|
148
233
|
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
|
|
149
234
|
@inheritable_attributes = {}
|
|
@@ -151,31 +236,53 @@ class Class # :nodoc:
|
|
|
151
236
|
inheritable_attributes[key] = value
|
|
152
237
|
end
|
|
153
238
|
|
|
239
|
+
# Sets the array attribute which copy is available to subclasses.
|
|
240
|
+
#
|
|
241
|
+
# ==== Parameters
|
|
242
|
+
# key<~to_s, String, Symbol>:: inheritable attribute name
|
|
243
|
+
# value<Array>:: value of inheritable attribute
|
|
244
|
+
#
|
|
245
|
+
# ==== Notes
|
|
246
|
+
# Inheritable array is re-created on each write.
|
|
154
247
|
def write_inheritable_array(key, elements)
|
|
155
248
|
write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
|
|
156
249
|
write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
|
|
157
250
|
end
|
|
158
251
|
|
|
252
|
+
# Sets the hash attribute which copy is available to subclasses.
|
|
253
|
+
#
|
|
254
|
+
# ==== Parameters
|
|
255
|
+
# key<~to_s, String, Symbol>:: inheritable attribute name
|
|
256
|
+
# value<Hash>:: value of inheritable attribute
|
|
257
|
+
#
|
|
258
|
+
# ==== Notes
|
|
259
|
+
# Inheritable hash is re-created on each write.
|
|
159
260
|
def write_inheritable_hash(key, hash)
|
|
160
261
|
write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
|
|
161
262
|
write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
|
|
162
263
|
end
|
|
163
264
|
|
|
265
|
+
# Reads value of inheritable attributes.
|
|
266
|
+
#
|
|
267
|
+
# ==== Returns
|
|
268
|
+
# Inheritable attribute value. Subclasses store copies of values.
|
|
164
269
|
def read_inheritable_attribute(key)
|
|
165
270
|
inheritable_attributes[key]
|
|
166
271
|
end
|
|
167
272
|
|
|
273
|
+
# Resets inheritable attributes to either EMPTY_INHERITABLE_ATTRIBUTES
|
|
274
|
+
# if it is defined or it's default value, new frozen Hash.
|
|
168
275
|
def reset_inheritable_attributes
|
|
169
276
|
@inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
|
|
170
277
|
end
|
|
171
|
-
|
|
278
|
+
|
|
172
279
|
private
|
|
173
280
|
# Prevent this constant from being created multiple times
|
|
174
281
|
EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
|
|
175
282
|
|
|
176
283
|
def inherited_with_inheritable_attributes(child)
|
|
177
284
|
inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
|
|
178
|
-
|
|
285
|
+
|
|
179
286
|
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
|
|
180
287
|
new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
|
|
181
288
|
else
|
|
@@ -183,10 +290,10 @@ class Class # :nodoc:
|
|
|
183
290
|
memo.update(key => (value.dup rescue value))
|
|
184
291
|
end
|
|
185
292
|
end
|
|
186
|
-
|
|
293
|
+
|
|
187
294
|
child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
|
|
188
295
|
end
|
|
189
296
|
|
|
190
297
|
alias inherited_without_inheritable_attributes inherited
|
|
191
298
|
alias inherited inherited_with_inheritable_attributes
|
|
192
|
-
end
|
|
299
|
+
end
|
|
@@ -14,13 +14,13 @@ class Hash
|
|
|
14
14
|
#
|
|
15
15
|
# ===== Typecasting
|
|
16
16
|
# Typecasting is performed on elements that have a +type+ attribute:
|
|
17
|
-
# integer::
|
|
17
|
+
# integer::
|
|
18
18
|
# boolean:: Anything other than "true" evaluates to false.
|
|
19
19
|
# datetime::
|
|
20
20
|
# Returns a Time object. See Time documentation for valid Time strings.
|
|
21
21
|
# date::
|
|
22
22
|
# Returns a Date object. See Date documentation for valid Date strings.
|
|
23
|
-
#
|
|
23
|
+
#
|
|
24
24
|
# Keys are automatically converted to +snake_case+
|
|
25
25
|
#
|
|
26
26
|
# ==== Examples
|
|
@@ -34,15 +34,15 @@ class Hash
|
|
|
34
34
|
# <is-cool type='boolean'>true</is-cool>
|
|
35
35
|
# </user>
|
|
36
36
|
#
|
|
37
|
-
# evaluates to
|
|
38
|
-
#
|
|
39
|
-
# { "user" => {
|
|
37
|
+
# evaluates to
|
|
38
|
+
#
|
|
39
|
+
# { "user" => {
|
|
40
40
|
# "gender" => "m",
|
|
41
41
|
# "age" => 35,
|
|
42
42
|
# "name" => "Home Simpson",
|
|
43
43
|
# "dob" => DateObject( 1998-01-01 ),
|
|
44
44
|
# "joined_at" => TimeObject( 2000-04-28 23:01),
|
|
45
|
-
# "is_cool" => true
|
|
45
|
+
# "is_cool" => true
|
|
46
46
|
# }
|
|
47
47
|
# }
|
|
48
48
|
#
|
|
@@ -54,7 +54,7 @@ class Hash
|
|
|
54
54
|
# evaluates to
|
|
55
55
|
#
|
|
56
56
|
# { "story" => "A Quick <em>brown</em> Fox" }
|
|
57
|
-
#
|
|
57
|
+
#
|
|
58
58
|
# ====== Attributes other than type on a node containing text
|
|
59
59
|
# <story is-good='false'>
|
|
60
60
|
# A Quick <em>brown</em> Fox
|
|
@@ -73,15 +73,19 @@ class Hash
|
|
|
73
73
|
ToHashParser.from_xml(xml)
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
# ==== Returns
|
|
78
78
|
# Mash:: This hash as a Mash for string or symbol key access.
|
|
79
|
+
#
|
|
80
|
+
# This class has semantics of ActiveSupport's HashWithIndifferentAccess
|
|
81
|
+
# and we only have it so that people can write
|
|
82
|
+
# params[:key] instead of params['key'].
|
|
79
83
|
def to_mash
|
|
80
84
|
hash = Mash.new(self)
|
|
81
85
|
hash.default = default
|
|
82
86
|
hash
|
|
83
87
|
end
|
|
84
|
-
|
|
88
|
+
|
|
85
89
|
# ==== Returns
|
|
86
90
|
# String:: This hash as a query string
|
|
87
91
|
#
|
|
@@ -97,7 +101,7 @@ class Hash
|
|
|
97
101
|
def to_params
|
|
98
102
|
params = ''
|
|
99
103
|
stack = []
|
|
100
|
-
|
|
104
|
+
|
|
101
105
|
each do |k, v|
|
|
102
106
|
if v.is_a?(Hash)
|
|
103
107
|
stack << [k,v]
|
|
@@ -105,7 +109,7 @@ class Hash
|
|
|
105
109
|
params << "#{k}=#{v}&"
|
|
106
110
|
end
|
|
107
111
|
end
|
|
108
|
-
|
|
112
|
+
|
|
109
113
|
stack.each do |parent, hash|
|
|
110
114
|
hash.each do |k, v|
|
|
111
115
|
if v.is_a?(Hash)
|
|
@@ -115,11 +119,11 @@ class Hash
|
|
|
115
119
|
end
|
|
116
120
|
end
|
|
117
121
|
end
|
|
118
|
-
|
|
122
|
+
|
|
119
123
|
params.chop! # trailing &
|
|
120
124
|
params
|
|
121
125
|
end
|
|
122
|
-
|
|
126
|
+
|
|
123
127
|
# ==== Parameters
|
|
124
128
|
# *allowed:: The hash keys to include.
|
|
125
129
|
#
|
|
@@ -129,10 +133,10 @@ class Hash
|
|
|
129
133
|
# ==== Examples
|
|
130
134
|
# { :one => 1, :two => 2, :three => 3 }.only(:one)
|
|
131
135
|
# #=> { :one => 1 }
|
|
132
|
-
def only(*allowed)
|
|
136
|
+
def only(*allowed)
|
|
133
137
|
reject { |k,v| !allowed.include?(k) }
|
|
134
138
|
end
|
|
135
|
-
|
|
139
|
+
|
|
136
140
|
# ==== Parameters
|
|
137
141
|
# *rejected:: The hash keys to exclude.
|
|
138
142
|
#
|
|
@@ -142,10 +146,10 @@ class Hash
|
|
|
142
146
|
# ==== Examples
|
|
143
147
|
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
|
144
148
|
# #=> { :two => 2, :three => 3 }
|
|
145
|
-
def except(*rejected)
|
|
149
|
+
def except(*rejected)
|
|
146
150
|
reject { |k,v| rejected.include?(k) }
|
|
147
151
|
end
|
|
148
|
-
|
|
152
|
+
|
|
149
153
|
# ==== Returns
|
|
150
154
|
# String:: The hash as attributes for an XML tag.
|
|
151
155
|
#
|
|
@@ -154,12 +158,12 @@ class Hash
|
|
|
154
158
|
# #=> 'one="1" two="TWO"'
|
|
155
159
|
def to_xml_attributes
|
|
156
160
|
map do |k,v|
|
|
157
|
-
%{#{k.to_s.camel_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
|
|
161
|
+
%{#{k.to_s.camel_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
|
|
158
162
|
end.join(' ')
|
|
159
163
|
end
|
|
160
|
-
|
|
164
|
+
|
|
161
165
|
alias_method :to_html_attributes, :to_xml_attributes
|
|
162
|
-
|
|
166
|
+
|
|
163
167
|
# ==== Parameters
|
|
164
168
|
# html_class<~to_s>::
|
|
165
169
|
# The HTML class to add to the :class key. The html_class will be
|
|
@@ -178,7 +182,7 @@ class Hash
|
|
|
178
182
|
self[:class] = html_class.to_s
|
|
179
183
|
end
|
|
180
184
|
end
|
|
181
|
-
|
|
185
|
+
|
|
182
186
|
# Converts all keys into string values. This is used during reloading to
|
|
183
187
|
# prevent problems when classes are no longer declared.
|
|
184
188
|
#
|
|
@@ -188,7 +192,7 @@ class Hash
|
|
|
188
192
|
def protect_keys!
|
|
189
193
|
keys.each {|key| self[key.to_s] = delete(key) }
|
|
190
194
|
end
|
|
191
|
-
|
|
195
|
+
|
|
192
196
|
# Attempts to convert all string keys into Class keys. We run this after
|
|
193
197
|
# reloading to convert protected hashes back into usable hashes.
|
|
194
198
|
#
|
|
@@ -197,11 +201,11 @@ class Hash
|
|
|
197
201
|
# hash = { "One" => 1, "Two" => 2 }.unproctect_keys!
|
|
198
202
|
# hash # => { One => 1, Two => 2 }
|
|
199
203
|
def unprotect_keys!
|
|
200
|
-
keys.each do |key|
|
|
204
|
+
keys.each do |key|
|
|
201
205
|
(self[Object.full_const_get(key)] = delete(key)) rescue nil
|
|
202
206
|
end
|
|
203
207
|
end
|
|
204
|
-
|
|
208
|
+
|
|
205
209
|
# Destructively and non-recursively convert each key to an uppercase string,
|
|
206
210
|
# deleting nil values along the way.
|
|
207
211
|
#
|
|
@@ -218,7 +222,7 @@ class Hash
|
|
|
218
222
|
self[key.to_s.upcase] = val
|
|
219
223
|
end
|
|
220
224
|
self
|
|
221
|
-
end
|
|
225
|
+
end
|
|
222
226
|
end
|
|
223
227
|
|
|
224
228
|
require 'rexml/parsers/streamparser'
|
|
@@ -233,7 +237,7 @@ require 'rexml/light/node'
|
|
|
233
237
|
class REXMLUtilityNode # :nodoc:
|
|
234
238
|
attr_accessor :name, :attributes, :children, :type
|
|
235
239
|
cattr_accessor :typecasts, :available_typecasts
|
|
236
|
-
|
|
240
|
+
|
|
237
241
|
self.typecasts = {}
|
|
238
242
|
self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
|
|
239
243
|
self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
|
|
@@ -247,14 +251,14 @@ class REXMLUtilityNode # :nodoc:
|
|
|
247
251
|
self.typecasts["string"] = lambda{|v| v.to_s}
|
|
248
252
|
self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
|
|
249
253
|
self.typecasts["base64Binary"] = lambda{|v| Base64.decode64(v)}
|
|
250
|
-
|
|
254
|
+
|
|
251
255
|
self.available_typecasts = self.typecasts.keys
|
|
252
256
|
|
|
253
257
|
def initialize(name, attributes = {})
|
|
254
258
|
@name = name.tr("-", "_")
|
|
255
259
|
# leave the type alone if we don't know what it is
|
|
256
260
|
@type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
|
|
257
|
-
|
|
261
|
+
|
|
258
262
|
@nil_element = attributes.delete("nil") == "true"
|
|
259
263
|
@attributes = undasherize_keys(attributes)
|
|
260
264
|
@children = []
|
|
@@ -268,7 +272,7 @@ class REXMLUtilityNode # :nodoc:
|
|
|
268
272
|
|
|
269
273
|
def to_hash
|
|
270
274
|
if @type == "file"
|
|
271
|
-
f = StringIO.new(::Base64.decode64(@children.first || ""))
|
|
275
|
+
f = StringIO.new(::Base64.decode64(@children.first || ""))
|
|
272
276
|
class << f
|
|
273
277
|
attr_accessor :original_filename, :content_type
|
|
274
278
|
end
|
|
@@ -276,13 +280,13 @@ class REXMLUtilityNode # :nodoc:
|
|
|
276
280
|
f.content_type = attributes['content_type'] || 'application/octet-stream'
|
|
277
281
|
return {name => f}
|
|
278
282
|
end
|
|
279
|
-
|
|
283
|
+
|
|
280
284
|
if @text
|
|
281
285
|
return { name => typecast_value( translate_xml_entities( inner_html ) ) }
|
|
282
286
|
else
|
|
283
287
|
#change repeating groups into an array
|
|
284
288
|
groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
|
|
285
|
-
|
|
289
|
+
|
|
286
290
|
out = nil
|
|
287
291
|
if @type == "array"
|
|
288
292
|
out = []
|
|
@@ -294,7 +298,7 @@ class REXMLUtilityNode # :nodoc:
|
|
|
294
298
|
end
|
|
295
299
|
end
|
|
296
300
|
out = out.flatten
|
|
297
|
-
|
|
301
|
+
|
|
298
302
|
else # If Hash
|
|
299
303
|
out = {}
|
|
300
304
|
groups.each do |k,v|
|
|
@@ -322,11 +326,11 @@ class REXMLUtilityNode # :nodoc:
|
|
|
322
326
|
#
|
|
323
327
|
# ==== Parameters
|
|
324
328
|
# value<String>:: The value that is being typecast.
|
|
325
|
-
#
|
|
329
|
+
#
|
|
326
330
|
# ==== :type options
|
|
327
|
-
# "integer"::
|
|
331
|
+
# "integer"::
|
|
328
332
|
# converts +value+ to an integer with #to_i
|
|
329
|
-
# "boolean"::
|
|
333
|
+
# "boolean"::
|
|
330
334
|
# checks whether +value+, after removing spaces, is the literal
|
|
331
335
|
# "true"
|
|
332
336
|
# "datetime"::
|
|
@@ -338,7 +342,7 @@ class REXMLUtilityNode # :nodoc:
|
|
|
338
342
|
# Integer, true, false, Time, Date, Object::
|
|
339
343
|
# The result of typecasting +value+.
|
|
340
344
|
#
|
|
341
|
-
# ====
|
|
345
|
+
# ==== Notes
|
|
342
346
|
# If +self+ does not have a "type" key, or if it's not one of the
|
|
343
347
|
# options specified above, the raw +value+ will be returned.
|
|
344
348
|
def typecast_value(value)
|
|
@@ -388,7 +392,7 @@ class REXMLUtilityNode # :nodoc:
|
|
|
388
392
|
|
|
389
393
|
# ==== Alias
|
|
390
394
|
# #to_html
|
|
391
|
-
def to_s
|
|
395
|
+
def to_s
|
|
392
396
|
to_html
|
|
393
397
|
end
|
|
394
398
|
end
|
|
@@ -398,7 +402,7 @@ class ToHashParser # :nodoc:
|
|
|
398
402
|
def self.from_xml(xml)
|
|
399
403
|
stack = []
|
|
400
404
|
parser = REXML::Parsers::BaseParser.new(xml)
|
|
401
|
-
|
|
405
|
+
|
|
402
406
|
while true
|
|
403
407
|
event = parser.pull
|
|
404
408
|
case event[0]
|
|
@@ -419,4 +423,4 @@ class ToHashParser # :nodoc:
|
|
|
419
423
|
end
|
|
420
424
|
stack.pop.to_hash
|
|
421
425
|
end
|
|
422
|
-
end
|
|
426
|
+
end
|