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,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/types/hash_with_indifferent_keys'
|
4
|
+
|
5
|
+
require 'cuprum/collections/errors/extra_attributes'
|
6
|
+
|
7
|
+
require 'cuprum/rails/command'
|
8
|
+
require 'cuprum/rails/commands'
|
9
|
+
|
10
|
+
module Cuprum::Rails::Commands
|
11
|
+
# Command for assigning attributes to an ActiveRecord model.
|
12
|
+
class AssignOne < Cuprum::Rails::Command
|
13
|
+
# @!method call(attributes:, entity:)
|
14
|
+
# Assigns the given attributes to the record.
|
15
|
+
#
|
16
|
+
# Any attributes on the record that are not part of the given attributes
|
17
|
+
# hash are unchanged.
|
18
|
+
#
|
19
|
+
# @param attributes [Hash] The attributes and values to update.
|
20
|
+
# @param entity [ActiveRecord::Base] The record to update.
|
21
|
+
#
|
22
|
+
# @return [ActiveRecord::Base] a copy of the record, merged with the given
|
23
|
+
# attributes.
|
24
|
+
#
|
25
|
+
# @example Assigning attributes
|
26
|
+
# entity = Book.new(
|
27
|
+
# 'title' => 'The Hobbit',
|
28
|
+
# 'author' => 'J.R.R. Tolkien',
|
29
|
+
# 'series' => nil,
|
30
|
+
# 'category' => 'Science Fiction and Fantasy'
|
31
|
+
# )
|
32
|
+
# attributes = { title: 'The Silmarillion' }
|
33
|
+
# command = Assign.new(record_class: Book)
|
34
|
+
# result = command.call(attributes: attributes, entity: entity)
|
35
|
+
# result.value.attributes
|
36
|
+
# #=> {
|
37
|
+
# 'id' => nil,
|
38
|
+
# 'title' => 'The Silmarillion',
|
39
|
+
# 'author' => 'J.R.R. Tolkien',
|
40
|
+
# 'series' => nil,
|
41
|
+
# 'category' => 'Science Fiction and Fantasy'
|
42
|
+
# }
|
43
|
+
validate_parameters :call do
|
44
|
+
keyword :attributes,
|
45
|
+
Stannum::Constraints::Types::HashWithIndifferentKeys.new
|
46
|
+
keyword :entity, Object
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def process(attributes:, entity:)
|
52
|
+
step { validate_entity(entity) }
|
53
|
+
|
54
|
+
entity.assign_attributes(attributes)
|
55
|
+
|
56
|
+
entity
|
57
|
+
rescue ActiveModel::UnknownAttributeError => exception
|
58
|
+
error = Cuprum::Collections::Errors::ExtraAttributes.new(
|
59
|
+
entity_class: record_class,
|
60
|
+
extra_attributes: [exception.attribute],
|
61
|
+
valid_attributes: record_class.attribute_names
|
62
|
+
)
|
63
|
+
failure(error)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/types/hash_with_indifferent_keys'
|
4
|
+
|
5
|
+
require 'cuprum/collections/errors/extra_attributes'
|
6
|
+
|
7
|
+
require 'cuprum/rails/command'
|
8
|
+
require 'cuprum/rails/commands'
|
9
|
+
|
10
|
+
module Cuprum::Rails::Commands
|
11
|
+
# Command for generating an ActiveRecord model from an attributes hash.
|
12
|
+
class BuildOne < Cuprum::Rails::Command
|
13
|
+
# @!method call(attributes:)
|
14
|
+
# Builds a new record with the given attributes.
|
15
|
+
#
|
16
|
+
# @param attributes [Hash] The attributes and values to assign.
|
17
|
+
#
|
18
|
+
# @return [ActiveRecord::Base] the newly built record.
|
19
|
+
#
|
20
|
+
# @example Building a record
|
21
|
+
# attributes = {
|
22
|
+
# 'title' => 'The Hobbit',
|
23
|
+
# 'author' => 'J.R.R. Tolkien',
|
24
|
+
# 'series' => nil,
|
25
|
+
# 'category' => 'Science Fiction and Fantasy'
|
26
|
+
# }
|
27
|
+
# command = Build.new(record_class: Book)
|
28
|
+
# result = command.call(attributes: attributes)
|
29
|
+
# result.value.attributes
|
30
|
+
# #=> {
|
31
|
+
# 'id' => nil,
|
32
|
+
# 'title' => 'The Silmarillion',
|
33
|
+
# 'author' => 'J.R.R. Tolkien',
|
34
|
+
# 'series' => nil,
|
35
|
+
# 'category' => 'Science Fiction and Fantasy'
|
36
|
+
# }
|
37
|
+
validate_parameters :call do
|
38
|
+
keyword :attributes,
|
39
|
+
Stannum::Constraints::Types::HashWithIndifferentKeys.new
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def process(attributes:)
|
45
|
+
record_class.new(attributes)
|
46
|
+
rescue ActiveModel::UnknownAttributeError => exception
|
47
|
+
error = Cuprum::Collections::Errors::ExtraAttributes.new(
|
48
|
+
entity_class: record_class,
|
49
|
+
extra_attributes: [exception.attribute],
|
50
|
+
valid_attributes: record_class.attribute_names
|
51
|
+
)
|
52
|
+
failure(error)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/not_found'
|
4
|
+
|
5
|
+
require 'cuprum/rails/command'
|
6
|
+
require 'cuprum/rails/commands'
|
7
|
+
|
8
|
+
module Cuprum::Rails::Commands
|
9
|
+
# Command for destroying an ActiveRecord record by primary key.
|
10
|
+
class DestroyOne < Cuprum::Rails::Command
|
11
|
+
# @!method call(primary_key:)
|
12
|
+
# Finds and destroys the record with the given primary key.
|
13
|
+
#
|
14
|
+
# The command will find the record with the given primary key and remove
|
15
|
+
# it from the collection. If the record is not found, the command will
|
16
|
+
# fail and return a NotFound error.
|
17
|
+
#
|
18
|
+
# @param primary_key [Object] The primary key of the requested record.
|
19
|
+
#
|
20
|
+
# @return [Cuprum::Result<Hash{String, Object}>] a result with the
|
21
|
+
# destroyed record.
|
22
|
+
validate_parameters :call do
|
23
|
+
keyword :primary_key, Object
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def process(primary_key:)
|
29
|
+
step { validate_primary_key(primary_key) }
|
30
|
+
|
31
|
+
entity = record_class.find(primary_key)
|
32
|
+
|
33
|
+
entity.destroy
|
34
|
+
rescue ActiveRecord::RecordNotFound
|
35
|
+
error = Cuprum::Collections::Errors::NotFound.new(
|
36
|
+
collection_name: collection_name,
|
37
|
+
primary_key_name: primary_key_name,
|
38
|
+
primary_key_values: [primary_key]
|
39
|
+
)
|
40
|
+
Cuprum::Result.new(error: error)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/boolean'
|
4
|
+
|
5
|
+
require 'cuprum/collections/commands/abstract_find_many'
|
6
|
+
|
7
|
+
require 'cuprum/rails/command'
|
8
|
+
require 'cuprum/rails/commands'
|
9
|
+
|
10
|
+
module Cuprum::Rails::Commands
|
11
|
+
# Command for finding multiple ActiveRecord records by primary key.
|
12
|
+
class FindMany < Cuprum::Rails::Command
|
13
|
+
include Cuprum::Collections::Commands::AbstractFindMany
|
14
|
+
|
15
|
+
# @!method call(primary_keys:, allow_partial: false, envelope: false, scope: nil) # rubocop:disable Layout/LineLength
|
16
|
+
# Queries the collection for the records with the given primary keys.
|
17
|
+
#
|
18
|
+
# The command will find and return the entities with the given primary
|
19
|
+
# keys. If any of the records are not found, the command will fail and
|
20
|
+
# return a NotFound error. If the :allow_partial option is set, the
|
21
|
+
# command will return a partial result unless none of the requested
|
22
|
+
# records are found.
|
23
|
+
#
|
24
|
+
# When the :envelope option is true, the command wraps the records in a
|
25
|
+
# Hash, using the name of the collection as the key.
|
26
|
+
#
|
27
|
+
# @param allow_partial [Boolean] If true, passes if any of the records are
|
28
|
+
# found.
|
29
|
+
# @param envelope [Boolean] If true, wraps the result value in a Hash.
|
30
|
+
# @param primary_keys [Array] The primary keys of the requested records.
|
31
|
+
# @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
|
32
|
+
# the query. Records must match the scope as well as the primary keys.
|
33
|
+
#
|
34
|
+
# @return [Cuprum::Result<Array<ActiveRecord>>] a result with the
|
35
|
+
# requested records.
|
36
|
+
validate_parameters :call do
|
37
|
+
keyword :allow_partial, Stannum::Constraints::Boolean.new, default: true
|
38
|
+
keyword :envelope, Stannum::Constraints::Boolean.new, default: true
|
39
|
+
keyword :primary_keys, Array
|
40
|
+
keyword :scope, Cuprum::Rails::Query, optional: true
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def build_query
|
46
|
+
Cuprum::Rails::Query.new(record_class)
|
47
|
+
end
|
48
|
+
|
49
|
+
def process(
|
50
|
+
primary_keys:,
|
51
|
+
allow_partial: false,
|
52
|
+
envelope: false,
|
53
|
+
scope: nil
|
54
|
+
)
|
55
|
+
step { validate_primary_keys(primary_keys) }
|
56
|
+
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/commands/abstract_find_matching'
|
4
|
+
require 'cuprum/collections/constraints/ordering'
|
5
|
+
|
6
|
+
require 'cuprum/rails/command'
|
7
|
+
require 'cuprum/rails/commands'
|
8
|
+
require 'cuprum/rails/query'
|
9
|
+
|
10
|
+
module Cuprum::Rails::Commands
|
11
|
+
# Command for querying filtered, ordered data from a Rails collection.
|
12
|
+
class FindMatching < Cuprum::Rails::Command
|
13
|
+
include Cuprum::Collections::Commands::AbstractFindMatching
|
14
|
+
|
15
|
+
# @!method call(envelope: false, limit: nil, offset: nil, order: nil, scope: nil, where: nil, &block) # rubocop:disable Layout/LineLength
|
16
|
+
# Queries the collection for records matching the given conditions.
|
17
|
+
#
|
18
|
+
# @param envelope [Boolean] If true, wraps the result value in a Hash.
|
19
|
+
# @param limit [Integer] The maximum number of results to return.
|
20
|
+
# @param offset [Integer] The initial ordered items to skip.
|
21
|
+
# @param order [Array<String, Symbol>, Hash<{String, Symbol => Symbol}>]
|
22
|
+
# The sort order of the returned items. Should be either an array of
|
23
|
+
# attribute names or a hash of attribute names and directions.
|
24
|
+
# @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
|
25
|
+
# the query. Records must match the scope as well as the :where filters.
|
26
|
+
# @param where [Object] Additional filters for selecting data. The command
|
27
|
+
# will only return data matching these filters.
|
28
|
+
# @yield The given block is passed to a QueryBuilder, which converts the
|
29
|
+
# block to query criteria and generates a new query using those
|
30
|
+
# criteria.
|
31
|
+
# @yieldreturn [Hash] The filters to apply to the query. The hash keys
|
32
|
+
# should be the names of attributes or columns, and the corresponding
|
33
|
+
# values should be either the literal value for that attribute or a
|
34
|
+
# method call for a valid operation defined for the query.
|
35
|
+
#
|
36
|
+
# @example Querying all records in the collection.
|
37
|
+
# command = FindMatching.new(collection_name: 'books', data: books)
|
38
|
+
# command.call
|
39
|
+
# #=> an enumerable iterating all records in the collection
|
40
|
+
#
|
41
|
+
# @example Querying all records matching some critera:
|
42
|
+
# command.call { { author: 'Nnedi Okorafor' } }
|
43
|
+
# #=> an enumerable iterating all records in the collection whose author
|
44
|
+
# is 'Nnedi Okorafor'
|
45
|
+
#
|
46
|
+
# @example Ordering query results
|
47
|
+
# command.call(order: :title) { { author: 'Nnedi Okorafor' } }
|
48
|
+
# #=> an enumerable iterating all records in the collection whose author
|
49
|
+
# # is 'Nnedi Okorafor', sorted by :title in ascending order
|
50
|
+
#
|
51
|
+
# @example Advanced filtering
|
52
|
+
# command.call do
|
53
|
+
# {
|
54
|
+
# category: eq('Science Fiction and Fantasy'),
|
55
|
+
# author: ne('J.R.R. Tolkien')
|
56
|
+
# }
|
57
|
+
# end
|
58
|
+
# #=> an enumerable iterating all records in the collection whose
|
59
|
+
# # category is 'Science Fiction and Fantasy', and whose author is not
|
60
|
+
# # 'J.R.R. Tolkien'.
|
61
|
+
#
|
62
|
+
# @example Advanced ordering
|
63
|
+
# order = { author: :asc, genre: :desc }
|
64
|
+
# command.call(order: order) { { author: 'Nnedi Okorafor' } }
|
65
|
+
# #=> an enumerable iterating all records in the collection whose author
|
66
|
+
# # is 'Nnedi Okorafor', sorted first by :author in ascending order
|
67
|
+
# # and within the same author by genre in descending order
|
68
|
+
#
|
69
|
+
# @example Filtering, ordering, and subsets
|
70
|
+
# command.call(offset: 50, limit: 10, order: :author) do
|
71
|
+
# { category: 'Science Fiction and Fantasy' }
|
72
|
+
# end
|
73
|
+
# #=> an enumerable iterating the 51st through 60th records in the
|
74
|
+
# # collection whose category is 'Science Fiction and Fantasy', sorted
|
75
|
+
# # by :author in ascending order.
|
76
|
+
#
|
77
|
+
# @example Wrapping the result in an envelope
|
78
|
+
# command =
|
79
|
+
# Filter.new(collection_name: 'books', data: books)
|
80
|
+
# command.call(envelope: true)
|
81
|
+
# #=> {
|
82
|
+
# 'books' => [] # an array containing the matching records
|
83
|
+
# }
|
84
|
+
#
|
85
|
+
# @overload call(limit: nil, offset: nil, order: nil, &block)
|
86
|
+
# When the :envelope option is false (default), the command returns an
|
87
|
+
# Enumerator which can be iterated to return the matching records.
|
88
|
+
#
|
89
|
+
# @return [Cuprum::Result<Enumerator>] the matching records in the
|
90
|
+
# specified order as an Enumerator.
|
91
|
+
#
|
92
|
+
# @overload call(limit: nil, offset: nil, order: nil, &block)
|
93
|
+
# When the :envelope option is true, the command immediately evaluates
|
94
|
+
# the query and wraps the resulting array in a Hash, using the name of
|
95
|
+
# the collection as the key.
|
96
|
+
#
|
97
|
+
# @return [Cuprum::Result<Hash{String, Array<ActiveRecord::Base>}>] a
|
98
|
+
# hash with the collection name as key and the matching records as
|
99
|
+
# value.
|
100
|
+
validate_parameters :call do
|
101
|
+
keyword :envelope,
|
102
|
+
Stannum::Constraints::Boolean.new,
|
103
|
+
default: true
|
104
|
+
keyword :limit, Integer, optional: true
|
105
|
+
keyword :offset, Integer, optional: true
|
106
|
+
keyword :order,
|
107
|
+
Cuprum::Collections::Constraints::Ordering.new,
|
108
|
+
optional: true
|
109
|
+
keyword :scope,
|
110
|
+
Cuprum::Rails::Query,
|
111
|
+
optional: true
|
112
|
+
keyword :where, Object, optional: true
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def build_query
|
118
|
+
Cuprum::Rails::Query.new(record_class)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/boolean'
|
4
|
+
|
5
|
+
require 'cuprum/collections/commands/abstract_find_one'
|
6
|
+
|
7
|
+
require 'cuprum/rails/command'
|
8
|
+
require 'cuprum/rails/commands'
|
9
|
+
|
10
|
+
module Cuprum::Rails::Commands
|
11
|
+
# Command for finding one ActiveRecord record by primary key.
|
12
|
+
class FindOne < Cuprum::Rails::Command
|
13
|
+
include Cuprum::Collections::Commands::AbstractFindOne
|
14
|
+
|
15
|
+
# @!method call(primary_key:, envelope: false, scope: nil)
|
16
|
+
# Queries the collection for the record with the given primary key.
|
17
|
+
#
|
18
|
+
# The command will find and return the entity with the given primary key.
|
19
|
+
# If the entity is not found, the command will fail and return a NotFound
|
20
|
+
# error.
|
21
|
+
#
|
22
|
+
# When the :envelope option is true, the command wraps the record in a
|
23
|
+
# Hash, using the singular name of the collection as the key.
|
24
|
+
#
|
25
|
+
# @param envelope [Boolean] If true, wraps the result value in a Hash.
|
26
|
+
# @param primary_key [Object] The primary key of the requested record.
|
27
|
+
# @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
|
28
|
+
# the query. The record must match the scope as well as the primary key.
|
29
|
+
#
|
30
|
+
# @return [Cuprum::Result<Hash{String, Object}>] a result with the
|
31
|
+
# requested record.
|
32
|
+
validate_parameters :call do
|
33
|
+
keyword :envelope, Stannum::Constraints::Boolean.new, default: true
|
34
|
+
keyword :primary_key, Object
|
35
|
+
keyword :scope, Cuprum::Rails::Query, optional: true
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_query
|
41
|
+
Cuprum::Rails::Query.new(record_class)
|
42
|
+
end
|
43
|
+
|
44
|
+
def process(primary_key:, envelope: false, scope: nil)
|
45
|
+
step { validate_primary_key(primary_key) }
|
46
|
+
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/already_exists'
|
4
|
+
|
5
|
+
require 'cuprum/rails/command'
|
6
|
+
require 'cuprum/rails/commands'
|
7
|
+
|
8
|
+
module Cuprum::Rails::Commands
|
9
|
+
# Command for inserting an ActiveRecord record into the collection.
|
10
|
+
class InsertOne < Cuprum::Rails::Command
|
11
|
+
# @!method call(entity:)
|
12
|
+
# Inserts the record into the collection.
|
13
|
+
#
|
14
|
+
# If the collection already includes a record with the same primary key,
|
15
|
+
# #call will fail and the collection will not be updated.
|
16
|
+
#
|
17
|
+
# @param entity [ActiveRecord::Base] The record to persist.
|
18
|
+
#
|
19
|
+
# @return [Cuprum::Result<ActiveRecord::Base>] the persisted record.
|
20
|
+
validate_parameters :call do
|
21
|
+
keyword :entity, Object
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def process(entity:)
|
27
|
+
step { validate_entity(entity) }
|
28
|
+
|
29
|
+
entity.save
|
30
|
+
|
31
|
+
entity
|
32
|
+
rescue ActiveRecord::RecordNotUnique
|
33
|
+
error = Cuprum::Collections::Errors::AlreadyExists.new(
|
34
|
+
collection_name: collection_name,
|
35
|
+
primary_key_name: primary_key_name,
|
36
|
+
primary_key_values: entity[primary_key_name]
|
37
|
+
)
|
38
|
+
failure(error)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/not_found'
|
4
|
+
|
5
|
+
require 'cuprum/rails/command'
|
6
|
+
require 'cuprum/rails/commands'
|
7
|
+
|
8
|
+
module Cuprum::Rails::Commands
|
9
|
+
# Command for updating an ActiveRecord record in the collection.
|
10
|
+
class UpdateOne < Cuprum::Rails::Command
|
11
|
+
# @!method call(entity:)
|
12
|
+
# Updates the record in the collection.
|
13
|
+
#
|
14
|
+
# If the collection does not already have a record with the same primary
|
15
|
+
# key, #call will fail and the collection will not be updated.
|
16
|
+
#
|
17
|
+
# @param entity [ActiveRecord::Base] The collection record to persist.
|
18
|
+
#
|
19
|
+
# @return [Cuprum::Result<ActiveRecord::Base>] the persisted record.
|
20
|
+
validate_parameters :call do
|
21
|
+
keyword :entity, Object
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def handle_missing_record(primary_key:)
|
27
|
+
query = record_class.where(primary_key_name => primary_key)
|
28
|
+
|
29
|
+
return if query.exists?
|
30
|
+
|
31
|
+
error = Cuprum::Collections::Errors::NotFound.new(
|
32
|
+
collection_name: collection_name,
|
33
|
+
primary_key_name: primary_key_name,
|
34
|
+
primary_key_values: primary_key
|
35
|
+
)
|
36
|
+
failure(error)
|
37
|
+
end
|
38
|
+
|
39
|
+
def process(entity:)
|
40
|
+
step { validate_entity(entity) }
|
41
|
+
|
42
|
+
step { handle_missing_record(primary_key: entity[primary_key_name]) }
|
43
|
+
|
44
|
+
entity.save
|
45
|
+
|
46
|
+
entity
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors/failed_validation'
|
4
|
+
|
5
|
+
require 'cuprum/rails/command'
|
6
|
+
require 'cuprum/rails/commands'
|
7
|
+
require 'cuprum/rails/map_errors'
|
8
|
+
|
9
|
+
module Cuprum::Rails::Commands
|
10
|
+
# Command for validating an ActiveRecord record.
|
11
|
+
class ValidateOne < Cuprum::Rails::Command
|
12
|
+
# @!method call(entity:, contract: nil)
|
13
|
+
# Validates the record against the given or default contract.
|
14
|
+
#
|
15
|
+
# If the record matches the contract, #call will return a passing result
|
16
|
+
# with the record as the result value. If the record does not match the
|
17
|
+
# contract, #call will return a failing result with a FailedValidation
|
18
|
+
# error and the validation errors.
|
19
|
+
#
|
20
|
+
# @param contract [Stannum::Constraints:Base] The contract with which to
|
21
|
+
# validate the record. If not given, the record will be validated using
|
22
|
+
# the collection's default contract.
|
23
|
+
# @param entity [ActiveRecord::Base] The collection record to validate.
|
24
|
+
#
|
25
|
+
# @return [Cuprum::Result<ActiveRecord::Base>] the validated record.
|
26
|
+
validate_parameters :call do
|
27
|
+
keyword :contract,
|
28
|
+
Stannum::Constraints::Base,
|
29
|
+
optional: true
|
30
|
+
keyword :entity, Object
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def map_errors(native_errors:)
|
36
|
+
Cuprum::Rails::MapErrors.instance.call(native_errors: native_errors)
|
37
|
+
end
|
38
|
+
|
39
|
+
def match_default(entity:)
|
40
|
+
return true if entity.valid?
|
41
|
+
|
42
|
+
errors = map_errors(native_errors: entity.errors)
|
43
|
+
|
44
|
+
[false, errors]
|
45
|
+
end
|
46
|
+
|
47
|
+
def process(entity:, contract: nil)
|
48
|
+
step { validate_entity(entity) }
|
49
|
+
|
50
|
+
step { validate_record(contract: contract, entity: entity) }
|
51
|
+
|
52
|
+
entity
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_record(contract:, entity:)
|
56
|
+
valid, errors =
|
57
|
+
contract ? contract.match(entity) : match_default(entity: entity)
|
58
|
+
|
59
|
+
return if valid
|
60
|
+
|
61
|
+
error = Cuprum::Collections::Errors::FailedValidation.new(
|
62
|
+
entity_class: entity.class,
|
63
|
+
errors: errors
|
64
|
+
)
|
65
|
+
failure(error)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails'
|
4
|
+
|
5
|
+
module Cuprum::Rails
|
6
|
+
# Namespace for commands implementing Rails collection functionality.
|
7
|
+
module Commands
|
8
|
+
autoload :AssignOne, 'cuprum/rails/commands/assign_one'
|
9
|
+
autoload :BuildOne, 'cuprum/rails/commands/build_one'
|
10
|
+
autoload :DestroyOne, 'cuprum/rails/commands/destroy_one'
|
11
|
+
autoload :FindMany, 'cuprum/rails/commands/find_many'
|
12
|
+
autoload :FindMatching, 'cuprum/rails/commands/find_matching'
|
13
|
+
autoload :FindOne, 'cuprum/rails/commands/find_one'
|
14
|
+
autoload :InsertOne, 'cuprum/rails/commands/insert_one'
|
15
|
+
autoload :UpdateOne, 'cuprum/rails/commands/update_one'
|
16
|
+
autoload :ValidateOne, 'cuprum/rails/commands/validate_one'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rails'
|
4
|
+
require 'cuprum/rails/controller_action'
|
5
|
+
require 'cuprum/rails/controllers/class_methods/configuration'
|
6
|
+
require 'cuprum/rails/controllers/class_methods/validations'
|
7
|
+
|
8
|
+
module Cuprum::Rails
|
9
|
+
# Provides a DSL for defining actions and responses.
|
10
|
+
#
|
11
|
+
# @example Defining A Controller
|
12
|
+
# class ExampleController < ApplicationController
|
13
|
+
# include Cuprum::Rails::Controller
|
14
|
+
#
|
15
|
+
# responder :html, CustomHtmlResponder
|
16
|
+
#
|
17
|
+
# action :process, ExampleProcessAction
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# @example Defining A RESTful Controller
|
21
|
+
# class BooksController
|
22
|
+
# include Cuprum::Rails::Controller
|
23
|
+
#
|
24
|
+
# responder :html, Cuprum::Rails::Responders::Html::PluralResource
|
25
|
+
#
|
26
|
+
# action :index, Cuprum::Rails::Actions::Index
|
27
|
+
# action :show, Cuprum::Rails::Actions::Show, member: true
|
28
|
+
# action :published, Books::Published
|
29
|
+
# action :publish, Books::Publish, member: true
|
30
|
+
# end
|
31
|
+
module Controller
|
32
|
+
# Exception when the controller does not define a resource.
|
33
|
+
class UndefinedResourceError < StandardError; end
|
34
|
+
|
35
|
+
# Exception when the controller does not have a responder for a format.
|
36
|
+
class UnknownFormatError < StandardError; end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
private
|
40
|
+
|
41
|
+
def included(other)
|
42
|
+
super
|
43
|
+
|
44
|
+
other.extend(Cuprum::Rails::Controllers::ClassMethods::Actions)
|
45
|
+
other.extend(Cuprum::Rails::Controllers::ClassMethods::Configuration)
|
46
|
+
other.extend(Cuprum::Rails::Controllers::ClassMethods::Validations)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|