cuprum-collections 0.4.0 → 0.5.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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +5 -5
  4. data/lib/cuprum/collections/association.rb +9 -28
  5. data/lib/cuprum/collections/associations/belongs_to.rb +1 -8
  6. data/lib/cuprum/collections/associations/has_many.rb +1 -10
  7. data/lib/cuprum/collections/associations/has_one.rb +1 -10
  8. data/lib/cuprum/collections/basic/collection.rb +56 -49
  9. data/lib/cuprum/collections/basic/command.rb +22 -88
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +2 -6
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +1 -4
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -8
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +4 -24
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +5 -21
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +3 -20
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +3 -6
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +3 -6
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +13 -18
  19. data/lib/cuprum/collections/basic/query.rb +26 -40
  20. data/lib/cuprum/collections/basic/repository.rb +4 -3
  21. data/lib/cuprum/collections/basic/scopes/all_scope.rb +25 -0
  22. data/lib/cuprum/collections/basic/scopes/base.rb +32 -0
  23. data/lib/cuprum/collections/basic/scopes/builder.rb +39 -0
  24. data/lib/cuprum/collections/basic/scopes/conjunction_scope.rb +20 -0
  25. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +62 -0
  26. data/lib/cuprum/collections/basic/scopes/disjunction_scope.rb +20 -0
  27. data/lib/cuprum/collections/basic/scopes/none_scope.rb +33 -0
  28. data/lib/cuprum/collections/basic/scopes.rb +23 -0
  29. data/lib/cuprum/collections/basic.rb +1 -0
  30. data/lib/cuprum/collections/collection.rb +24 -82
  31. data/lib/cuprum/collections/collection_command.rb +116 -0
  32. data/lib/cuprum/collections/commands/abstract_find_many.rb +11 -21
  33. data/lib/cuprum/collections/commands/abstract_find_matching.rb +43 -24
  34. data/lib/cuprum/collections/commands/abstract_find_one.rb +7 -10
  35. data/lib/cuprum/collections/commands/associations/find_many.rb +3 -8
  36. data/lib/cuprum/collections/commands/associations/require_many.rb +5 -5
  37. data/lib/cuprum/collections/commands/create.rb +3 -3
  38. data/lib/cuprum/collections/commands/find_one_matching.rb +6 -6
  39. data/lib/cuprum/collections/commands/query_command.rb +19 -0
  40. data/lib/cuprum/collections/commands/update.rb +3 -3
  41. data/lib/cuprum/collections/commands/upsert.rb +10 -10
  42. data/lib/cuprum/collections/commands.rb +1 -0
  43. data/lib/cuprum/collections/constraints/ordering.rb +2 -2
  44. data/lib/cuprum/collections/errors/abstract_find_error.rb +25 -42
  45. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  46. data/lib/cuprum/collections/errors/failed_validation.rb +2 -2
  47. data/lib/cuprum/collections/errors/invalid_parameters.rb +2 -2
  48. data/lib/cuprum/collections/errors/invalid_query.rb +10 -16
  49. data/lib/cuprum/collections/errors/missing_default_contract.rb +1 -1
  50. data/lib/cuprum/collections/errors/unknown_operator.rb +1 -1
  51. data/lib/cuprum/collections/queries.rb +31 -0
  52. data/lib/cuprum/collections/query.rb +50 -62
  53. data/lib/cuprum/collections/relation.rb +5 -383
  54. data/lib/cuprum/collections/relations/cardinality.rb +66 -0
  55. data/lib/cuprum/collections/relations/options.rb +18 -0
  56. data/lib/cuprum/collections/relations/parameters.rb +217 -0
  57. data/lib/cuprum/collections/relations/primary_keys.rb +23 -0
  58. data/lib/cuprum/collections/relations/scope.rb +65 -0
  59. data/lib/cuprum/collections/relations.rb +14 -0
  60. data/lib/cuprum/collections/repository.rb +5 -5
  61. data/lib/cuprum/collections/resource.rb +10 -41
  62. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +80 -90
  63. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +69 -111
  64. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +42 -1335
  65. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +352 -531
  66. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +74 -191
  67. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +13 -13
  68. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +1029 -0
  69. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +856 -0
  70. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +1430 -0
  71. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +2217 -0
  72. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +297 -0
  73. data/lib/cuprum/collections/rspec/contracts/scopes.rb +13 -0
  74. data/lib/cuprum/collections/rspec/contracts.rb +2 -0
  75. data/lib/cuprum/collections/rspec/deferred/association_examples.rb +2098 -0
  76. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +338 -0
  77. data/lib/cuprum/collections/rspec/deferred/command_examples.rb +160 -0
  78. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +178 -0
  79. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +94 -0
  80. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +118 -0
  81. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +307 -0
  82. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +143 -0
  83. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +116 -0
  84. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +103 -0
  85. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +99 -0
  86. data/lib/cuprum/collections/rspec/deferred/commands/validate_one_examples.rb +117 -0
  87. data/lib/cuprum/collections/rspec/deferred/commands.rb +8 -0
  88. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +1437 -0
  89. data/lib/cuprum/collections/rspec/deferred/resource_examples.rb +26 -0
  90. data/lib/cuprum/collections/rspec/deferred.rb +8 -0
  91. data/lib/cuprum/collections/scope.rb +29 -0
  92. data/lib/cuprum/collections/scopes/all.rb +51 -0
  93. data/lib/cuprum/collections/scopes/all_scope.rb +18 -0
  94. data/lib/cuprum/collections/scopes/base.rb +79 -0
  95. data/lib/cuprum/collections/scopes/builder.rb +39 -0
  96. data/lib/cuprum/collections/scopes/building.rb +221 -0
  97. data/lib/cuprum/collections/scopes/composition.rb +162 -0
  98. data/lib/cuprum/collections/scopes/conjunction.rb +44 -0
  99. data/lib/cuprum/collections/scopes/conjunction_scope.rb +12 -0
  100. data/lib/cuprum/collections/scopes/container.rb +65 -0
  101. data/lib/cuprum/collections/scopes/criteria/parser.rb +241 -0
  102. data/lib/cuprum/collections/scopes/criteria.rb +206 -0
  103. data/lib/cuprum/collections/scopes/criteria_scope.rb +12 -0
  104. data/lib/cuprum/collections/scopes/disjunction.rb +45 -0
  105. data/lib/cuprum/collections/scopes/disjunction_scope.rb +12 -0
  106. data/lib/cuprum/collections/scopes/none.rb +62 -0
  107. data/lib/cuprum/collections/scopes/none_scope.rb +18 -0
  108. data/lib/cuprum/collections/scopes.rb +23 -0
  109. data/lib/cuprum/collections/version.rb +2 -2
  110. data/lib/cuprum/collections.rb +14 -9
  111. metadata +61 -15
  112. data/lib/cuprum/collections/basic/query_builder.rb +0 -69
  113. data/lib/cuprum/collections/command.rb +0 -26
  114. data/lib/cuprum/collections/queries/parse.rb +0 -22
  115. data/lib/cuprum/collections/queries/parse_block.rb +0 -206
  116. data/lib/cuprum/collections/queries/parse_strategy.rb +0 -91
  117. data/lib/cuprum/collections/query_builder.rb +0 -61
  118. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +0 -484
@@ -4,28 +4,24 @@ require 'cuprum/command_factory'
4
4
 
5
5
  require 'cuprum/collections'
6
6
  require 'cuprum/collections/relation'
7
+ require 'cuprum/collections/relations/options'
8
+ require 'cuprum/collections/relations/parameters'
9
+ require 'cuprum/collections/relations/primary_keys'
10
+ require 'cuprum/collections/relations/scope'
11
+ require 'cuprum/collections/scopes/all_scope'
7
12
 
8
13
  module Cuprum::Collections
9
14
  # Provides a base implementation for collections.
10
15
  class Collection < Cuprum::CommandFactory
11
- include Cuprum::Collections::Relation::Parameters
12
- include Cuprum::Collections::Relation::PrimaryKeys
13
- include Cuprum::Collections::Relation::Disambiguation
16
+ include Cuprum::Collections::Relations::Options
17
+ include Cuprum::Collections::Relations::Parameters
18
+ include Cuprum::Collections::Relations::PrimaryKeys
19
+ include Cuprum::Collections::Relations::Scope
14
20
 
15
21
  # Error raised when trying to call an abstract collection method.
16
22
  class AbstractCollectionError < StandardError; end
17
23
 
18
- IGNORED_PARAMETERS = %i[
19
- collection_name
20
- entity_class
21
- member_name
22
- name
23
- qualified_name
24
- singular_name
25
- ].freeze
26
- private_constant :IGNORED_PARAMETERS
27
-
28
- # @overload initialize(entity_class: nil, name: nil, qualified_name: nil, singular_name: nil, **options)
24
+ # @!method initialize(entity_class: nil, name: nil, qualified_name: nil, singular_name: nil, **options)
29
25
  # @param entity_class [Class, String] the class of entity represented by
30
26
  # the relation.
31
27
  # @param name [String] the name of the relation.
@@ -35,27 +31,11 @@ module Cuprum::Collections
35
31
  #
36
32
  # @option options primary_key_name [String] the name of the primary key
37
33
  # attribute. Defaults to 'id'.
38
- # @option primary_key_type [Class, Stannum::Constraint] the type of
39
- # the primary key attribute. Defaults to Integer.
40
- def initialize(**parameters) # rubocop:disable Metrics/MethodLength
41
- super()
42
-
43
- relation_params = resolve_parameters(
44
- parameters,
45
- name: :collection_name,
46
- singular_name: :member_name
47
- )
48
- @entity_class = relation_params[:entity_class]
49
- @name = relation_params[:name]
50
- @plural_name = relation_params[:plural_name]
51
- @qualified_name = relation_params[:qualified_name]
52
- @singular_name = relation_params[:singular_name]
53
-
54
- @options = ignore_parameters(**parameters)
55
- end
56
-
57
- # @return [Hash<Symbol>] additional options for the collection.
58
- attr_reader :options
34
+ # @option options primary_key_type [Class, Stannum::Constraint] the type
35
+ # of the primary key attribute. Defaults to Integer.
36
+ # @option options scope
37
+ # [Cuprum::Collections::Scopes::Base, Hash, Proc, nil] the configured
38
+ # scope for the relation.
59
39
 
60
40
  # @param other [Object] The object to compare.
61
41
  #
@@ -67,14 +47,6 @@ module Cuprum::Collections
67
47
  comparable_options == other.comparable_options
68
48
  end
69
49
 
70
- # @return [String] the name of the collection.
71
- def collection_name
72
- tools.core_tools.deprecate '#collection_name method',
73
- message: 'Use #name instead'
74
-
75
- name
76
- end
77
-
78
50
  # @return [Integer] the count of items in the collection.
79
51
  def count
80
52
  query.count
@@ -97,58 +69,28 @@ module Cuprum::Collections
97
69
  comparable_options >= expected
98
70
  end
99
71
 
100
- # @return [String] the name of an entity in the relation.
101
- def member_name
102
- tools.core_tools.deprecate '#member_name method',
103
- message: 'Use #singular_name instead'
104
-
105
- singular_name
106
- end
107
-
108
72
  # A new Query instance, used for querying against the collection data.
109
73
  #
110
74
  # @return [Object] the query.
111
75
  def query
112
76
  raise AbstractCollectionError,
113
- "#{self.class.name} is an abstract class. Define a repository " \
77
+ "#{self.class.name} is an abstract class. Define a collection " \
114
78
  'subclass and implement the #query method.'
115
79
  end
116
80
 
117
81
  protected
118
82
 
119
83
  def comparable_options
120
- command_options.merge(
121
- name: name,
122
- qualified_name: qualified_name,
123
- singular_name: singular_name
124
- )
125
- end
126
-
127
- private
128
-
129
- def command_options
130
- @command_options ||= {
131
- collection_name: name,
132
- entity_class: entity_class,
133
- member_name: singular_name,
134
- primary_key_name: primary_key_name,
135
- primary_key_type: primary_key_type,
84
+ @comparable_options ||= {
85
+ entity_class:,
86
+ name:,
87
+ primary_key_name:,
88
+ primary_key_type:,
89
+ qualified_name:,
90
+ scope:,
91
+ singular_name:,
136
92
  **options
137
93
  }
138
94
  end
139
-
140
- def ignore_parameters(**parameters)
141
- parameters
142
- .reject { |key, _| ignored_parameters.include?(key) }
143
- .to_h
144
- end
145
-
146
- def ignored_parameters
147
- @ignored_parameters ||= Set.new(IGNORED_PARAMETERS)
148
- end
149
-
150
- def tools
151
- SleepingKingStudios::Tools::Toolbelt.instance
152
- end
153
95
  end
154
96
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/parameter_validation'
4
+
5
+ require 'cuprum/collections'
6
+ require 'cuprum/collections/errors/invalid_parameters'
7
+
8
+ module Cuprum::Collections
9
+ # Abstract base class for commands implementing collection actions.
10
+ class CollectionCommand < Cuprum::Command
11
+ include Cuprum::ParameterValidation
12
+
13
+ # @param collection [Cuprum::Collections::Collection] the collection to
14
+ # which the command belongs.
15
+ def initialize(collection:)
16
+ super()
17
+
18
+ @collection = collection
19
+ end
20
+
21
+ # @return [Cuprum::Collections::Collection] the collection to which the
22
+ # command belongs.
23
+ attr_reader :collection
24
+
25
+ # @return [String] the name of the relation.
26
+ def name
27
+ @name ||= collection.name
28
+ end
29
+ alias collection_name name
30
+
31
+ # @return [String] the name of the primary key attribute. Defaults to 'id'.
32
+ def primary_key_name
33
+ @primary_key_name ||= collection.primary_key_name
34
+ end
35
+
36
+ # @return [Class, Stannum::Constraint] the type of the primary key
37
+ # attribute. Defaults to Integer.
38
+ def primary_key_type
39
+ @primary_key_type ||= collection.primary_key_type
40
+ end
41
+
42
+ # A new Query instance, used for querying against the collection data.
43
+ #
44
+ # @return [Object] the query.
45
+ def query
46
+ collection.query
47
+ end
48
+
49
+ # @returnb [String] the name of an entity in the collection.
50
+ def singular_name
51
+ @singular_name ||= collection.singular_name
52
+ end
53
+ alias member_name singular_name
54
+
55
+ private
56
+
57
+ def error_message_for(message = nil, as:, expected:)
58
+ tools.assertions.error_message_for(
59
+ message || 'sleeping_king_studios.tools.assertions.instance_of',
60
+ as:,
61
+ expected:
62
+ )
63
+ end
64
+
65
+ def tools
66
+ SleepingKingStudios::Tools::Toolbelt.instance
67
+ end
68
+
69
+ def validate_attributes(value, as: 'attributes')
70
+ return error_message_for(as:, expected: Hash) unless value.is_a?(Hash)
71
+
72
+ return [] if value.empty?
73
+
74
+ validator = tools.assertions.aggregator_class.new
75
+
76
+ value.each_key do |key|
77
+ validator.validate_name(
78
+ key,
79
+ as: "#{as}[#{key.inspect}] key"
80
+ )
81
+ end
82
+
83
+ validator.each.to_a
84
+ end
85
+
86
+ def validate_primary_key(value, as: nil)
87
+ return nil if primary_key_type.nil?
88
+
89
+ if primary_key_type.is_a?(Stannum::Constraints::Base)
90
+ match, errors = primary_key_type.match(value)
91
+
92
+ return match ? nil : "#{as || primary_key_name} #{errors.summary}"
93
+ end
94
+
95
+ return nil if value.is_a?(primary_key_type)
96
+
97
+ error_message_for(as: as || primary_key_name, expected: primary_key_type)
98
+ end
99
+
100
+ def validate_primary_keys(primary_keys, as: 'value')
101
+ unless primary_keys.is_a?(Array)
102
+ return error_message_for(as:, expected: Array)
103
+ end
104
+
105
+ messages = []
106
+
107
+ primary_keys.each.with_index do |item, index|
108
+ message = validate_primary_key(item, as: "#{as}[#{index}]")
109
+
110
+ messages << message if message
111
+ end
112
+
113
+ messages
114
+ end
115
+ end
116
+ end
@@ -5,16 +5,13 @@ require 'cuprum/collections/errors/not_found'
5
5
 
6
6
  module Cuprum::Collections::Commands
7
7
  # Abstract implementation of the FindMany command.
8
- #
9
- # Subclasses must define the #build_query method, which returns an empty
10
- # Query instance for that collection.
11
8
  module AbstractFindMany
12
9
  private
13
10
 
14
- def apply_query(primary_keys:, scope:)
11
+ def apply_query(primary_keys:)
15
12
  key = primary_key_name
16
13
 
17
- (scope || build_query).where { { key => one_of(primary_keys) } }
14
+ query.where { |scope| { key => scope.one_of(primary_keys) } }
18
15
  end
19
16
 
20
17
  def build_results(items:, primary_keys:)
@@ -26,16 +23,14 @@ module Cuprum::Collections::Commands
26
23
  end
27
24
 
28
25
  def build_result_list(results, allow_partial:, envelope:)
29
- unless envelope
30
- return Cuprum::ResultList.new(*results, allow_partial: allow_partial)
31
- end
26
+ return Cuprum::ResultList.new(*results, allow_partial:) unless envelope
32
27
 
33
28
  value = envelope ? wrap_items(results.map(&:value)) : nil
34
29
 
35
30
  Cuprum::ResultList.new(
36
31
  *results,
37
- allow_partial: allow_partial,
38
- value: value
32
+ allow_partial:,
33
+ value:
39
34
  )
40
35
  end
41
36
 
@@ -49,25 +44,20 @@ module Cuprum::Collections::Commands
49
44
  Cuprum::Collections::Errors::NotFound.new(
50
45
  attribute_name: primary_key_name,
51
46
  attribute_value: primary_key_value,
52
- collection_name: collection_name,
47
+ collection_name:,
53
48
  primary_key: true
54
49
  )
55
50
  end
56
51
 
57
- def process(
58
- primary_keys:,
59
- allow_partial: false,
60
- envelope: false,
61
- scope: nil
62
- )
63
- query = apply_query(primary_keys: primary_keys, scope: scope)
52
+ def process(primary_keys:, allow_partial: false, envelope: false)
53
+ query = apply_query(primary_keys:)
64
54
  items = items_with_primary_keys(items: query.to_a)
65
- results = build_results(items: items, primary_keys: primary_keys)
55
+ results = build_results(items:, primary_keys:)
66
56
 
67
57
  build_result_list(
68
58
  results,
69
- allow_partial: allow_partial,
70
- envelope: envelope
59
+ allow_partial:,
60
+ envelope:
71
61
  )
72
62
  end
73
63
 
@@ -1,62 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cuprum/collections/commands'
4
- require 'cuprum/collections/queries/parse'
4
+ require 'cuprum/collections/constraints/ordering'
5
+ require 'cuprum/collections/errors/invalid_query'
5
6
 
6
7
  module Cuprum::Collections::Commands
7
8
  # Abstract implementation of the FindMatching command.
8
- #
9
- # Subclasses must define the #build_query method, which returns an empty
10
- # Query instance for that collection.
11
9
  module AbstractFindMatching
12
10
  private
13
11
 
14
- def apply_query(criteria:, limit:, offset:, order:, scope:)
15
- query = scope || build_query
12
+ def apply_query(limit:, offset:, order:, scope:)
13
+ query = self.query
16
14
  query = query.limit(limit) if limit
17
15
  query = query.offset(offset) if offset
18
16
  query = query.order(order) if order
19
- query = query.where(criteria, strategy: :unsafe) unless criteria.empty?
17
+ query = query.where(scope) if scope
20
18
 
21
19
  success(query)
22
20
  end
23
21
 
24
- def parse_criteria(strategy:, where:, &block)
25
- return [] if strategy.nil? && where.nil? && !block_given?
22
+ def build_scope(value, &)
23
+ return Cuprum::Collections::Scope.build(&) if block_given?
26
24
 
27
- Cuprum::Collections::Queries::Parse.new.call(
28
- strategy: strategy,
29
- where: where || block
25
+ return value if value.is_a?(Cuprum::Collections::Scopes::Base)
26
+
27
+ Cuprum::Collections::Scope.build(value) if value
28
+ rescue ArgumentError => exception
29
+ error = Cuprum::Collections::Errors::InvalidQuery.new(
30
+ message: exception.message,
31
+ query: value
30
32
  )
33
+
34
+ failure(error)
31
35
  end
32
36
 
33
- def process( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
37
+ def process(
34
38
  envelope: false,
35
39
  limit: nil,
36
40
  offset: nil,
37
41
  order: nil,
38
- scope: nil,
39
- strategy: nil,
40
42
  where: nil,
41
43
  &block
42
44
  )
43
- criteria = step do
44
- parse_criteria(strategy: strategy, where: where, &block)
45
- end
46
-
45
+ scope = step { build_scope(where, &block) }
47
46
  query = step do
48
47
  apply_query(
49
- criteria: criteria,
50
- limit: limit,
51
- offset: offset,
52
- order: order,
53
- scope: scope
48
+ limit:,
49
+ offset:,
50
+ order:,
51
+ scope:
54
52
  )
55
53
  end
56
54
 
57
55
  envelope ? wrap_query(query) : query.each
58
56
  end
59
57
 
58
+ def validate_order(value, as: 'order')
59
+ return if value.nil?
60
+
61
+ match, errors =
62
+ Cuprum::Collections::Constraints::Ordering.new.match(value)
63
+
64
+ return if match
65
+
66
+ "#{as} #{errors.summary}"
67
+ end
68
+
69
+ def validate_where(value, as: 'where')
70
+ return if value.nil?
71
+
72
+ return if value.is_a?(Cuprum::Collections::Scopes::Base)
73
+
74
+ return if validate_attributes(value, as:).empty?
75
+
76
+ "#{as} is not a scope or query hash"
77
+ end
78
+
60
79
  def wrap_query(query)
61
80
  { collection_name => query.to_a }
62
81
  end
@@ -5,16 +5,13 @@ require 'cuprum/collections/errors/not_found'
5
5
 
6
6
  module Cuprum::Collections::Commands
7
7
  # Abstract implementation of the FindOne command.
8
- #
9
- # Subclasses must define the #build_query method, which returns an empty
10
- # Query instance for that collection.
11
8
  module AbstractFindOne
12
9
  private
13
10
 
14
- def apply_query(primary_key:, scope:)
11
+ def apply_query(primary_key:)
15
12
  key = primary_key_name
16
13
 
17
- (scope || build_query).where { { key => equals(primary_key) } }.limit(1)
14
+ query.where { |scope| { key => scope.equals(primary_key) } }.limit(1)
18
15
  end
19
16
 
20
17
  def handle_missing_item(item:, primary_key:)
@@ -23,17 +20,17 @@ module Cuprum::Collections::Commands
23
20
  error = Cuprum::Collections::Errors::NotFound.new(
24
21
  attribute_name: primary_key_name,
25
22
  attribute_value: primary_key,
26
- collection_name: collection_name,
23
+ collection_name:,
27
24
  primary_key: true
28
25
  )
29
- Cuprum::Result.new(error: error)
26
+ Cuprum::Result.new(error:)
30
27
  end
31
28
 
32
- def process(envelope:, primary_key:, scope:)
33
- query = apply_query(primary_key: primary_key, scope: scope)
29
+ def process(primary_key:, envelope: false)
30
+ query = apply_query(primary_key:)
34
31
  item = query.to_a.first
35
32
 
36
- step { handle_missing_item(item: item, primary_key: primary_key) }
33
+ step { handle_missing_item(item:, primary_key:) }
37
34
 
38
35
  envelope ? wrap_item(item) : item
39
36
  end
@@ -75,7 +75,6 @@ module Cuprum::Collections::Commands::Associations
75
75
 
76
76
  def collection
77
77
  repository.find_or_create(
78
- name: tools.string_tools.pluralize(association.name),
79
78
  qualified_name: association.qualified_name
80
79
  )
81
80
  end
@@ -132,9 +131,9 @@ module Cuprum::Collections::Commands::Associations
132
131
 
133
132
  values = step do
134
133
  perform_query(
135
- association: association,
136
- expected_keys: expected_keys,
137
- plural: plural
134
+ association:,
135
+ expected_keys:,
136
+ plural:
138
137
  )
139
138
  end
140
139
 
@@ -153,9 +152,5 @@ module Cuprum::Collections::Commands::Associations
153
152
 
154
153
  [keys, plural]
155
154
  end
156
-
157
- def tools
158
- SleepingKingStudios::Tools::Toolbelt.instance
159
- end
160
155
  end
161
156
  end
@@ -10,7 +10,7 @@ module Cuprum::Collections::Commands::Associations
10
10
  private
11
11
 
12
12
  def find_missing_keys(entities:, expected_keys:)
13
- expected_keys - map_entity_keys(entities: entities)
13
+ expected_keys - map_entity_keys(entities:)
14
14
  end
15
15
 
16
16
  def map_entity_keys(entities:)
@@ -23,7 +23,7 @@ module Cuprum::Collections::Commands::Associations
23
23
 
24
24
  Cuprum::Collections::Errors::NotFound.new(
25
25
  attribute_name: association.query_key_name,
26
- attribute_value: attribute_value,
26
+ attribute_value:,
27
27
  collection_name: association.name,
28
28
  primary_key: association.primary_key_query?
29
29
  )
@@ -32,15 +32,15 @@ module Cuprum::Collections::Commands::Associations
32
32
  def perform_query(association:, expected_keys:, plural:, **)
33
33
  entities = step { super }
34
34
  missing_keys = find_missing_keys(
35
- entities: entities,
36
- expected_keys: expected_keys
35
+ entities:,
36
+ expected_keys:
37
37
  )
38
38
 
39
39
  return success(entities) if missing_keys.empty?
40
40
 
41
41
  missing_keys = missing_keys.first unless plural
42
42
  error =
43
- missing_keys_error(missing_keys: missing_keys, plural: plural)
43
+ missing_keys_error(missing_keys:, plural:)
44
44
 
45
45
  failure(error)
46
46
  end
@@ -50,11 +50,11 @@ module Cuprum::Collections::Commands
50
50
  private
51
51
 
52
52
  def process(attributes:)
53
- entity = step { collection.build_one.call(attributes: attributes) }
53
+ entity = step { collection.build_one.call(attributes:) }
54
54
 
55
- step { collection.validate_one.call(contract: contract, entity: entity) }
55
+ step { collection.validate_one.call(contract:, entity:) }
56
56
 
57
- collection.insert_one.call(entity: entity)
57
+ collection.insert_one.call(entity:)
58
58
  end
59
59
  end
60
60
  end
@@ -96,20 +96,20 @@ module Cuprum::Collections::Commands
96
96
  if block_given?
97
97
  { query: collection.query.where(&block) }
98
98
  else
99
- { attributes: attributes }
99
+ { attributes: }
100
100
  end
101
101
  )
102
102
  end
103
103
 
104
104
  def not_found_error(attributes: nil, &block)
105
105
  Cuprum::Collections::Errors::NotFound.new(
106
- **error_params_for(attributes: attributes, &block)
106
+ **error_params_for(attributes:, &block)
107
107
  )
108
108
  end
109
109
 
110
110
  def not_unique_error(attributes: nil, &block)
111
111
  Cuprum::Collections::Errors::NotUnique.new(
112
- **error_params_for(attributes: attributes, &block)
112
+ **error_params_for(attributes:, &block)
113
113
  )
114
114
  end
115
115
 
@@ -117,17 +117,17 @@ module Cuprum::Collections::Commands
117
117
  query = block || -> { attributes }
118
118
  entities = step { collection.find_matching.call(limit: 2, &query) }
119
119
 
120
- require_one_entity(attributes: attributes, entities: entities, &block)
120
+ require_one_entity(attributes:, entities:, &block)
121
121
  end
122
122
 
123
123
  def require_one_entity(attributes:, entities:, &block)
124
124
  case entities.count
125
125
  when 0
126
- failure(not_found_error(attributes: attributes, &block))
126
+ failure(not_found_error(attributes:, &block))
127
127
  when 1
128
128
  entities.first
129
129
  when 2
130
- failure(not_unique_error(attributes: attributes, &block))
130
+ failure(not_unique_error(attributes:, &block))
131
131
  end
132
132
  end
133
133
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/commands'
4
+
5
+ module Cuprum::Collections::Commands
6
+ # Shared functionality for defining commands that query the collection.
7
+ module QueryCommand
8
+ # @param query [#call] the query object used to access the collection data.
9
+ # @param options [Hash] additional options for the collection.
10
+ def initialize(query:, **options)
11
+ super(**options)
12
+
13
+ @query = query
14
+ end
15
+
16
+ # @return [#call] the query object used to access the collection data.
17
+ attr_reader :query
18
+ end
19
+ end
@@ -63,12 +63,12 @@ module Cuprum::Collections::Commands
63
63
 
64
64
  def process(attributes:, entity:)
65
65
  entity = step do
66
- collection.assign_one.call(attributes: attributes, entity: entity)
66
+ collection.assign_one.call(attributes:, entity:)
67
67
  end
68
68
 
69
- step { collection.validate_one.call(entity: entity, contract: contract) }
69
+ step { collection.validate_one.call(entity:, contract:) }
70
70
 
71
- step { collection.update_one.call(entity: entity) }
71
+ step { collection.update_one.call(entity:) }
72
72
  end
73
73
  end
74
74
  end