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
@@ -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