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.
@@ -1,4 +1,11 @@
1
- === 0.9.11 / 2009-03-29
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
 
@@ -1,11 +1,12 @@
1
- History.txt
1
+ History.rdoc
2
2
  LICENSE
3
3
  Manifest.txt
4
- README.textile
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
@@ -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.textile LICENSE TODO History.txt ] }
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/sam/dm-more/tree/master/#{GEM_NAME}"
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
@@ -1,7 +1,7 @@
1
1
  require "rubygems"
2
2
  require 'pathname'
3
3
 
4
- gem 'dm-core', '0.9.11'
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, :class_name => 'Cow'
34
- belongs_to :mother_cow, :class_name => 'Cow'
33
+ has n, :baby_cows, :model => 'Cow'
34
+ belongs_to :mother_cow, :model => 'Cow'
35
35
  end
36
36
 
37
37
  require "benchwarmer"
@@ -8,7 +8,7 @@ module DataMapper
8
8
  only_properties = Array(options[:only])
9
9
  excluded_properties = Array(options[:exclude])
10
10
 
11
- self.class.properties(repository.name).reject do |p|
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
- module Resource
22
- include Serialize
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
@@ -4,11 +4,11 @@ if RUBY_VERSION >= '1.9.0'
4
4
  require 'csv'
5
5
  else
6
6
  begin
7
- gem 'fastercsv', '~>1.4.0'
7
+ gem 'fastercsv', '~>1.5.0'
8
8
  require 'fastercsv'
9
9
  CSV = FasterCSV
10
10
  rescue LoadError
11
- nil
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
- self.class.properties(repository.name).each do |property|
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
- begin
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
- result = '{ '
19
- fields = []
12
+
13
+ result = {}
20
14
 
21
15
  propset = properties_to_serialize(options)
22
16
 
23
- fields += propset.map do |property|
24
- "#{property.name.to_json}: #{send(property.getter).to_json}"
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
- fields << "#{meth.to_json}: #{send(meth).to_json}"
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
- fields << "#{rel.to_json}: #{send(rel).to_json(opts)}"
34
+ result[rel] = send(rel).to_json(opts.merge(:to_json => false))
41
35
  end
42
36
  end
43
37
 
44
- result << fields.join(', ')
45
- result << ' }'
46
- result
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
- "[" << map {|e| e.to_json(opts)}.join(",") << "]"
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