cuprum-rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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