cuprum-rails 0.1.0 → 0.2.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +145 -0
  3. data/DEVELOPMENT.md +20 -0
  4. data/README.md +356 -63
  5. data/lib/cuprum/rails/action.rb +32 -16
  6. data/lib/cuprum/rails/actions/create.rb +62 -15
  7. data/lib/cuprum/rails/actions/destroy.rb +23 -7
  8. data/lib/cuprum/rails/actions/edit.rb +23 -7
  9. data/lib/cuprum/rails/actions/index.rb +30 -10
  10. data/lib/cuprum/rails/actions/middleware/associations/cache.rb +112 -0
  11. data/lib/cuprum/rails/actions/middleware/associations/find.rb +23 -0
  12. data/lib/cuprum/rails/actions/middleware/associations/parent.rb +70 -0
  13. data/lib/cuprum/rails/actions/middleware/associations/query.rb +140 -0
  14. data/lib/cuprum/rails/actions/middleware/associations.rb +12 -0
  15. data/lib/cuprum/rails/actions/middleware/log_request.rb +126 -0
  16. data/lib/cuprum/rails/actions/middleware/log_result.rb +51 -0
  17. data/lib/cuprum/rails/actions/middleware/resources/find.rb +44 -0
  18. data/lib/cuprum/rails/actions/middleware/resources/query.rb +91 -0
  19. data/lib/cuprum/rails/actions/middleware/resources.rb +11 -0
  20. data/lib/cuprum/rails/actions/middleware.rb +13 -0
  21. data/lib/cuprum/rails/actions/new.rb +16 -4
  22. data/lib/cuprum/rails/actions/parameter_validation.rb +60 -0
  23. data/lib/cuprum/rails/actions/resource_action.rb +119 -42
  24. data/lib/cuprum/rails/actions/show.rb +23 -7
  25. data/lib/cuprum/rails/actions/update.rb +70 -22
  26. data/lib/cuprum/rails/actions.rb +11 -7
  27. data/lib/cuprum/rails/collection.rb +27 -47
  28. data/lib/cuprum/rails/command.rb +3 -1
  29. data/lib/cuprum/rails/commands/destroy_one.rb +10 -6
  30. data/lib/cuprum/rails/commands/find_many.rb +8 -1
  31. data/lib/cuprum/rails/commands/find_matching.rb +1 -1
  32. data/lib/cuprum/rails/commands/find_one.rb +8 -0
  33. data/lib/cuprum/rails/commands/insert_one.rb +17 -6
  34. data/lib/cuprum/rails/commands/update_one.rb +16 -5
  35. data/lib/cuprum/rails/constraints/parameters_contract.rb +14 -0
  36. data/lib/cuprum/rails/constraints.rb +10 -0
  37. data/lib/cuprum/rails/controller.rb +12 -2
  38. data/lib/cuprum/rails/controllers/action.rb +100 -0
  39. data/lib/cuprum/rails/controllers/class_methods/actions.rb +33 -7
  40. data/lib/cuprum/rails/controllers/class_methods/configuration.rb +36 -0
  41. data/lib/cuprum/rails/controllers/class_methods/middleware.rb +88 -0
  42. data/lib/cuprum/rails/controllers/class_methods/validations.rb +2 -2
  43. data/lib/cuprum/rails/controllers/configuration.rb +41 -1
  44. data/lib/cuprum/rails/controllers/middleware.rb +59 -0
  45. data/lib/cuprum/rails/controllers.rb +2 -0
  46. data/lib/cuprum/rails/errors/invalid_parameters.rb +55 -0
  47. data/lib/cuprum/rails/errors/invalid_statement.rb +11 -0
  48. data/lib/cuprum/rails/errors/missing_parameter.rb +42 -0
  49. data/lib/cuprum/rails/errors/resource_error.rb +46 -0
  50. data/lib/cuprum/rails/errors.rb +6 -1
  51. data/lib/cuprum/rails/map_errors.rb +29 -1
  52. data/lib/cuprum/rails/query.rb +1 -1
  53. data/lib/cuprum/rails/repository.rb +12 -25
  54. data/lib/cuprum/rails/request.rb +149 -60
  55. data/lib/cuprum/rails/resource.rb +119 -85
  56. data/lib/cuprum/rails/responders/base_responder.rb +78 -0
  57. data/lib/cuprum/rails/responders/html/plural_resource.rb +9 -39
  58. data/lib/cuprum/rails/responders/html/rendering.rb +81 -0
  59. data/lib/cuprum/rails/responders/html/resource.rb +107 -0
  60. data/lib/cuprum/rails/responders/html/singular_resource.rb +9 -38
  61. data/lib/cuprum/rails/responders/html.rb +2 -0
  62. data/lib/cuprum/rails/responders/html_responder.rb +8 -52
  63. data/lib/cuprum/rails/responders/json/resource.rb +3 -3
  64. data/lib/cuprum/rails/responders/json_responder.rb +31 -16
  65. data/lib/cuprum/rails/responders/matching.rb +29 -27
  66. data/lib/cuprum/rails/responders/serialization.rb +11 -9
  67. data/lib/cuprum/rails/responders.rb +1 -0
  68. data/lib/cuprum/rails/responses/head_response.rb +24 -0
  69. data/lib/cuprum/rails/responses/html/redirect_back_response.rb +55 -0
  70. data/lib/cuprum/rails/responses/html/redirect_response.rb +19 -4
  71. data/lib/cuprum/rails/responses/html/render_response.rb +17 -5
  72. data/lib/cuprum/rails/responses/html.rb +6 -2
  73. data/lib/cuprum/rails/responses.rb +1 -0
  74. data/lib/cuprum/rails/result.rb +36 -0
  75. data/lib/cuprum/rails/routes.rb +36 -23
  76. data/lib/cuprum/rails/rspec/contract_helpers.rb +57 -0
  77. data/lib/cuprum/rails/rspec/contracts/action_contracts.rb +754 -0
  78. data/lib/cuprum/rails/rspec/contracts/actions/create_contracts.rb +289 -0
  79. data/lib/cuprum/rails/rspec/contracts/actions/destroy_contracts.rb +164 -0
  80. data/lib/cuprum/rails/rspec/contracts/actions/edit_contracts.rb +73 -0
  81. data/lib/cuprum/rails/rspec/contracts/actions/index_contracts.rb +108 -0
  82. data/lib/cuprum/rails/rspec/contracts/actions/new_contracts.rb +111 -0
  83. data/lib/cuprum/rails/rspec/contracts/actions/show_contracts.rb +72 -0
  84. data/lib/cuprum/rails/rspec/contracts/actions/update_contracts.rb +263 -0
  85. data/lib/cuprum/rails/rspec/contracts/actions.rb +8 -0
  86. data/lib/cuprum/rails/rspec/contracts/command_contracts.rb +479 -0
  87. data/lib/cuprum/rails/rspec/contracts/responder_contracts.rb +232 -0
  88. data/lib/cuprum/rails/rspec/contracts/routes_contracts.rb +363 -0
  89. data/lib/cuprum/rails/rspec/contracts/serializers_contracts.rb +70 -0
  90. data/lib/cuprum/rails/rspec/contracts.rb +8 -0
  91. data/lib/cuprum/rails/rspec/matchers/be_a_result_matcher.rb +64 -0
  92. data/lib/cuprum/rails/rspec/matchers.rb +41 -0
  93. data/lib/cuprum/rails/serializers/base_serializer.rb +60 -0
  94. data/lib/cuprum/rails/serializers/context.rb +84 -0
  95. data/lib/cuprum/rails/serializers/json/active_record_serializer.rb +2 -2
  96. data/lib/cuprum/rails/serializers/json/array_serializer.rb +9 -8
  97. data/lib/cuprum/rails/serializers/json/attributes_serializer.rb +95 -172
  98. data/lib/cuprum/rails/serializers/json/error_serializer.rb +2 -2
  99. data/lib/cuprum/rails/serializers/json/hash_serializer.rb +9 -8
  100. data/lib/cuprum/rails/serializers/json/identity_serializer.rb +3 -3
  101. data/lib/cuprum/rails/serializers/json/properties_serializer.rb +252 -0
  102. data/lib/cuprum/rails/serializers/json.rb +2 -1
  103. data/lib/cuprum/rails/serializers.rb +3 -1
  104. data/lib/cuprum/rails/version.rb +1 -1
  105. data/lib/cuprum/rails.rb +19 -16
  106. metadata +73 -131
  107. data/lib/cuprum/rails/controller_action.rb +0 -121
  108. data/lib/cuprum/rails/errors/missing_parameters.rb +0 -33
  109. data/lib/cuprum/rails/errors/missing_primary_key.rb +0 -46
  110. data/lib/cuprum/rails/errors/undefined_permitted_attributes.rb +0 -34
  111. data/lib/cuprum/rails/rspec/command_contract.rb +0 -460
  112. data/lib/cuprum/rails/rspec/define_route_contract.rb +0 -84
  113. data/lib/cuprum/rails/serializers/json/serializer.rb +0 -66
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cuprum/rails/controller_action'
3
+ require 'cuprum/rails/controllers/action'
4
4
  require 'cuprum/rails/controllers/class_methods'
5
5
 
6
6
  module Cuprum::Rails::Controllers::ClassMethods
@@ -18,8 +18,7 @@ module Cuprum::Rails::Controllers::ClassMethods
18
18
  validate_class(action_class, as: 'action class')
19
19
 
20
20
  action_name = action_name.intern
21
- own_actions[action_name] = Cuprum::Rails::ControllerAction.new(
22
- configuration,
21
+ own_actions[action_name] = Cuprum::Rails::Controllers::Action.new(
23
22
  action_class: action_class,
24
23
  action_name: action_name,
25
24
  member_action: member
@@ -28,7 +27,7 @@ module Cuprum::Rails::Controllers::ClassMethods
28
27
  define_action(action_name)
29
28
  end
30
29
 
31
- # @return [Hash<Symbol, Cuprum::Rails::ControllerAction>] the actions
30
+ # @return [Hash<Symbol, Cuprum::Rails::Controllers::Action>] the actions
32
31
  # defined for the controller.
33
32
  def actions
34
33
  ancestors
@@ -38,18 +37,45 @@ module Cuprum::Rails::Controllers::ClassMethods
38
37
  .reduce(&:merge)
39
38
  end
40
39
 
40
+ # @private
41
+ def apply_request_defaults(request)
42
+ request.format ||= configuration.default_format
43
+ end
44
+
45
+ # Generates a Cuprum::Rails::Request from a native request.
46
+ #
47
+ # Override this method to generate a request subclass.
48
+ #
49
+ # @param context [#request] the controller or controller context.
50
+ # @param options [Hash{Symbol=>Object}] additional options for the request.
51
+ #
52
+ # @return [Cuprum::Rails::Request] the generated request.
53
+ def build_request(context, **options)
54
+ Cuprum::Rails::Request.build(
55
+ context: context,
56
+ request: context.request,
57
+ **options
58
+ )
59
+ end
60
+
41
61
  # @private
42
62
  def own_actions
63
+ # :nocov:
43
64
  @own_actions ||= {}
65
+ # :nocov:
44
66
  end
45
67
 
46
68
  private
47
69
 
48
70
  def define_action(action_name)
49
71
  define_method(action_name) do
50
- request = Cuprum::Rails::Request.build(request: self.request)
51
- action = self.class.actions[action_name]
52
- response = action.call(request)
72
+ action = self.class.actions[action_name]
73
+ request =
74
+ self
75
+ .class
76
+ .build_request(self, member_action: action.member_action?)
77
+ .tap { |req| self.class.apply_request_defaults(req) }
78
+ response = action.call(self, request)
53
79
  response.call(self)
54
80
  end
55
81
  end
@@ -13,11 +13,40 @@ module Cuprum::Rails::Controllers::ClassMethods
13
13
  Cuprum::Rails::Controllers::Configuration.new(self)
14
14
  end
15
15
 
16
+ # @overload default_format
17
+ # @return [Symbol] the default format for controller requests.
18
+ #
19
+ # @overload default_format(format)
20
+ # Sets the default format for controller requests.
21
+ #
22
+ # @param format [String, Symbol] The format to set as default.
23
+ def default_format(format = nil)
24
+ if format.nil?
25
+ return @default_format if @default_format
26
+
27
+ return superclass.default_format if controller_class?(superclass)
28
+
29
+ return nil
30
+ end
31
+
32
+ validate_name(format, as: 'format')
33
+
34
+ @default_format = format.intern
35
+ end
36
+
16
37
  # @private
17
38
  def own_responders
18
39
  @own_responders ||= {}
19
40
  end
20
41
 
42
+ # Returns the repository defined for the controller.
43
+ #
44
+ # @return [Cuprum::Collections::Repository] the repository containing the
45
+ # data collections for the application or scope.
46
+ def repository
47
+ nil
48
+ end
49
+
21
50
  # Returns the resource defined for the controller.
22
51
  #
23
52
  # Controller subclasses must override this method.
@@ -60,5 +89,12 @@ module Cuprum::Rails::Controllers::ClassMethods
60
89
  json: Cuprum::Rails::Serializers::Json.default_serializers
61
90
  }
62
91
  end
92
+
93
+ private
94
+
95
+ def controller_class?(other)
96
+ other.singleton_class <
97
+ Cuprum::Rails::Controllers::ClassMethods::Configuration
98
+ end
63
99
  end
64
100
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rails/controllers/class_methods'
4
+ require 'cuprum/rails/controllers/middleware'
5
+
6
+ module Cuprum::Rails::Controllers::ClassMethods
7
+ # Provides a DSL for defining controller middleware.
8
+ module Middleware
9
+ # @overload middleware
10
+ # @return [Array<Cuprum::Rails::Controllers::Middleware>] the configured
11
+ # middleware for the controller.
12
+ #
13
+ # @overload middleware(command, except: [], only: {]})
14
+ # Defines middleware for the controller.
15
+ #
16
+ # @param command [Class, Cuprum::Command] The middleware command.
17
+ # Middleware commands must take two parameters: a next_command argument,
18
+ # and a request: keyword.
19
+ # @param except [Array<String, Symbol>] Action names to exclude. The
20
+ # middleware will not be applied to actions on this list.
21
+ # @param only [Array<String, Symbol>] Action names to include If this is
22
+ # not empty, the middleware will only be applied to actions on this
23
+ # list.
24
+ #
25
+ # @see Cuprum::Middleware
26
+ def middleware(command = nil, except: [], only: [])
27
+ unless command.nil?
28
+ own_middleware <<
29
+ build_middleware(command: command, except: except, only: only)
30
+ end
31
+
32
+ ancestors
33
+ .select { |ancestor| ancestor.respond_to?(:own_middleware) }
34
+ .reverse_each
35
+ .map(&:own_middleware)
36
+ .reduce(&:+)
37
+ end
38
+
39
+ # @private
40
+ def own_middleware
41
+ @own_middleware ||= []
42
+ end
43
+
44
+ private
45
+
46
+ def build_middleware(command:, except:, only:)
47
+ validate_command!(command)
48
+ validate_action_names!(except, as: 'except')
49
+ validate_action_names!(only, as: 'only')
50
+
51
+ Cuprum::Rails::Controllers::Middleware.new(
52
+ command: command,
53
+ except: except,
54
+ only: only
55
+ )
56
+ end
57
+
58
+ def valid_action_names?(action_names)
59
+ return false unless action_names.is_a?(Array)
60
+
61
+ action_names.all? do |action_name|
62
+ next false unless action_name.is_a?(String) || action_name.is_a?(Symbol)
63
+
64
+ !action_name.empty?
65
+ end
66
+ end
67
+
68
+ def validate_action_names!(action_names, as:)
69
+ return if action_names.nil?
70
+
71
+ return if valid_action_names?(action_names)
72
+
73
+ raise ArgumentError,
74
+ "#{as} must be a list of action names",
75
+ caller(1..-1)
76
+ end
77
+
78
+ def validate_command!(command)
79
+ return if command.is_a?(Cuprum::Command)
80
+
81
+ return if command.is_a?(Class) && command < Cuprum::Command
82
+
83
+ raise ArgumentError,
84
+ 'command must be an instance of or subclass of Cuprum::Command',
85
+ caller(1..-1)
86
+ end
87
+ end
88
+ end
@@ -7,13 +7,13 @@ module Cuprum::Rails::Controllers::ClassMethods
7
7
  module Validations
8
8
  private
9
9
 
10
- def validate_class(value, as:) # rubocop:disable Naming/MethodParameterName
10
+ def validate_class(value, as:)
11
11
  return if value.is_a?(Class)
12
12
 
13
13
  raise ArgumentError, "#{as} must be a Class", caller(1..-1)
14
14
  end
15
15
 
16
- def validate_name(value, as:) # rubocop:disable Naming/MethodParameterName
16
+ def validate_name(value, as:)
17
17
  raise ArgumentError, "#{as} can't be blank", caller(1..-1) if value.nil?
18
18
 
19
19
  unless value.is_a?(String) || value.is_a?(Symbol)
@@ -18,6 +18,20 @@ module Cuprum::Rails::Controllers
18
18
  # @return [#resource, #responders] the controller to delegate configuration.
19
19
  attr_reader :controller
20
20
 
21
+ # @!method controller_name
22
+ # @return [String] the name of the controller.
23
+
24
+ # @!method default_format
25
+ # @return [Symbol] the default format for controller requests.
26
+
27
+ # @!method middleware
28
+ # @return [Array<Cuprum::Rails::Controllers::Middleware>] the middleware
29
+ # defined for the controller.
30
+
31
+ # @!method repository
32
+ # @return [Cuprum::Collections::Repository] the repository containing the
33
+ # data collections for the application or scope.
34
+
21
35
  # @!method resource
22
36
  # @return [Cuprum::Rails::Resource] the resource defined for the
23
37
  # controller.
@@ -31,13 +45,27 @@ module Cuprum::Rails::Controllers
31
45
  # serializers for converting result values into serialized data.
32
46
 
33
47
  def_delegators :@controller,
48
+ :controller_name,
49
+ :default_format,
50
+ :middleware,
51
+ :repository,
34
52
  :resource,
35
53
  :responders,
36
54
  :serializers
37
55
 
56
+ # Finds the configured middleware for the requested action name.
57
+ #
58
+ # @param action_name [Symbol] The name of the action.
59
+ #
60
+ # @return [Array<Cuprum::Rails::Controllers::Middleware>] the configured
61
+ # middleware for the action.
62
+ def middleware_for(action_name)
63
+ middleware.select { |item| item.matches?(action_name) }
64
+ end
65
+
38
66
  # Finds the configured responder for the requested format.
39
67
  #
40
- # @param format [Symbol] The format to respond to.
68
+ # @param format [Symbol] the format to respond to.
41
69
  #
42
70
  # @return [Class] the responder class defined for the format.
43
71
  #
@@ -49,5 +77,17 @@ module Cuprum::Rails::Controllers
49
77
  "no responder registered for format #{format.inspect}"
50
78
  end
51
79
  end
80
+
81
+ # Finds the configured serializers for the requested format.
82
+ #
83
+ # @param format [Symbol] the format to respond to.
84
+ #
85
+ # @return [Hash<Class, Object>] the serializers for converting result values
86
+ # into serialized data.
87
+ def serializers_for(format)
88
+ serializers
89
+ .select { |key, _| key.is_a?(Class) }
90
+ .merge(serializers.fetch(format, {}))
91
+ end
52
92
  end
53
93
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ require 'cuprum/rails/controllers'
6
+
7
+ module Cuprum::Rails::Controllers
8
+ # A configured middleware option for a controller.
9
+ class Middleware
10
+ # @param command [Cuprum::Command] The middleware command to wrap the
11
+ # action or actions.
12
+ # @param except [Array<Symbol>] A list of action names; the middleware will
13
+ # not be applied to actions on the list.
14
+ # @param only [Array<Symbol>] A list of action names; the middleware will
15
+ # be applied only to actions on the list.
16
+ def initialize(command:, except: [], only: [])
17
+ @command = command
18
+ @except = Set.new(except.map(&:intern))
19
+ @only = Set.new(only.map(&:intern))
20
+ end
21
+
22
+ # @return [Cuprum::Middleware] the middleware command to wrap the action or
23
+ # actions.
24
+ attr_reader :command
25
+
26
+ # @return [Array<Symbol>] a list of action names; the middleware will not be
27
+ # applied to actions on the list.
28
+ attr_reader :except
29
+
30
+ # @return [Array<Symbol>] a list of action names; the middleware will be
31
+ # applied only to actions on the list.
32
+ attr_reader :only
33
+
34
+ # @private
35
+ def ==(other)
36
+ other.is_a?(Cuprum::Rails::Controllers::Middleware) &&
37
+ other.command == command &&
38
+ other.except == except &&
39
+ other.only == only
40
+ end
41
+
42
+ # Checks if the middleware will be applied to the named action.
43
+ #
44
+ # If the middleware defines any :except actions, returns false if the action
45
+ # name is in the set. If the middleware defines any :only actions, returns
46
+ # false unless the action name is in the set. Otherwise, returns true.
47
+ #
48
+ # @param action_name [Symbol] The name of the action.
49
+ #
50
+ # @return [true, false] whether the middleware will be applied.
51
+ def matches?(action_name)
52
+ return false unless except.empty? || except.exclude?(action_name)
53
+ return false unless only.empty? || only.include?(action_name)
54
+
55
+ true
56
+ end
57
+ alias match? matches?
58
+ end
59
+ end
@@ -5,6 +5,8 @@ require 'cuprum/rails'
5
5
  module Cuprum::Rails
6
6
  # Namespace for controller-specific functionality.
7
7
  module Controllers
8
+ autoload :Action, 'cuprum/rails/controllers/action'
8
9
  autoload :Configuration, 'cuprum/rails/controllers/configuration'
10
+ autoload :Middleware, 'cuprum/rails/controllers/middleware'
9
11
  end
10
12
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/error'
4
+
5
+ require 'cuprum/rails/errors'
6
+
7
+ module Cuprum::Rails::Errors
8
+ # Error class when a parameters hash does not match the expected contract.
9
+ class InvalidParameters < Cuprum::Error
10
+ # Short string used to identify the type of error.
11
+ TYPE = 'cuprum.rails.errors.invalid_parameters'
12
+
13
+ # @param errors [Stannum::Errors] the errors returned by the contract.
14
+ def initialize(errors:)
15
+ @errors = errors
16
+
17
+ super(message: default_message, errors: errors)
18
+ end
19
+
20
+ # @return [Stannum::Errors] the errors returned by the contract.
21
+ attr_reader :errors
22
+
23
+ private
24
+
25
+ def as_json_data
26
+ error_data =
27
+ errors
28
+ .group_by_path
29
+ .to_h do |path, errors|
30
+ [
31
+ join_path(path),
32
+ errors.map { |error| format_error(error) }
33
+ ]
34
+ end
35
+
36
+ { 'errors' => error_data }
37
+ end
38
+
39
+ def default_message
40
+ "invalid request parameters - #{errors.summary}"
41
+ end
42
+
43
+ def format_error(error)
44
+ tools.hash_tools.convert_keys_to_strings(error)
45
+ end
46
+
47
+ def join_path(path)
48
+ path.join('.')
49
+ end
50
+
51
+ def tools
52
+ SleepingKingStudios::Tools::Toolbelt.instance
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rails/errors'
4
+
5
+ module Cuprum::Rails::Errors
6
+ # Error class when a database execution error occurs.
7
+ class InvalidStatement < Cuprum::Error
8
+ # Short string used to identify the type of error.
9
+ TYPE = 'cuprum.rails.errors.invalid_statement'
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rails/errors'
4
+
5
+ module Cuprum::Rails::Errors
6
+ # Error class when a parameters hash does not include the expected keys.
7
+ class MissingParameter < Cuprum::Error
8
+ # Short string used to identify the type of error.
9
+ TYPE = 'cuprum.rails.errors.missing_parameter'
10
+
11
+ # @param parameter_name [String, Symbol] the name of the missing parameter.
12
+ # @param parameters [Hash] the received parameters.
13
+ def initialize(parameter_name:, parameters:)
14
+ @parameter_name = parameter_name
15
+ @parameters = parameters
16
+
17
+ super(
18
+ message: default_message,
19
+ parameter_name: parameter_name
20
+ )
21
+ end
22
+
23
+ # @return [String, Symbol] the name of the missing parameter.
24
+ attr_reader :parameter_name
25
+
26
+ # @return [Hash] the received parameters.
27
+ attr_reader :parameters
28
+
29
+ private
30
+
31
+ def as_json_data
32
+ {
33
+ 'parameter_name' => parameter_name,
34
+ 'parameters' => parameters
35
+ }
36
+ end
37
+
38
+ def default_message
39
+ "missing parameter #{parameter_name.inspect}"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/error'
4
+
5
+ require 'cuprum/rails/errors'
6
+
7
+ module Cuprum::Rails::Errors
8
+ # Error class when a resource is not correctly configured for an action.
9
+ class ResourceError < Cuprum::Error
10
+ # Short string used to identify the type of error.
11
+ TYPE = 'cuprum.rails.errors.resource_error'
12
+
13
+ # @param resource [Cuprum::Rails::Resource] the errored resource.
14
+ # @param message [String] the message to display, if any.
15
+ def initialize(resource:, message: nil)
16
+ @resource = resource
17
+
18
+ super(message: generate_message(message), resource: resource)
19
+ end
20
+
21
+ # @return [Cuprum::Rails::Resource] the errored resource.
22
+ attr_reader :resource
23
+
24
+ private
25
+
26
+ def as_json_data
27
+ {
28
+ 'resource' => {
29
+ 'entity_class' => resource.entity_class.to_s,
30
+ 'name' => resource.name.to_s,
31
+ 'qualified_name' => resource.qualified_name.to_s,
32
+ 'singular' => resource.singular?,
33
+ 'singular_name' => resource.singular_name.to_s
34
+ }
35
+ }
36
+ end
37
+
38
+ def generate_message(message = nil)
39
+ prefix = "invalid resource #{resource.name}"
40
+
41
+ return prefix if message.blank?
42
+
43
+ "#{prefix} - #{message}"
44
+ end
45
+ end
46
+ end
@@ -4,5 +4,10 @@ require 'cuprum/rails'
4
4
 
5
5
  module Cuprum::Rails
6
6
  # Namespace for custom Cuprum::Rails error classes.
7
- module Errors; end
7
+ module Errors
8
+ autoload :InvalidParameters, 'cuprum/rails/errors/invalid_parameters'
9
+ autoload :InvalidStatement, 'cuprum/rails/errors/invalid_statement'
10
+ autoload :MissingParameter, 'cuprum/rails/errors/missing_parameter'
11
+ autoload :ResourceError, 'cuprum/rails/errors/resource_error'
12
+ end
8
13
  end
@@ -28,17 +28,45 @@ module Cuprum::Rails
28
28
 
29
29
  private
30
30
 
31
+ # :nocov:
31
32
  def map_errors(native_errors:)
33
+ if Rails.version < '6.1'
34
+ map_errors_hash(native_errors: native_errors)
35
+ else
36
+ map_errors_object(native_errors: native_errors)
37
+ end
38
+ end
39
+
40
+ def map_errors_hash(native_errors:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
41
+ errors = Stannum::Errors.new
42
+ details = native_errors.details
43
+ messages = native_errors.messages
44
+
45
+ native_errors.keys.each do |attribute| # rubocop:disable Style/HashEachMethods
46
+ scoped = attribute == :base ? errors : errors[attribute]
47
+
48
+ details[attribute].each.with_index do |hsh, index|
49
+ message = messages[attribute][index]
50
+
51
+ scoped.add(hsh[:error], **hsh.except(:error).merge(message: message))
52
+ end
53
+ end
54
+
55
+ errors
56
+ end
57
+
58
+ def map_errors_object(native_errors:)
32
59
  errors = Stannum::Errors.new
33
60
 
34
61
  native_errors.each do |error|
35
62
  attribute = error.attribute
36
63
  scoped = attribute == :base ? errors : errors[attribute]
37
64
 
38
- scoped.add(error.type, message: error.message, **error.options)
65
+ scoped.add(error.type, **error.options.merge(message: error.message))
39
66
  end
40
67
 
41
68
  errors
42
69
  end
43
70
  end
71
+ # :nocov:
44
72
  end
@@ -24,7 +24,7 @@ module Cuprum::Rails
24
24
  super()
25
25
 
26
26
  default_order = { record_class.primary_key => :asc }
27
- @native_query = native_query || record_class.all.order(default_order)
27
+ @native_query = native_query || record_class.order(default_order)
28
28
  @record_class = record_class
29
29
  @limit = nil
30
30
  @offset = nil
@@ -8,37 +8,24 @@ require 'cuprum/rails/collection'
8
8
  module Cuprum::Rails
9
9
  # A repository represents a group of Rails collections.
10
10
  class Repository < Cuprum::Collections::Repository
11
- # Adds a new collection with the given name to the repository.
12
- #
13
- # @param record_class [Class] The ActiveRecord class for the collection.
14
- # @param options [Hash] Additional options to pass to Collection.new
15
- #
16
- # @return [Cuprum::Rails::Collection] the created collection.
17
- #
18
- # @see Cuprum::Rails::Collection#initialize.
19
- def build(record_class:, **options)
20
- validate_record_class!(record_class)
21
-
22
- collection = Cuprum::Rails::Collection.new(
23
- record_class: record_class,
24
- **options
25
- )
26
-
27
- add(collection)
11
+ private
28
12
 
29
- collection
13
+ def build_collection(**options)
14
+ Cuprum::Rails::Collection.new(**options)
30
15
  end
31
16
 
32
- private
17
+ def qualified_name_for(**parameters)
18
+ Cuprum::Collections::Relation::Disambiguation
19
+ .resolve_parameters(
20
+ parameters,
21
+ entity_class: :record_class,
22
+ name: :collection_name
23
+ )
24
+ .fetch(:qualified_name)
25
+ end
33
26
 
34
27
  def valid_collection?(collection)
35
28
  collection.is_a?(Cuprum::Rails::Collection)
36
29
  end
37
-
38
- def validate_record_class!(record_class)
39
- return if record_class.is_a?(Class) && record_class < ActiveRecord::Base
40
-
41
- raise ArgumentError, 'record class must be an ActiveRecord model'
42
- end
43
30
  end
44
31
  end