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
@@ -5,13 +5,18 @@ require 'cuprum/rails/responses/html'
5
5
  module Cuprum::Rails::Responses::Html
6
6
  # Encapsulates an HTML response that redirects to a given path.
7
7
  class RedirectResponse
8
- # @param path [String] The path or url to redirect to.
9
- # @param status [Integer] The HTTP status of the response.
10
- def initialize(path, status: 302)
8
+ # @param flash [Hash] the flash messages to set.
9
+ # @param path [String] the path or url to redirect to.
10
+ # @param status [Integer] the HTTP status of the response.
11
+ def initialize(path, flash: {}, status: 302)
12
+ @flash = flash
11
13
  @path = path
12
14
  @status = status
13
15
  end
14
16
 
17
+ # @return [Hash] the flash messages to set.
18
+ attr_reader :flash
19
+
15
20
  # @return [String] the path or url to redirect to.
16
21
  attr_reader :path
17
22
 
@@ -20,10 +25,20 @@ module Cuprum::Rails::Responses::Html
20
25
 
21
26
  # Calls the renderer's #redirect_to method with the path and status.
22
27
  #
23
- # @param renderer [#redirect_to] The context for executing the response,
28
+ # @param renderer [#redirect_to] The tontext for executing the response,
24
29
  # such as a Rails controller.
25
30
  def call(renderer)
31
+ assign_flash(renderer)
32
+
26
33
  renderer.redirect_to(path, status: status)
27
34
  end
35
+
36
+ private
37
+
38
+ def assign_flash(renderer)
39
+ flash.each do |key, value|
40
+ renderer.flash[key] = value
41
+ end
42
+ end
28
43
  end
29
44
  end
@@ -5,12 +5,14 @@ require 'cuprum/rails/responses/html'
5
5
  module Cuprum::Rails::Responses::Html
6
6
  # Encapsulates an HTML response that renders a given template.
7
7
  class RenderResponse
8
- # @param assigns [Hash] Variables to assign when rendering the template.
9
- # @param layout [String] The layout to render.
10
- # @param status [Integer] The HTTP status of the response.
11
- # @param template [String, Symbol] The template to render.
12
- def initialize(template, assigns: {}, layout: nil, status: 200)
8
+ # @param assigns [Hash] variables to assign when rendering the template.
9
+ # @param flash [Hash] the flash messages to set.
10
+ # @param layout [String] the layout to render.
11
+ # @param status [Integer] the HTTP status of the response.
12
+ # @param template [String, Symbol] the template to render.
13
+ def initialize(template, assigns: {}, flash: {}, layout: nil, status: 200)
13
14
  @assigns = assigns
15
+ @flash = flash
14
16
  @layout = layout
15
17
  @status = status
16
18
  @template = template
@@ -19,6 +21,9 @@ module Cuprum::Rails::Responses::Html
19
21
  # @return [Hash] variables to assign when rendering the template.
20
22
  attr_reader :assigns
21
23
 
24
+ # @return [Hash] the flash messages to set.
25
+ attr_reader :flash
26
+
22
27
  # @return [String] the layout to render.
23
28
  attr_reader :layout
24
29
 
@@ -33,6 +38,7 @@ module Cuprum::Rails::Responses::Html
33
38
  # @param renderer [#render] The context for executing the response, such as
34
39
  # a Rails controller.
35
40
  def call(renderer)
41
+ assign_flash(renderer)
36
42
  assign_variables(renderer)
37
43
 
38
44
  options = { status: status }
@@ -43,6 +49,12 @@ module Cuprum::Rails::Responses::Html
43
49
 
44
50
  private
45
51
 
52
+ def assign_flash(renderer)
53
+ flash.each do |key, value|
54
+ renderer.flash.now[key] = value
55
+ end
56
+ end
57
+
46
58
  def assign_variables(renderer)
47
59
  assigns.each do |key, value|
48
60
  renderer.instance_variable_set("@#{key}", value)
@@ -5,7 +5,11 @@ require 'cuprum/rails/responses'
5
5
  module Cuprum::Rails::Responses
6
6
  # Namespace for response objects, which encapsulate Html responses.
7
7
  module Html
8
- autoload :RedirectResponse, 'cuprum/rails/responses/html/redirect_response'
9
- autoload :RenderResponse, 'cuprum/rails/responses/html/render_response'
8
+ autoload :RedirectBackResponse,
9
+ 'cuprum/rails/responses/html/redirect_back_response'
10
+ autoload :RedirectResponse,
11
+ 'cuprum/rails/responses/html/redirect_response'
12
+ autoload :RenderResponse,
13
+ 'cuprum/rails/responses/html/render_response'
10
14
  end
11
15
  end
@@ -5,6 +5,7 @@ require 'cuprum/rails'
5
5
  module Cuprum::Rails
6
6
  # Namespace for response objects, which encapsulate server responses.
7
7
  module Responses
8
+ autoload :HeadResponse, 'cuprum/rails/responses/head_response'
8
9
  autoload :Html, 'cuprum/rails/responses/html'
9
10
  autoload :JsonResponse, 'cuprum/rails/responses/json_response'
10
11
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rails'
4
+
5
+ module Cuprum::Rails
6
+ # Result class representing the result of calling an action.
7
+ #
8
+ # In addition to the standard properties inherited from Cuprum::Result, each
9
+ # Cuprum::Rails::Result also includes a #metadata property. This represents
10
+ # secondary information about the result that may be relevant for rendering or
11
+ # displaying the data, but is not part of the requested value. For example.
12
+ # information about the current controller would be metadata, as would the
13
+ # current authentication session.
14
+ class Result < Cuprum::Result
15
+ # @param value [Object] the value returned by calling the action.
16
+ # @param error [Cuprum::Error] the error (if any) generated when the action
17
+ # was called.
18
+ # @param metadata [Hash{Symbol => Object}] the request or action metadata.
19
+ # @param status [String, Symbol, nil] the status of the result. Must be
20
+ # :success, :failure, or nil.
21
+ def initialize(error: nil, metadata: {}, status: nil, value: nil)
22
+ super(error: error, status: status, value: value)
23
+
24
+ @metadata = metadata
25
+ end
26
+
27
+ # @return [Hash{Symbol => Object}] the request or action metadata.
28
+ attr_reader :metadata
29
+
30
+ # @return [Hash{Symbol => Object}] a Hash representation of the result.
31
+ def properties
32
+ super().merge(metadata: metadata)
33
+ end
34
+ alias to_h properties
35
+ end
36
+ end
@@ -4,7 +4,7 @@ require 'cuprum/rails'
4
4
 
5
5
  module Cuprum::Rails
6
6
  # Represent the routes available for a given resource.
7
- class Routes
7
+ class Routes # rubocop:disable Metrics/ClassLength
8
8
  # Error class when a wildcard value is missing for a route.
9
9
  class MissingWildcardError < StandardError; end
10
10
 
@@ -37,18 +37,18 @@ module Cuprum::Rails
37
37
  private
38
38
 
39
39
  def define_collection_route(action_name, original_path)
40
- define_method :"#{action_name}_path" do
40
+ define_method :"#{action_name}_path" do |**wildcards|
41
41
  path = resolve_base_path(original_path)
42
42
 
43
- insert_wildcards(path)
43
+ insert_wildcards(path, **wildcards)
44
44
  end
45
45
  end
46
46
 
47
47
  def define_member_route(action_name, original_path)
48
- define_method :"#{action_name}_path" do |entity|
48
+ define_method :"#{action_name}_path" do |entity = nil, **wildcards|
49
49
  path = resolve_base_path(original_path)
50
50
 
51
- insert_wildcards(path, entity)
51
+ insert_wildcards(path, entity, **wildcards)
52
52
  end
53
53
  end
54
54
 
@@ -75,10 +75,12 @@ module Cuprum::Rails
75
75
  end
76
76
  end
77
77
 
78
- # @param base_path [String] The relative path of the resource.
79
- def initialize(base_path:, &block)
80
- @base_path = base_path
81
- @wildcards = {}
78
+ # @param base_path [String] the relative path of the resource.
79
+ # @param parent_path [String] the path to the parent resource, if any.
80
+ def initialize(base_path:, parent_path: nil, &block)
81
+ @base_path = base_path
82
+ @parent_path = parent_path
83
+ @wildcards = {}
82
84
 
83
85
  singleton_class.instance_exec(&block) if block_given?
84
86
  end
@@ -91,7 +93,9 @@ module Cuprum::Rails
91
93
 
92
94
  # @return [String] the path to the parent resource index.
93
95
  def parent_path
94
- root_path
96
+ return '/' if @parent_path.nil?
97
+
98
+ insert_wildcards(@parent_path)
95
99
  end
96
100
 
97
101
  # @return [String] the root path for the application.
@@ -120,15 +124,15 @@ module Cuprum::Rails
120
124
 
121
125
  private
122
126
 
123
- def insert_wildcards(path, entity = nil)
127
+ def insert_wildcards(path, value_or_entity = nil, **wildcards)
128
+ wildcards = wildcards.merge('id' => value_or_entity) if value_or_entity
129
+
124
130
  path
125
131
  .split('/')
126
132
  .map do |segment|
127
133
  next segment unless segment.start_with?(':')
128
134
 
129
- next resolve_primary_key(entity) if segment == ':id'
130
-
131
- resolve_wildcard(segment)
135
+ resolve_wildcard(segment, **wildcards)
132
136
  end
133
137
  .join('/')
134
138
  end
@@ -141,24 +145,33 @@ module Cuprum::Rails
141
145
  "#{base_path}/#{path}"
142
146
  end
143
147
 
144
- def resolve_primary_key(entity)
145
- raise MissingWildcardError, 'missing wildcard :id' if entity.nil?
148
+ def resolve_primary_key(value_or_entity)
149
+ raise MissingWildcardError, 'missing wildcard :id' if value_or_entity.nil?
150
+
151
+ unless value_or_entity.class.respond_to?(:primary_key)
152
+ return value_or_entity
153
+ end
146
154
 
147
- primary_key = entity.class.primary_key
155
+ primary_key = value_or_entity.class.primary_key
148
156
 
149
- entity[primary_key]
157
+ value_or_entity[primary_key]
150
158
  end
151
159
 
152
- def resolve_wildcard(segment)
160
+ def resolve_wildcard(segment, **wildcards)
161
+ wildcards = self.wildcards.merge(wildcards)
162
+ value = wildcard_value(segment, **wildcards)
163
+
164
+ resolve_primary_key(value)
165
+ end
166
+
167
+ def wildcard_value(segment, **wildcards)
153
168
  wildcard = segment[1..] # :something_id to something_id
154
169
 
155
- return wildcards[wildcard] if wildcards.include?(wildcard)
170
+ return wildcards[wildcard] if wildcards.key?(wildcard)
156
171
 
157
172
  wildcard = segment[1...-3] # :something_id to something
158
173
 
159
- if wildcards.include?(wildcard)
160
- return resolve_primary_key(wildcards[wildcard])
161
- end
174
+ return wildcards[wildcard] if wildcards.key?(wildcard)
162
175
 
163
176
  raise MissingWildcardError, "missing wildcard #{segment}"
164
177
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cuprum::Rails::RSpec
4
+ # Helper methods for defining RSpec contracts.
5
+ module ContractHelpers
6
+ class << self
7
+ # Resolves a configuration option.
8
+ #
9
+ # If the configured value is a Proc, the Proc is executed in the context
10
+ # of the example group and the resulting value returned. Otherwise,
11
+ # returns the configured value, or the given default if the configured
12
+ # value is nil.
13
+ #
14
+ # @param value [Object] The configured value for the option.
15
+ # @param context [RSpec::SleepingKingStudios::ExampleGroup] The example
16
+ # group used as a context if the configured value is a Proc.
17
+ # @param default [Object] An optional default value if the configured
18
+ # value is nil.
19
+ def option_with_default(value, context:, default: nil)
20
+ # :nocov:
21
+ case value
22
+ when Proc
23
+ return context.instance_exec(&value) if value.arity.zero?
24
+
25
+ context.instance_exec(default, &value)
26
+ when nil
27
+ default
28
+ else
29
+ value
30
+ end
31
+ # :nocov:
32
+ end
33
+ end
34
+
35
+ # Resolves a configuration option.
36
+ #
37
+ # If the configured value is a Proc, the Proc is executed in the context of
38
+ # the example group and the resulting value returned. Otherwise, returns the
39
+ # configured value, or the given default if the configured value is nil.
40
+ #
41
+ # @param value [Object] The configured value for the option.
42
+ # @param context [RSpec::SleepingKingStudios::ExampleGroup] The example
43
+ # group used as a context if the configured value is a Proc.
44
+ # @param default [Object] An optional default value if the configured value
45
+ # is nil.
46
+ def option_with_default(value, context: nil, default: nil)
47
+ # :nocov:
48
+ Cuprum::Rails::RSpec::ContractHelpers
49
+ .option_with_default(
50
+ value,
51
+ context: context || self,
52
+ default: default
53
+ )
54
+ # :nocov:
55
+ end
56
+ end
57
+ end