@@ -3,85 +3,146 @@ module Serializers
3
3
class Validation
4
4
attr_reader :object
5
5
6
- def initialize ( object , relationship_params = { } , relationship_message = { } )
6
+ def initialize ( object , relationship_payloads = { } , relationship_meta = { } )
7
7
@object = object
8
- @relationship_params = relationship_params || { }
9
- @relationship_message = relationship_message
8
+ @relationship_payloads = relationship_payloads
9
+ @relationship_meta = relationship_meta
10
10
end
11
11
12
- def errors
13
- return [ ] unless object . respond_to? ( :errors )
14
-
15
- all_errors = object . errors . to_hash . map do |attribute , messages |
16
- messages . map do |message |
17
- meta = { attribute : attribute , message : message } . merge ( @relationship_message )
18
- meta = { relationship : meta } if @relationship_message . present?
19
-
20
- detail = object . errors . full_message ( attribute , message )
21
- detail = message if attribute . to_s . downcase == 'base'
22
-
23
- {
12
+ def attribute_errors
13
+ [ ] . tap do |errors |
14
+ each_error do |attribute , message , code |
15
+ error = {
24
16
code : 'unprocessable_entity' ,
25
17
status : '422' ,
26
18
title : 'Validation Error' ,
27
- detail : detail ,
19
+ detail : detail_for ( attribute , message ) ,
28
20
source : { pointer : pointer_for ( object , attribute ) } ,
29
- meta : meta
21
+ meta : meta_for ( attribute , message , code , @relationship_meta )
30
22
}
23
+
24
+ errors << error
31
25
end
32
- end . flatten
33
- all_errors << relationship_errors ( @relationship_params )
34
- all_errors . flatten!
35
- all_errors . compact!
26
+ end
27
+ end
28
+
29
+ def errors
30
+ return [ ] unless object . respond_to? ( :errors )
31
+
32
+ all_errors = attribute_errors
33
+ all_errors |= relationship_errors ( @relationship_payloads )
36
34
all_errors
37
35
end
38
36
37
+ private
38
+
39
+ def each_error
40
+ object . errors . messages . each_pair do |attribute , messages |
41
+ details = if Rails ::VERSION ::MAJOR >= 5
42
+ object . errors . details . find { |k , v | k == attribute } [ 1 ]
43
+ end
44
+
45
+ messages . each_with_index do |message , index |
46
+ code = details [ index ] [ :error ] if details
47
+ yield attribute , message , code
48
+ end
49
+ end
50
+ end
51
+
39
52
def relationship? ( name )
40
- return false unless activemodel?
53
+ relationship_names = [ ]
54
+ if activerecord?
55
+ relationship_names = object . class
56
+ . reflect_on_all_associations . map ( &:name )
57
+ elsif object . respond_to? ( :relationship_names )
58
+ relationship_names = object . relationship_names
59
+ end
41
60
42
- relation_names = object . class . reflect_on_all_associations . map ( &:name )
43
- relation_names . include? ( name )
61
+ relationship_names . include? ( name )
44
62
end
45
63
46
64
def attribute? ( name )
47
65
object . respond_to? ( name )
48
66
end
49
67
50
- private
68
+ def meta_for ( attribute , message , code , relationship_meta )
69
+ meta = {
70
+ attribute : attribute ,
71
+ message : message
72
+ }
73
+ meta . merge! ( code : code ) if Rails ::VERSION ::MAJOR >= 5
74
+
75
+ unless relationship_meta . empty?
76
+ meta = {
77
+ relationship : meta . merge ( relationship_meta )
78
+ }
79
+ end
80
+
81
+ meta
82
+ end
51
83
84
+ def detail_for ( attribute , message )
85
+ detail = object . errors . full_message ( attribute , message )
86
+ detail = message if attribute . to_s . downcase == 'base'
87
+ detail
88
+ end
89
+
90
+ # @richmolj: Keeping this to support ember-data, but I hate the concept.
52
91
def pointer_for ( object , name )
53
92
if relationship? ( name )
54
93
"/data/relationships/#{ name } "
55
94
elsif attribute? ( name )
56
95
"/data/attributes/#{ name } "
96
+ elsif name == :base
97
+ nil
57
98
else
58
99
# Probably a nested relation, like post.comments
59
100
"/data/relationships/#{ name } "
60
101
end
61
102
end
62
103
63
- def activemodel ?
104
+ def activerecord ?
64
105
object . class . respond_to? ( :reflect_on_all_associations )
65
106
end
66
107
67
- def relationship_errors ( relationship_params )
68
- errors = [ ]
108
+ def traverse_relationships ( relationship_params )
109
+ return unless relationship_params
110
+
69
111
relationship_params . each_pair do |name , payload |
70
- related = Array ( @object . send ( name ) )
71
- related . each do |r |
112
+ relationship_objects = Array ( @object . send ( name ) )
113
+
114
+ relationship_objects . each do |relationship_object |
115
+ related_payload = payload
72
116
if payload . is_a? ( Array )
73
- related_payload = payload . find { |p | p [ :meta ] [ :temp_id ] === r . instance_variable_get ( :@_jsonapi_temp_id ) || p [ :meta ] [ :id ] == r . id }
74
- else
75
- related_payload = payload
117
+ related_payload = payload . find do |p |
118
+ temp_id = relationship_object
119
+ . instance_variable_get ( :@_jsonapi_temp_id )
120
+ p [ :meta ] [ :temp_id ] === temp_id ||
121
+ p [ :meta ] [ :id ] == relationship_object . id . to_s
122
+ end
76
123
end
77
- relationship_message = {
78
- name : name ,
79
- id : r . id ,
80
- :'temp-id' => r . instance_variable_get ( :@_jsonapi_temp_id )
81
- }
82
124
83
- errors << Validation . new ( r , related_payload [ :relationships ] , relationship_message ) . errors
125
+ yield name , relationship_object , related_payload
126
+ traverse_relationships ( related_payload [ :relationships ] )
127
+ end
128
+ end
129
+ end
130
+
131
+ def relationship_errors ( relationship_payloads )
132
+ errors = [ ]
133
+ traverse_relationships ( relationship_payloads ) do |name , model , payload |
134
+ meta = { } . tap do |hash |
135
+ hash [ :name ] = name
136
+ hash [ :type ] = payload [ :meta ] [ :jsonapi_type ]
137
+ if temp_id = model . instance_variable_get ( :@_jsonapi_temp_id )
138
+ hash [ :'temp-id' ] = temp_id
139
+ else
140
+ hash [ :id ] = model . id
141
+ end
84
142
end
143
+
144
+ serializer = self . class . new ( model , payload [ :relationships ] , meta )
145
+ errors |= serializer . errors
85
146
end
86
147
errors
87
148
end
0 commit comments