dm-serializer 0.9.11 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|