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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +4 -3
- data/README.md +119 -153
- data/Rakefile +1 -2
- data/examples/mongo_embedded.rb +27 -21
- data/examples/scopes.rb +28 -0
- data/examples/sql_repository.rb +14 -26
- data/examples/timestamps.rb +7 -10
- data/examples/webapp/models/task.rb +9 -0
- data/examples/webapp/repositories/task_repository.rb +14 -0
- data/examples/webapp/server.rb +25 -0
- data/examples/webapp/support/mappers.rb +11 -0
- data/examples/webapp/support/repositories.rb +12 -0
- data/examples/webapp/support/sources.rb +13 -0
- data/examples/webapp/views/index.erb +20 -0
- data/lib/rmodel.rb +11 -8
- data/lib/rmodel/array_mapper.rb +23 -0
- data/lib/rmodel/base_mapper.rb +56 -0
- data/lib/rmodel/dummy_mapper.rb +15 -0
- data/lib/rmodel/mongo/mapper.rb +11 -0
- data/lib/rmodel/mongo/source.rb +50 -0
- data/lib/rmodel/repository.rb +36 -0
- data/lib/rmodel/repository_ext/scopable.rb +44 -0
- data/lib/rmodel/repository_ext/sugarable.rb +35 -0
- data/lib/rmodel/repository_ext/timestampable.rb +29 -0
- data/lib/rmodel/scope.rb +31 -0
- data/lib/rmodel/sequel/mapper.rb +6 -0
- data/lib/rmodel/sequel/source.rb +43 -0
- data/lib/rmodel/uni_hash.rb +16 -0
- data/lib/rmodel/version.rb +1 -1
- data/rmodel.gemspec +9 -3
- data/spec/rmodel/array_mapper_spec.rb +43 -0
- data/spec/rmodel/mongo/mapper_spec.rb +154 -0
- data/spec/rmodel/mongo/repository_spec.rb +16 -37
- data/spec/rmodel/mongo/source_spec.rb +113 -0
- data/spec/rmodel/sequel/mapper_spec.rb +57 -0
- data/spec/rmodel/sequel/repository_spec.rb +27 -38
- data/spec/rmodel/sequel/source_spec.rb +121 -0
- data/spec/shared/base_mapper.rb +39 -0
- data/spec/shared/clean_moped.rb +6 -2
- data/spec/shared/clean_sequel.rb +1 -1
- data/spec/shared/{repository_ext → repository}/crud.rb +20 -14
- data/spec/shared/repository/initialization.rb +39 -0
- data/spec/shared/repository/scopable.rb +137 -0
- data/spec/shared/repository/sugarable.rb +67 -0
- data/spec/shared/repository/timestampable.rb +137 -0
- data/spec/spec_helper.rb +17 -18
- metadata +120 -54
- data/examples/advanced_creation_of_repository.rb +0 -15
- data/examples/user.rb +0 -48
- data/lib/rmodel/base/repository.rb +0 -12
- data/lib/rmodel/base/repository_ext/sugarable.rb +0 -17
- data/lib/rmodel/base/repository_ext/timestampable.rb +0 -19
- data/lib/rmodel/mongo/repository.rb +0 -62
- data/lib/rmodel/mongo/repository_ext/query.rb +0 -34
- data/lib/rmodel/mongo/repository_ext/queryable.rb +0 -34
- data/lib/rmodel/mongo/setup.rb +0 -17
- data/lib/rmodel/mongo/simple_factory.rb +0 -78
- data/lib/rmodel/sequel/repository.rb +0 -61
- data/lib/rmodel/sequel/repository_ext/query.rb +0 -34
- data/lib/rmodel/sequel/repository_ext/queryable.rb +0 -30
- data/lib/rmodel/sequel/setup.rb +0 -12
- data/lib/rmodel/sequel/simple_factory.rb +0 -28
- data/lib/rmodel/setup.rb +0 -34
- data/spec/rmodel/mongo/repository_ext/queryable_spec.rb +0 -103
- data/spec/rmodel/mongo/repository_initialize_spec.rb +0 -174
- data/spec/rmodel/mongo/simple_factory_spec.rb +0 -195
- data/spec/rmodel/sequel/repository_ext/queryable_spec.rb +0 -114
- data/spec/rmodel/sequel/repository_initialize_spec.rb +0 -118
- data/spec/rmodel/sequel/simple_factory_spec.rb +0 -55
- data/spec/rmodel/setup_spec.rb +0 -54
- data/spec/shared/repository_ext/sugarable.rb +0 -44
- data/spec/shared/repository_ext/timestampable.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1cabda8d90f129c91be844839240e35c667e866
|
4
|
+
data.tar.gz: 63d948217cbee488f8e93be4ef575ff838d9cabf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1eb0276bbb918397139567f1a3c081fa3de431eff3cbffb406f2aa0e524c4d862082d5d5155bad31ce2188d5cb66ea300272a3657edc4d8f1e3debbc54f03628
|
7
|
+
data.tar.gz: 85fa52294b5ee0e0cbed4fb23a443f995ef442114e9ec67738aabbf21b8d437506af32834b26de7fa64f9ff217096577ca4dea48972fb8afd9f61a9bbf691dcb
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
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. **
|
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`, `
|
35
|
-
2. Scopes: `
|
36
|
-
3. Query-based operations: `
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
81
|
-
require 'rmodel'
|
74
|
+
mapper = Rmodel::Mongo::Mapper.new(User).define_attributes(:name, :email)
|
82
75
|
|
83
|
-
Rmodel.
|
84
|
-
client :default, { hosts: [ 'localhost' ], database: 'test' }
|
85
|
-
end
|
76
|
+
user_repository = Rmodel::Repository.new(source, mapper)
|
86
77
|
```
|
87
78
|
|
88
|
-
|
79
|
+
Here 3 main components of Rmodel are described:
|
89
80
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
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
|
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
|
109
|
+
#<struct User id=BSON::ObjectId('...'), name="John", email="john@example.com">
|
119
110
|
```
|
120
111
|
|
121
|
-
Let's update John and
|
112
|
+
Let's update John and destroy Bob.
|
122
113
|
|
123
114
|
```ruby
|
124
115
|
john.name = 'John Smith'
|
125
|
-
|
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
|
-
|
124
|
+
The `insert` method is polysemantic. All options below are valid.
|
128
125
|
|
129
|
-
|
130
|
-
|
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::
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
167
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
-
|
246
|
-
|
247
|
-
end
|
240
|
+
DB = Sequel.connect(adapter: 'sqlite', database: 'rmodel_test.sqlite3')
|
241
|
+
source = Rmodel::Sequel::Source.new(DB, :things)
|
248
242
|
|
249
|
-
|
250
|
-
|
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
|
-
|
258
|
-
|
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.
|
281
|
-
p repo.
|
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
|
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
|
-
|
332
|
-
|
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
|
-
|
347
|
-
|
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
|
-
|
331
|
+
bed_mapper = Rmodel::Mongo::Mapper.new(Bed).define_attribute(:type)
|
356
332
|
|
357
|
-
|
358
|
-
|
333
|
+
room_mapper = Rmodel::Mongo::Mapper.new(Room)
|
334
|
+
.define_attributes(:name, :square)
|
335
|
+
.define_attribute(:bed, bed_mapper)
|
359
336
|
|
360
|
-
Rmodel.
|
361
|
-
|
362
|
-
|
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
|
-
|
365
|
-
|
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(
|
384
|
-
Room.new(
|
385
|
-
Room.new(
|
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(
|
353
|
+
flat.owner = Owner.new('John', 'Doe')
|
388
354
|
|
389
355
|
repo.insert(flat)
|
390
356
|
p repo.find(flat.id)
|