representable 2.4.1 → 3.0.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.
- checksums.yaml +4 -4
- data/.travis.yml +7 -12
- data/CHANGES.md +6 -27
- data/README.md +28 -1326
- data/lib/representable.rb +4 -14
- data/lib/representable/binding.rb +3 -7
- data/lib/representable/definition.rb +1 -2
- data/lib/representable/populator.rb +35 -0
- data/lib/representable/version.rb +1 -1
- data/test/definition_test.rb +0 -7
- data/test/exec_context_test.rb +2 -2
- data/test/instance_test.rb +0 -19
- data/test/realistic_benchmark.rb +0 -13
- data/test/representable_test.rb +0 -16
- data/test/skip_test.rb +1 -1
- data/test/test_helper.rb +0 -2
- metadata +3 -65
- data/lib/representable/deprecations.rb +0 -127
- data/lib/representable/parse_strategies.rb +0 -93
- data/test-with-deprecations/as_test.rb +0 -65
- data/test-with-deprecations/benchmarking.rb +0 -83
- data/test-with-deprecations/binding_test.rb +0 -46
- data/test-with-deprecations/blaaaaaaaa_test.rb +0 -69
- data/test-with-deprecations/cached_test.rb +0 -147
- data/test-with-deprecations/class_test.rb +0 -119
- data/test-with-deprecations/coercion_test.rb +0 -52
- data/test-with-deprecations/config/inherit_test.rb +0 -135
- data/test-with-deprecations/config_test.rb +0 -122
- data/test-with-deprecations/decorator_scope_test.rb +0 -28
- data/test-with-deprecations/decorator_test.rb +0 -96
- data/test-with-deprecations/default_test.rb +0 -34
- data/test-with-deprecations/defaults_options_test.rb +0 -93
- data/test-with-deprecations/definition_test.rb +0 -264
- data/test-with-deprecations/example.rb +0 -310
- data/test-with-deprecations/examples/object.rb +0 -31
- data/test-with-deprecations/exec_context_test.rb +0 -93
- data/test-with-deprecations/features_test.rb +0 -70
- data/test-with-deprecations/filter_test.rb +0 -57
- data/test-with-deprecations/for_collection_test.rb +0 -74
- data/test-with-deprecations/generic_test.rb +0 -116
- data/test-with-deprecations/getter_setter_test.rb +0 -21
- data/test-with-deprecations/hash_bindings_test.rb +0 -87
- data/test-with-deprecations/hash_test.rb +0 -160
- data/test-with-deprecations/heritage_test.rb +0 -62
- data/test-with-deprecations/if_test.rb +0 -79
- data/test-with-deprecations/include_exclude_test.rb +0 -88
- data/test-with-deprecations/inherit_test.rb +0 -159
- data/test-with-deprecations/inline_test.rb +0 -272
- data/test-with-deprecations/instance_test.rb +0 -266
- data/test-with-deprecations/is_representable_test.rb +0 -77
- data/test-with-deprecations/json_test.rb +0 -355
- data/test-with-deprecations/lonely_test.rb +0 -239
- data/test-with-deprecations/mongoid_test.rb +0 -31
- data/test-with-deprecations/nested_test.rb +0 -115
- data/test-with-deprecations/object_test.rb +0 -60
- data/test-with-deprecations/parse_pipeline_test.rb +0 -64
- data/test-with-deprecations/parse_strategy_test.rb +0 -279
- data/test-with-deprecations/pass_options_test.rb +0 -27
- data/test-with-deprecations/pipeline_test.rb +0 -277
- data/test-with-deprecations/populator_test.rb +0 -105
- data/test-with-deprecations/prepare_test.rb +0 -67
- data/test-with-deprecations/private_options_test.rb +0 -18
- data/test-with-deprecations/reader_writer_test.rb +0 -19
- data/test-with-deprecations/realistic_benchmark.rb +0 -115
- data/test-with-deprecations/render_nil_test.rb +0 -21
- data/test-with-deprecations/represent_test.rb +0 -88
- data/test-with-deprecations/representable_test.rb +0 -511
- data/test-with-deprecations/schema_test.rb +0 -148
- data/test-with-deprecations/serialize_deserialize_test.rb +0 -33
- data/test-with-deprecations/skip_test.rb +0 -81
- data/test-with-deprecations/stringify_hash_test.rb +0 -41
- data/test-with-deprecations/test_helper.rb +0 -135
- data/test-with-deprecations/test_helper_test.rb +0 -25
- data/test-with-deprecations/uncategorized_test.rb +0 -67
- data/test-with-deprecations/user_options_test.rb +0 -15
- data/test-with-deprecations/wrap_test.rb +0 -152
- data/test-with-deprecations/xml_bindings_test.rb +0 -62
- data/test-with-deprecations/xml_test.rb +0 -503
- data/test-with-deprecations/yaml_test.rb +0 -162
- data/test/parse_strategy_test.rb +0 -279
@@ -1,162 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class YamlTest < MiniTest::Spec
|
4
|
-
def self.yaml_representer(&block)
|
5
|
-
Module.new do
|
6
|
-
include Representable::YAML
|
7
|
-
instance_exec &block
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def yaml_representer(&block)
|
12
|
-
self.class.yaml_representer(&block)
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
describe "property" do
|
17
|
-
let (:yaml) { yaml_representer do property :best_song end }
|
18
|
-
|
19
|
-
let (:album) { Album.new.tap do |album|
|
20
|
-
album.best_song = "Liar"
|
21
|
-
end }
|
22
|
-
|
23
|
-
describe "#to_yaml" do
|
24
|
-
it "renders plain property" do
|
25
|
-
album.extend(yaml).to_yaml.must_equal(
|
26
|
-
"---
|
27
|
-
best_song: Liar
|
28
|
-
")
|
29
|
-
end
|
30
|
-
|
31
|
-
it "always renders values into strings" do
|
32
|
-
Album.new.tap { |a| a.best_song = 8675309 }.extend(yaml).to_yaml.must_equal(
|
33
|
-
"---
|
34
|
-
best_song: 8675309
|
35
|
-
"
|
36
|
-
)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
describe "#from_yaml" do
|
42
|
-
it "parses plain property" do
|
43
|
-
album.extend(yaml).from_yaml(
|
44
|
-
"---
|
45
|
-
best_song: This Song Is Recycled
|
46
|
-
").best_song.must_equal "This Song Is Recycled"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
describe "with :class and :extend" do
|
52
|
-
yaml_song = yaml_representer do property :name end
|
53
|
-
let (:yaml_album) { Module.new do
|
54
|
-
include Representable::YAML
|
55
|
-
property :best_song, :extend => yaml_song, :class => Song
|
56
|
-
end }
|
57
|
-
|
58
|
-
let (:album) { Album.new.tap do |album|
|
59
|
-
album.best_song = Song.new("Liar")
|
60
|
-
end }
|
61
|
-
|
62
|
-
|
63
|
-
describe "#to_yaml" do
|
64
|
-
it "renders embedded typed property" do
|
65
|
-
album.extend(yaml_album).to_yaml.must_equal "---
|
66
|
-
best_song:
|
67
|
-
name: Liar
|
68
|
-
"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "#from_yaml" do
|
73
|
-
it "parses embedded typed property" do
|
74
|
-
album.extend(yaml_album).from_yaml("---
|
75
|
-
best_song:
|
76
|
-
name: Go With Me
|
77
|
-
").must_equal Album.new(nil,Song.new("Go With Me"))
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
describe "collection" do
|
85
|
-
let (:yaml) { yaml_representer do collection :songs end }
|
86
|
-
|
87
|
-
let (:album) { Album.new.tap do |album|
|
88
|
-
album.songs = ["Jackhammer", "Terrible Man"]
|
89
|
-
end }
|
90
|
-
|
91
|
-
|
92
|
-
describe "#to_yaml" do
|
93
|
-
it "renders a block style list per default" do
|
94
|
-
album.extend(yaml).to_yaml.must_equal "---
|
95
|
-
songs:
|
96
|
-
- Jackhammer
|
97
|
-
- Terrible Man
|
98
|
-
"
|
99
|
-
end
|
100
|
-
|
101
|
-
it "renders a flow style list when :style => :flow set" do
|
102
|
-
yaml = yaml_representer { collection :songs, :style => :flow }
|
103
|
-
album.extend(yaml).to_yaml.must_equal "---
|
104
|
-
songs: [Jackhammer, Terrible Man]
|
105
|
-
"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
describe "#from_yaml" do
|
111
|
-
it "parses a block style list" do
|
112
|
-
album.extend(yaml).from_yaml("---
|
113
|
-
songs:
|
114
|
-
- Off Key Melody
|
115
|
-
- Sinking").must_equal Album.new(["Off Key Melody", "Sinking"])
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
it "parses a flow style list" do
|
120
|
-
album.extend(yaml).from_yaml("---
|
121
|
-
songs: [Off Key Melody, Sinking]").must_equal Album.new(["Off Key Melody", "Sinking"])
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
describe "with :class and :extend" do
|
127
|
-
let (:yaml_album) { Module.new do
|
128
|
-
include Representable::YAML
|
129
|
-
collection :songs, :class => Song do
|
130
|
-
property :name
|
131
|
-
property :track
|
132
|
-
end
|
133
|
-
end }
|
134
|
-
|
135
|
-
let (:album) { Album.new([Song.new("Liar", 1), Song.new("What I Know", 2)]) }
|
136
|
-
|
137
|
-
|
138
|
-
describe "#to_yaml" do
|
139
|
-
it "renders collection of typed property" do
|
140
|
-
album.extend(yaml_album).to_yaml.must_equal "---
|
141
|
-
songs:
|
142
|
-
- name: Liar
|
143
|
-
track: 1
|
144
|
-
- name: What I Know
|
145
|
-
track: 2
|
146
|
-
"
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
describe "#from_yaml" do
|
151
|
-
it "parses collection of typed property" do
|
152
|
-
album.extend(yaml_album).from_yaml("---
|
153
|
-
songs:
|
154
|
-
- name: One Shot Deal
|
155
|
-
track: 4
|
156
|
-
- name: Three Way Dance
|
157
|
-
track: 5").must_equal Album.new([Song.new("One Shot Deal", 4), Song.new("Three Way Dance", 5)])
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
data/test/parse_strategy_test.rb
DELETED
@@ -1,279 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
# parse_strategy: :sync
|
4
|
-
# parse_strategy: :replace
|
5
|
-
# parse_strategy: :find_or_instantiate ("expand" since we don't delete existing unmatched in target)
|
6
|
-
|
7
|
-
|
8
|
-
class ParseStrategySyncTest < BaseTest
|
9
|
-
for_formats(
|
10
|
-
:hash => [Representable::Hash, {"song"=>{"title"=>"Resist Stance"}}, {"song"=>{"title"=>"Suffer"}}],
|
11
|
-
:xml => [Representable::XML, "<open_struct><song><title>Resist Stance</title></song></open_struct>", "<open_struct><song><title>Suffer</title></song></open_struct>",],
|
12
|
-
:yaml => [Representable::YAML, "---\nsong:\n title: Resist Stance\n", "---\nsong:\n title: Suffer\n"],
|
13
|
-
) do |format, mod, output, input|
|
14
|
-
|
15
|
-
describe "[#{format}] property with parse_strategy: :sync" do # TODO: introduce :representable option?
|
16
|
-
let (:format) { format }
|
17
|
-
|
18
|
-
representer!(:module => mod, :name => :song_representer) do
|
19
|
-
property :title
|
20
|
-
self.representation_wrap = :song if format == :xml
|
21
|
-
end
|
22
|
-
|
23
|
-
representer!(:inject => :song_representer, :module => mod) do
|
24
|
-
property :song, :parse_strategy => :sync, :extend => song_representer
|
25
|
-
end
|
26
|
-
|
27
|
-
let (:hit) { hit = OpenStruct.new(:song => song).extend(representer) }
|
28
|
-
|
29
|
-
it "calls #to_hash on song instance, nothing else" do
|
30
|
-
render(hit).must_equal_document(output)
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
it "calls #from_hash on the existing song instance, nothing else" do
|
35
|
-
song_id = hit.song.object_id
|
36
|
-
|
37
|
-
parse(hit, input)
|
38
|
-
|
39
|
-
hit.song.title.must_equal "Suffer"
|
40
|
-
hit.song.object_id.must_equal song_id
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# FIXME: there's a bug with XML and the collection name!
|
46
|
-
for_formats(
|
47
|
-
:hash => [Representable::Hash, {"songs"=>[{"title"=>"Resist Stance"}]}, {"songs"=>[{"title"=>"Suffer"}]}],
|
48
|
-
#:json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"],
|
49
|
-
:xml => [Representable::XML, "<open_struct><song><title>Resist Stance</title></song></open_struct>", "<open_struct><songs><title>Suffer</title></songs></open_struct>"],
|
50
|
-
:yaml => [Representable::YAML, "---\nsongs:\n- title: Resist Stance\n", "---\nsongs:\n- title: Suffer\n"],
|
51
|
-
) do |format, mod, output, input|
|
52
|
-
|
53
|
-
describe "[#{format}] collection with :parse_strategy: :sync" do # TODO: introduce :representable option?
|
54
|
-
let (:format) { format }
|
55
|
-
representer!(:module => mod, :name => :song_representer) do
|
56
|
-
property :title
|
57
|
-
self.representation_wrap = :song if format == :xml
|
58
|
-
end
|
59
|
-
|
60
|
-
representer!(:inject => :song_representer, :module => mod) do
|
61
|
-
collection :songs, :parse_strategy => :sync, :extend => song_representer
|
62
|
-
end
|
63
|
-
|
64
|
-
let (:album) { OpenStruct.new(:songs => [song]).extend(representer) }
|
65
|
-
|
66
|
-
it "calls #to_hash on song instances, nothing else" do
|
67
|
-
render(album).must_equal_document(output)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "calls #from_hash on the existing song instance, nothing else" do
|
71
|
-
collection_id = album.songs.object_id
|
72
|
-
song = album.songs.first
|
73
|
-
song_id = song.object_id
|
74
|
-
|
75
|
-
parse(album, input)
|
76
|
-
|
77
|
-
album.songs.first.title.must_equal "Suffer"
|
78
|
-
song.title.must_equal "Suffer"
|
79
|
-
#album.songs.object_id.must_equal collection_id # TODO: don't replace!
|
80
|
-
song.object_id.must_equal song_id
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
# Sync errors, when model and incoming are not in sync.
|
87
|
-
describe ":sync with error" do
|
88
|
-
representer! do
|
89
|
-
property :song, :parse_strategy => :sync do
|
90
|
-
property :title
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# object.song is nil whereas the document contains one.
|
95
|
-
it do
|
96
|
-
assert_raises Representable::DeserializeError do
|
97
|
-
OpenStruct.new.extend(representer).from_hash({"song" => {"title" => "Perpetual"}})
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# Lonely Collection
|
105
|
-
for_formats(
|
106
|
-
:hash => [Representable::Hash::Collection, [{"title"=>"Resist Stance"}], [{"title"=>"Suffer"}]],
|
107
|
-
# :xml => [Representable::XML, "<open_struct><song><title>Resist Stance</title></song></open_struct>", "<open_struct><songs><title>Suffer</title></songs></open_struct>"],
|
108
|
-
) do |format, mod, output, input|
|
109
|
-
|
110
|
-
describe "[#{format}] lonely collection with :parse_strategy: :sync" do # TODO: introduce :representable option?
|
111
|
-
let (:format) { format }
|
112
|
-
representer!(:module => Representable::Hash, :name => :song_representer) do
|
113
|
-
property :title
|
114
|
-
self.representation_wrap = :song if format == :xml
|
115
|
-
end
|
116
|
-
|
117
|
-
representer!(:inject => :song_representer, :module => mod) do
|
118
|
-
items :parse_strategy => :sync, :extend => song_representer
|
119
|
-
end
|
120
|
-
|
121
|
-
let (:album) { [song].extend(representer) }
|
122
|
-
|
123
|
-
it "calls #to_hash on song instances, nothing else" do
|
124
|
-
render(album).must_equal_document(output)
|
125
|
-
end
|
126
|
-
|
127
|
-
it "calls #from_hash on the existing song instance, nothing else" do
|
128
|
-
#collection_id = album.object_id
|
129
|
-
song = album.first
|
130
|
-
song_id = song.object_id
|
131
|
-
|
132
|
-
parse(album, input)
|
133
|
-
|
134
|
-
album.first.title.must_equal "Suffer"
|
135
|
-
song.title.must_equal "Suffer"
|
136
|
-
song.object_id.must_equal song_id
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
|
143
|
-
class ParseStrategyFindOrInstantiateTest < BaseTest
|
144
|
-
# parse_strategy: :find_or_instantiate
|
145
|
-
|
146
|
-
Song = Struct.new(:id, :title)
|
147
|
-
Song.class_eval do
|
148
|
-
def self.find_by(attributes={})
|
149
|
-
return new(1, "Resist Stan") if attributes[:id]==1# we should return the same object here
|
150
|
-
new
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
representer!(:name => :song_representer) do
|
155
|
-
property :title
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
describe "collection" do
|
160
|
-
representer!(:inject => :song_representer) do
|
161
|
-
collection :songs, :parse_strategy => :find_or_instantiate, :extend => song_representer, :class => Song
|
162
|
-
end
|
163
|
-
|
164
|
-
let (:album) { Struct.new(:songs).new([]).extend(representer) }
|
165
|
-
|
166
|
-
|
167
|
-
it "replaces the existing collection with a new consisting of existing items or new items" do
|
168
|
-
songs_id = album.songs.object_id
|
169
|
-
|
170
|
-
album.from_hash({"songs"=>[{"id" => 1, "title"=>"Resist Stance"}, {"title"=>"Suffer"}]})
|
171
|
-
|
172
|
-
album.songs[0].title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
|
173
|
-
album.songs[0].id.must_equal 1
|
174
|
-
album.songs[1].title.must_equal "Suffer"
|
175
|
-
album.songs[1].id.must_equal nil
|
176
|
-
|
177
|
-
album.songs.object_id.wont_equal songs_id
|
178
|
-
end
|
179
|
-
|
180
|
-
# TODO: test with existing collection
|
181
|
-
end
|
182
|
-
|
183
|
-
|
184
|
-
describe "property" do
|
185
|
-
representer!(:inject => :song_representer) do
|
186
|
-
property :song, :parse_strategy => :find_or_instantiate, :extend => song_representer, :class => Song
|
187
|
-
end
|
188
|
-
|
189
|
-
let (:album) { Struct.new(:song).new.extend(representer) }
|
190
|
-
|
191
|
-
|
192
|
-
it "finds song by id" do
|
193
|
-
album.from_hash({"song"=>{"id" => 1, "title"=>"Resist Stance"}})
|
194
|
-
|
195
|
-
album.song.title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
|
196
|
-
album.song.id.must_equal 1
|
197
|
-
end
|
198
|
-
|
199
|
-
it "creates song" do
|
200
|
-
album.from_hash({"song"=>{"title"=>"Off The Track"}})
|
201
|
-
|
202
|
-
album.song.title.must_equal "Off The Track"
|
203
|
-
album.song.id.must_equal nil
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
|
208
|
-
describe "property with dynamic :class" do
|
209
|
-
representer!(:inject => :song_representer) do
|
210
|
-
property :song, :parse_strategy => :find_or_instantiate, :extend => song_representer,
|
211
|
-
:class => lambda { |options| options[:fragment]["class"] }
|
212
|
-
end
|
213
|
-
|
214
|
-
let (:album) { Struct.new(:song).new.extend(representer) }
|
215
|
-
|
216
|
-
|
217
|
-
it "finds song by id" do
|
218
|
-
album.from_hash({"song"=>{"id" => 1, "title"=>"Resist Stance", "class"=>Song}})
|
219
|
-
|
220
|
-
album.song.title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
|
221
|
-
album.song.id.must_equal 1
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
|
227
|
-
class ParseStrategyLambdaTest < MiniTest::Spec
|
228
|
-
Song = Struct.new(:id, :title)
|
229
|
-
Song.class_eval do
|
230
|
-
def self.find_by(attributes={})
|
231
|
-
return new(1, "Resist Stan") if attributes[:id]==1# we should return the same object here
|
232
|
-
new
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
representer!(:name => :song_representer) do
|
237
|
-
property :title
|
238
|
-
end
|
239
|
-
|
240
|
-
# property with instance: lambda, using representable's setter. # TODO: that should be handled better via my api.
|
241
|
-
describe "property parse_strategy: lambda, representable: false" do
|
242
|
-
representer! do
|
243
|
-
property :title,
|
244
|
-
:instance => lambda { |options| options[:fragment].to_s }, # this will still call song.title= "8675309".
|
245
|
-
:representable => false # don't call object.from_hash
|
246
|
-
end
|
247
|
-
|
248
|
-
let (:song) { Song.new(nil, nil) }
|
249
|
-
it { song.extend(representer).from_hash("title" => 8675309).title.must_equal "8675309" }
|
250
|
-
end
|
251
|
-
|
252
|
-
|
253
|
-
describe "collection" do
|
254
|
-
representer!(:inject => :song_representer) do
|
255
|
-
collection :songs, :parse_strategy => lambda { |options|
|
256
|
-
songs << song = Song.new
|
257
|
-
song
|
258
|
-
}, :extend => song_representer
|
259
|
-
end
|
260
|
-
|
261
|
-
let (:album) { Struct.new(:songs).new([Song.new(1, "A Walk")]).extend(representer) }
|
262
|
-
|
263
|
-
|
264
|
-
it "adds to existing collection" do
|
265
|
-
songs_id = album.songs.object_id
|
266
|
-
|
267
|
-
album.from_hash({"songs"=>[{"title"=>"Resist Stance"}]})
|
268
|
-
|
269
|
-
album.songs[0].title.must_equal "A Walk" # note how title is updated from "Resist Stan"
|
270
|
-
album.songs[0].id.must_equal 1
|
271
|
-
album.songs[1].title.must_equal "Resist Stance"
|
272
|
-
album.songs[1].id.must_equal nil
|
273
|
-
|
274
|
-
album.songs.object_id.must_equal songs_id
|
275
|
-
end
|
276
|
-
|
277
|
-
# TODO: test with existing collection
|
278
|
-
end
|
279
|
-
end
|