dm-serializer 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,17 @@
1
+ === 0.9.9 / 2009-01-04
2
+
3
+ * 1 major enhancement:
4
+
5
+ * Uses LibXML or Nokogiri if available for a ~6x improvement in
6
+ serialization performance.
7
+
8
+ * 4 minor enhancements:
9
+
10
+ * Added :element_name and :collection_element_name options to #to_xml
11
+ * Removed #xml_element_name override in #to_xml
12
+ * Removed :read_only_attributes option from #to_json
13
+ * Removed #serialized_properties override in #to_json
14
+
1
15
  === 0.9.8 / 2008-12-07
2
16
 
3
17
  * 2 minor enhancements:
data/Manifest.txt CHANGED
@@ -1,13 +1,23 @@
1
1
  History.txt
2
2
  LICENSE
3
3
  Manifest.txt
4
- README.txt
4
+ README.textile
5
5
  Rakefile
6
6
  TODO
7
7
  autotest/discover.rb
8
8
  autotest/dmserializer_rspec.rb
9
+ benchmarks/to_xml.rb
9
10
  lib/dm-serializer.rb
11
+ lib/dm-serializer/common.rb
12
+ lib/dm-serializer/to_csv.rb
13
+ lib/dm-serializer/to_json.rb
14
+ lib/dm-serializer/to_xml.rb
15
+ lib/dm-serializer/to_yaml.rb
10
16
  lib/dm-serializer/version.rb
17
+ lib/dm-serializer/xml_serializers.rb
18
+ lib/dm-serializer/xml_serializers/libxml.rb
19
+ lib/dm-serializer/xml_serializers/nokogiri.rb
20
+ lib/dm-serializer/xml_serializers/rexml.rb
11
21
  spec/fixtures/cow.rb
12
22
  spec/fixtures/planet.rb
13
23
  spec/fixtures/quan_tum_cat.rb
data/README.textile ADDED
@@ -0,0 +1,61 @@
1
+ h1. dm-serializer
2
+
3
+ h2. Overview
4
+
5
+ dm-serializer allows DataMapper models and collections to be serialized to a variety of formats (currently JSON, XML, YAML and CSV).
6
+
7
+ h2. How it works
8
+
9
+ One method is added to each model/collection for each serialization type - @to_json@, @to_xml@, @to_yaml@, and @to_csv@. With the exception of @to_csv@, all of these methods share the same interface. @to_json@ will be used for examples. Any method specific behaviour is documented in its own section below.
10
+
11
+ <pre><code>require 'dm-serializer'
12
+
13
+ class Cow
14
+ include DataMapper::Resource
15
+
16
+ property :id, Integer, :key => true
17
+ property :name, String
18
+
19
+ def description
20
+ "A Cow"
21
+ end
22
+ end
23
+
24
+ cow = Cow.create(
25
+ :id => 1,
26
+ :name => "Berta"
27
+ )
28
+
29
+ cow.to_json # => { "id": 1, "name": "Berta" }
30
+ cow.to_json(:only => [:name]) # => { "name": "Berta" }
31
+ cow.to_json(:exclude => [:id]) # => { "name": "Berta" }
32
+ cow.to_json(:methods => [:desc]) # => { "id": 1, "name": "Berta", "desc": "A Cow" }</code></pre>
33
+
34
+ You can include associations by passing the association accessor the @:methods@ option.
35
+
36
+ If you want to only load a particular serialization method, that's cool, you can do that:
37
+
38
+ <pre><code>require 'dm-serializer/to_json'</code></pre>
39
+
40
+ h2. to_xml
41
+
42
+ @to_xml@ supports some extra options to allow you to override the element names
43
+
44
+ <pre><code>cow.to_xml( :element_name => 'bovine') # => <bovine><id>1</id><name>Berta</name></bovine>
45
+ cows.to_xml(:collection_element_name => 'kine') # => <kine><bovine><id>1</id><name>Berta</name></bovine></kine></code></pre>
46
+
47
+ If you would like a nice speed boost (~5x), require @libxml@ or @nokogiri@ before @dm-serializer@, and that library will be used rather than REXML.
48
+
49
+ h2. to_csv
50
+
51
+ @to_csv@ currently doesn't support any options yet. It will in the future. It will not support serializing child associations.
52
+
53
+ h2. Arrays, Hashes, and other core classes
54
+
55
+ @dm-serializer@ only adds serialization methods to DataMapper objects and collections, however some libraries used (json, yaml) add methods to core classes, such as @Array@. Note that passing @dm-serializer@ options (such as @:only@) to these methods is *not supported*.
56
+
57
+ <pre><code>Cow.all.to_a.to_yaml(:only => 'name') # WILL NOT WORK</code></pre>
58
+
59
+ h2. Beware
60
+
61
+ If you go spelunking through the code you will find other undocumented options. Use at your own risk, I plan on removing or changing these in the near future.
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ GEM_NAME = 'dm-serializer'
14
14
  GEM_VERSION = DataMapper::Serializer::VERSION
15
15
  GEM_DEPENDENCIES = [['dm-core', "~>#{GEM_VERSION}"]]
16
16
  GEM_CLEAN = %w[ log pkg coverage ]
17
- GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] }
17
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.textile LICENSE TODO History.txt ] }
18
18
 
19
19
  PROJECT_NAME = 'datamapper'
20
20
  PROJECT_URL = "http://github.com/sam/dm-more/tree/master/#{GEM_NAME}"
@@ -0,0 +1,89 @@
1
+ require "rubygems"
2
+ require 'pathname'
3
+
4
+ gem 'dm-core', '~>0.9.9'
5
+ require 'dm-core'
6
+
7
+ spec_dir_path = Pathname(__FILE__).dirname.expand_path
8
+ $LOAD_PATH.unshift(spec_dir_path.parent + 'lib/')
9
+ require 'dm-serializer'
10
+
11
+ def load_driver(name, default_uri)
12
+ begin
13
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
14
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
15
+ DataMapper::Repository.adapters[:alternate] = DataMapper::Repository.adapters[name]
16
+ true
17
+ rescue LoadError => e
18
+ warn "Could not load do_#{name}: #{e}"
19
+ false
20
+ end
21
+ end
22
+
23
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
24
+
25
+ class Cow
26
+ include DataMapper::Resource
27
+
28
+ property :id, Integer, :key => true
29
+ property :composite, Integer, :key => true
30
+ property :name, String
31
+ property :breed, String
32
+
33
+ has n, :baby_cows, :class_name => 'Cow'
34
+ belongs_to :mother_cow, :class_name => 'Cow'
35
+ end
36
+
37
+ require "benchwarmer"
38
+
39
+ TIMES = 2000
40
+ DataMapper.auto_migrate!
41
+ cow = Cow.create(
42
+ :id => 89,
43
+ :composite => 34,
44
+ :name => 'Berta',
45
+ :breed => 'Guernsey'
46
+ )
47
+ all_cows = Cow.all
48
+
49
+ puts "REXML"
50
+ Benchmark.warmer(TIMES) do
51
+ group("Serialization:") do
52
+ report "Single Resource" do
53
+ cow.to_xml
54
+ end
55
+ report "Collection" do
56
+ all_cows.to_xml
57
+ end
58
+ end
59
+ end
60
+
61
+ require 'nokogiri'
62
+ load 'dm-serializer/xml_serializers.rb'
63
+
64
+ puts "Nokogiri"
65
+ Benchmark.warmer(TIMES) do
66
+ group("Serialization:") do
67
+ report "Single Resource" do
68
+ cow.to_xml
69
+ end
70
+ report "Collection" do
71
+ all_cows.to_xml
72
+ end
73
+ end
74
+ end
75
+
76
+ require 'libxml'
77
+ load 'dm-serializer/xml_serializers.rb'
78
+
79
+ puts "LibXML"
80
+ Benchmark.warmer(TIMES) do
81
+ group("Serialization:") do
82
+ report "Single Resource" do
83
+ cow.to_xml
84
+ end
85
+ report "Collection" do
86
+ all_cows.to_xml
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,24 @@
1
+ module DataMapper
2
+ module Serialize
3
+ # Returns propreties to serialize based on :only or :exclude arrays, if provided
4
+ # :only takes precendence over :exclude
5
+ #
6
+ # @return <Array> properties that need to be serialized
7
+ def properties_to_serialize(options)
8
+ only_properties = Array(options[:only])
9
+ excluded_properties = Array(options[:exclude])
10
+
11
+ self.class.properties(repository.name).reject do |p|
12
+ if only_properties.include? p.name
13
+ false
14
+ else
15
+ excluded_properties.include?(p.name) || !(only_properties.empty? || only_properties.include?(p.name))
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module Resource
22
+ include Serialize
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ require 'dm-serializer/common'
2
+
3
+ begin
4
+ gem('fastercsv')
5
+ require 'faster_csv'
6
+ rescue LoadError
7
+ nil
8
+ end
9
+
10
+ module DataMapper
11
+ module Serialize
12
+ # Serialize a Resource to comma-separated values (CSV).
13
+ #
14
+ # @return <String> a CSV representation of the Resource
15
+ def to_csv(writer = '')
16
+ FasterCSV.generate(writer) do |csv|
17
+ row = []
18
+ self.class.properties(repository.name).each do |property|
19
+ row << send(property.name).to_s
20
+ end
21
+ csv << row
22
+ end
23
+ end
24
+ end
25
+
26
+ class Collection
27
+ def to_csv
28
+ result = ""
29
+ each do |item|
30
+ result << item.to_csv + "\n"
31
+ end
32
+ result
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ require 'dm-serializer/common'
2
+
3
+ begin
4
+ gem('json')
5
+ require 'json/ext'
6
+ rescue LoadError
7
+ gem('json_pure')
8
+ require 'json/pure'
9
+ end
10
+
11
+ module DataMapper
12
+ module Serialize
13
+ # Serialize a Resource to JavaScript Object Notation (JSON; RFC 4627)
14
+ #
15
+ # @return <String> a JSON representation of the Resource
16
+ def to_json(*args)
17
+ options = args.first || {}
18
+ result = '{ '
19
+ fields = []
20
+
21
+ propset = properties_to_serialize(options)
22
+
23
+ fields += propset.map do |property|
24
+ "#{property.name.to_json}: #{send(property.getter).to_json}"
25
+ end
26
+
27
+ # add methods
28
+ (options[:methods] || []).each do |meth|
29
+ if self.respond_to?(meth)
30
+ fields << "#{meth.to_json}: #{send(meth).to_json}"
31
+ end
32
+ end
33
+
34
+ # Note: if you want to include a whole other model via relation, use :methods
35
+ # comments.to_json(:relationships=>{:user=>{:include=>[:first_name],:methods=>[:age]}})
36
+ # add relationships
37
+ # TODO: This needs tests and also needs to be ported to #to_xml and #to_yaml
38
+ (options[:relationships] || {}).each do |rel,opts|
39
+ if self.respond_to?(rel)
40
+ fields << "#{rel.to_json}: #{send(rel).to_json(opts)}"
41
+ end
42
+ end
43
+
44
+ result << fields.join(', ')
45
+ result << ' }'
46
+ result
47
+ end
48
+ end
49
+
50
+ module Associations
51
+ # the json gem adds Object#to_json, which breaks the DM proxies, since it
52
+ # happens *after* the proxy has been blank slated. This code removes the added
53
+ # method, so it is delegated correctly to the Collection
54
+ proxies = []
55
+
56
+ proxies << ManyToMany::Proxy if defined?(ManyToMany::Proxy)
57
+ proxies << OneToMany::Proxy if defined?(OneToMany::Proxy)
58
+ proxies << ManyToOne::Proxy if defined?(ManyToOne::Proxy)
59
+
60
+ proxies.each do |proxy|
61
+ if proxy.public_instance_methods.any? { |m| m.to_sym == :to_json }
62
+ proxy.send(:undef_method, :to_json)
63
+ end
64
+ end
65
+ end
66
+
67
+ class Collection
68
+ def to_json(*args)
69
+ opts = args.first || {}
70
+ "[" << map {|e| e.to_json(opts)}.join(",") << "]"
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,61 @@
1
+ require 'dm-serializer/common'
2
+ require 'dm-serializer/xml_serializers'
3
+ require 'rexml/document'
4
+
5
+ module DataMapper
6
+ module Serialize
7
+ # Serialize a Resource to XML
8
+ #
9
+ # @return <REXML::Document> an XML representation of this Resource
10
+ def to_xml(opts = {})
11
+ to_xml_document(opts).to_s
12
+ end
13
+
14
+ protected
15
+ # This method requires certain methods to be implemented in the individual
16
+ # serializer library subclasses:
17
+ # new_document
18
+ # root_node
19
+ # add_property_node
20
+ # add_node
21
+ def to_xml_document(opts={}, doc = nil)
22
+ xml = XMLSerializers::SERIALIZER
23
+ doc ||= xml.new_document
24
+ default_xml_element_name = lambda { Extlib::Inflection.underscore(self.class.name).tr("/", "-") }
25
+ root = xml.root_node(doc, opts[:element_name] || default_xml_element_name[])
26
+ properties_to_serialize(opts).each do |property|
27
+ value = send(property.name)
28
+ attrs = (property.type == String) ? {} : {'type' => property.type.to_s.downcase}
29
+ xml.add_node(root, property.name.to_s, value, attrs)
30
+ end
31
+
32
+ (opts[:methods] || []).each do |meth|
33
+ if self.respond_to?(meth)
34
+ xml_name = meth.to_s.gsub(/[^a-z0-9_]/, '')
35
+ value = send(meth)
36
+ xml.add_node(root, xml_name, value.to_s) unless value.nil?
37
+ end
38
+ end
39
+ doc
40
+ end
41
+ end
42
+
43
+ class Collection
44
+ def to_xml(opts = {})
45
+ to_xml_document(opts).to_s
46
+ end
47
+
48
+ protected
49
+
50
+ def to_xml_document(opts = {})
51
+ xml = DataMapper::Serialize::XMLSerializers::SERIALIZER
52
+ doc = xml.new_document
53
+ default_collection_element_name = lambda {Extlib::Inflection.pluralize(Extlib::Inflection.underscore(self.model.to_s)).tr("/", "-")}
54
+ root = xml.root_node(doc, opts[:collection_element_name] || default_collection_element_name[], {'type' => 'array'})
55
+ self.each do |item|
56
+ item.send(:to_xml_document, opts, doc)
57
+ end
58
+ doc
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ require 'dm-serializer/common'
2
+
3
+ module DataMapper
4
+ module Serialize
5
+ # Serialize a Resource to YAML
6
+ #
7
+ # @return <YAML> a YAML representation of this Resource
8
+ def to_yaml(opts_or_emitter = {})
9
+ if opts_or_emitter.is_a?(YAML::Syck::Emitter)
10
+ emitter = opts_or_emitter
11
+ opts = {}
12
+ else
13
+ emitter = {}
14
+ opts = opts_or_emitter
15
+ end
16
+
17
+ YAML::quick_emit(object_id,emitter) do |out|
18
+ out.map(nil,to_yaml_style) do |map|
19
+ propset = properties_to_serialize(opts)
20
+ propset.each do |property|
21
+ value = send(property.name.to_sym)
22
+ map.add(property.name, value.is_a?(Class) ? value.to_s : value)
23
+ end
24
+ # add methods
25
+ (opts[:methods] || []).each do |meth|
26
+ if self.respond_to?(meth)
27
+ map.add(meth.to_sym, send(meth))
28
+ end
29
+ end
30
+ (instance_variable_get("@yaml_addes") || []).each do |k,v|
31
+ map.add(k.to_s,v)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class Collection
39
+ def to_yaml(opts_or_emitter = {})
40
+ if opts_or_emitter.is_a?(YAML::Syck::Emitter)
41
+ to_a.to_yaml(opts_or_emitter)
42
+ else
43
+ # FIXME: Don't double handle the YAML (remove the YAML.load)
44
+ to_a.collect {|x| YAML.load(x.to_yaml(opts_or_emitter)) }.to_yaml
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  module Serializer
3
- VERSION = '0.9.8'
3
+ VERSION = '0.9.9'
4
4
  end
5
5
  end
@@ -0,0 +1,29 @@
1
+ module DataMapper
2
+ module Serialize
3
+ module XMLSerializers
4
+ module LibXML
5
+ def self.new_document
6
+ ::LibXML::XML::Document.new
7
+ end
8
+
9
+ def self.root_node(doc, name, attrs = {})
10
+ root = ::LibXML::XML::Node.new(name)
11
+ attrs.each do |attr_name, attr_val|
12
+ root[attr_name] = attr_val
13
+ end
14
+ doc.root.nil? ? doc.root = root : doc.root << root
15
+ root
16
+ end
17
+
18
+ def self.add_node(parent, name, value, attrs = {})
19
+ value_str = value.to_s unless value.nil?
20
+ node = ::LibXML::XML::Node.new(name, value_str)
21
+ attrs.each do |attr_name, attr_val|
22
+ node[attr_name] = attr_val
23
+ end
24
+ parent << node
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ module DataMapper
2
+ module Serialize
3
+ module XMLSerializers
4
+ module Nokogiri
5
+ def self.new_document
6
+ ::Nokogiri::XML::Document.new
7
+ end
8
+
9
+ def self.root_node(doc, name, attrs = {})
10
+ root = ::Nokogiri::XML::Node.new(name, doc)
11
+ attrs.each do |attr_name, attr_val|
12
+ root[attr_name] = attr_val
13
+ end
14
+ doc.root.nil? ? doc.root = root : doc.root << root
15
+ root
16
+ end
17
+
18
+ def self.add_node(parent, name, value, attrs = {})
19
+ node = ::Nokogiri::XML::Node.new(name, parent.document)
20
+ node << ::Nokogiri::XML::Text.new(value.to_s, parent.document) unless value.nil?
21
+ attrs.each {|attr_name, attr_val| node[attr_name] = attr_val }
22
+ parent << node
23
+ node
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ module DataMapper
2
+ module Serialize
3
+ module XMLSerializers
4
+ module REXML
5
+ def self.new_document
6
+ ::REXML::Document.new
7
+ end
8
+
9
+ def self.root_node(document, name, attrs = {})
10
+ add_node(document.root || document, name, nil, attrs)
11
+ end
12
+
13
+ def self.add_node(parent, name, value, attrs = {})
14
+ node = parent.add_element(name)
15
+ attrs.each {|attr_name, attr_val| node.attributes[attr_name] = attr_val}
16
+ node << ::REXML::Text.new(value.to_s) unless value.nil?
17
+ node
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'dm-serializer/xml_serializers/rexml'
2
+ require 'dm-serializer/xml_serializers/nokogiri'
3
+ require 'dm-serializer/xml_serializers/libxml'
4
+
5
+ module DataMapper
6
+ module Serialize
7
+ module XMLSerializers
8
+ SERIALIZER = if defined?(::LibXML)
9
+ LibXML
10
+ elsif defined?(::Nokogiri)
11
+ Nokogiri
12
+ else
13
+ REXML
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/dm-serializer.rb CHANGED
@@ -1,230 +1,4 @@
1
- require 'rexml/document'
2
-
3
- begin
4
- gem('fastercsv')
5
- require 'faster_csv'
6
- rescue LoadError
7
- nil
8
- end
9
-
10
- begin
11
- gem('json')
12
- require 'json/ext'
13
- rescue LoadError
14
- gem('json_pure')
15
- require 'json/pure'
16
- end
17
-
18
- module DataMapper
19
- module Serialize
20
- # Serialize a Resource to JavaScript Object Notation (JSON; RFC 4627)
21
- #
22
- # @return <String> a JSON representation of the Resource
23
- def to_json(*args)
24
- options = args.first || {}
25
- result = '{ '
26
- fields = []
27
-
28
- propset = properties_to_serialize(options)
29
-
30
- fields += propset.map do |property|
31
- "#{property.name.to_json}: #{send(property.getter).to_json}"
32
- end
33
-
34
- if self.respond_to?(:serialize_properties)
35
- self.serialize_properties.each do |k,v|
36
- fields << "#{k.to_json}: #{v.to_json}"
37
- end
38
- end
39
-
40
- if self.class.respond_to?(:read_only_attributes) && exclude_read_only
41
- self.class.read_only_attributes.each do |property|
42
- fields << "#{property.to_json}: #{send(property).to_json}"
43
- end
44
- end
45
-
46
- # add methods
47
- (options[:methods] || []).each do |meth|
48
- if self.respond_to?(meth)
49
- fields << "#{meth.to_json}: #{send(meth).to_json}"
50
- end
51
- end
52
-
53
- # Note: if you want to include a whole other model via relation, use :methods
54
- # comments.to_json(:relationships=>{:user=>{:include=>[:first_name],:methods=>[:age]}})
55
- # add relationships
56
- (options[:relationships] || {}).each do |rel,opts|
57
- if self.respond_to?(rel)
58
- fields << "#{rel.to_json}: #{send(rel).to_json(opts)}"
59
- end
60
- end
61
-
62
- result << fields.join(', ')
63
- result << ' }'
64
- result
65
- end
66
-
67
- # Serialize a Resource to comma-separated values (CSV).
68
- #
69
- # @return <String> a CSV representation of the Resource
70
- def to_csv(writer = '')
71
- FasterCSV.generate(writer) do |csv|
72
- row = []
73
- self.class.properties(repository.name).each do |property|
74
- row << send(property.name).to_s
75
- end
76
- csv << row
77
- end
78
- end
79
-
80
- # Serialize a Resource to XML
81
- #
82
- # @return <REXML::Document> an XML representation of this Resource
83
- def to_xml(opts = {})
84
-
85
- to_xml_document(opts).to_s
86
- end
87
-
88
- # Serialize a Resource to YAML
89
- #
90
- # @return <YAML> a YAML representation of this Resource
91
- def to_yaml(opts = {})
92
- YAML::quick_emit(object_id,opts) do |out|
93
- out.map(nil,to_yaml_style) do |map|
94
- propset = properties_to_serialize(opts)
95
- propset.each do |property|
96
- value = send(property.name.to_sym)
97
- map.add(property.name, value.is_a?(Class) ? value.to_s : value)
98
- end
99
- # add methods
100
- (opts[:methods] || []).each do |meth|
101
- if self.respond_to?(meth)
102
- map.add(meth.to_sym, send(meth))
103
- end
104
- end
105
- (instance_variable_get("@yaml_addes") || []).each do |k,v|
106
- map.add(k.to_s,v)
107
- end
108
- end
109
- end
110
- end
111
-
112
- protected
113
-
114
- # Returns propreties to serialize based on :only or :exclude arrays, if provided
115
- # :only takes precendence over :exclude
116
- #
117
- # @return <Array> properties that need to be serialized
118
- def properties_to_serialize(options)
119
- only_properties = Array(options[:only])
120
- excluded_properties = Array(options[:exclude])
121
- exclude_read_only = options[:without_read_only_attributes] || false
122
-
123
- self.class.properties(repository.name).reject do |p|
124
- if only_properties.include? p.name
125
- false
126
- else
127
- excluded_properties.include?(p.name) || !(only_properties.empty? || only_properties.include?(p.name))
128
- end
129
- end
130
- end
131
-
132
-
133
- # Return the name of this Resource - to be used as the root element name.
134
- # This can be overloaded.
135
- #
136
- # @return <String> name of this Resource
137
- def xml_element_name
138
- Extlib::Inflection.underscore(self.class.name).tr("/", "-")
139
- end
140
-
141
- # Return a REXML::Document representing this Resource
142
- #
143
- # @return <REXML::Document> an XML representation of this Resource
144
- def to_xml_document(opts={}, doc=nil)
145
- doc ||= REXML::Document.new
146
- root = doc.add_element(xml_element_name)
147
-
148
- #TODO old code base was converting single quote to double quote on attribs
149
-
150
- propset = properties_to_serialize(opts)
151
- propset.each do |property|
152
- value = send(property.name)
153
- node = root.add_element(property.name.to_s)
154
- unless property.type == String
155
- node.attributes["type"] = property.type.to_s.downcase
156
- end
157
- node << REXML::Text.new(value.to_s) unless value.nil?
158
- end
159
-
160
- # add methods
161
- (opts[:methods] || []).each do |meth|
162
- if self.respond_to?(meth)
163
- xml_name = meth.to_s.gsub(/[^a-z0-9_]/, '')
164
- node = root.add_element(xml_name)
165
- value = send(meth)
166
- node << REXML::Text.new(value.to_s, :raw => true) unless value.nil?
167
- end
168
- end
169
- doc
170
- end
171
-
172
- end # module Serialize
173
-
174
- module Resource
175
- include Serialize
176
- end # module Resource
177
-
178
- # the json gem adds Object#to_json, which breaks the DM proxies, since it
179
- # happens *after* the proxy has been blank slated. This code removes the added
180
- # method, so it is delegated correctly to the Collection
181
- [
182
- Associations::OneToMany::Proxy,
183
- (Associations::ManyToOne::Proxy if defined?(Associations::ManyToOne::Proxy)),
184
- Associations::ManyToMany::Proxy
185
- ].each do |proxy|
186
- [:to_json].each do |method|
187
- proxy.send(:undef_method, :to_json) rescue nil
188
- end
189
- end
190
-
191
- class Collection
192
- def to_yaml(opts = {})
193
- # FIXME: Don't double handle the YAML (remove the YAML.load)
194
- to_a.collect {|x| YAML.load(x.to_yaml(opts)) }.to_yaml
195
- end
196
-
197
- def to_json(*args)
198
- opts = args.first || {}
199
- "[" << map {|e| e.to_json(opts)}.join(",") << "]"
200
- end
201
-
202
- def to_xml(opts = {})
203
- to_xml_document(opts).to_s
204
- end
205
-
206
- def to_csv
207
- result = ""
208
- each do |item|
209
- result << item.to_csv + "\n"
210
- end
211
- result
212
- end
213
-
214
- protected
215
- def xml_element_name
216
- Extlib::Inflection.pluralize(Extlib::Inflection.underscore(self.model.to_s)).tr("/", "-")
217
- end
218
-
219
- def to_xml_document(opts={})
220
- doc = REXML::Document.new
221
- root = doc.add_element(xml_element_name)
222
- root.attributes["type"] = 'array'
223
- each do |item|
224
- item.send(:to_xml_document, opts, root)
225
- end
226
- doc
227
- end
228
- end
229
-
230
- end # module DataMapper
1
+ require 'dm-serializer/to_json'
2
+ require 'dm-serializer/to_xml'
3
+ require 'dm-serializer/to_yaml'
4
+ require 'dm-serializer/to_csv'
data/spec/fixtures/cow.rb CHANGED
@@ -8,8 +8,4 @@ class Cow
8
8
 
9
9
  has n, :baby_cows, :class_name => 'Cow'
10
10
  belongs_to :mother_cow, :class_name => 'Cow'
11
-
12
- def serialize_properties
13
- {:extra => "Extra", :another => 42}
14
- end
15
11
  end
@@ -1,6 +1,47 @@
1
1
  require 'pathname'
2
2
  require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
+ share_examples_for 'A serialization method that also serializes core classes' do
5
+ # This spec ensures that we don't break any serialization methods attached
6
+ # to core classes, such as Array
7
+ before(:all) do
8
+ %w[ @harness ].each do |ivar|
9
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
10
+ end
11
+
12
+ DataMapper.auto_migrate!
13
+ end
14
+
15
+ before(:each) do
16
+ Cow.all.destroy!
17
+ Planet.all.destroy!
18
+ FriendedPlanet.all.destroy!
19
+ end
20
+
21
+ it 'serializes an array of extended objects' do
22
+ Cow.create(
23
+ :id => 89,
24
+ :composite => 34,
25
+ :name => 'Berta',
26
+ :breed => 'Guernsey'
27
+ )
28
+ result = @harness.test(Cow.all.to_a)
29
+ result[0].values_at("id", "composite", "name", "breed").should ==
30
+ [89, 34, "Berta", "Guernsey"]
31
+ end
32
+
33
+ it 'serializes an array of collections' do
34
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
35
+ collection = DataMapper::Collection.new(query) do |c|
36
+ c.load([1, 2, 'Betsy', 'Jersey'])
37
+ c.load([89, 34, 'Berta', 'Guernsey'])
38
+ end
39
+ result = @harness.test([collection])
40
+ result[0][1].values_at("id", "composite", "name", "breed").should ==
41
+ [89, 34, "Berta", "Guernsey"]
42
+ end
43
+ end
44
+
4
45
  share_examples_for 'A serialization method' do
5
46
  before(:all) do
6
47
  %w[ @harness ].each do |ivar|
@@ -29,45 +29,7 @@ describe DataMapper::Serialize, '#to_json' do
29
29
  end
30
30
 
31
31
  it_should_behave_like "A serialization method"
32
-
33
- it "should serialize an array of collections" do
34
- deserialized_collection = JSON.parse([@collection].to_json).first
35
- betsy = deserialized_collection.first
36
- berta = deserialized_collection.last
37
-
38
- betsy["id"].should == 1
39
- betsy["composite"].should == 2
40
- betsy["name"].should == "Betsy"
41
- betsy["breed"].should == "Jersey"
42
-
43
- berta["id"].should == 10
44
- berta["composite"].should == 20
45
- berta["name"].should == "Berta"
46
- berta["breed"].should == "Guernsey"
47
- end
48
-
49
- it "should serialize an array of extended objects" do
50
- deserialized_collection = JSON.parse(@collection.to_a.to_json)
51
- betsy = deserialized_collection.first
52
- berta = deserialized_collection.last
53
-
54
- betsy["id"].should == 1
55
- betsy["composite"].should == 2
56
- betsy["name"].should == "Betsy"
57
- betsy["breed"].should == "Jersey"
58
-
59
- berta["id"].should == 10
60
- berta["composite"].should == 20
61
- berta["name"].should == "Berta"
62
- berta["breed"].should == "Guernsey"
63
- end
64
-
65
- it "handles extra properties" do
66
- deserialized_hash = JSON.parse(Cow.new(:id => 1, :name => "Harry", :breed => "Angus").to_json)
67
-
68
- deserialized_hash["extra"].should == "Extra"
69
- deserialized_hash["another"].should == 42
70
- end
32
+ it_should_behave_like 'A serialization method that also serializes core classes'
71
33
 
72
34
  it "handles options given to a collection properly" do
73
35
  deserialized_collection = JSON.parse(@collection.to_json(:only => [:composite]))
@@ -1,89 +1,102 @@
1
1
  require 'pathname'
2
2
  require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
- describe DataMapper::Serialize, '#to_xml' do
5
- #
6
- # ==== enterprisey XML
7
- #
4
+ {
5
+ 'REXML' => nil,
6
+ 'LibXML' => 'libxml',
7
+ 'Nokogiri' => 'nokogiri'
8
+ }.each do |lib, file_to_require|
9
+ begin
10
+ require file_to_require if file_to_require
11
+ rescue LoadError
12
+ warn "[WARNING] Cannot require '#{file_to_require}', not running #to_xml specs for #{lib}"
13
+ next
14
+ end
8
15
 
9
- before(:all) do
10
- @harness = Class.new(SerializerTestHarness) do
11
- def method_name
12
- :to_xml
13
- end
16
+ describe DataMapper::Serialize, "#to_xml using #{lib}" do
17
+ #
18
+ # ==== enterprisey XML
19
+ #
20
+
21
+ before(:all) do
22
+ @harness = Class.new(SerializerTestHarness) do
23
+ def method_name
24
+ :to_xml
25
+ end
14
26
 
15
- protected
27
+ protected
16
28
 
17
- def deserialize(result)
18
- doc = REXML::Document.new(result)
19
- root = doc.elements[1]
20
- if root.attributes["type"] == "array"
21
- root.elements.collect do |element|
29
+ def deserialize(result)
30
+ doc = REXML::Document.new(result)
31
+ root = doc.elements[1]
32
+ if root.attributes["type"] == "array"
33
+ root.elements.collect do |element|
34
+ a = {}
35
+ element.elements.each do |v|
36
+ a.update(v.name => cast(v.text, v.attributes["type"]))
37
+ end
38
+ a
39
+ end
40
+ else
22
41
  a = {}
23
- element.elements.each do |v|
42
+ root.elements.each do |v|
24
43
  a.update(v.name => cast(v.text, v.attributes["type"]))
25
44
  end
26
45
  a
27
46
  end
28
- else
29
- a = {}
30
- root.elements.each do |v|
31
- a.update(v.name => cast(v.text, v.attributes["type"]))
32
- end
33
- a
34
47
  end
35
- end
36
-
37
- def cast(value, type)
38
- boolean_conversions = {"true" => true, "false" => false}
39
- value = boolean_conversions[value] if boolean_conversions.has_key?(value)
40
- value = value.to_i if value && type == "integer"
41
- value
42
- end
43
- end.new
44
- end
45
-
46
- it_should_behave_like "A serialization method"
47
48
 
48
- describe 'Resource#xml_element_name' do
49
- it 'should return the class name underscored and with slashes replaced with dashes' do
50
- QuanTum::Cat.new.send(:xml_element_name).should == 'quan_tum-cat'
49
+ def cast(value, type)
50
+ boolean_conversions = {"true" => true, "false" => false}
51
+ value = boolean_conversions[value] if boolean_conversions.has_key?(value)
52
+ value = value.to_i if value && type == "integer"
53
+ value
54
+ end
55
+ end.new
56
+ DataMapper::Serialize::XMLSerializers.instance_eval { remove_const('SERIALIZER') }
57
+ DataMapper::Serialize::XMLSerializers::SERIALIZER = DataMapper::Serialize::XMLSerializers::const_get(lib)
51
58
  end
52
59
 
53
- it 'should be used as the root node name by #to_xml' do
54
- planet = Planet.new
55
- class << planet
56
- def xml_element_name
57
- "aplanet"
58
- end
60
+ it_should_behave_like "A serialization method"
61
+
62
+ describe ':element_name option for Resource' do
63
+ it 'should be used as the root node name by #to_xml' do
64
+ planet = Planet.new
65
+ xml = planet.to_xml(:element_name => "aplanet")
66
+ REXML::Document.new(xml).elements[1].name.should == "aplanet"
59
67
  end
60
68
 
61
- xml = planet.to_xml
62
- REXML::Document.new(xml).elements[1].name.should == "aplanet"
69
+ it 'when not specified the class name underscored and with slashes replaced with dashes should be used as the root node name' do
70
+ cat = QuanTum::Cat.new
71
+ xml = cat.to_xml
72
+ REXML::Document.new(xml).elements[1].name.should == "quan_tum-cat"
73
+ end
63
74
  end
64
- end
65
75
 
66
- describe 'Collection#xml_element_name' do
67
- before(:each) do
68
- query = DataMapper::Query.new(DataMapper::repository(:default), QuanTum::Cat)
69
- @collection = DataMapper::Collection.new(query) {}
70
- end
76
+ describe ':collection_element_name for Collection' do
77
+ before(:each) do
78
+ query = DataMapper::Query.new(DataMapper::repository(:default), QuanTum::Cat)
79
+ @collection = DataMapper::Collection.new(query) {}
80
+ end
71
81
 
72
- it 'should return the class name tableized and with slashes replaced with dashes' do
73
- @collection.send(:xml_element_name).should == 'quan_tum-cats'
74
- end
82
+ it 'when not specified the class name tableized and with slashes replaced with dashes should be used as the root node name' do
83
+ xml = @collection.to_xml
84
+ REXML::Document.new(xml).elements[1].name.should == "quan_tum-cats"
85
+ end
75
86
 
76
- it 'should be used as the root node name by #to_xml' do
77
- planet = Planet.new
78
- @collection.load([1])
79
- class << @collection
80
- def xml_element_name
81
- "somanycats"
82
- end
87
+ it 'should be used as the root node name by #to_xml' do
88
+ @collection.load([1])
89
+
90
+ xml = @collection.to_xml(:collection_element_name => "somanycats")
91
+ REXML::Document.new(xml).elements[1].name.should == "somanycats"
83
92
  end
84
93
 
85
- xml = @collection.to_xml
86
- REXML::Document.new(xml).elements[1].name.should == "somanycats"
94
+ it 'should respect :element_name for collection elements' do
95
+ @collection.load([1])
96
+
97
+ xml = @collection.to_xml(:collection_element_name => "somanycats", :element_name => 'cat')
98
+ REXML::Document.new(xml).elements[1].elements[1].name.should == "cat"
99
+ end
87
100
  end
88
101
  end
89
102
  end
@@ -18,15 +18,20 @@ describe DataMapper::Serialize, '#to_yaml' do
18
18
  def deserialize(result)
19
19
  stringify_keys = lambda {|hash| hash.inject({}) {|a, (key, value)| a.update(key.to_s => value) }}
20
20
  result = YAML.load(result)
21
- if result.is_a?(Array)
22
- result.collect(&stringify_keys)
23
- else
24
- stringify_keys[result]
25
- end
21
+ (process = lambda {|object|
22
+ if object.is_a?(Array)
23
+ object.collect(&process)
24
+ elsif object.is_a?(Hash)
25
+ stringify_keys[object]
26
+ else
27
+ object
28
+ end
29
+ })[result]
26
30
  end
27
31
  end.new
28
32
  end
29
33
 
30
- it_should_behave_like "A serialization method"
34
+ it_should_behave_like 'A serialization method'
35
+ it_should_behave_like 'A serialization method that also serializes core classes'
31
36
 
32
37
  end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  require 'pathname'
2
2
  require 'rubygems'
3
3
 
4
- gem 'dm-core', '~>0.9.8'
4
+ gem 'dm-core', '~>0.9.9'
5
5
  require 'dm-core'
6
6
 
7
7
  spec_dir_path = Pathname(__FILE__).dirname.expand_path
8
- require spec_dir_path.parent + 'lib/dm-serializer'
8
+ $LOAD_PATH.unshift(spec_dir_path.parent + 'lib/')
9
+ require 'dm-serializer'
9
10
 
10
11
  def load_driver(name, default_uri)
11
12
  return false if ENV['ADAPTER'] != name.to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dm-serializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 0.9.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guy van den Berg
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-07 00:00:00 -08:00
12
+ date: 2009-01-04 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: 0.9.8
23
+ version: 0.9.9
24
24
  version:
25
25
  description: DataMapper plugin for serializing DataMapper objects
26
26
  email:
@@ -30,7 +30,7 @@ executables: []
30
30
  extensions: []
31
31
 
32
32
  extra_rdoc_files:
33
- - README.txt
33
+ - README.textile
34
34
  - LICENSE
35
35
  - TODO
36
36
  - History.txt
@@ -38,13 +38,23 @@ files:
38
38
  - History.txt
39
39
  - LICENSE
40
40
  - Manifest.txt
41
- - README.txt
41
+ - README.textile
42
42
  - Rakefile
43
43
  - TODO
44
44
  - autotest/discover.rb
45
45
  - autotest/dmserializer_rspec.rb
46
+ - benchmarks/to_xml.rb
46
47
  - lib/dm-serializer.rb
48
+ - lib/dm-serializer/common.rb
49
+ - lib/dm-serializer/to_csv.rb
50
+ - lib/dm-serializer/to_json.rb
51
+ - lib/dm-serializer/to_xml.rb
52
+ - lib/dm-serializer/to_yaml.rb
47
53
  - lib/dm-serializer/version.rb
54
+ - lib/dm-serializer/xml_serializers.rb
55
+ - lib/dm-serializer/xml_serializers/libxml.rb
56
+ - lib/dm-serializer/xml_serializers/nokogiri.rb
57
+ - lib/dm-serializer/xml_serializers/rexml.rb
48
58
  - spec/fixtures/cow.rb
49
59
  - spec/fixtures/planet.rb
50
60
  - spec/fixtures/quan_tum_cat.rb
data/README.txt DELETED
@@ -1,3 +0,0 @@
1
- = dm-serializer
2
-
3
- DataMapper plugin for serializing DataMapper objects.