lotus-model 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -168
- data/EXAMPLE.md +35 -40
- data/README.md +152 -12
- data/lib/lotus/entity.rb +107 -24
- data/lib/lotus/model.rb +169 -8
- data/lib/lotus/model/adapters/abstract.rb +3 -2
- data/lib/lotus/model/adapters/file_system_adapter.rb +272 -0
- data/lib/lotus/model/adapters/implementation.rb +1 -1
- data/lib/lotus/model/adapters/memory/command.rb +2 -1
- data/lib/lotus/model/adapters/memory/query.rb +49 -10
- data/lib/lotus/model/adapters/memory_adapter.rb +13 -5
- data/lib/lotus/model/adapters/null_adapter.rb +20 -0
- data/lib/lotus/model/adapters/sql/collection.rb +18 -18
- data/lib/lotus/model/adapters/sql/query.rb +23 -3
- data/lib/lotus/model/config/adapter.rb +108 -0
- data/lib/lotus/model/config/mapper.rb +45 -0
- data/lib/lotus/model/configuration.rb +187 -0
- data/lib/lotus/model/mapper.rb +26 -3
- data/lib/lotus/model/mapping.rb +24 -0
- data/lib/lotus/model/mapping/coercer.rb +3 -3
- data/lib/lotus/model/mapping/coercions.rb +30 -0
- data/lib/lotus/model/mapping/collection.rb +127 -9
- data/lib/lotus/model/version.rb +1 -1
- data/lib/lotus/repository.rb +19 -11
- data/lotus-model.gemspec +2 -1
- metadata +16 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae1042adba4d02f0e12ad69ccb025061603a3b24
|
4
|
+
data.tar.gz: 0c976c625163281c1d8e03409f1ff79db9838ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91017c7d534780cb9dd7004969835762c1185596845ac8b9a968d762af7913709ddfbd17d5b68daea1c6851152a3b45e6135d2b23b65f548619b77f22a5d8ec5
|
7
|
+
data.tar.gz: 5a40607f634bdea786e3d2f6005d14acc5f27160f01bb6cb0a0f028414a1163bb0612131ba8554cd1264bcfa7086be853054f7e246d55ff17d9797b7ebe83335
|
data/CHANGELOG.md
CHANGED
@@ -1,168 +1,52 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
a9df2ec 2014-04-15 **Luca Guidi** Load Mapper when the framework is loaded
|
55
|
-
|
56
|
-
de21101 2014-04-15 **Luca Guidi** Extracted Mapping::Collection::REPOSITORY_SUFFIX constant
|
57
|
-
|
58
|
-
b4ed0fe 2014-04-15 **Luca Guidi** Expose Mapper#load! to make Lotus::Model thread safe
|
59
|
-
|
60
|
-
0936848 2014-04-14 **Luca Guidi** Moved UnmappedCollectionError under a separated file
|
61
|
-
|
62
|
-
b6e49ff 2014-04-14 **Luca Guidi** Removed serialization responsibility from Mapper
|
63
|
-
|
64
|
-
9057fbd 2014-04-14 **Luca Guidi** Removed unnecessary conditional in test
|
65
|
-
|
66
|
-
43c462f 2014-04-14 **Luca Guidi** Renamed Lotus::Model::Mapping::Collection#key into #identity
|
67
|
-
|
68
|
-
af59039 2014-04-14 **Luca Guidi** Removed serialization responsibility from Sql::Command
|
69
|
-
|
70
|
-
92101bb 2014-04-14 **Luca Guidi** Removed deserialization responsibility from Sql::Query
|
71
|
-
|
72
|
-
04e9597 2014-04-14 **Luca Guidi** Rewritten Sql::Command, it now works on scoped queries
|
73
|
-
|
74
|
-
eb49746 2014-04-14 **Luca Guidi** Coerce with the right type the primary key for Repository.find
|
75
|
-
|
76
|
-
daf3e04 2014-04-14 **Luca Guidi** Implemented Command for mutation actions such as insert, update, delete. Removed serialization responsibility to the adapter. Removed unused code.
|
77
|
-
|
78
|
-
5ae8b11 2014-04-13 **Luca Guidi** Sql and Memory adapter are now using Query to serve #all, #find, #first, #last
|
79
|
-
|
80
|
-
b0a35c5 2014-04-13 **Luca Guidi** Implemented Query#asc and #desc
|
81
|
-
|
82
|
-
ef2d1fa 2014-04-13 **Luca Guidi** Make querying thread safe for MemoryAdapter
|
83
|
-
|
84
|
-
db7e699 2014-04-12 **Luca Guidi** Implemented Query#select
|
85
|
-
|
86
|
-
2d83581 2014-04-12 **Luca Guidi** Implemented Query#exist?
|
87
|
-
|
88
|
-
13cfde0 2014-04-12 **Luca Guidi** Implemented Query#exclude
|
89
|
-
|
90
|
-
79596de 2014-04-12 **Luca Guidi** Implemented Query#range
|
91
|
-
|
92
|
-
70df238 2014-04-12 **Luca Guidi** Implemented Query#interval
|
93
|
-
|
94
|
-
a0cbb8c 2014-04-12 **Luca Guidi** Implemented Query#min
|
95
|
-
|
96
|
-
41e3d69 2014-04-12 **Luca Guidi** Implemented Query#max
|
97
|
-
|
98
|
-
5577283 2014-04-12 **Luca Guidi** Changed the semantic of Query#average: let return a float if needed, handle strings and nil values
|
99
|
-
|
100
|
-
3e1bddc 2014-04-12 **Luca Guidi** Implemented Query#sum
|
101
|
-
|
102
|
-
97d0fb9 2014-04-12 **Luca Guidi** Implemented Lotus::Model::Adapters::Memory::Query#average
|
103
|
-
|
104
|
-
d7068b5 2014-04-12 **Luca Guidi** Implemented Lotus::Model::Adapters::Sql::Query#average
|
105
|
-
|
106
|
-
21abc27 2014-04-10 **Luca Guidi** Introduced Sql::Query
|
107
|
-
|
108
|
-
7b63b7f 2014-04-10 **Luca Guidi** Implemented Memory::Query#count
|
109
|
-
|
110
|
-
517a89a 2014-04-10 **Luca Guidi** Make the results of Repository queries lazy
|
111
|
-
|
112
|
-
e6a756c 2014-04-10 **Luca Guidi** Renamed adapters with the "Adapter" suffix, in order to keep namespaces free.
|
113
|
-
|
114
|
-
8a076b9 2014-04-10 **Luca Guidi** Implemented Query#or, #limit and #offset
|
115
|
-
|
116
|
-
388957d 2014-04-09 **Luca Guidi** Added tests for SQL adapter and implemented #where, #and and #order for all the adapters
|
117
|
-
|
118
|
-
5a97c12 2014-04-09 **Luca Guidi** Initial design for quering the datasource
|
119
|
-
|
120
|
-
6f804db 2014-04-08 **Luca Guidi** Use Lotus::Utils::Kernel conversions
|
121
|
-
|
122
|
-
4ae6967 2014-04-07 **Luca Guidi** Allow the mapper to specify the primary key of a collection with Lotus::Model::Mapper::Collection#key.
|
123
|
-
|
124
|
-
85def40 2014-04-07 **Luca Guidi** Extracted Lotus::Model::Adapters::Memory::Collection::PrimaryKey
|
125
|
-
|
126
|
-
ff8d6d4 2014-04-07 **Luca Guidi** Lotus::Repository.collection is now configured by the framework internals.
|
127
|
-
|
128
|
-
122e040 2014-04-02 **Luca Guidi** Introduced attributes mapping and (de)serializations policies based on it.
|
129
|
-
|
130
|
-
f925ebc 2014-03-26 **Luca Guidi** Lotus::Entity#id is always the primary key
|
131
|
-
|
132
|
-
0bf8bb5 2014-03-26 **Luca Guidi** Introduced Lotus::Model::Adapters::Sql
|
133
|
-
|
134
|
-
cfbed99 2014-03-26 **Luca Guidi** Lotus::Model::Repository => Lotus::Repository
|
135
|
-
|
136
|
-
3a72d68 2014-03-26 **Luca Guidi** Lotus::Model::Entity => Lotus::Entity
|
137
|
-
|
138
|
-
ed29d2d 2014-03-26 **Luca Guidi** Preload Lotus::Model::Repository
|
139
|
-
|
140
|
-
f1bda7f 2014-03-26 **Luca Guidi** Improved tests and better semantic for Lotus::Model::Repository
|
141
|
-
|
142
|
-
65b8e1a 2014-03-26 **Luca Guidi** Tests and thready safety for Lotus::Model::Adapters::Memory
|
143
|
-
|
144
|
-
63d9fc5 2014-03-26 **Luca Guidi** When generate Entity#initialize use class attribute 'attributes', instead of the homonym argument
|
145
|
-
|
146
|
-
442987d 2014-02-17 **Luca Guidi** Lotus::Model::Repository.find raises a Lotus::Model::RecordNotFound exception if it can't find a record, associated with the given ID
|
147
|
-
|
148
|
-
c2b94e0 2014-02-15 **Luca Guidi** Ensure memory adapted is able to find a record for a string id
|
149
|
-
|
150
|
-
b258ea0 2014-02-07 **Luca Guidi** Make Repository to work with entities
|
151
|
-
|
152
|
-
0371b41 2014-02-05 **Luca Guidi** Implemented Entity
|
153
|
-
|
154
|
-
9d2bfe3 2014-02-05 **Luca Guidi** Renamed "object" in "entity" in method signatures. Repositories and adapters work on objects that are aware of the identity's concept.
|
155
|
-
|
156
|
-
be0e4e0 2014-02-05 **Luca Guidi** Extracted Abstract adapter and made Memory to inherit from it
|
157
|
-
|
158
|
-
3ca28af 2014-02-05 **Luca Guidi** Let Repository to delegate operations to the current adapter
|
159
|
-
|
160
|
-
3318bd1 2014-02-05 **Luca Guidi** Made Repository methods to accept one object instead of a collection
|
161
|
-
|
162
|
-
baf378a 2014-02-05 **Luca Guidi** Implemented Repository.delete
|
163
|
-
|
164
|
-
1b8c0a0 2014-02-05 **Luca Guidi** Implemented Repository.persist, .create and .update
|
165
|
-
|
166
|
-
c6bde87 2014-02-05 **Luca Guidi** Implemented Repository.find
|
167
|
-
|
168
|
-
a45248f 2014-02-05 **Luca Guidi** Initial mess
|
1
|
+
# Lotus::Model
|
2
|
+
A persistence layer for Lotus
|
3
|
+
|
4
|
+
## v0.2.0 - 2014-12-23
|
5
|
+
### Added
|
6
|
+
- [Luca Guidi] Introduced file system adapter
|
7
|
+
– [Benny Klotz & Trung Lê] Introduced `Entity` inheritance of attributes
|
8
|
+
- [Trung Lê] Introduced `Entity#update` for bulk update of attributes
|
9
|
+
- [Luca Guidi] Improved error when try to use a repository which wasn't configured or when the framework wasn't loaded yet
|
10
|
+
- [Trung Lê] Introduced `Entity#to_h`
|
11
|
+
- [Trung Lê] Introduced `Lotus::Model.duplicate`
|
12
|
+
- [Trung Lê] Made `Lotus::Mapper` lazy
|
13
|
+
- [Trung Lê] Introduced thread safe autoloading for adapters
|
14
|
+
- [Felipe Sere] Add support for `Symbol` coercion
|
15
|
+
- [Celso Fernandes] Add support for `BigDecimal` coercion
|
16
|
+
- [Trung Lê] Introduced `Lotus::Model.load!` as entry point for loading
|
17
|
+
- [Trung Lê] Introduced `Mapper#repository` as DSL to associate a repository to a collection
|
18
|
+
- [Trung Lê & Tao Guo] Introduced `Configuration#mapping` as DSL to configure the mapping
|
19
|
+
- [Coen Wessels] Allow `where`, `exclude` and `or` to accept blocks
|
20
|
+
- [Trung Lê & Tao Guo] Introduced `Configuration#adapter` as DSL to configure the adapter
|
21
|
+
- [Trung Lê] Introduced `Lotus::Model::Configuration`
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
- [Trung Lê] Changed `Entity.attributes=` to `Entity.attributes`
|
25
|
+
- [Trung Lê] In case of missing entity, let `Repository#find` returns `nil` instead of raise an exception
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
- [Rik Tonnard] Ensure correct behavior of `#offset` in memory adapter
|
29
|
+
- [Benny Klotz] Ensure `Entity` to set the attributes even when the given Hash uses strings as keys
|
30
|
+
- [Ben Askins] Always return the entity from `Repository#persist`
|
31
|
+
- [Jeremy Stephens] Made `Memory::Query#where` and `#or` behave more like the SQL counter-part
|
32
|
+
|
33
|
+
## v0.1.2 - 2014-06-26
|
34
|
+
### Fixed
|
35
|
+
- [Stanislav Spiridonov] Ensure to require `'lotus/model/mapping/coercions'`
|
36
|
+
- [Krzysztof Zalewski] `Entity` defines `#id` accessor by default
|
37
|
+
|
38
|
+
|
39
|
+
## v0.1.1 - 2014-06-23
|
40
|
+
### Added
|
41
|
+
- [Luca Guidi] Introduced `Lotus::Model::Mapping::Coercions` in order to decouple from `Lotus::Utils::Kernel`
|
42
|
+
- [Luca Guidi] Official support for Ruby 2.1
|
43
|
+
|
44
|
+
## v0.1.0 - 2014-04-23
|
45
|
+
### Added
|
46
|
+
- [Luca Guidi] Allow to inject coercer into mapper
|
47
|
+
- [Luca Guidi] Introduced database mapping
|
48
|
+
- [Luca Guidi] Introduced `Lotus::Entity`
|
49
|
+
- [Luca Guidi] Introduced SQL adapter
|
50
|
+
- [Luca Guidi] Introduced memory adapter
|
51
|
+
– [Luca Guidi] Introduced adapters for repositories
|
52
|
+
- [Luca Guidi] Introduced `Lotus::Repository`
|
data/EXAMPLE.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Lotus::Model
|
2
2
|
|
3
|
-
This is a guide that helps you to
|
3
|
+
This is a guide that helps you to get started with [**Lotus::Model**](https://github.com/lotus/model).
|
4
4
|
You can find the full code source [here](https://gist.github.com/jodosha/11211048).
|
5
5
|
|
6
6
|
## Gems
|
@@ -18,8 +18,12 @@ Then we can fetch the dependencies with `bundle install`.
|
|
18
18
|
|
19
19
|
## Setup
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
<a name="connection-url"></a>
|
22
|
+
|
23
|
+
**Lotus::Model** doesn't have migrations.
|
24
|
+
For this example we will use [Sequel](http://sequel.jeremyevans.net).
|
25
|
+
We create the database first.
|
26
|
+
Then we create two tables: `authors` and `articles`.
|
23
27
|
|
24
28
|
```ruby
|
25
29
|
require 'bundler/setup'
|
@@ -60,7 +64,7 @@ end
|
|
60
64
|
|
61
65
|
class Article
|
62
66
|
include Lotus::Entity
|
63
|
-
|
67
|
+
attributes :author_id, :title, :comments_count, :published # id is implicit
|
64
68
|
|
65
69
|
def published?
|
66
70
|
!!published
|
@@ -111,7 +115,7 @@ class ArticleRepository
|
|
111
115
|
end
|
112
116
|
|
113
117
|
def self.best_article_ever
|
114
|
-
rank.limit(1)
|
118
|
+
rank.limit(1).first
|
115
119
|
end
|
116
120
|
|
117
121
|
def self.comments_average
|
@@ -120,48 +124,38 @@ class ArticleRepository
|
|
120
124
|
end
|
121
125
|
```
|
122
126
|
|
123
|
-
##
|
124
|
-
|
125
|
-
We create a correspondence between the database columns with the entities' attributes.
|
127
|
+
## Loading
|
126
128
|
|
127
129
|
```ruby
|
128
|
-
|
129
|
-
|
130
|
-
entity Author
|
131
|
-
|
132
|
-
attribute :id, Integer
|
133
|
-
attribute :name, String
|
134
|
-
end
|
130
|
+
Lotus::Model.configure do
|
131
|
+
adapter type: :sql, uri: connection_uri
|
135
132
|
|
136
|
-
|
137
|
-
|
133
|
+
mapping do
|
134
|
+
collection :authors do
|
135
|
+
entity Author
|
136
|
+
repository AuthorRepository
|
138
137
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
attribute :comments_count, Integer
|
143
|
-
attribute :published, Boolean
|
144
|
-
end
|
145
|
-
end
|
146
|
-
```
|
147
|
-
|
148
|
-
## Loading
|
138
|
+
attribute :id, Integer
|
139
|
+
attribute :name, String
|
140
|
+
end
|
149
141
|
|
150
|
-
|
151
|
-
|
152
|
-
|
142
|
+
collection :articles do
|
143
|
+
entity Article
|
144
|
+
repository ArticleRepository
|
153
145
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
146
|
+
attribute :id, Integer
|
147
|
+
attribute :author_id, Integer
|
148
|
+
attribute :title, String
|
149
|
+
attribute :comments_count, Integer
|
150
|
+
attribute :published, Boolean
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end.load!
|
160
154
|
```
|
161
155
|
|
162
156
|
## Persist
|
163
157
|
|
164
|
-
|
158
|
+
We instantiate and persist an `Author` and a few `Articles` for our example:
|
165
159
|
|
166
160
|
```ruby
|
167
161
|
author = Author.new(name: 'Luca')
|
@@ -181,7 +175,7 @@ end
|
|
181
175
|
|
182
176
|
## Query
|
183
177
|
|
184
|
-
We
|
178
|
+
We use the repositories to query the database and return the entities we're looking for:
|
185
179
|
|
186
180
|
```ruby
|
187
181
|
ArticleRepository.first # => return the first article
|
@@ -200,10 +194,11 @@ ArticleRepository.most_recent_by_author(author) # => most recent articles by an
|
|
200
194
|
ArticleRepository.most_recent_published_by_author(author) # => most recent published articles by an author
|
201
195
|
```
|
202
196
|
|
203
|
-
## Business
|
197
|
+
## Business Logic
|
204
198
|
|
205
199
|
As we've seen above, `Article` implements an API for publishing.
|
206
|
-
We
|
200
|
+
We use that logic to alter the state of an article (from draft to published).
|
201
|
+
We then use the repository to persist this new state.
|
207
202
|
|
208
203
|
```ruby
|
209
204
|
article = ArticleRepository.drafts.first
|
data/README.md
CHANGED
@@ -41,7 +41,9 @@ __Lotus::Model__ supports Ruby (MRI) 2+
|
|
41
41
|
|
42
42
|
Add this line to your application's Gemfile:
|
43
43
|
|
44
|
-
|
44
|
+
```ruby
|
45
|
+
gem 'lotus-model'
|
46
|
+
```
|
45
47
|
|
46
48
|
And then execute:
|
47
49
|
|
@@ -53,9 +55,52 @@ Or install it yourself as:
|
|
53
55
|
|
54
56
|
## Usage
|
55
57
|
|
58
|
+
This class provides a DSL to configure adapter, mapping and collection.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'lotus/model'
|
62
|
+
|
63
|
+
class User
|
64
|
+
include Lotus::Entity
|
65
|
+
attributes :name, :age
|
66
|
+
end
|
67
|
+
|
68
|
+
class UserRepository
|
69
|
+
include Lotus::Repository
|
70
|
+
end
|
71
|
+
|
72
|
+
Lotus::Model.configure do
|
73
|
+
adapter type: :sql, uri: 'postgres://localhost/database'
|
74
|
+
|
75
|
+
mapping do
|
76
|
+
collection :users do
|
77
|
+
entity User
|
78
|
+
respository UserRepository
|
79
|
+
|
80
|
+
attribute :id, Integer
|
81
|
+
attribute :name, String
|
82
|
+
attribute :age, Integer
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Lotus::Model.load!
|
88
|
+
|
89
|
+
user = User.new(name: 'Luca', age: 32)
|
90
|
+
user = UserRepository.create(user)
|
91
|
+
|
92
|
+
puts user.id # => 1
|
93
|
+
|
94
|
+
u = UserRepository.find(user.id)
|
95
|
+
u == user # => true
|
96
|
+
```
|
97
|
+
|
98
|
+
## Concepts
|
99
|
+
|
56
100
|
### Entities
|
57
101
|
|
58
102
|
An object that is defined by its identity.
|
103
|
+
See "Domain Driven Design" by Eric Evans.
|
59
104
|
|
60
105
|
An entity is the core of an application, where the part of the domain logic is implemented.
|
61
106
|
It's a small, cohesive object that expresses coherent and meaningful behaviors.
|
@@ -72,17 +117,17 @@ require 'lotus/model'
|
|
72
117
|
|
73
118
|
class Person
|
74
119
|
include Lotus::Entity
|
75
|
-
|
120
|
+
attributes :name, :age
|
76
121
|
end
|
77
122
|
```
|
78
123
|
|
79
|
-
When a class includes `Lotus::Entity` it
|
124
|
+
When a class includes `Lotus::Entity` it receives the following interface:
|
80
125
|
|
81
126
|
* `#id`
|
82
127
|
* `#id=`
|
83
128
|
* `#initialize(attributes = {})`
|
84
129
|
|
85
|
-
|
130
|
+
`Lotus::Entity` also provides the `.attributes` for defining attribute accessors for the given names.
|
86
131
|
|
87
132
|
If we expand the code above in **pure Ruby**, it would be:
|
88
133
|
|
@@ -96,8 +141,32 @@ class Person
|
|
96
141
|
end
|
97
142
|
```
|
98
143
|
|
99
|
-
|
100
|
-
|
144
|
+
**Lotus::Model** ships `Lotus::Entity` for developers's convenience.
|
145
|
+
|
146
|
+
**Lotus::Model** depends on a narrow and well-defined interface for an Entity - `#id`, `#id=`, `#initialize(attributes={})`.
|
147
|
+
If your object implements that interface then that object can be used as an Entity in the **Lotus::Model** framework.
|
148
|
+
|
149
|
+
However, we suggest to implement this interface by including `Lotus::Entity`, in case that future versions of the framework will expand it.
|
150
|
+
|
151
|
+
See [Dependency Inversion Principle](http://en.wikipedia.org/wiki/Dependency_inversion_principle) for more on interfaces.
|
152
|
+
|
153
|
+
When a class extend an entity class, it will also *inherit* its mother's attributes.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
require 'lotus/model'
|
157
|
+
|
158
|
+
class Article
|
159
|
+
include Lotus::Entity
|
160
|
+
attributes :name
|
161
|
+
end
|
162
|
+
|
163
|
+
class RareArticle < Article
|
164
|
+
attributes :price
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
That is, `RareArticle`'s attributes carry over `:name` attribute from `Article`,
|
169
|
+
thus is `:id, :name, :price`.
|
101
170
|
|
102
171
|
### Repositories
|
103
172
|
|
@@ -138,7 +207,7 @@ When a class includes `Lotus::Repository`, it will receive the following interfa
|
|
138
207
|
It corresponds to a table for a SQL database or to a MongoDB collection.
|
139
208
|
|
140
209
|
**All the queries are private**.
|
141
|
-
This decision forces developers to define intention revealing API, instead
|
210
|
+
This decision forces developers to define intention revealing API, instead of leaking storage API details outside of a repository.
|
142
211
|
|
143
212
|
Look at the following code:
|
144
213
|
|
@@ -229,6 +298,28 @@ class ArticleRepository
|
|
229
298
|
end
|
230
299
|
```
|
231
300
|
|
301
|
+
**Your models and repositories have to be in the same namespace.** Otherwise `Lotus::Model::Mapper#load!`
|
302
|
+
will not initialize your repositories correctly.
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
class MyLotusApp::Model::User
|
306
|
+
include Lotus::Entity
|
307
|
+
# your code here
|
308
|
+
end
|
309
|
+
|
310
|
+
# This repository will work...
|
311
|
+
class MyLotusApp::Model::UserRepository
|
312
|
+
include Lotus::Repository
|
313
|
+
# your code here
|
314
|
+
end
|
315
|
+
|
316
|
+
# ...this will not!
|
317
|
+
class MyLotusApp::Repository::UserRepository
|
318
|
+
include Lotus::Repository
|
319
|
+
# your code here
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
232
323
|
### Data Mapper
|
233
324
|
|
234
325
|
A persistence mapper that keeps entities independent from database details.
|
@@ -250,19 +341,53 @@ mapper = Lotus::Model::Mapper.new do
|
|
250
341
|
end
|
251
342
|
```
|
252
343
|
|
253
|
-
For simplicity sake, imagine that the mapper above is used with a SQL database.
|
344
|
+
For simplicity's sake, imagine that the mapper above is used with a SQL database.
|
254
345
|
We use `#collection` to indicate the name of the table that we want to map, `#entity` to indicate the class that we want to associate.
|
255
|
-
In the end, each `#attribute`
|
346
|
+
In the end, each call to `#attribute` associates the specified column with a corresponding Ruby type.
|
256
347
|
|
257
348
|
For advanced mapping and legacy databases, please have a look at the API doc.
|
258
349
|
|
350
|
+
**Known limitations**
|
351
|
+
|
352
|
+
Please be noted there are limitations with inherited entities:
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
require 'lotus/model'
|
356
|
+
|
357
|
+
class Article
|
358
|
+
include Lotus::Entity
|
359
|
+
attributes :name
|
360
|
+
end
|
361
|
+
|
362
|
+
class RareArticle < Article
|
363
|
+
attributes :price
|
364
|
+
end
|
365
|
+
|
366
|
+
mapper = Lotus::Model::Mapper.new do
|
367
|
+
collection :articles do
|
368
|
+
entity Article
|
369
|
+
|
370
|
+
attribute :id, Integer
|
371
|
+
attribute :name, String
|
372
|
+
attribute :price, Integer
|
373
|
+
end
|
374
|
+
end
|
375
|
+
```
|
376
|
+
|
377
|
+
In the example above, there are few problems:
|
378
|
+
|
379
|
+
* `Article` could not be fetched because mapping could not map `price`.
|
380
|
+
* Finding a persisted `RareArticle` record, for eg. `ArticleRepository.find(123)`,
|
381
|
+
the result is an `Article` not `RareArticle`.
|
382
|
+
|
259
383
|
### Adapter
|
260
384
|
|
261
385
|
An adapter is a concrete implementation of persistence logic for a specific database.
|
262
|
-
**Lotus::Model** is shipped with
|
386
|
+
**Lotus::Model** is shipped with three adapters:
|
263
387
|
|
264
388
|
* SqlAdapter
|
265
389
|
* MemoryAdapter
|
390
|
+
* FileSystemAdapter
|
266
391
|
|
267
392
|
An adapter can be associated with one or multiple repositories.
|
268
393
|
|
@@ -286,7 +411,7 @@ In the example above, we reuse the adapter because the target tables (`people` a
|
|
286
411
|
|
287
412
|
### Query
|
288
413
|
|
289
|
-
An object that implements an interface for
|
414
|
+
An object that implements an interface for querying the database.
|
290
415
|
This interface may vary, according to the adapter's specifications.
|
291
416
|
Think of an adapter for Redis, it will probably employ different strategies to filter records than an SQL query object.
|
292
417
|
|
@@ -294,6 +419,21 @@ Think of an adapter for Redis, it will probably employ different strategies to f
|
|
294
419
|
|
295
420
|
* A repository must be named after an entity, by appending `"Repository"` to the entity class name (eg. `Article` => `ArticleRepository`).
|
296
421
|
|
422
|
+
### Configurations
|
423
|
+
|
424
|
+
* Non-standard respository can be configured for an entity, by setting `repository` on the collection.
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
require 'lotus/model'
|
428
|
+
|
429
|
+
mapper = Lotus::Model::Mapper.new do
|
430
|
+
collection :users do
|
431
|
+
entity User
|
432
|
+
repository EmployeeRepository
|
433
|
+
end
|
434
|
+
end
|
435
|
+
```
|
436
|
+
|
297
437
|
### Thread safety
|
298
438
|
|
299
439
|
**Lotus::Model**'s is thread safe during the runtime, but it isn't during the loading process.
|
@@ -301,7 +441,7 @@ The mapper compiles some code internally, be sure to safely load it before your
|
|
301
441
|
|
302
442
|
```ruby
|
303
443
|
Mutex.new.synchronize do
|
304
|
-
|
444
|
+
Lotus::Model.load!
|
305
445
|
end
|
306
446
|
```
|
307
447
|
|