roxml 2.4.3 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/History.txt +54 -0
  2. data/Manifest.txt +9 -6
  3. data/README.rdoc +24 -17
  4. data/Rakefile +2 -1
  5. data/TODO +30 -31
  6. data/examples/active_record.rb +69 -0
  7. data/examples/amazon.rb +1 -1
  8. data/examples/current_weather.rb +1 -1
  9. data/examples/posts.rb +8 -8
  10. data/examples/twitter.rb +2 -2
  11. data/examples/xml/active_record.xml +70 -0
  12. data/lib/roxml.rb +174 -174
  13. data/lib/roxml/definition.rb +165 -89
  14. data/lib/roxml/extensions/deprecation.rb +5 -0
  15. data/lib/roxml/extensions/string/conversions.rb +2 -3
  16. data/lib/roxml/hash_definition.rb +26 -25
  17. data/lib/roxml/xml.rb +15 -6
  18. data/lib/roxml/xml/parsers/libxml.rb +9 -6
  19. data/lib/roxml/xml/parsers/rexml.rb +1 -1
  20. data/lib/roxml/xml/references.rb +14 -17
  21. data/roxml.gemspec +8 -5
  22. data/spec/definition_spec.rb +563 -0
  23. data/spec/examples/active_record_spec.rb +43 -0
  24. data/spec/roxml_spec.rb +372 -0
  25. data/spec/shared_specs.rb +15 -0
  26. data/spec/spec_helper.rb +21 -4
  27. data/spec/string_spec.rb +15 -0
  28. data/spec/xml/parser_spec.rb +22 -0
  29. data/test/fixtures/book_valid.xml +1 -1
  30. data/test/fixtures/person_with_guarded_mothers.xml +3 -3
  31. data/test/mocks/mocks.rb +57 -45
  32. data/test/unit/definition_test.rb +161 -12
  33. data/test/unit/deprecations_test.rb +97 -0
  34. data/test/unit/to_xml_test.rb +30 -1
  35. data/test/unit/xml_bool_test.rb +15 -3
  36. data/test/unit/xml_construct_test.rb +6 -6
  37. data/test/unit/xml_hash_test.rb +18 -0
  38. data/test/unit/xml_initialize_test.rb +6 -3
  39. data/test/unit/xml_object_test.rb +66 -5
  40. data/test/unit/xml_text_test.rb +3 -0
  41. metadata +23 -15
  42. data/test/unit/array_test.rb +0 -16
  43. data/test/unit/freeze_test.rb +0 -71
  44. data/test/unit/inheritance_test.rb +0 -63
  45. data/test/unit/overriden_output_test.rb +0 -33
  46. data/test/unit/roxml_test.rb +0 -60
  47. data/test/unit/string_test.rb +0 -11
@@ -4,6 +4,10 @@ module ROXML
4
4
  require 'libxml'
5
5
  XML_PARSER = 'libxml' # :nodoc:
6
6
  rescue LoadError
7
+ warn <<WARNING
8
+ ROXML is unable to locate libxml on your system, and so is falling back to
9
+ the much slower REXML. It's best to check this out and get libxml working if possible.
10
+ WARNING
7
11
  XML_PARSER = 'rexml' # :nodoc:
8
12
  end
9
13
  end
@@ -13,15 +17,20 @@ module ROXML
13
17
  module XML
14
18
  class Node
15
19
  def self.from(data)
16
- if data.is_a?(XML::Node)
20
+ case data
21
+ when XML::Node
17
22
  data
18
- elsif data.is_a?(File) || data.is_a?(IO)
23
+ when XML::Document
24
+ data.root
25
+ when File, IO
19
26
  Parser.parse_io(data).root
20
- elsif (defined?(URI) && data.is_a?(URI::Generic)) ||
21
- (defined?(Pathname) && data.is_a?(Pathname))
22
- Parser.parse_file(data.to_s).root
23
27
  else
24
- Parser.parse(data).root
28
+ if (defined?(URI) && data.is_a?(URI::Generic)) ||
29
+ (defined?(Pathname) && data.is_a?(Pathname))
30
+ Parser.parse_file(data.to_s).root
31
+ else
32
+ Parser.parse(data).root
33
+ end
25
34
  end
26
35
  end
27
36
  end
@@ -5,13 +5,14 @@ module ROXML
5
5
  Document = LibXML::XML::Document
6
6
  Node = LibXML::XML::Node
7
7
  Parser = LibXML::XML::Parser
8
+ Error = LibXML::XML::Error
8
9
 
9
10
  module NamespacedSearch
10
11
  def search(xpath)
11
12
  begin
12
- if default_namespace && !xpath.include?(':')
13
+ if namespaces.default && !xpath.include?(':')
13
14
  find(namespaced(xpath),
14
- in_default_namespace(default_namespace.href))
15
+ in_default_namespace(namespaces.default.href))
15
16
  else
16
17
  find(xpath)
17
18
  end
@@ -40,15 +41,17 @@ module ROXML
40
41
  include NamespacedSearch
41
42
 
42
43
  private
43
- delegate :default_namespace, :to => :root
44
+ delegate :namespaces, :to => :root
44
45
  end
45
46
 
46
47
  class Node
47
48
  include NamespacedSearch
48
49
 
49
- private
50
- def default_namespace
51
- @default_namespace ||= namespace && namespace.find {|n| n.to_s.nil? }
50
+ class << self
51
+ def new_with_entity_escaping(name, content = nil, namespace = nil)
52
+ new_without_entity_escaping(name, content && CGI.escapeHTML(content), namespace)
53
+ end
54
+ alias_method_chain :new, :entity_escaping
52
55
  end
53
56
  end
54
57
 
@@ -11,7 +11,7 @@ module ROXML
11
11
  REXML::CData.new(content)
12
12
  end
13
13
 
14
- def new_element(name)
14
+ def new(name)
15
15
  name = name.id2name if name.is_a? Symbol
16
16
  REXML::Element.new(name)
17
17
  end
@@ -36,13 +36,11 @@ module ROXML
36
36
  end
37
37
  end
38
38
 
39
- def default
40
- @default ||= @opts.default || (@opts.array? ? Array.new : nil)
41
- @default.duplicable? ? @default.dup : @default
42
- end
43
-
44
39
  def value_in(xml)
40
+ xml = XML::Node.from(xml)
45
41
  value = fetch_value(xml)
42
+ value = default if value.nil?
43
+
46
44
  freeze(apply_blocks(value))
47
45
  end
48
46
 
@@ -51,7 +49,6 @@ module ROXML
51
49
 
52
50
  def conventionize(what)
53
51
  if !what.blank? && @instance.try(:class).try(:roxml_naming_convention).respond_to?(:call)
54
- require 'uri'
55
52
  URI.unescape(@instance.class.roxml_naming_convention.call(URI.escape(what, /\/|::/)))
56
53
  else
57
54
  what
@@ -71,7 +68,7 @@ module ROXML
71
68
  end
72
69
 
73
70
  def freeze(val)
74
- if opts.freeze?
71
+ if opts.frozen?
75
72
  val.each(&:freeze) if val.is_a?(Enumerable)
76
73
  val.freeze
77
74
  else
@@ -92,7 +89,7 @@ module ROXML
92
89
  if child = xml.children.find {|c| c.name == wrapper }
93
90
  return child
94
91
  end
95
- xml.child_add(XML::Node.new_element(wrapper))
92
+ xml.child_add(XML::Node.new(wrapper))
96
93
  end
97
94
 
98
95
  def nodes_in(xml)
@@ -123,7 +120,7 @@ module ROXML
123
120
  # Updates the attribute in the given XML block to
124
121
  # the value provided.
125
122
  def write_xml(xml, value)
126
- xml.attributes[name] = value.to_s.to_utf
123
+ xml.attributes[name] = value.to_s.to_utf_without_deprecation
127
124
  end
128
125
 
129
126
  def fetch_value(xml)
@@ -156,10 +153,10 @@ module ROXML
156
153
  xml.name = value
157
154
  elsif array?
158
155
  value.each do |v|
159
- add(xml.child_add(XML::Node.new_element(name)), v)
156
+ add(xml.child_add(XML::Node.new(name)), v)
160
157
  end
161
158
  else
162
- add(xml.child_add(XML::Node.new_element(name)), value)
159
+ add(xml.child_add(XML::Node.new(name)), value)
163
160
  end
164
161
  end
165
162
 
@@ -182,7 +179,7 @@ module ROXML
182
179
  nodes_in(xml) do |nodes|
183
180
  if array?
184
181
  nodes.collect do |e|
185
- e.content.strip.to_latin
182
+ e.content.strip.to_latin_without_deprecation
186
183
  end
187
184
  else
188
185
  nodes.first.content
@@ -193,9 +190,9 @@ module ROXML
193
190
 
194
191
  def add(dest, value)
195
192
  if cdata?
196
- dest.child_add(XML::Node.new_cdata(value.to_s.to_utf))
193
+ dest.child_add(XML::Node.new_cdata(value.to_s.to_utf_without_deprecation))
197
194
  else
198
- dest.content = value.to_s.to_utf
195
+ dest.content = CGI.escapeHTML(value.to_s.to_utf_without_deprecation)
199
196
  end
200
197
  end
201
198
  end
@@ -214,7 +211,7 @@ module ROXML
214
211
  # the value provided.
215
212
  def write_xml(xml, value)
216
213
  value.each_pair do |k, v|
217
- node = xml.child_add(XML::Node.new_element(hash.wrapper))
214
+ node = xml.child_add(XML::Node.new(hash.wrapper))
218
215
  @key.update_xml(node, k)
219
216
  @value.update_xml(node, v)
220
217
  end
@@ -238,7 +235,7 @@ module ROXML
238
235
  end
239
236
 
240
237
  def freeze(vals)
241
- if opts.freeze?
238
+ if opts.frozen?
242
239
  vals.each_pair{|k, v| k.freeze; v.freeze }
243
240
  vals.freeze
244
241
  else
@@ -261,7 +258,7 @@ module ROXML
261
258
  elsif value.is_a?(ROXML)
262
259
  xml.child_add(value.to_xml(name))
263
260
  else
264
- node = XML::Node.new_element(name)
261
+ node = XML::Node.new(name)
265
262
  node.content = value.to_xml
266
263
  xml.child_add(node)
267
264
  end
@@ -2,23 +2,23 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{roxml}
5
- s.version = "2.4.3"
5
+ s.version = "2.5.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Ben Woosley", "Zak Mandhro", "Anders Engstrom", "Russ Olsen"]
9
- s.date = %q{2009-02-01}
9
+ s.date = %q{2009-02-24}
10
10
  s.description = %q{ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes. As a result, ROXML simplifies the development of RESTful applications, Web Services, and XML-RPC.}
11
11
  s.email = %q{ben.woosley@gmail.com}
12
12
  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
13
- s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "TODO", "config/website.yml", "examples/amazon.rb", "examples/current_weather.rb", "examples/dashed_elements.rb", "examples/posts.rb", "examples/twitter.rb", "examples/xml/amazon.xml", "examples/xml/current_weather.xml", "examples/xml/dashed_elements.xml", "examples/xml/posts.xml", "examples/xml/twitter.xml", "lib/roxml.rb", "lib/roxml/definition.rb", "lib/roxml/extensions.rb", "lib/roxml/extensions/active_support.rb", "lib/roxml/extensions/array.rb", "lib/roxml/extensions/array/conversions.rb", "lib/roxml/extensions/deprecation.rb", "lib/roxml/extensions/string.rb", "lib/roxml/extensions/string/conversions.rb", "lib/roxml/extensions/string/iterators.rb", "lib/roxml/hash_definition.rb", "lib/roxml/xml.rb", "lib/roxml/xml/parsers/libxml.rb", "lib/roxml/xml/parsers/rexml.rb", "lib/roxml/xml/references.rb", "roxml.gemspec", "spec/examples/amazon_spec.rb", "spec/examples/current_weather_spec.rb", "spec/examples/dashed_elements_spec.rb", "spec/examples/post_spec.rb", "spec/examples/twitter_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake", "tasks/test.rake", "test/bugs/rexml_bugs.rb", "test/fixtures/book_malformed.xml", "test/fixtures/book_pair.xml", "test/fixtures/book_text_with_attribute.xml", "test/fixtures/book_valid.xml", "test/fixtures/book_with_authors.xml", "test/fixtures/book_with_contributions.xml", "test/fixtures/book_with_contributors.xml", "test/fixtures/book_with_contributors_attrs.xml", "test/fixtures/book_with_default_namespace.xml", "test/fixtures/book_with_depth.xml", "test/fixtures/book_with_octal_pages.xml", "test/fixtures/book_with_publisher.xml", "test/fixtures/book_with_wrapped_attr.xml", "test/fixtures/dictionary_of_attr_name_clashes.xml", "test/fixtures/dictionary_of_attrs.xml", "test/fixtures/dictionary_of_guarded_names.xml", "test/fixtures/dictionary_of_mixeds.xml", "test/fixtures/dictionary_of_name_clashes.xml", "test/fixtures/dictionary_of_names.xml", "test/fixtures/dictionary_of_texts.xml", "test/fixtures/library.xml", "test/fixtures/library_uppercase.xml", "test/fixtures/muffins.xml", "test/fixtures/nameless_ageless_youth.xml", "test/fixtures/node_with_attr_name_conflicts.xml", "test/fixtures/node_with_name_conflicts.xml", "test/fixtures/numerology.xml", "test/fixtures/person.xml", "test/fixtures/person_with_guarded_mothers.xml", "test/fixtures/person_with_mothers.xml", "test/mocks/dictionaries.rb", "test/mocks/mocks.rb", "test/release/dependencies_test.rb", "test/test_helper.rb", "test/unit/array_test.rb", "test/unit/definition_test.rb", "test/unit/freeze_test.rb", "test/unit/inheritance_test.rb", "test/unit/overriden_output_test.rb", "test/unit/roxml_test.rb", "test/unit/string_test.rb", "test/unit/to_xml_test.rb", "test/unit/xml_attribute_test.rb", "test/unit/xml_block_test.rb", "test/unit/xml_bool_test.rb", "test/unit/xml_construct_test.rb", "test/unit/xml_convention_test.rb", "test/unit/xml_hash_test.rb", "test/unit/xml_initialize_test.rb", "test/unit/xml_name_test.rb", "test/unit/xml_namespace_test.rb", "test/unit/xml_object_test.rb", "test/unit/xml_required_test.rb", "test/unit/xml_text_test.rb", "vendor/override_rake_task/README", "vendor/override_rake_task/init.rb", "vendor/override_rake_task/install.rb", "vendor/override_rake_task/lib/override_rake_task.rb", "website/index.html"]
13
+ s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "TODO", "config/website.yml", "examples/active_record.rb", "examples/amazon.rb", "examples/current_weather.rb", "examples/dashed_elements.rb", "examples/posts.rb", "examples/twitter.rb", "examples/xml/active_record.xml", "examples/xml/amazon.xml", "examples/xml/current_weather.xml", "examples/xml/dashed_elements.xml", "examples/xml/posts.xml", "examples/xml/twitter.xml", "lib/roxml.rb", "lib/roxml/definition.rb", "lib/roxml/extensions.rb", "lib/roxml/extensions/active_support.rb", "lib/roxml/extensions/array.rb", "lib/roxml/extensions/array/conversions.rb", "lib/roxml/extensions/deprecation.rb", "lib/roxml/extensions/string.rb", "lib/roxml/extensions/string/conversions.rb", "lib/roxml/extensions/string/iterators.rb", "lib/roxml/hash_definition.rb", "lib/roxml/xml.rb", "lib/roxml/xml/parsers/libxml.rb", "lib/roxml/xml/parsers/rexml.rb", "lib/roxml/xml/references.rb", "roxml.gemspec", "spec/definition_spec.rb", "spec/examples/active_record_spec.rb", "spec/examples/amazon_spec.rb", "spec/examples/current_weather_spec.rb", "spec/examples/dashed_elements_spec.rb", "spec/examples/post_spec.rb", "spec/examples/twitter_spec.rb", "spec/roxml_spec.rb", "spec/shared_specs.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/string_spec.rb", "spec/xml/parser_spec.rb", "tasks/rspec.rake", "tasks/test.rake", "test/bugs/rexml_bugs.rb", "test/fixtures/book_malformed.xml", "test/fixtures/book_pair.xml", "test/fixtures/book_text_with_attribute.xml", "test/fixtures/book_valid.xml", "test/fixtures/book_with_authors.xml", "test/fixtures/book_with_contributions.xml", "test/fixtures/book_with_contributors.xml", "test/fixtures/book_with_contributors_attrs.xml", "test/fixtures/book_with_default_namespace.xml", "test/fixtures/book_with_depth.xml", "test/fixtures/book_with_octal_pages.xml", "test/fixtures/book_with_publisher.xml", "test/fixtures/book_with_wrapped_attr.xml", "test/fixtures/dictionary_of_attr_name_clashes.xml", "test/fixtures/dictionary_of_attrs.xml", "test/fixtures/dictionary_of_guarded_names.xml", "test/fixtures/dictionary_of_mixeds.xml", "test/fixtures/dictionary_of_name_clashes.xml", "test/fixtures/dictionary_of_names.xml", "test/fixtures/dictionary_of_texts.xml", "test/fixtures/library.xml", "test/fixtures/library_uppercase.xml", "test/fixtures/muffins.xml", "test/fixtures/nameless_ageless_youth.xml", "test/fixtures/node_with_attr_name_conflicts.xml", "test/fixtures/node_with_name_conflicts.xml", "test/fixtures/numerology.xml", "test/fixtures/person.xml", "test/fixtures/person_with_guarded_mothers.xml", "test/fixtures/person_with_mothers.xml", "test/mocks/dictionaries.rb", "test/mocks/mocks.rb", "test/release/dependencies_test.rb", "test/test_helper.rb", "test/unit/definition_test.rb", "test/unit/deprecations_test.rb", "test/unit/to_xml_test.rb", "test/unit/xml_attribute_test.rb", "test/unit/xml_block_test.rb", "test/unit/xml_bool_test.rb", "test/unit/xml_construct_test.rb", "test/unit/xml_convention_test.rb", "test/unit/xml_hash_test.rb", "test/unit/xml_initialize_test.rb", "test/unit/xml_name_test.rb", "test/unit/xml_namespace_test.rb", "test/unit/xml_object_test.rb", "test/unit/xml_required_test.rb", "test/unit/xml_text_test.rb", "vendor/override_rake_task/README", "vendor/override_rake_task/init.rb", "vendor/override_rake_task/install.rb", "vendor/override_rake_task/lib/override_rake_task.rb", "website/index.html"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://roxml.rubyforge.org}
16
16
  s.rdoc_options = ["--main", "README.rdoc"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{roxml}
19
- s.rubygems_version = %q{1.3.1.xported}
19
+ s.rubygems_version = %q{1.3.1}
20
20
  s.summary = %q{Ruby Object to XML mapping library}
21
- s.test_files = ["test/unit/freeze_test.rb", "test/unit/array_test.rb", "test/unit/xml_convention_test.rb", "test/unit/xml_object_test.rb", "test/unit/xml_required_test.rb", "test/unit/xml_bool_test.rb", "test/unit/roxml_test.rb", "test/unit/xml_name_test.rb", "test/unit/definition_test.rb", "test/unit/xml_construct_test.rb", "test/unit/string_test.rb", "test/unit/xml_namespace_test.rb", "test/unit/xml_text_test.rb", "test/unit/overriden_output_test.rb", "test/unit/xml_block_test.rb", "test/unit/xml_attribute_test.rb", "test/unit/inheritance_test.rb", "test/unit/xml_initialize_test.rb", "test/unit/xml_hash_test.rb", "test/unit/to_xml_test.rb"]
21
+ s.test_files = ["test/unit/xml_convention_test.rb", "test/unit/xml_object_test.rb", "test/unit/xml_required_test.rb", "test/unit/xml_bool_test.rb", "test/unit/xml_name_test.rb", "test/unit/definition_test.rb", "test/unit/xml_construct_test.rb", "test/unit/xml_namespace_test.rb", "test/unit/deprecations_test.rb", "test/unit/xml_text_test.rb", "test/unit/xml_block_test.rb", "test/unit/xml_attribute_test.rb", "test/unit/xml_initialize_test.rb", "test/unit/xml_hash_test.rb", "test/unit/to_xml_test.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -26,15 +26,18 @@ Gem::Specification.new do |s|
26
26
 
27
27
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
28
  s.add_runtime_dependency(%q<activesupport>, [">= 2.1.0"])
29
+ s.add_runtime_dependency(%q<libxml-ruby>, [">= 0.8.6"])
29
30
  s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
30
31
  s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
31
32
  else
32
33
  s.add_dependency(%q<activesupport>, [">= 2.1.0"])
34
+ s.add_dependency(%q<libxml-ruby>, [">= 0.8.6"])
33
35
  s.add_dependency(%q<newgem>, [">= 1.2.3"])
34
36
  s.add_dependency(%q<hoe>, [">= 1.8.0"])
35
37
  end
36
38
  else
37
39
  s.add_dependency(%q<activesupport>, [">= 2.1.0"])
40
+ s.add_dependency(%q<libxml-ruby>, [">= 0.8.6"])
38
41
  s.add_dependency(%q<newgem>, [">= 1.2.3"])
39
42
  s.add_dependency(%q<hoe>, [">= 1.8.0"])
40
43
  end
@@ -0,0 +1,563 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ class RoxmlObject
4
+ include ROXML
5
+ end
6
+
7
+ describe ROXML::Definition do
8
+ describe "#name_explicit?" do
9
+ it "should indicate whether from option is present" do
10
+ ROXML::Definition.new(:element, :from => 'somewhere').name_explicit?.should be_true
11
+ ROXML::Definition.new(:element).name_explicit?.should be_false
12
+ end
13
+
14
+ it "should not consider name proxies as explicit" do
15
+ ROXML::Definition.new(:element, :from => :attr).name_explicit?.should be_false
16
+ ROXML::Definition.new(:element, :from => :content).name_explicit?.should be_false
17
+ end
18
+ end
19
+
20
+ describe "hash options declaration", :shared => true do
21
+ it "should represent a hash" do
22
+ @opts.hash?.should be_true
23
+ end
24
+
25
+ it "should have hash definition" do
26
+ {@opts.hash.key.type => @opts.hash.key.name}.should == @hash_args[:key]
27
+ {@opts.hash.value.type => @opts.hash.value.name}.should == @hash_args[:value]
28
+ end
29
+
30
+ it "should not represent an array" do
31
+ @opts.array?.should be_false
32
+ end
33
+ end
34
+
35
+ describe "types" do
36
+ describe ":content" do
37
+ it "should be recognized" do
38
+ ROXML::Definition.new(:author).content?.should be_false
39
+ ROXML::Definition.new(:author, :content).content?.should be_true
40
+ end
41
+
42
+ it "should be deprecated"
43
+ end
44
+
45
+ describe "array reference" do
46
+ it "[] means array of texts" do
47
+ opts = ROXML::Definition.new(:authors, [])
48
+ opts.array?.should be_true
49
+ opts.type.should == :text
50
+ end
51
+
52
+ it "[:text] means array of texts" do
53
+ opts = ROXML::Definition.new(:authors, [:text])
54
+ opts.array?.should be_true
55
+ opts.type.should == :text
56
+ end
57
+
58
+ it "[:attr] means array of attrs" do
59
+ opts = ROXML::Definition.new(:authors, [:attr])
60
+ opts.array?.should be_true
61
+ opts.type.should == :attr
62
+ end
63
+
64
+ it "[Object] means array of objects" do
65
+ opts = ROXML::Definition.new(:authors, [Hash])
66
+ opts.array?.should be_true
67
+ opts.type.should == Hash
68
+ end
69
+ end
70
+
71
+ describe "{}" do
72
+ describe "hash with attr key and text val" do
73
+ before do
74
+ @opts = ROXML::Definition.new(:attributes, {:key => {:attr => :name},
75
+ :value => :value})
76
+ @hash_args = {:key => {:attr => 'name'},
77
+ :value => {:text => 'value'}}
78
+ end
79
+
80
+ it_should_behave_like "hash options declaration"
81
+ end
82
+
83
+ describe "hash with String class for type" do
84
+ before do
85
+ @opts = ROXML::Definition.new(:attributes, {:key => {String => 'name'},
86
+ :value => {String => 'value'}})
87
+ @hash_args = {:key => {:text => 'name'}, :value => {:text => 'value'}}
88
+ end
89
+
90
+ it_should_behave_like "hash options declaration"
91
+ end
92
+
93
+ describe "hash with attr key and content val" do
94
+ before do
95
+ @opts = ROXML::Definition.new(:attributes, {:key => {:attr => :name},
96
+ :value => :content})
97
+ @hash_args = {:key => {:attr => 'name'}, :value => {:text => '.'}}
98
+ end
99
+
100
+ it_should_behave_like "hash options declaration"
101
+ end
102
+
103
+ describe "hash of attrs" do
104
+ before do
105
+ @hash_args = {:key => {:attr => 'name'}, :value => {:attr => 'value'}}
106
+ @opts = ROXML::Definition.new(:attributes, {:attrs => [:name, :value]})
107
+ end
108
+
109
+ it_should_behave_like "hash options declaration"
110
+
111
+ describe "with options" do
112
+ before do
113
+ @hash_args = {:key => {:attr => 'dt'}, :value => {:attr => 'dd'}}
114
+ @opts = ROXML::Definition.new(:definitions, {:attrs => [:dt, :dd]},
115
+ :in => 'definitions')
116
+ end
117
+
118
+ it_should_behave_like "hash options declaration"
119
+
120
+ it "should not interfere with options" do
121
+ @opts.wrapper.should == 'definitions'
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ describe ":as" do
129
+ describe "=> :array" do
130
+ it "should be deprecated"
131
+ end
132
+
133
+ describe "=> []" do
134
+ it "should means array of texts" do
135
+ opts = ROXML::Definition.new(:authors, :as => [])
136
+ opts.array?.should be_true
137
+ opts.type.should == :text
138
+ end
139
+
140
+ it "should unescape xml entities" do
141
+ ROXML::Definition.new(:questions, :as => []).to_ref(RoxmlObject.new).value_in(%{
142
+ <xml>
143
+ <question>&quot;Wickard &amp; Filburn&quot; &gt;</question>
144
+ <question> &lt; McCulloch &amp; Maryland?</question>
145
+ </xml>
146
+ }).should == ["\"Wickard & Filburn\" >", "< McCulloch & Maryland?"]
147
+ end
148
+ end
149
+
150
+ describe "=> {}" do
151
+ describe "hash with attr key and text val" do
152
+ before do
153
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => {:attr => :name},
154
+ :value => :value})
155
+ @hash_args = {:key => {:attr => 'name'},
156
+ :value => {:text => 'value'}}
157
+ end
158
+
159
+ it_should_behave_like "hash options declaration"
160
+ end
161
+
162
+ describe "hash with String class for type" do
163
+ before do
164
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => {String => 'name'},
165
+ :value => {String => 'value'}})
166
+ @hash_args = {:key => {:text => 'name'}, :value => {:text => 'value'}}
167
+ end
168
+
169
+ it_should_behave_like "hash options declaration"
170
+ end
171
+
172
+ describe "hash with attr key and content val" do
173
+ before do
174
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => {:attr => :name},
175
+ :value => :content})
176
+ @hash_args = {:key => {:attr => 'name'}, :value => {:text => '.'}}
177
+ end
178
+
179
+ it_should_behave_like "hash options declaration"
180
+ end
181
+
182
+ describe "hash of attrs" do
183
+ before do
184
+ @hash_args = {:key => {:attr => 'name'}, :value => {:attr => 'value'}}
185
+ @opts = ROXML::Definition.new(:attributes, :as => {:attrs => [:name, :value]})
186
+ end
187
+
188
+ it_should_behave_like "hash options declaration"
189
+
190
+ describe "with options" do
191
+ before do
192
+ @hash_args = {:key => {:attr => 'dt'}, :value => {:attr => 'dd'}}
193
+ @opts = ROXML::Definition.new(:definitions, :as => {:attrs => [:dt, :dd]},
194
+ :in => 'definitions')
195
+ end
196
+
197
+ it_should_behave_like "hash options declaration"
198
+
199
+ it "should not interfere with options" do
200
+ @opts.wrapper.should == 'definitions'
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ describe "for block shorthand" do
207
+ describe "in literal array" do
208
+ before do
209
+ @opts = ROXML::Definition.new(:intarray, :as => [Integer])
210
+ end
211
+
212
+ it "should be detected as array reference" do
213
+ @opts.array?.should be_true
214
+ end
215
+
216
+ it "should be normal otherwise" do
217
+ @opts.type.should == :text
218
+ @opts.blocks.size.should == 1
219
+ end
220
+ end
221
+
222
+ it "should have no blocks without a shorthand" do
223
+ ROXML::Definition.new(:count).blocks.should be_empty
224
+ ROXML::Definition.new(:count, :as => :bogus).blocks.should be_empty
225
+ ROXML::Definition.new(:count, :as => :foat).blocks.should be_empty # misspelled
226
+ end
227
+
228
+ describe "block shorthand type declaration", :shared => true do
229
+ it "should translate nil to nil" do
230
+ @definition.blocks.first.call(nil).should be_nil
231
+ end
232
+
233
+ it "should translate empty strings to nil" do
234
+ @definition.blocks.first.call("").should be_nil
235
+ @definition.blocks.first.call(" ").should be_nil
236
+ end
237
+ end
238
+
239
+ describe ":as => Integer", :shared => true do
240
+ it_should_behave_like "block shorthand type declaration"
241
+
242
+ it "should translate text to integers" do
243
+ @definition.blocks.first['3'].should == 3
244
+ @definition.blocks.first['792'].should == 792
245
+ end
246
+
247
+ it "should raise on non-integer values" do
248
+ proc { @definition.blocks.first['08'] }.should raise_error(ArgumentError)
249
+ proc { @definition.blocks.first['793.12'] }.should raise_error(ArgumentError)
250
+ proc { @definition.blocks.first['junk 11'] }.should raise_error(ArgumentError)
251
+ proc { @definition.blocks.first['11sttf'] }.should raise_error(ArgumentError)
252
+ end
253
+
254
+ context "when passed an array" do
255
+ it "should translate the array elements to integer" do
256
+ @definition.blocks.first.call(["792", "12", "328"]).should == [792, 12, 328]
257
+ end
258
+ end
259
+ end
260
+
261
+ describe "Integer" do
262
+ before do
263
+ @definition = ROXML::Definition.new(:intvalue, :as => Integer)
264
+ end
265
+
266
+ it_should_behave_like ":as => Integer"
267
+ end
268
+
269
+ describe ":integer" do
270
+ before do
271
+ @definition = ROXML::Definition.new(:intvalue, :as => :integer)
272
+ end
273
+
274
+ it_should_behave_like ":as => Integer"
275
+
276
+ it "should be deprecated"
277
+ end
278
+
279
+ describe ":as => Float", :shared => true do
280
+ it_should_behave_like "block shorthand type declaration"
281
+
282
+ it "should translate text to float" do
283
+ @definition.blocks.first['3'].should == 3.0
284
+ @definition.blocks.first['12.7'].should == 12.7
285
+ end
286
+
287
+ it "should raise on non-float values" do
288
+ proc { @definition.blocks.first['junk 11.3'] }.should raise_error(ArgumentError)
289
+ proc { @definition.blocks.first['11.1sttf'] }.should raise_error(ArgumentError)
290
+ end
291
+
292
+ context "when passed an array" do
293
+ it "should translate the array elements to integer" do
294
+ @definition.blocks.first.call(["792.13", "240", "3.14"]).should == [792.13, 240.0, 3.14]
295
+ end
296
+ end
297
+ end
298
+
299
+ describe ":float" do
300
+ before do
301
+ @definition = ROXML::Definition.new(:floatvalue, :as => :float)
302
+ end
303
+
304
+ it_should_behave_like ":as => Float"
305
+
306
+ it "should be deprecated"
307
+ end
308
+
309
+ describe "Float" do
310
+ before do
311
+ @definition = ROXML::Definition.new(:floatvalue, :as => Float)
312
+ end
313
+
314
+ it_should_behave_like ":as => Float"
315
+ end
316
+
317
+ describe "BigDecimal" do
318
+ before do
319
+ @definition = ROXML::Definition.new(:decimalvalue, :as => BigDecimal)
320
+ end
321
+
322
+ it_should_behave_like "block shorthand type declaration"
323
+
324
+ it "should translate text to decimal numbers" do
325
+ @definition.blocks.first['3'].should == BigDecimal.new("3.0")
326
+ @definition.blocks.first['0.3'].should == BigDecimal.new("0.3")
327
+ end
328
+
329
+ it "should extract what it can, and fall back to 0" do
330
+ @definition.blocks.first['junk 11'].should eql(BigDecimal.new("0"))
331
+ @definition.blocks.first['11sttf'].should eql(BigDecimal.new("11.0"))
332
+ end
333
+
334
+ context "when passed an array" do
335
+ it "should translate the array elements to integer" do
336
+ @definition.blocks.first.call(["12.1", "328.2"]).should == [BigDecimal.new("12.1"), BigDecimal.new("328.2")]
337
+ end
338
+ end
339
+ end
340
+
341
+ describe "Fixnum" do
342
+ before do
343
+ @definition = ROXML::Definition.new(:fixnumvalue, :as => Fixnum)
344
+ end
345
+
346
+ it_should_behave_like "block shorthand type declaration"
347
+
348
+ it "should translate text to integers" do
349
+ @definition.blocks.first['3'].should == 3
350
+ @definition.blocks.first['792'].should == 792
351
+ @definition.blocks.first['08'].should == 8
352
+ @definition.blocks.first['279.23'].should == 279
353
+ end
354
+
355
+ it "should extract whatever is possible and fall back to 0" do
356
+ @definition.blocks.first['junk 11'].should eql(0)
357
+ @definition.blocks.first['.?sttf'].should eql(0)
358
+ @definition.blocks.first['11sttf'].should eql(11)
359
+ end
360
+
361
+ context "when passed an array" do
362
+ it "should translate the array elements to integer" do
363
+ @definition.blocks.first.call(["792", "12", "328"]).should == [792, 12, 328]
364
+ end
365
+ end
366
+ end
367
+
368
+ describe ":bool" do
369
+ it "should boolify individual values" do
370
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("1").should be_true
371
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("True").should be_true
372
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("Yes").should be_true
373
+ end
374
+
375
+ context "when an array is passed in" do
376
+ it "should boolify arrays of values" do
377
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("0").should be_false
378
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("false").should be_false
379
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("nO").should be_false
380
+ end
381
+ end
382
+
383
+ context "when no value is detected" do
384
+ it "should return nil" do
385
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("junk").should be_nil
386
+ end
387
+
388
+ context "when a literal block is available" do
389
+ it "should pass the value itself to the block"
390
+ end
391
+ end
392
+ end
393
+
394
+ describe "Time" do
395
+ it "should return nil on empty string" do
396
+ ROXML::Definition.new(:floatvalue, :as => Time).blocks.first.call(" ").should be_nil
397
+ end
398
+
399
+ it "should return a time version of the string" do
400
+ ROXML::Definition.new(:datevalue, :as => Time).blocks.first.call("12:31am").min.should == 31
401
+ end
402
+
403
+ context "when passed an array of values" do
404
+ it "should timify all of them" do
405
+ ROXML::Definition.new(:datevalue, :as => Time).blocks.first.call(["12:31am", "3:00pm", "11:59pm"]).map(&:min).should == [31, 0, 59]
406
+ end
407
+ end
408
+ end
409
+
410
+ describe "Date" do
411
+ it "should return nil on empty string" do
412
+ ROXML::Definition.new(:floatvalue, :as => Date).blocks.first.call(" ").should be_nil
413
+ end
414
+
415
+ it "should return a time version of the string" do
416
+ ROXML::Definition.new(:datevalue, :as => Date).blocks.first.call("September 3rd, 1970").to_s == "1970-09-03"
417
+ end
418
+
419
+ context "when passed an array of values" do
420
+ it "should timify all of them" do
421
+ ROXML::Definition.new(:datevalue, :as => Date).blocks.first.call(["September 3rd, 1970", "1776-07-04"]).map(&:to_s).should == ["1970-09-03", "1776-07-04"]
422
+ end
423
+ end
424
+ end
425
+
426
+ describe "DateTime" do
427
+ it "should return nil on empty string" do
428
+ ROXML::Definition.new(:floatvalue, :as => DateTime).blocks.first.call(" ").should be_nil
429
+ end
430
+
431
+ it "should return a time version of the string" do
432
+ ROXML::Definition.new(:datevalue, :as => DateTime).blocks.first.call("12:05pm, September 3rd, 1970").to_s == "1970-09-03T12:05:00+00:00"
433
+ end
434
+
435
+ context "when passed an array of values" do
436
+ it "should timify all of them" do
437
+ ROXML::Definition.new(:datevalue, :as => DateTime).blocks.first.call(["12:05pm, September 3rd, 1970", "3:00pm, May 22, 1700"]).map(&:to_s).should == ["1970-09-03T12:05:00+00:00", "1700-05-22T15:00:00+00:00"]
438
+ end
439
+ end
440
+ end
441
+
442
+ it "should prohibit multiple shorthands" do
443
+ proc { ROXML::Definition.new(:count, :as => [Float, Integer]) }.should raise_error(ArgumentError)
444
+ end
445
+
446
+ it "should stack block shorthands with explicit blocks" do
447
+ ROXML::Definition.new(:count, :as => Integer) {|val| val.to_i }.blocks.size.should == 2
448
+ ROXML::Definition.new(:count, :as => Float) {|val| val.object_id }.blocks.size.should == 2
449
+ end
450
+ end
451
+ end
452
+
453
+ describe ":from" do
454
+ describe "attribute reference", :shared => true do
455
+ it "should be interpreted as :attr" do
456
+ @opts.type.should == :attr
457
+ end
458
+
459
+ it "should strip '@' from name" do
460
+ @opts.name.should == 'attr_name'
461
+ end
462
+
463
+ it "should unescape xml entities" do
464
+ @opts.to_ref(RoxmlObject.new).value_in(%{
465
+ <question attr_name="&quot;Wickard &amp; Filburn&quot; &gt; / &lt; McCulloch &amp; Marryland?" />
466
+ }).should == "\"Wickard & Filburn\" > / < McCulloch & Marryland?"
467
+ end
468
+ end
469
+
470
+ context ":attr" do
471
+ before do
472
+ @opts = ROXML::Definition.new(:attr_name, :from => :attr)
473
+ end
474
+
475
+ it_should_behave_like "attribute reference"
476
+ end
477
+
478
+ context "@attribute_name" do
479
+ before do
480
+ @opts = ROXML::Definition.new(:attr_name, :from => '@attr_name')
481
+ end
482
+
483
+ it_should_behave_like "attribute reference"
484
+
485
+ describe "and with :attr" do
486
+ before do
487
+ @opts = ROXML::Definition.new(:attr_name, :attr, :from => '@attr_name')
488
+ end
489
+
490
+ it_should_behave_like "attribute reference"
491
+ it "should be deprecated"
492
+ end
493
+ end
494
+
495
+ describe ":content" do
496
+ it "should be recognized" do
497
+ ROXML::Definition.new(:author, :from => :content).content?.should == true
498
+ end
499
+
500
+ it "should be equivalent to :from => '.'" do
501
+ ROXML::Definition.new(:author, :from => '.').content?.should == true
502
+ end
503
+ end
504
+ end
505
+
506
+ describe ":in" do
507
+ context "as xpath" do
508
+ it "should pass through as wrapper" do
509
+ ROXML::Definition.new(:manufacturer, :in => './').wrapper.should == './'
510
+ end
511
+ end
512
+
513
+ context "as xpath" do
514
+ it "should pass through as wrapper" do
515
+ ROXML::Definition.new(:manufacturer, :in => 'wrapper').wrapper.should == 'wrapper'
516
+ end
517
+ end
518
+ end
519
+
520
+ describe "options" do
521
+
522
+ describe "boolean option", :shared => true do
523
+ it "should be recognized" do
524
+ ROXML::Definition.new(:author, :content, @option => true).respond_to?(:"#{@option}?")
525
+ ROXML::Definition.new(:author, :content, @option => true).send(:"#{@option}?").should be_true
526
+ ROXML::Definition.new(:author, :content, @option => false).send(:"#{@option}?").should be_false
527
+ end
528
+
529
+ it "should default to false" do
530
+ ROXML::Definition.new(:author, :content).send(:"#{@option}?").should be_false
531
+ end
532
+ end
533
+
534
+ describe ":required" do
535
+ before do
536
+ @option = :required
537
+ end
538
+
539
+ it_should_behave_like "boolean option"
540
+
541
+ it "should not be allowed together with :else" do
542
+ proc { ROXML::Definition.new(:author, :content, :required => true, :else => 'Johnny') }.should raise_error(ArgumentError)
543
+ proc { ROXML::Definition.new(:author, :content, :required => false, :else => 'Johnny') }.should_not raise_error
544
+ end
545
+ end
546
+
547
+ describe ":frozen" do
548
+ before do
549
+ @option = :frozen
550
+ end
551
+
552
+ it_should_behave_like "boolean option"
553
+ end
554
+
555
+ describe ":cdata" do
556
+ before do
557
+ @option = :cdata
558
+ end
559
+
560
+ it_should_behave_like "boolean option"
561
+ end
562
+ end
563
+ end