cuprum-collections 0.2.0 → 0.3.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/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
|