core_ext 0.0.1

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.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/core_ext/array/access.rb +76 -0
  4. data/lib/core_ext/array/conversions.rb +211 -0
  5. data/lib/core_ext/array/extract_options.rb +29 -0
  6. data/lib/core_ext/array/grouping.rb +116 -0
  7. data/lib/core_ext/array/inquiry.rb +17 -0
  8. data/lib/core_ext/array/prepend_and_append.rb +7 -0
  9. data/lib/core_ext/array/wrap.rb +46 -0
  10. data/lib/core_ext/array.rb +7 -0
  11. data/lib/core_ext/array_inquirer.rb +44 -0
  12. data/lib/core_ext/benchmark.rb +14 -0
  13. data/lib/core_ext/benchmarkable.rb +49 -0
  14. data/lib/core_ext/big_decimal/conversions.rb +14 -0
  15. data/lib/core_ext/big_decimal.rb +1 -0
  16. data/lib/core_ext/builder.rb +6 -0
  17. data/lib/core_ext/callbacks.rb +770 -0
  18. data/lib/core_ext/class/attribute.rb +128 -0
  19. data/lib/core_ext/class/attribute_accessors.rb +4 -0
  20. data/lib/core_ext/class/subclasses.rb +42 -0
  21. data/lib/core_ext/class.rb +2 -0
  22. data/lib/core_ext/concern.rb +142 -0
  23. data/lib/core_ext/configurable.rb +148 -0
  24. data/lib/core_ext/date/acts_like.rb +8 -0
  25. data/lib/core_ext/date/blank.rb +12 -0
  26. data/lib/core_ext/date/calculations.rb +143 -0
  27. data/lib/core_ext/date/conversions.rb +93 -0
  28. data/lib/core_ext/date/zones.rb +6 -0
  29. data/lib/core_ext/date.rb +5 -0
  30. data/lib/core_ext/date_and_time/calculations.rb +328 -0
  31. data/lib/core_ext/date_and_time/zones.rb +40 -0
  32. data/lib/core_ext/date_time/acts_like.rb +14 -0
  33. data/lib/core_ext/date_time/blank.rb +12 -0
  34. data/lib/core_ext/date_time/calculations.rb +177 -0
  35. data/lib/core_ext/date_time/conversions.rb +104 -0
  36. data/lib/core_ext/date_time/zones.rb +6 -0
  37. data/lib/core_ext/date_time.rb +5 -0
  38. data/lib/core_ext/deprecation/behaviors.rb +86 -0
  39. data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
  40. data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
  41. data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
  42. data/lib/core_ext/deprecation/reporting.rb +105 -0
  43. data/lib/core_ext/deprecation.rb +43 -0
  44. data/lib/core_ext/digest/uuid.rb +51 -0
  45. data/lib/core_ext/duration.rb +157 -0
  46. data/lib/core_ext/enumerable.rb +106 -0
  47. data/lib/core_ext/file/atomic.rb +68 -0
  48. data/lib/core_ext/file.rb +1 -0
  49. data/lib/core_ext/hash/compact.rb +20 -0
  50. data/lib/core_ext/hash/conversions.rb +261 -0
  51. data/lib/core_ext/hash/deep_merge.rb +38 -0
  52. data/lib/core_ext/hash/except.rb +22 -0
  53. data/lib/core_ext/hash/indifferent_access.rb +23 -0
  54. data/lib/core_ext/hash/keys.rb +170 -0
  55. data/lib/core_ext/hash/reverse_merge.rb +22 -0
  56. data/lib/core_ext/hash/slice.rb +48 -0
  57. data/lib/core_ext/hash/transform_values.rb +29 -0
  58. data/lib/core_ext/hash.rb +9 -0
  59. data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
  60. data/lib/core_ext/inflections.rb +70 -0
  61. data/lib/core_ext/inflector/inflections.rb +244 -0
  62. data/lib/core_ext/inflector/methods.rb +381 -0
  63. data/lib/core_ext/inflector/transliterate.rb +112 -0
  64. data/lib/core_ext/inflector.rb +7 -0
  65. data/lib/core_ext/integer/inflections.rb +29 -0
  66. data/lib/core_ext/integer/multiple.rb +10 -0
  67. data/lib/core_ext/integer/time.rb +29 -0
  68. data/lib/core_ext/integer.rb +3 -0
  69. data/lib/core_ext/json/decoding.rb +67 -0
  70. data/lib/core_ext/json/encoding.rb +127 -0
  71. data/lib/core_ext/json.rb +2 -0
  72. data/lib/core_ext/kernel/agnostics.rb +11 -0
  73. data/lib/core_ext/kernel/concern.rb +10 -0
  74. data/lib/core_ext/kernel/reporting.rb +41 -0
  75. data/lib/core_ext/kernel/singleton_class.rb +6 -0
  76. data/lib/core_ext/kernel.rb +4 -0
  77. data/lib/core_ext/load_error.rb +30 -0
  78. data/lib/core_ext/logger.rb +57 -0
  79. data/lib/core_ext/logger_silence.rb +24 -0
  80. data/lib/core_ext/marshal.rb +19 -0
  81. data/lib/core_ext/module/aliasing.rb +74 -0
  82. data/lib/core_ext/module/anonymous.rb +28 -0
  83. data/lib/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/core_ext/module/concerning.rb +135 -0
  86. data/lib/core_ext/module/delegation.rb +218 -0
  87. data/lib/core_ext/module/deprecation.rb +23 -0
  88. data/lib/core_ext/module/introspection.rb +62 -0
  89. data/lib/core_ext/module/method_transplanting.rb +3 -0
  90. data/lib/core_ext/module/qualified_const.rb +52 -0
  91. data/lib/core_ext/module/reachable.rb +8 -0
  92. data/lib/core_ext/module/remove_method.rb +35 -0
  93. data/lib/core_ext/module.rb +11 -0
  94. data/lib/core_ext/multibyte/chars.rb +231 -0
  95. data/lib/core_ext/multibyte/unicode.rb +388 -0
  96. data/lib/core_ext/multibyte.rb +21 -0
  97. data/lib/core_ext/name_error.rb +31 -0
  98. data/lib/core_ext/numeric/bytes.rb +64 -0
  99. data/lib/core_ext/numeric/conversions.rb +132 -0
  100. data/lib/core_ext/numeric/inquiry.rb +26 -0
  101. data/lib/core_ext/numeric/time.rb +74 -0
  102. data/lib/core_ext/numeric.rb +4 -0
  103. data/lib/core_ext/object/acts_like.rb +10 -0
  104. data/lib/core_ext/object/blank.rb +140 -0
  105. data/lib/core_ext/object/conversions.rb +4 -0
  106. data/lib/core_ext/object/deep_dup.rb +53 -0
  107. data/lib/core_ext/object/duplicable.rb +98 -0
  108. data/lib/core_ext/object/inclusion.rb +27 -0
  109. data/lib/core_ext/object/instance_variables.rb +28 -0
  110. data/lib/core_ext/object/json.rb +199 -0
  111. data/lib/core_ext/object/to_param.rb +1 -0
  112. data/lib/core_ext/object/to_query.rb +84 -0
  113. data/lib/core_ext/object/try.rb +146 -0
  114. data/lib/core_ext/object/with_options.rb +69 -0
  115. data/lib/core_ext/object.rb +14 -0
  116. data/lib/core_ext/option_merger.rb +25 -0
  117. data/lib/core_ext/ordered_hash.rb +48 -0
  118. data/lib/core_ext/ordered_options.rb +81 -0
  119. data/lib/core_ext/range/conversions.rb +34 -0
  120. data/lib/core_ext/range/each.rb +21 -0
  121. data/lib/core_ext/range/include_range.rb +23 -0
  122. data/lib/core_ext/range/overlaps.rb +8 -0
  123. data/lib/core_ext/range.rb +4 -0
  124. data/lib/core_ext/regexp.rb +5 -0
  125. data/lib/core_ext/rescuable.rb +119 -0
  126. data/lib/core_ext/securerandom.rb +23 -0
  127. data/lib/core_ext/security_utils.rb +20 -0
  128. data/lib/core_ext/string/access.rb +104 -0
  129. data/lib/core_ext/string/behavior.rb +6 -0
  130. data/lib/core_ext/string/conversions.rb +56 -0
  131. data/lib/core_ext/string/exclude.rb +11 -0
  132. data/lib/core_ext/string/filters.rb +102 -0
  133. data/lib/core_ext/string/indent.rb +43 -0
  134. data/lib/core_ext/string/inflections.rb +235 -0
  135. data/lib/core_ext/string/inquiry.rb +13 -0
  136. data/lib/core_ext/string/multibyte.rb +53 -0
  137. data/lib/core_ext/string/output_safety.rb +261 -0
  138. data/lib/core_ext/string/starts_ends_with.rb +4 -0
  139. data/lib/core_ext/string/strip.rb +23 -0
  140. data/lib/core_ext/string/zones.rb +14 -0
  141. data/lib/core_ext/string.rb +13 -0
  142. data/lib/core_ext/string_inquirer.rb +26 -0
  143. data/lib/core_ext/tagged_logging.rb +78 -0
  144. data/lib/core_ext/test_case.rb +88 -0
  145. data/lib/core_ext/testing/assertions.rb +99 -0
  146. data/lib/core_ext/testing/autorun.rb +12 -0
  147. data/lib/core_ext/testing/composite_filter.rb +54 -0
  148. data/lib/core_ext/testing/constant_lookup.rb +50 -0
  149. data/lib/core_ext/testing/declarative.rb +26 -0
  150. data/lib/core_ext/testing/deprecation.rb +36 -0
  151. data/lib/core_ext/testing/file_fixtures.rb +34 -0
  152. data/lib/core_ext/testing/isolation.rb +115 -0
  153. data/lib/core_ext/testing/method_call_assertions.rb +41 -0
  154. data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
  155. data/lib/core_ext/testing/stream.rb +42 -0
  156. data/lib/core_ext/testing/tagged_logging.rb +25 -0
  157. data/lib/core_ext/testing/time_helpers.rb +134 -0
  158. data/lib/core_ext/time/acts_like.rb +8 -0
  159. data/lib/core_ext/time/calculations.rb +284 -0
  160. data/lib/core_ext/time/conversions.rb +66 -0
  161. data/lib/core_ext/time/zones.rb +95 -0
  162. data/lib/core_ext/time.rb +20 -0
  163. data/lib/core_ext/time_with_zone.rb +503 -0
  164. data/lib/core_ext/time_zone.rb +464 -0
  165. data/lib/core_ext/uri.rb +25 -0
  166. data/lib/core_ext/version.rb +3 -0
  167. data/lib/core_ext/xml_mini/jdom.rb +181 -0
  168. data/lib/core_ext/xml_mini/libxml.rb +79 -0
  169. data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
  170. data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
  171. data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
  172. data/lib/core_ext/xml_mini/rexml.rb +130 -0
  173. data/lib/core_ext/xml_mini.rb +194 -0
  174. data/lib/core_ext.rb +3 -0
  175. metadata +310 -0
@@ -0,0 +1,79 @@
1
+ require 'libxml'
2
+ require 'core_ext/object/blank'
3
+ require 'stringio'
4
+
5
+ module CoreExt
6
+ module XmlMini_LibXML #:nodoc:
7
+ extend self
8
+
9
+ # Parse an XML Document string or IO into a simple hash using libxml.
10
+ # data::
11
+ # XML Document string or IO to parse
12
+ def parse(data)
13
+ if !data.respond_to?(:read)
14
+ data = StringIO.new(data || '')
15
+ end
16
+
17
+ char = data.getc
18
+ if char.nil?
19
+ {}
20
+ else
21
+ data.ungetc(char)
22
+ LibXML::XML::Parser.io(data).parse.to_hash
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ module LibXML #:nodoc:
30
+ module Conversions #:nodoc:
31
+ module Document #:nodoc:
32
+ def to_hash
33
+ root.to_hash
34
+ end
35
+ end
36
+
37
+ module Node #:nodoc:
38
+ CONTENT_ROOT = '__content__'.freeze
39
+
40
+ # Convert XML document to hash.
41
+ #
42
+ # hash::
43
+ # Hash to merge the converted element into.
44
+ def to_hash(hash={})
45
+ node_hash = {}
46
+
47
+ # Insert node hash into parent hash correctly.
48
+ case hash[name]
49
+ when Array then hash[name] << node_hash
50
+ when Hash then hash[name] = [hash[name], node_hash]
51
+ when nil then hash[name] = node_hash
52
+ end
53
+
54
+ # Handle child elements
55
+ each_child do |c|
56
+ if c.element?
57
+ c.to_hash(node_hash)
58
+ elsif c.text? || c.cdata?
59
+ node_hash[CONTENT_ROOT] ||= ''
60
+ node_hash[CONTENT_ROOT] << c.content
61
+ end
62
+ end
63
+
64
+ # Remove content node if it is blank
65
+ if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
66
+ node_hash.delete(CONTENT_ROOT)
67
+ end
68
+
69
+ # Handle attributes
70
+ each_attr { |a| node_hash[a.name] = a.value }
71
+
72
+ hash
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ LibXML::XML::Document.include(LibXML::Conversions::Document)
79
+ LibXML::XML::Node.include(LibXML::Conversions::Node)
@@ -0,0 +1,85 @@
1
+ require 'libxml'
2
+ require 'core_ext/object/blank'
3
+ require 'stringio'
4
+
5
+ module CoreExt
6
+ module XmlMini_LibXMLSAX #:nodoc:
7
+ extend self
8
+
9
+ # Class that will build the hash while the XML document
10
+ # is being parsed using SAX events.
11
+ class HashBuilder
12
+
13
+ include LibXML::XML::SaxParser::Callbacks
14
+
15
+ CONTENT_KEY = '__content__'.freeze
16
+ HASH_SIZE_KEY = '__hash_size__'.freeze
17
+
18
+ attr_reader :hash
19
+
20
+ def current_hash
21
+ @hash_stack.last
22
+ end
23
+
24
+ def on_start_document
25
+ @hash = { CONTENT_KEY => '' }
26
+ @hash_stack = [@hash]
27
+ end
28
+
29
+ def on_end_document
30
+ @hash = @hash_stack.pop
31
+ @hash.delete(CONTENT_KEY)
32
+ end
33
+
34
+ def on_start_element(name, attrs = {})
35
+ new_hash = { CONTENT_KEY => '' }.merge!(attrs)
36
+ new_hash[HASH_SIZE_KEY] = new_hash.size + 1
37
+
38
+ case current_hash[name]
39
+ when Array then current_hash[name] << new_hash
40
+ when Hash then current_hash[name] = [current_hash[name], new_hash]
41
+ when nil then current_hash[name] = new_hash
42
+ end
43
+
44
+ @hash_stack.push(new_hash)
45
+ end
46
+
47
+ def on_end_element(name)
48
+ if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ''
49
+ current_hash.delete(CONTENT_KEY)
50
+ end
51
+ @hash_stack.pop
52
+ end
53
+
54
+ def on_characters(string)
55
+ current_hash[CONTENT_KEY] << string
56
+ end
57
+
58
+ alias_method :on_cdata_block, :on_characters
59
+ end
60
+
61
+ attr_accessor :document_class
62
+ self.document_class = HashBuilder
63
+
64
+ def parse(data)
65
+ if !data.respond_to?(:read)
66
+ data = StringIO.new(data || '')
67
+ end
68
+
69
+ char = data.getc
70
+ if char.nil?
71
+ {}
72
+ else
73
+ data.ungetc(char)
74
+
75
+ LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
76
+ parser = LibXML::XML::SaxParser.io(data)
77
+ document = self.document_class.new
78
+
79
+ parser.callbacks = document
80
+ parser.parse
81
+ document.hash
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,83 @@
1
+ begin
2
+ require 'nokogiri'
3
+ rescue LoadError => e
4
+ $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
5
+ raise e
6
+ end
7
+ require 'core_ext/object/blank'
8
+ require 'stringio'
9
+
10
+ module CoreExt
11
+ module XmlMini_Nokogiri #:nodoc:
12
+ extend self
13
+
14
+ # Parse an XML Document string or IO into a simple hash using libxml / nokogiri.
15
+ # data::
16
+ # XML Document string or IO to parse
17
+ def parse(data)
18
+ if !data.respond_to?(:read)
19
+ data = StringIO.new(data || '')
20
+ end
21
+
22
+ char = data.getc
23
+ if char.nil?
24
+ {}
25
+ else
26
+ data.ungetc(char)
27
+ doc = Nokogiri::XML(data)
28
+ raise doc.errors.first if doc.errors.length > 0
29
+ doc.to_hash
30
+ end
31
+ end
32
+
33
+ module Conversions #:nodoc:
34
+ module Document #:nodoc:
35
+ def to_hash
36
+ root.to_hash
37
+ end
38
+ end
39
+
40
+ module Node #:nodoc:
41
+ CONTENT_ROOT = '__content__'.freeze
42
+
43
+ # Convert XML document to hash.
44
+ #
45
+ # hash::
46
+ # Hash to merge the converted element into.
47
+ def to_hash(hash={})
48
+ node_hash = {}
49
+
50
+ # Insert node hash into parent hash correctly.
51
+ case hash[name]
52
+ when Array then hash[name] << node_hash
53
+ when Hash then hash[name] = [hash[name], node_hash]
54
+ when nil then hash[name] = node_hash
55
+ end
56
+
57
+ # Handle child elements
58
+ children.each do |c|
59
+ if c.element?
60
+ c.to_hash(node_hash)
61
+ elsif c.text? || c.cdata?
62
+ node_hash[CONTENT_ROOT] ||= ''
63
+ node_hash[CONTENT_ROOT] << c.content
64
+ end
65
+ end
66
+
67
+ # Remove content node if it is blank and there are child tags
68
+ if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
69
+ node_hash.delete(CONTENT_ROOT)
70
+ end
71
+
72
+ # Handle attributes
73
+ attribute_nodes.each { |a| node_hash[a.node_name] = a.value }
74
+
75
+ hash
76
+ end
77
+ end
78
+ end
79
+
80
+ Nokogiri::XML::Document.include(Conversions::Document)
81
+ Nokogiri::XML::Node.include(Conversions::Node)
82
+ end
83
+ end
@@ -0,0 +1,87 @@
1
+ begin
2
+ require 'nokogiri'
3
+ rescue LoadError => e
4
+ $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
5
+ raise e
6
+ end
7
+ require 'core_ext/object/blank'
8
+ require 'stringio'
9
+
10
+ module CoreExt
11
+ module XmlMini_NokogiriSAX #:nodoc:
12
+ extend self
13
+
14
+ # Class that will build the hash while the XML document
15
+ # is being parsed using SAX events.
16
+ class HashBuilder < Nokogiri::XML::SAX::Document
17
+
18
+ CONTENT_KEY = '__content__'.freeze
19
+ HASH_SIZE_KEY = '__hash_size__'.freeze
20
+
21
+ attr_reader :hash
22
+
23
+ def current_hash
24
+ @hash_stack.last
25
+ end
26
+
27
+ def start_document
28
+ @hash = {}
29
+ @hash_stack = [@hash]
30
+ end
31
+
32
+ def end_document
33
+ raise "Parse stack not empty!" if @hash_stack.size > 1
34
+ end
35
+
36
+ def error(error_message)
37
+ raise error_message
38
+ end
39
+
40
+ def start_element(name, attrs = [])
41
+ new_hash = { CONTENT_KEY => '' }.merge!(Hash[attrs])
42
+ new_hash[HASH_SIZE_KEY] = new_hash.size + 1
43
+
44
+ case current_hash[name]
45
+ when Array then current_hash[name] << new_hash
46
+ when Hash then current_hash[name] = [current_hash[name], new_hash]
47
+ when nil then current_hash[name] = new_hash
48
+ end
49
+
50
+ @hash_stack.push(new_hash)
51
+ end
52
+
53
+ def end_element(name)
54
+ if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ''
55
+ current_hash.delete(CONTENT_KEY)
56
+ end
57
+ @hash_stack.pop
58
+ end
59
+
60
+ def characters(string)
61
+ current_hash[CONTENT_KEY] << string
62
+ end
63
+
64
+ alias_method :cdata_block, :characters
65
+ end
66
+
67
+ attr_accessor :document_class
68
+ self.document_class = HashBuilder
69
+
70
+ def parse(data)
71
+ if !data.respond_to?(:read)
72
+ data = StringIO.new(data || '')
73
+ end
74
+
75
+ char = data.getc
76
+ if char.nil?
77
+ {}
78
+ else
79
+ data.ungetc(char)
80
+ document = self.document_class.new
81
+ parser = Nokogiri::XML::SAX::Parser.new(document)
82
+ parser.parse(data)
83
+ document.hash
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,130 @@
1
+ require 'core_ext/kernel/reporting'
2
+ require 'core_ext/object/blank'
3
+ require 'stringio'
4
+
5
+ module CoreExt
6
+ module XmlMini_REXML #:nodoc:
7
+ extend self
8
+
9
+ CONTENT_KEY = '__content__'.freeze
10
+
11
+ # Parse an XML Document string or IO into a simple hash.
12
+ #
13
+ # Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
14
+ # and uses the defaults from Active Support.
15
+ #
16
+ # data::
17
+ # XML Document string or IO to parse
18
+ def parse(data)
19
+ if !data.respond_to?(:read)
20
+ data = StringIO.new(data || '')
21
+ end
22
+
23
+ char = data.getc
24
+ if char.nil?
25
+ {}
26
+ else
27
+ data.ungetc(char)
28
+ silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
29
+ doc = REXML::Document.new(data)
30
+
31
+ if doc.root
32
+ merge_element!({}, doc.root, XmlMini.depth)
33
+ else
34
+ raise REXML::ParseException,
35
+ "The document #{doc.to_s.inspect} does not have a valid root"
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+ # Convert an XML element and merge into the hash
42
+ #
43
+ # hash::
44
+ # Hash to merge the converted element into.
45
+ # element::
46
+ # XML element to merge into hash
47
+ def merge_element!(hash, element, depth)
48
+ raise REXML::ParseException, "The document is too deep" if depth == 0
49
+ merge!(hash, element.name, collapse(element, depth))
50
+ end
51
+
52
+ # Actually converts an XML document element into a data structure.
53
+ #
54
+ # element::
55
+ # The document element to be collapsed.
56
+ def collapse(element, depth)
57
+ hash = get_attributes(element)
58
+
59
+ if element.has_elements?
60
+ element.each_element {|child| merge_element!(hash, child, depth - 1) }
61
+ merge_texts!(hash, element) unless empty_content?(element)
62
+ hash
63
+ else
64
+ merge_texts!(hash, element)
65
+ end
66
+ end
67
+
68
+ # Merge all the texts of an element into the hash
69
+ #
70
+ # hash::
71
+ # Hash to add the converted element to.
72
+ # element::
73
+ # XML element whose texts are to me merged into the hash
74
+ def merge_texts!(hash, element)
75
+ unless element.has_text?
76
+ hash
77
+ else
78
+ # must use value to prevent double-escaping
79
+ texts = ''
80
+ element.texts.each { |t| texts << t.value }
81
+ merge!(hash, CONTENT_KEY, texts)
82
+ end
83
+ end
84
+
85
+ # Adds a new key/value pair to an existing Hash. If the key to be added
86
+ # already exists and the existing value associated with key is not
87
+ # an Array, it will be wrapped in an Array. Then the new value is
88
+ # appended to that Array.
89
+ #
90
+ # hash::
91
+ # Hash to add key/value pair to.
92
+ # key::
93
+ # Key to be added.
94
+ # value::
95
+ # Value to be associated with key.
96
+ def merge!(hash, key, value)
97
+ if hash.has_key?(key)
98
+ if hash[key].instance_of?(Array)
99
+ hash[key] << value
100
+ else
101
+ hash[key] = [hash[key], value]
102
+ end
103
+ elsif value.instance_of?(Array)
104
+ hash[key] = [value]
105
+ else
106
+ hash[key] = value
107
+ end
108
+ hash
109
+ end
110
+
111
+ # Converts the attributes array of an XML element into a hash.
112
+ # Returns an empty Hash if node has no attributes.
113
+ #
114
+ # element::
115
+ # XML element to extract attributes from.
116
+ def get_attributes(element)
117
+ attributes = {}
118
+ element.attributes.each { |n,v| attributes[n] = v }
119
+ attributes
120
+ end
121
+
122
+ # Determines if a document element has text content
123
+ #
124
+ # element::
125
+ # XML element to be checked.
126
+ def empty_content?(element)
127
+ element.texts.join.blank?
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,194 @@
1
+ require 'time'
2
+ require 'base64'
3
+ require 'bigdecimal'
4
+ require 'core_ext/module/delegation'
5
+ require 'core_ext/string/inflections'
6
+ require 'core_ext/date_time/calculations'
7
+
8
+ module CodeExt
9
+ # = XmlMini
10
+ #
11
+ # To use the much faster libxml parser:
12
+ # gem 'libxml-ruby', '=0.9.7'
13
+ # XmlMini.backend = 'LibXML'
14
+ module XmlMini
15
+ extend self
16
+
17
+ # This module decorates files deserialized using Hash.from_xml with
18
+ # the <tt>original_filename</tt> and <tt>content_type</tt> methods.
19
+ module FileLike #:nodoc:
20
+ attr_writer :original_filename, :content_type
21
+
22
+ def original_filename
23
+ @original_filename || 'untitled'
24
+ end
25
+
26
+ def content_type
27
+ @content_type || 'application/octet-stream'
28
+ end
29
+ end
30
+
31
+ DEFAULT_ENCODINGS = {
32
+ "binary" => "base64"
33
+ } unless defined?(DEFAULT_ENCODINGS)
34
+
35
+ TYPE_NAMES = {
36
+ "Symbol" => "symbol",
37
+ "Fixnum" => "integer",
38
+ "Bignum" => "integer",
39
+ "BigDecimal" => "decimal",
40
+ "Float" => "float",
41
+ "TrueClass" => "boolean",
42
+ "FalseClass" => "boolean",
43
+ "Date" => "date",
44
+ "DateTime" => "dateTime",
45
+ "Time" => "dateTime",
46
+ "Array" => "array",
47
+ "Hash" => "hash"
48
+ } unless defined?(TYPE_NAMES)
49
+
50
+ FORMATTING = {
51
+ "symbol" => Proc.new { |symbol| symbol.to_s },
52
+ "date" => Proc.new { |date| date.to_s(:db) },
53
+ "dateTime" => Proc.new { |time| time.xmlschema },
54
+ "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
55
+ "yaml" => Proc.new { |yaml| yaml.to_yaml }
56
+ } unless defined?(FORMATTING)
57
+
58
+ # TODO use regexp instead of Date.parse
59
+ unless defined?(PARSING)
60
+ PARSING = {
61
+ "symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
62
+ "date" => Proc.new { |date| ::Date.parse(date) },
63
+ "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
64
+ "integer" => Proc.new { |integer| integer.to_i },
65
+ "float" => Proc.new { |float| float.to_f },
66
+ "decimal" => Proc.new { |number| BigDecimal(number) },
67
+ "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
68
+ "string" => Proc.new { |string| string.to_s },
69
+ "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
70
+ "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
71
+ "binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
72
+ "file" => Proc.new { |file, entity| _parse_file(file, entity) }
73
+ }
74
+
75
+ PARSING.update(
76
+ "double" => PARSING["float"],
77
+ "dateTime" => PARSING["datetime"]
78
+ )
79
+ end
80
+
81
+ attr_accessor :depth
82
+ self.depth = 100
83
+
84
+ delegate :parse, :to => :backend
85
+
86
+ def backend
87
+ current_thread_backend || @backend
88
+ end
89
+
90
+ def backend=(name)
91
+ backend = name && cast_backend_name_to_module(name)
92
+ self.current_thread_backend = backend if current_thread_backend
93
+ @backend = backend
94
+ end
95
+
96
+ def with_backend(name)
97
+ old_backend = current_thread_backend
98
+ self.current_thread_backend = name && cast_backend_name_to_module(name)
99
+ yield
100
+ ensure
101
+ self.current_thread_backend = old_backend
102
+ end
103
+
104
+ def to_tag(key, value, options)
105
+ type_name = options.delete(:type)
106
+ merged_options = options.merge(:root => key, :skip_instruct => true)
107
+
108
+ if value.is_a?(::Method) || value.is_a?(::Proc)
109
+ if value.arity == 1
110
+ value.call(merged_options)
111
+ else
112
+ value.call(merged_options, key.to_s.singularize)
113
+ end
114
+ elsif value.respond_to?(:to_xml)
115
+ value.to_xml(merged_options)
116
+ else
117
+ type_name ||= TYPE_NAMES[value.class.name]
118
+ type_name ||= value.class.name if value && !value.respond_to?(:to_str)
119
+ type_name = type_name.to_s if type_name
120
+ type_name = "dateTime" if type_name == "datetime"
121
+
122
+ key = rename_key(key.to_s, options)
123
+
124
+ attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name }
125
+ attributes[:nil] = true if value.nil?
126
+
127
+ encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
128
+ attributes[:encoding] = encoding if encoding
129
+
130
+ formatted_value = FORMATTING[type_name] && !value.nil? ?
131
+ FORMATTING[type_name].call(value) : value
132
+
133
+ options[:builder].tag!(key, formatted_value, attributes)
134
+ end
135
+ end
136
+
137
+ def rename_key(key, options = {})
138
+ camelize = options[:camelize]
139
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
140
+ if camelize
141
+ key = true == camelize ? key.camelize : key.camelize(camelize)
142
+ end
143
+ key = _dasherize(key) if dasherize
144
+ key
145
+ end
146
+
147
+ protected
148
+
149
+ def _dasherize(key)
150
+ # $2 must be a non-greedy regex for this to work
151
+ left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
152
+ "#{left}#{middle.tr('_ ', '--')}#{right}"
153
+ end
154
+
155
+ # TODO: Add support for other encodings
156
+ def _parse_binary(bin, entity) #:nodoc:
157
+ case entity['encoding']
158
+ when 'base64'
159
+ ::Base64.decode64(bin)
160
+ else
161
+ bin
162
+ end
163
+ end
164
+
165
+ def _parse_file(file, entity)
166
+ f = StringIO.new(::Base64.decode64(file))
167
+ f.extend(FileLike)
168
+ f.original_filename = entity['name']
169
+ f.content_type = entity['content_type']
170
+ f
171
+ end
172
+
173
+ private
174
+
175
+ def current_thread_backend
176
+ Thread.current[:xml_mini_backend]
177
+ end
178
+
179
+ def current_thread_backend=(name)
180
+ Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
181
+ end
182
+
183
+ def cast_backend_name_to_module(name)
184
+ if name.is_a?(Module)
185
+ name
186
+ else
187
+ require "core_ext/xml_mini/#{name.downcase}"
188
+ CoreExt.const_get("XmlMini_#{name}")
189
+ end
190
+ end
191
+ end
192
+
193
+ XmlMini.backend = 'REXML'
194
+ end
data/lib/core_ext.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
2
+ require path
3
+ end