rmodel 0.4.0.dev → 1.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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +4 -0
  4. data/.travis.yml +4 -3
  5. data/README.md +119 -153
  6. data/Rakefile +1 -2
  7. data/examples/mongo_embedded.rb +27 -21
  8. data/examples/scopes.rb +28 -0
  9. data/examples/sql_repository.rb +14 -26
  10. data/examples/timestamps.rb +7 -10
  11. data/examples/webapp/models/task.rb +9 -0
  12. data/examples/webapp/repositories/task_repository.rb +14 -0
  13. data/examples/webapp/server.rb +25 -0
  14. data/examples/webapp/support/mappers.rb +11 -0
  15. data/examples/webapp/support/repositories.rb +12 -0
  16. data/examples/webapp/support/sources.rb +13 -0
  17. data/examples/webapp/views/index.erb +20 -0
  18. data/lib/rmodel.rb +11 -8
  19. data/lib/rmodel/array_mapper.rb +23 -0
  20. data/lib/rmodel/base_mapper.rb +56 -0
  21. data/lib/rmodel/dummy_mapper.rb +15 -0
  22. data/lib/rmodel/mongo/mapper.rb +11 -0
  23. data/lib/rmodel/mongo/source.rb +50 -0
  24. data/lib/rmodel/repository.rb +36 -0
  25. data/lib/rmodel/repository_ext/scopable.rb +44 -0
  26. data/lib/rmodel/repository_ext/sugarable.rb +35 -0
  27. data/lib/rmodel/repository_ext/timestampable.rb +29 -0
  28. data/lib/rmodel/scope.rb +31 -0
  29. data/lib/rmodel/sequel/mapper.rb +6 -0
  30. data/lib/rmodel/sequel/source.rb +43 -0
  31. data/lib/rmodel/uni_hash.rb +16 -0
  32. data/lib/rmodel/version.rb +1 -1
  33. data/rmodel.gemspec +9 -3
  34. data/spec/rmodel/array_mapper_spec.rb +43 -0
  35. data/spec/rmodel/mongo/mapper_spec.rb +154 -0
  36. data/spec/rmodel/mongo/repository_spec.rb +16 -37
  37. data/spec/rmodel/mongo/source_spec.rb +113 -0
  38. data/spec/rmodel/sequel/mapper_spec.rb +57 -0
  39. data/spec/rmodel/sequel/repository_spec.rb +27 -38
  40. data/spec/rmodel/sequel/source_spec.rb +121 -0
  41. data/spec/shared/base_mapper.rb +39 -0
  42. data/spec/shared/clean_moped.rb +6 -2
  43. data/spec/shared/clean_sequel.rb +1 -1
  44. data/spec/shared/{repository_ext → repository}/crud.rb +20 -14
  45. data/spec/shared/repository/initialization.rb +39 -0
  46. data/spec/shared/repository/scopable.rb +137 -0
  47. data/spec/shared/repository/sugarable.rb +67 -0
  48. data/spec/shared/repository/timestampable.rb +137 -0
  49. data/spec/spec_helper.rb +17 -18
  50. metadata +120 -54
  51. data/examples/advanced_creation_of_repository.rb +0 -15
  52. data/examples/user.rb +0 -48
  53. data/lib/rmodel/base/repository.rb +0 -12
  54. data/lib/rmodel/base/repository_ext/sugarable.rb +0 -17
  55. data/lib/rmodel/base/repository_ext/timestampable.rb +0 -19
  56. data/lib/rmodel/mongo/repository.rb +0 -62
  57. data/lib/rmodel/mongo/repository_ext/query.rb +0 -34
  58. data/lib/rmodel/mongo/repository_ext/queryable.rb +0 -34
  59. data/lib/rmodel/mongo/setup.rb +0 -17
  60. data/lib/rmodel/mongo/simple_factory.rb +0 -78
  61. data/lib/rmodel/sequel/repository.rb +0 -61
  62. data/lib/rmodel/sequel/repository_ext/query.rb +0 -34
  63. data/lib/rmodel/sequel/repository_ext/queryable.rb +0 -30
  64. data/lib/rmodel/sequel/setup.rb +0 -12
  65. data/lib/rmodel/sequel/simple_factory.rb +0 -28
  66. data/lib/rmodel/setup.rb +0 -34
  67. data/spec/rmodel/mongo/repository_ext/queryable_spec.rb +0 -103
  68. data/spec/rmodel/mongo/repository_initialize_spec.rb +0 -174
  69. data/spec/rmodel/mongo/simple_factory_spec.rb +0 -195
  70. data/spec/rmodel/sequel/repository_ext/queryable_spec.rb +0 -114
  71. data/spec/rmodel/sequel/repository_initialize_spec.rb +0 -118
  72. data/spec/rmodel/sequel/simple_factory_spec.rb +0 -55
  73. data/spec/rmodel/setup_spec.rb +0 -54
  74. data/spec/shared/repository_ext/sugarable.rb +0 -44
  75. data/spec/shared/repository_ext/timestampable.rb +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfd28def37943474200080f54ed361a5a7ff742c
4
- data.tar.gz: 09936b026f1c1d5b232cafe8ccce68665726a0eb
3
+ metadata.gz: f1cabda8d90f129c91be844839240e35c667e866
4
+ data.tar.gz: 63d948217cbee488f8e93be4ef575ff838d9cabf
5
5
  SHA512:
6
- metadata.gz: d7857a21445b42cab34bb02205949cbb3204cd245fd59508204cfca4cfa6c74753f698d1d5ce56596559dc03dbbe5abd58ab6092ca486332b426c9e887d6fa94
7
- data.tar.gz: fd441d8e206b3d21eb0cc636c6c178e319f7fe05114b5924e44d0440ff1f6b572735642e4080a255729580a21a6353b960ef7f2520664f62def8d8632ba0273f
6
+ metadata.gz: 1eb0276bbb918397139567f1a3c081fa3de431eff3cbffb406f2aa0e524c4d862082d5d5155bad31ce2188d5cb66ea300272a3657edc4d8f1e3debbc54f03628
7
+ data.tar.gz: 85fa52294b5ee0e0cbed4fb23a443f995ef442114e9ec67738aabbf21b8d437506af32834b26de7fa64f9ff217096577ca4dea48972fb8afd9f61a9bbf691dcb
data/.gitignore CHANGED
@@ -21,3 +21,4 @@ tmp
21
21
  *.a
22
22
  mkmf.log
23
23
  rmodel_test.sqlite3
24
+ run.rb
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+ Style/AndOr:
4
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
- script: rspec
2
+ script: bundle exec rubocop -D && bundle exec rspec
3
3
  rvm:
4
4
  - 2.0.0
5
- - 2.1.7
6
- - 2.2.3
5
+ - 2.1.8
6
+ - 2.2.4
7
+ - 2.3.0
7
8
  services:
8
9
  - mongodb
data/README.md CHANGED
@@ -20,6 +20,7 @@ The main thoughts of it are:
20
20
 
21
21
  * let you models be simple and independent of the persistent layer,
22
22
  * be able to switch the persistent layer at any moment,
23
+ * be able to use different databases for different entities,
23
24
  * keep the simplicity of the Active Record pattern by default,
24
25
  * be able to implement any type of persistence: SQL, NoSQL, files, HTTP etc.
25
26
 
@@ -27,13 +28,13 @@ It consists of 3 major components:
27
28
 
28
29
  1. **Entities**; ex.: User, Order etc.
29
30
  2. **Repositories**, which are used to fetch, save and delete entities; ex.: UserRepository, OrderRepository
30
- 3. **Factories**, which play the role of mappers.
31
+ 3. **Mappers**, which are used to serialize/deserialize entities to/from database tuples.
31
32
 
32
33
  Basic implemented features:
33
34
 
34
- 1. CRUD operations: `find`, `insert`, `update`, `remove`;
35
- 2. Scopes: `userRepository.query.recent.sorted`
36
- 3. Query-based operations: `userRepository.query.recent.remove`
35
+ 1. CRUD operations: `find`, `insert`, `update`, `destroy`;
36
+ 2. Scopes: `repo.fetch.recent.sorted`;
37
+ 3. Query-based operations: `repo.fetch.recent.delete_all`.
37
38
 
38
39
  ## Installation
39
40
 
@@ -54,80 +55,78 @@ Or install it yourself as:
54
55
  Let's define an entity
55
56
 
56
57
  ```ruby
57
- class User
58
- attr_accessor :id, :name, :email
59
- end
58
+ User = Struct.new(:id, :name, :email)
60
59
  ```
61
60
 
62
- As you see it's a plain ruby class with attributes. It must have either the zero-argument `#initialize` method or no `#initialize` at all.
61
+ As you see it's a PORO (Plain Old Ruby Objects), a class which inherits from nothing.
62
+ It must have either the zero-argument `#initialize` method or no `#initialize` at all.
63
63
 
64
64
  Of course we need a repository to save users.
65
65
 
66
66
  ```ruby
67
67
  require 'rmodel' # dont forget to require the gem
68
68
 
69
- class User
70
- attr_accessor :id, :name, :email
71
- end
72
-
73
- class UserRepository < Rmodel::Mongo::Repository
74
- end
69
+ User = Struct.new(:id, :name, :email)
75
70
 
76
- userRepository = UserRepository.new
77
- ```
78
- The code above raises the exception *Client driver is not setup (ArgumentError)*. UserRepository derives from Rmodel::Mongo::Repository, which uses the ruby mongo driver to access the database. We must provide the appropriate connection options. To do this we use the following code:
71
+ DB = Mongo::Client.new(['localhost'], database: 'test')
72
+ source = Rmodel::Mongo::Source.new(DB, :users)
79
73
 
80
- ```ruby
81
- require 'rmodel'
74
+ mapper = Rmodel::Mongo::Mapper.new(User).define_attributes(:name, :email)
82
75
 
83
- Rmodel.setup do
84
- client :default, { hosts: [ 'localhost' ], database: 'test' }
85
- end
76
+ user_repository = Rmodel::Repository.new(source, mapper)
86
77
  ```
87
78
 
88
- The `:default` client is used by every repository that doesn't specify it's client explicitly.
79
+ Here 3 main components of Rmodel are described:
89
80
 
90
- Run the code again and get another error *Factory can not be guessed (ArgumentError)*. The factory is used to convert the array of database tuples (hashes) to the array of User objects.
91
-
92
- ```ruby
93
- class UserRepository < Rmodel::Mongo::Repository
94
- simple_factory User, :name, :email
95
- end
96
- ```
97
-
98
- The `simple_factory` class macro says that every database tuple will be straightforwardly converted to an instance of User with attributes :id, :name and :email. There is no need to specify :id, because it's required.
81
+ 1. `source` points to the `users` collection withing MongoDB.
82
+ 2. `mapper` is an example of a mapper class instance. It's methods
83
+ `#initialize` and `define_attributes` are used to declare the mapping rules
84
+ (User -> Hash and Hash -> User). It's a rather easy mapper. Every database
85
+ tuple is straightforwardly converted to an instance of User with attributes
86
+ :id, :name and :email. There is no need to specify :id.
87
+ 3. Finally, `user_repository` takes `source` and `mapper` and makes all magic
88
+ about fetching and saving users from/to the database.
99
89
 
100
90
  ### CRUD
101
91
 
102
92
  Let's create and insert several users.
103
93
 
104
94
  ```ruby
105
- john = User.new('John', 'john@example.com')
106
- bill = User.new('Bill', 'bill@example.com')
107
- bob = User.new('Bob', 'bob@example.com')
95
+ john = User.new(nil, 'John', 'john@example.com')
96
+ bill = User.new(nil, 'Bill', 'bill@example.com')
97
+ bob = User.new(nil, 'Bob', 'bob@example.com')
108
98
 
109
- userRepository.insert(john)
110
- userRepository.insert(bill)
111
- userRepository.insert(bob)
99
+ user_repository.insert(john)
100
+ user_repository.insert(bill)
101
+ user_repository.insert(bob)
112
102
  ```
113
103
 
114
- Now you can check you `test` database. There are 3 new users there. Print the `john`. As you can see it's got the `@id`.
104
+ Now you can check you `test` database. There must be 3 new users there. Print
105
+ the `john`. As you can see it's got the `@id`.
115
106
 
116
107
  ```ruby
117
108
  p john
118
- #<User:0x00... @name="John", @email="john@example.com", @id=BSON::ObjectId('562a...')>
109
+ #<struct User id=BSON::ObjectId('...'), name="John", email="john@example.com">
119
110
  ```
120
111
 
121
- Let's update John and remove Bob.
112
+ Let's update John and destroy Bob.
122
113
 
123
114
  ```ruby
124
115
  john.name = 'John Smith'
125
- userRepository.update(john)
116
+ user_repository.update(john)
117
+
118
+ user_repository.destroy(bob)
119
+
120
+ p user_repository.find(john.id) # <struct User id=BSON::ObjectId('...'), ...>
121
+ p user_repository.find(bob.id) # nil
122
+ ```
126
123
 
127
- userRepository.remove(bob)
124
+ The `insert` method is polysemantic. All options below are valid.
128
125
 
129
- p userRepository.find(john.id) # #<User:0x000000037237d0 @name="John Smith" ... >
130
- p userRepository.find(bob.id) # nil
126
+ ```ruby
127
+ repo.insert(object)
128
+ repo.insert([ object1, object2, object3 ])
129
+ repo.insert(object1, object2, object3)
131
130
  ```
132
131
 
133
132
  ### Scopes
@@ -135,9 +134,7 @@ p userRepository.find(bob.id) # nil
135
134
  Scopes are defined inside the repository.
136
135
 
137
136
  ```ruby
138
- class UserRepository < Rmodel::Mongo::Repository
139
- simple_factory User, :name, :email
140
-
137
+ class UserRepository < Rmodel::Repository
141
138
  scope :have_email do
142
139
  where(email: { '$exists' => true })
143
140
  end
@@ -147,24 +144,38 @@ class UserRepository < Rmodel::Mongo::Repository
147
144
  end
148
145
  end
149
146
 
150
- userRepository.query.start_with('b').to_a
147
+ repo = UserRepository.new(source, mapper)
148
+
149
+ p repo.fetch.start_with('b').to_a
151
150
  ```
152
151
 
153
152
  Of course you can chain scopes.
154
153
 
155
154
  ```ruby
156
- userRepository.query.start_with('b').have_email
155
+ p repo.fetch.start_with('b').have_email.to_a
157
156
  ```
158
157
 
159
- The result of the scope is Enumerable, so you can apply the #each method and others (map, select etc).
158
+ The result of the scope is Enumerable, so you can apply the #each method and
159
+ others (map, select etc).
160
160
 
161
- Inside the scopes you can use any methods supported by the driver (database client). In our case we use Origin (https://github.com/mongoid/origin) as a query builder for mongo.
161
+ Inside the scopes you can use any methods supported by the driver (database
162
+ connection). In our case we use Origin (https://github.com/mongoid/origin) as
163
+ a query builder for mongo.
162
164
 
163
165
  Also it's possible to use scopes to run the multi-row operations.
164
166
 
165
167
  ```ruby
166
- userRepository.query.have_email.remove
167
- p userRepository.query.count # 0
168
+ repo.fetch.have_email.delete_all # simply run the operation against the database
169
+ repo.fetch.have_email.destroy_all # extract users and run repo.destroy for the each one
170
+ p repo.fetch.count # 0
171
+ ```
172
+
173
+ If you have no scopes then just call
174
+
175
+ ```ruby
176
+ repo.all
177
+ repo.delete_all
178
+ repo.destroy_all
168
179
  ```
169
180
 
170
181
  ### Timestamps
@@ -172,14 +183,20 @@ p userRepository.query.count # 0
172
183
  Here is an example how to track the time, when the entity was created and updated.
173
184
 
174
185
  ```ruby
186
+ require 'rmodel'
187
+
188
+ DB = Mongo::Client.new(['localhost'], database: 'test')
189
+ source = Rmodel::Mongo::Source.new(DB, :things)
190
+
175
191
  class Thing
176
192
  attr_accessor :id, :name, :created_at, :updated_at
177
193
  end
178
194
 
179
- class ThingRepository < Rmodel::Mongo::Repository
180
- simple_factory Thing, :name, :created_at, :updated_at
181
- end
182
- repo = ThingRepository.new
195
+ mapper = Rmodel::Mongo::Mapper.new(Thing)
196
+ .define_attribute(:name)
197
+ .define_timestamps
198
+
199
+ repo = Rmodel::Repository.new(source, mapper)
183
200
 
184
201
  thing = Thing.new
185
202
  thing.name = 'chair'
@@ -208,28 +225,6 @@ If the object has no not-nil id then it gets inserted. Otherwise it gets updated
208
225
  The `find!` method works like the simple `find`
209
226
  , but instead of nil it raises the Rmodel::NotFound error.
210
227
 
211
- ### Advanced creation of repository
212
-
213
- ```ruby
214
- require 'rmodel'
215
-
216
- class Thing
217
- attr_accessor :id, :name
218
- end
219
-
220
- class ThingRepository < Rmodel::Mongo::Repository
221
- end
222
-
223
- client = Mongo::Client.new([ 'localhost:27017' ], database: 'test')
224
- collection = :things
225
- factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name)
226
-
227
- repo = ThingRepository.new(client, collection, factory)
228
- repo.find(1)
229
- ```
230
-
231
- The `factory` is an object, which has 2 methods: `#fromHash(hash)` and `#toHash(object)`.
232
-
233
228
  ### SQL repository
234
229
 
235
230
  SQL amenities is based on the Sequel gem (http://sequel.jeremyevans.net/).
@@ -242,50 +237,38 @@ Below you can the the example how to setup Rmodel for any supported SQL database
242
237
  ```ruby
243
238
  require 'rmodel'
244
239
 
245
- Rmodel.setup do
246
- client :default, { adapter: 'sqlite', database: 'rmodel_test.sqlite3' }
247
- end
240
+ DB = Sequel.connect(adapter: 'sqlite', database: 'rmodel_test.sqlite3')
241
+ source = Rmodel::Sequel::Source.new(DB, :things)
248
242
 
249
- client = Rmodel.setup.establish_sequel_client(:default)
250
- client.drop_table? :things
251
- client.create_table :things do
243
+ DB.drop_table? :things
244
+ DB.create_table :things do
252
245
  primary_key :id
253
246
  String :name
254
247
  Float :price
255
248
  end
256
249
 
257
- class Thing
258
- attr_accessor :id, :name, :price
259
-
260
- def initialize(name = nil, price = nil)
261
- self.name = name
262
- self.price = price
263
- end
264
- end
265
-
266
- class ThingRepository < Rmodel::Sequel::Repository
267
- simple_factory Thing, :name, :price
250
+ Thing = Struct.new :id, :name, :price
251
+ mapper = Rmodel::Sequel::Mapper.new(Thing).define_attributes(:name, :price)
268
252
 
253
+ class ThingRepository < Rmodel::Repository
269
254
  scope :worth_more_than do |amount|
270
255
  # use Sequel dataset filtering http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
271
256
  where { price >= amount }
272
257
  end
273
258
  end
274
259
 
275
- repo = ThingRepository.new
276
- repo.insert Thing.new('iPod', 200)
277
- repo.insert Thing.new('iPhone', 300)
278
- repo.insert Thing.new('iPad', 500)
260
+ repo = ThingRepository.new(source, mapper)
261
+ repo.insert Thing.new(nil, 'iPod', 200)
262
+ repo.insert Thing.new(nil, 'iPhone', 300)
263
+ repo.insert Thing.new(nil, 'iPad', 500)
279
264
 
280
- p repo.query.count # 3
281
- p repo.query.worth_more_than(400).count # 1
282
- p repo.query.worth_more_than(400).to_sql
265
+ p repo.fetch.count # 3
266
+ p repo.fetch.worth_more_than(400).count # 1
283
267
  ```
284
268
 
285
269
  ### Embedded documents in MongoDB
286
270
 
287
- Let's assume that we have the `flats` collection and every documents reveals
288
- the following structure.
271
+ Let's assume that we have the `flats` collection and every documents reveals the following structure.
289
272
 
290
273
  ```js
291
274
  > db.flats.findOne()
@@ -325,66 +308,49 @@ the following structure.
325
308
  }
326
309
  ```
327
310
 
328
- We need a rather complicated factory to build such object. Here is the example how we can map nested embedded documents with SimpleFactory.
311
+ We need a rather complicated mapper to build such object. Here is the example how we can map nested embedded documents with Rmodel::Mongo::Mapper.
312
+
313
+ The idea is easy:
314
+ * define primitive mappers,
315
+ * use them in declaration of composite mappers.
329
316
 
330
317
  ```ruby
331
- Owner = Struct.new(:id, :first_name, :last_name)
332
- Room = Struct.new(:id, :name, :square, :bed)
318
+ require 'rmodel'
319
+
320
+ DB = Mongo::Client.new(['localhost'], database: 'test')
321
+ source = Rmodel::Mongo::Source.new(DB, :flats)
322
+
323
+ Owner = Struct.new(:first_name, :last_name)
324
+ Bed = Struct.new(:type)
325
+ Room = Struct.new(:name, :square, :bed)
333
326
  Flat = Struct.new(:id, :address, :rooms, :owner)
334
- Bed = Struct.new(:id, :type)
335
-
336
- class FlatRepository < Rmodel::Mongo::Repository
337
- simple_factory Flat, :address do
338
- embeds_many :rooms, simple_factory(Room, :name, :square) do
339
- embeds_one :bed, simple_factory(Bed, :type)
340
- end
341
- embeds_one :owner, simple_factory(Owner, :first_name, :last_name)
342
- end
343
- end
344
- ```
345
327
 
346
- 1. The row `simple_factory Flat, :address` create a factory for Flat.
347
- * It takes a block, where the detailed declaration goes.
348
- * `embeds_many` and `embeds_one` are methods of the created factory.
349
- 2. The row `embeds_many :rooms, simple_factory(Room, :name, :square)` describes the embedded array of rooms within the flat.
350
- * The first argument `:room` is the name of the flat attribute (`flat.rooms`).
351
- * The second argument is another simple factory for the Room class.
352
- * The row `embeds_many :rooms` takes the block, that described nested embedded documents for the Room factory.
353
- 3. etc.
328
+ owner_mapper = Rmodel::Mongo::Mapper.new(Owner)
329
+ .define_attributes(:first_name, :last_name)
354
330
 
355
- The full example.
331
+ bed_mapper = Rmodel::Mongo::Mapper.new(Bed).define_attribute(:type)
356
332
 
357
- ```ruby
358
- require 'rmodel'
333
+ room_mapper = Rmodel::Mongo::Mapper.new(Room)
334
+ .define_attributes(:name, :square)
335
+ .define_attribute(:bed, bed_mapper)
359
336
 
360
- Rmodel.setup do
361
- client :default, { hosts: [ 'localhost'], database: 'test' }
362
- end
337
+ rooms_mapper = Rmodel::ArrayMapper.new(room_mapper)
338
+ flat_mapper = Rmodel::Mongo::Mapper.new(Flat)
339
+ .define_attribute(:address)
340
+ .define_attribute(:rooms, rooms_mapper)
341
+ .define_attribute(:owner, owner_mapper)
363
342
 
364
- Owner = Struct.new(:id, :first_name, :last_name)
365
- Room = Struct.new(:id, :name, :square, :bed)
366
- Flat = Struct.new(:id, :address, :rooms, :owner)
367
- Bed = Struct.new(:id, :type)
368
-
369
- class FlatRepository < Rmodel::Mongo::Repository
370
- simple_factory Flat, :address do
371
- embeds_many :rooms, simple_factory(Room, :name, :square) do
372
- embeds_one :bed, simple_factory(Bed, :type)
373
- end
374
- embeds_one :owner, simple_factory(Owner, :first_name, :last_name)
375
- end
376
- end
377
- repo = FlatRepository.new
378
- repo.query.remove
343
+ repo = Rmodel::Repository.new(source, flat_mapper)
344
+ repo.delete_all
379
345
 
380
346
  flat = Flat.new
381
347
  flat.address = 'Googleplex, Mountain View, California, U.S'
382
348
  flat.rooms = [
383
- Room.new(nil, 'dining room', 150),
384
- Room.new(nil, 'sleeping room #1', 50, Bed.new(nil, 'single')),
385
- Room.new(nil, 'sleeping room #2', 20, Bed.new(nil, 'king-size'))
349
+ Room.new('dining room', 150),
350
+ Room.new('sleeping room #1', 50, Bed.new('single')),
351
+ Room.new('sleeping room #2', 20, Bed.new('king-size'))
386
352
  ]
387
- flat.owner = Owner.new(nil, 'John', 'Doe')
353
+ flat.owner = Owner.new('John', 'Doe')
388
354
 
389
355
  repo.insert(flat)
390
356
  p repo.find(flat.id)