cuprum-rails 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +98 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/DEVELOPMENT.md +28 -0
- data/LICENSE +22 -0
- data/README.md +1045 -0
- data/lib/cuprum/rails/action.rb +45 -0
- data/lib/cuprum/rails/actions/create.rb +49 -0
- data/lib/cuprum/rails/actions/destroy.rb +22 -0
- data/lib/cuprum/rails/actions/edit.rb +22 -0
- data/lib/cuprum/rails/actions/index.rb +55 -0
- data/lib/cuprum/rails/actions/new.rb +19 -0
- data/lib/cuprum/rails/actions/resource_action.rb +75 -0
- data/lib/cuprum/rails/actions/show.rb +22 -0
- data/lib/cuprum/rails/actions/update.rb +59 -0
- data/lib/cuprum/rails/actions.rb +16 -0
- data/lib/cuprum/rails/collection.rb +115 -0
- data/lib/cuprum/rails/command.rb +137 -0
- data/lib/cuprum/rails/commands/assign_one.rb +66 -0
- data/lib/cuprum/rails/commands/build_one.rb +55 -0
- data/lib/cuprum/rails/commands/destroy_one.rb +43 -0
- data/lib/cuprum/rails/commands/find_many.rb +60 -0
- data/lib/cuprum/rails/commands/find_matching.rb +121 -0
- data/lib/cuprum/rails/commands/find_one.rb +50 -0
- data/lib/cuprum/rails/commands/insert_one.rb +41 -0
- data/lib/cuprum/rails/commands/update_one.rb +49 -0
- data/lib/cuprum/rails/commands/validate_one.rb +68 -0
- data/lib/cuprum/rails/commands.rb +18 -0
- data/lib/cuprum/rails/controller.rb +50 -0
- data/lib/cuprum/rails/controller_action.rb +121 -0
- data/lib/cuprum/rails/controllers/class_methods/actions.rb +57 -0
- data/lib/cuprum/rails/controllers/class_methods/configuration.rb +64 -0
- data/lib/cuprum/rails/controllers/class_methods/validations.rb +30 -0
- data/lib/cuprum/rails/controllers/class_methods.rb +15 -0
- data/lib/cuprum/rails/controllers/configuration.rb +53 -0
- data/lib/cuprum/rails/controllers.rb +10 -0
- data/lib/cuprum/rails/errors/missing_parameters.rb +33 -0
- data/lib/cuprum/rails/errors/missing_primary_key.rb +46 -0
- data/lib/cuprum/rails/errors/undefined_permitted_attributes.rb +34 -0
- data/lib/cuprum/rails/errors.rb +8 -0
- data/lib/cuprum/rails/map_errors.rb +44 -0
- data/lib/cuprum/rails/query.rb +77 -0
- data/lib/cuprum/rails/query_builder.rb +78 -0
- data/lib/cuprum/rails/repository.rb +44 -0
- data/lib/cuprum/rails/request.rb +105 -0
- data/lib/cuprum/rails/resource.rb +145 -0
- data/lib/cuprum/rails/responders/actions.rb +73 -0
- data/lib/cuprum/rails/responders/html/plural_resource.rb +62 -0
- data/lib/cuprum/rails/responders/html/singular_resource.rb +59 -0
- data/lib/cuprum/rails/responders/html.rb +11 -0
- data/lib/cuprum/rails/responders/html_responder.rb +129 -0
- data/lib/cuprum/rails/responders/json/resource.rb +60 -0
- data/lib/cuprum/rails/responders/json.rb +10 -0
- data/lib/cuprum/rails/responders/json_responder.rb +122 -0
- data/lib/cuprum/rails/responders/matching.rb +145 -0
- data/lib/cuprum/rails/responders/serialization.rb +36 -0
- data/lib/cuprum/rails/responders.rb +15 -0
- data/lib/cuprum/rails/responses/html/redirect_response.rb +29 -0
- data/lib/cuprum/rails/responses/html/render_response.rb +52 -0
- data/lib/cuprum/rails/responses/html.rb +11 -0
- data/lib/cuprum/rails/responses/json_response.rb +29 -0
- data/lib/cuprum/rails/responses.rb +11 -0
- data/lib/cuprum/rails/routes.rb +166 -0
- data/lib/cuprum/rails/routing/plural_routes.rb +26 -0
- data/lib/cuprum/rails/routing/singular_routes.rb +24 -0
- data/lib/cuprum/rails/routing.rb +11 -0
- data/lib/cuprum/rails/rspec/command_contract.rb +460 -0
- data/lib/cuprum/rails/rspec/define_route_contract.rb +84 -0
- data/lib/cuprum/rails/rspec.rb +8 -0
- data/lib/cuprum/rails/serializers/json/active_record_serializer.rb +24 -0
- data/lib/cuprum/rails/serializers/json/array_serializer.rb +40 -0
- data/lib/cuprum/rails/serializers/json/attributes_serializer.rb +217 -0
- data/lib/cuprum/rails/serializers/json/error_serializer.rb +24 -0
- data/lib/cuprum/rails/serializers/json/hash_serializer.rb +44 -0
- data/lib/cuprum/rails/serializers/json/identity_serializer.rb +21 -0
- data/lib/cuprum/rails/serializers/json/serializer.rb +66 -0
- data/lib/cuprum/rails/serializers/json.rb +40 -0
- data/lib/cuprum/rails/serializers.rb +10 -0
- data/lib/cuprum/rails/version.rb +59 -0
- data/lib/cuprum/rails.rb +31 -0
- metadata +286 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/repository'
|
4
|
+
|
5
|
+
require 'cuprum/rails'
|
6
|
+
require 'cuprum/rails/collection'
|
7
|
+
|
8
|
+
module Cuprum::Rails
|
9
|
+
# A repository represents a group of Rails collections.
|
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)
|
28
|
+
|
29
|
+
collection
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def valid_collection?(collection)
|
35
|
+
collection.is_a?(Cuprum::Rails::Collection)
|
36
|
+
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
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails'
|
4
|
+
|
5
|
+
module Cuprum::Rails
|
6
|
+
# Wraps a web request with a generic interface.
|
7
|
+
class Request
|
8
|
+
class << self
|
9
|
+
FILTERED_HEADER_PREFIXES = %w[
|
10
|
+
action_
|
11
|
+
puma
|
12
|
+
rack
|
13
|
+
].freeze
|
14
|
+
private_constant :FILTERED_HEADER_PREFIXES
|
15
|
+
|
16
|
+
FILTERED_PARAMS = %w[controller action].freeze
|
17
|
+
private_constant :FILTERED_PARAMS
|
18
|
+
|
19
|
+
# Generates a Request from a native Rails request.
|
20
|
+
#
|
21
|
+
# @param request [] The native request to build.
|
22
|
+
#
|
23
|
+
# @return [Cuprum::Rails::Request] the generated request.
|
24
|
+
def build(request:)
|
25
|
+
new(
|
26
|
+
authorization: request.authorization,
|
27
|
+
body_params: request.request_parameters,
|
28
|
+
format: request.format.symbol,
|
29
|
+
headers: filter_headers(request.headers),
|
30
|
+
method: request.request_method_symbol,
|
31
|
+
params: filter_params(request.params),
|
32
|
+
path: request.fullpath,
|
33
|
+
query_params: request.query_parameters
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def filter_headers(headers)
|
40
|
+
headers.reject do |key, _|
|
41
|
+
FILTERED_HEADER_PREFIXES.any? { |prefix| key.start_with?(prefix) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def filter_params(params)
|
46
|
+
params.reject { |key, _| FILTERED_PARAMS.include?(key) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param authorization [String, nil] The authorization header, if any.
|
51
|
+
# @param body_params [Hash<String, Object>] The parameters from the request
|
52
|
+
# body.
|
53
|
+
# @param format [Symbol] The request format, e.g. :html or :json.
|
54
|
+
# @param headers [Hash<String, String>] The request headers.
|
55
|
+
# @param method [Symbol] The HTTP method used for the request.
|
56
|
+
# @param params [Hash<String, Object>] The merged GET and POST parameters.
|
57
|
+
# @param query_params [Hash<String, Object>] The query parameters.
|
58
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
59
|
+
body_params:,
|
60
|
+
format:,
|
61
|
+
headers:,
|
62
|
+
method:,
|
63
|
+
params:,
|
64
|
+
path:,
|
65
|
+
query_params:,
|
66
|
+
authorization: nil
|
67
|
+
)
|
68
|
+
@authorization = authorization
|
69
|
+
@body_params = body_params
|
70
|
+
@format = format
|
71
|
+
@headers = headers
|
72
|
+
@method = method
|
73
|
+
@path = path
|
74
|
+
@params = params
|
75
|
+
@query_params = query_params
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [String, nil] the authorization header, if any.
|
79
|
+
attr_reader :authorization
|
80
|
+
|
81
|
+
# @return [Hash<String, Object>] The parameters from the request body.
|
82
|
+
attr_reader :body_params
|
83
|
+
alias body_parameters body_params
|
84
|
+
|
85
|
+
# @return [Symbol] the request format, e.g. :html or :json.
|
86
|
+
attr_reader :format
|
87
|
+
|
88
|
+
# @return [Hash<String, String>] the request headers.
|
89
|
+
attr_reader :headers
|
90
|
+
|
91
|
+
# @return [Symbol] the HTTP method used for the request.
|
92
|
+
attr_reader :method
|
93
|
+
|
94
|
+
# @return [Hash<String, Object>] The merged GET and POST parameters.
|
95
|
+
attr_reader :params
|
96
|
+
alias parameters params
|
97
|
+
|
98
|
+
# @return [String] the relative path of the request, including params.
|
99
|
+
attr_reader :path
|
100
|
+
|
101
|
+
# @return [Hash<String, Object>] the query parameters.
|
102
|
+
attr_reader :query_params
|
103
|
+
alias query_parameters query_params
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails'
|
4
|
+
|
5
|
+
module Cuprum::Rails
|
6
|
+
# Value object representing a controller resource.
|
7
|
+
class Resource
|
8
|
+
# @param collection [Cuprum::Collections::Base] Collection representing the
|
9
|
+
# resource data.
|
10
|
+
# @param options [Hash] Additional options for the resource.
|
11
|
+
# @param resource_class [Class] Class representing the resource items.
|
12
|
+
# @param resource_name [String] The name of the resource.
|
13
|
+
# @param routes [Cuprum::Rails::Routes] The routes defined for the resource.
|
14
|
+
# @param singular [Boolean] Indicates that the resource is a singular
|
15
|
+
# collection, and has only one member.
|
16
|
+
#
|
17
|
+
# @option options default_order [Hash] The default ordering for the resource
|
18
|
+
# items.
|
19
|
+
# @option options permitted_attributes [Array] List of attributes that can
|
20
|
+
# be set or changed by resourceful actions.
|
21
|
+
# @option options primary_key [String, Symbol] The name of the primary key
|
22
|
+
# for the resource, if any.
|
23
|
+
# @option options singular_resource_name [String] The singular form of the
|
24
|
+
# resource name.
|
25
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
26
|
+
collection: nil,
|
27
|
+
resource_class: nil,
|
28
|
+
resource_name: nil,
|
29
|
+
routes: nil,
|
30
|
+
singular: false,
|
31
|
+
**options
|
32
|
+
)
|
33
|
+
unless resource_class || resource_name
|
34
|
+
raise ArgumentError, 'missing keyword :resource_class or :resource_name'
|
35
|
+
end
|
36
|
+
|
37
|
+
validate_permitted_attributes(options[:permitted_attributes])
|
38
|
+
|
39
|
+
@collection = collection
|
40
|
+
@options = options
|
41
|
+
@resource_class = resource_class
|
42
|
+
@resource_name = resource_name.to_s unless resource_name.nil?
|
43
|
+
@routes = routes
|
44
|
+
@singular = !!singular
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Cuprum::Collections::Base] collection representing the resource
|
48
|
+
# data.
|
49
|
+
attr_reader :collection
|
50
|
+
|
51
|
+
# @return [Hash] additional options for the resource.
|
52
|
+
attr_reader :options
|
53
|
+
|
54
|
+
# @return [Class] class representing the resource items.
|
55
|
+
attr_reader :resource_class
|
56
|
+
|
57
|
+
# @return [String] the base url for the resource.
|
58
|
+
def base_path
|
59
|
+
@base_path ||=
|
60
|
+
options
|
61
|
+
.fetch(:base_path) { "/#{resource_name.underscore}" }
|
62
|
+
.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Hash] the default ordering for the resource items.
|
66
|
+
def default_order
|
67
|
+
@default_order ||= options.fetch(:default_order, {})
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Array] list of attributes that can be set or changed by
|
71
|
+
# resourceful actions.
|
72
|
+
def permitted_attributes
|
73
|
+
@permitted_attributes ||= options.fetch(:permitted_attributes, nil)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Boolean] true if the collection is a plural collection, otherwise
|
77
|
+
# false.
|
78
|
+
def plural?
|
79
|
+
!@singular
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [String] the name of the primary key for the resource, if any.
|
83
|
+
def primary_key
|
84
|
+
@primary_key ||=
|
85
|
+
options
|
86
|
+
.fetch(:primary_key) { resource_class&.primary_key }
|
87
|
+
.yield_self { |value| value.nil? ? nil : value.to_s }
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [String] the name of the resource.
|
91
|
+
def resource_name
|
92
|
+
return @resource_name if @resource_name
|
93
|
+
|
94
|
+
name = resource_class.name.split('::').last.underscore
|
95
|
+
|
96
|
+
@resource_name = plural? ? name.pluralize : name
|
97
|
+
end
|
98
|
+
|
99
|
+
# Generates the routes for the resource and injects the given wildcards.
|
100
|
+
#
|
101
|
+
# @param wildcards [Hash] The wildcard values to use in the routes.
|
102
|
+
#
|
103
|
+
# @return [Cuprum::Rails::Routes] the routes with injected wildcards.
|
104
|
+
def routes(wildcards: {})
|
105
|
+
routes_without_wildcards.with_wildcards(wildcards)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Boolean] true if the collection is a singular collection,
|
109
|
+
# otherwise false.
|
110
|
+
def singular?
|
111
|
+
@singular
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [String] the singular form of the resource name.
|
115
|
+
def singular_resource_name
|
116
|
+
@singular_resource_name ||=
|
117
|
+
options
|
118
|
+
.fetch(:singular_resource_name) do
|
119
|
+
resource_name.singularize
|
120
|
+
end
|
121
|
+
.to_s
|
122
|
+
end
|
123
|
+
|
124
|
+
def validate_permitted_attributes(attributes)
|
125
|
+
return if attributes.nil? || attributes.is_a?(Array)
|
126
|
+
|
127
|
+
raise ArgumentError,
|
128
|
+
'keyword :permitted_attributes must be an Array or nil',
|
129
|
+
caller(1..-1)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def routes_without_wildcards
|
135
|
+
return @routes if @routes
|
136
|
+
|
137
|
+
@routes =
|
138
|
+
if plural?
|
139
|
+
Cuprum::Rails::Routing::PluralRoutes.new(base_path: base_path)
|
140
|
+
else
|
141
|
+
Cuprum::Rails::Routing::SingularRoutes.new(base_path: base_path)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/matcher'
|
4
|
+
|
5
|
+
require 'cuprum/rails/responders'
|
6
|
+
|
7
|
+
module Cuprum::Rails::Responders
|
8
|
+
# Implements matching a result to action-specific response clauses.
|
9
|
+
module Actions
|
10
|
+
# Provides a DSL for generating action-specific response clauses.
|
11
|
+
module ClassMethods
|
12
|
+
# Creates a new response matcher specific to the specified action.
|
13
|
+
#
|
14
|
+
# @param action_name [String, Symbol] The name of the action.
|
15
|
+
#
|
16
|
+
# @yield The matcher definition.
|
17
|
+
def action(action_name, &block)
|
18
|
+
validate_action_name!(action_name)
|
19
|
+
|
20
|
+
actions[action_name.intern] = Cuprum::Matcher.new(&block)
|
21
|
+
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# @private
|
26
|
+
def actions
|
27
|
+
@actions ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# @private
|
31
|
+
def matchers(action_name: nil, **_keywords)
|
32
|
+
return super unless action_name
|
33
|
+
|
34
|
+
action = actions[action_name.intern]
|
35
|
+
|
36
|
+
action.nil? ? super : [action, *super]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def validate_action_name!(action_name)
|
42
|
+
if action_name.nil? || action_name.to_s.empty?
|
43
|
+
raise ArgumentError, "action name can't be blank", caller(1..-1)
|
44
|
+
end
|
45
|
+
|
46
|
+
return if action_name.is_a?(String) || action_name.is_a?(Symbol)
|
47
|
+
|
48
|
+
raise ArgumentError, 'action name must be a String or Symbol',
|
49
|
+
caller(1..-1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!method call(result)
|
54
|
+
# (see Cuprum::Rails::Responders::Matching#call)
|
55
|
+
#
|
56
|
+
# If the responder defines an action matcher that matches the given action
|
57
|
+
# name, that matcher is matched against the result before any match
|
58
|
+
# clauses defined directly on the responder.
|
59
|
+
|
60
|
+
# @private
|
61
|
+
def self.included(other)
|
62
|
+
super
|
63
|
+
|
64
|
+
other.extend(ClassMethods)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def matcher_options
|
70
|
+
super().merge(action_name: action_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/failed_validation'
|
4
|
+
|
5
|
+
require 'cuprum/rails/responders/html'
|
6
|
+
|
7
|
+
module Cuprum::Rails::Responders::Html
|
8
|
+
# Defines default responses for a plural RESTful resource.
|
9
|
+
#
|
10
|
+
# - #create failure: renders the :new template.
|
11
|
+
# - #create success: redirects to the resource #show page.
|
12
|
+
# - #destroy success: redirects to the resource #index page.
|
13
|
+
# - #index failure: redirects to the root path.
|
14
|
+
# - #update failure: renders the :edit template.
|
15
|
+
# - #update success: redirects to the resource #show page.
|
16
|
+
#
|
17
|
+
# Responds to any other successful result by rendering the template for the
|
18
|
+
# action name and passing the result value as assigned variables. For a
|
19
|
+
# failing result, redirects to either the show page or the index page for the
|
20
|
+
# resource, based on the resource's defined #routes.
|
21
|
+
class PluralResource < Cuprum::Rails::Responders::HtmlResponder
|
22
|
+
action :create do
|
23
|
+
match :failure, error: Cuprum::Collections::Errors::FailedValidation do
|
24
|
+
render :new,
|
25
|
+
assigns: result.value.merge(errors: result.error.errors),
|
26
|
+
status: 422 # rubocop:disable Rails/HttpStatus
|
27
|
+
end
|
28
|
+
|
29
|
+
match :success do
|
30
|
+
entity = result.value[resource.singular_resource_name]
|
31
|
+
|
32
|
+
redirect_to resource.routes.show_path(entity)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
action :destroy do
|
37
|
+
match :success do
|
38
|
+
redirect_to resource.routes.index_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
action :index do
|
43
|
+
match :failure do
|
44
|
+
redirect_to resource.routes.root_path
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
action :update do
|
49
|
+
match :failure, error: Cuprum::Collections::Errors::FailedValidation do
|
50
|
+
render :edit,
|
51
|
+
assigns: result.value.merge(errors: result.error.errors),
|
52
|
+
status: 422 # rubocop:disable Rails/HttpStatus
|
53
|
+
end
|
54
|
+
|
55
|
+
match :success do
|
56
|
+
entity = result.value[resource.singular_resource_name]
|
57
|
+
|
58
|
+
redirect_to resource.routes.show_path(entity)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/failed_validation'
|
4
|
+
|
5
|
+
require 'cuprum/rails/responders/html'
|
6
|
+
require 'cuprum/rails/responders/html_responder'
|
7
|
+
|
8
|
+
module Cuprum::Rails::Responders::Html
|
9
|
+
# Defines default responses for a singular RESTful resource.
|
10
|
+
#
|
11
|
+
# - #create failure: renders the :new template.
|
12
|
+
# - #create success: redirects to the resource #show page.
|
13
|
+
# - #destroy success: redirects to the parent resource.
|
14
|
+
# - #update failure: renders the :edit template.
|
15
|
+
# - #update success: redirects to the resource #show page.
|
16
|
+
#
|
17
|
+
# Responds to any other successful result by rendering the template for the
|
18
|
+
# action name and passing the result value as assigned variables. For a
|
19
|
+
# failing result, redirects to the parent resource.
|
20
|
+
class SingularResource < Cuprum::Rails::Responders::HtmlResponder
|
21
|
+
action :create do
|
22
|
+
match :failure, error: Cuprum::Collections::Errors::FailedValidation do
|
23
|
+
render :new,
|
24
|
+
assigns: result.value.merge(errors: result.error.errors),
|
25
|
+
status: 422 # rubocop:disable Rails/HttpStatus
|
26
|
+
end
|
27
|
+
|
28
|
+
match :success do
|
29
|
+
redirect_to resource.routes.show_path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
action :destroy do
|
34
|
+
match :success do
|
35
|
+
next redirect_to(resource.base_path) unless resource.routes
|
36
|
+
|
37
|
+
redirect_to(resource.routes.parent_path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
action :update do
|
42
|
+
match :failure, error: Cuprum::Collections::Errors::FailedValidation do
|
43
|
+
render :edit,
|
44
|
+
assigns: result.value.merge(errors: result.error.errors),
|
45
|
+
status: 422 # rubocop:disable Rails/HttpStatus
|
46
|
+
end
|
47
|
+
|
48
|
+
match :success do
|
49
|
+
redirect_to resource.routes.show_path
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
match :failure do
|
54
|
+
next redirect_to(resource.base_path) unless resource.routes
|
55
|
+
|
56
|
+
redirect_to(resource.routes.parent_path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails/responders'
|
4
|
+
|
5
|
+
module Cuprum::Rails::Responders
|
6
|
+
# Namespace for HTML responders, which process action results into responses.
|
7
|
+
module Html
|
8
|
+
autoload :PluralResource, 'cuprum/rails/responders/html/plural_resource'
|
9
|
+
autoload :SingularResource, 'cuprum/rails/responders/html/singular_resource'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails/responses/html/redirect_response'
|
4
|
+
require 'cuprum/rails/responders'
|
5
|
+
require 'cuprum/rails/responders/actions'
|
6
|
+
require 'cuprum/rails/responders/matching'
|
7
|
+
|
8
|
+
module Cuprum::Rails::Responders
|
9
|
+
# Provides a DSL for defining responses to HTML requests.
|
10
|
+
#
|
11
|
+
# By default, responds to any successful result by rendering the template for
|
12
|
+
# the action name and passing the result value as assigned variables. For a
|
13
|
+
# failing result, redirects to either the show page or the index page for the
|
14
|
+
# resource, based on the resource's defined #routes.
|
15
|
+
#
|
16
|
+
# @example Defining A Response
|
17
|
+
# class CustomResponder < Cuprum::Rails::Responders::HtmlResponder
|
18
|
+
# match :failure, error: Spec::AuthorizationError do
|
19
|
+
# redirect_to('/login')
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Defining Responses For An Action
|
24
|
+
# class ActionsResponder < Cuprum::Rails::Responders::HtmlResponder
|
25
|
+
# action :publish do
|
26
|
+
# match :failure do
|
27
|
+
# redirect_to(resource.routes.index_path)
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# match :success do
|
31
|
+
# redirect_to(resource.routes.show_path(@result.value))
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# @see Cuprum::Rails::Responders::Actions::ClassMethods#action
|
37
|
+
# @see Cuprum::Rails::Responders::Matching::ClassMethods#match
|
38
|
+
class HtmlResponder
|
39
|
+
include Cuprum::Rails::Responders::Matching
|
40
|
+
include Cuprum::Rails::Responders::Actions
|
41
|
+
|
42
|
+
match :success do
|
43
|
+
render action_name
|
44
|
+
end
|
45
|
+
|
46
|
+
match :failure do
|
47
|
+
next redirect_to(resource.base_path) unless resource.routes
|
48
|
+
|
49
|
+
path = resource_path(result) || resource.routes.index_path
|
50
|
+
|
51
|
+
redirect_to(path)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!method call(result)
|
55
|
+
# (see Cuprum::Rails::Responders::Actions#call)
|
56
|
+
|
57
|
+
# @return [Symbol] the format of the responder.
|
58
|
+
def format
|
59
|
+
:html
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates a RedirectResponse based on the given path and HTTP status.
|
63
|
+
#
|
64
|
+
# @param path [String] The path or url to redirect to.
|
65
|
+
# @param status [Integer] The HTTP status of the response.
|
66
|
+
#
|
67
|
+
# @return [Cuprum::Rails::Responses::Html::RedirectResponse] the response.
|
68
|
+
def redirect_to(path, status: 302)
|
69
|
+
Cuprum::Rails::Responses::Html::RedirectResponse.new(path, status: status)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates a RenderResponse based on the given template and parameters.
|
73
|
+
#
|
74
|
+
# @param assigns [Hash] Variables to assign when rendering the template.
|
75
|
+
# @param layout [String] The layout to render.
|
76
|
+
# @param status [Integer] The HTTP status of the response.
|
77
|
+
# @param template [String, Symbol] The template to render.
|
78
|
+
#
|
79
|
+
# @return [Cuprum::Rails::Responses::Html::RenderResponse] the response.
|
80
|
+
def render(template, assigns: nil, layout: nil, status: 200)
|
81
|
+
Cuprum::Rails::Responses::Html::RenderResponse.new(
|
82
|
+
template,
|
83
|
+
assigns: assigns || default_assigns,
|
84
|
+
layout: layout,
|
85
|
+
status: status
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def default_assigns
|
92
|
+
return nil if result.nil?
|
93
|
+
|
94
|
+
assigns = default_value
|
95
|
+
|
96
|
+
assigns[:error] = result.error unless result.error.nil?
|
97
|
+
|
98
|
+
assigns
|
99
|
+
end
|
100
|
+
|
101
|
+
def default_value
|
102
|
+
if result.value.is_a?(Hash)
|
103
|
+
result.value
|
104
|
+
elsif !result.value.nil?
|
105
|
+
{ value: result.value }
|
106
|
+
else
|
107
|
+
{}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def resource_entity
|
112
|
+
if result.value.is_a?(Hash)
|
113
|
+
result.value[resource.singular_resource_name]
|
114
|
+
else
|
115
|
+
result.value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def resource_path(result)
|
120
|
+
return resource.routes.index_path if result.value.nil?
|
121
|
+
|
122
|
+
entity = resource_entity
|
123
|
+
|
124
|
+
return resource.routes.show_path(entity) if entity
|
125
|
+
|
126
|
+
resource.routes.index_path
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/already_exists'
|
4
|
+
require 'cuprum/collections/errors/extra_attributes'
|
5
|
+
require 'cuprum/collections/errors/failed_validation'
|
6
|
+
require 'cuprum/collections/errors/not_found'
|
7
|
+
|
8
|
+
require 'cuprum/rails/errors/missing_parameters'
|
9
|
+
require 'cuprum/rails/responders/json'
|
10
|
+
require 'cuprum/rails/responders/json_responder'
|
11
|
+
|
12
|
+
module Cuprum::Rails::Responders::Json
|
13
|
+
# Defines default responses for a RESTful resource.
|
14
|
+
#
|
15
|
+
# - #create success: serializes the data with status 201 Created.
|
16
|
+
# - failure AlreadyExists: serializes the error with status 422 Unprocessable
|
17
|
+
# Entity.
|
18
|
+
# - failure ExtraAttributes: serializes the error with status 422
|
19
|
+
# Unprocessable Entity.
|
20
|
+
# - failure FailedValidation: serializes the error with status 422
|
21
|
+
# Unprocessable Entity.
|
22
|
+
# - failure MissingParameters: serializes the error with status 400 Bad
|
23
|
+
# Request.
|
24
|
+
# - failure NotFound: serializes the error with status 404 Not Found.
|
25
|
+
#
|
26
|
+
# Responds to any other successful result by serializing the result value with
|
27
|
+
# status 200. For a failing result, serializes a generic error with status
|
28
|
+
# 500 Internal Server Error.
|
29
|
+
class Resource < Cuprum::Rails::Responders::JsonResponder
|
30
|
+
action :create do
|
31
|
+
match :success do |result|
|
32
|
+
render_success(result.value, status: 201)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
match :failure, error: Cuprum::Collections::Errors::AlreadyExists \
|
37
|
+
do |result|
|
38
|
+
render_failure(result.error, status: 422)
|
39
|
+
end
|
40
|
+
|
41
|
+
match :failure, error: Cuprum::Collections::Errors::ExtraAttributes \
|
42
|
+
do |result|
|
43
|
+
render_failure(result.error, status: 422)
|
44
|
+
end
|
45
|
+
|
46
|
+
match :failure, error: Cuprum::Collections::Errors::FailedValidation \
|
47
|
+
do |result|
|
48
|
+
render_failure(result.error, status: 422)
|
49
|
+
end
|
50
|
+
|
51
|
+
match :failure, error: Cuprum::Collections::Errors::NotFound \
|
52
|
+
do |result|
|
53
|
+
render_failure(result.error, status: 404)
|
54
|
+
end
|
55
|
+
|
56
|
+
match :failure, error: Cuprum::Rails::Errors::MissingParameters do |result|
|
57
|
+
render_failure(result.error, status: 400)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails/responders'
|
4
|
+
|
5
|
+
module Cuprum::Rails::Responders
|
6
|
+
# Namespace for JSON responders, which process action results into responses.
|
7
|
+
module Json
|
8
|
+
autoload :Resource, 'cuprum/rails/responders/json/resource'
|
9
|
+
end
|
10
|
+
end
|