cuprum-collections 0.3.0.rc.0 → 0.4.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -4
  3. data/DEVELOPMENT.md +2 -2
  4. data/README.md +13 -11
  5. data/lib/cuprum/collections/association.rb +256 -0
  6. data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
  7. data/lib/cuprum/collections/associations/has_many.rb +23 -0
  8. data/lib/cuprum/collections/associations/has_one.rb +23 -0
  9. data/lib/cuprum/collections/associations.rb +10 -0
  10. data/lib/cuprum/collections/basic/collection.rb +39 -74
  11. data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
  12. data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
  13. data/lib/cuprum/collections/basic/repository.rb +9 -33
  14. data/lib/cuprum/collections/basic.rb +1 -0
  15. data/lib/cuprum/collections/collection.rb +154 -0
  16. data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
  17. data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
  18. data/lib/cuprum/collections/commands/associations.rb +13 -0
  19. data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
  20. data/lib/cuprum/collections/commands.rb +1 -0
  21. data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
  22. data/lib/cuprum/collections/relation.rb +401 -0
  23. data/lib/cuprum/collections/repository.rb +71 -4
  24. data/lib/cuprum/collections/resource.rb +65 -0
  25. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
  26. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
  27. data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
  28. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
  29. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
  30. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
  31. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
  32. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
  33. data/lib/cuprum/collections/rspec/contracts.rb +23 -0
  34. data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
  35. data/lib/cuprum/collections/rspec.rb +4 -1
  36. data/lib/cuprum/collections/version.rb +2 -2
  37. data/lib/cuprum/collections.rb +9 -4
  38. metadata +23 -19
  39. data/lib/cuprum/collections/base.rb +0 -11
  40. data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
  41. data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
  42. data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
  43. data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
  44. data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
  45. data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
  46. data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
  47. data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
  48. data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
  49. data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
  50. data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
  51. data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
  52. data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
  53. data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
  54. data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +0 -96
@@ -0,0 +1,401 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ require 'cuprum/collections'
6
+
7
+ module Cuprum::Collections
8
+ # Abstract class representing a group or view of entities.
9
+ class Relation
10
+ # Methods for resolving a singular or plural relation.
11
+ module Cardinality
12
+ # @return [Boolean] true if the relation is plural; otherwise false.
13
+ def plural?
14
+ @plural
15
+ end
16
+
17
+ # @return [Boolean] true if the relation is singular; otherwise false.
18
+ def singular?
19
+ !@plural
20
+ end
21
+
22
+ private
23
+
24
+ def resolve_plurality(**params) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
25
+ if params.key?(:plural) && !params[:plural].nil?
26
+ if params.key?(:singular) && !params[:singular].nil?
27
+ message =
28
+ 'ambiguous cardinality: initialized with parameters ' \
29
+ "plural: #{params[:plural].inspect} and singular: " \
30
+ "#{params[:singular].inspect}"
31
+
32
+ raise ArgumentError, message
33
+ end
34
+
35
+ validate_cardinality(params[:plural], as: 'plural')
36
+
37
+ return params[:plural]
38
+ end
39
+
40
+ if params.key?(:singular) && !params[:singular].nil?
41
+ validate_cardinality(params[:singular], as: 'singular')
42
+
43
+ return !params[:singular]
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def validate_cardinality(value, as:)
50
+ return if value == true || value == false # rubocop:disable Style/MultipleComparison
51
+
52
+ raise ArgumentError, "#{as} must be true or false"
53
+ end
54
+ end
55
+
56
+ # Methods for disambiguating parameters with multiple keywords.
57
+ module Disambiguation
58
+ class << self
59
+ # Helper method for resolving an ambiguous keyword.
60
+ #
61
+ # @param params [Hash] the original method keywords.
62
+ # @param key [Symbol] the original key to resolve.
63
+ # @param alternatives [Symbol, Array<Symbol>] the additional keywords.
64
+ #
65
+ # @return [Hash] the disambiguated keywords.
66
+ def disambiguate_keyword(params, key, *alternatives) # rubocop:disable Metrics/MethodLength
67
+ params = params.dup
68
+ values = keyword_values(params, key, *alternatives)
69
+
70
+ return params if values.empty?
71
+
72
+ if values.size == 1
73
+ match, value = values.first
74
+
75
+ unless match == key
76
+ tools.core_tools.deprecate(
77
+ "#{match.inspect} keyword",
78
+ message: "Use #{key.inspect} instead"
79
+ )
80
+ end
81
+
82
+ return params.merge(key => value)
83
+ end
84
+
85
+ raise ArgumentError, ambiguous_keywords_error(values)
86
+ end
87
+
88
+ # Helper method for resolving a Relation's required parameters.
89
+ #
90
+ # The returned Hash will define the :entity_class, :singular_name,
91
+ # :name, and :qualified_name keys.
92
+ #
93
+ # @param params [Hash] the parameters to resolve.
94
+ # @param ambiguous [Hash{Symbol => Symbol, Array<Symbol>}] ambiguous
95
+ # keywords to resolve. Each key-value pair is passed to
96
+ # .disambiguate_keyword before the parameters are resolved.
97
+ #
98
+ # @return [Hash] the resolved parameters.
99
+ #
100
+ # @see .disambiguate_keyword
101
+ # @see Cuprum::Collections::Relation::Parameters.resolve_parameters
102
+ def resolve_parameters(params, **ambiguous)
103
+ params = ambiguous.reduce(params) do |hsh, (key, alternatives)|
104
+ disambiguate_keyword(hsh, key, *alternatives)
105
+ end
106
+
107
+ Cuprum::Collections::Relation::Parameters.resolve_parameters(params)
108
+ end
109
+
110
+ private
111
+
112
+ def ambiguous_keywords_error(values)
113
+ expected, _ = values.first
114
+ formatted =
115
+ values
116
+ .map { |key, value| "#{key}: #{value.inspect}" }
117
+ .join(', ')
118
+
119
+ "ambiguous parameter #{expected}: initialized with parameters " \
120
+ "#{formatted}"
121
+ end
122
+
123
+ def keyword_values(keywords, *keys)
124
+ keys
125
+ .map { |key| [key, keywords.delete(key)] }
126
+ .reject { |_, value| value.nil? } # rubocop:disable Style/CollectionCompact
127
+ end
128
+
129
+ def tools
130
+ SleepingKingStudios::Tools::Toolbelt.instance
131
+ end
132
+ end
133
+
134
+ # (see Cuprum::Collections::Relation::Disambiguation.disambiguate_keyword)
135
+ def disambiguate_keyword(params, key, *alternatives)
136
+ Disambiguation.disambiguate_keyword(params, key, *alternatives)
137
+ end
138
+
139
+ # (see Cuprum::Collections::Relation::Disambiguation.resolve_parameters)
140
+ def resolve_parameters(params, **ambiguous)
141
+ Disambiguation.resolve_parameters(params, **ambiguous)
142
+ end
143
+ end
144
+
145
+ # Methods for resolving a relations's naming and entity class from options.
146
+ module Parameters # rubocop:disable Metrics/ModuleLength
147
+ PARAMETER_KEYS = %i[entity_class name qualified_name].freeze
148
+ private_constant :PARAMETER_KEYS
149
+
150
+ class << self
151
+ # @overload resolve_parameters(entity_class: nil, singular_name: nil, name: nil, qualified_name: nil)
152
+ # Helper method for resolving a Relation's required parameters.
153
+ #
154
+ # The returned Hash will define the :entity_class, :singular_name,
155
+ # :name, and :qualified_name keys.
156
+ #
157
+ # @param entity_class [Class, String] the class of entity represented
158
+ # by the relation.
159
+ # @param singular_name [String] the name of an entity in the relation.
160
+ # @param name [String] the name of the relation.
161
+ # @param qualified_name [String] a scoped name for the relation.
162
+ #
163
+ # @return [Hash] the resolved parameters.
164
+ def resolve_parameters(params) # rubocop:disable Metrics/MethodLength
165
+ validate_parameters(**params)
166
+
167
+ entity_class = entity_class_from(**params)
168
+ class_name = entity_class_name(entity_class)
169
+ name = relation_name_from(**params, class_name: class_name)
170
+ plural_name = plural_name_from(**params, name: name)
171
+ qualified_name = qualified_name_from(**params, class_name: class_name)
172
+ singular_name = singular_name_from(**params, name: name)
173
+
174
+ {
175
+ entity_class: entity_class,
176
+ name: name,
177
+ plural_name: plural_name,
178
+ qualified_name: qualified_name,
179
+ singular_name: singular_name
180
+ }
181
+ end
182
+
183
+ private
184
+
185
+ def classify(raw)
186
+ raw
187
+ .then { |str| tools.string_tools.singularize(str).to_s }
188
+ .split('/')
189
+ .map { |str| tools.string_tools.camelize(str) }
190
+ .join('::')
191
+ end
192
+
193
+ def entity_class_from(**params)
194
+ if has_key?(params, :entity_class)
195
+ entity_class = params[:entity_class]
196
+
197
+ return entity_class.is_a?(Class) ? entity_class : entity_class.to_s
198
+ end
199
+
200
+ if has_key?(params, :qualified_name)
201
+ return classify(params[:qualified_name])
202
+ end
203
+
204
+ classify(params[:name])
205
+ end
206
+
207
+ def entity_class_name(entity_class, scoped: true)
208
+ (entity_class.is_a?(Class) ? entity_class.name : entity_class)
209
+ .split('::')
210
+ .map { |str| tools.string_tools.underscore(str) }
211
+ .then { |ary| scoped ? ary.join('/') : ary.last }
212
+ end
213
+
214
+ def has_key?(params, key) # rubocop:disable Naming/PredicateName
215
+ return false unless params.key?(key)
216
+
217
+ !params[key].nil?
218
+ end
219
+
220
+ def plural_name_from(name:, **parameters)
221
+ if parameters.key?(:plural_name) && !parameters[:plural_name].nil?
222
+ return validate_parameter(
223
+ parameters[:plural_name],
224
+ as: 'plural name'
225
+ )
226
+ end
227
+
228
+ tools.string_tools.pluralize(name)
229
+ end
230
+
231
+ def qualified_name_from(class_name:, **params)
232
+ if has_key?(params, :qualified_name)
233
+ return params[:qualified_name].to_s
234
+ end
235
+
236
+ tools.string_tools.pluralize(class_name)
237
+ end
238
+
239
+ def relation_name_from(class_name:, **params)
240
+ return params[:name].to_s if has_key?(params, :name)
241
+
242
+ tools.string_tools.pluralize(class_name.split('/').last)
243
+ end
244
+
245
+ def singular_name_from(name:, **parameters)
246
+ if parameters.key?(:singular_name) && !parameters[:singular_name].nil?
247
+ return validate_parameter(
248
+ parameters[:singular_name],
249
+ as: 'singular name'
250
+ )
251
+ end
252
+
253
+ tools.string_tools.singularize(name)
254
+ end
255
+
256
+ def tools
257
+ SleepingKingStudios::Tools::Toolbelt.instance
258
+ end
259
+
260
+ def validate_entity_class(value)
261
+ return if value.is_a?(Class)
262
+
263
+ if value.nil? || value.is_a?(String) || value.is_a?(Symbol)
264
+ tools.assertions.validate_name(value, as: 'entity class')
265
+
266
+ return
267
+ end
268
+
269
+ raise ArgumentError,
270
+ 'entity class is not a Class, a String or a Symbol'
271
+ end
272
+
273
+ def validate_parameter(value, as:)
274
+ tools.assertions.validate_name(value, as: as)
275
+
276
+ value.to_s
277
+ end
278
+
279
+ def validate_parameter_keys(params)
280
+ return if PARAMETER_KEYS.any? { |key| has_key?(params, key) }
281
+
282
+ raise ArgumentError, "name or entity class can't be blank"
283
+ end
284
+
285
+ def validate_parameters(**params) # rubocop:disable Metrics/MethodLength
286
+ validate_parameter_keys(params)
287
+
288
+ if has_key?(params, :entity_class)
289
+ validate_entity_class(params[:entity_class])
290
+ end
291
+
292
+ if has_key?(params, :name)
293
+ validate_parameter(params[:name], as: 'name')
294
+ end
295
+
296
+ if has_key?(params, :plural_name)
297
+ validate_parameter(params[:plural_name], as: 'plural name')
298
+ end
299
+
300
+ if has_key?(params, :qualified_name)
301
+ validate_parameter(params[:qualified_name], as: 'qualified name')
302
+ end
303
+
304
+ if has_key?(params, :singular_name) # rubocop:disable Style/GuardClause
305
+ validate_parameter(params[:singular_name], as: 'singular name')
306
+ end
307
+ end
308
+ end
309
+
310
+ # @return [String] the name of the relation.
311
+ attr_reader :name
312
+
313
+ # @return [String] the pluralized name of the relation.
314
+ attr_reader :plural_name
315
+
316
+ # @return [String] a scoped name for the relation.
317
+ attr_reader :qualified_name
318
+
319
+ # @return [String] the name of an entity in the relation.
320
+ attr_reader :singular_name
321
+
322
+ # @return [Class] the class of entity represented by the relation.
323
+ def entity_class
324
+ return @entity_class if @entity_class.is_a?(Class)
325
+
326
+ @entity_class = Object.const_get(@entity_class)
327
+ end
328
+
329
+ # (see Cuprum::Collections::Relation::Parameters.resolve_parameters)
330
+ def resolve_parameters(parameters)
331
+ Parameters.resolve_parameters(parameters)
332
+ end
333
+ end
334
+
335
+ # Methods for specifying a relation's primary key.
336
+ module PrimaryKeys
337
+ # @return [String] the name of the primary key attribute. Defaults to
338
+ # 'id'.
339
+ def primary_key_name
340
+ @primary_key_name ||= options.fetch(:primary_key_name, 'id').to_s
341
+ end
342
+
343
+ # @return [Class, Stannum::Constraint] the type of the primary key
344
+ # attribute. Defaults to Integer.
345
+ def primary_key_type
346
+ @primary_key_type ||=
347
+ options
348
+ .fetch(:primary_key_type, Integer)
349
+ .then { |obj| obj.is_a?(String) ? Object.const_get(obj) : obj }
350
+ end
351
+ end
352
+
353
+ IGNORED_PARAMETERS = %i[
354
+ entity_class
355
+ name
356
+ qualified_name
357
+ singular_name
358
+ ].freeze
359
+ private_constant :IGNORED_PARAMETERS
360
+
361
+ include Cuprum::Collections::Relation::Parameters
362
+
363
+ # @overload initialize(entity_class: nil, name: nil, qualified_name: nil, singular_name: nil, **options)
364
+ # @param entity_class [Class, String] the class of entity represented by
365
+ # the relation.
366
+ # @param name [String] the name of the relation.
367
+ # @param qualified_name [String] a scoped name for the relation.
368
+ # @param singular_name [String] the name of an entity in the relation.
369
+ # @param options [Hash] additional options for the relation.
370
+ def initialize(**parameters)
371
+ relation_params = resolve_parameters(parameters)
372
+
373
+ @entity_class = relation_params[:entity_class]
374
+ @name = relation_params[:name]
375
+ @plural_name = relation_params[:plural_name]
376
+ @qualified_name = relation_params[:qualified_name]
377
+ @singular_name = relation_params[:singular_name]
378
+
379
+ @options = ignore_parameters(**parameters)
380
+ end
381
+
382
+ # @return [Hash] additional options for the relation.
383
+ attr_reader :options
384
+
385
+ private
386
+
387
+ def ignore_parameters(**parameters)
388
+ parameters
389
+ .reject { |key, _| ignored_parameters.include?(key) }
390
+ .to_h
391
+ end
392
+
393
+ def ignored_parameters
394
+ @ignored_parameters ||= Set.new(IGNORED_PARAMETERS)
395
+ end
396
+
397
+ def tools
398
+ SleepingKingStudios::Tools::Toolbelt.instance
399
+ end
400
+ end
401
+ end
@@ -3,6 +3,7 @@
3
3
  require 'forwardable'
4
4
 
5
5
  require 'cuprum/collections'
6
+ require 'cuprum/collections/relation'
6
7
 
7
8
  module Cuprum::Collections
8
9
  # A repository represents a group of collections.
@@ -16,6 +17,9 @@ module Cuprum::Collections
16
17
  class Repository
17
18
  extend Forwardable
18
19
 
20
+ # Error raised when trying to call an abstract repository method.
21
+ class AbstractRepositoryError < StandardError; end
22
+
19
23
  # Error raised when trying to add an existing collection to the repository.
20
24
  class DuplicateCollectionError < StandardError; end
21
25
 
@@ -57,12 +61,12 @@ module Cuprum::Collections
57
61
  # The collection must implement the #collection_name property. Repository
58
62
  # subclasses may enforce additional requirements.
59
63
  #
60
- # @param collection [#collection_name] The collection to add to the
61
- # repository.
62
- # @param force [true, false] If true, override an existing collection with
64
+ # @param collection [Cuprum::Collections::Collection] the collection to add
65
+ # to the repository.
66
+ # @param force [true, false] if true, override an existing collection with
63
67
  # the same name.
64
68
  #
65
- # @return [Cuprum::Rails::Repository] the repository.
69
+ # @return [Cuprum::Collections::Repository] the repository.
66
70
  #
67
71
  # @raise [DuplicateCollectionError] if a collection with the same name
68
72
  # already exists in the repository.
@@ -80,6 +84,57 @@ module Cuprum::Collections
80
84
  end
81
85
  alias << add
82
86
 
87
+ # @overload create(collection_name: nil, entity_class: nil, force: false, **options)
88
+ # Adds a new collection with the given name to the repository.
89
+ #
90
+ # @param collection_name [String] the name of the new collection.
91
+ # @param entity_class [Class, String] the class of entity represented in
92
+ # the collection.
93
+ # @param force [true, false] if true, override an existing collection with
94
+ # the same name.
95
+ # @param options [Hash] additional options to pass to Collection.new.
96
+ #
97
+ # @return [Cuprum::Collections::Collection] the created collection.
98
+ #
99
+ # @raise [DuplicateCollectionError] if a collection with the same name
100
+ # already exists in the repository.
101
+ def create(force: false, **options)
102
+ collection = build_collection(**options)
103
+
104
+ add(collection, force: force)
105
+
106
+ collection
107
+ end
108
+
109
+ # @overload find_or_create(collection_name: nil, entity_class: nil, **options)
110
+ # Finds or creates a new collection with the given name.
111
+ #
112
+ # @param collection_name [String] the name of the new collection.
113
+ # @param entity_class [Class, String] the class of entity represented in
114
+ # the collection.
115
+ # @param options [Hash] additional options to pass to Collection.new.
116
+ #
117
+ # @return [Cuprum::Collections::Collection] the created collection.
118
+ #
119
+ # @raise [DuplicateCollectionError] if a collection with the same name
120
+ # but different parameters already exists in the repository.
121
+ def find_or_create(**parameters)
122
+ qualified_name = qualified_name_for(**parameters)
123
+
124
+ unless key?(qualified_name)
125
+ create(**parameters)
126
+
127
+ return @collections[qualified_name]
128
+ end
129
+
130
+ collection = @collections[qualified_name]
131
+
132
+ return collection if collection.matches?(**parameters)
133
+
134
+ raise DuplicateCollectionError,
135
+ "collection #{qualified_name} already exists"
136
+ end
137
+
83
138
  # Checks if a collection with the given name exists in the repository.
84
139
  #
85
140
  # @param qualified_name [String, Symbol] The name to check for.
@@ -91,6 +146,18 @@ module Cuprum::Collections
91
146
 
92
147
  private
93
148
 
149
+ def build_collection(**)
150
+ raise AbstractRepositoryError,
151
+ "#{self.class.name} is an abstract class. Define a repository " \
152
+ 'subclass and implement the #build_collection method.'
153
+ end
154
+
155
+ def qualified_name_for(**parameters)
156
+ Cuprum::Collections::Relation::Disambiguation
157
+ .resolve_parameters(parameters, name: :collection_name)
158
+ .fetch(:qualified_name)
159
+ end
160
+
94
161
  def valid_collection?(collection)
95
162
  collection.respond_to?(:collection_name)
96
163
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections'
4
+ require 'cuprum/collections/relation'
5
+
6
+ module Cuprum::Collections
7
+ # Class representing a singular or plural resource of entities.
8
+ class Resource < Cuprum::Collections::Relation
9
+ include Cuprum::Collections::Relation::Cardinality
10
+ include Cuprum::Collections::Relation::Disambiguation
11
+ include Cuprum::Collections::Relation::PrimaryKeys
12
+
13
+ # @overload initialize(entity_class: nil, name: nil, qualified_name: nil, singular_name: nil, **options)
14
+ # @param entity_class [Class, String] the class of entity represented by
15
+ # the resource.
16
+ # @param name [String] the name of the resource. Aliased as
17
+ # :resource_name.
18
+ # @param qualified_name [String] a scoped name for the resource.
19
+ # @param singular_name [String] the name of an entity in the resource.
20
+ # @param options [Hash] additional options for the resource.
21
+ #
22
+ # @option options plural [Boolean] if true, the resource represents a
23
+ # plural resource. Defaults to true. Can also be specified as :singular.
24
+ # @option options primary_key_name [String] the name of the primary key
25
+ # attribute. Defaults to 'id'.
26
+ # @option primary_key_type [Class, Stannum::Constraint] the type of
27
+ # the primary key attribute. Defaults to Integer.
28
+ def initialize(**params)
29
+ params = disambiguate_keyword(params, :entity_class, :resource_class)
30
+ params = disambiguate_keyword(params, :name, :resource_name)
31
+ params = disambiguate_keyword(
32
+ params,
33
+ :singular_name,
34
+ :singular_resource_name
35
+ )
36
+ @plural = resolve_plurality(**params)
37
+
38
+ super(**params)
39
+ end
40
+
41
+ # @return [Class] the class of entity represented by the resource.
42
+ def resource_class
43
+ tools.core_tools.deprecate '#resource_class method',
44
+ message: 'Use #entity_class instead'
45
+
46
+ entity_class
47
+ end
48
+
49
+ # @return [String] the name of the resource.
50
+ def resource_name
51
+ tools.core_tools.deprecate '#resource_name method',
52
+ message: 'Use #name instead'
53
+
54
+ name
55
+ end
56
+
57
+ # @return[String] the name of an entity in the resource.
58
+ def singular_resource_name
59
+ tools.core_tools.deprecate '#singular_resource_name method',
60
+ message: 'Use #singular_name instead'
61
+
62
+ singular_name
63
+ end
64
+ end
65
+ end