merb 0.4.2 → 0.5.0
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/README +21 -14
- data/Rakefile +157 -108
- data/SVN_REVISION +1 -0
- data/app_generators/merb/templates/Rakefile +20 -4
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +1 -1
- data/app_generators/merb/templates/config/boot.rb +1 -1
- data/app_generators/merb/templates/config/dependencies.rb +3 -3
- data/app_generators/merb/templates/config/merb.yml +5 -0
- data/app_generators/merb/templates/config/merb_init.rb +3 -3
- data/app_generators/merb/templates/script/destroy +3 -0
- data/app_generators/merb/templates/script/generate +1 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +2 -2
- data/app_generators/merb/templates/test/test_helper.rb +1 -1
- data/app_generators/merb_plugin/merb_plugin_generator.rb +4 -0
- data/bin/merb +1 -3
- data/lib/merb.rb +144 -76
- data/lib/merb/abstract_controller.rb +6 -5
- data/lib/merb/assets.rb +119 -0
- data/lib/merb/boot_loader.rb +217 -0
- data/lib/merb/caching.rb +1 -1
- data/lib/merb/caching/action_cache.rb +1 -1
- data/lib/merb/caching/fragment_cache.rb +1 -1
- data/lib/merb/caching/store/file_cache.rb +1 -1
- data/lib/merb/config.rb +290 -0
- data/lib/merb/controller.rb +5 -5
- data/lib/merb/core_ext/get_args.rb +1 -0
- data/lib/merb/core_ext/hash.rb +182 -169
- data/lib/merb/core_ext/kernel.rb +57 -26
- data/lib/merb/dispatcher.rb +6 -6
- data/lib/merb/drb_server.rb +1 -1
- data/lib/merb/generators/merb_generator_helpers.rb +7 -6
- data/lib/merb/logger.rb +1 -1
- data/lib/merb/mail_controller.rb +3 -4
- data/lib/merb/mailer.rb +2 -2
- data/lib/merb/mixins/basic_authentication.rb +2 -2
- data/lib/merb/mixins/controller.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +13 -20
- data/lib/merb/mixins/inline_partial.rb +32 -0
- data/lib/merb/mixins/render.rb +3 -3
- data/lib/merb/mixins/responder.rb +1 -1
- data/lib/merb/mixins/view_context.rb +159 -33
- data/lib/merb/mongrel_handler.rb +9 -9
- data/lib/merb/plugins.rb +1 -1
- data/lib/merb/request.rb +25 -1
- data/lib/merb/router.rb +264 -226
- data/lib/merb/server.rb +66 -560
- data/lib/merb/session/cookie_store.rb +14 -13
- data/lib/merb/session/mem_cache_session.rb +20 -10
- data/lib/merb/session/memory_session.rb +21 -11
- data/lib/merb/template.rb +2 -2
- data/lib/merb/template/erubis.rb +3 -33
- data/lib/merb/template/haml.rb +8 -3
- data/lib/merb/test/fake_request.rb +8 -3
- data/lib/merb/test/helper.rb +66 -22
- data/lib/merb/test/rspec.rb +9 -155
- data/lib/merb/test/rspec_matchers/controller_matchers.rb +117 -0
- data/lib/merb/test/rspec_matchers/markup_matchers.rb +98 -0
- data/lib/merb/upload_handler.rb +2 -1
- data/lib/merb/version.rb +38 -3
- data/lib/merb/view_context.rb +1 -2
- data/lib/tasks/merb.rake +11 -11
- data/merb_generators/part_controller/USAGE +5 -0
- data/merb_generators/part_controller/part_controller_generator.rb +27 -0
- data/merb_generators/part_controller/templates/controller.rb +8 -0
- data/merb_generators/part_controller/templates/helper.rb +5 -0
- data/merb_generators/part_controller/templates/index.html.erb +3 -0
- data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +1 -1
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/fixtures/controllers/dispatch_spec_controllers.rb +9 -1
- data/spec/fixtures/controllers/render_spec_controllers.rb +5 -5
- data/spec/fixtures/models/router_spec_models.rb +10 -0
- data/spec/merb/abstract_controller_spec.rb +2 -2
- data/spec/merb/assets_spec.rb +207 -0
- data/spec/merb/caching_spec.rb +2 -2
- data/spec/merb/controller_spec.rb +7 -2
- data/spec/merb/cookie_store_spec.rb +1 -1
- data/spec/merb/core_ext/class_spec.rb +97 -0
- data/spec/merb/core_ext/enumerable_spec.rb +27 -0
- data/spec/merb/core_ext/hash_spec.rb +251 -0
- data/spec/merb/core_ext/inflector_spec.rb +34 -0
- data/spec/merb/core_ext/kernel_spec.rb +25 -0
- data/spec/merb/core_ext/numeric_spec.rb +26 -0
- data/spec/merb/core_ext/object_spec.rb +47 -0
- data/spec/merb/core_ext/string_spec.rb +22 -0
- data/spec/merb/core_ext/symbol_spec.rb +7 -0
- data/spec/merb/dependency_spec.rb +22 -0
- data/spec/merb/dispatch_spec.rb +23 -12
- data/spec/merb/fake_request_spec.rb +8 -0
- data/spec/merb/generator_spec.rb +140 -21
- data/spec/merb/handler_spec.rb +5 -5
- data/spec/merb/mail_controller_spec.rb +3 -3
- data/spec/merb/render_spec.rb +1 -1
- data/spec/merb/responder_spec.rb +3 -3
- data/spec/merb/router_spec.rb +260 -191
- data/spec/merb/server_spec.rb +5 -5
- data/spec/merb/upload_handler_spec.rb +7 -0
- data/spec/merb/version_spec.rb +33 -0
- data/spec/merb/view_context_spec.rb +217 -59
- data/spec/spec_generator_helper.rb +15 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/spec_helpers/url_shared_behaviour.rb +5 -7
- metadata +32 -7
- data/lib/merb/caching/store/memcache.rb +0 -20
- data/lib/merb/mixins/form_control.rb +0 -332
- data/lib/patch +0 -69
- data/spec/merb/core_ext_spec.rb +0 -464
- data/spec/merb/form_control_mixin_spec.rb +0 -431
data/lib/merb/controller.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Merb
|
|
|
18
18
|
#
|
|
19
19
|
# === Session Store
|
|
20
20
|
#
|
|
21
|
-
# The session store is set in
|
|
21
|
+
# The session store is set in Merb.root/config/merb.yml :
|
|
22
22
|
#
|
|
23
23
|
# :session_store: your_store
|
|
24
24
|
#
|
|
@@ -83,14 +83,14 @@ module Merb
|
|
|
83
83
|
|
|
84
84
|
def set_dispatch_variables(request, response, status, headers)
|
|
85
85
|
if request.params.key?(_session_id_key)
|
|
86
|
-
if Merb::
|
|
86
|
+
if Merb::Config[:session_id_cookie_only]
|
|
87
87
|
# This condition allows for certain controller/action paths to allow
|
|
88
88
|
# a session ID to be passed in a query string. This is needed for
|
|
89
89
|
# Flash Uploads to work since flash will not pass a Session Cookie
|
|
90
90
|
# Recommend running session.regenerate after any controller taking
|
|
91
91
|
# advantage of this in case someone is attempting a session fixation
|
|
92
92
|
# attack
|
|
93
|
-
if Merb::
|
|
93
|
+
if Merb::Config[:query_string_whitelist].include?("#{request.controller_name}/#{request.action}")
|
|
94
94
|
# FIXME to use routes not controller and action names -----^
|
|
95
95
|
request.cookies[_session_id_key] = request.params[_session_id_key]
|
|
96
96
|
end
|
|
@@ -105,7 +105,7 @@ module Merb
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def dispatch(action=:index)
|
|
108
|
-
start = Time.now
|
|
108
|
+
start = Time.now
|
|
109
109
|
if self.class.callable_actions[action.to_s]
|
|
110
110
|
params[:action] ||= action
|
|
111
111
|
setup_session
|
|
@@ -115,7 +115,7 @@ module Merb
|
|
|
115
115
|
raise ActionNotFound, "Action '#{action}' was not found in #{self.class}"
|
|
116
116
|
end
|
|
117
117
|
@_benchmarks[:action_time] = Time.now - start
|
|
118
|
-
|
|
118
|
+
Merb.logger.info("Time spent in #{self.class}##{action} action: #{@_benchmarks[:action_time]} seconds")
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# Accessor for @_body. Please use body and never @body directly.
|
data/lib/merb/core_ext/hash.rb
CHANGED
|
@@ -1,139 +1,154 @@
|
|
|
1
|
-
#require 'hpricot'
|
|
1
|
+
# require 'hpricot'
|
|
2
2
|
|
|
3
3
|
class Hash
|
|
4
|
-
|
|
5
4
|
class << self
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
5
|
+
# Converts valid XML into a Ruby Hash structure.
|
|
6
|
+
# <tt>xml</tt>:: A string representation of valid XML
|
|
7
|
+
#
|
|
8
|
+
# == Typecasting
|
|
9
|
+
# Typecasting is performed on elements that have a "<tt>type</tt>" attribute of
|
|
10
|
+
# <tt>integer</tt>::
|
|
11
|
+
# <tt>boolean</tt>:: anything other than "true" evaluates to false
|
|
12
|
+
# <tt>datetime</tt>:: Returns a Time object. See +Time+ documentation for valid Time strings
|
|
13
|
+
# <tt>date</tt>:: Returns a Date object. See +Date+ documentation for valid Date strings
|
|
14
|
+
#
|
|
15
|
+
# Keys are automatically converted to +snake_case+
|
|
16
|
+
#
|
|
17
|
+
# == Caveats
|
|
18
|
+
# * Mixed content tags are assumed to be text and any xml tags are kept as a String
|
|
19
|
+
# * Any attributes other than type on a node containing a text node will be discarded
|
|
20
|
+
#
|
|
21
|
+
# == Examples
|
|
22
|
+
#
|
|
23
|
+
# === Standard
|
|
24
|
+
# <user gender='m'>
|
|
25
|
+
# <age type='integer'>35</age>
|
|
26
|
+
# <name>Home Simpson</name>
|
|
27
|
+
# <dob type='date'>1988-01-01</dob>
|
|
28
|
+
# <joined-at type='datetime'>2000-04-28 23:01</joined-at>
|
|
29
|
+
# <is-cool type='boolean'>true</is-cool>
|
|
30
|
+
# </user>
|
|
31
|
+
#
|
|
32
|
+
# evaluates to
|
|
33
|
+
#
|
|
34
|
+
# { "user" =>
|
|
35
|
+
# { "gender" => "m",
|
|
36
|
+
# "age" => 35,
|
|
37
|
+
# "name" => "Home Simpson",
|
|
38
|
+
# "dob" => DateObject( 1998-01-01 ),
|
|
39
|
+
# "joined_at" => TimeObject( 2000-04-28 23:01),
|
|
40
|
+
# "is_cool" => true
|
|
41
|
+
# }
|
|
42
|
+
# }
|
|
43
|
+
#
|
|
44
|
+
# === Mixed Content
|
|
45
|
+
# <story>
|
|
46
|
+
# A Quick <em>brown</em> Fox
|
|
47
|
+
# </story>
|
|
48
|
+
#
|
|
49
|
+
# evaluates to
|
|
50
|
+
# { "story" => "A Quick <em>brown</em> Fox" }
|
|
51
|
+
#
|
|
52
|
+
# === Attributes other than type on a node containing text
|
|
53
|
+
# <story is-good='fasle'>
|
|
54
|
+
# A Quick <em>brown</em> Fox
|
|
55
|
+
# </story>
|
|
56
|
+
#
|
|
57
|
+
# evaluates to
|
|
58
|
+
# { "story" => "A Quick <em>brown</em> Fox" }
|
|
59
|
+
#
|
|
60
|
+
# <bicep unit='inches' type='integer'>60</bicep>
|
|
61
|
+
#
|
|
62
|
+
# evaluates with a typecast to an integer. But ignores the unit attribute
|
|
63
|
+
# { "bicep" => 60 }
|
|
64
|
+
def from_xml( xml )
|
|
65
|
+
ToHashParser.from_xml(xml)
|
|
66
|
+
end
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
# convert this hash into a Mash for string or symbol key access
|
|
72
70
|
def to_mash
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
hash = Mash.new(self)
|
|
72
|
+
hash.default = default
|
|
73
|
+
hash
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Convert this hash to a query string:
|
|
77
|
+
#
|
|
78
|
+
# { :name => "Bob",
|
|
79
|
+
# :address => {
|
|
80
|
+
# :street => '111 Ruby Ave.',
|
|
81
|
+
# :city => 'Ruby Central',
|
|
82
|
+
# :phones => ['111-111-1111', '222-222-2222']
|
|
83
|
+
# }
|
|
84
|
+
# }.to_params
|
|
80
85
|
# #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
|
|
86
|
+
#
|
|
81
87
|
def to_params
|
|
82
|
-
|
|
88
|
+
params = ''
|
|
83
89
|
stack = []
|
|
84
|
-
|
|
85
|
-
each do |
|
|
86
|
-
|
|
90
|
+
|
|
91
|
+
each do |k, v|
|
|
92
|
+
if v.is_a?(Hash)
|
|
93
|
+
stack << [k,v]
|
|
94
|
+
else
|
|
95
|
+
params << "#{k}=#{v}&"
|
|
96
|
+
end
|
|
87
97
|
end
|
|
88
|
-
|
|
98
|
+
|
|
89
99
|
stack.each do |parent, hash|
|
|
90
|
-
hash.each do |
|
|
91
|
-
if Hash
|
|
92
|
-
stack << ["#{parent}[#{
|
|
100
|
+
hash.each do |k, v|
|
|
101
|
+
if v.is_a?(Hash)
|
|
102
|
+
stack << ["#{parent}[#{k}]", v]
|
|
93
103
|
else
|
|
94
|
-
|
|
104
|
+
params << "#{parent}[#{k}]=#{v}&"
|
|
95
105
|
end
|
|
96
106
|
end
|
|
97
107
|
end
|
|
98
|
-
|
|
108
|
+
|
|
109
|
+
params.chop! # trailing &
|
|
110
|
+
params
|
|
99
111
|
end
|
|
100
112
|
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
# Returns a new Hash with only the selected keys:
|
|
114
|
+
#
|
|
115
|
+
# { :one => 1, :two => 2, :three => 3 }.only(:one)
|
|
116
|
+
# #=> { :one => 1 }
|
|
117
|
+
def only(*allowed)
|
|
118
|
+
reject { |k,v| !allowed.include?(k) }
|
|
106
119
|
end
|
|
107
|
-
alias only pass
|
|
108
120
|
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
# Returns a new hash without the selected keys:
|
|
122
|
+
#
|
|
123
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
|
124
|
+
# #=> { :two => 2, :three => 3 }
|
|
125
|
+
#
|
|
126
|
+
def except(*rejected)
|
|
127
|
+
reject { |k,v| rejected.include?(k) }
|
|
114
128
|
end
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
|
|
117
130
|
# Converts the hash into xml attributes
|
|
131
|
+
#
|
|
118
132
|
# { :one => "ONE", "two"=>"TWO" }.to_xml_attributes
|
|
119
133
|
# #=> 'one="ONE" two="TWO"'
|
|
134
|
+
#
|
|
120
135
|
def to_xml_attributes
|
|
121
136
|
map do |k,v|
|
|
122
|
-
|
|
123
|
-
end.join(
|
|
137
|
+
%{#{k.to_s.camelize.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
|
|
138
|
+
end.join(' ')
|
|
124
139
|
end
|
|
125
140
|
|
|
126
141
|
alias_method :to_html_attributes, :to_xml_attributes
|
|
127
|
-
|
|
142
|
+
|
|
128
143
|
# Adds the given class symbol or string to the hash in the
|
|
129
144
|
# :class key. This will add a html class if there are already any existing
|
|
130
145
|
# or create the key and add this as the first class
|
|
131
|
-
#
|
|
132
|
-
# Example
|
|
146
|
+
#
|
|
133
147
|
# @hash[:class] #=> nil
|
|
134
148
|
# @hash.add_html_class!(:selected) #=> @hash[:class] == "selected"
|
|
135
|
-
#
|
|
149
|
+
#
|
|
136
150
|
# @hash.add_html_class!("class1 class2") #=> @hash[:class] == "selected class1 class2"
|
|
151
|
+
#
|
|
137
152
|
def add_html_class!(html_class)
|
|
138
153
|
if self[:class]
|
|
139
154
|
self[:class] = "#{self[:class]} #{html_class}"
|
|
@@ -142,40 +157,38 @@ class Hash
|
|
|
142
157
|
end
|
|
143
158
|
end
|
|
144
159
|
|
|
145
|
-
# Destructively convert all keys to symbols
|
|
160
|
+
# Destructively convert all keys which respond_to?(:to_sym) to symbols.
|
|
161
|
+
# Works recursively if given nested hashes.
|
|
162
|
+
#
|
|
163
|
+
# { 'one' => 1, 'two' => 2 }.symbolize_keys!
|
|
164
|
+
# #=> { :one => 1, :two => 2 }
|
|
165
|
+
#
|
|
146
166
|
def symbolize_keys!
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
end
|
|
152
|
-
if Hash === (sub = self[key.to_sym])
|
|
153
|
-
sub.symbolize_keys!
|
|
154
|
-
end
|
|
167
|
+
each do |k,v|
|
|
168
|
+
sym = k.respond_to?(:to_sym) ? k.to_sym : k
|
|
169
|
+
self[sym] = Hash === v ? v.symbolize_keys! : v
|
|
170
|
+
delete(k) unless k == sym
|
|
155
171
|
end
|
|
156
172
|
self
|
|
157
173
|
end
|
|
158
174
|
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
175
|
+
# Destructively and non-recursively convert each key to an uppercase string.
|
|
176
|
+
#
|
|
177
|
+
# { :name => "Bob", "age" => 12, "nick" => "Bobinator" }.environmentize_keys!
|
|
178
|
+
# #=> { "NAME" => "Bob", "NICK" => "Bobinator", "AGE" => 12 }
|
|
179
|
+
#
|
|
162
180
|
def environmentize_keys!
|
|
163
|
-
|
|
181
|
+
keys.each do |key|
|
|
164
182
|
self[key.to_s.upcase] = delete(key)
|
|
165
183
|
end
|
|
166
184
|
self
|
|
167
185
|
end
|
|
168
|
-
|
|
169
|
-
def method_missing(m,*a) #:nodoc:
|
|
170
|
-
m.to_s =~ /=$/ ? self[$`]=a[0] : a==[] ? self[m] : raise(NoMethodError,"#{m}")
|
|
171
|
-
end
|
|
172
186
|
|
|
173
|
-
def respond_to?(method, include_private=false)
|
|
187
|
+
def respond_to?(method, include_private = false)
|
|
174
188
|
return true if keys.include?(method)
|
|
175
189
|
super(method, include_private)
|
|
176
190
|
end
|
|
177
|
-
|
|
178
|
-
end
|
|
191
|
+
end
|
|
179
192
|
|
|
180
193
|
require 'rexml/parsers/streamparser'
|
|
181
194
|
require 'rexml/parsers/baseparser'
|
|
@@ -208,29 +221,25 @@ class REXMLUtilityNode # :nodoc:
|
|
|
208
221
|
#change repeating groups into an array
|
|
209
222
|
# group by the first key of each element of the array to find repeating groups
|
|
210
223
|
groups = @children.group_by{ |c| c.name }
|
|
211
|
-
|
|
212
|
-
hash = {}
|
|
224
|
+
|
|
225
|
+
hash = {}
|
|
213
226
|
groups.each do |key, values|
|
|
214
227
|
if values.size == 1
|
|
215
|
-
hash.merge!
|
|
228
|
+
hash.merge! values.first
|
|
216
229
|
else
|
|
217
|
-
hash.merge!
|
|
230
|
+
hash.merge! key => values.map { |element| element.to_hash[key] }
|
|
218
231
|
end
|
|
219
232
|
end
|
|
220
|
-
|
|
233
|
+
|
|
221
234
|
# merge the arrays, including attributes
|
|
222
|
-
hash.merge!
|
|
223
|
-
|
|
235
|
+
hash.merge! attributes unless attributes.empty?
|
|
236
|
+
|
|
237
|
+
{ name => hash }
|
|
224
238
|
end
|
|
225
239
|
end
|
|
226
|
-
|
|
227
|
-
def to_s
|
|
228
|
-
self.to_html
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
|
|
240
|
+
|
|
232
241
|
def typecast_value(value)
|
|
233
|
-
return value unless attributes["type"]
|
|
242
|
+
return value unless attributes["type"]
|
|
234
243
|
|
|
235
244
|
case attributes["type"]
|
|
236
245
|
when "integer" then value.to_i
|
|
@@ -240,7 +249,7 @@ class REXMLUtilityNode # :nodoc:
|
|
|
240
249
|
else value
|
|
241
250
|
end
|
|
242
251
|
end
|
|
243
|
-
|
|
252
|
+
|
|
244
253
|
def translate_xml_entities(value)
|
|
245
254
|
value.gsub(/</, "<").
|
|
246
255
|
gsub(/>/, ">").
|
|
@@ -248,46 +257,50 @@ class REXMLUtilityNode # :nodoc:
|
|
|
248
257
|
gsub(/'/, "'").
|
|
249
258
|
gsub(/&/, "&")
|
|
250
259
|
end
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
260
|
+
|
|
261
|
+
def undasherize_keys(params)
|
|
262
|
+
params.keys.each do |key, vvalue|
|
|
263
|
+
params[key.tr("-", "_")] = params.delete(key)
|
|
264
|
+
end
|
|
265
|
+
params
|
|
257
266
|
end
|
|
258
|
-
|
|
267
|
+
|
|
259
268
|
def inner_html
|
|
260
269
|
@children.join
|
|
261
270
|
end
|
|
262
|
-
|
|
263
|
-
def to_html
|
|
271
|
+
|
|
272
|
+
def to_html
|
|
264
273
|
"<#{name}#{attributes.to_xml_attributes}>#{inner_html}</#{name}>"
|
|
265
274
|
end
|
|
275
|
+
|
|
276
|
+
def to_s
|
|
277
|
+
to_html
|
|
278
|
+
end
|
|
266
279
|
end
|
|
267
280
|
|
|
268
281
|
class ToHashParser # :nodoc:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
282
|
+
def self.from_xml(xml)
|
|
283
|
+
stack = []
|
|
284
|
+
parser = REXML::Parsers::BaseParser.new(xml)
|
|
285
|
+
|
|
286
|
+
while true
|
|
287
|
+
event = parser.pull
|
|
288
|
+
case event[0]
|
|
289
|
+
when :end_document
|
|
290
|
+
break
|
|
291
|
+
when :end_doctype, :start_doctype
|
|
292
|
+
# do nothing
|
|
293
|
+
when :start_element
|
|
294
|
+
stack.push REXMLUtilityNode.new(event[1], event[2])
|
|
295
|
+
when :end_element
|
|
296
|
+
if stack.size > 1
|
|
297
|
+
temp = stack.pop
|
|
298
|
+
stack.last.add_node(temp)
|
|
299
|
+
end
|
|
300
|
+
when :text, :cdata
|
|
301
|
+
stack.last.add_node(event[1]) unless event[1].strip.length == 0
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
stack.pop.to_hash
|
|
305
|
+
end
|
|
293
306
|
end
|