representable 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +7 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/History.txt +354 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +186 -0
  8. data/Rakefile +10 -0
  9. data/TODO +37 -0
  10. data/VERSION +1 -0
  11. data/examples/amazon.rb +35 -0
  12. data/examples/current_weather.rb +27 -0
  13. data/examples/dashed_elements.rb +20 -0
  14. data/examples/library.rb +40 -0
  15. data/examples/posts.rb +27 -0
  16. data/examples/rails.rb +70 -0
  17. data/examples/twitter.rb +37 -0
  18. data/examples/xml/active_record.xml +70 -0
  19. data/examples/xml/amazon.xml +133 -0
  20. data/examples/xml/current_weather.xml +89 -0
  21. data/examples/xml/dashed_elements.xml +52 -0
  22. data/examples/xml/posts.xml +23 -0
  23. data/examples/xml/twitter.xml +422 -0
  24. data/lib/representable.rb +257 -0
  25. data/lib/representable/definition.rb +109 -0
  26. data/lib/representable/nokogiri_extensions.rb +19 -0
  27. data/lib/representable/references.rb +153 -0
  28. data/lib/representable/version.rb +3 -0
  29. data/lib/representable/xml.rb +79 -0
  30. data/representable.gemspec +29 -0
  31. data/spec/definition_spec.rb +495 -0
  32. data/spec/examples/active_record_spec.rb +41 -0
  33. data/spec/examples/amazon_spec.rb +54 -0
  34. data/spec/examples/current_weather_spec.rb +37 -0
  35. data/spec/examples/dashed_elements_spec.rb +20 -0
  36. data/spec/examples/library_spec.rb +46 -0
  37. data/spec/examples/post_spec.rb +24 -0
  38. data/spec/examples/twitter_spec.rb +32 -0
  39. data/spec/roxml_integration_test.rb +289 -0
  40. data/spec/roxml_spec.rb +372 -0
  41. data/spec/shared_specs.rb +15 -0
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/support/libxml.rb +3 -0
  44. data/spec/support/nokogiri.rb +3 -0
  45. data/spec/xml/array_spec.rb +36 -0
  46. data/spec/xml/attributes_spec.rb +71 -0
  47. data/spec/xml/encoding_spec.rb +53 -0
  48. data/spec/xml/namespace_spec.rb +270 -0
  49. data/spec/xml/namespaces_spec.rb +67 -0
  50. data/spec/xml/object_spec.rb +82 -0
  51. data/spec/xml/parser_spec.rb +21 -0
  52. data/spec/xml/text_spec.rb +71 -0
  53. data/test/fixtures/book_malformed.xml +5 -0
  54. data/test/fixtures/book_pair.xml +8 -0
  55. data/test/fixtures/book_text_with_attribute.xml +5 -0
  56. data/test/fixtures/book_valid.xml +5 -0
  57. data/test/fixtures/book_with_authors.xml +7 -0
  58. data/test/fixtures/book_with_contributions.xml +9 -0
  59. data/test/fixtures/book_with_contributors.xml +7 -0
  60. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  61. data/test/fixtures/book_with_default_namespace.xml +9 -0
  62. data/test/fixtures/book_with_depth.xml +6 -0
  63. data/test/fixtures/book_with_octal_pages.xml +4 -0
  64. data/test/fixtures/book_with_publisher.xml +7 -0
  65. data/test/fixtures/book_with_wrapped_attr.xml +3 -0
  66. data/test/fixtures/dictionary_of_attr_name_clashes.xml +8 -0
  67. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  68. data/test/fixtures/dictionary_of_guarded_names.xml +6 -0
  69. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  70. data/test/fixtures/dictionary_of_name_clashes.xml +10 -0
  71. data/test/fixtures/dictionary_of_names.xml +4 -0
  72. data/test/fixtures/dictionary_of_texts.xml +10 -0
  73. data/test/fixtures/library.xml +30 -0
  74. data/test/fixtures/library_uppercase.xml +30 -0
  75. data/test/fixtures/muffins.xml +3 -0
  76. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  77. data/test/fixtures/node_with_attr_name_conflicts.xml +1 -0
  78. data/test/fixtures/node_with_name_conflicts.xml +4 -0
  79. data/test/fixtures/numerology.xml +4 -0
  80. data/test/fixtures/person.xml +1 -0
  81. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  82. data/test/fixtures/person_with_mothers.xml +10 -0
  83. data/test/mocks/dictionaries.rb +57 -0
  84. data/test/mocks/mocks.rb +279 -0
  85. data/test/roxml_test.rb +58 -0
  86. data/test/support/fixtures.rb +11 -0
  87. data/test/test_helper.rb +6 -0
  88. data/test/unit/definition_test.rb +235 -0
  89. data/test/unit/deprecations_test.rb +24 -0
  90. data/test/unit/to_xml_test.rb +81 -0
  91. data/test/unit/xml_attribute_test.rb +39 -0
  92. data/test/unit/xml_block_test.rb +81 -0
  93. data/test/unit/xml_bool_test.rb +122 -0
  94. data/test/unit/xml_convention_test.rb +150 -0
  95. data/test/unit/xml_hash_test.rb +115 -0
  96. data/test/unit/xml_initialize_test.rb +49 -0
  97. data/test/unit/xml_name_test.rb +141 -0
  98. data/test/unit/xml_namespace_test.rb +31 -0
  99. data/test/unit/xml_object_test.rb +206 -0
  100. data/test/unit/xml_required_test.rb +94 -0
  101. data/test/unit/xml_text_test.rb +71 -0
  102. data/website/index.html +98 -0
  103. metadata +248 -0
@@ -0,0 +1,81 @@
1
+ require_relative './../test_helper'
2
+
3
+ class ArrayWithBlockShorthand
4
+ include ROXML
5
+
6
+ xml_reader :array, :as => [Integer], :from => 'number'
7
+ end
8
+
9
+ class ArrayWithBlock
10
+ include ROXML
11
+
12
+ xml_reader :array, :as => [], :from => 'number' do |arr|
13
+ arr.map(&:to_i).reverse
14
+ end
15
+ end
16
+
17
+ class TestXMLBlocks < ActiveSupport::TestCase
18
+ def test_block_is_applied
19
+ muffins = Muffins.from_xml(fixture(:muffins))
20
+
21
+ assert muffins.count > 0
22
+ assert_equal 0, muffins.count % 13
23
+ end
24
+
25
+ def test_block_is_applied_to_hash
26
+ numerology = Numerology.from_xml(fixture(:numerology))
27
+
28
+ assert !numerology.predictions.keys.empty?
29
+ assert numerology.predictions.keys.all? {|k| k.is_a? Integer }
30
+ assert numerology.predictions.values.all? {|k| k.is_a? String }
31
+ end
32
+
33
+ def test_stacked_blocks_are_applied
34
+ muffins = MuffinsWithStackedBlocks.from_xml(fixture(:muffins))
35
+
36
+ assert muffins.count > 0
37
+ assert_equal 0, muffins.count % 13
38
+ end
39
+
40
+ def test_block_shorthand_applied_properly_to_array
41
+ obj = ArrayWithBlockShorthand.from_xml(%{
42
+ <array_with_block_shorthand>
43
+ <number>1</number>
44
+ <number>2</number>
45
+ <number>3</number>
46
+ </array_with_block_shorthand>
47
+ })
48
+
49
+ assert_equal [1, 2, 3], obj.array
50
+ end
51
+
52
+ def test_block_applied_properly_to_array
53
+ obj = ArrayWithBlock.from_xml(%{
54
+ <array_with_block>
55
+ <number>1</number>
56
+ <number>2</number>
57
+ <number>3</number>
58
+ </array_with_block>
59
+ })
60
+
61
+ assert_equal [3, 2, 1], obj.array
62
+ end
63
+
64
+ def test_block_shorthand_applied_properly_to_empty_array
65
+ obj = ArrayWithBlockShorthand.from_xml(%{
66
+ <array_with_block_shorthand>
67
+ </array_with_block_shorthand>
68
+ })
69
+
70
+ assert_equal [], obj.array
71
+ end
72
+
73
+ def test_block_applied_properly_to_empty_array
74
+ obj = ArrayWithBlock.from_xml(%{
75
+ <array_with_block>
76
+ </array_with_block>
77
+ })
78
+
79
+ assert_equal [], obj.array
80
+ end
81
+ end
@@ -0,0 +1,122 @@
1
+ require_relative './../test_helper'
2
+
3
+ PROC_TRUE = proc {|val| val ? 'TRUE' : 'FALSE'}
4
+ PROC_True = proc {|val| val ? 'True' : 'False'}
5
+ PROC_true = proc {|val| val.to_s}
6
+ PROC_1 = proc {|val| val ? 1 : 0}
7
+
8
+ class XmlBool
9
+ include ROXML
10
+
11
+ xml_name 'xml_bool'
12
+ xml_reader :true_from_TRUE?, :to_xml => PROC_TRUE
13
+ xml_reader :false_from_FALSE?, :from => 'text_for_FALSE', :to_xml => PROC_TRUE
14
+ xml_reader :true_from_one?, :from => '@attr_for_one', :to_xml => PROC_1
15
+ xml_reader :false_from_zero?, :from => 'text_for_zero', :in => 'container', :to_xml => PROC_1
16
+ xml_reader :true_from_True?, :from => '@attr_for_True', :in => 'container', :to_xml => PROC_True
17
+ xml_reader :false_from_False?, :from => 'false_from_cdata_False', :cdata => true, :to_xml => PROC_True
18
+ xml_reader :true_from_true?, :to_xml => PROC_true
19
+ xml_reader :false_from_false?, :to_xml => PROC_true
20
+ xml_reader :missing?
21
+ end
22
+
23
+ class XmlBoolRequired
24
+ include ROXML
25
+
26
+ xml_reader :required?, :required => true
27
+ end
28
+
29
+ class XmlBoolUnexpected
30
+ include ROXML
31
+
32
+ xml_reader :unexpected?
33
+ end
34
+
35
+ class XmlBoolUnexpectedWithBlock
36
+ include ROXML
37
+
38
+ xml_reader :unexpected? do |val|
39
+ val
40
+ end
41
+ end
42
+
43
+ BOOL_XML = %{
44
+ <xml_bool attr_for_one="1">
45
+ <true_from_TRUE>TRUE</true_from_TRUE>
46
+ <text_for_FALSE>FALSE</text_for_FALSE>
47
+ <container attr_for_True="True">
48
+ <text_for_zero>0</text_for_zero>
49
+ </container>
50
+ <false_from_cdata_False><![CDATA[False]]></false_from_cdata_False>
51
+ <true_from_true>true</true_from_true>
52
+ <false_from_false>false</false_from_false>
53
+ </xml_bool>
54
+ }
55
+ PRESENT = %{
56
+ <xml_bool_required>
57
+ <required>true</required>
58
+ </xml_bool_required>
59
+ }
60
+ ABSENT = %{
61
+ <xml_bool_required>
62
+ </xml_bool_required>
63
+ }
64
+ UNEXPECTED_VALUE_XML = %{
65
+ <xml_bool_unexpected>
66
+ <unexpected>Unexpected Value</unexpected>
67
+ </xml_bool_unexpected>
68
+ }
69
+
70
+
71
+ class TestXMLBool < ActiveSupport::TestCase
72
+ def test_bool_results_for_various_inputs
73
+ x = XmlBool.from_xml(BOOL_XML)
74
+ assert_equal true, x.true_from_TRUE?
75
+ assert_equal false, x.false_from_FALSE?
76
+ assert_equal true, x.true_from_one?
77
+ assert_equal false, x.false_from_zero?
78
+ assert_equal true, x.true_from_True?
79
+ assert_equal false, x.false_from_False?
80
+ assert_equal true, x.true_from_true?
81
+ assert_equal false, x.false_from_false?
82
+ end
83
+
84
+ def test_missing_results_in_nil
85
+ x = XmlBool.from_xml(BOOL_XML)
86
+ assert_equal nil, x.missing?
87
+ end
88
+
89
+ def test_unexpected_value_results_in_nil
90
+ x = XmlBoolUnexpected.from_xml(UNEXPECTED_VALUE_XML)
91
+ assert_equal nil, x.unexpected?
92
+ end
93
+
94
+ def test_block_recieves_unexpected_value_rather_than_nil
95
+ x = XmlBoolUnexpectedWithBlock.from_xml(UNEXPECTED_VALUE_XML)
96
+ assert_equal "Unexpected Value", x.unexpected?
97
+ end
98
+
99
+ def test_required_raises_on_missing
100
+ assert_nothing_raised do
101
+ XmlBoolRequired.from_xml(PRESENT)
102
+ end
103
+
104
+ assert_raise ROXML::RequiredElementMissing do
105
+ XmlBoolRequired.from_xml(ABSENT)
106
+ end
107
+ end
108
+
109
+ def test_writable_references_properly_handle_punctuation
110
+ klass = Class.new do
111
+ include ROXML
112
+ xml_accessor :punctuation?
113
+ end
114
+
115
+ instance = klass.from_xml("<xml><punctuation>True</punctuation></xml>")
116
+ assert_equal true, instance.punctuation?
117
+ instance.punctuation = false
118
+ assert_equal false, instance.punctuation?
119
+ end
120
+
121
+ to_xml_test XmlBool => BOOL_XML
122
+ end
@@ -0,0 +1,150 @@
1
+ require_relative './../test_helper'
2
+
3
+ XML_CAMELLOWER = %{
4
+ <bookCase name="Jonas' Books">
5
+ <bookCount>12</bookCount>
6
+ <bigBooks>
7
+ <bigBook>GED</bigBook>
8
+ <bigBook>House of Leaves</bigBook>
9
+ </bigBooks>
10
+ </bookCase>
11
+ }
12
+
13
+ XML_CAMELCASE = %{
14
+ <BookCase Name="Jonas' Books">
15
+ <BookCount>12</BookCount>
16
+ <BigBooks>
17
+ <BigBook>GED</BigBook>
18
+ <BigBook>House of Leaves</BigBook>
19
+ </BigBooks>
20
+ </BookCase>
21
+ }
22
+
23
+ XML_UNDERSCORE = %{
24
+ <book_case name="Jonas' Books">
25
+ <book_count>12</book_count>
26
+ <big_books>
27
+ <big_book>GED</big_book>
28
+ <big_book>House of Leaves</big_book>
29
+ </big_books>
30
+ </book_case>
31
+ }
32
+
33
+ XML_DASHES = %{
34
+ <book-case name="Jonas' Books">
35
+ <book-count>12</book-count>
36
+ <big-books>
37
+ <big-book>GED</big-book>
38
+ <big-book>House of Leaves</big-book>
39
+ </big-books>
40
+ </book-case>
41
+ }
42
+
43
+ XML_UPCASE = %{
44
+ <BOOKCASE NAME="Jonas' Books">
45
+ <BOOKCOUNT>12</BOOKCOUNT>
46
+ <BIGBOOKS>
47
+ <BIGBOOK>GED</BIGBOOK>
48
+ <BIGBOOK>House of Leaves</BIGBOOK>
49
+ </BIGBOOKS>
50
+ </BOOKCASE>
51
+ }
52
+
53
+ class BookCase
54
+ include ROXML
55
+
56
+ xml_reader :book_count, :as => Integer, :required => true
57
+ xml_reader :big_books, :as => [], :required => true
58
+ end
59
+
60
+ class BookCaseCamelCase < BookCase
61
+ xml_convention :camelcase
62
+ end
63
+
64
+ class BookCaseUnderScore < BookCase
65
+ xml_convention :underscore
66
+ end
67
+
68
+ class BookCaseDashes < BookCase
69
+ xml_convention &:dasherize
70
+ end
71
+
72
+ class BookCaseCamelLower < BookCase
73
+ xml_convention {|val| val.camelcase(:lower) }
74
+ end
75
+
76
+ class BookCaseUpCase < BookCase
77
+ xml_convention {|val| val.gsub('_', '').upcase }
78
+ end
79
+
80
+ class InheritedBookCaseCamelCase < BookCaseCamelCase
81
+ end
82
+
83
+ class InheritedBookCaseUpCase < BookCaseUpCase
84
+ end
85
+
86
+ # Same as BookCase. Needed to keep BookCase clean for other tests
87
+ class ParentBookCaseDefault
88
+ include ROXML
89
+
90
+ xml_reader :book_count, :as => Integer, :required => true
91
+ xml_reader :big_books, :as => [], :required => true
92
+ end
93
+
94
+ class InheritedBookCaseDefault < ParentBookCaseDefault
95
+ end
96
+
97
+ class TestXMLConvention < ActiveSupport::TestCase
98
+ # TODO: Test convention applies to xml_name as well...
99
+
100
+ def test_default_convention_is_underscore
101
+ bc = BookCase.from_xml(XML_UNDERSCORE)
102
+ assert_has_book_case_info(bc)
103
+ end
104
+
105
+ [BookCaseUpCase, BookCaseCamelLower, BookCaseDashes, BookCaseUnderScore, BookCaseCamelCase].each do |klass|
106
+ define_method(:"test_xml_convention_#{klass.to_s.underscore}") do
107
+ data = :"XML_#{klass.to_s.sub('BookCase', '').upcase}"
108
+ assert_equal Proc, klass.roxml_naming_convention.class
109
+
110
+ bc = klass.from_xml(Object.const_get(data))
111
+ assert_has_book_case_info(bc)
112
+ end
113
+ end
114
+
115
+ def test_child_should_inherit_convention_if_it_doesnt_declare_one
116
+ [InheritedBookCaseUpCase, InheritedBookCaseCamelCase].each do |klass|
117
+ data = :"XML_#{klass.to_s.sub('InheritedBookCase', '').upcase}"
118
+ assert_equal Proc, klass.roxml_naming_convention.class
119
+
120
+ bc = klass.from_xml(Object.const_get(data))
121
+ assert_has_book_case_info(bc)
122
+ end
123
+ end
124
+
125
+ def test_child_should_inherit_convention_even_if_it_is_added_after_child_declaration
126
+ bc = InheritedBookCaseDefault.from_xml(XML_UNDERSCORE)
127
+ assert_has_book_case_info(bc)
128
+
129
+ ParentBookCaseDefault.class_eval do
130
+ xml_convention :dasherize
131
+ end
132
+
133
+ bc = InheritedBookCaseDefault.from_xml(XML_DASHES)
134
+ assert_has_book_case_info(bc)
135
+ assert_raise ROXML::RequiredElementMissing do
136
+ InheritedBookCaseDefault.from_xml(XML_UNDERSCORE)
137
+ end
138
+ end
139
+
140
+ def test_tag_name_should_get_convention_treatment_as_well
141
+ assert_equal "book-case-dashes", BookCaseDashes.tag_name
142
+ assert_equal "INHERITEDBOOKCASEUPCASE", InheritedBookCaseUpCase.tag_name
143
+ assert_equal "InheritedBookCaseCamelCase", InheritedBookCaseCamelCase.tag_name
144
+ end
145
+
146
+ def assert_has_book_case_info(bc)
147
+ assert_equal 12, bc.book_count
148
+ assert_equal ['GED', 'House of Leaves'], bc.big_books
149
+ end
150
+ end
@@ -0,0 +1,115 @@
1
+ require_relative './../test_helper'
2
+
3
+ class BookWithContributorHash
4
+ include ROXML
5
+
6
+ xml_reader :contributors, :as => {:key => '@role',
7
+ :value => 'name'}
8
+ end
9
+
10
+ class TestXMLHash < ActiveSupport::TestCase
11
+ def setup
12
+ @contents = {'quaquaversally' => 'adjective: (of a geological formation) sloping downward from the center in all directions.',
13
+ 'tergiversate' => 'To use evasions or ambiguities; equivocate.'}
14
+ end
15
+
16
+ def test_hash_preserves_data
17
+ b = BookWithContributorHash.from_xml(%{
18
+ <book isbn="0974514055">
19
+ <contributor role="author"><name>David Thomas</name></contributor>
20
+ <contributor role="supporting author"><name>Andrew Hunt</name></contributor>
21
+ <contributor role="supporting author"><name>Chad Fowler</name></contributor>
22
+ </book>
23
+ })
24
+ assert_equal({'author' => 'David Thomas', 'supporting author' => ['Andrew Hunt', 'Chad Fowler']},
25
+ b.contributors)
26
+ end
27
+
28
+ def test_hash_with_object_key_fails
29
+ assert_raise ArgumentError do
30
+ Class.new do
31
+ include ROXML
32
+
33
+ xml_reader :object_key_to_text, :as => {:key => BookWithContributorHash,
34
+ :value => 'text_node'}
35
+ end
36
+ end
37
+ end
38
+
39
+ def test_hash_with_object_value_fails
40
+ assert_raise ArgumentError do
41
+ Class.new do
42
+ include ROXML
43
+
44
+ xml_reader :key_to_object_value, :as => {:key => '@text_node',
45
+ :value => BookWithContributorHash}
46
+ end
47
+ end
48
+ end
49
+
50
+ def test_attrs_hash
51
+ dict = DictionaryOfAttrs.from_xml(fixture(:dictionary_of_attrs))
52
+ assert_equal Hash, dict.definitions.class
53
+ assert_equal @contents, dict.definitions
54
+ end
55
+
56
+ def test_text_hash
57
+ dict = DictionaryOfTexts.from_xml(fixture(:dictionary_of_texts))
58
+ assert_equal Hash, dict.definitions.class
59
+ assert_equal @contents, dict.definitions
60
+ end
61
+
62
+ def test_mixed_content_hash
63
+ dict = DictionaryOfMixeds.from_xml(fixture(:dictionary_of_mixeds))
64
+ assert_equal Hash, dict.definitions.class
65
+ assert_equal @contents, dict.definitions
66
+ end
67
+
68
+ def test_name_hash
69
+ dict = DictionaryOfNames.from_xml(fixture(:dictionary_of_names))
70
+ assert_equal Hash, dict.definitions.class
71
+ assert_equal @contents, dict.definitions
72
+ end
73
+
74
+ def test_guarded_name_hash
75
+ dict = DictionaryOfGuardedNames.from_xml(fixture(:dictionary_of_guarded_names))
76
+ assert_equal Hash, dict.definitions.class
77
+ assert_equal @contents, dict.definitions
78
+ end
79
+
80
+ def test_text_name_clashes
81
+ dict = DictionaryOfNameClashes.from_xml(fixture(:dictionary_of_name_clashes))
82
+ assert_equal Hash, dict.definitions.class
83
+ assert_equal @contents, dict.definitions
84
+ end
85
+
86
+ def test_attr_name_clashes
87
+ dict = DictionaryOfAttrNameClashes.from_xml(fixture(:dictionary_of_attr_name_clashes))
88
+ assert_equal Hash, dict.definitions.class
89
+ assert_equal @contents, dict.definitions
90
+ end
91
+
92
+ def test_it_should_gracefully_handle_empty_hash
93
+ dict = Class.new do
94
+ include ROXML
95
+
96
+ xml_reader :missing_hash, :as => {:key => :name, :value => :content}, :in => 'EmptyDictionary'
97
+ end
98
+
99
+ assert_equal({}, dict.from_xml(%{
100
+ <dict>
101
+ <EmptyDictionary>
102
+ </EmptyDictionary>
103
+ </dict>
104
+ }).missing_hash)
105
+ end
106
+
107
+ def test_as_hash_of_as_type_not_deprecated
108
+ assert_not_deprecated do
109
+ opts = ROXML::Definition.new(:name, :as => {:key => :name, :value => {:from => 'value', :as => OctalInteger}})
110
+ assert opts.hash?
111
+ assert_equal OctalInteger, opts.hash.value.sought_type
112
+ assert_equal 'value', opts.hash.value.name
113
+ end
114
+ end
115
+ end