cuprum-collections 0.3.0 → 0.4.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 +48 -0
- data/DEVELOPMENT.md +2 -2
- data/README.md +13 -11
- data/lib/cuprum/collections/association.rb +256 -0
- data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
- data/lib/cuprum/collections/associations/has_many.rb +23 -0
- data/lib/cuprum/collections/associations/has_one.rb +23 -0
- data/lib/cuprum/collections/associations.rb +10 -0
- data/lib/cuprum/collections/basic/collection.rb +39 -74
- data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
- data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
- data/lib/cuprum/collections/basic/repository.rb +9 -33
- data/lib/cuprum/collections/basic.rb +1 -0
- data/lib/cuprum/collections/collection.rb +154 -0
- data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
- data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
- data/lib/cuprum/collections/commands/associations.rb +13 -0
- data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
- data/lib/cuprum/collections/commands.rb +1 -0
- data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
- data/lib/cuprum/collections/relation.rb +401 -0
- data/lib/cuprum/collections/repository.rb +71 -4
- data/lib/cuprum/collections/resource.rb +65 -0
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
- data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
- data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
- data/lib/cuprum/collections/rspec/contracts.rb +23 -0
- data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
- data/lib/cuprum/collections/rspec.rb +4 -1
- data/lib/cuprum/collections/version.rb +1 -1
- data/lib/cuprum/collections.rb +9 -4
- metadata +23 -19
- data/lib/cuprum/collections/base.rb +0 -11
- data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
- data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
- data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
- data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
- data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
- data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
- data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
- data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
- data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
- data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +0 -96
@@ -15,49 +15,25 @@ module Cuprum::Collections::Basic
|
|
15
15
|
@data = data
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# @param data [Hash<String, Object>] The inital data for the collection. If
|
22
|
-
# not specified, defaults to the data used to initialize the repository.
|
23
|
-
# @param options [Hash] Additional options to pass to Collection.new
|
24
|
-
#
|
25
|
-
# @return [Cuprum::Collections::Basic::Collection] the created collection.
|
26
|
-
#
|
27
|
-
# @see Cuprum::Collections::Basic::Collection#initialize.
|
28
|
-
def build(collection_name:, data: nil, **options)
|
29
|
-
validate_collection_name!(collection_name)
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_collection(data: nil, **parameters)
|
30
21
|
validate_data!(data)
|
31
22
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
)
|
23
|
+
qualified_name =
|
24
|
+
Cuprum::Collections::Relation::Disambiguation
|
25
|
+
.resolve_parameters(parameters, name: :collection_name)
|
26
|
+
.fetch(:qualified_name)
|
37
27
|
|
38
|
-
|
28
|
+
data ||= @data.fetch(qualified_name, [])
|
39
29
|
|
40
|
-
|
30
|
+
Cuprum::Collections::Basic.new(data: data, **parameters)
|
41
31
|
end
|
42
32
|
|
43
|
-
private
|
44
|
-
|
45
33
|
def valid_collection?(collection)
|
46
34
|
collection.is_a?(Cuprum::Collections::Basic::Collection)
|
47
35
|
end
|
48
36
|
|
49
|
-
def validate_collection_name!(name)
|
50
|
-
raise ArgumentError, "collection name can't be blank" if name.nil?
|
51
|
-
|
52
|
-
unless name.is_a?(String) || name.is_a?(Symbol)
|
53
|
-
raise ArgumentError, 'collection name must be a String or Symbol'
|
54
|
-
end
|
55
|
-
|
56
|
-
return unless name.empty?
|
57
|
-
|
58
|
-
raise ArgumentError, "collection name can't be blank"
|
59
|
-
end
|
60
|
-
|
61
37
|
def validate_data!(data)
|
62
38
|
return if data.nil? || data.is_a?(Array)
|
63
39
|
|
@@ -18,5 +18,6 @@ module Cuprum::Collections
|
|
18
18
|
autoload :Command, 'cuprum/collections/basic/command'
|
19
19
|
autoload :Commands, 'cuprum/collections/basic/commands'
|
20
20
|
autoload :Query, 'cuprum/collections/basic/query'
|
21
|
+
autoload :Repository, 'cuprum/collections/basic/repository'
|
21
22
|
end
|
22
23
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/command_factory'
|
4
|
+
|
5
|
+
require 'cuprum/collections'
|
6
|
+
require 'cuprum/collections/relation'
|
7
|
+
|
8
|
+
module Cuprum::Collections
|
9
|
+
# Provides a base implementation for collections.
|
10
|
+
class Collection < Cuprum::CommandFactory
|
11
|
+
include Cuprum::Collections::Relation::Parameters
|
12
|
+
include Cuprum::Collections::Relation::PrimaryKeys
|
13
|
+
include Cuprum::Collections::Relation::Disambiguation
|
14
|
+
|
15
|
+
# Error raised when trying to call an abstract collection method.
|
16
|
+
class AbstractCollectionError < StandardError; end
|
17
|
+
|
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)
|
29
|
+
# @param entity_class [Class, String] the class of entity represented by
|
30
|
+
# the relation.
|
31
|
+
# @param name [String] the name of the relation.
|
32
|
+
# @param qualified_name [String] a scoped name for the relation.
|
33
|
+
# @param singular_name [String] the name of an entity in the relation.
|
34
|
+
# @param options [Hash] additional options for the relation.
|
35
|
+
#
|
36
|
+
# @option options primary_key_name [String] the name of the primary key
|
37
|
+
# 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
|
59
|
+
|
60
|
+
# @param other [Object] The object to compare.
|
61
|
+
#
|
62
|
+
# @return [true, false] true if the other object is a collection with the
|
63
|
+
# same options, otherwise false.
|
64
|
+
def ==(other)
|
65
|
+
return false unless self.class == other.class
|
66
|
+
|
67
|
+
comparable_options == other.comparable_options
|
68
|
+
end
|
69
|
+
|
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
|
+
# @return [Integer] the count of items in the collection.
|
79
|
+
def count
|
80
|
+
query.count
|
81
|
+
end
|
82
|
+
alias size count
|
83
|
+
|
84
|
+
# Checks if the collection matches the expected options.
|
85
|
+
#
|
86
|
+
# @param expected [Hash] the options to compare.
|
87
|
+
#
|
88
|
+
# @return [Boolean] true if all of the expected options match, otherwise
|
89
|
+
# false.
|
90
|
+
def matches?(**expected)
|
91
|
+
if expected[:entity_class].is_a?(String)
|
92
|
+
expected = expected.merge(
|
93
|
+
entity_class: Object.const_get(expected[:entity_class])
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
comparable_options >= expected
|
98
|
+
end
|
99
|
+
|
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
|
+
# A new Query instance, used for querying against the collection data.
|
109
|
+
#
|
110
|
+
# @return [Object] the query.
|
111
|
+
def query
|
112
|
+
raise AbstractCollectionError,
|
113
|
+
"#{self.class.name} is an abstract class. Define a repository " \
|
114
|
+
'subclass and implement the #query method.'
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
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,
|
136
|
+
**options
|
137
|
+
}
|
138
|
+
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
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/commands/associations'
|
4
|
+
|
5
|
+
module Cuprum::Collections::Commands::Associations
|
6
|
+
# Command for querying entities by association.
|
7
|
+
class FindMany < Cuprum::Command
|
8
|
+
PERMITTED_KEYWORDS = Set.new(%i[entities entity key keys]).freeze
|
9
|
+
private_constant :PERMITTED_KEYWORDS
|
10
|
+
|
11
|
+
# @!method call(**params)
|
12
|
+
# @overload call(key:)
|
13
|
+
# Finds the association values for the given key.
|
14
|
+
#
|
15
|
+
# @param key [Object] the primary or foreign key for querying the
|
16
|
+
# association.
|
17
|
+
#
|
18
|
+
# @return [Object, nil] the association value or nil, if the association
|
19
|
+
# is singular.
|
20
|
+
# @return [Array<Object>] the association values, if the association is
|
21
|
+
# plural.
|
22
|
+
#
|
23
|
+
# @overload call(keys:)
|
24
|
+
# Finds the association values for the given Array of keys.
|
25
|
+
# @return [Array<Object>] the association values.
|
26
|
+
#
|
27
|
+
# @param keys [Array<Object>] the primary or foreign keys for querying
|
28
|
+
# the association.
|
29
|
+
#
|
30
|
+
# @return [Array<Object>] the association values.
|
31
|
+
#
|
32
|
+
# @overload call(entity:)
|
33
|
+
# Finds the association values for the given entity.
|
34
|
+
#
|
35
|
+
# @param entity [Object] the base entity for querying the association.
|
36
|
+
#
|
37
|
+
# @return [Object, nil] the association value or nil, if the association
|
38
|
+
# is singular.
|
39
|
+
# @return [Array<Object>] the association values, if the association is
|
40
|
+
# plural.
|
41
|
+
#
|
42
|
+
# @overload call(entities:)
|
43
|
+
# Finds the association values for the given Array of entities.
|
44
|
+
#
|
45
|
+
# @param entity [Array<Object>] the base entities for querying the
|
46
|
+
# association.
|
47
|
+
#
|
48
|
+
# @return [Array<Object>] the association values.
|
49
|
+
|
50
|
+
# @param association [Cuprum::Collections::Association] the association to
|
51
|
+
# query.
|
52
|
+
# @param repository [Cuprum::Collections::Repository] the repository to
|
53
|
+
# query from.
|
54
|
+
# @param resource [Cuprum::Collections::Resource] the base resource for the
|
55
|
+
# association.
|
56
|
+
def initialize(association:, repository:, resource:)
|
57
|
+
super()
|
58
|
+
|
59
|
+
@association = association
|
60
|
+
@repository = repository
|
61
|
+
@resource = resource
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Cuprum::Collections::Association] the association to query.
|
65
|
+
attr_reader :association
|
66
|
+
|
67
|
+
# @return [Cuprum::Collections::Repository] the repository to query from.
|
68
|
+
attr_reader :repository
|
69
|
+
|
70
|
+
# @return [Cuprum::Collections::Resource] the base resource for the
|
71
|
+
# association.
|
72
|
+
attr_reader :resource
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def collection
|
77
|
+
repository.find_or_create(
|
78
|
+
name: tools.string_tools.pluralize(association.name),
|
79
|
+
qualified_name: association.qualified_name
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_keys(association, hsh)
|
84
|
+
return hsh[:key] if hsh.key?(:key)
|
85
|
+
return hsh[:keys] if hsh.key?(:keys)
|
86
|
+
|
87
|
+
values = hsh.fetch(:entity) { hsh[:entities] }
|
88
|
+
|
89
|
+
if values.is_a?(Array)
|
90
|
+
association.map_entities_to_keys(*values)
|
91
|
+
else
|
92
|
+
association.map_entities_to_keys(values).first
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_ambiguous_keys(hsh)
|
97
|
+
return if hsh.keys.size == 1
|
98
|
+
|
99
|
+
raise ArgumentError,
|
100
|
+
"ambiguous keywords #{hsh.each_key.map(&:inspect).join(', ')} " \
|
101
|
+
'- must provide exactly one parameter'
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_extra_keys(hsh)
|
105
|
+
return if hsh.keys.all? { |key| PERMITTED_KEYWORDS.include?(key) }
|
106
|
+
|
107
|
+
extra_keys = hsh.keys - PERMITTED_KEYWORDS.to_a
|
108
|
+
|
109
|
+
raise ArgumentError,
|
110
|
+
"invalid keywords #{extra_keys.map(&:inspect).join(', ')}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_missing_keys(hsh)
|
114
|
+
return unless hsh.empty?
|
115
|
+
|
116
|
+
raise ArgumentError, 'missing keyword :entity, :entities, :key, or :keys'
|
117
|
+
end
|
118
|
+
|
119
|
+
def perform_query(association:, expected_keys:, **)
|
120
|
+
query = association.build_keys_query(*expected_keys)
|
121
|
+
find_command = collection.find_matching
|
122
|
+
|
123
|
+
find_command.call(&query)
|
124
|
+
end
|
125
|
+
|
126
|
+
def process(**params) # rubocop:disable Metrics/MethodLength
|
127
|
+
association = @association.with_inverse(resource)
|
128
|
+
expected_keys, plural = resolve_keys(association, **params)
|
129
|
+
plural ||= association.plural?
|
130
|
+
|
131
|
+
return plural ? [] : nil if expected_keys.empty?
|
132
|
+
|
133
|
+
values = step do
|
134
|
+
perform_query(
|
135
|
+
association: association,
|
136
|
+
expected_keys: expected_keys,
|
137
|
+
plural: plural
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
plural ? values.to_a : values.first
|
142
|
+
end
|
143
|
+
|
144
|
+
def resolve_keys(association, **params)
|
145
|
+
handle_missing_keys(params)
|
146
|
+
handle_extra_keys(params)
|
147
|
+
handle_ambiguous_keys(params)
|
148
|
+
|
149
|
+
keys = extract_keys(association, params)
|
150
|
+
plural = keys.is_a?(Array)
|
151
|
+
keys = [keys] unless plural
|
152
|
+
keys = keys.compact.uniq
|
153
|
+
|
154
|
+
[keys, plural]
|
155
|
+
end
|
156
|
+
|
157
|
+
def tools
|
158
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/commands/associations'
|
4
|
+
require 'cuprum/collections/commands/associations/require_many'
|
5
|
+
require 'cuprum/collections/errors/not_found'
|
6
|
+
|
7
|
+
module Cuprum::Collections::Commands::Associations
|
8
|
+
# Command for querying required entities by association.
|
9
|
+
class RequireMany < Cuprum::Collections::Commands::Associations::FindMany
|
10
|
+
private
|
11
|
+
|
12
|
+
def find_missing_keys(entities:, expected_keys:)
|
13
|
+
expected_keys - map_entity_keys(entities: entities)
|
14
|
+
end
|
15
|
+
|
16
|
+
def map_entity_keys(entities:)
|
17
|
+
entities.map { |entity| entity[association.query_key_name] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def missing_keys_error(missing_keys:, plural:)
|
21
|
+
attribute_value =
|
22
|
+
!plural && missing_keys.is_a?(Array) ? missing_keys.first : missing_keys
|
23
|
+
|
24
|
+
Cuprum::Collections::Errors::NotFound.new(
|
25
|
+
attribute_name: association.query_key_name,
|
26
|
+
attribute_value: attribute_value,
|
27
|
+
collection_name: association.name,
|
28
|
+
primary_key: association.primary_key_query?
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform_query(association:, expected_keys:, plural:, **)
|
33
|
+
entities = step { super }
|
34
|
+
missing_keys = find_missing_keys(
|
35
|
+
entities: entities,
|
36
|
+
expected_keys: expected_keys
|
37
|
+
)
|
38
|
+
|
39
|
+
return success(entities) if missing_keys.empty?
|
40
|
+
|
41
|
+
missing_keys = missing_keys.first unless plural
|
42
|
+
error =
|
43
|
+
missing_keys_error(missing_keys: missing_keys, plural: plural)
|
44
|
+
|
45
|
+
failure(error)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/commands'
|
4
|
+
|
5
|
+
module Cuprum::Collections::Commands
|
6
|
+
# Namespace for commands that operate on entity associations.
|
7
|
+
module Associations
|
8
|
+
autoload :FindMany,
|
9
|
+
'cuprum/collections/commands/associations/find_many'
|
10
|
+
autoload :RequireMany,
|
11
|
+
'cuprum/collections/commands/associations/require_many'
|
12
|
+
end
|
13
|
+
end
|
@@ -92,7 +92,7 @@ module Cuprum::Collections::Commands
|
|
92
92
|
private
|
93
93
|
|
94
94
|
def error_params_for(attributes: nil, &block)
|
95
|
-
{ collection_name: collection.
|
95
|
+
{ collection_name: collection.name }.merge(
|
96
96
|
if block_given?
|
97
97
|
{ query: collection.query.where(&block) }
|
98
98
|
else
|
@@ -5,6 +5,7 @@ require 'cuprum/collections'
|
|
5
5
|
module Cuprum::Collections
|
6
6
|
# Namespace for abstract commands and collection-independent commands.
|
7
7
|
module Commands
|
8
|
+
autoload :Associations, 'cuprum/collections/commands/associations'
|
8
9
|
autoload :Create, 'cuprum/collections/commands/create'
|
9
10
|
autoload :FindOneMatching, 'cuprum/collections/commands/find_one_matching'
|
10
11
|
autoload :Update, 'cuprum/collections/commands/update'
|
@@ -14,7 +14,7 @@ module Cuprum::Collections::Errors
|
|
14
14
|
].freeze
|
15
15
|
private_constant :PERMITTED_KEYWORDS
|
16
16
|
|
17
|
-
# @overload initialize(attribute_name:, attribute_value:, collection_name:, primary_key: false)
|
17
|
+
# @overload initialize(attribute_name:, attribute_value:, collection_name:, primary_key: false)
|
18
18
|
# @param attribute_name [String] The name of the queried attribute.
|
19
19
|
# @param attribute_value [Object] The value of the queried attribute.
|
20
20
|
# @param collection_name [String] The name of the collection.
|