6 # Locked down XmlSimple#xml_in_string
8 # Same as xml_in but doesn't try to smartly shoot itself in the foot.
9 def xml_in_string(string
, options
= nil)
10 handle_options('in', options
)
13 result
= collapse(@doc.root
)
15 if @options['keeproot']
16 merge({}, @doc.root
.name
, result
)
22 def self.xml_in_string(string
, options
= nil)
23 new
.xml_in_string(string
, options
)
27 # This module exists to decorate files deserialized using Hash.from_xml with
28 # the <tt>original_filename</tt> and <tt>content_type</tt> methods.
29 module FileLike
#:nodoc:
30 attr_writer
:original_filename, :content_type
33 @original_filename || 'untitled'
37 @content_type || 'application/octet-stream'
41 module ActiveSupport
#:nodoc:
42 module CoreExtensions
#:nodoc:
48 "Fixnum" => "integer",
49 "Bignum" => "integer",
50 "BigDecimal" => "decimal",
53 "DateTime" => "datetime",
55 "TrueClass" => "boolean",
56 "FalseClass" => "boolean"
57 } unless defined?(XML_TYPE_NAMES
)
60 "symbol" => Proc
.new
{ |symbol
| symbol
.to_s
},
61 "date" => Proc
.new
{ |date
| date
.to_s(:db) },
62 "datetime" => Proc
.new
{ |time
| time
.xmlschema
},
63 "binary" => Proc
.new
{ |binary
| ActiveSupport
::Base64.encode64(binary
) },
64 "yaml" => Proc
.new
{ |yaml
| yaml
.to_yaml
}
65 } unless defined?(XML_FORMATTING
)
67 # TODO: use Time.xmlschema instead of Time.parse;
68 # use regexp instead of Date.parse
69 unless defined?(XML_PARSING
)
71 "symbol" => Proc
.new
{ |symbol
| symbol
.to_sym
},
72 "date" => Proc
.new
{ |date
| ::Date.parse(date
) },
73 "datetime" => Proc
.new
{ |time
| ::Time.parse(time
).utc
rescue ::DateTime.parse(time
).utc
},
74 "integer" => Proc
.new
{ |integer
| integer
.to_i
},
75 "float" => Proc
.new
{ |float
| float
.to_f
},
76 "decimal" => Proc
.new
{ |number
| BigDecimal(number
) },
77 "boolean" => Proc
.new
{ |boolean
| %w(1 true).include?(boolean
.strip
) },
78 "string" => Proc
.new
{ |string
| string
.to_s
},
79 "yaml" => Proc
.new
{ |yaml
| YAML
::load(yaml
) rescue yaml
},
80 "base64Binary" => Proc
.new
{ |bin
| ActiveSupport
::Base64.decode64(bin
) },
81 "file" => Proc
.new
do |file
, entity
|
82 f
= StringIO
.new(ActiveSupport
::Base64.decode64(file
))
84 f
.original_filename
= entity
['name']
85 f
.content_type
= entity
['content_type']
91 "double" => XML_PARSING
["float"],
92 "dateTime" => XML_PARSING
["datetime"]
96 def self.included(klass
)
97 klass
.extend(ClassMethods
)
100 # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
101 # passed to enclose the param names (see example below).
104 # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
106 # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
107 def to_query(namespace
= nil)
108 collect
do |key
, value
|
109 value
.to_query(namespace
? "#{namespace}[#{key}]" : key
)
113 alias_method
:to_param, :to_query
115 def to_xml(options
= {})
116 options
[:indent] ||= 2
117 options
.reverse_merge
!({ :builder => Builder
::XmlMarkup.new(:indent => options
[:indent]),
119 options
[:builder].instruct
! unless options
.delete(:skip_instruct)
120 dasherize
= !options
.has_key
?(:dasherize) || options
[:dasherize]
121 root
= dasherize
? options
[:root].to_s
.dasherize
: options
[:root].to_s
123 options
[:builder].__send__(:method_missing, root
) do
127 value
.to_xml(options
.merge({ :root => key
, :skip_instruct => true }))
129 value
.to_xml(options
.merge({ :root => key
, :children => key
.to_s
.singularize
, :skip_instruct => true}))
130 when ::Method, ::Proc
131 # If the Method or Proc takes two arguments, then
132 # pass the suggested child element name. This is
133 # used if the Method or Proc will be operating over
134 # multiple records and needs to create an containing
135 # element that will contain the objects being
138 value
.call(options
.merge({ :root => key
, :skip_instruct => true }))
140 value
.call(options
.merge({ :root => key
, :skip_instruct => true }), key
.to_s
.singularize
)
143 if value
.respond_to
?(:to_xml)
144 value
.to_xml(options
.merge({ :root => key
, :skip_instruct => true }))
146 type_name
= XML_TYPE_NAMES
[value
.class.name
]
148 key
= dasherize
? key
.to_s
.dasherize
: key
.to_s
150 attributes
= options
[:skip_types] || value
.nil? || type_name
.nil? ? { } : { :type => type_name
}
152 attributes
[:nil] = true
155 options
[:builder].tag
!(key
,
156 XML_FORMATTING
[type_name
] ? XML_FORMATTING
[type_name
].call(value
) : value
,
163 yield options
[:builder] if block_given
?
170 # TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
171 typecast_xml_value(undasherize_keys(XmlSimple
.xml_in_string(xml
,
172 'forcearray' => false,
173 'forcecontent' => true,
175 'contentkey' => '__content__')
180 def typecast_xml_value(value
)
181 case value
.class.to_s
183 if value
['type'] == 'array'
184 child_key
, entries
= value
.detect
{ |k
,v
| k
!= 'type' } # child_key is throwaway
185 if entries
.nil? || (c
= value
['__content__'] && c
.blank
?)
188 case entries
.class.to_s
# something weird with classes not matching here. maybe singleton methods breaking is_a?
190 entries
.collect
{ |v
| typecast_xml_value(v
) }
192 [typecast_xml_value(entries
)]
194 raise "can't typecast #{entries.inspect}"
197 elsif value
.has_key
?("__content__")
198 content
= value
["__content__"]
199 if parser
= XML_PARSING
[value
["type"]]
201 XML_PARSING
[value
["type"]].call(content
, value
)
203 XML_PARSING
[value
["type"]].call(content
)
208 elsif value
['type'] == 'string' && value
['nil'] != 'true'
210 # blank or nil parsed values are represented by nil
211 elsif value
.blank
? || value
['nil'] == 'true'
213 # If the type is the only element which makes it then
214 # this still makes the value nil, except if type is
215 # a XML node(where type['value'] is a Hash)
216 elsif value
['type'] && value
.size
== 1 && !value
['type'].is_a
?(::Hash)
219 xml_value
= value
.inject({}) do |h
,(k
,v
)|
220 h
[k
] = typecast_xml_value(v
)
224 # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
225 # how multipart uploaded files from HTML appear
226 xml_value
["file"].is_a
?(StringIO
) ? xml_value
["file"] : xml_value
229 value
.map
! { |i
| typecast_xml_value(i
) }
232 when 1 then value
.first
238 raise "can't typecast #{value.class.name} - #{value.inspect}"
242 def undasherize_keys(params
)
243 case params
.class.to_s
245 params
.inject({}) do |h
,(k
,v
)|
246 h
[k
.to_s
.tr("-", "_")] = undasherize_keys(v
)
250 params
.map
{ |v
| undasherize_keys(v
) }