Empact-roxml 2.4.3 → 2.5.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 (51) hide show
  1. data/History.txt +65 -0
  2. data/Manifest.txt +11 -6
  3. data/README.rdoc +48 -26
  4. data/Rakefile +5 -2
  5. data/TODO +30 -31
  6. data/examples/active_record.rb +70 -0
  7. data/examples/amazon.rb +1 -1
  8. data/examples/current_weather.rb +1 -1
  9. data/examples/library.rb +40 -0
  10. data/examples/posts.rb +8 -8
  11. data/examples/twitter.rb +2 -2
  12. data/examples/xml/active_record.xml +70 -0
  13. data/lib/roxml.rb +174 -174
  14. data/lib/roxml/definition.rb +165 -89
  15. data/lib/roxml/extensions/deprecation.rb +5 -0
  16. data/lib/roxml/extensions/string/conversions.rb +2 -3
  17. data/lib/roxml/hash_definition.rb +26 -25
  18. data/lib/roxml/xml.rb +15 -6
  19. data/lib/roxml/xml/parsers/libxml.rb +14 -6
  20. data/lib/roxml/xml/parsers/rexml.rb +16 -5
  21. data/lib/roxml/xml/references.rb +14 -17
  22. data/roxml.gemspec +14 -5
  23. data/spec/definition_spec.rb +563 -0
  24. data/spec/examples/active_record_spec.rb +40 -0
  25. data/spec/examples/library_spec.rb +41 -0
  26. data/spec/roxml_spec.rb +372 -0
  27. data/spec/shared_specs.rb +15 -0
  28. data/spec/spec_helper.rb +21 -4
  29. data/spec/string_spec.rb +15 -0
  30. data/spec/xml/parser_spec.rb +47 -0
  31. data/test/fixtures/book_valid.xml +1 -1
  32. data/test/fixtures/person_with_guarded_mothers.xml +3 -3
  33. data/test/mocks/mocks.rb +57 -45
  34. data/test/test_helper.rb +1 -1
  35. data/test/unit/definition_test.rb +161 -12
  36. data/test/unit/deprecations_test.rb +97 -0
  37. data/test/unit/to_xml_test.rb +30 -1
  38. data/test/unit/xml_bool_test.rb +15 -3
  39. data/test/unit/xml_construct_test.rb +6 -6
  40. data/test/unit/xml_hash_test.rb +18 -0
  41. data/test/unit/xml_initialize_test.rb +6 -3
  42. data/test/unit/xml_namespace_test.rb +1 -0
  43. data/test/unit/xml_object_test.rb +66 -5
  44. data/test/unit/xml_text_test.rb +3 -0
  45. metadata +57 -24
  46. data/test/unit/array_test.rb +0 -16
  47. data/test/unit/freeze_test.rb +0 -71
  48. data/test/unit/inheritance_test.rb +0 -63
  49. data/test/unit/overriden_output_test.rb +0 -33
  50. data/test/unit/roxml_test.rb +0 -60
  51. data/test/unit/string_test.rb +0 -11
data/History.txt CHANGED
@@ -1,3 +1,68 @@
1
+ == 2.5.1 (March 2, 2009)
2
+
3
+ * minor enhancements
4
+
5
+ * Add Document#save to REXML support, complete with XMLDecl output
6
+
7
+ * bug fixes
8
+
9
+ * rexml support has been fixed
10
+ * the first example in the readme was broken and has been fixed
11
+
12
+ == 2.5.0 (February 24, 2009)
13
+
14
+ * major enhancements
15
+
16
+ * support for mapping ActiveRecord classes. See examples/active_record.rb.
17
+ * .from_xml will now use the setter for the declared variable, if one is available,
18
+ rather than directly setting the instance variable
19
+ * All declaration type arguments are now supported via the :as parameter, e.g. :as => [MyType]. Other uses are deprecated.
20
+ * All xml source path arguments are now supported via the :from and :in parameters, e.g. :from => :attr or :from => '@MyAttr'. Other uses are deprecated.
21
+ * All other options are presented separately, e.g. :cdata => true rather than :as => :cdata. Other uses are deprecated.
22
+
23
+ * minor enhancements
24
+
25
+ * .xml_attr declaration declares neither a reader nor a writer. With it you're left to your own devices.
26
+ * You can use literal [] for the [:text] object type declaration,
27
+ though they should be used in the :as parameter: :as => []
28
+ * You can use [] with your :as declarations. e.g. :as => [Float] is
29
+ equivalent to the old :as => [Float, :array]
30
+ * Show the actual call point of a deprecation, rather than some internal path
31
+ * Add support for BigDecimal and Fixnum as block shorthands [James Healy]
32
+ * Update libxml support to 0.9.6, and add it as a dependency, to ensure correct versioning, and
33
+ as it's an order of magnitude faster than rexml
34
+
35
+ * breaking changes
36
+
37
+ * :else option only applies to instances created via .from_xml
38
+ * On .from_xml, #initialize is now called with the *initialization_args before extracting attributes from the xml.
39
+ * #xml_initialize has been replaced with the #after_parse callback, which takes no arguments.
40
+ * .xml_accessor will overwrite the setter for this variable if it has already been defined. Use .xml_reader or .xml_attr,
41
+ or define your writer later, if this is not the behavior you want.
42
+
43
+ * deprecations
44
+
45
+ * Use :cdata => true rather than :as => :cdata
46
+ * Use literal [] around your regular object type, rather than :as => :array
47
+ * Use :from => :content rather than the :content object declaration type
48
+ * Specifying an unknown symbol or Class for :as will raise in 3.0
49
+ * Specifying :as with anything other than a type argument e.g. :bool, Float, [Date],
50
+ will not be supported in 3.0
51
+ * Use :from => :attr or :from => '@attribute_name' rather than the :attr
52
+ object declaration type
53
+ * Passing any type declaration outside the :as parameter is deprecated
54
+ * In 3.0, attributes ending in _on and _at will default to :as => Date and DateTime, respectively,
55
+ rather than :text
56
+ * Deprecated hash :attrs declaration syntax in favor of {:key => '@attr1', :value => '@attr2'}
57
+ * Deprecated hash {Type => 'name'} declaration syntax in favor of {:as => Type, :from => 'name}
58
+ * Deprecated String#to_utf and #to_latin.
59
+
60
+ * bug fixes
61
+
62
+ * xml_accessor now properly handles punctuation, such that the writer appears without '?' for boolean attributes
63
+ * text node contents are no longer truncated when '&' are present in the contents
64
+ * When using :as => Integer or Float, don't raise on missing element [James Healy]
65
+
1
66
  == 2.4.3 (February 1, 2009)
2
67
 
3
68
  * 1 bug fix
data/Manifest.txt CHANGED
@@ -5,11 +5,14 @@ README.rdoc
5
5
  Rakefile
6
6
  TODO
7
7
  config/website.yml
8
+ examples/active_record.rb
8
9
  examples/amazon.rb
9
10
  examples/current_weather.rb
10
11
  examples/dashed_elements.rb
12
+ examples/library.rb
11
13
  examples/posts.rb
12
14
  examples/twitter.rb
15
+ examples/xml/active_record.xml
13
16
  examples/xml/amazon.xml
14
17
  examples/xml/current_weather.xml
15
18
  examples/xml/dashed_elements.xml
@@ -31,13 +34,20 @@ lib/roxml/xml/parsers/libxml.rb
31
34
  lib/roxml/xml/parsers/rexml.rb
32
35
  lib/roxml/xml/references.rb
33
36
  roxml.gemspec
37
+ spec/definition_spec.rb
38
+ spec/examples/active_record_spec.rb
34
39
  spec/examples/amazon_spec.rb
35
40
  spec/examples/current_weather_spec.rb
36
41
  spec/examples/dashed_elements_spec.rb
42
+ spec/examples/library_spec.rb
37
43
  spec/examples/post_spec.rb
38
44
  spec/examples/twitter_spec.rb
45
+ spec/roxml_spec.rb
46
+ spec/shared_specs.rb
39
47
  spec/spec.opts
40
48
  spec/spec_helper.rb
49
+ spec/string_spec.rb
50
+ spec/xml/parser_spec.rb
41
51
  tasks/rspec.rake
42
52
  tasks/test.rake
43
53
  test/bugs/rexml_bugs.rb
@@ -75,13 +85,8 @@ test/mocks/dictionaries.rb
75
85
  test/mocks/mocks.rb
76
86
  test/release/dependencies_test.rb
77
87
  test/test_helper.rb
78
- test/unit/array_test.rb
79
88
  test/unit/definition_test.rb
80
- test/unit/freeze_test.rb
81
- test/unit/inheritance_test.rb
82
- test/unit/overriden_output_test.rb
83
- test/unit/roxml_test.rb
84
- test/unit/string_test.rb
89
+ test/unit/deprecations_test.rb
85
90
  test/unit/to_xml_test.rb
86
91
  test/unit/xml_attribute_test.rb
87
92
  test/unit/xml_block_test.rb
data/README.rdoc CHANGED
@@ -14,36 +14,36 @@ ROXML, you can annotate the Ruby classes as follows:
14
14
  class Book
15
15
  include ROXML
16
16
 
17
- xml_reader :isbn, :attr => "ISBN" # attribute with name 'ISBN'
18
- xml_reader :title
19
- xml_reader :description, :as => :cdata # text node with cdata protection
20
- xml_reader :author
17
+ xml_accessor :isbn, :from => "@ISBN" # attribute with name 'ISBN'
18
+ xml_accessor :title
19
+ xml_accessor :description, :cdata => true # text node with cdata protection
20
+ xml_accessor :author
21
21
  end
22
22
 
23
23
  class Library
24
24
  include ROXML
25
25
 
26
- xml_accessor :name, :from => "NAME", :as => :cdata
27
- xml_accessor :books, [Book], :in => "books"
26
+ xml_accessor :name, :from => "NAME", :cdata => true
27
+ xml_accessor :books, :as => [Book] # by default roxml searches for books for in <book> child nodes, then, if none are present, in ./books/book children
28
28
  end
29
29
 
30
30
  To create a library and put a number of books in it we could run the following code:
31
31
 
32
- book = Book.new()
32
+ book = Book.new
33
33
  book.isbn = "0201710897"
34
34
  book.title = "The PickAxe"
35
35
  book.description = "Best Ruby book out there!"
36
36
  book.author = "David Thomas, Andrew Hunt, Dave Thomas"
37
37
 
38
- lib = Library.new()
38
+ lib = Library.new
39
39
  lib.name = "Favorite Books"
40
- lib << book
40
+ lib.books = [book]
41
41
 
42
42
  To save this information to an XML file:
43
43
 
44
- File.open("library.xml", "w") do |f|
45
- lib.to_xml.write(f, 0)
46
- end
44
+ doc = ROXML::XML::Document.new
45
+ doc.root = lib.to_xml
46
+ doc.save("library.xml")
47
47
 
48
48
  To later populate the library object from the XML file:
49
49
 
@@ -62,11 +62,22 @@ you would add a reference to another ROXML class. For example:
62
62
 
63
63
  can be mapped using the following code:
64
64
 
65
+ class Publisher
66
+ include ROXML
67
+
68
+ xml_accessor :name
69
+
70
+ # other important functionality
71
+ end
72
+
65
73
  class BookWithPublisher
66
74
  include ROXML
67
75
 
68
- xml_name :book
69
- xml_reader :publisher, Publisher
76
+ xml_name 'book'
77
+ xml_reader :publisher, :as => Publisher
78
+
79
+ # or, alternatively, if no class is needed to hang functionality on:
80
+ # xml_reader :publisher, :from => 'name', :in => 'publisher'
70
81
  end
71
82
 
72
83
  Note: In the above example, _xml_name_ annotation tells ROXML to set the element
@@ -78,34 +89,45 @@ in this case.
78
89
  Extending the above examples, say you want to parse a book's page count and have it available as an Integer.
79
90
  In such a case, you can extend any object with a block to manipulate it's value at parse time. For example:
80
91
 
81
- class Child
92
+ class Dog
82
93
  include ROXML
83
94
 
84
- xml_reader :age, :attr do |val|
85
- Integer(val)
86
- end
95
+ xml_reader(:age, :from => '@human_years', :as => Integer) {|years| years * 7 }
87
96
  end
88
97
 
89
98
  The result of the block above is stored, rather than the actual value parsed from the document.
90
99
 
91
100
  == Construction
92
101
 
93
- Complicated initialization may require action on multiple attributes of an object. As such, you can
94
- define method xml_initialize to perform initialization after instantiation and parsing, including
95
- causing your ROXML object to call its own constructor, as in the following:
102
+ Object life-cycle is as follows: .from_xml is called with a first argument representing the xml
103
+ in file, string, or path form, and with optional initialization_args following.
104
+
105
+ Firt .new and thus #initialize, is called with those same initialization_args, or no args if none
106
+ are present. Then the object is populated with the attribute values from xml. Then the
107
+ #after_parse callback is called, with no arguments.
108
+
109
+ In #after_parse you can ensure that your object initialization is complete, including initialization which
110
+ requires more than one variable in concert.
111
+
112
+ E.g.:
96
113
 
97
114
  class Measurement
98
115
  include ROXML
99
116
 
100
- xml_reader :units, :attr
101
- xml_reader :value, :content
117
+ xml_reader :units, :from => :attr
118
+ xml_reader :value, :from => :content
119
+
120
+ def initialize(value = 0, units = 'meters')
121
+ to_metric
122
+ end
102
123
 
103
- def xml_initialize
124
+ private
125
+ def after_parse
104
126
  # xml attributes of self are already valid
105
- initialize(value, units)
127
+ to_metric
106
128
  end
107
129
 
108
- def initialize(value, units)
130
+ def to_metric
109
131
  # translate units & value into metric, for example
110
132
  end
111
133
  end
data/Rakefile CHANGED
@@ -16,10 +16,13 @@ $hoe = Hoe.new('roxml', ROXML::VERSION) do |p|
16
16
  p.changes = File.read("History.txt").split(/^==/)[1].strip
17
17
  p.rubyforge_name = p.name
18
18
  p.extra_deps = [
19
- ['activesupport','>= 2.1.0']
19
+ ['activesupport','>= 2.1.0'],
20
+ ['libxml-ruby', '>= 0.8.6']
20
21
  ]
21
22
  p.extra_dev_deps = [
22
- ['newgem', ">= #{::Newgem::VERSION}"]
23
+ ['newgem', ">= #{::Newgem::VERSION}"],
24
+ ['sqlite3-ruby', '>= 1.2.4' ],
25
+ ['activerecord', '>= 2.2.2' ]
23
26
  ]
24
27
 
25
28
  p.summary = "Ruby Object to XML mapping library"
data/TODO CHANGED
@@ -1,37 +1,10 @@
1
1
  Planned:
2
2
 
3
- v 2.5
4
- * Test out compatibility with ActiveRecord such that it's easy
5
- to have both xml & database-sourced objects
6
-
7
- * provide an overridable extension point for the #to_xml-ing of
8
- values. This would enable easy accommodation of non-standard formats.
9
- For example, formatting a number with leading zeros.
10
-
11
- * Ensure (perhaps optionally) that references are unambiguous. That is error/warn
12
- a singular specification has multiple possible node references
13
-
14
- * Automatically use Date or DateTime for accessors ending in '_on' and '_at', respectively
15
-
16
- * Add xml_in helper to share :in declarations between several attributes. E.g.:
17
-
18
- xml_reader :count, :in => 'Attributes', :as => Integer
19
- xml_reader :something_else, :in => 'Attributes', :as => Date
20
-
21
- becomes:
22
-
23
- xml_in 'Attributes' do |xml|
24
- xml.reader :count, :as => Integer
25
- xml.reader :something_else, :as => Date
26
- end
27
-
28
- * :self => true for sending method_missing to this attribute?
29
-
30
- * :attributes extensions ala HappyMapper?
31
-
32
3
  v 3.0
33
4
  * remove deprecated functionality
34
5
 
6
+ * Automatically use Date or DateTime for accessors ending in '_on' and '_at', respectively
7
+
35
8
  * Clarify API via seperation of concerns. :as for Type, :from and :in for location,
36
9
  All other options as named options, e.g. :cdata => true rather than :as => :cdata.
37
10
  A few not so great examples...:
@@ -52,12 +25,38 @@ v 3.0
52
25
 
53
26
  xml_reader :count, :size, :number, :as => Integer, :from => :attr
54
27
 
28
+ * Consider class_inheritable_attribute rather than superclass.try stuff.
29
+
30
+ * Do some benchmarking
31
+
32
+ v 3.1
33
+
34
+ * Back with http://xml-object.rubyforge.org/doc/ to minimize need for specifications?
35
+
55
36
  * Commandeer #parse to use opposite #from_xml, but in an unrooted, collection-friendly fashion,
56
37
  ala HappyMapper's parse
57
38
 
58
- * Back with http://xml-object.rubyforge.org/doc/ to minimize need for specifications?
39
+ v 3.x
40
+
41
+ * :self => true for sending method_missing to this attribute?
42
+
43
+ * :attributes extensions ala HappyMapper?
44
+
45
+ * Add xml_attrs helper to share :in declarations between several attributes. E.g.:
46
+
47
+ xml_reader :count, :in => 'Attributes', :as => Integer
48
+ xml_reader :something_else, :in => 'Attributes', :as => Date
49
+
50
+ becomes:
51
+
52
+ xml_attrs :in => 'Attributes' do |xml|
53
+ xml.reader :count, :as => Integer
54
+ xml.reader :something_else, :as => Date
55
+ end
56
+
57
+ * Ensure (perhaps optionally) that references are unambiguous. That is error/warn
58
+ a singular specification has multiple possible node references
59
59
 
60
60
  * Use lazy evaluation to minimize parsing time for large files
61
61
 
62
- * Consider class_inheritable_attribute rather than superclass.try stuff.
63
62
 
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec/spec_helper')
3
+ require 'sqlite3'
4
+ require 'activerecord'
5
+
6
+ DB_PATH = File.join(File.dirname(__FILE__), 'active_record.sqlite3')
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => DB_PATH
10
+ )
11
+
12
+ class Waypoint < ActiveRecord::Base
13
+ include ROXML
14
+
15
+ belongs_to :route
16
+
17
+ xml_attr :isLeg
18
+ xml_attr :lonlatx
19
+ xml_attr :lonlaty
20
+ xml_attr :gridReference
21
+ xml_attr :ascent
22
+ xml_attr :descent
23
+ xml_attr :distance
24
+ xml_attr :bearing
25
+ xml_attr :timemins
26
+ end
27
+
28
+ class Route < ActiveRecord::Base
29
+ include ROXML
30
+
31
+ has_many :waypoints
32
+
33
+ xml_attr :title
34
+ xml_attr :totalDist
35
+ xml_attr :totalMins
36
+ xml_attr :totalHg
37
+ xml_attr :lonlatx
38
+ xml_attr :lonlaty
39
+ xml_attr :grcenter
40
+
41
+ xml_attr :waypoints, :as => [Waypoint], :in => "waypoints"
42
+ end
43
+
44
+ # do a quick pseudo migration. This should only get executed on the first run
45
+ if !Waypoint.table_exists?
46
+ ActiveRecord::Base.connection.create_table(:waypoints) do |t|
47
+ t.column :route_id, :integer
48
+ t.column :isLeg, :string
49
+ t.column :lonlatx, :string
50
+ t.column :lonlaty, :string
51
+ t.column :gridReference, :string
52
+ t.column :ascent, :string
53
+ t.column :descent, :string
54
+ t.column :distance, :string
55
+ t.column :bearing, :string
56
+ t.column :timeMins, :string
57
+ end
58
+ end
59
+
60
+ if !Route.table_exists?
61
+ ActiveRecord::Base.connection.create_table(:routes) do |t|
62
+ t.column :title, :string
63
+ t.column :totalDist, :string
64
+ t.column :totalMins, :string
65
+ t.column :totalHg, :string
66
+ t.column :lonlatx, :string
67
+ t.column :lonlaty, :string
68
+ t.column :grcenter, :string
69
+ end
70
+ end
data/examples/amazon.rb CHANGED
@@ -21,7 +21,7 @@ module PITA
21
21
  class ItemSearchResponse < Base
22
22
  xml_reader :total_results, :as => Integer, :in => 'Items'
23
23
  xml_reader :total_pages, :as => Integer, :in => 'Items'
24
- xml_reader :items, [Item]
24
+ xml_reader :items, :as => [Item]
25
25
  end
26
26
  end
27
27
 
@@ -15,7 +15,7 @@ class WeatherObservation < Base
15
15
  end
16
16
 
17
17
  class Weather < Base
18
- xml_reader :observation, WeatherObservation, :required => true
18
+ xml_reader :observation, :as => WeatherObservation, :required => true
19
19
  end
20
20
 
21
21
  unless defined?(Spec)
@@ -0,0 +1,40 @@
1
+ class Publisher
2
+ include ROXML
3
+
4
+ xml_accessor :name
5
+
6
+ def initialize(name = nil)
7
+ @name = name
8
+ end
9
+
10
+ def ==(other)
11
+ name == other.name
12
+ end
13
+
14
+ # other important functionality
15
+ end
16
+
17
+ class Novel
18
+ include ROXML
19
+
20
+ xml_accessor :isbn, :from => "@ISBN" # attribute with name 'ISBN'
21
+ xml_accessor :title
22
+ xml_accessor :description, :cdata => true # text node with cdata protection
23
+ xml_accessor :author
24
+ xml_accessor :publisher, :as => Publisher # singular object reference for illustrative purposes.
25
+
26
+ def ==(other)
27
+ self.class.roxml_attrs.map(&:accessor).all? {|attr| send(attr) == other.send(attr) }
28
+ end
29
+ end
30
+
31
+ class Library
32
+ include ROXML
33
+
34
+ xml_accessor :name, :from => "NAME", :cdata => true
35
+ xml_accessor :novels, :as => [Novel] # by default roxml searches for books for in <novel> children, then, if none are present, in ./novels/novel children
36
+
37
+ def ==(other)
38
+ name == other.name && novels == other.novels
39
+ end
40
+ end