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