dm-serializer 0.9.7 → 0.9.8

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 +1,10 @@
1
+ === 0.9.8 / 2008-12-07
1
2
 
3
+ * 2 minor enhancements:
4
+
5
+ * to_yaml and to_xml now support :only and :exclude options
6
+ * to_yaml and to_xml now support serializing methods
7
+
8
+ * 1 bug fix:
9
+
10
+ * gem requires now preceded by gem() call
@@ -10,11 +10,14 @@ lib/dm-serializer.rb
10
10
  lib/dm-serializer/version.rb
11
11
  spec/fixtures/cow.rb
12
12
  spec/fixtures/planet.rb
13
- spec/fixtures/quatum_cat.rb
13
+ spec/fixtures/quan_tum_cat.rb
14
+ spec/lib/serialization_method_shared_spec.rb
15
+ spec/public/serializer_spec.rb
16
+ spec/public/to_csv_spec.rb
17
+ spec/public/to_json_spec.rb
18
+ spec/public/to_xml_spec.rb
19
+ spec/public/to_yaml_spec.rb
14
20
  spec/spec.opts
15
21
  spec/spec_helper.rb
16
- spec/unit/serializer_spec.rb
17
- spec/unit/to_csv_spec.rb
18
- spec/unit/to_json_spec.rb
19
- spec/unit/to_xml_spec.rb
20
- spec/unit/to_yaml_spec.rb
22
+ tasks/install.rb
23
+ tasks/spec.rb
data/Rakefile CHANGED
@@ -1,58 +1,25 @@
1
- require 'rubygems'
2
- require 'spec'
3
- require 'spec/rake/spectask'
4
1
  require 'pathname'
2
+ require 'rubygems'
3
+
4
+ ROOT = Pathname(__FILE__).dirname.expand_path
5
+ JRUBY = RUBY_PLATFORM =~ /java/
6
+ WINDOWS = Gem.win_platform?
7
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
5
8
 
6
- ROOT = Pathname(__FILE__).dirname.expand_path
7
9
  require ROOT + 'lib/dm-serializer/version'
8
10
 
9
- AUTHOR = "Guy van den Berg"
10
- EMAIL = "vandenberg.guy@gmail.com"
11
- GEM_NAME = "dm-serializer"
11
+ AUTHOR = 'Guy van den Berg'
12
+ EMAIL = 'vandenberg.guy [a] gmail [d] com'
13
+ GEM_NAME = 'dm-serializer'
12
14
  GEM_VERSION = DataMapper::Serializer::VERSION
13
- GEM_DEPENDENCIES = [["dm-core", GEM_VERSION]]
14
- GEM_CLEAN = ["log", "pkg", "coverage"]
15
- GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO ] }
16
-
17
- PROJECT_NAME = "datamapper"
18
- PROJECT_URL = "http://github.com/sam/dm-more/tree/master/dm-serializer"
19
- PROJECT_DESCRIPTION = PROJECT_SUMMARY = "DataMapper plugin for serializing DataMapper objects"
20
-
21
- require ROOT.parent + 'tasks/hoe'
22
-
23
- task :default => [ :spec ]
24
-
25
- WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
26
- SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
27
-
28
- desc "Install #{GEM_NAME} #{GEM_VERSION} (default ruby)"
29
- task :install => [ :package ] do
30
- sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
31
- end
32
-
33
- desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
34
- task :uninstall => [ :clobber ] do
35
- sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
36
- end
37
-
38
- namespace :jruby do
39
- desc "Install #{GEM_NAME} #{GEM_VERSION} with JRuby"
40
- task :install => [ :package ] do
41
- sh %{#{SUDO} jruby -S gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
42
- end
43
- end
15
+ GEM_DEPENDENCIES = [['dm-core', "~>#{GEM_VERSION}"]]
16
+ GEM_CLEAN = %w[ log pkg coverage ]
17
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] }
44
18
 
45
- desc 'Run specifications'
46
- Spec::Rake::SpecTask.new(:spec) do |t|
47
- t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
48
- t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
19
+ PROJECT_NAME = 'datamapper'
20
+ PROJECT_URL = "http://github.com/sam/dm-more/tree/master/#{GEM_NAME}"
21
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin for serializing DataMapper objects'
49
22
 
50
- begin
51
- t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
52
- t.rcov_opts << '--exclude' << 'spec'
53
- t.rcov_opts << '--text-summary'
54
- t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
55
- rescue Exception
56
- # rcov not installed
57
- end
23
+ [ ROOT, ROOT.parent ].each do |dir|
24
+ Pathname.glob(dir.join('tasks/**/*.rb').to_s).each { |f| require f }
58
25
  end
data/TODO CHANGED
@@ -1,8 +0,0 @@
1
- TODO
2
- ====
3
-
4
-
5
-
6
- ---
7
- TODO tickets may also be found in the DataMapper Issue Tracker:
8
- http://wm.lighthouseapp.com/projects/4819-datamapper/overview
@@ -1,36 +1,31 @@
1
- require Pathname('rexml/document')
1
+ require 'rexml/document'
2
2
 
3
3
  begin
4
+ gem('fastercsv')
4
5
  require 'faster_csv'
5
6
  rescue LoadError
6
7
  nil
7
8
  end
8
9
 
9
10
  begin
10
- require Pathname('json/ext')
11
+ gem('json')
12
+ require 'json/ext'
11
13
  rescue LoadError
12
- require Pathname('json/pure')
14
+ gem('json_pure')
15
+ require 'json/pure'
13
16
  end
14
17
 
15
18
  module DataMapper
16
19
  module Serialize
17
-
18
20
  # Serialize a Resource to JavaScript Object Notation (JSON; RFC 4627)
19
21
  #
20
22
  # @return <String> a JSON representation of the Resource
21
- def to_json(options = {})
23
+ def to_json(*args)
24
+ options = args.first || {}
22
25
  result = '{ '
23
26
  fields = []
24
27
 
25
- # FIXME: this should go into bunch of protected methods shared with other serialization methods
26
- only_properties = Array(options[:only])
27
- excluded_properties = Array(options[:exclude])
28
- exclude_read_only = options[:without_read_only_attributes] || false
29
-
30
- propset = self.class.properties(repository.name).reject do |p|
31
- next if only_properties.include? p.name
32
- excluded_properties.include?(p.name) || !(only_properties.empty? || only_properties.include?(p.name))
33
- end
28
+ propset = properties_to_serialize(options)
34
29
 
35
30
  fields += propset.map do |property|
36
31
  "#{property.name.to_json}: #{send(property.getter).to_json}"
@@ -96,10 +91,17 @@ module DataMapper
96
91
  def to_yaml(opts = {})
97
92
  YAML::quick_emit(object_id,opts) do |out|
98
93
  out.map(nil,to_yaml_style) do |map|
99
- self.class.properties(repository.name).each do |property|
94
+ propset = properties_to_serialize(opts)
95
+ propset.each do |property|
100
96
  value = send(property.name.to_sym)
101
97
  map.add(property.name, value.is_a?(Class) ? value.to_s : value)
102
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
103
105
  (instance_variable_get("@yaml_addes") || []).each do |k,v|
104
106
  map.add(k.to_s,v)
105
107
  end
@@ -109,12 +111,31 @@ module DataMapper
109
111
 
110
112
  protected
111
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
+
112
133
  # Return the name of this Resource - to be used as the root element name.
113
134
  # This can be overloaded.
114
135
  #
115
136
  # @return <String> name of this Resource
116
137
  def xml_element_name
117
- Extlib::Inflection.underscore(self.class.name)
138
+ Extlib::Inflection.underscore(self.class.name).tr("/", "-")
118
139
  end
119
140
 
120
141
  # Return a REXML::Document representing this Resource
@@ -126,7 +147,8 @@ module DataMapper
126
147
 
127
148
  #TODO old code base was converting single quote to double quote on attribs
128
149
 
129
- self.class.properties(repository.name).each do |property|
150
+ propset = properties_to_serialize(opts)
151
+ propset.each do |property|
130
152
  value = send(property.name)
131
153
  node = root.add_element(property.name.to_s)
132
154
  unless property.type == String
@@ -134,6 +156,16 @@ module DataMapper
134
156
  end
135
157
  node << REXML::Text.new(value.to_s) unless value.nil?
136
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
137
169
  doc
138
170
  end
139
171
 
@@ -143,12 +175,27 @@ module DataMapper
143
175
  include Serialize
144
176
  end # module Resource
145
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
+
146
191
  class Collection
147
192
  def to_yaml(opts = {})
148
- to_a.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
149
195
  end
150
196
 
151
- def to_json(opts = {})
197
+ def to_json(*args)
198
+ opts = args.first || {}
152
199
  "[" << map {|e| e.to_json(opts)}.join(",") << "]"
153
200
  end
154
201
 
@@ -166,7 +213,7 @@ module DataMapper
166
213
 
167
214
  protected
168
215
  def xml_element_name
169
- Extlib::Inflection.tableize(self.model.to_s)
216
+ Extlib::Inflection.pluralize(Extlib::Inflection.underscore(self.model.to_s)).tr("/", "-")
170
217
  end
171
218
 
172
219
  def to_xml_document(opts={})
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  module Serializer
3
- VERSION = "0.9.7"
3
+ VERSION = '0.9.8'
4
4
  end
5
5
  end
@@ -6,6 +6,9 @@ class Cow
6
6
  property :name, String
7
7
  property :breed, String
8
8
 
9
+ has n, :baby_cows, :class_name => 'Cow'
10
+ belongs_to :mother_cow, :class_name => 'Cow'
11
+
9
12
  def serialize_properties
10
13
  {:extra => "Extra", :another => 42}
11
14
  end
@@ -4,6 +4,11 @@ class Planet
4
4
  property :name, String, :key => true
5
5
  property :aphelion, Float
6
6
 
7
+ # Sorry these associations don't make any sense
8
+ # I just needed a many-to-many association to test against
9
+ has n, :friended_planets
10
+ has n, :friend_planets, :through => :friended_planets, :class_name => 'Planet'
11
+
7
12
  def category
8
13
  case self.name.downcase
9
14
  when "mercury", "venus", "earth", "mars" then "terrestrial"
@@ -16,3 +21,13 @@ class Planet
16
21
  self.name.downcase == "earth"
17
22
  end
18
23
  end
24
+
25
+ class FriendedPlanet
26
+ include DataMapper::Resource
27
+
28
+ property :planet_name, String, :key => true
29
+ property :friend_planet_name, String, :key => true
30
+
31
+ belongs_to :planet, :child_key => [:planet_name]
32
+ belongs_to :friend_planet, :class_name => 'Planet', :child_key => [:friend_planet_name]
33
+ end
@@ -0,0 +1,15 @@
1
+ # Yes, this crazy capitalization is intentional,
2
+ # to test xml root element name generation
3
+ module QuanTum
4
+ class Cat
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :name, String
9
+ property :location, String
10
+
11
+ repository(:alternate) do
12
+ property :is_dead, Boolean
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,193 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ share_examples_for 'A serialization method' do
5
+ before(:all) do
6
+ %w[ @harness ].each do |ivar|
7
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
8
+ end
9
+
10
+ DataMapper.auto_migrate!
11
+ end
12
+
13
+ before(:each) do
14
+ Cow.all.destroy!
15
+ Planet.all.destroy!
16
+ FriendedPlanet.all.destroy!
17
+ end
18
+
19
+ describe '(serializing single resources)' do
20
+ it 'should serialize Model.first' do
21
+ # At the moment this is implied by serializing a resource, but this
22
+ # test ensures the contract even if dm-core changes
23
+ Cow.create(
24
+ :id => 89,
25
+ :composite => 34,
26
+ :name => 'Berta',
27
+ :breed => 'Guernsey'
28
+ )
29
+ result = @harness.test(Cow.first)
30
+ result.values_at("name", "breed").should == ["Berta", "Guernsey"]
31
+ end
32
+
33
+ it 'should serialize a resource' do
34
+ cow = Cow.new(
35
+ :id => 89,
36
+ :composite => 34,
37
+ :name => 'Berta',
38
+ :breed => 'Guernsey'
39
+ )
40
+
41
+ result = @harness.test(cow)
42
+ result.values_at("id", "composite", "name", "breed").should == [89, 34, 'Berta', 'Guernsey']
43
+ end
44
+
45
+ it 'should exclude nil properties' do
46
+ cow = Cow.new(
47
+ :id => 89,
48
+ :name => nil
49
+ )
50
+
51
+ result = @harness.test(cow)
52
+ result.values_at("id", "composite").should == [89, nil]
53
+ end
54
+
55
+ it "should only includes properties given to :only option" do
56
+ planet = Planet.new(
57
+ :name => "Mars",
58
+ :aphelion => 249_209_300.4
59
+ )
60
+
61
+ result = @harness.test(planet, :only => [:name])
62
+ result.values_at("name", "aphelion").should == ["Mars", nil]
63
+ end
64
+
65
+ it "should serialize values returned by methods given to :methods option" do
66
+ planet = Planet.new(
67
+ :name => "Mars",
68
+ :aphelion => 249_209_300.4
69
+ )
70
+
71
+ result = @harness.test(planet, :methods => [:category, :has_known_form_of_life?])
72
+ # XML currently can't serialize ? at the end of method names
73
+ boolean_method_name = @harness.method_name == :to_xml ? "has_known_form_of_life" : "has_known_form_of_life?"
74
+ result.values_at("category", boolean_method_name).should == ["terrestrial", false]
75
+ end
76
+
77
+ it "should only include properties given to :only option" do
78
+ planet = Planet.new(
79
+ :name => "Mars",
80
+ :aphelion => 249_209_300.4
81
+ )
82
+
83
+ result = @harness.test(planet, :only => [:name])
84
+ result.values_at("name", "aphelion").should == ["Mars", nil]
85
+ end
86
+
87
+ it "should exclude properties given to :exclude option" do
88
+ planet = Planet.new(
89
+ :name => "Mars",
90
+ :aphelion => 249_209_300.4
91
+ )
92
+
93
+ result = @harness.test(planet, :exclude => [:aphelion])
94
+ result.values_at("name", "aphelion").should == ["Mars", nil]
95
+ end
96
+
97
+ it "should give higher precendence to :only option over :exclude" do
98
+ planet = Planet.new(
99
+ :name => "Mars",
100
+ :aphelion => 249_209_300.4
101
+ )
102
+
103
+ result = @harness.test(planet, :only => [:name], :exclude => [:name])
104
+ result.values_at("name", "aphelion").should == ["Mars", nil]
105
+ end
106
+ end
107
+
108
+ describe "(collections and proxies)" do
109
+ it 'should serialize Model.all' do
110
+ # At the moment this is implied by serializing a collection, but this
111
+ # test ensures the contract even if dm-core changes
112
+ Cow.create(
113
+ :id => 89,
114
+ :composite => 34,
115
+ :name => 'Berta',
116
+ :breed => 'Guernsey'
117
+ )
118
+ result = @harness.test(Cow.all)
119
+ result[0].values_at("name", "breed").should == ["Berta", "Guernsey"]
120
+ end
121
+
122
+ it 'should serialize a collection' do
123
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
124
+ collection = DataMapper::Collection.new(query) do |c|
125
+ c.load([1, 2, 'Betsy', 'Jersey'])
126
+ c.load([10, 20, 'Berta', 'Guernsey'])
127
+ end
128
+
129
+ result = @harness.test(collection)
130
+ result[0].values_at("id", "composite", "name", "breed").should == [1, 2, 'Betsy', 'Jersey']
131
+ result[1].values_at("id", "composite", "name", "breed").should == [10, 20, 'Berta', 'Guernsey']
132
+ end
133
+
134
+ it 'should serialize an empty collection' do
135
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
136
+ collection = DataMapper::Collection.new(query) {}
137
+
138
+ result = @harness.test(collection)
139
+ result.should be_empty
140
+ end
141
+
142
+ it "serializes a one to many relationship" do
143
+ parent = Cow.new(:id => 1, :composite => 322, :name => "Harry", :breed => "Angus")
144
+ baby = Cow.new(:mother_cow => parent, :id => 2, :composite => 321, :name => "Felix", :breed => "Angus")
145
+
146
+ parent.save
147
+ baby.save
148
+
149
+ result = @harness.test(parent.baby_cows)
150
+ result.should be_kind_of(Array)
151
+
152
+ result[0].values_at(*%w{id composite name breed}).should == [2, 321, "Felix", "Angus"]
153
+ end
154
+
155
+ it "serializes a many to one relationship" do
156
+ parent = Cow.new(:id => 1, :composite => 322, :name => "Harry", :breed => "Angus")
157
+ baby = Cow.new(:mother_cow => parent, :id => 2, :composite => 321, :name => "Felix", :breed => "Angus")
158
+
159
+ parent.save
160
+ baby.save
161
+
162
+ result = @harness.test(baby.mother_cow)
163
+ result.should be_kind_of(Hash)
164
+ result.values_at(*%w{id composite name breed}).should == [1, 322, "Harry", "Angus"]
165
+ end
166
+
167
+ it "serializes a many to many relationship" do
168
+ p1 = Planet.create(:name => 'earth')
169
+ p2 = Planet.create(:name => 'mars')
170
+
171
+ FriendedPlanet.create(:planet => p1, :friend_planet => p2)
172
+
173
+ result = @harness.test(p1.reload.friend_planets)
174
+ result.should be_kind_of(Array)
175
+
176
+ result[0]["name"].should == "mars"
177
+ end
178
+ end
179
+
180
+ describe "(multiple repositories)" do
181
+ before(:all) do
182
+ QuanTum::Cat.auto_migrate!
183
+ repository(:alternate){QuanTum::Cat.auto_migrate!}
184
+ end
185
+
186
+ it "should use the repsoitory for the model" do
187
+ gerry = QuanTum::Cat.create(:name => "gerry")
188
+ george = repository(:alternate){QuanTum::Cat.create(:name => "george", :is_dead => false)}
189
+ @harness.test(gerry )['is_dead'].should be(nil)
190
+ @harness.test(george)['is_dead'].should be(false)
191
+ end
192
+ end
193
+ end
File without changes
@@ -23,24 +23,24 @@ describe DataMapper::Serialize, '#to_csv' do
23
23
  peter.composite = 344
24
24
  peter.name = 'Peter'
25
25
  peter.breed = 'Long Horn'
26
- peter.to_csv.chomp.should == '44,344,Peter,Long Horn'
26
+ peter.to_csv.chomp.split(',')[0..3].should == ['44','344','Peter','Long Horn']
27
27
  end
28
28
 
29
29
  it "should serialize a collection to CSV" do
30
- @collection.to_csv.gsub(/[[:space:]]+\n/, "\n").should ==
31
- "1,2,Betsy,Jersey\n" +
32
- "10,20,Berta,Guernsey\n"
30
+ result = @collection.to_csv.gsub(/[[:space:]]+\n/, "\n")
31
+ result.to_a[0].split(',')[0..3].should == ['1','2','Betsy','Jersey']
32
+ result.to_a[1].split(',')[0..3].should == ['10','20','Berta','Guernsey']
33
33
  end
34
34
 
35
35
  describe "multiple repositories" do
36
36
  before(:all) do
37
- QuantumCat.auto_migrate!
38
- repository(:alternate){QuantumCat.auto_migrate!}
37
+ QuanTum::Cat.auto_migrate!
38
+ repository(:alternate){QuanTum::Cat.auto_migrate!}
39
39
  end
40
40
 
41
41
  it "should use the repsoitory for the model" do
42
- gerry = QuantumCat.create(:name => "gerry")
43
- george = repository(:alternate){QuantumCat.create(:name => "george", :is_dead => false)}
42
+ gerry = QuanTum::Cat.create(:name => "gerry")
43
+ george = repository(:alternate){QuanTum::Cat.create(:name => "george", :is_dead => false)}
44
44
  gerry.to_csv.should_not match(/false/)
45
45
  george.to_csv.should match(/false/)
46
46
  end
@@ -0,0 +1,94 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataMapper::Serialize, '#to_json' do
5
+ #
6
+ # ==== ajaxy JSON
7
+ #
8
+
9
+ before(:all) do
10
+ DataMapper.auto_migrate!
11
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
12
+
13
+ @collection = DataMapper::Collection.new(query) do |c|
14
+ c.load([1, 2, 'Betsy', 'Jersey'])
15
+ c.load([10, 20, 'Berta', 'Guernsey'])
16
+ end
17
+
18
+ @harness = Class.new(SerializerTestHarness) do
19
+ def method_name
20
+ :to_json
21
+ end
22
+
23
+ protected
24
+
25
+ def deserialize(result)
26
+ JSON.parse(result)
27
+ end
28
+ end.new
29
+ end
30
+
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
71
+
72
+ it "handles options given to a collection properly" do
73
+ deserialized_collection = JSON.parse(@collection.to_json(:only => [:composite]))
74
+ betsy = deserialized_collection.first
75
+ berta = deserialized_collection.last
76
+
77
+ betsy["id"].should be_nil
78
+ betsy["composite"].should == 2
79
+ betsy["name"].should be_nil
80
+ betsy["breed"].should be_nil
81
+
82
+ berta["id"].should be_nil
83
+ berta["composite"].should == 20
84
+ berta["name"].should be_nil
85
+ berta["breed"].should be_nil
86
+ end
87
+
88
+ it "supports :include option for one level depth"
89
+
90
+ it "supports :include option for more than one level depth"
91
+
92
+ it "has :repository option to override used repository"
93
+
94
+ end
@@ -0,0 +1,89 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataMapper::Serialize, '#to_xml' do
5
+ #
6
+ # ==== enterprisey XML
7
+ #
8
+
9
+ before(:all) do
10
+ @harness = Class.new(SerializerTestHarness) do
11
+ def method_name
12
+ :to_xml
13
+ end
14
+
15
+ protected
16
+
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|
22
+ a = {}
23
+ element.elements.each do |v|
24
+ a.update(v.name => cast(v.text, v.attributes["type"]))
25
+ end
26
+ a
27
+ 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
+ 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
+ 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'
51
+ end
52
+
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
59
+ end
60
+
61
+ xml = planet.to_xml
62
+ REXML::Document.new(xml).elements[1].name.should == "aplanet"
63
+ end
64
+ end
65
+
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
71
+
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
75
+
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
83
+ end
84
+
85
+ xml = @collection.to_xml
86
+ REXML::Document.new(xml).elements[1].name.should == "somanycats"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,32 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
4
+
5
+ describe DataMapper::Serialize, '#to_yaml' do
6
+ #
7
+ # ==== yummy YAML
8
+ #
9
+
10
+ before(:all) do
11
+ @harness = Class.new(SerializerTestHarness) do
12
+ def method_name
13
+ :to_yaml
14
+ end
15
+
16
+ protected
17
+
18
+ def deserialize(result)
19
+ stringify_keys = lambda {|hash| hash.inject({}) {|a, (key, value)| a.update(key.to_s => value) }}
20
+ result = YAML.load(result)
21
+ if result.is_a?(Array)
22
+ result.collect(&stringify_keys)
23
+ else
24
+ stringify_keys[result]
25
+ end
26
+ end
27
+ end.new
28
+ end
29
+
30
+ it_should_behave_like "A serialization method"
31
+
32
+ end
@@ -1,2 +1 @@
1
- --format specdoc
2
1
  --colour
@@ -1,7 +1,7 @@
1
- require 'rubygems'
2
1
  require 'pathname'
2
+ require 'rubygems'
3
3
 
4
- gem 'dm-core', '~>0.9.7'
4
+ gem 'dm-core', '~>0.9.8'
5
5
  require 'dm-core'
6
6
 
7
7
  spec_dir_path = Pathname(__FILE__).dirname.expand_path
@@ -10,17 +10,13 @@ require spec_dir_path.parent + 'lib/dm-serializer'
10
10
  def load_driver(name, default_uri)
11
11
  return false if ENV['ADAPTER'] != name.to_s
12
12
 
13
- lib = "do_#{name}"
14
-
15
13
  begin
16
- gem lib, '~>0.9.7'
17
- require lib
18
14
  DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
19
15
  DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
20
16
  DataMapper::Repository.adapters[:alternate] = DataMapper::Repository.adapters[name]
21
17
  true
22
- rescue Gem::LoadError => e
23
- warn "Could not load #{lib}: #{e}"
18
+ rescue LoadError => e
19
+ warn "Could not load do_#{name}: #{e}"
24
20
  false
25
21
  end
26
22
  end
@@ -31,6 +27,13 @@ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
31
27
  HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
32
28
  HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
33
29
 
30
+ class SerializerTestHarness
31
+ def test(object, *args)
32
+ deserialize(object.send(method_name, *args))
33
+ end
34
+ end
35
+
36
+ require spec_dir_path + 'lib/serialization_method_shared_spec'
34
37
 
35
38
  # require fixture resources
36
39
  Dir[spec_dir_path + "fixtures/*.rb"].each do |fixture_file|
@@ -0,0 +1,13 @@
1
+ def sudo_gem(cmd)
2
+ sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
3
+ end
4
+
5
+ desc "Install #{GEM_NAME} #{GEM_VERSION}"
6
+ task :install => [ :package ] do
7
+ sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
8
+ end
9
+
10
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
11
+ task :uninstall => [ :clobber ] do
12
+ sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -Ix"
13
+ end
@@ -0,0 +1,25 @@
1
+ begin
2
+ gem 'rspec', '~>1.1.11'
3
+ require 'spec'
4
+ require 'spec/rake/spectask'
5
+
6
+ task :default => [ :spec ]
7
+
8
+ desc 'Run specifications'
9
+ Spec::Rake::SpecTask.new(:spec) do |t|
10
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
11
+ t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
12
+
13
+ begin
14
+ gem 'rcov', '~>0.8'
15
+ t.rcov = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
16
+ t.rcov_opts << '--exclude' << 'spec'
17
+ t.rcov_opts << '--text-summary'
18
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
19
+ rescue LoadError
20
+ # rcov not installed
21
+ end
22
+ end
23
+ rescue LoadError
24
+ # rspec not installed
25
+ end
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.7
4
+ version: 0.9.8
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-11-18 00:00:00 -08:00
12
+ date: 2008-12-07 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -18,23 +18,13 @@ dependencies:
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
- - - "="
21
+ - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: 0.9.7
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: hoe
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 1.8.2
23
+ version: 0.9.8
34
24
  version:
35
25
  description: DataMapper plugin for serializing DataMapper objects
36
26
  email:
37
- - vandenberg.guy@gmail.com
27
+ - vandenberg.guy [a] gmail [d] com
38
28
  executables: []
39
29
 
40
30
  extensions: []
@@ -43,6 +33,7 @@ extra_rdoc_files:
43
33
  - README.txt
44
34
  - LICENSE
45
35
  - TODO
36
+ - History.txt
46
37
  files:
47
38
  - History.txt
48
39
  - LICENSE
@@ -56,14 +47,17 @@ files:
56
47
  - lib/dm-serializer/version.rb
57
48
  - spec/fixtures/cow.rb
58
49
  - spec/fixtures/planet.rb
59
- - spec/fixtures/quatum_cat.rb
50
+ - spec/fixtures/quan_tum_cat.rb
51
+ - spec/lib/serialization_method_shared_spec.rb
52
+ - spec/public/serializer_spec.rb
53
+ - spec/public/to_csv_spec.rb
54
+ - spec/public/to_json_spec.rb
55
+ - spec/public/to_xml_spec.rb
56
+ - spec/public/to_yaml_spec.rb
60
57
  - spec/spec.opts
61
58
  - spec/spec_helper.rb
62
- - spec/unit/serializer_spec.rb
63
- - spec/unit/to_csv_spec.rb
64
- - spec/unit/to_json_spec.rb
65
- - spec/unit/to_xml_spec.rb
66
- - spec/unit/to_yaml_spec.rb
59
+ - tasks/install.rb
60
+ - tasks/spec.rb
67
61
  has_rdoc: true
68
62
  homepage: http://github.com/sam/dm-more/tree/master/dm-serializer
69
63
  post_install_message:
@@ -1,11 +0,0 @@
1
- class QuantumCat
2
- include DataMapper::Resource
3
-
4
- property :id, Serial
5
- property :name, String
6
- property :location, String
7
-
8
- repository(:alternate) do
9
- property :is_dead, Boolean
10
- end
11
- end
@@ -1,130 +0,0 @@
1
- require 'pathname'
2
- require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
-
4
- describe DataMapper::Serialize, '#to_json' do
5
- #
6
- # ==== ajaxy JSON
7
- #
8
-
9
- before(:all) do
10
- query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
11
-
12
- @collection = DataMapper::Collection.new(query) do |c|
13
- c.load([1, 2, 'Betsy', 'Jersey'])
14
- c.load([10, 20, 'Berta', 'Guernsey'])
15
- end
16
-
17
- @empty_collection = DataMapper::Collection.new(query) {}
18
- end
19
-
20
- it "serializes resource to JSON" do
21
- deserialized_hash = JSON.parse(Cow.new(:id => 1, :composite => 322, :name => "Harry", :breed => "Angus").to_json)
22
-
23
- deserialized_hash["id"].should == 1
24
- deserialized_hash["composite"].should == 322
25
- deserialized_hash["name"].should == "Harry"
26
- deserialized_hash["breed"].should == "Angus"
27
- end
28
-
29
- it "excludes nil attributes" do
30
- deserialized_hash = JSON.parse(Cow.new(:id => 1, :name => "Harry", :breed => "Angus").to_json)
31
-
32
- deserialized_hash["id"].should == 1
33
- deserialized_hash["composite"].should be(nil)
34
- deserialized_hash["name"].should == "Harry"
35
- deserialized_hash["breed"].should == "Angus"
36
- end
37
-
38
- it "serializes collections to JSON by serializing each member" do
39
- deserialized_collection = JSON.parse(@collection.to_json)
40
- betsy = deserialized_collection.first
41
- berta = deserialized_collection.last
42
-
43
- betsy["id"].should == 1
44
- betsy["composite"].should == 2
45
- betsy["name"].should == "Betsy"
46
- betsy["breed"].should == "Jersey"
47
-
48
- berta["id"].should == 10
49
- berta["composite"].should == 20
50
- berta["name"].should == "Berta"
51
- berta["breed"].should == "Guernsey"
52
- end
53
-
54
- it "handles extra properties" do
55
- deserialized_hash = JSON.parse(Cow.new(:id => 1, :name => "Harry", :breed => "Angus").to_json)
56
-
57
- deserialized_hash["extra"].should == "Extra"
58
- deserialized_hash["another"].should == 42
59
- end
60
-
61
- it "handles empty collections just fine" do
62
- deserialized_collection = JSON.parse(@empty_collection.to_json)
63
- deserialized_collection.should be_empty
64
- end
65
-
66
- it "handles options given to a collection properly" do
67
- deserialized_collection = JSON.parse(@collection.to_json(:only => [:composite]))
68
- betsy = deserialized_collection.first
69
- berta = deserialized_collection.last
70
-
71
- betsy["id"].should be_nil
72
- betsy["composite"].should == 2
73
- betsy["name"].should be_nil
74
- betsy["breed"].should be_nil
75
-
76
- berta["id"].should be_nil
77
- berta["composite"].should == 20
78
- berta["name"].should be_nil
79
- berta["breed"].should be_nil
80
- end
81
-
82
- it "serializes values returned by methods given to :methods option" do
83
- deserialized_hash = JSON.parse(Planet.new(:name => "Mars", :aphelion => 249_209_300.4).to_json(:methods => [:category, :has_known_form_of_life?]))
84
-
85
- deserialized_hash["category"].should == "terrestrial"
86
- deserialized_hash["has_known_form_of_life?"].should be(false)
87
- end
88
-
89
- it "only includes properties given to :only option" do
90
- deserialized_hash = JSON.parse(Planet.new(:name => "Mars", :aphelion => 249_209_300.4).to_json(:only => [:name]))
91
-
92
- deserialized_hash["name"].should == "Mars"
93
- deserialized_hash["aphelion"].should be(nil)
94
- end
95
-
96
- it "only includes properties given to :only option" do
97
- deserialized_hash = JSON.parse(Planet.new(:name => "Mars", :aphelion => 249_209_300.4).to_json(:exclude => [:aphelion]))
98
-
99
- deserialized_hash["name"].should == "Mars"
100
- deserialized_hash["aphelion"].should be(nil)
101
- end
102
-
103
- it "has higher presedence for :only option" do
104
- deserialized_hash = JSON.parse(Planet.new(:name => "Mars", :aphelion => 249_209_300.4).to_json(:only => [:aphelion], :exclude => [:aphelion]))
105
-
106
- deserialized_hash["name"].should be(nil)
107
- deserialized_hash["aphelion"].should == 249_209_300.4
108
- end
109
-
110
- describe "multiple repositories" do
111
- before(:all) do
112
- QuantumCat.auto_migrate!
113
- repository(:alternate){QuantumCat.auto_migrate!}
114
- end
115
-
116
- it "should use the repsoitory for the model" do
117
- gerry = QuantumCat.create(:name => "gerry")
118
- george = repository(:alternate){QuantumCat.create(:name => "george", :is_dead => false)}
119
- gerry.to_json.should_not match(/is_dead/)
120
- george.to_json.should match(/is_dead/)
121
- end
122
- end
123
-
124
- it "supports :include option for one level depth"
125
-
126
- it "supports :include option for more than one level depth"
127
-
128
- it "has :repository option to override used repository"
129
-
130
- end
@@ -1,73 +0,0 @@
1
- require 'pathname'
2
- require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
-
4
- describe DataMapper::Serialize, '#to_xml' do
5
- #
6
- # ==== enterprisey XML
7
- #
8
-
9
- before(:all) do
10
- query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
11
-
12
- @time = DateTime.now
13
-
14
-
15
- @collection = DataMapper::Collection.new(query) do |c|
16
- c.load([1, 2, 'Betsy', 'Jersey'])
17
- c.load([10, 20, 'Berta', 'Guernsey'])
18
- end
19
-
20
- @empty_collection = DataMapper::Collection.new(query) {}
21
- end
22
-
23
- it "should serialize a resource to XML" do
24
- berta = Cow.new
25
- berta.id = 89
26
- berta.composite = 34
27
- berta.name = 'Berta'
28
- berta.breed = 'Guernsey'
29
-
30
- berta.to_xml.should == <<-EOS.compress_lines(false)
31
- <cow>
32
- <id type='integer'>89</id>
33
- <composite type='integer'>34</composite>
34
- <name>Berta</name>
35
- <breed>Guernsey</breed>
36
- </cow>
37
- EOS
38
- end
39
-
40
- it "should serialize a collection to XML" do
41
- @collection.to_xml.should == <<-EOS.compress_lines(false)
42
- <#{Extlib::Inflection.tableize("Cow")} type='array'>
43
- <cow>
44
- <id type='integer'>1</id>
45
- <composite type='integer'>2</composite>
46
- <name>Betsy</name>
47
- <breed>Jersey</breed>
48
- </cow>
49
- <cow>
50
- <id type='integer'>10</id>
51
- <composite type='integer'>20</composite>
52
- <name>Berta</name>
53
- <breed>Guernsey</breed>
54
- </cow>
55
- </#{Extlib::Inflection.tableize("Cow")}>
56
- EOS
57
- end
58
-
59
- describe "multiple repositories" do
60
- before(:all) do
61
- QuantumCat.auto_migrate!
62
- repository(:alternate){QuantumCat.auto_migrate!}
63
- end
64
-
65
- it "should use the repsoitory for the model" do
66
- gerry = QuantumCat.create(:name => "gerry")
67
- george = repository(:alternate){QuantumCat.create(:name => "george", :is_dead => false)}
68
- gerry.to_xml.should_not match(/is_dead/)
69
- george.to_xml.should match(/is_dead/)
70
- end
71
- end
72
-
73
- end
@@ -1,76 +0,0 @@
1
- require 'pathname'
2
- require 'yaml'
3
- require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
4
-
5
- describe DataMapper::Serialize, '#to_yaml' do
6
- #
7
- # ==== yummy YAML
8
- #
9
-
10
- before(:all) do
11
- query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
12
-
13
- @collection = DataMapper::Collection.new(query) do |c|
14
- c.load([1, 2, 'Betsy', 'Jersey'])
15
- c.load([10, 20, 'Berta', 'Guernsey'])
16
- end
17
-
18
- @empty_collection = DataMapper::Collection.new(query) {}
19
- end
20
-
21
- it "serializes single resource to YAML" do
22
- betsy = Cow.new(:id => 230, :composite => 22, :name => "Betsy", :breed => "Jersey")
23
- deserialized_hash = YAML.load(betsy.to_yaml)
24
-
25
- deserialized_hash[:id].should == 230
26
- deserialized_hash[:name].should == "Betsy"
27
- deserialized_hash[:composite].should == 22
28
- deserialized_hash[:breed].should == "Jersey"
29
- end
30
-
31
- it "leaves out nil properties" do
32
- betsy = Cow.new(:id => 230, :name => "Betsy", :breed => "Jersey")
33
- deserialized_hash = YAML.load(betsy.to_yaml)
34
-
35
- deserialized_hash[:id].should == 230
36
- deserialized_hash[:name].should == "Betsy"
37
- deserialized_hash[:composite].should be(nil)
38
- deserialized_hash[:breed].should == "Jersey"
39
- end
40
-
41
- it "serializes a collection to YAML" do
42
- deserialized_collection = YAML.load(@collection.to_yaml)
43
-
44
- betsy = deserialized_collection.first
45
- berta = deserialized_collection.last
46
-
47
- betsy[:id].should == 1
48
- betsy[:name].should == "Betsy"
49
- betsy[:composite].should == 2
50
- betsy[:breed].should == "Jersey"
51
-
52
- berta[:id].should == 10
53
- berta[:name].should == "Berta"
54
- berta[:composite].should == 20
55
- berta[:breed].should == "Guernsey"
56
- end
57
-
58
- it "handles empty collections just fine" do
59
- YAML.load(@empty_collection.to_yaml).should be_empty
60
- end
61
-
62
- describe "multiple repositories" do
63
- before(:all) do
64
- QuantumCat.auto_migrate!
65
- repository(:alternate){QuantumCat.auto_migrate!}
66
- end
67
-
68
- it "should use the repsoitory for the model" do
69
- gerry = QuantumCat.create(:name => "gerry")
70
- george = repository(:alternate){QuantumCat.create(:name => "george", :is_dead => false)}
71
- gerry.to_yaml.should_not match(/is_dead/)
72
- george.to_yaml.should match(/is_dead/)
73
- end
74
- end
75
-
76
- end