rmodel 0.4.0.dev → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)