smg 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README.rdoc +6 -62
  2. data/examples/discogs/label.rb +62 -0
  3. data/examples/discogs/search.rb +60 -0
  4. data/examples/plant.rb +1 -2
  5. data/examples/twitter.rb +8 -5
  6. data/examples/weather.rb +146 -0
  7. data/lib/smg.rb +4 -0
  8. data/lib/smg/document.rb +25 -8
  9. data/lib/smg/http.rb +66 -0
  10. data/lib/smg/http/exceptions.rb +37 -0
  11. data/lib/smg/http/request.rb +126 -0
  12. data/lib/smg/mapping.rb +11 -1
  13. data/lib/smg/mapping/element.rb +29 -13
  14. data/lib/smg/mapping/typecasts.rb +2 -1
  15. data/lib/smg/model.rb +9 -3
  16. data/lib/smg/resource.rb +5 -1
  17. data/spec/collect_spec.rb +254 -0
  18. data/spec/context_spec.rb +189 -0
  19. data/spec/extract_spec.rb +200 -0
  20. data/spec/filtering_spec.rb +164 -0
  21. data/spec/fixtures/discogs/948224.xml +1 -0
  22. data/spec/fixtures/discogs/Enzyme+Records.xml +9 -0
  23. data/spec/fixtures/discogs/Genosha+Recordings.xml +13 -0
  24. data/spec/fixtures/discogs/Ophidian.xml +6 -0
  25. data/spec/fixtures/fake/malus.xml +18 -0
  26. data/spec/fixtures/fake/valve.xml +8 -0
  27. data/spec/fixtures/twitter/pipopolam.xml +46 -0
  28. data/spec/fixtures/yahoo.weather.com.xml +50 -0
  29. data/spec/http/request_spec.rb +186 -0
  30. data/spec/http/shared/automatic.rb +43 -0
  31. data/spec/http/shared/non_automatic.rb +36 -0
  32. data/spec/http/shared/redirectable.rb +30 -0
  33. data/spec/http_spec.rb +76 -0
  34. data/spec/lib/helpers/http_helpers.rb +27 -0
  35. data/spec/lib/matchers/instance_methods.rb +38 -0
  36. data/spec/mapping/element_spec.rb +241 -0
  37. data/spec/mapping/typecasts_spec.rb +52 -0
  38. data/spec/resource_spec.rb +30 -0
  39. data/spec/root_spec.rb +26 -0
  40. data/spec/spec_helper.rb +23 -0
  41. metadata +53 -10
  42. data/examples/discogs.rb +0 -39
@@ -0,0 +1,189 @@
1
+ require File.expand_path File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe SMG::Model do
4
+
5
+ before :each do
6
+ @klass = Class.new { include SMG::Resource }
7
+ @data = data = File.read(FIXTURES_DIR + 'fake/malus.xml')
8
+ end
9
+
10
+ describe ".extract with context" do
11
+
12
+ before :each do
13
+ @data = File.read(FIXTURES_DIR + 'fake/malus.xml')
14
+ end
15
+
16
+ it "does not extract data with the custom :context, when no context passed" do
17
+
18
+ @klass.root 'spec'
19
+
20
+ @klass.extract :genus
21
+ @klass.extract :binomial
22
+ @klass.extract :conservation , :context => [:conservation]
23
+
24
+ malus = @klass.parse(@data)
25
+ malus.genus.should == "Malus"
26
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
27
+ malus.conservation.should be_nil
28
+
29
+ malus = @klass.parse(@data, :conservation)
30
+ malus.genus.should == "Malus"
31
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
32
+ malus.conservation.should == "Vulnerable (IUCN 2.3)"
33
+
34
+ end
35
+
36
+ it "respects the :context of elements and attributes" do
37
+
38
+ @klass.root 'spec'
39
+
40
+ @klass.extract :genus , :context => [:classification]
41
+ @klass.extract :binomial , :context => [:conservation, :classification]
42
+ @klass.extract :conservation , :context => [:conservation], :as => :status
43
+ @klass.extract :conservation , :context => [:conservation], :at => :code
44
+
45
+ malus = @klass.parse(@data,:conservation)
46
+ malus.genus.should be_nil
47
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
48
+ malus.status.should == "Vulnerable (IUCN 2.3)"
49
+ malus.code.should == "VU"
50
+
51
+ malus = @klass.parse(@data,:classification)
52
+ malus.genus.should == "Malus"
53
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
54
+ malus.status.should be_nil
55
+ malus.code.should be_nil
56
+
57
+ end
58
+
59
+ it "respects the :context of nested resources" do
60
+
61
+ cclass = Class.new { include SMG::Resource }
62
+ cclass.extract :conservation , :at => :code
63
+ cclass.extract :conservation , :as => :status
64
+
65
+ @klass.root 'spec'
66
+
67
+ @klass.extract :genus , :context => [:classification]
68
+ @klass.extract :binomial , :context => [:conservation, :classification]
69
+ @klass.extract :conservation , :context => [:conservation], :class => cclass
70
+
71
+ malus = @klass.parse(@data,:classification)
72
+ malus.genus.should == "Malus"
73
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
74
+ malus.conservation.should be_nil
75
+
76
+ malus = @klass.parse(@data,:conservation)
77
+ malus.genus.should be_nil
78
+ malus.binomial.should == "Malus sieversii (Ledeb.) M.Roem."
79
+
80
+ malus.conservation.should be_an_instance_of cclass
81
+ malus.conservation.status.should == "Vulnerable (IUCN 2.3)"
82
+ malus.conservation.code.should == "VU"
83
+
84
+ end
85
+
86
+ it "respects the :context IN nested resources" do
87
+
88
+ cclass = Class.new { include SMG::Resource }
89
+ cclass.extract :conservation , :at => :code , :context => [:conservation]
90
+ cclass.extract :conservation , :as => :status , :context => [:conservation, :info]
91
+
92
+ @klass.root 'spec'
93
+ @klass.extract :conservation , :class => cclass
94
+
95
+ malus = @klass.parse(@data,:conservation)
96
+ malus.conservation.should be_an_instance_of cclass
97
+ malus.conservation.status.should == "Vulnerable (IUCN 2.3)"
98
+ malus.conservation.code.should == "VU"
99
+
100
+ malus = @klass.parse(@data,:info)
101
+ malus.conservation.should be_an_instance_of cclass
102
+ malus.conservation.status.should == "Vulnerable (IUCN 2.3)"
103
+ malus.conservation.code.should be_nil
104
+
105
+ end
106
+
107
+ end
108
+
109
+ describe ".collect with context" do
110
+
111
+ before :each do
112
+ @data = File.read(FIXTURES_DIR + 'discogs/Ophidian.xml')
113
+ end
114
+
115
+ it "does not collect data with the custom :context, when no context passed" do
116
+
117
+ @klass.root 'resp/artist'
118
+ @klass.collect 'namevariations/name' , :as => :namevariations
119
+ @klass.collect 'aliases/name' , :as => :aliases, :context => [:aliases]
120
+
121
+ ophidian = @klass.parse(@data)
122
+ ophidian.namevariations.should == ["Ohidian", "Ophidian as Raziel", "Ophidian [C. Hoyer]", "Ophidiom"]
123
+ ophidian.aliases.should be_an ::Array
124
+ ophidian.aliases.should be_empty
125
+
126
+ ophidian = @klass.parse(@data, :aliases)
127
+ ophidian.namevariations.should == ["Ohidian", "Ophidian as Raziel", "Ophidian [C. Hoyer]", "Ophidiom"]
128
+ ophidian.aliases.should == ["Conrad Hoyer", "Cubist Boy", "Meander", "Trypticon"]
129
+
130
+ end
131
+
132
+ it "respects the :context of collections" do
133
+
134
+ @klass.root 'resp/artist'
135
+ @klass.collect 'namevariations/name' , :context => [:namevariations] , :as => :namevariations
136
+ @klass.collect 'aliases/name' , :context => [:aliases] , :as => :aliases
137
+
138
+ ophidian = @klass.parse(@data,:namevariations)
139
+ ophidian.namevariations.should == ["Ohidian", "Ophidian as Raziel", "Ophidian [C. Hoyer]", "Ophidiom"]
140
+ ophidian.aliases.should be_an ::Array
141
+ ophidian.aliases.should be_empty
142
+
143
+ ophidian = @klass.parse(@data,:aliases)
144
+ ophidian.namevariations.should be_an ::Array
145
+ ophidian.namevariations.should be_empty
146
+ ophidian.aliases.should == ["Conrad Hoyer", "Cubist Boy", "Meander", "Trypticon"]
147
+
148
+ end
149
+
150
+ it "respects the :context IN collections" do
151
+
152
+ imgclass = Class.new { include SMG::Resource }
153
+ imgclass.extract :image , :at => :uri150 , :as => :preview
154
+ imgclass.extract :image , :at => :uri , :as => :fullsize , :context => [:everything]
155
+ imgclass.extract :image , :at => :height , :context => [:everything]
156
+ imgclass.extract :image , :at => :width , :context => [:everything]
157
+
158
+ @klass.root 'resp/artist'
159
+ @klass.collect 'images/image', :as => :images, :class => imgclass
160
+
161
+ ophidian = @klass.parse(@data)
162
+ ophidian.images.should be_an ::Array
163
+ ophidian.images.size.should == 5
164
+
165
+ image = ophidian.images[2]
166
+ image.should be_an_instance_of imgclass
167
+ image.height.should be_nil
168
+ image.width.should be_nil
169
+ image.fullsize.should be_nil
170
+ image.preview.should == "http://www.discogs.com/image/A-150-15203-1172410732.jpeg"
171
+
172
+ ophidian = @klass.parse(@data, :everything)
173
+ ophidian.images.should be_an ::Array
174
+ ophidian.images.size.should == 5
175
+
176
+ image = ophidian.images[2]
177
+ image.should be_an_instance_of imgclass
178
+ image.height.should == "399"
179
+ image.width.should == "598"
180
+ image.fullsize.should == "http://www.discogs.com/image/A-15203-1172410732.jpeg"
181
+ image.preview.should == "http://www.discogs.com/image/A-150-15203-1172410732.jpeg"
182
+
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+ # EOF
@@ -0,0 +1,200 @@
1
+ require File.expand_path File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe SMG::Model, ".extract" do
4
+
5
+ before :each do
6
+ @klass = Class.new { include SMG::Resource }
7
+ @data = File.read(FIXTURES_DIR + 'fake/malus.xml')
8
+ end
9
+
10
+ include Spec::Matchers::HaveInstanceMethodMixin
11
+
12
+ it "defines appropriate reader and writer" do
13
+ @klass.extract :whatever
14
+ @klass.should have_instance_method :whatever
15
+ @klass.should have_instance_method :whatever=
16
+ end
17
+
18
+ it "never overrides readers" do
19
+ @klass.class_eval "def kingdom; return @kingdom.to_s.upcase; end"
20
+ @klass.root 'spec'
21
+ @klass.extract :kingdom
22
+ malus = @klass.parse(@data)
23
+ malus.kingdom.should == "PLANTAE"
24
+ malus.instance_variable_get(:@kingdom).should == "Plantae"
25
+ end
26
+
27
+ it "never overrides writers" do
28
+ @klass.class_eval "def kingdom=(value); @kingdom = value.to_s.upcase; end"
29
+ @klass.root 'spec'
30
+ @klass.extract :kingdom
31
+ malus = @klass.parse(@data)
32
+ malus.kingdom.should == "PLANTAE"
33
+ malus.instance_variable_get(:@kingdom).should == "PLANTAE"
34
+ end
35
+
36
+ it "extracts an empty String if there's an empty element" do
37
+ @klass.root 'spec'
38
+ @klass.extract :additional
39
+ malus = @klass.parse(@data)
40
+ malus.additional.should == ""
41
+ end
42
+
43
+ it "extracts nothing if there's no appropriate element" do
44
+ @klass.root 'spec'
45
+ @klass.extract :bogus
46
+ malus = @klass.parse(@data)
47
+ malus.bogus.should == nil
48
+ end
49
+
50
+ it "extracts the text of an element otherwise" do
51
+ @klass.root 'spec'
52
+ @klass.extract :genus
53
+ malus = @klass.parse(@data)
54
+ malus.should respond_to :genus
55
+ malus.should respond_to :genus=
56
+ malus.genus.should == 'Malus'
57
+ end
58
+
59
+ describe "using :at option" do
60
+
61
+ it "extracts the appropriate attribute" do
62
+ @klass.extract 'spec/conservation', :as => :conservation_code, :at => :code
63
+ malus = @klass.parse(@data)
64
+ malus.conservation_code.should == 'VU'
65
+ end
66
+
67
+ it "extracts nothing if there'no appropriate attribute" do
68
+ @klass.extract 'spec/conservation', :as => :conservation_code, :at => :bogus
69
+ malus = @klass.parse(@data)
70
+ malus.conservation_code.should == nil
71
+ end
72
+
73
+ it "extracts nothing if there'no appropriate element" do
74
+ @klass.extract 'spec/whatever', :as => :whatever, :at => :bogus
75
+ malus = @klass.parse(@data)
76
+ malus.whatever.should == nil
77
+ end
78
+
79
+ end
80
+
81
+ describe "using :as option" do
82
+
83
+ it "defines appropriate reader and writer" do
84
+ @klass.extract 'spec/conservation', :as => :conservation_status
85
+ malus = @klass.parse(@data)
86
+ malus.should respond_to :conservation_status
87
+ malus.should respond_to :conservation_status=
88
+ end
89
+
90
+ it "extracts data from an element using the 'as' accessor" do
91
+ @klass.extract 'spec/conservation', :as => :conservation_status
92
+ malus = @klass.parse(@data)
93
+ malus.conservation_status.should == 'Vulnerable (IUCN 2.3)'
94
+ end
95
+
96
+ it "extracts nothing if there's no appropriate element" do
97
+ @klass.extract 'spec/bogus', :as => :conservation_code, :at => :bogus
98
+ malus = @klass.parse(@data)
99
+ malus.conservation_code.should == nil
100
+ end
101
+
102
+ end
103
+
104
+ describe "using :class option" do
105
+
106
+ describe "when :class represents an SMG::Resource" do
107
+
108
+ it "extracts data into the class (AKA has_one)" do
109
+ @foo = Class.new
110
+ @foo.send(:include, SMG::Resource)
111
+ @foo.root 'description'
112
+ @foo.extract :common
113
+ @foo.extract :described_by
114
+ @foo.extract :described_as
115
+ @foo.extract :described_at
116
+ @klass.extract 'spec/description' , :as => :description, :class => @foo
117
+ malus = @klass.parse(@data)
118
+ malus.description.should be_an_instance_of @foo
119
+ malus.description.common.should == "Asian Wild Apple"
120
+ malus.description.described_at.should == "1983"
121
+ malus.description.described_by.should == "Carl Friedrich von Ledebour"
122
+ malus.description.described_as.should == "Pyrus sieversii"
123
+ end
124
+
125
+ end
126
+
127
+ describe "when :class represents a built-in typecast" do
128
+
129
+ it "makes an attempt to perform a typecast" do
130
+ Class.new { include SMG::Resource }
131
+ @klass.root 'spec'
132
+ @klass.extract :conservation, :at => :year, :class => :integer, :as => :year_of_conservation_check
133
+ malus = @klass.parse(@data)
134
+ malus.year_of_conservation_check.should == 2007
135
+ end
136
+
137
+ it "raises an ArgumentError if typecasting fails" do
138
+ Class.new { include SMG::Resource }
139
+ @klass.root 'spec'
140
+ @klass.extract :conservation, :at => :year, :class => :datetime, :as => :year_of_conservation_check
141
+ lambda { @klass.parse(@data) }.
142
+ should raise_error ArgumentError, %r{"2007" is not a valid source for :datetime}
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+
151
+ describe SMG::Model, ".extract" do
152
+
153
+ describe "when XML contains two or more suitable elements" do
154
+
155
+ before :each do
156
+ @klass = Class.new { include SMG::Resource }
157
+ @data = File.read(FIXTURES_DIR + 'fake/valve.xml')
158
+ end
159
+
160
+ it "ignores everything except the first one suitable thing" do
161
+ @klass.extract "games/game", :as => :title
162
+ parsed = @klass.parse(@data)
163
+ parsed.title.should == "Half-Life 2"
164
+ end
165
+
166
+ describe "using :at option" do
167
+
168
+ it "ignores everything except the first one suitable thing" do
169
+ @klass.extract "games/game", :at => :AppID, :as => :steam_application_id, :class => :integer
170
+ parsed = @klass.parse(@data)
171
+ parsed.steam_application_id.should == 220
172
+ end
173
+
174
+ end
175
+
176
+ describe "using :class option, when :class option represents an SMG::Resource" do
177
+
178
+ it "ignores everything except the first one suitable thing" do
179
+ @game = Class.new { include SMG::Resource }
180
+ @game.extract :game, :at => "AppID", :as => :steam_application_id, :class => :integer
181
+ @game.extract :game, :at => "date"
182
+ @game.extract :game, :at => "type", :as => :content
183
+ @game.extract :game, :as => :title
184
+
185
+ @klass.extract 'games/game', :class => @game
186
+ parsed = @klass.parse(@data)
187
+ parsed.game.should_not be_nil
188
+ parsed.game.title.should == "Half-Life 2"
189
+ parsed.game.content.should == "fullprice"
190
+ parsed.game.steam_application_id.should == 220
191
+ parsed.game.date.should == "2004-11-16"
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ # EOF
@@ -0,0 +1,164 @@
1
+ require File.expand_path File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe SMG::Model, ".extract" do
4
+
5
+ before :each do
6
+ @klass = Class.new { include SMG::Resource }
7
+ @data = File.read(FIXTURES_DIR + 'fake/valve.xml')
8
+ end
9
+
10
+ describe "using :with option" do
11
+
12
+ it "extracts the text of an element if conditions are OK" do
13
+ @klass.extract 'games/game', :as => :title, :with => {:type => "episodic", :AppID => 420}
14
+ game = @klass.parse(@data)
15
+ game.title.should == "Half-Life 2: Episode Two"
16
+
17
+ @klass.extract 'games/game', :as => :title, :with => {:type => "episodic", :date => "To be announced"}
18
+ game = @klass.parse(@data)
19
+ game.title.should == "Half-Life 2: Episode Three"
20
+
21
+ @klass.extract 'games/game', :as => :title, :with => {:type => "episodic", :AppID => nil} # i.e. without @AppID attribute
22
+ game = @klass.parse(@data)
23
+ game.title.should == "Half-Life 2: Episode Three"
24
+ end
25
+
26
+ it "extracts nothing otherwise" do
27
+ @klass.extract 'games/game', :as => :title, :with => {:type => "episodic", :AppID => "malformed"}
28
+ game = @klass.parse(@data)
29
+ game.title.should == nil
30
+ end
31
+
32
+ end
33
+
34
+ describe "using :with and :at options together" do
35
+
36
+ it "extracts the text of an element if conditions are OK" do
37
+ conditions = {:type => "modification", :date => "2007-10-06"}
38
+ @klass.extract 'games/game', :as => :steam_application_id, :with => conditions, :at => "AppID", :class => :integer
39
+ @klass.extract 'games/game', :as => :title, :with => conditions
40
+ game = @klass.parse(@data)
41
+ game.steam_application_id.should == 380
42
+ game.title.should == "Minerva: Metastasis"
43
+ end
44
+
45
+ it "extracts nothing otherwise" do
46
+ conditions = {:type => "modification", :date => "fake"}
47
+ @klass.extract 'games/game', :as => :steam_application_id, :with => conditions, :at => "AppID", :class => :integer
48
+ @klass.extract 'games/game', :as => :title, :with => conditions
49
+ game = @klass.parse(@data)
50
+ game.steam_application_id.should == nil
51
+ game.title.should == nil
52
+ end
53
+
54
+ end
55
+
56
+ describe "using :with and :class options together" do
57
+
58
+ before :each do
59
+ @game = Class.new { include SMG::Resource }
60
+ @game.extract :game, :at => "AppID", :as => :steam_application_id, :class => :integer
61
+ @game.extract :game, :at => "date"
62
+ @game.extract :game, :at => "type", :as => :content
63
+ @game.extract :game, :as => :title
64
+ end
65
+
66
+ it "extracts the text of an element if conditions are OK" do
67
+ conditions = {:type => "modification", :date => "2007-10-06"}
68
+ @klass.extract 'games/game', :class => @game, :with => conditions
69
+ parsed = @klass.parse(@data)
70
+ parsed.game.should_not be_nil
71
+ parsed.game.title.should == "Minerva: Metastasis"
72
+ parsed.game.content.should == "modification"
73
+ parsed.game.steam_application_id.should == 380
74
+ parsed.game.date.should == "2007-10-06"
75
+ end
76
+
77
+ it "extracts nothing otherwise" do
78
+ conditions = {:type => "modification", :date => "malformed"}
79
+ @klass.extract 'games/game', :class => @game, :with => conditions
80
+ parsed = @klass.parse(@data)
81
+ parsed.game.should be_nil
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ describe SMG::Model, ".collect" do
89
+
90
+ before :each do
91
+ @klass = Class.new { include SMG::Resource }
92
+ @data = File.read(FIXTURES_DIR + 'fake/valve.xml')
93
+ end
94
+
95
+ describe "using :with option" do
96
+
97
+ it "collects texts, if conditions are OK" do
98
+ @klass.collect 'games/game', :with => {"type" => "episodic"}, :as => :titles
99
+ parsed = @klass.parse(@data)
100
+ parsed.titles.should == ["Half-Life 2: Episode One", "Half-Life 2: Episode Two", "Half-Life 2: Episode Three"]
101
+ end
102
+
103
+ it "collects texts, if conditions are OK" do
104
+ @klass.collect 'games/game', :with => {"type" => "unknowb"}, :as => :titles
105
+ parsed = @klass.parse(@data)
106
+ parsed.titles.should be_empty
107
+ end
108
+
109
+ end
110
+
111
+ describe "using :with and :at options together" do
112
+
113
+ it "collects attributes, if conditions are OK" do
114
+ @klass.collect 'games/game', :with => {"type" => "episodic"}, :at => :date, :as => :dates
115
+ parsed = @klass.parse(@data)
116
+ parsed.dates.should == ["2006-06-01", "2007-10-10", "To be announced"]
117
+ end
118
+
119
+ it "collects nothing otherwise" do
120
+ @klass.collect 'games/game', :with => {"type" => "unknown"}, :at => :date, :as => :dates
121
+ parsed = @klass.parse(@data)
122
+ parsed.dates.should be_empty
123
+ end
124
+
125
+ end
126
+
127
+ describe "using :with and :class options together" do
128
+
129
+ before :each do
130
+ @game = Class.new { include SMG::Resource }
131
+ @game.extract :game, :at => "AppID", :as => :steam_application_id, :class => :integer
132
+ @game.extract :game, :at => "date"
133
+ @game.extract :game, :as => :title
134
+ end
135
+
136
+ it "collects resources if conditions are OK" do
137
+ conditions = {:type => "modification"}
138
+ @klass.collect 'games/game', :class => @game, :with => conditions, :as => :games
139
+ collection = @klass.parse(@data)
140
+
141
+ collection.games.should_not be_empty
142
+ collection.games.size.should == 2
143
+
144
+ collection.games[0].title.should == "Minerva: Metastasis"
145
+ collection.games[0].steam_application_id.should == 380
146
+ collection.games[0].date.should == "2007-10-06"
147
+
148
+ collection.games[1].title.should == "Black Mesa"
149
+ collection.games[1].steam_application_id.should == nil
150
+ collection.games[1].date.should == "To be announced"
151
+ end
152
+
153
+ it "collects nothing otherwise" do
154
+ conditions = {:type => "unknown"}
155
+ @klass.collect 'games/game', :class => @game, :with => conditions, :as => :games
156
+ collection = @klass.parse(@data)
157
+ collection.games.should be_empty
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+ # EOF