cuprum-collections 0.2.0 → 0.3.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 +18 -0
- data/README.md +255 -8
- data/lib/cuprum/collections/basic/collection.rb +13 -0
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -3
- data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
- data/lib/cuprum/collections/basic/commands/insert_one.rb +4 -3
- data/lib/cuprum/collections/basic/commands/update_one.rb +4 -3
- data/lib/cuprum/collections/basic/query.rb +3 -3
- data/lib/cuprum/collections/commands/abstract_find_many.rb +33 -32
- data/lib/cuprum/collections/commands/abstract_find_one.rb +4 -3
- data/lib/cuprum/collections/commands/create.rb +60 -0
- data/lib/cuprum/collections/commands/find_one_matching.rb +134 -0
- data/lib/cuprum/collections/commands/update.rb +74 -0
- data/lib/cuprum/collections/commands/upsert.rb +162 -0
- data/lib/cuprum/collections/commands.rb +7 -2
- data/lib/cuprum/collections/errors/abstract_find_error.rb +210 -0
- data/lib/cuprum/collections/errors/already_exists.rb +4 -72
- data/lib/cuprum/collections/errors/extra_attributes.rb +8 -18
- data/lib/cuprum/collections/errors/failed_validation.rb +5 -18
- data/lib/cuprum/collections/errors/invalid_parameters.rb +7 -15
- data/lib/cuprum/collections/errors/invalid_query.rb +5 -15
- data/lib/cuprum/collections/errors/missing_default_contract.rb +5 -17
- data/lib/cuprum/collections/errors/not_found.rb +4 -67
- data/lib/cuprum/collections/errors/not_unique.rb +18 -0
- data/lib/cuprum/collections/errors/unknown_operator.rb +7 -17
- data/lib/cuprum/collections/errors.rb +13 -1
- data/lib/cuprum/collections/queries/ordering.rb +2 -2
- data/lib/cuprum/collections/repository.rb +23 -10
- data/lib/cuprum/collections/rspec/collection_contract.rb +140 -103
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +8 -6
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +114 -34
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +12 -9
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +4 -3
- data/lib/cuprum/collections/rspec/query_contract.rb +3 -3
- data/lib/cuprum/collections/rspec/querying_contract.rb +2 -2
- data/lib/cuprum/collections/rspec/repository_contract.rb +167 -93
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +4 -3
- data/lib/cuprum/collections/version.rb +1 -1
- data/lib/cuprum/collections.rb +1 -0
- metadata +20 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dda6613af3def3463515616a0b448dce3ce0fb666b6b81b9c9d54cf9e4f6bef0
|
4
|
+
data.tar.gz: e8cae0cd8e96daf887268b0b467352d34a7a93357729c14115375f7d14d2e2d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 138cf1bd095d6d03e196c90ac4779ee6d8dec8743f8cb321c4114117c707ef0124f378642653beef0e3db1c0d127cebd05967a4356ce5361cd3294bd298642e7
|
7
|
+
data.tar.gz: 968b185b8e1b0447b55acc093387ad527e0ba5154b0e4a24d2ace52cf258ca29939eefa0cc772451703d1b56286dce907c84402a735a360152d3573ea8cb5db7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.0
|
4
|
+
|
5
|
+
### Collections
|
6
|
+
|
7
|
+
Updated `Cuprum::Collections::Basic::Collection`.
|
8
|
+
|
9
|
+
- Implemented `#count` method.
|
10
|
+
- Implemented `#qualified_name` method.
|
11
|
+
|
12
|
+
### Commands
|
13
|
+
|
14
|
+
Implemented built-in Commands, which take a `:collection` parameter:
|
15
|
+
|
16
|
+
- `Commands::Create`
|
17
|
+
- `Commands::FindOneMatching`
|
18
|
+
- `Commands::Update`
|
19
|
+
- `Commands::Upsert`
|
20
|
+
|
3
21
|
## 0.2.0
|
4
22
|
|
5
23
|
Implemented `Cuprum::Collections::Repository`.
|
data/README.md
CHANGED
@@ -26,7 +26,7 @@ The Ruby ecosystem has a wide variety of tools and libraries for managing data a
|
|
26
26
|
|
27
27
|
### Compatibility
|
28
28
|
|
29
|
-
Cuprum::Collections is tested against Ruby (MRI) 2.
|
29
|
+
Cuprum::Collections is tested against Ruby (MRI) 2.7 through 3.2.
|
30
30
|
|
31
31
|
### Documentation
|
32
32
|
|
@@ -34,9 +34,9 @@ Documentation is generated using [YARD](https://yardoc.org/), and can be generat
|
|
34
34
|
|
35
35
|
### License
|
36
36
|
|
37
|
-
Copyright (c) 2020-
|
37
|
+
Copyright (c) 2020-2023 Rob Smith
|
38
38
|
|
39
|
-
|
39
|
+
Cuprum::Collections is released under the [MIT License](https://opensource.org/licenses/MIT).
|
40
40
|
|
41
41
|
### Contribute
|
42
42
|
|
@@ -105,6 +105,13 @@ end
|
|
105
105
|
|
106
106
|
Because a collection can represent any sort of data, from a raw Ruby Hash to an ORM record, the term used to indicate "one item in the collection" is an *entity*. Likewise, the class of the items in the collection is the *entity_class*. In our example above, our entities are books, and the entity class is Hash.
|
107
107
|
|
108
|
+
Each collection also defines the following methods:
|
109
|
+
|
110
|
+
- `#collection_name`: The collection name is a short description of what the collection contains. For example, a collection of `Book` objects might have a collection name of `'books'`, while a collection of `Authorization::Credentials::ApiKey` objects might have a collection name of `'api_keys'`.
|
111
|
+
- `#qualified_name`: The qualified name is a full description of the collection, and should be unique. For example, a collection of `Book` objects might have a qualified name of `'books'`, while a collection of `Authorization::Credentials::ApiKey` objects might have a qualified name of `'authorization/credentials/api_keys'`.
|
112
|
+
|
113
|
+
As a general rule, the `#collection_name` is used when displaying information to the user, while the `#qualified_name` is used to uniquely identify the collection (such as when adding to or retrieving from a [Repository](#repositories)).
|
114
|
+
|
108
115
|
<a id="commands"></a>
|
109
116
|
|
110
117
|
#### Commands
|
@@ -214,7 +221,7 @@ If the collection does not include an entity with each of the specified primary
|
|
214
221
|
|
215
222
|
##### Find Matching
|
216
223
|
|
217
|
-
The `FindMatching` command takes a set of query parameters and queries data from the collection. You can specify filters using the `:where` keyword or by passing a block, sort the results using the `:order` keyword, or return a subset of the results using the `:limit` and `:offset` keywords.
|
224
|
+
The `FindMatching` command takes a set of query parameters and queries data from the collection. You can specify filters using the `:where` keyword or by passing a block, sort the results using the `:order` keyword, or return a subset of the results using the `:limit` and `:offset` keywords.
|
218
225
|
|
219
226
|
```ruby
|
220
227
|
result =
|
@@ -260,7 +267,11 @@ The `FindMatching` command has several options:
|
|
260
267
|
#=> { books: [{ ... }, { ... }, { ... }] }
|
261
268
|
```
|
262
269
|
|
270
|
+
- The `:limit` keyword caps the number of results returned.
|
271
|
+
- The `:offset` keyword skips the specified number of results.
|
272
|
+
- The `:order` keyword specifies the order of results.
|
263
273
|
- The `:scope` keyword allows you to pass a query to the command. Only entities that match the given scope will be found and returned by `#find_matching`.
|
274
|
+
- The `:where` keyword defines filters for which results are to be returned.
|
264
275
|
|
265
276
|
##### Find One
|
266
277
|
|
@@ -299,7 +310,7 @@ The `InsertOne` command takes an entity and inserts that entity into the collect
|
|
299
310
|
|
300
311
|
```ruby
|
301
312
|
book = { 'id' => 10, 'title' => 'Gideon the Ninth', 'author' => 'Tamsyn Muir' }
|
302
|
-
result = collection.insert_one.call(entity:
|
313
|
+
result = collection.insert_one.call(entity: book)
|
303
314
|
|
304
315
|
result.value
|
305
316
|
#=> {
|
@@ -369,16 +380,16 @@ If the collection does not specify a default contract and no `:contract` keyword
|
|
369
380
|
require 'cuprum/collections/repository'
|
370
381
|
```
|
371
382
|
|
372
|
-
A repository is a group of collections. While a collection might be be a single data set, such as the records in a table, the repository represents all of the data sets in a data source, such as the tables in a database.
|
383
|
+
A repository is a group of collections. While a collection might be be a single data set, such as the records in a table, the repository represents all of the data sets in a data source, such as the tables in a database. Each repository uses the `#qualified_name` of its collections as a unique key.
|
373
384
|
|
374
385
|
```ruby
|
375
386
|
repository = Cuprum::Collections::Repository.new
|
376
|
-
repository.key?('
|
387
|
+
repository.key?('books')
|
377
388
|
#=> false
|
378
389
|
|
379
390
|
repository.add(books_collection)
|
380
391
|
|
381
|
-
repository.key?('
|
392
|
+
repository.key?('books')
|
382
393
|
#=> true
|
383
394
|
repository.keys
|
384
395
|
#=> ['books']
|
@@ -386,6 +397,31 @@ repository['books']
|
|
386
397
|
#=> the books collection
|
387
398
|
```
|
388
399
|
|
400
|
+
When accessing a collection with a qualified name, you must pass the qualified name to `#[]` or `#key?`, rather than the collection name.
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
repository = Cuprum::Collections::Repository.new
|
404
|
+
repository.key?('api_keys')
|
405
|
+
#=> false
|
406
|
+
repository.key?('authorization/credentials/api_keys')
|
407
|
+
#=> false
|
408
|
+
|
409
|
+
api_keys_collection.collection_name
|
410
|
+
#=> 'api_keys'
|
411
|
+
api_keys_collection.qualified_name
|
412
|
+
#=> 'authorization/credentials/api_keys'
|
413
|
+
repository.add(api_keys_collection)
|
414
|
+
|
415
|
+
repository.key?('api_keys')
|
416
|
+
#=> false
|
417
|
+
repository.key?('authorization/credentials/api_keys')
|
418
|
+
#=> true
|
419
|
+
repository.keys
|
420
|
+
#=> ['authorization/credentials/api_keys']
|
421
|
+
repository['authorization/credentials/api_keys']
|
422
|
+
#=> the api keys collection
|
423
|
+
```
|
424
|
+
|
389
425
|
#### Basic Collection
|
390
426
|
|
391
427
|
```ruby
|
@@ -412,6 +448,7 @@ You can also specify some optional keywords:
|
|
412
448
|
- The `:member_name` parameter is used to create an envelope for singular query commands such as the `FindOne` command. If not given, the member name will be generated automatically as a singular form of the collection name.
|
413
449
|
- The `:primary_key_name` parameter specifies the attribute that serves as the primary key for the collection entities. The default value is `:id`.
|
414
450
|
- The `:primary_key_type` parameter specifies the type of the primary key attribute. The default value is `Integer`.
|
451
|
+
- The `:qualified_name` parameter sets the qualified name for the collection. It is used to uniquely identify the collection in a repository.
|
415
452
|
|
416
453
|
##### Basic Repositories
|
417
454
|
|
@@ -1007,3 +1044,213 @@ query.each.map(&:title)
|
|
1007
1044
|
# 'The Farthest Shore'
|
1008
1045
|
# ]
|
1009
1046
|
```
|
1047
|
+
|
1048
|
+
<a id="builtin-commands"></a>
|
1049
|
+
|
1050
|
+
### Built In Commands
|
1051
|
+
|
1052
|
+
`Cuprum::Collections` defines some basic commands. Each command takes a `:collection` parameter, which is used for performing the data operations.
|
1053
|
+
|
1054
|
+
#### Create
|
1055
|
+
|
1056
|
+
```ruby
|
1057
|
+
require 'cuprum/collections/commands/create'
|
1058
|
+
```
|
1059
|
+
|
1060
|
+
The `Create` command takes an attributes Hash and adds an entity with those attributes to the collection. Internally, it calls the `#build_one`, `#validate_one`, and `#insert_one` commands on the collection.
|
1061
|
+
|
1062
|
+
```ruby
|
1063
|
+
command = Cuprum::Collections::Commands::Create.new(collection: books_collection)
|
1064
|
+
|
1065
|
+
books_collection.count
|
1066
|
+
#=> 0
|
1067
|
+
result = command.call(attributes: { 'title' => 'Gideon the Ninth' })
|
1068
|
+
result.value
|
1069
|
+
#=> a Book with title "Gideon the Ninth"
|
1070
|
+
books_collection.count
|
1071
|
+
#=> 1
|
1072
|
+
|
1073
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.first
|
1074
|
+
#=> a Book with title "Gideon the Ninth"
|
1075
|
+
```
|
1076
|
+
|
1077
|
+
If the contract does not match the entity, the `Create` command will return a failing result with a `ValidationFailed` error.
|
1078
|
+
|
1079
|
+
If the collection does not specify a default contract and no :contract keyword is provided, the `Create` command will return a failing result with a `MissingDefaultContract` error.
|
1080
|
+
|
1081
|
+
If the collection already includes an entity with the specified primary key, the `Create` command will return a failing result with an `AlreadyExists` error.
|
1082
|
+
|
1083
|
+
#### FindOneMatching
|
1084
|
+
|
1085
|
+
```ruby
|
1086
|
+
require 'cuprum/collections/commands/find_one_matching'
|
1087
|
+
```
|
1088
|
+
|
1089
|
+
The `FindOneMatching` command takes either an attributes hash or a [Query](#queries) block, and returns the unique entity from the collection with those entities or matching that query.
|
1090
|
+
|
1091
|
+
```ruby
|
1092
|
+
command = Cuprum::Collections::Commands::FindOneMatching.new(collection: books_collection)
|
1093
|
+
|
1094
|
+
result = command.call(attributes: { 'title' => 'Gideon the Ninth'})
|
1095
|
+
result.success?
|
1096
|
+
#=> true
|
1097
|
+
result.value
|
1098
|
+
#=> the unique Book with title "Gideon the Ninth"
|
1099
|
+
|
1100
|
+
result = command.call do
|
1101
|
+
{
|
1102
|
+
'series' => 'The Locked Tomb',
|
1103
|
+
'published_at' => less_than('2020-01-01')
|
1104
|
+
}
|
1105
|
+
end
|
1106
|
+
result.success?
|
1107
|
+
#=> true
|
1108
|
+
result.value
|
1109
|
+
#=> the unique Book with the given series and published before the given date
|
1110
|
+
```
|
1111
|
+
|
1112
|
+
If there are no entities in the collection matching the attributes or query, the `FindOneMatching` command returns a failing result with a `Cuprum::Collections::Errors::NotFound` error.
|
1113
|
+
|
1114
|
+
```ruby
|
1115
|
+
result = command.call(attributes: { 'title' => 'Gideon the Eleventh'})
|
1116
|
+
result.success?
|
1117
|
+
#=> false
|
1118
|
+
result.error
|
1119
|
+
#=> an instance of Cuprum::Collections::Errors::NotFound
|
1120
|
+
```
|
1121
|
+
|
1122
|
+
If there are two or more entities in the collection matching the attributes or query, the `FindOneMatching` command returns a failing result with a `Cuprum::Collections::Errors::NotUnique` error.
|
1123
|
+
|
1124
|
+
```ruby
|
1125
|
+
result = command.call(attributes: { 'author' => 'Tamsyn Muir'})
|
1126
|
+
result.success?
|
1127
|
+
#=> false
|
1128
|
+
result.error
|
1129
|
+
#=> an instance of Cuprum::Collections::Errors::NotUnique
|
1130
|
+
```
|
1131
|
+
|
1132
|
+
#### Update
|
1133
|
+
|
1134
|
+
```ruby
|
1135
|
+
require 'cuprum/collections/commands/update'
|
1136
|
+
```
|
1137
|
+
|
1138
|
+
The `Update` command takes an attributes Hash and an entity and updates the corresponding entity in the collection with those attributes. Internally, it calls the `#assign_one`, `#validate_one`, and `#update_one` commands on the collection.
|
1139
|
+
|
1140
|
+
```ruby
|
1141
|
+
command = Cuprum::Collections::Commands::Update.new(collection: books_collection)
|
1142
|
+
|
1143
|
+
entity = books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.first
|
1144
|
+
#=> a Book with title "Gideon the Ninth" and series "The Locked Tomb"
|
1145
|
+
|
1146
|
+
result = command.call(
|
1147
|
+
attributes: { 'series' => 'Space Necromancers' },
|
1148
|
+
entity: entity
|
1149
|
+
)
|
1150
|
+
result.value
|
1151
|
+
#=> a Book with title "Gideon the Ninth" and series "Space Necromancers"
|
1152
|
+
|
1153
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.first
|
1154
|
+
#=> a Book with title "Gideon the Ninth" and series "Space Necromancers"
|
1155
|
+
```
|
1156
|
+
|
1157
|
+
If the contract does not match the entity, the `Update` command will return a failing result with a `ValidationFailed` error.
|
1158
|
+
|
1159
|
+
If the collection does not specify a default contract and no :contract keyword is provided, the `Update` command will return a failing result with a `MissingDefaultContract` error.
|
1160
|
+
|
1161
|
+
If the collection does not include an entity with the specified entity's primary key, the `Update` command will return a failing result with a `NotFound` error.
|
1162
|
+
|
1163
|
+
#### Upsert
|
1164
|
+
|
1165
|
+
```ruby
|
1166
|
+
require 'cuprum/collections/commands/upsert'
|
1167
|
+
```
|
1168
|
+
|
1169
|
+
The `Upsert` command takes an attributes Hash and checks the collection for an entity with a matching primary key. If an entity is found, it updates the entity with the given attributes, as per the `Update` command (see above); if an entity is not found, it creates a new entity with the given attributes, as per the `Create` command.
|
1170
|
+
|
1171
|
+
```ruby
|
1172
|
+
command = Cuprum::Collections::Commands::Upsert.new(collection: books_collection)
|
1173
|
+
|
1174
|
+
# Creating An Entity
|
1175
|
+
books_collection.count
|
1176
|
+
#=> 0
|
1177
|
+
result = command.call(attributes: { 'id' => 0, 'title' => 'Gideon the Ninth' })
|
1178
|
+
result.value
|
1179
|
+
#=> a Book with id 0 and title "Gideon the Ninth"
|
1180
|
+
books_collection.count
|
1181
|
+
#=> 1
|
1182
|
+
|
1183
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.first
|
1184
|
+
#=> a Book with id 0 and title "Gideon the Ninth"
|
1185
|
+
|
1186
|
+
# Updating An Entity
|
1187
|
+
result = command.call(attributes: { 'id' => 0, 'author' => 'Tamsyn Muir' })
|
1188
|
+
result.value
|
1189
|
+
#=> a Book with id 0, title "Gideon the Ninth", and author "Tamsyn Muir"
|
1190
|
+
|
1191
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.first
|
1192
|
+
#=> a Book with id 0, title "Gideon the Ninth", and author "Tamsyn Muir"
|
1193
|
+
```
|
1194
|
+
|
1195
|
+
The `Upsert` command can also be configured with an attribute name or list of attribute names; the command will then search for an entity in the collection matching those attributes, rather than by primary key.
|
1196
|
+
|
1197
|
+
```ruby
|
1198
|
+
command =
|
1199
|
+
Cuprum::Collections::Commands::Upsert
|
1200
|
+
.new(attribute_names: %w[title author], collection: books_collection)
|
1201
|
+
other_entity = books_collection.build_one.call(
|
1202
|
+
attributes: {
|
1203
|
+
'id' => 0,
|
1204
|
+
'title' => 'Gideon the Ninth',
|
1205
|
+
'author' => 'T. M.'
|
1206
|
+
}
|
1207
|
+
)
|
1208
|
+
books_collection.insert_one.call(entity: entity)
|
1209
|
+
|
1210
|
+
# Creating An Entity
|
1211
|
+
books_collection.count
|
1212
|
+
#=> 1
|
1213
|
+
result = command.call(
|
1214
|
+
attributes: {
|
1215
|
+
'id' => 1,
|
1216
|
+
'title' => 'Gideon the Ninth',
|
1217
|
+
'author' => 'Tamsyn Muir'
|
1218
|
+
}
|
1219
|
+
)
|
1220
|
+
result.value
|
1221
|
+
#=> a Book with id 1, title "Gideon the Ninth", and author "Tamsyn Muir"
|
1222
|
+
books_collection.count
|
1223
|
+
#=> 1
|
1224
|
+
|
1225
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.count
|
1226
|
+
#=> 2
|
1227
|
+
books_collection.find_matching.call { { 'title' => 'Gideon the Ninth' } }.value.map(&:author)
|
1228
|
+
#=> ['T. M.', 'Tamsyn Muir']
|
1229
|
+
|
1230
|
+
# Updating An Entity
|
1231
|
+
result = command.call(
|
1232
|
+
attributes: {
|
1233
|
+
'title' => 'Gideon the Ninth',
|
1234
|
+
'author' => 'Tamsyn Muir',
|
1235
|
+
'series' => 'The Locked Tomb'
|
1236
|
+
}
|
1237
|
+
)
|
1238
|
+
result.value
|
1239
|
+
#=> a Book with id 1, title "Gideon the Ninth", author "Tamsyn Muir", and series
|
1240
|
+
# "The Locked Tomb"
|
1241
|
+
|
1242
|
+
books_collection.find_matching.call do
|
1243
|
+
{
|
1244
|
+
'title' => 'Gideon the Ninth',
|
1245
|
+
'author' => 'Tamsyn Muir'
|
1246
|
+
}
|
1247
|
+
end.value.first
|
1248
|
+
#=> a Book with id 1, title "Gideon the Ninth", author "Tamsyn Muir", and series
|
1249
|
+
# "The Locked Tomb"
|
1250
|
+
```
|
1251
|
+
|
1252
|
+
If there are two or more entities in the collection matching the attributes or query, the `Upsert` command returns a failing result with a `Cuprum::Collections::Errors::NotUnique` error.
|
1253
|
+
|
1254
|
+
If the contract does not match the entity, the `Upsert` command will return a failing result with a `ValidationFailed` error.
|
1255
|
+
|
1256
|
+
If the collection does not specify a default contract and no :contract keyword is provided, the `Upsert` command will return a failing result with a `MissingDefaultContract` error.
|
@@ -17,6 +17,8 @@ module Cuprum::Collections::Basic
|
|
17
17
|
# Defaults to :id.
|
18
18
|
# @param primary_key_type [Class, Stannum::Constraint] The type of the
|
19
19
|
# primary key attribute. Defaults to Integer.
|
20
|
+
# @param qualified_name [String] The qualified name of the collection, which
|
21
|
+
# should be unique. Defaults to the collection name.
|
20
22
|
# @param options [Hash<Symbol>] Additional options for the command.
|
21
23
|
def initialize( # rubocop:disable Metrics/ParameterLists
|
22
24
|
collection_name:,
|
@@ -25,6 +27,7 @@ module Cuprum::Collections::Basic
|
|
25
27
|
member_name: nil,
|
26
28
|
primary_key_name: :id,
|
27
29
|
primary_key_type: Integer,
|
30
|
+
qualified_name: nil,
|
28
31
|
**options
|
29
32
|
)
|
30
33
|
super()
|
@@ -37,6 +40,7 @@ module Cuprum::Collections::Basic
|
|
37
40
|
@options = options
|
38
41
|
@primary_key_name = primary_key_name
|
39
42
|
@primary_key_type = primary_key_type
|
43
|
+
@qualified_name = qualified_name || @collection_name
|
40
44
|
end
|
41
45
|
|
42
46
|
# @return [String] the name of the collection.
|
@@ -62,6 +66,9 @@ module Cuprum::Collections::Basic
|
|
62
66
|
# attribute.
|
63
67
|
attr_reader :primary_key_type
|
64
68
|
|
69
|
+
# @return [String] the qualified name of the collection.
|
70
|
+
attr_reader :qualified_name
|
71
|
+
|
65
72
|
command_class :assign_one do
|
66
73
|
Cuprum::Collections::Basic::Commands::AssignOne
|
67
74
|
.subclass(**command_options)
|
@@ -107,6 +114,12 @@ module Cuprum::Collections::Basic
|
|
107
114
|
.subclass(**command_options)
|
108
115
|
end
|
109
116
|
|
117
|
+
# @return [Integer] the count of items in the collection.
|
118
|
+
def count
|
119
|
+
query.count
|
120
|
+
end
|
121
|
+
alias size count
|
122
|
+
|
110
123
|
# A new Query instance, used for querying against the collection data.
|
111
124
|
#
|
112
125
|
# @return [Cuprum::Collections::Basic::Query] the query.
|
@@ -28,9 +28,10 @@ module Cuprum::Collections::Basic::Commands
|
|
28
28
|
return if index
|
29
29
|
|
30
30
|
error = Cuprum::Collections::Errors::NotFound.new(
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
attribute_name: primary_key_name,
|
32
|
+
attribute_value: primary_key,
|
33
|
+
collection_name: collection_name,
|
34
|
+
primary_key: true
|
34
35
|
)
|
35
36
|
Cuprum::Result.new(error: error)
|
36
37
|
end
|
@@ -32,9 +32,10 @@ module Cuprum::Collections::Basic::Commands
|
|
32
32
|
return if index.nil?
|
33
33
|
|
34
34
|
error = Cuprum::Collections::Errors::AlreadyExists.new(
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
attribute_name: primary_key_name,
|
36
|
+
attribute_value: value,
|
37
|
+
collection_name: collection_name,
|
38
|
+
primary_key: true
|
38
39
|
)
|
39
40
|
failure(error)
|
40
41
|
end
|
@@ -32,9 +32,10 @@ module Cuprum::Collections::Basic::Commands
|
|
32
32
|
return index unless index.nil?
|
33
33
|
|
34
34
|
error = Cuprum::Collections::Errors::NotFound.new(
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
attribute_name: primary_key_name,
|
36
|
+
attribute_value: entity[primary_key_name.to_s],
|
37
|
+
collection_name: collection_name,
|
38
|
+
primary_key: true
|
38
39
|
)
|
39
40
|
failure(error)
|
40
41
|
end
|
@@ -151,9 +151,9 @@ module Cuprum::Collections::Basic
|
|
151
151
|
def filtered_data
|
152
152
|
@filtered_data ||=
|
153
153
|
data
|
154
|
-
.
|
155
|
-
.
|
156
|
-
.
|
154
|
+
.then { |ary| apply_filters(ary) }
|
155
|
+
.then { |ary| apply_order(ary) }
|
156
|
+
.then { |ary| apply_limit_offset(ary) }
|
157
157
|
.map(&:dup)
|
158
158
|
end
|
159
159
|
end
|
@@ -17,39 +17,41 @@ module Cuprum::Collections::Commands
|
|
17
17
|
(scope || build_query).where { { key => one_of(primary_keys) } }
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def build_results(items:, primary_keys:)
|
21
|
+
primary_keys.map do |primary_key_value|
|
22
|
+
next success(items[primary_key_value]) if items.key?(primary_key_value)
|
22
23
|
|
23
|
-
|
24
|
+
failure(not_found_error(primary_key_value))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_result_list(results, allow_partial:, envelope:)
|
29
|
+
unless envelope
|
30
|
+
return Cuprum::ResultList.new(*results, allow_partial: allow_partial)
|
31
|
+
end
|
24
32
|
|
25
|
-
|
33
|
+
value = envelope ? wrap_items(results.map(&:value)) : nil
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
Cuprum::ResultList.new(
|
36
|
+
*results,
|
37
|
+
allow_partial: allow_partial,
|
38
|
+
value: value
|
31
39
|
)
|
32
|
-
Cuprum::Result.new(error: error)
|
33
40
|
end
|
34
41
|
|
35
42
|
def items_with_primary_keys(items:)
|
36
43
|
# :nocov:
|
37
|
-
items.
|
44
|
+
items.to_h { |item| [item.send(primary_key_name), item] }
|
38
45
|
# :nocov:
|
39
46
|
end
|
40
47
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
item.nil? ? (missing << key) : (found << item)
|
50
|
-
end
|
51
|
-
|
52
|
-
[found, missing]
|
48
|
+
def not_found_error(primary_key_value)
|
49
|
+
Cuprum::Collections::Errors::NotFound.new(
|
50
|
+
attribute_name: primary_key_name,
|
51
|
+
attribute_value: primary_key_value,
|
52
|
+
collection_name: collection_name,
|
53
|
+
primary_key: true
|
54
|
+
)
|
53
55
|
end
|
54
56
|
|
55
57
|
def process(
|
@@ -58,16 +60,15 @@ module Cuprum::Collections::Commands
|
|
58
60
|
envelope: false,
|
59
61
|
scope: nil
|
60
62
|
)
|
61
|
-
query
|
62
|
-
items
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
envelope ? wrap_items(items) : items
|
63
|
+
query = apply_query(primary_keys: primary_keys, scope: scope)
|
64
|
+
items = items_with_primary_keys(items: query.to_a)
|
65
|
+
results = build_results(items: items, primary_keys: primary_keys)
|
66
|
+
|
67
|
+
build_result_list(
|
68
|
+
results,
|
69
|
+
allow_partial: allow_partial,
|
70
|
+
envelope: envelope
|
71
|
+
)
|
71
72
|
end
|
72
73
|
|
73
74
|
def wrap_items(items)
|
@@ -21,9 +21,10 @@ module Cuprum::Collections::Commands
|
|
21
21
|
return if item
|
22
22
|
|
23
23
|
error = Cuprum::Collections::Errors::NotFound.new(
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
attribute_name: primary_key_name,
|
25
|
+
attribute_value: primary_key,
|
26
|
+
collection_name: collection_name,
|
27
|
+
primary_key: true
|
27
28
|
)
|
28
29
|
Cuprum::Result.new(error: error)
|
29
30
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/commands'
|
4
|
+
|
5
|
+
module Cuprum::Collections::Commands
|
6
|
+
# Command for building, validating and inserting an entity into a collection.
|
7
|
+
#
|
8
|
+
# @example Creating An Entity
|
9
|
+
# command =
|
10
|
+
# Cuprum::Collections::Commands::Create.new(collection:)
|
11
|
+
# .new(collection: books_collection)
|
12
|
+
#
|
13
|
+
# # With Invalid Attributes
|
14
|
+
# attributes = { 'title' => '' }
|
15
|
+
# result = command.call(attributes: attributes)
|
16
|
+
# result.success?
|
17
|
+
# #=> false
|
18
|
+
# result.error
|
19
|
+
# #=> an instance of Cuprum::Collections::Errors::FailedValidation
|
20
|
+
# books_collection.query.count
|
21
|
+
# #=> 0
|
22
|
+
#
|
23
|
+
# # With Valid Attributes
|
24
|
+
# attributes = { 'title' => 'Gideon the Ninth' }
|
25
|
+
# result = command.call(attributes: attributes)
|
26
|
+
# result.success?
|
27
|
+
# #=> true
|
28
|
+
# result.value
|
29
|
+
# #=> a Book with title 'Gideon the Ninth'
|
30
|
+
# books_collection.query.count
|
31
|
+
# #=> 1
|
32
|
+
class Create < Cuprum::Command
|
33
|
+
# @param collection [Object] The collection used to store the entity.
|
34
|
+
# @param contract [Stannum::Constraint] The constraint used to validate the
|
35
|
+
# entity. If not given, defaults to the default contract for the
|
36
|
+
# collection.
|
37
|
+
def initialize(collection:, contract: nil)
|
38
|
+
super()
|
39
|
+
|
40
|
+
@collection = collection
|
41
|
+
@contract = contract
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Object] the collection used to store the entity.
|
45
|
+
attr_reader :collection
|
46
|
+
|
47
|
+
# @return [Stannum::Constraint] the constraint used to validate the entity.
|
48
|
+
attr_reader :contract
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def process(attributes:)
|
53
|
+
entity = step { collection.build_one.call(attributes: attributes) }
|
54
|
+
|
55
|
+
step { collection.validate_one.call(contract: contract, entity: entity) }
|
56
|
+
|
57
|
+
collection.insert_one.call(entity: entity)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|