dm-serializer 0.9.8 → 0.9.9

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.
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.