caprese 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/CHANGELOG.md +8 -0
- data/caprese.gemspec +2 -2
- data/lib/caprese/adapter/json_api/resource_identifier.rb +11 -15
- data/lib/caprese/controller/concerns/callbacks.rb +15 -3
- data/lib/caprese/controller/concerns/persistence.rb +18 -25
- data/lib/caprese/controller/concerns/relationships.rb +22 -5
- data/lib/caprese/controller/concerns/rendering.rb +17 -1
- data/lib/caprese/record/associated_validator.rb +33 -0
- data/lib/caprese/record.rb +1 -0
- data/lib/caprese/serializer/concerns/links.rb +26 -8
- data/lib/caprese/serializer/concerns/lookup.rb +17 -1
- data/lib/caprese/serializer.rb +4 -0
- data/lib/caprese/version.rb +1 -1
- data/lib/caprese.rb +1 -1
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c285f46b32620d9d16fe5b6698098a385f5e163f
|
4
|
+
data.tar.gz: 559a81f42af6e01fac7cecec6b8a9b9c8134139b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 346d727d18ae79c56606c0dc60e5badc09995e0430dff1fff2147a2f85a3cdeeef63a6e1ab0d4f0aea2bcb120ad1470de2d0b04fa60de347e93f11f819b9f53e
|
7
|
+
data.tar.gz: f7fe6949fb0241f3a4fec64ad91b3470a55bfffb3bc4131e0ac4690ec53829be8da23d69f11e0699aeb2f96f5e4943c9e2f18f4e5524b044700b6c876f2f3ddf
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
## 0.3.0
|
2
|
+
|
3
|
+
* Have children controllers inherit callbacks from their parent
|
4
|
+
* Have child records without their own serializers inherit their type names from their parent
|
5
|
+
* Enforce `Caprese::Record` on all records serialized
|
6
|
+
* Allow editing of meta tag document
|
7
|
+
* Modify `validates_associated` to propagate nested association errors to the record itself
|
8
|
+
* Fail with `422 Unprocessable Entity` if any callbacks add an error to a record being persisted or updated
|
data/caprese.gemspec
CHANGED
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.required_ruby_version = '>= 2.1'
|
23
23
|
|
24
|
-
spec.add_dependency 'active_model_serializers', '
|
25
|
-
spec.add_dependency 'kaminari'
|
24
|
+
spec.add_dependency 'active_model_serializers', '0.10.2'
|
25
|
+
spec.add_dependency 'kaminari', '0.17.0'
|
26
26
|
spec.add_dependency 'rails', '>= 4.2.0'
|
27
27
|
|
28
28
|
spec.add_development_dependency 'bundler'
|
@@ -3,20 +3,16 @@ module Caprese
|
|
3
3
|
class JsonApi
|
4
4
|
class ResourceIdentifier
|
5
5
|
def self.type_for(class_name, serializer_type = nil, transform_options = {})
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raw_type = class_name.underscore
|
17
|
-
raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
|
18
|
-
raw_type
|
19
|
-
end
|
6
|
+
inflection =
|
7
|
+
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
8
|
+
:singularize
|
9
|
+
else
|
10
|
+
:pluralize
|
11
|
+
end
|
12
|
+
|
13
|
+
raw_type = serializer_type || class_name.underscore
|
14
|
+
raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
|
15
|
+
|
20
16
|
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
|
21
17
|
end
|
22
18
|
|
@@ -37,7 +33,7 @@ module Caprese
|
|
37
33
|
private
|
38
34
|
|
39
35
|
def type_for(serializer, transform_options)
|
40
|
-
self.class.type_for(serializer.object.class.name, serializer.
|
36
|
+
self.class.type_for(serializer.object.class.name, serializer.json_key, transform_options)
|
41
37
|
end
|
42
38
|
|
43
39
|
def id_for(serializer)
|
@@ -49,10 +49,22 @@ module Caprese
|
|
49
49
|
#
|
50
50
|
# @param [Symbol,Array<Symbol>] callbacks the name(s) of callbacks to add to list of callbacks
|
51
51
|
define_singleton_method method_name do |*callbacks|
|
52
|
-
|
53
|
-
all_callbacks = self.instance_variable_set(instance_variable_name, [])
|
54
|
-
end
|
52
|
+
all_callbacks = self.instance_variable_get(instance_variable_name) || []
|
55
53
|
all_callbacks.push *callbacks
|
54
|
+
self.instance_variable_set(instance_variable_name, all_callbacks)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module ClassMethods
|
60
|
+
# Is called when any controller class inherits from a parent controller, copying to the child controller
|
61
|
+
# all of the callbacks that have been stored in instance variables on the parent
|
62
|
+
#
|
63
|
+
# @param [Class] subclass the child class that is to inherit the callbacks
|
64
|
+
def inherited(subclass)
|
65
|
+
CALLBACKS.each do |method_name|
|
66
|
+
instance_variable_name = "@#{method_name}_callbacks"
|
67
|
+
subclass.instance_variable_set(instance_variable_name, instance_variable_get(instance_variable_name))
|
56
68
|
end
|
57
69
|
end
|
58
70
|
end
|
@@ -35,28 +35,19 @@ module Caprese
|
|
35
35
|
#
|
36
36
|
# @note For this action to succeed, the given controller must define `create_params`
|
37
37
|
# @see #create_params
|
38
|
-
#
|
39
|
-
# 1. Check that type of record to be created matches type that the given controller manages
|
40
|
-
# 2. Build the appropriate attributes/associations for the create action
|
41
|
-
# 3. Build a record with the attributes
|
42
|
-
# 4. Execute after_initialize callbacks
|
43
|
-
# 5. Execute before_create callbacks
|
44
|
-
# 6. Execute before_save callbacks
|
45
|
-
# 7. Create the record by saving it (or fail with RecordInvalid and render errors)
|
46
|
-
# 8. Execute after_create callbacks
|
47
|
-
# 9. Execute after_save callbacks
|
48
|
-
# 10. Return the created resource with 204 Created
|
49
|
-
# @see #rescue_from ActiveRecord::RecordInvalid
|
50
38
|
def create
|
51
39
|
fail_on_type_mismatch(data_params[:type])
|
52
40
|
|
53
41
|
record = queried_record_scope.build
|
54
42
|
assign_record_attributes(record, permitted_params_for(:create), data_params)
|
43
|
+
|
55
44
|
execute_after_initialize_callbacks(record)
|
56
45
|
|
57
46
|
execute_before_create_callbacks(record)
|
58
47
|
execute_before_save_callbacks(record)
|
59
48
|
|
49
|
+
fail RecordInvalidError.new(record) if record.errors.any?
|
50
|
+
|
60
51
|
record.save!
|
61
52
|
|
62
53
|
execute_after_create_callbacks(record)
|
@@ -74,22 +65,16 @@ module Caprese
|
|
74
65
|
#
|
75
66
|
# @note For this action to succeed, the given controller must define `update_params`
|
76
67
|
# @see #update_params
|
77
|
-
#
|
78
|
-
# 1. Check that type of record to be updated matches type that the given controller manages
|
79
|
-
# 2. Execute before_update callbacks
|
80
|
-
# 3. Execute before_save callbacks
|
81
|
-
# 4. Update the record (or fail with RecordInvalid and render errors)
|
82
|
-
# 5. Execute after_update callbacks
|
83
|
-
# 6. Execute after_save callbacks
|
84
|
-
# 7. Return the updated resource
|
85
|
-
# @see #rescue_from ActiveRecord::RecordInvalid
|
86
68
|
def update
|
87
69
|
fail_on_type_mismatch(data_params[:type])
|
88
70
|
|
71
|
+
assign_record_attributes(queried_record, permitted_params_for(:update), data_params)
|
72
|
+
|
89
73
|
execute_before_update_callbacks(queried_record)
|
90
74
|
execute_before_save_callbacks(queried_record)
|
91
75
|
|
92
|
-
|
76
|
+
fail RecordInvalidError.new(queried_record) if queried_record.errors.any?
|
77
|
+
|
93
78
|
queried_record.save!
|
94
79
|
|
95
80
|
execute_after_update_callbacks(queried_record)
|
@@ -285,21 +270,29 @@ module Caprese
|
|
285
270
|
# @param [Hash] resource_identifier the resource identifier for the resource
|
286
271
|
# @return [ActiveRecord::Base] the found or built resource for the relationship
|
287
272
|
def record_for_relationship(owner, relationship_name, resource_identifier)
|
288
|
-
|
273
|
+
if resource_identifier[:type]
|
274
|
+
# { type: '...', id: '...' }
|
289
275
|
if (id = resource_identifier[Caprese.config.resource_primary_key])
|
290
276
|
get_record!(
|
291
277
|
resource_identifier[:type],
|
292
278
|
Caprese.config.resource_primary_key,
|
293
279
|
id
|
294
280
|
)
|
281
|
+
|
282
|
+
# { type: '...', attributes: { ... } }
|
295
283
|
elsif contains_constructable_data?(resource_identifier)
|
296
284
|
record_scope(resource_identifier[:type]).build
|
285
|
+
|
286
|
+
# { type: '...' }
|
297
287
|
else
|
298
288
|
owner.errors.add(relationship_name)
|
299
289
|
nil
|
300
290
|
end
|
301
|
-
|
302
|
-
|
291
|
+
else
|
292
|
+
# { id: '...' } && { attributes: { ... } }
|
293
|
+
owner.errors.add(relationship_name)
|
294
|
+
nil
|
295
|
+
end
|
303
296
|
end
|
304
297
|
|
305
298
|
# Assigns permitted attributes for a record in a relationship, for a given action
|
@@ -58,12 +58,14 @@ module Caprese
|
|
58
58
|
links = { self: request.original_url }
|
59
59
|
|
60
60
|
if !target.respond_to?(:to_ary) &&
|
61
|
-
|
61
|
+
Rails.application.routes.url_helpers
|
62
|
+
.respond_to?(related_url = version_name("#{params[:relationship].singularize}_url"))
|
62
63
|
|
63
64
|
links[:related] =
|
64
|
-
send(
|
65
|
+
Rails.application.routes.url_helpers.send(
|
65
66
|
related_url,
|
66
|
-
target.read_attribute(self.config.resource_primary_key)
|
67
|
+
target.read_attribute(self.config.resource_primary_key),
|
68
|
+
host: caprese_default_url_options_host
|
67
69
|
)
|
68
70
|
end
|
69
71
|
|
@@ -110,8 +112,15 @@ module Caprese
|
|
110
112
|
links = { self: request.original_url }
|
111
113
|
|
112
114
|
# Add related link for this relationship if it exists
|
113
|
-
if
|
114
|
-
|
115
|
+
if Rails.application.routes.url_helpers
|
116
|
+
.respond_to?(related_path = "relationship_data_#{version_name(unversion(params[:controller]).singularize)}_url")
|
117
|
+
|
118
|
+
links[:related] = Rails.application.routes.url_helpers.send(
|
119
|
+
related_path,
|
120
|
+
params[:id],
|
121
|
+
params[:relationship],
|
122
|
+
host: caprese_default_url_options_host
|
123
|
+
)
|
115
124
|
end
|
116
125
|
|
117
126
|
target = queried_association.reader
|
@@ -246,5 +255,13 @@ module Caprese
|
|
246
255
|
|
247
256
|
@queried_association
|
248
257
|
end
|
258
|
+
|
259
|
+
# Fetches the host from Caprese.config.default_url_options or fails if it is not set
|
260
|
+
# @note default_url_options[:host] is used to render the host in links that are serialized in the response
|
261
|
+
def caprese_default_url_options_host
|
262
|
+
Caprese.config.default_url_options.fetch(:host) do
|
263
|
+
fail 'Caprese requires that config.default_url_options[:host] be set when rendering links.'
|
264
|
+
end
|
265
|
+
end
|
249
266
|
end
|
250
267
|
end
|
@@ -9,6 +9,7 @@ module Caprese
|
|
9
9
|
# instead of requiring that they be explicity stated
|
10
10
|
def render(options = {})
|
11
11
|
options[:adapter] = Caprese::Adapter::JsonApi
|
12
|
+
options[:meta] = meta unless meta.empty?
|
12
13
|
|
13
14
|
if options[:json].respond_to?(:to_ary)
|
14
15
|
if options[:json].first.is_a?(Error)
|
@@ -27,6 +28,16 @@ module Caprese
|
|
27
28
|
super
|
28
29
|
end
|
29
30
|
|
31
|
+
# Allows for meta tags to be added in response document
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# meta[:redirect_url] = ...
|
35
|
+
#
|
36
|
+
# @return [Hash] the meta tag object
|
37
|
+
def meta
|
38
|
+
@caprese_meta ||= {}
|
39
|
+
end
|
40
|
+
|
30
41
|
private
|
31
42
|
|
32
43
|
# Finds a versioned serializer for a given resource
|
@@ -34,10 +45,15 @@ module Caprese
|
|
34
45
|
# @example
|
35
46
|
# serializer_for(post) => API::V1::PostSerializer
|
36
47
|
#
|
48
|
+
# @note The reason this method is a duplicate of Caprese::Serializer.serializer_for is
|
49
|
+
# because the latter is only ever called from children of Caprese::Serializer, like those
|
50
|
+
# in the API::V1:: scope. If we tried to use that method instead of this one, we end up
|
51
|
+
# with Caprese::[record.class.name]Serializer instead of the proper versioned serializer
|
52
|
+
#
|
37
53
|
# @param [ActiveRecord::Base] record the record to find a serializer for
|
38
54
|
# @return [Serializer,Nil] the serializer for the given record
|
39
55
|
def serializer_for(record)
|
40
|
-
version_module("#{record.class.name}Serializer").constantize
|
56
|
+
version_module("#{record.class.name}Serializer").constantize if Serializer.valid_for_serialization(record)
|
41
57
|
end
|
42
58
|
end
|
43
59
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Caprese
|
2
|
+
module Record
|
3
|
+
# Formats nested errors on associations in a more useful way than Rails alone
|
4
|
+
#
|
5
|
+
# @note BEFORE
|
6
|
+
# POST /posts (with invalid resources) =>
|
7
|
+
# [
|
8
|
+
# { "key"=>"invalid", "field"=>"attachment", "message"=>"Attachment is invalid." }
|
9
|
+
# ]
|
10
|
+
#
|
11
|
+
# @note AFTER
|
12
|
+
# POST /posts (with invalid resources) =>
|
13
|
+
# [
|
14
|
+
# { "key"=>"not_found", "field"=>"attachment.file", "message"=>"Could not find a file at ..."}
|
15
|
+
# ]
|
16
|
+
class AssociatedValidator < ActiveModel::EachValidator
|
17
|
+
def validate_each(record, attribute, value)
|
18
|
+
Array(value).reject { |r| r.marked_for_destruction? || r.valid? }.each do |invalid_record|
|
19
|
+
invalid_record.errors.to_a.each do |error|
|
20
|
+
field_name = error.field ? "#{attribute}.#{error.field}" : attribute
|
21
|
+
record.errors.add(field_name, error.code, options.merge(value: invalid_record))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def validates_associated(*attr_names)
|
29
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/caprese/record.rb
CHANGED
@@ -12,8 +12,14 @@ module Caprese
|
|
12
12
|
# object = Order<@token='asd27h'>
|
13
13
|
# links = { self: '/api/v1/orders/asd27hß' }
|
14
14
|
link :self do
|
15
|
-
if
|
16
|
-
|
15
|
+
if Rails.application.routes.url_helpers
|
16
|
+
.respond_to?(url = serializer.version_name("#{object.class.name.underscore}_url"))
|
17
|
+
|
18
|
+
Rails.application.routes.url_helpers.send(
|
19
|
+
url,
|
20
|
+
object.read_attribute(Caprese.config.resource_primary_key),
|
21
|
+
host: serializer.send(:caprese_default_url_options_host)
|
22
|
+
)
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -67,22 +73,24 @@ module Caprese
|
|
67
73
|
Proc.new do |serializer|
|
68
74
|
link :self do
|
69
75
|
url = "relationship_definition_#{serializer.version_name("#{object.class.name.underscore}_url")}"
|
70
|
-
if respond_to? url
|
71
|
-
send(
|
76
|
+
if Rails.application.routes.url_helpers.respond_to? url
|
77
|
+
Rails.application.routes.url_helpers.send(
|
72
78
|
url,
|
73
79
|
primary_key => object.read_attribute(primary_key),
|
74
|
-
relationship: reflection_name
|
80
|
+
relationship: reflection_name,
|
81
|
+
host: serializer.send(:caprese_default_url_options_host)
|
75
82
|
)
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
79
86
|
link :related do
|
80
87
|
url = "relationship_data_#{serializer.version_name("#{object.class.name.underscore}_url")}"
|
81
|
-
if respond_to? url
|
82
|
-
send(
|
88
|
+
if Rails.application.routes.url_helpers.respond_to? url
|
89
|
+
Rails.application.routes.url_helpers.send(
|
83
90
|
url,
|
84
91
|
primary_key => object.read_attribute(primary_key),
|
85
|
-
relationship: reflection_name
|
92
|
+
relationship: reflection_name,
|
93
|
+
host: serializer.send(:caprese_default_url_options_host)
|
86
94
|
)
|
87
95
|
end
|
88
96
|
end
|
@@ -91,6 +99,16 @@ module Caprese
|
|
91
99
|
end
|
92
100
|
end
|
93
101
|
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Fetches the host from Caprese.config.default_url_options or fails if it is not set
|
106
|
+
# @note default_url_options[:host] is used to render the host in links that are serialized in the response
|
107
|
+
def caprese_default_url_options_host
|
108
|
+
Caprese.config.default_url_options.fetch(:host) do
|
109
|
+
fail 'Caprese requires that config.default_url_options[:host] be set when rendering links.'
|
110
|
+
end
|
111
|
+
end
|
94
112
|
end
|
95
113
|
end
|
96
114
|
end
|
@@ -14,7 +14,23 @@ module Caprese
|
|
14
14
|
# @param [Hash] options options for `super` to use when getting the serializer
|
15
15
|
# @return [Serializer,Nil] the serializer for the given record
|
16
16
|
def serializer_for(record, options = {})
|
17
|
-
|
17
|
+
return ActiveModel::Serializer::CollectionSerializer if record.respond_to?(:to_ary)
|
18
|
+
|
19
|
+
get_serializer_for(record.class) if valid_for_serialization(record)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Indicates whether or not the record specified can be serialized by Caprese
|
23
|
+
#
|
24
|
+
# @note The only requirement right now is that the record model has Caprese::Record included
|
25
|
+
#
|
26
|
+
# @param [Object] record the record to check if is valid for serialization
|
27
|
+
# @return [True] this method either returns true, or fails - breaking control flow
|
28
|
+
def valid_for_serialization(record)
|
29
|
+
if record && !record.class.included_modules.include?(Caprese::Record)
|
30
|
+
fail 'All models managed by Caprese must include Caprese::Record'
|
31
|
+
end
|
32
|
+
|
33
|
+
true
|
18
34
|
end
|
19
35
|
|
20
36
|
private
|
data/lib/caprese/serializer.rb
CHANGED
data/lib/caprese/version.rb
CHANGED
data/lib/caprese.rb
CHANGED
@@ -23,7 +23,7 @@ module Caprese
|
|
23
23
|
config.only_path_links ||= true
|
24
24
|
|
25
25
|
# If true, relationship data will not be serialized unless it is in `include`
|
26
|
-
config.optimize_relationships ||=
|
26
|
+
config.optimize_relationships ||= false
|
27
27
|
|
28
28
|
# Defines the translation scope for model and controller errors
|
29
29
|
config.i18n_scope ||= '' # 'api.v1.errors'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caprese
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Landgrebe
|
@@ -10,36 +10,36 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: active_model_serializers
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.10.
|
21
|
+
version: 0.10.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - '='
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.10.
|
28
|
+
version: 0.10.2
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: kaminari
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - '='
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 0.17.0
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - '='
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
42
|
+
version: 0.17.0
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: rails
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- ".coveralls.yml"
|
205
205
|
- ".gitignore"
|
206
206
|
- ".travis.yml"
|
207
|
+
- CHANGELOG.md
|
207
208
|
- Gemfile
|
208
209
|
- LICENSE.txt
|
209
210
|
- README.md
|
@@ -233,6 +234,7 @@ files:
|
|
233
234
|
- lib/caprese/error.rb
|
234
235
|
- lib/caprese/errors.rb
|
235
236
|
- lib/caprese/record.rb
|
237
|
+
- lib/caprese/record/associated_validator.rb
|
236
238
|
- lib/caprese/record/errors.rb
|
237
239
|
- lib/caprese/routing/caprese_resources.rb
|
238
240
|
- lib/caprese/serializer.rb
|