cuprum-collections 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +59 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/DEVELOPMENT.md +25 -0
  5. data/LICENSE +22 -0
  6. data/README.md +950 -0
  7. data/lib/cuprum/collections/base.rb +11 -0
  8. data/lib/cuprum/collections/basic/collection.rb +135 -0
  9. data/lib/cuprum/collections/basic/command.rb +112 -0
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +54 -0
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +45 -0
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +48 -0
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +65 -0
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +126 -0
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +49 -0
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +50 -0
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +52 -0
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +69 -0
  19. data/lib/cuprum/collections/basic/commands.rb +18 -0
  20. data/lib/cuprum/collections/basic/query.rb +160 -0
  21. data/lib/cuprum/collections/basic/query_builder.rb +69 -0
  22. data/lib/cuprum/collections/basic/rspec/command_contract.rb +392 -0
  23. data/lib/cuprum/collections/basic/rspec.rb +8 -0
  24. data/lib/cuprum/collections/basic.rb +22 -0
  25. data/lib/cuprum/collections/command.rb +26 -0
  26. data/lib/cuprum/collections/commands/abstract_find_many.rb +77 -0
  27. data/lib/cuprum/collections/commands/abstract_find_matching.rb +64 -0
  28. data/lib/cuprum/collections/commands/abstract_find_one.rb +44 -0
  29. data/lib/cuprum/collections/commands.rb +8 -0
  30. data/lib/cuprum/collections/constraints/attribute_name.rb +22 -0
  31. data/lib/cuprum/collections/constraints/order/attributes_array.rb +26 -0
  32. data/lib/cuprum/collections/constraints/order/attributes_hash.rb +27 -0
  33. data/lib/cuprum/collections/constraints/order/complex_ordering.rb +46 -0
  34. data/lib/cuprum/collections/constraints/order/sort_direction.rb +32 -0
  35. data/lib/cuprum/collections/constraints/order.rb +8 -0
  36. data/lib/cuprum/collections/constraints/ordering.rb +114 -0
  37. data/lib/cuprum/collections/constraints/query_hash.rb +25 -0
  38. data/lib/cuprum/collections/constraints.rb +8 -0
  39. data/lib/cuprum/collections/errors/already_exists.rb +86 -0
  40. data/lib/cuprum/collections/errors/extra_attributes.rb +66 -0
  41. data/lib/cuprum/collections/errors/failed_validation.rb +66 -0
  42. data/lib/cuprum/collections/errors/invalid_parameters.rb +50 -0
  43. data/lib/cuprum/collections/errors/invalid_query.rb +55 -0
  44. data/lib/cuprum/collections/errors/missing_default_contract.rb +49 -0
  45. data/lib/cuprum/collections/errors/not_found.rb +81 -0
  46. data/lib/cuprum/collections/errors/unknown_operator.rb +71 -0
  47. data/lib/cuprum/collections/errors.rb +8 -0
  48. data/lib/cuprum/collections/queries/ordering.rb +74 -0
  49. data/lib/cuprum/collections/queries/parse.rb +22 -0
  50. data/lib/cuprum/collections/queries/parse_block.rb +206 -0
  51. data/lib/cuprum/collections/queries/parse_strategy.rb +91 -0
  52. data/lib/cuprum/collections/queries.rb +25 -0
  53. data/lib/cuprum/collections/query.rb +247 -0
  54. data/lib/cuprum/collections/query_builder.rb +61 -0
  55. data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +168 -0
  56. data/lib/cuprum/collections/rspec/build_one_command_contract.rb +93 -0
  57. data/lib/cuprum/collections/rspec/collection_contract.rb +153 -0
  58. data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +106 -0
  59. data/lib/cuprum/collections/rspec/find_many_command_contract.rb +327 -0
  60. data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +194 -0
  61. data/lib/cuprum/collections/rspec/find_one_command_contract.rb +154 -0
  62. data/lib/cuprum/collections/rspec/fixtures.rb +89 -0
  63. data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +83 -0
  64. data/lib/cuprum/collections/rspec/query_builder_contract.rb +92 -0
  65. data/lib/cuprum/collections/rspec/query_contract.rb +650 -0
  66. data/lib/cuprum/collections/rspec/querying_contract.rb +298 -0
  67. data/lib/cuprum/collections/rspec/update_one_command_contract.rb +79 -0
  68. data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +96 -0
  69. data/lib/cuprum/collections/rspec.rb +8 -0
  70. data/lib/cuprum/collections/version.rb +59 -0
  71. data/lib/cuprum/collections.rb +26 -0
  72. metadata +219 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/command_factory'
4
+
5
+ require 'cuprum/collections'
6
+
7
+ module Cuprum::Collections
8
+ # Abstract base class for Collection factories.
9
+ class Base < Cuprum::CommandFactory
10
+ end
11
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/command_factory'
4
+
5
+ require 'cuprum/collections/basic'
6
+ require 'cuprum/collections/basic/commands'
7
+
8
+ module Cuprum::Collections::Basic
9
+ # Wraps an in-memory array of hashes data store as a Cuprum collection.
10
+ class Collection < Cuprum::CommandFactory
11
+ # @param collection_name [String, Symbol] The name of the collection.
12
+ # @param data [Array<Hash>] The current data in the collection.
13
+ # @param default_contract [Stannum::Constraints::Base, nil] The default
14
+ # contract for validating items in the collection.
15
+ # @param member_name [String] The name of a collection entity.
16
+ # @param primary_key_name [Symbol] The name of the primary key attribute.
17
+ # Defaults to :id.
18
+ # @param primary_key_type [Class, Stannum::Constraint] The type of the
19
+ # primary key attribute. Defaults to Integer.
20
+ # @param options [Hash<Symbol>] Additional options for the command.
21
+ def initialize( # rubocop:disable Metrics/ParameterLists
22
+ collection_name:,
23
+ data:,
24
+ default_contract: nil,
25
+ member_name: nil,
26
+ primary_key_name: :id,
27
+ primary_key_type: Integer,
28
+ **options
29
+ )
30
+ super()
31
+
32
+ @collection_name = collection_name.to_s
33
+ @data = data
34
+ @default_contract = default_contract
35
+ @member_name =
36
+ member_name ? member_name.to_s : tools.str.singularize(@collection_name)
37
+ @options = options
38
+ @primary_key_name = primary_key_name
39
+ @primary_key_type = primary_key_type
40
+ end
41
+
42
+ # @return [String] the name of the collection.
43
+ attr_reader :collection_name
44
+
45
+ # @return [Array<Hash>] the current data in the collection.
46
+ attr_reader :data
47
+
48
+ # @return [Stannum::Constraints::Base, nil] the default contract for
49
+ # validating items in the collection.
50
+ attr_reader :default_contract
51
+
52
+ # @return [String] the name of a collection entity.
53
+ attr_reader :member_name
54
+
55
+ # @return [Hash<Symbol>] additional options for the command.
56
+ attr_reader :options
57
+
58
+ # @return [Symbol] the name of the primary key attribute.
59
+ attr_reader :primary_key_name
60
+
61
+ # @return [Class, Stannum::Constraint] the type of the primary key
62
+ # attribute.
63
+ attr_reader :primary_key_type
64
+
65
+ command_class :assign_one do
66
+ Cuprum::Collections::Basic::Commands::AssignOne
67
+ .subclass(**command_options)
68
+ end
69
+
70
+ command_class :build_one do
71
+ Cuprum::Collections::Basic::Commands::BuildOne
72
+ .subclass(**command_options)
73
+ end
74
+
75
+ command_class :destroy_one do
76
+ Cuprum::Collections::Basic::Commands::DestroyOne
77
+ .subclass(**command_options)
78
+ end
79
+
80
+ command_class :find_many do
81
+ Cuprum::Collections::Basic::Commands::FindMany
82
+ .subclass(**command_options)
83
+ end
84
+
85
+ command_class :find_matching do
86
+ Cuprum::Collections::Basic::Commands::FindMatching
87
+ .subclass(**command_options)
88
+ end
89
+
90
+ command_class :find_one do
91
+ Cuprum::Collections::Basic::Commands::FindOne
92
+ .subclass(**command_options)
93
+ end
94
+
95
+ command_class :insert_one do
96
+ Cuprum::Collections::Basic::Commands::InsertOne
97
+ .subclass(**command_options)
98
+ end
99
+
100
+ command_class :update_one do
101
+ Cuprum::Collections::Basic::Commands::UpdateOne
102
+ .subclass(**command_options)
103
+ end
104
+
105
+ command_class :validate_one do
106
+ Cuprum::Collections::Basic::Commands::ValidateOne
107
+ .subclass(**command_options)
108
+ end
109
+
110
+ # A new Query instance, used for querying against the collection data.
111
+ #
112
+ # @return [Cuprum::Collections::Basic::Query] the query.
113
+ def query
114
+ Cuprum::Collections::Basic::Query.new(data)
115
+ end
116
+
117
+ private
118
+
119
+ def command_options
120
+ @command_options ||= {
121
+ collection_name: collection_name,
122
+ data: data,
123
+ default_contract: default_contract,
124
+ member_name: member_name,
125
+ primary_key_name: primary_key_name,
126
+ primary_key_type: primary_key_type,
127
+ **options
128
+ }
129
+ end
130
+
131
+ def tools
132
+ SleepingKingStudios::Tools::Toolbelt.instance
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/types/array_type'
4
+
5
+ require 'cuprum/collections/basic'
6
+
7
+ module Cuprum::Collections::Basic
8
+ # Abstract base class for basic collection commands.
9
+ class Command < Cuprum::Collections::Command
10
+ # Creates a subclass with the given parameters applied to the constructor.
11
+ def self.subclass(**default_options)
12
+ Class.new(self) do
13
+ define_method(:initialize) do |**options|
14
+ super(**default_options.merge(options))
15
+ end
16
+ end
17
+ end
18
+
19
+ # @param collection_name [String, Symbol] The name of the collection.
20
+ # @param data [Array<Hash>] The current data in the collection.
21
+ # @param default_contract [Stannum::Constraints::Base, nil] The default
22
+ # contract for validating items in the collection.
23
+ # @param member_name [String] The name of a collection entity.
24
+ # @param primary_key_name [Symbol] The name of the primary key attribute.
25
+ # Defaults to :id.
26
+ # @param primary_key_type [Class, Stannum::Constraint] The type of the
27
+ # primary key attribute. Defaults to Integer.
28
+ # @param options [Hash<Symbol>] Additional options for the command.
29
+ def initialize( # rubocop:disable Metrics/ParameterLists
30
+ collection_name:,
31
+ data:,
32
+ default_contract: nil,
33
+ member_name: nil,
34
+ primary_key_name: :id,
35
+ primary_key_type: Integer,
36
+ **options
37
+ )
38
+ super()
39
+
40
+ @collection_name = collection_name.to_s
41
+ @data = data
42
+ @default_contract = default_contract
43
+ @member_name =
44
+ member_name ? member_name.to_s : tools.str.singularize(@collection_name)
45
+ @options = options
46
+ @primary_key_name = primary_key_name
47
+ @primary_key_type = primary_key_type
48
+ end
49
+
50
+ # @return [String] the name of the collection.
51
+ attr_reader :collection_name
52
+
53
+ # @return [Array<Hash>] the current data in the collection.
54
+ attr_reader :data
55
+
56
+ # @return [Stannum::Constraints::Base, nil] the default contract for
57
+ # validating items in the collection.
58
+ attr_reader :default_contract
59
+
60
+ # @return [String] the name of a collection entity.
61
+ attr_reader :member_name
62
+
63
+ # @return [Hash<Symbol>] additional options for the command.
64
+ attr_reader :options
65
+
66
+ # @return [Symbol] the name of the primary key attribute.
67
+ attr_reader :primary_key_name
68
+
69
+ # @return [Class, Stannum::Constraint] the type of the primary key
70
+ # attribute.
71
+ attr_reader :primary_key_type
72
+
73
+ private
74
+
75
+ def primary_key_contract
76
+ type = primary_key_type
77
+
78
+ @primary_key_contract ||= Stannum::Contracts::ParametersContract.new do
79
+ keyword :primary_key, type
80
+ end
81
+ end
82
+
83
+ def primary_keys_contract
84
+ type = primary_key_type
85
+
86
+ @primary_keys_contract ||= Stannum::Contracts::ParametersContract.new do
87
+ keyword :primary_keys,
88
+ Stannum::Constraints::Types::ArrayType.new(item_type: type)
89
+ end
90
+ end
91
+
92
+ def tools
93
+ SleepingKingStudios::Tools::Toolbelt.instance
94
+ end
95
+
96
+ def validate_primary_key(primary_key)
97
+ match_parameters_to_contract(
98
+ contract: primary_key_contract,
99
+ keywords: { primary_key: primary_key },
100
+ method_name: :call
101
+ )
102
+ end
103
+
104
+ def validate_primary_keys(primary_keys)
105
+ match_parameters_to_contract(
106
+ contract: primary_keys_contract,
107
+ keywords: { primary_keys: primary_keys },
108
+ method_name: :call
109
+ )
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/types/hash_with_indifferent_keys'
4
+ require 'stannum/constraints/types/hash_with_string_keys'
5
+
6
+ require 'cuprum/collections/basic/command'
7
+ require 'cuprum/collections/basic/commands'
8
+
9
+ module Cuprum::Collections::Basic::Commands
10
+ # Command for assigning attributes to a collection entity.
11
+ class AssignOne < Cuprum::Collections::Basic::Command
12
+ # @!method call(attributes:, entity:)
13
+ # Assigns the given attributes to the entity.
14
+ #
15
+ # Any attributes on the entity that are not part of the given attributes
16
+ # hash are unchanged.
17
+ #
18
+ # @param attributes [Hash] The attributes and values to update.
19
+ # @param entity [Hash] The collection entity to update.
20
+ #
21
+ # @return [Hash] a copy of the entity, merged with the given attributes.
22
+ #
23
+ # @example Assigning attributes
24
+ # entity = {
25
+ # 'title' => 'The Hobbit',
26
+ # 'author' => 'J.R.R. Tolkien',
27
+ # 'series' => nil,
28
+ # 'category' => 'Science Fiction and Fantasy'
29
+ # }
30
+ # attributes = { title: 'The Silmarillion' }
31
+ # command = AssignOne.new(collection_name: 'books', data: books)
32
+ # command.call(attributes: attributes, entity: entity)
33
+ # #=> {
34
+ # 'title' => 'The Silmarillion',
35
+ # 'author' => 'J.R.R. Tolkien',
36
+ # 'series' => nil,
37
+ # 'category' => 'Science Fiction and Fantasy'
38
+ # }
39
+ validate_parameters :call do
40
+ keyword :attributes,
41
+ Stannum::Constraints::Types::HashWithIndifferentKeys.new
42
+ keyword :entity,
43
+ Stannum::Constraints::Types::HashWithStringKeys.new
44
+ end
45
+
46
+ private
47
+
48
+ def process(attributes:, entity:)
49
+ attributes = tools.hsh.convert_keys_to_strings(attributes)
50
+
51
+ entity.merge(attributes)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/types/hash_with_indifferent_keys'
4
+
5
+ require 'cuprum/collections/basic/command'
6
+ require 'cuprum/collections/basic/commands'
7
+
8
+ module Cuprum::Collections::Basic::Commands
9
+ # Command for generating a collection entity from an attributes hash.
10
+ class BuildOne < Cuprum::Collections::Basic::Command
11
+ # @!method call(attributes:, entity:)
12
+ # Builds a new entity with the given attributes.
13
+ #
14
+ # @param attributes [Hash] The attributes and values to assign.
15
+ #
16
+ # @return [Hash] the newly built entity.
17
+ #
18
+ # @example Building an entity
19
+ # attributes = {
20
+ # 'title' => 'The Hobbit',
21
+ # 'author' => 'J.R.R. Tolkien',
22
+ # 'series' => nil,
23
+ # 'category' => 'Science Fiction and Fantasy'
24
+ # }
25
+ # command = BuildOne.new(collection_name: 'books', data: books)
26
+ # result = command.call(attributes: attributes)
27
+ # result.value
28
+ # #=> {
29
+ # 'title' => 'The Silmarillion',
30
+ # 'author' => 'J.R.R. Tolkien',
31
+ # 'series' => nil,
32
+ # 'category' => 'Science Fiction and Fantasy'
33
+ # }
34
+ validate_parameters :call do
35
+ keyword :attributes,
36
+ Stannum::Constraints::Types::HashWithIndifferentKeys.new
37
+ end
38
+
39
+ private
40
+
41
+ def process(attributes:)
42
+ tools.hsh.convert_keys_to_strings(attributes)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/basic/command'
4
+ require 'cuprum/collections/basic/commands'
5
+ require 'cuprum/collections/errors/not_found'
6
+
7
+ module Cuprum::Collections::Basic::Commands
8
+ # Command for destroying one collection item by primary key.
9
+ class DestroyOne < Cuprum::Collections::Basic::Command
10
+ # @!method call(primary_key:)
11
+ # Finds and destroys the item with the given primary key.
12
+ #
13
+ # The command will find the entity with the given primary key and remove
14
+ # it from the collection. If the entity is not found, the command will
15
+ # fail and return a NotFound error.
16
+ #
17
+ # @param primary_key [Object] The primary key of the requested item.
18
+ #
19
+ # @return [Cuprum::Result<Hash{String, Object}>] a result with the
20
+ # destroyed item.
21
+ validate_parameters :call do
22
+ keyword :primary_key, Object
23
+ end
24
+
25
+ private
26
+
27
+ def handle_missing_item(index:, primary_key:)
28
+ return if index
29
+
30
+ error = Cuprum::Collections::Errors::NotFound.new(
31
+ collection_name: collection_name,
32
+ primary_key_name: primary_key_name,
33
+ primary_key_values: [primary_key]
34
+ )
35
+ Cuprum::Result.new(error: error)
36
+ end
37
+
38
+ def process(primary_key:)
39
+ step { validate_primary_key(primary_key) }
40
+
41
+ index = data.index { |item| item[primary_key_name.to_s] == primary_key }
42
+
43
+ step { handle_missing_item(index: index, primary_key: primary_key) }
44
+
45
+ data.delete_at(index)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/boolean'
4
+
5
+ require 'cuprum/collections/basic/command'
6
+ require 'cuprum/collections/basic/commands'
7
+ require 'cuprum/collections/commands/abstract_find_many'
8
+
9
+ module Cuprum::Collections::Basic::Commands
10
+ # Command for finding multiple collection items by primary key.
11
+ class FindMany < Cuprum::Collections::Basic::Command
12
+ include Cuprum::Collections::Commands::AbstractFindMany
13
+
14
+ # @!method call(primary_keys:, allow_partial: false, envelope: false, scope: nil) # rubocop:disable Layout/LineLength
15
+ # Queries the collection for the items with the given primary keys.
16
+ #
17
+ # The command will find and return the entities with the given primary
18
+ # keys. If any of the items are not found, the command will fail and
19
+ # return a NotFound error. If the :allow_partial option is set, the
20
+ # command will return a partial result unless none of the requested items
21
+ # are found.
22
+ #
23
+ # When the :envelope option is true, the command wraps the items in a
24
+ # Hash, using the name of the collection as the key.
25
+ #
26
+ # @param allow_partial [Boolean] If true, passes if any of the items are
27
+ # found.
28
+ # @param envelope [Boolean] If true, wraps the result value in a Hash.
29
+ # @param primary_keys [Array] The primary keys of the requested items.
30
+ # @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
31
+ # the query. Items must match the scope as well as the primary keys.
32
+ #
33
+ # @return [Cuprum::Result<Array<Hash{String, Object}>>] a result with the
34
+ # requested items.
35
+ validate_parameters :call do
36
+ keyword :allow_partial, Stannum::Constraints::Boolean.new, default: true
37
+ keyword :envelope, Stannum::Constraints::Boolean.new, default: true
38
+ keyword :primary_keys, Array
39
+ keyword :scope, Cuprum::Collections::Basic::Query, optional: true
40
+ end
41
+
42
+ private
43
+
44
+ def build_query
45
+ Cuprum::Collections::Basic::Query.new(data)
46
+ end
47
+
48
+ def items_with_primary_keys(items:)
49
+ # :nocov:
50
+ items.map { |item| [item[primary_key_name.to_s], item] }.to_h
51
+ # :nocov:
52
+ end
53
+
54
+ def process(
55
+ primary_keys:,
56
+ allow_partial: false,
57
+ envelope: false,
58
+ scope: nil
59
+ )
60
+ step { validate_primary_keys(primary_keys) }
61
+
62
+ super
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/boolean'
4
+
5
+ require 'cuprum/collections/basic/command'
6
+ require 'cuprum/collections/basic/commands'
7
+ require 'cuprum/collections/basic/query'
8
+ require 'cuprum/collections/commands/abstract_find_matching'
9
+ require 'cuprum/collections/constraints/ordering'
10
+
11
+ module Cuprum::Collections::Basic::Commands
12
+ # Command for querying filtered, ordered data from a basic collection.
13
+ class FindMatching < Cuprum::Collections::Basic::Command
14
+ include Cuprum::Collections::Commands::AbstractFindMatching
15
+
16
+ # @!method call(envelope: false, limit: nil, offset: nil, order: nil, scope: nil, where: nil, &block) # rubocop:disable Layout/LineLength
17
+ # Queries the collection for items matching the given conditions.
18
+ #
19
+ # @param envelope [Boolean] If true, wraps the result value in a Hash.
20
+ # @param limit [Integer] The maximum number of results to return.
21
+ # @param offset [Integer] The initial ordered items to skip.
22
+ # @param order [Array<String, Symbol>, Hash<{String, Symbol => Symbol}>]
23
+ # The sort order of the returned items. Should be either an array of
24
+ # attribute names or a hash of attribute names and directions.
25
+ # @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
26
+ # the query. Items must match the scope as well as the :where filters.
27
+ # @param where [Object] Additional filters for selecting data. The command
28
+ # will only return data matching these filters.
29
+ # @yield The given block is passed to a QueryBuilder, which converts the
30
+ # block to query criteria and generates a new query using those
31
+ # criteria.
32
+ # @yieldreturn [Hash] The filters to apply to the query. The hash keys
33
+ # should be the names of attributes or columns, and the corresponding
34
+ # values should be either the literal value for that attribute or a
35
+ # method call for a valid operation defined for the query.
36
+ #
37
+ # @example Querying all items in the collection.
38
+ # command = FindMatching.new(collection_name: 'books', data: books)
39
+ # command.call
40
+ # #=> an enumerable iterating all items in the collection
41
+ #
42
+ # @example Querying all items matching some critera:
43
+ # command.call { { author: 'Nnedi Okorafor' } }
44
+ # #=> an enumerable iterating all items in the collection whose author
45
+ # is 'Nnedi Okorafor'
46
+ #
47
+ # @example Ordering query results
48
+ # command.call(order: :title) { { author: 'Nnedi Okorafor' } }
49
+ # #=> an enumerable iterating all items in the collection whose author
50
+ # # is 'Nnedi Okorafor', sorted by :title in ascending order
51
+ #
52
+ # @example Advanced filtering
53
+ # command.call do
54
+ # {
55
+ # category: eq('Science Fiction and Fantasy'),
56
+ # author: ne('J.R.R. Tolkien')
57
+ # }
58
+ # end
59
+ # #=> an enumerable iterating all items in the collection whose category
60
+ # # is 'Science Fiction and Fantasy', and whose author is not
61
+ # # 'J.R.R. Tolkien'.
62
+ #
63
+ # @example Advanced ordering
64
+ # order = { author: :asc, genre: :desc }
65
+ # command.call(order: order) { { author: 'Nnedi Okorafor' } }
66
+ # #=> an enumerable iterating all items in the collection whose author
67
+ # # is 'Nnedi Okorafor', sorted first by :author in ascending order
68
+ # # and within the same author by genre in descending order
69
+ #
70
+ # @example Filtering, ordering, and subsets
71
+ # command.call(offset: 50, limit: 10, order: :author) do
72
+ # { category: 'Science Fiction and Fantasy' }
73
+ # end
74
+ # #=> an enumerable iterating the 51st through 60th items in the
75
+ # # collection whose category is 'Science Fiction and Fantasy', sorted
76
+ # # by :author in ascending order.
77
+ #
78
+ # @example Wrapping the result in an envelope
79
+ # command =
80
+ # FindMatching.new(
81
+ # collection_name: 'books',
82
+ # data: books
83
+ # )
84
+ # command.call(envelope: true)
85
+ # #=> {
86
+ # 'books' => [] # an array containing the matching items
87
+ # }
88
+ #
89
+ # @overload call(limit: nil, offset: nil, order: nil, &block)
90
+ # When the :envelope option is false (default), the command returns an
91
+ # Enumerator which can be iterated to return the matching items.
92
+ #
93
+ # @return [Cuprum::Result<Enumerator>] the matching items in the
94
+ # specified order as an Enumerator.
95
+ #
96
+ # @overload call(limit: nil, offset: nil, order: nil, &block)
97
+ # When the :envelope option is true, the command immediately evaluates
98
+ # the query and wraps the resulting array in a Hash, using the name of
99
+ # the collection as the key.
100
+ #
101
+ # @return [Cuprum::Result<Hash{String, Array<Hash{String, Object}>}>] a
102
+ # hash with the collection name as key and the matching items as
103
+ # value.
104
+
105
+ validate_parameters :call do
106
+ keyword :envelope,
107
+ Stannum::Constraints::Boolean.new,
108
+ default: true
109
+ keyword :limit, Integer, optional: true
110
+ keyword :offset, Integer, optional: true
111
+ keyword :order,
112
+ Cuprum::Collections::Constraints::Ordering.new,
113
+ optional: true
114
+ keyword :scope,
115
+ Cuprum::Collections::Basic::Query,
116
+ optional: true
117
+ keyword :where, Object, optional: true
118
+ end
119
+
120
+ private
121
+
122
+ def build_query
123
+ Cuprum::Collections::Basic::Query.new(data)
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/boolean'
4
+
5
+ require 'cuprum/collections/basic/command'
6
+ require 'cuprum/collections/basic/commands'
7
+ require 'cuprum/collections/commands/abstract_find_one'
8
+
9
+ module Cuprum::Collections::Basic::Commands
10
+ # Command for finding one collection item by primary key.
11
+ class FindOne < Cuprum::Collections::Basic::Command
12
+ include Cuprum::Collections::Commands::AbstractFindOne
13
+
14
+ # @!method call(primary_key:, envelope: false, scope: nil)
15
+ # Queries the collection for the item with the given primary key.
16
+ #
17
+ # The command will find and return the entity with the given primary key.
18
+ # If the entity is not found, the command will fail and return a NotFound
19
+ # error.
20
+ #
21
+ # When the :envelope option is true, the command wraps the item in a Hash,
22
+ # using the singular name of the collection as the key.
23
+ #
24
+ # @param envelope [Boolean] If true, wraps the result value in a Hash.
25
+ # @param primary_key [Object] The primary key of the requested item.
26
+ # @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
27
+ # the query. The item must match the scope as well as the primary key.
28
+ #
29
+ # @return [Cuprum::Result<Hash{String, Object}>] a result with the
30
+ # requested item.
31
+ validate_parameters :call do
32
+ keyword :envelope, Stannum::Constraints::Boolean.new, default: true
33
+ keyword :primary_key, Object
34
+ keyword :scope, Cuprum::Collections::Basic::Query, optional: true
35
+ end
36
+
37
+ private
38
+
39
+ def build_query
40
+ Cuprum::Collections::Basic::Query.new(data)
41
+ end
42
+
43
+ def process(primary_key:, envelope: false, scope: nil)
44
+ step { validate_primary_key(primary_key) }
45
+
46
+ super
47
+ end
48
+ end
49
+ end