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