cuprum-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +98 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/DEVELOPMENT.md +28 -0
  5. data/LICENSE +22 -0
  6. data/README.md +1045 -0
  7. data/lib/cuprum/rails/action.rb +45 -0
  8. data/lib/cuprum/rails/actions/create.rb +49 -0
  9. data/lib/cuprum/rails/actions/destroy.rb +22 -0
  10. data/lib/cuprum/rails/actions/edit.rb +22 -0
  11. data/lib/cuprum/rails/actions/index.rb +55 -0
  12. data/lib/cuprum/rails/actions/new.rb +19 -0
  13. data/lib/cuprum/rails/actions/resource_action.rb +75 -0
  14. data/lib/cuprum/rails/actions/show.rb +22 -0
  15. data/lib/cuprum/rails/actions/update.rb +59 -0
  16. data/lib/cuprum/rails/actions.rb +16 -0
  17. data/lib/cuprum/rails/collection.rb +115 -0
  18. data/lib/cuprum/rails/command.rb +137 -0
  19. data/lib/cuprum/rails/commands/assign_one.rb +66 -0
  20. data/lib/cuprum/rails/commands/build_one.rb +55 -0
  21. data/lib/cuprum/rails/commands/destroy_one.rb +43 -0
  22. data/lib/cuprum/rails/commands/find_many.rb +60 -0
  23. data/lib/cuprum/rails/commands/find_matching.rb +121 -0
  24. data/lib/cuprum/rails/commands/find_one.rb +50 -0
  25. data/lib/cuprum/rails/commands/insert_one.rb +41 -0
  26. data/lib/cuprum/rails/commands/update_one.rb +49 -0
  27. data/lib/cuprum/rails/commands/validate_one.rb +68 -0
  28. data/lib/cuprum/rails/commands.rb +18 -0
  29. data/lib/cuprum/rails/controller.rb +50 -0
  30. data/lib/cuprum/rails/controller_action.rb +121 -0
  31. data/lib/cuprum/rails/controllers/class_methods/actions.rb +57 -0
  32. data/lib/cuprum/rails/controllers/class_methods/configuration.rb +64 -0
  33. data/lib/cuprum/rails/controllers/class_methods/validations.rb +30 -0
  34. data/lib/cuprum/rails/controllers/class_methods.rb +15 -0
  35. data/lib/cuprum/rails/controllers/configuration.rb +53 -0
  36. data/lib/cuprum/rails/controllers.rb +10 -0
  37. data/lib/cuprum/rails/errors/missing_parameters.rb +33 -0
  38. data/lib/cuprum/rails/errors/missing_primary_key.rb +46 -0
  39. data/lib/cuprum/rails/errors/undefined_permitted_attributes.rb +34 -0
  40. data/lib/cuprum/rails/errors.rb +8 -0
  41. data/lib/cuprum/rails/map_errors.rb +44 -0
  42. data/lib/cuprum/rails/query.rb +77 -0
  43. data/lib/cuprum/rails/query_builder.rb +78 -0
  44. data/lib/cuprum/rails/repository.rb +44 -0
  45. data/lib/cuprum/rails/request.rb +105 -0
  46. data/lib/cuprum/rails/resource.rb +145 -0
  47. data/lib/cuprum/rails/responders/actions.rb +73 -0
  48. data/lib/cuprum/rails/responders/html/plural_resource.rb +62 -0
  49. data/lib/cuprum/rails/responders/html/singular_resource.rb +59 -0
  50. data/lib/cuprum/rails/responders/html.rb +11 -0
  51. data/lib/cuprum/rails/responders/html_responder.rb +129 -0
  52. data/lib/cuprum/rails/responders/json/resource.rb +60 -0
  53. data/lib/cuprum/rails/responders/json.rb +10 -0
  54. data/lib/cuprum/rails/responders/json_responder.rb +122 -0
  55. data/lib/cuprum/rails/responders/matching.rb +145 -0
  56. data/lib/cuprum/rails/responders/serialization.rb +36 -0
  57. data/lib/cuprum/rails/responders.rb +15 -0
  58. data/lib/cuprum/rails/responses/html/redirect_response.rb +29 -0
  59. data/lib/cuprum/rails/responses/html/render_response.rb +52 -0
  60. data/lib/cuprum/rails/responses/html.rb +11 -0
  61. data/lib/cuprum/rails/responses/json_response.rb +29 -0
  62. data/lib/cuprum/rails/responses.rb +11 -0
  63. data/lib/cuprum/rails/routes.rb +166 -0
  64. data/lib/cuprum/rails/routing/plural_routes.rb +26 -0
  65. data/lib/cuprum/rails/routing/singular_routes.rb +24 -0
  66. data/lib/cuprum/rails/routing.rb +11 -0
  67. data/lib/cuprum/rails/rspec/command_contract.rb +460 -0
  68. data/lib/cuprum/rails/rspec/define_route_contract.rb +84 -0
  69. data/lib/cuprum/rails/rspec.rb +8 -0
  70. data/lib/cuprum/rails/serializers/json/active_record_serializer.rb +24 -0
  71. data/lib/cuprum/rails/serializers/json/array_serializer.rb +40 -0
  72. data/lib/cuprum/rails/serializers/json/attributes_serializer.rb +217 -0
  73. data/lib/cuprum/rails/serializers/json/error_serializer.rb +24 -0
  74. data/lib/cuprum/rails/serializers/json/hash_serializer.rb +44 -0
  75. data/lib/cuprum/rails/serializers/json/identity_serializer.rb +21 -0
  76. data/lib/cuprum/rails/serializers/json/serializer.rb +66 -0
  77. data/lib/cuprum/rails/serializers/json.rb +40 -0
  78. data/lib/cuprum/rails/serializers.rb +10 -0
  79. data/lib/cuprum/rails/version.rb +59 -0
  80. data/lib/cuprum/rails.rb +31 -0
  81. 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