dm-serializer 0.9.11 → 0.10.0
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 → History.rdoc} +8 -1
- data/Manifest.txt +3 -2
- data/README.rdoc +64 -0
- data/Rakefile +2 -3
- data/benchmarks/to_json.rb +137 -0
- data/benchmarks/to_xml.rb +3 -3
- data/lib/dm-serializer/common.rb +8 -4
- data/lib/dm-serializer/to_csv.rb +27 -6
- data/lib/dm-serializer/to_json.rb +36 -17
- data/lib/dm-serializer/to_xml.rb +41 -4
- data/lib/dm-serializer/to_yaml.rb +15 -4
- data/lib/dm-serializer/version.rb +1 -1
- data/lib/dm-serializer/xml_serializers/libxml.rb +5 -0
- data/lib/dm-serializer/xml_serializers/nokogiri.rb +4 -0
- data/lib/dm-serializer/xml_serializers/rexml.rb +4 -0
- data/spec/fixtures/cow.rb +2 -2
- data/spec/fixtures/planet.rb +17 -4
- data/spec/fixtures/quan_tum_cat.rb +2 -2
- data/spec/lib/serialization_method_shared_spec.rb +53 -24
- data/spec/public/serializer_spec.rb +1 -2
- data/spec/public/to_csv_spec.rb +49 -35
- data/spec/public/to_json_spec.rb +9 -6
- data/spec/public/to_xml_spec.rb +31 -23
- data/spec/public/to_yaml_spec.rb +27 -9
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +13 -6
- data/tasks/install.rb +1 -1
- data/tasks/spec.rb +4 -4
- metadata +15 -21
- data/README.textile +0 -61
@@ -1,4 +1,11 @@
|
|
1
|
-
=== 0.
|
1
|
+
=== 0.10.0 / 2009-10-15
|
2
|
+
|
3
|
+
* Support serialization of dm-validation errors collection (#718)
|
4
|
+
* Don't require json gem on ruby 1.9 (#776)
|
5
|
+
* Allow YAML.dump(dm_object) (#792)
|
6
|
+
* Correctly serialize child objects (#829)
|
7
|
+
|
8
|
+
=== 0.9.11
|
2
9
|
|
3
10
|
* No changes this version
|
4
11
|
|
data/Manifest.txt
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
History.
|
1
|
+
History.rdoc
|
2
2
|
LICENSE
|
3
3
|
Manifest.txt
|
4
|
-
README.
|
4
|
+
README.rdoc
|
5
5
|
Rakefile
|
6
6
|
TODO
|
7
7
|
autotest/discover.rb
|
8
8
|
autotest/dmserializer_rspec.rb
|
9
|
+
benchmarks/to_json.rb
|
9
10
|
benchmarks/to_xml.rb
|
10
11
|
lib/dm-serializer.rb
|
11
12
|
lib/dm-serializer/common.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
= dm-serializer
|
2
|
+
|
3
|
+
== 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
|
+
== How it works
|
8
|
+
|
9
|
+
One method is added to each model/collection for each serialization type - <tt>to_json</tt>, <tt>to_xml</tt>, <tt>to_yaml</tt>, and <tt>to_csv</tt>. With the exception of <tt>to_csv</tt>, all of these methods share the same interface. <tt>to_json</tt> will be used for examples. Any method specific behaviour is documented in its own section below.
|
10
|
+
|
11
|
+
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" }
|
33
|
+
|
34
|
+
You can include associations by passing the association accessor the <tt>:methods</tt> option.
|
35
|
+
|
36
|
+
If you want to only load a particular serialization method, that's cool, you can do that:
|
37
|
+
|
38
|
+
require 'dm-serializer/to_json'
|
39
|
+
|
40
|
+
== to_xml
|
41
|
+
|
42
|
+
<tt>to_xml</tt> supports some extra options to allow you to override the element names
|
43
|
+
|
44
|
+
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>
|
46
|
+
|
47
|
+
|
48
|
+
If you would like a nice speed boost (~5x), require <tt>libxml</tt> or <tt>nokogiri</tt> before <tt>dm-serializer</tt>, and that library will be used rather than REXML.
|
49
|
+
|
50
|
+
== to_csv
|
51
|
+
|
52
|
+
<tt>to_csv</tt> currently doesn't support any options yet. It will in the future. It will not support serializing child associations.
|
53
|
+
|
54
|
+
|
55
|
+
== Arrays, Hashes, and other core classes
|
56
|
+
|
57
|
+
<tt>dm-serializer</tt> only adds serialization methods to DataMapper objects and collections, however some libraries used (json, yaml) add methods to core classes, such as <tt>Array</tt>. Note that passing <tt>dm-serializer</tt> options (such as <tt>:only</tt>) to these methods is *not supported*.
|
58
|
+
|
59
|
+
Cow.all.to_a.to_yaml(:only => 'name') # WILL NOT WORK
|
60
|
+
|
61
|
+
|
62
|
+
== Beware
|
63
|
+
|
64
|
+
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
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
require 'rubygems'
|
3
2
|
|
4
3
|
ROOT = Pathname(__FILE__).dirname.expand_path
|
5
4
|
JRUBY = RUBY_PLATFORM =~ /java/
|
@@ -14,10 +13,10 @@ GEM_NAME = 'dm-serializer'
|
|
14
13
|
GEM_VERSION = DataMapper::Serializer::VERSION
|
15
14
|
GEM_DEPENDENCIES = [['dm-core', GEM_VERSION]]
|
16
15
|
GEM_CLEAN = %w[ log pkg coverage ]
|
17
|
-
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.
|
16
|
+
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.rdoc LICENSE TODO History.rdoc ] }
|
18
17
|
|
19
18
|
PROJECT_NAME = 'datamapper'
|
20
|
-
PROJECT_URL = "http://github.com/
|
19
|
+
PROJECT_URL = "http://github.com/datamapper/dm-more/tree/master/#{GEM_NAME}"
|
21
20
|
PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin for serializing DataMapper objects'
|
22
21
|
|
23
22
|
[ ROOT, ROOT.parent ].each do |dir|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require 'pathname'
|
3
|
+
require 'better-benchmark'
|
4
|
+
|
5
|
+
gem 'dm-core', '0.10.0'
|
6
|
+
require 'dm-core'
|
7
|
+
|
8
|
+
spec_dir_path = Pathname(__FILE__).dirname.expand_path
|
9
|
+
$LOAD_PATH.unshift(spec_dir_path.parent + 'lib/')
|
10
|
+
require 'dm-serializer'
|
11
|
+
|
12
|
+
def load_driver(name, default_uri)
|
13
|
+
begin
|
14
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
15
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
16
|
+
DataMapper::Repository.adapters[:alternate] = DataMapper::Repository.adapters[name]
|
17
|
+
true
|
18
|
+
rescue LoadError => e
|
19
|
+
warn "Could not load do_#{name}: #{e}"
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
25
|
+
|
26
|
+
module DataMapper
|
27
|
+
module Serialize
|
28
|
+
# Serialize a Resource to JavaScript Object Notation (JSON; RFC 4627)
|
29
|
+
#
|
30
|
+
# @return <String> a JSON representation of the Resource
|
31
|
+
def to_json_old(*args)
|
32
|
+
options = args.first || {}
|
33
|
+
result = '{ '
|
34
|
+
fields = []
|
35
|
+
|
36
|
+
propset = properties_to_serialize(options)
|
37
|
+
|
38
|
+
fields += propset.map do |property|
|
39
|
+
"#{property.name.to_json}: #{send(property.name).to_json}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# add methods
|
43
|
+
(options[:methods] || []).each do |meth|
|
44
|
+
if self.respond_to?(meth)
|
45
|
+
fields << "#{meth.to_json}: #{send(meth).to_json}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Note: if you want to include a whole other model via relation, use :methods
|
50
|
+
# comments.to_json(:relationships=>{:user=>{:include=>[:first_name],:methods=>[:age]}})
|
51
|
+
# add relationships
|
52
|
+
# TODO: This needs tests and also needs to be ported to #to_xml and #to_yaml
|
53
|
+
(options[:relationships] || {}).each do |rel,opts|
|
54
|
+
if self.respond_to?(rel)
|
55
|
+
fields << "#{rel.to_json}: #{send(rel).to_json(opts)}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
result << fields.join(', ')
|
60
|
+
result << ' }'
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Collection
|
66
|
+
def to_json_old(*args)
|
67
|
+
opts = args.first || {}
|
68
|
+
'[' << map { |e| e.to_json_old(opts) }.join(',') << ']'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
class Cow
|
77
|
+
include DataMapper::Resource
|
78
|
+
|
79
|
+
property :id, Integer, :key => true
|
80
|
+
property :composite, Integer, :key => true
|
81
|
+
property :name, String
|
82
|
+
property :breed, String
|
83
|
+
|
84
|
+
has n, :baby_cows, :model => 'Cow'
|
85
|
+
belongs_to :mother_cow, :model => 'Cow', :nullable => true
|
86
|
+
end
|
87
|
+
|
88
|
+
DataMapper.auto_migrate!
|
89
|
+
cow = Cow.create(
|
90
|
+
:id => 89,
|
91
|
+
:composite => 34,
|
92
|
+
:name => 'Berta',
|
93
|
+
:breed => 'Guernsey'
|
94
|
+
)
|
95
|
+
|
96
|
+
10.times do |n|
|
97
|
+
Cow.create(
|
98
|
+
:id => n*10,
|
99
|
+
:composite => n,
|
100
|
+
:name => "Bertha"*n,
|
101
|
+
:breed => "Mooing#{n}"
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
all_cows = Cow.all.reload
|
106
|
+
|
107
|
+
puts "Benchmarking single resource serialization."
|
108
|
+
puts "Set 1: old method"
|
109
|
+
puts "Set 2: new method"
|
110
|
+
result = Benchmark.compare_realtime(
|
111
|
+
:iterations => 10,
|
112
|
+
:inner_iterations => 20000,
|
113
|
+
:verbose => true
|
114
|
+
) { |iteration|
|
115
|
+
cow.to_json_old
|
116
|
+
}.with { |iteration|
|
117
|
+
cow.to_json
|
118
|
+
}
|
119
|
+
|
120
|
+
Benchmark.report_on result
|
121
|
+
|
122
|
+
puts
|
123
|
+
|
124
|
+
puts "Benchmarking collection serialization."
|
125
|
+
puts "Set 1: old method"
|
126
|
+
puts "Set 2: new method"
|
127
|
+
result = Benchmark.compare_realtime(
|
128
|
+
:iterations => 10,
|
129
|
+
:inner_iterations => 5000,
|
130
|
+
:verbose => true
|
131
|
+
) { |iteration|
|
132
|
+
all_cows.to_json_old
|
133
|
+
}.with { |iteration|
|
134
|
+
all_cows.to_json
|
135
|
+
}
|
136
|
+
|
137
|
+
Benchmark.report_on result
|
data/benchmarks/to_xml.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require 'pathname'
|
3
3
|
|
4
|
-
gem 'dm-core', '0.
|
4
|
+
gem 'dm-core', '0.10.0'
|
5
5
|
require 'dm-core'
|
6
6
|
|
7
7
|
spec_dir_path = Pathname(__FILE__).dirname.expand_path
|
@@ -30,8 +30,8 @@ class Cow
|
|
30
30
|
property :name, String
|
31
31
|
property :breed, String
|
32
32
|
|
33
|
-
has n, :baby_cows, :
|
34
|
-
belongs_to :mother_cow, :
|
33
|
+
has n, :baby_cows, :model => 'Cow'
|
34
|
+
belongs_to :mother_cow, :model => 'Cow'
|
35
35
|
end
|
36
36
|
|
37
37
|
require "benchwarmer"
|
data/lib/dm-serializer/common.rb
CHANGED
@@ -8,7 +8,7 @@ module DataMapper
|
|
8
8
|
only_properties = Array(options[:only])
|
9
9
|
excluded_properties = Array(options[:exclude])
|
10
10
|
|
11
|
-
|
11
|
+
model.properties(repository.name).reject do |p|
|
12
12
|
if only_properties.include? p.name
|
13
13
|
false
|
14
14
|
else
|
@@ -16,9 +16,13 @@ module DataMapper
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
|
-
end
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
Model.append_inclusions self
|
21
|
+
|
22
|
+
class Support
|
23
|
+
def self.dm_validations_loaded?
|
24
|
+
DataMapper.const_defined?("Validate")
|
25
|
+
end
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
data/lib/dm-serializer/to_csv.rb
CHANGED
@@ -4,11 +4,11 @@ if RUBY_VERSION >= '1.9.0'
|
|
4
4
|
require 'csv'
|
5
5
|
else
|
6
6
|
begin
|
7
|
-
gem 'fastercsv', '~>1.
|
7
|
+
gem 'fastercsv', '~>1.5.0'
|
8
8
|
require 'fastercsv'
|
9
9
|
CSV = FasterCSV
|
10
10
|
rescue LoadError
|
11
|
-
|
11
|
+
# do nothing
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -19,9 +19,8 @@ module DataMapper
|
|
19
19
|
# @return <String> a CSV representation of the Resource
|
20
20
|
def to_csv(writer = '')
|
21
21
|
CSV.generate(writer) do |csv|
|
22
|
-
row =
|
23
|
-
|
24
|
-
row << send(property.name).to_s
|
22
|
+
row = model.properties(repository.name).map do |property|
|
23
|
+
send(property.name).to_s
|
25
24
|
end
|
26
25
|
csv << row
|
27
26
|
end
|
@@ -30,11 +29,33 @@ module DataMapper
|
|
30
29
|
|
31
30
|
class Collection
|
32
31
|
def to_csv
|
33
|
-
result =
|
32
|
+
result = ''
|
34
33
|
each do |item|
|
35
34
|
result << item.to_csv + "\n"
|
36
35
|
end
|
37
36
|
result
|
38
37
|
end
|
39
38
|
end
|
39
|
+
|
40
|
+
if Serialize::Support.dm_validations_loaded?
|
41
|
+
|
42
|
+
module Validate
|
43
|
+
class ValidationErrors
|
44
|
+
def to_csv(writer = '')
|
45
|
+
CSV.generate(writer) do |csv|
|
46
|
+
errors.each do |key, value|
|
47
|
+
value.each do |error|
|
48
|
+
row = []
|
49
|
+
row << key.to_s
|
50
|
+
row << error.to_s
|
51
|
+
csv << row
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
40
61
|
end
|
@@ -1,12 +1,6 @@
|
|
1
1
|
require 'dm-serializer/common'
|
2
2
|
|
3
|
-
|
4
|
-
gem('json')
|
5
|
-
require 'json/ext'
|
6
|
-
rescue LoadError
|
7
|
-
gem('json_pure')
|
8
|
-
require 'json/pure'
|
9
|
-
end
|
3
|
+
require 'json'
|
10
4
|
|
11
5
|
module DataMapper
|
12
6
|
module Serialize
|
@@ -15,19 +9,19 @@ module DataMapper
|
|
15
9
|
# @return <String> a JSON representation of the Resource
|
16
10
|
def to_json(*args)
|
17
11
|
options = args.first || {}
|
18
|
-
|
19
|
-
|
12
|
+
|
13
|
+
result = {}
|
20
14
|
|
21
15
|
propset = properties_to_serialize(options)
|
22
16
|
|
23
|
-
|
24
|
-
|
17
|
+
propset.each do |property|
|
18
|
+
result[property.name] = send(property.name)
|
25
19
|
end
|
26
20
|
|
27
21
|
# add methods
|
28
22
|
(options[:methods] || []).each do |meth|
|
29
23
|
if self.respond_to?(meth)
|
30
|
-
|
24
|
+
result[meth] = send(meth)
|
31
25
|
end
|
32
26
|
end
|
33
27
|
|
@@ -37,16 +31,20 @@ module DataMapper
|
|
37
31
|
# TODO: This needs tests and also needs to be ported to #to_xml and #to_yaml
|
38
32
|
(options[:relationships] || {}).each do |rel,opts|
|
39
33
|
if self.respond_to?(rel)
|
40
|
-
|
34
|
+
result[rel] = send(rel).to_json(opts.merge(:to_json => false))
|
41
35
|
end
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
# default to making JSON
|
39
|
+
if options.fetch(:to_json, true)
|
40
|
+
result.to_json
|
41
|
+
else
|
42
|
+
result
|
43
|
+
end
|
47
44
|
end
|
48
45
|
end
|
49
46
|
|
47
|
+
|
50
48
|
module Associations
|
51
49
|
# the json gem adds Object#to_json, which breaks the DM proxies, since it
|
52
50
|
# happens *after* the proxy has been blank slated. This code removes the added
|
@@ -67,7 +65,28 @@ module DataMapper
|
|
67
65
|
class Collection
|
68
66
|
def to_json(*args)
|
69
67
|
opts = args.first || {}
|
70
|
-
|
68
|
+
|
69
|
+
options = opts.merge(:to_json => false)
|
70
|
+
collection = map { |e| e.to_json(options) }
|
71
|
+
|
72
|
+
# default to making JSON
|
73
|
+
if opts.fetch(:to_json, true)
|
74
|
+
collection.to_json
|
75
|
+
else
|
76
|
+
collection
|
77
|
+
end
|
71
78
|
end
|
72
79
|
end
|
80
|
+
|
81
|
+
if Serialize::Support.dm_validations_loaded?
|
82
|
+
|
83
|
+
module Validate
|
84
|
+
class ValidationErrors
|
85
|
+
def to_json(*args)
|
86
|
+
errors.to_hash.to_json
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
73
92
|
end
|