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