3 module ActiveSupport
#:nodoc:
4 module CoreExtensions
#:nodoc:
7 # This module exists to decorate files deserialized using Hash.from_xml with
8 # the <tt>original_filename</tt> and <tt>content_type</tt> methods.
9 module FileLike
#:nodoc:
10 attr_writer
:original_filename, :content_type
13 @original_filename || 'untitled'
17 @content_type || 'application/octet-stream'
23 "Fixnum" => "integer",
24 "Bignum" => "integer",
25 "BigDecimal" => "decimal",
27 "TrueClass" => "boolean",
28 "FalseClass" => "boolean",
30 "DateTime" => "datetime",
32 "ActiveSupport::TimeWithZone" => "datetime"
33 } unless defined?(XML_TYPE_NAMES
)
36 "symbol" => Proc
.new
{ |symbol
| symbol
.to_s
},
37 "date" => Proc
.new
{ |date
| date
.to_s(:db) },
38 "datetime" => Proc
.new
{ |time
| time
.xmlschema
},
39 "binary" => Proc
.new
{ |binary
| ActiveSupport
::Base64.encode64(binary
) },
40 "yaml" => Proc
.new
{ |yaml
| yaml
.to_yaml
}
41 } unless defined?(XML_FORMATTING
)
43 # TODO: use Time.xmlschema instead of Time.parse;
44 # use regexp instead of Date.parse
45 unless defined?(XML_PARSING
)
47 "symbol" => Proc
.new
{ |symbol
| symbol
.to_sym
},
48 "date" => Proc
.new
{ |date
| ::Date.parse(date
) },
49 "datetime" => Proc
.new
{ |time
| ::Time.parse(time
).utc
rescue ::DateTime.parse(time
).utc
},
50 "integer" => Proc
.new
{ |integer
| integer
.to_i
},
51 "float" => Proc
.new
{ |float
| float
.to_f
},
52 "decimal" => Proc
.new
{ |number
| BigDecimal(number
) },
53 "boolean" => Proc
.new
{ |boolean
| %w(1 true).include?(boolean
.strip
) },
54 "string" => Proc
.new
{ |string
| string
.to_s
},
55 "yaml" => Proc
.new
{ |yaml
| YAML
::load(yaml
) rescue yaml
},
56 "base64Binary" => Proc
.new
{ |bin
| ActiveSupport
::Base64.decode64(bin
) },
57 "file" => Proc
.new
do |file
, entity
|
58 f
= StringIO
.new(ActiveSupport
::Base64.decode64(file
))
60 f
.original_filename
= entity
['name']
61 f
.content_type
= entity
['content_type']
67 "double" => XML_PARSING
["float"],
68 "dateTime" => XML_PARSING
["datetime"]
72 def self.included(klass
)
73 klass
.extend(ClassMethods
)
76 # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
77 # passed to enclose the param names (see example below).
80 # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
82 # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
83 def to_query(namespace
= nil)
84 collect
do |key
, value
|
85 value
.to_query(namespace
? "#{namespace}[#{key}]" : key
)
89 alias_method
:to_param, :to_query
91 def to_xml(options
= {})
92 require 'builder' unless defined?(Builder
)
94 options
[:indent] ||= 2
95 options
.reverse_merge
!({ :builder => Builder
::XmlMarkup.new(:indent => options
[:indent]),
97 options
[:builder].instruct
! unless options
.delete(:skip_instruct)
98 root
= rename_key(options
[:root].to_s
, options
)
100 options
[:builder].__send__(:method_missing, root
) do
104 value
.to_xml(options
.merge({ :root => key
, :skip_instruct => true }))
106 value
.to_xml(options
.merge({ :root => key
, :children => key
.to_s
.singularize
, :skip_instruct => true}))
107 when ::Method, ::Proc
108 # If the Method or Proc takes two arguments, then
109 # pass the suggested child element name. This is
110 # used if the Method or Proc will be operating over
111 # multiple records and needs to create an containing
112 # element that will contain the objects being
115 value
.call(options
.merge({ :root => key
, :skip_instruct => true }))
117 value
.call(options
.merge({ :root => key
, :skip_instruct => true }), key
.to_s
.singularize
)
120 if value
.respond_to
?(:to_xml)
121 value
.to_xml(options
.merge({ :root => key
, :skip_instruct => true }))
123 type_name
= XML_TYPE_NAMES
[value
.class.name
]
125 key
= rename_key(key
.to_s
, options
)
127 attributes
= options
[:skip_types] || value
.nil? || type_name
.nil? ? { } : { :type => type_name
}
129 attributes
[:nil] = true
132 options
[:builder].tag
!(key
,
133 XML_FORMATTING
[type_name
] ? XML_FORMATTING
[type_name
].call(value
) : value
,
140 yield options
[:builder] if block_given
?
145 def rename_key(key
, options
= {})
146 camelize
= options
.has_key
?(:camelize) && options
[:camelize]
147 dasherize
= !options
.has_key
?(:dasherize) || options
[:dasherize]
148 key
= key
.camelize
if camelize
149 dasherize
? key
.dasherize
: key
154 typecast_xml_value(unrename_keys(XmlMini
.parse(xml
)))
158 def typecast_xml_value(value
)
159 case value
.class.to_s
161 if value
['type'] == 'array'
162 child_key
, entries
= value
.detect
{ |k
,v
| k
!= 'type' } # child_key is throwaway
163 if entries
.nil? || (c
= value
['__content__'] && c
.blank
?)
166 case entries
.class.to_s
# something weird with classes not matching here. maybe singleton methods breaking is_a?
168 entries
.collect
{ |v
| typecast_xml_value(v
) }
170 [typecast_xml_value(entries
)]
172 raise "can't typecast #{entries.inspect}"
175 elsif value
.has_key
?("__content__")
176 content
= value
["__content__"]
177 if parser
= XML_PARSING
[value
["type"]]
179 XML_PARSING
[value
["type"]].call(content
, value
)
181 XML_PARSING
[value
["type"]].call(content
)
186 elsif value
['type'] == 'string' && value
['nil'] != 'true'
188 # blank or nil parsed values are represented by nil
189 elsif value
.blank
? || value
['nil'] == 'true'
191 # If the type is the only element which makes it then
192 # this still makes the value nil, except if type is
193 # a XML node(where type['value'] is a Hash)
194 elsif value
['type'] && value
.size
== 1 && !value
['type'].is_a
?(::Hash)
197 xml_value
= value
.inject({}) do |h
,(k
,v
)|
198 h
[k
] = typecast_xml_value(v
)
202 # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
203 # how multipart uploaded files from HTML appear
204 xml_value
["file"].is_a
?(StringIO
) ? xml_value
["file"] : xml_value
207 value
.map
! { |i
| typecast_xml_value(i
) }
210 when 1 then value
.first
216 raise "can't typecast #{value.class.name} - #{value.inspect}"
220 def unrename_keys(params
)
221 case params
.class.to_s
223 params
.inject({}) do |h
,(k
,v
)|
224 h
[k
.to_s
.underscore
.tr("-", "_")] = unrename_keys(v
)
228 params
.map
{ |v
| unrename_keys(v
) }