lotus-model 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +6 -0
  4. data/.yardopts +5 -0
  5. data/EXAMPLE.md +217 -0
  6. data/Gemfile +14 -2
  7. data/README.md +303 -3
  8. data/Rakefile +17 -1
  9. data/lib/lotus-model.rb +1 -0
  10. data/lib/lotus/entity.rb +157 -0
  11. data/lib/lotus/model.rb +23 -2
  12. data/lib/lotus/model/adapters/abstract.rb +167 -0
  13. data/lib/lotus/model/adapters/implementation.rb +111 -0
  14. data/lib/lotus/model/adapters/memory/collection.rb +132 -0
  15. data/lib/lotus/model/adapters/memory/command.rb +90 -0
  16. data/lib/lotus/model/adapters/memory/query.rb +457 -0
  17. data/lib/lotus/model/adapters/memory_adapter.rb +149 -0
  18. data/lib/lotus/model/adapters/sql/collection.rb +209 -0
  19. data/lib/lotus/model/adapters/sql/command.rb +67 -0
  20. data/lib/lotus/model/adapters/sql/query.rb +615 -0
  21. data/lib/lotus/model/adapters/sql_adapter.rb +154 -0
  22. data/lib/lotus/model/mapper.rb +101 -0
  23. data/lib/lotus/model/mapping.rb +23 -0
  24. data/lib/lotus/model/mapping/coercer.rb +80 -0
  25. data/lib/lotus/model/mapping/collection.rb +336 -0
  26. data/lib/lotus/model/version.rb +4 -1
  27. data/lib/lotus/repository.rb +620 -0
  28. data/lotus-model.gemspec +15 -11
  29. data/test/entity_test.rb +126 -0
  30. data/test/fixtures.rb +81 -0
  31. data/test/model/adapters/abstract_test.rb +75 -0
  32. data/test/model/adapters/implementation_test.rb +22 -0
  33. data/test/model/adapters/memory/query_test.rb +91 -0
  34. data/test/model/adapters/memory_adapter_test.rb +1044 -0
  35. data/test/model/adapters/sql/query_test.rb +121 -0
  36. data/test/model/adapters/sql_adapter_test.rb +1078 -0
  37. data/test/model/mapper_test.rb +94 -0
  38. data/test/model/mapping/coercer_test.rb +27 -0
  39. data/test/model/mapping/collection_test.rb +82 -0
  40. data/test/repository_test.rb +283 -0
  41. data/test/test_helper.rb +30 -0
  42. data/test/version_test.rb +7 -0
  43. metadata +109 -11
@@ -0,0 +1,94 @@
1
+ require 'test_helper'
2
+
3
+ describe Lotus::Model::Mapper do
4
+ before do
5
+ @mapper = Lotus::Model::Mapper.new
6
+ end
7
+
8
+ describe '#initialize' do
9
+ before do
10
+ class FakeCoercer
11
+ end
12
+ end
13
+
14
+ after do
15
+ Object.send(:remove_const, :FakeCoercer)
16
+ end
17
+
18
+ it 'uses the given coercer' do
19
+ mapper = Lotus::Model::Mapper.new(FakeCoercer) do
20
+ collection :articles do
21
+ end
22
+ end
23
+
24
+ mapper.collection(:articles).coercer_class.must_equal(FakeCoercer)
25
+ end
26
+
27
+ it 'executes the given block' do
28
+ mapper = Lotus::Model::Mapper.new do
29
+ collection :articles do
30
+ entity Article
31
+ end
32
+ end.load!
33
+
34
+ mapper.collection(:articles).must_be_kind_of Lotus::Model::Mapping::Collection
35
+ end
36
+ end
37
+
38
+ describe '#collections' do
39
+ before do
40
+ @mapper = Lotus::Model::Mapper.new do
41
+ collection :teas do
42
+ end
43
+ end
44
+ end
45
+
46
+ it 'returns the mapped collections' do
47
+ name, collection = @mapper.collections.first
48
+
49
+ name.must_equal :teas
50
+ collection.must_be_kind_of Lotus::Model::Mapping::Collection
51
+ end
52
+ end
53
+
54
+ describe '#collection' do
55
+ describe 'when a block is given' do
56
+ it 'register a collection' do
57
+ @mapper.collection :users do
58
+ entity User
59
+ end
60
+
61
+ @mapper.load!
62
+
63
+ collection = @mapper.collection(:users)
64
+ collection.must_be_kind_of Lotus::Model::Mapping::Collection
65
+ collection.name.must_equal :users
66
+ end
67
+ end
68
+
69
+ describe 'when only the name is passed' do
70
+ describe 'and the collection is present' do
71
+ before do
72
+ @mapper.collection :users do
73
+ entity User
74
+ end
75
+
76
+ @mapper.load!
77
+ end
78
+
79
+ it 'returns the collection' do
80
+ collection = @mapper.collection(:users)
81
+
82
+ collection.must_be_kind_of(Lotus::Model::Mapping::Collection)
83
+ collection.name.must_equal :users
84
+ end
85
+ end
86
+
87
+ describe 'and the collection is missing' do
88
+ it 'raises an error' do
89
+ -> { @mapper.collection(:unknown) }.must_raise(Lotus::Model::Mapping::UnmappedCollectionError)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ describe Lotus::Model::Mapping::Coercer do
4
+ let(:entity) { User.new(name: 'Tyrion Lannister') }
5
+ let(:collection) { Lotus::Model::Mapping::Collection.new(:users, Lotus::Model::Mapping::Coercer) }
6
+ let(:coercer) { Lotus::Model::Mapping::Coercer.new(collection) }
7
+
8
+ before do
9
+ collection.entity(User)
10
+ collection.attribute :id, Integer
11
+ collection.attribute :name, String
12
+ end
13
+
14
+ describe '#to_record' do
15
+ it 'should not return identity column' do
16
+ coercer.to_record(entity).must_equal(name: 'Tyrion Lannister')
17
+ end
18
+
19
+ describe 'when identity is set' do
20
+ let(:entity) { User.new(id: 3, name: 'Tyrion Lannister') }
21
+
22
+ it 'should return identity as well' do
23
+ coercer.to_record(entity).must_equal(id: 3, name: 'Tyrion Lannister')
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ describe Lotus::Model::Mapping::Collection do
4
+ before do
5
+ @collection = Lotus::Model::Mapping::Collection.new(:users, Lotus::Model::Mapping::Coercer)
6
+ end
7
+
8
+ describe '::Boolean' do
9
+ it 'defines top level constant' do
10
+ assert defined?(::Boolean)
11
+ end
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'assigns the name' do
16
+ @collection.name.must_equal :users
17
+ end
18
+
19
+ it 'assigns the coercer class' do
20
+ @collection.coercer_class.must_equal Lotus::Model::Mapping::Coercer
21
+ end
22
+
23
+ it 'executes the given block' do
24
+ collection = Lotus::Model::Mapping::Collection.new(:users, Lotus::Model::Mapping::Coercer) do
25
+ entity User
26
+ end
27
+
28
+ collection.entity.must_equal User
29
+ end
30
+ end
31
+
32
+ describe '#entity' do
33
+ describe 'when a value is given' do
34
+ before do
35
+ @collection.entity(User)
36
+ end
37
+
38
+ it 'sets the value' do
39
+ @collection.entity.must_equal User
40
+ end
41
+ end
42
+
43
+ describe 'when a value is not given' do
44
+ it 'retuns the value' do
45
+ @collection.entity.must_be_nil
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#identity' do
51
+ describe 'when a value is given' do
52
+ before do
53
+ @collection.identity(:_id)
54
+ end
55
+
56
+ it 'sets the value' do
57
+ @collection.identity.must_equal :_id
58
+ end
59
+ end
60
+
61
+ describe 'when a value is not given' do
62
+ it 'retuns the value' do
63
+ @collection.identity.must_equal(:id)
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#attribute' do
69
+ before do
70
+ @collection.attribute :id, Integer
71
+ @collection.attribute :name, String, as: 't_name'
72
+ end
73
+
74
+ it 'defines an attribute' do
75
+ @collection.attributes[:id].must_equal [Integer, :id]
76
+ end
77
+
78
+ it 'defines a mapped attribute' do
79
+ @collection.attributes[:name].must_equal [String, :t_name]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,283 @@
1
+ require 'test_helper'
2
+
3
+ describe Lotus::Repository do
4
+ let(:user1) { User.new(name: 'L') }
5
+ let(:user2) { User.new(name: 'MG') }
6
+ let(:users) { [user1, user2] }
7
+
8
+ let(:article1) { Article.new(user_id: user1.id, title: 'Introducing Lotus::Model', comments_count: '23') }
9
+ let(:article2) { Article.new(user_id: user1.id, title: 'Thread safety', comments_count: '42') }
10
+ let(:article3) { Article.new(user_id: user2.id, title: 'Love Relationships', comments_count: '4') }
11
+
12
+ { memory: [Lotus::Model::Adapters::MemoryAdapter, nil, MAPPER],
13
+ sql: [Lotus::Model::Adapters::SqlAdapter, SQLITE_CONNECTION_STRING, MAPPER] }.each do |adapter_name, (adapter,uri,mapper)|
14
+ describe "with #{ adapter_name } adapter" do
15
+ before do
16
+ UserRepository.adapter = adapter.new(mapper, uri)
17
+ ArticleRepository.adapter = adapter.new(mapper, uri)
18
+
19
+ UserRepository.collection = :users
20
+ ArticleRepository.collection = :articles
21
+
22
+ UserRepository.clear
23
+ ArticleRepository.clear
24
+ end
25
+
26
+ describe '.collection' do
27
+ it 'returns the collection name' do
28
+ UserRepository.collection.must_equal :users
29
+ ArticleRepository.collection.must_equal :articles
30
+ end
31
+ end
32
+
33
+ describe '.persist' do
34
+ describe 'when non persisted' do
35
+ before do
36
+ UserRepository.persist(user)
37
+ end
38
+
39
+ let(:user) { User.new(name: 'S') }
40
+
41
+ it 'is created' do
42
+ id = UserRepository.last.id
43
+ UserRepository.find(id).must_equal(user)
44
+ end
45
+ end
46
+
47
+ describe 'when already persisted' do
48
+ before do
49
+ UserRepository.create(user1)
50
+
51
+ user1.name = 'Luke'
52
+ UserRepository.persist(user1)
53
+ end
54
+
55
+ let(:id) { user1.id }
56
+
57
+ it 'is updated' do
58
+ UserRepository.find(id).must_equal(user1)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '.create' do
64
+ before do
65
+ UserRepository.create(user1)
66
+ UserRepository.create(user2)
67
+ end
68
+
69
+ it 'persist entities' do
70
+ UserRepository.all.must_equal(users)
71
+ end
72
+
73
+ it 'creates different kind of entities' do
74
+ ArticleRepository.create(article1)
75
+ ArticleRepository.all.must_equal([article1])
76
+ end
77
+
78
+ it 'does nothing when already persisted' do
79
+ id = user1.id
80
+
81
+ UserRepository.create(user1)
82
+ user1.id.must_equal id
83
+ end
84
+ end
85
+
86
+ describe '.update' do
87
+ before do
88
+ UserRepository.create(user1)
89
+ end
90
+
91
+ let(:id) { user1.id }
92
+
93
+ it 'updates entities' do
94
+ user = User.new(name: 'Luca')
95
+ user.id = id
96
+
97
+ UserRepository.update(user)
98
+
99
+ u = UserRepository.find(id)
100
+ u.name.must_equal('Luca')
101
+ end
102
+
103
+ it 'raises an error when not persisted' do
104
+ -> { UserRepository.update(user2) }.must_raise(Lotus::Model::NonPersistedEntityError)
105
+ end
106
+ end
107
+
108
+ describe '.delete' do
109
+ before do
110
+ UserRepository.create(user)
111
+ UserRepository.delete(user)
112
+ end
113
+
114
+ let(:user) { User.new(name: 'D') }
115
+
116
+ it 'delete entity' do
117
+ UserRepository.all.wont_include(user)
118
+ end
119
+
120
+ it 'raises error when the given entity is not persisted' do
121
+ -> { UserRepository.delete(user2) }.must_raise(Lotus::Model::NonPersistedEntityError)
122
+ end
123
+ end
124
+
125
+ describe '.all' do
126
+ describe 'without data' do
127
+ it 'returns an empty collection' do
128
+ UserRepository.all.must_be_empty
129
+ end
130
+ end
131
+
132
+ describe 'with data' do
133
+ before do
134
+ UserRepository.create(user1)
135
+ UserRepository.create(user2)
136
+ end
137
+
138
+ it 'returns all the entities' do
139
+ UserRepository.all.must_equal(users)
140
+ end
141
+ end
142
+ end
143
+
144
+ describe '.find' do
145
+ describe 'without data' do
146
+ it 'raises error' do
147
+ -> { UserRepository.find(1) }.must_raise(Lotus::Model::EntityNotFound)
148
+ end
149
+ end
150
+
151
+ describe 'with data' do
152
+ before do
153
+ TestPrimaryKey = Struct.new(:id) do
154
+ def to_int
155
+ id
156
+ end
157
+ end
158
+
159
+ UserRepository.create(user1)
160
+ UserRepository.create(user2)
161
+
162
+ ArticleRepository.create(article1)
163
+ end
164
+
165
+ after do
166
+ Object.send(:remove_const, :TestPrimaryKey)
167
+ end
168
+
169
+ it 'returns the entity associated with the given id' do
170
+ UserRepository.find(user1.id).must_equal(user1)
171
+ end
172
+
173
+ it 'accepts a string as argument' do
174
+ UserRepository.find(user2.id.to_s).must_equal(user2)
175
+ end
176
+
177
+ it 'accepts an object that can be coerced to integer' do
178
+ id = TestPrimaryKey.new(user2.id)
179
+ UserRepository.find(id).must_equal(user2)
180
+ end
181
+
182
+ it 'coerces attributes as indicated by the mapper' do
183
+ result = ArticleRepository.find(article1.id)
184
+ result.comments_count.must_be_kind_of(Integer)
185
+ end
186
+
187
+ it "doesn't assign a value to unmapped attributes" do
188
+ ArticleRepository.find(article1.id).unmapped_attribute.must_be_nil
189
+ end
190
+
191
+ it "raises error when the given id isn't associated with any entity" do
192
+ -> { UserRepository.find(1_000_000) }.must_raise(Lotus::Model::EntityNotFound)
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '.first' do
198
+ describe 'without data' do
199
+ it 'returns nil' do
200
+ UserRepository.first.must_be_nil
201
+ end
202
+ end
203
+
204
+ describe 'with data' do
205
+ before do
206
+ UserRepository.create(user1)
207
+ UserRepository.create(user2)
208
+ end
209
+
210
+ it 'returns first record' do
211
+ UserRepository.first.must_equal(user1)
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '.last' do
217
+ describe 'without data' do
218
+ it 'returns nil' do
219
+ UserRepository.last.must_be_nil
220
+ end
221
+ end
222
+
223
+ describe 'with data' do
224
+ before do
225
+ UserRepository.create(user1)
226
+ UserRepository.create(user2)
227
+ end
228
+
229
+ it 'returns last record' do
230
+ UserRepository.last.must_equal(user2)
231
+ end
232
+ end
233
+ end
234
+
235
+ describe '.clear' do
236
+ describe 'without data' do
237
+ it 'removes all the records' do
238
+ UserRepository.clear
239
+ UserRepository.all.must_be_empty
240
+ end
241
+ end
242
+
243
+ describe 'with data' do
244
+ before do
245
+ UserRepository.create(user1)
246
+ UserRepository.create(user2)
247
+ end
248
+
249
+ it 'removes all the records' do
250
+ UserRepository.clear
251
+ UserRepository.all.must_be_empty
252
+ end
253
+ end
254
+ end
255
+
256
+ describe 'querying' do
257
+ before do
258
+ UserRepository.create(user1)
259
+ ArticleRepository.create(article1)
260
+ ArticleRepository.create(article2)
261
+ ArticleRepository.create(article3)
262
+ end
263
+
264
+ it 'defines custom finders' do
265
+ actual = ArticleRepository.by_user(user1)
266
+ actual.all.must_equal [article1, article2]
267
+ end
268
+
269
+ if adapter_name == :sql
270
+ it 'combines queries' do
271
+ actual = ArticleRepository.rank_by_user(user1)
272
+ actual.all.must_equal [article2, article1]
273
+ end
274
+
275
+ it 'negates a query' do
276
+ actual = ArticleRepository.not_by_user(user1)
277
+ actual.all.must_equal []
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end