gql_serializer 2.2.1 → 3.0.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.
@@ -1,178 +1,218 @@
1
- require "gql_serializer/version"
2
- require "gql_serializer/extensions"
3
- require "gql_serializer/configuration"
4
-
5
-
6
- module GqlSerializer
7
-
8
- def self.configuration
9
- @configuration ||= Configuration.new
10
- end
11
-
12
- def self.configure
13
- yield configuration
14
- end
15
-
16
-
17
- def self.parse_query(input)
18
- query = input.dup
19
- query.strip!
20
- query.gsub!(/[\r\n\t ]+/, ' ')
21
- query.gsub!(/\{ /, '{')
22
- query.gsub!(/ }/, '}')
23
-
24
- result, _ = self.parse_it(query)
25
- result
26
- end
27
-
28
- def self.query_include(model, hasharray)
29
- include_array = []
30
- relations = model.reflections
31
- hasharray.each do |element|
32
- if element.is_a? String
33
- key = element.split(':')[0]
34
- include_array.push(key) if relations[key]
35
- elsif element.is_a? Hash
36
- key = element.keys.first.split(':')[0]
37
- relation_model = model.reflections[key].klass
38
- relation_hasharray = self.query_include(relation_model, element.values.first)
39
- if relation_hasharray.empty?
40
- include_array.push(key)
41
- else
42
- include_array.push({key => relation_hasharray})
43
- end
44
- end
45
- end
46
- include_array
47
- end
48
-
49
- # example hasharray = ["id", "name", "tags", { "panels" => ["id", { "cards" => ["content"] }] }]
50
- def self.serialize(records, hasharray, options, instructions = {})
51
-
52
- if records.nil?
53
- return nil
54
- end
55
-
56
-
57
- if records.respond_to? :map
58
- return records.map do |record|
59
- self.serialize(record, hasharray, options, instructions)
60
- end
61
- end
62
- record = records
63
-
64
- id = "#{record.class}, #{hasharray}"
65
- instruction = instructions[id]
66
- if (!instruction)
67
- instruction = {klass: record.class, hasharray: hasharray, relations: [], attributes: []}
68
- instructions[id] = instruction
69
-
70
-
71
- model = record.class
72
- all_relations = model.reflections.keys
73
-
74
- relations = hasharray.filter do |relation|
75
- next true if !relation.is_a?(String)
76
-
77
- key, alias_key = relation.split(':')
78
-
79
- all_relations.include?(key)
80
- end
81
-
82
- attributes = hasharray - relations
83
- attributes = model.attribute_names if attributes.empty?
84
-
85
- attributes.each do |attribute|
86
- key, alias_key = attribute.split(':')
87
- alias_key = apply_case(alias_key || key, options[:case])
88
-
89
- instruction[:attributes].push({key: key, alias_key: alias_key})
90
- end
91
-
92
- relations.each do |relation|
93
- if relation.is_a? String
94
- key, alias_key = relation.split(':')
95
- alias_key = apply_case(alias_key || key, options[:case])
96
-
97
- instruction[:relations].push({key: key, alias_key: alias_key, hasharray: []})
98
- else
99
- key, alias_key = relation.keys.first.split(':')
100
- alias_key = apply_case(alias_key || key, options[:case])
101
-
102
- instruction[:relations].push({key: key, alias_key: alias_key, hasharray: relation.values.first})
103
- end
104
- end
105
- end
106
-
107
- hash = {}
108
- instruction[:attributes].each do |attribute|
109
- hash[attribute[:alias_key]] = coerce_value(record.public_send(attribute[:key]))
110
- end
111
- instruction[:relations].each do |relation|
112
- relation_records = record.public_send(relation[:key])
113
- hash[relation[:alias_key]] = self.serialize(relation_records, relation[:hasharray], options, instructions)
114
- end
115
-
116
- hash
117
- end
118
-
119
- def self.coerce_value(value)
120
- return value.to_f if value.is_a? BigDecimal
121
- return value.new_offset(0).strftime("%FT%TZ") if value.is_a? DateTime
122
- return value.utc.iso8601 if value.is_a? Time
123
- value
124
- end
125
-
126
-
127
- private
128
-
129
- def self.apply_case(key, key_case)
130
- case key_case
131
- when Configuration::CAMEL_CASE
132
- result = key.camelize
133
- result[0] = result[0].downcase
134
- when Configuration::SNAKE_CASE
135
- result = key.underscore
136
- else
137
- result = key
138
- end
139
-
140
- result
141
- end
142
-
143
- def self.parse_it(query)
144
- result = []
145
- while query&.length&.> 0
146
- if query[0] == ' '
147
- query.strip!
148
- next
149
- elsif query[0] == '}'
150
- return result, query[1..-1]
151
- end
152
-
153
- next_key = query[/[_a-zA-Z0-9:]+/]
154
- query = query[next_key.length..-1]
155
- query.strip!
156
-
157
- if query.nil? || query.empty? || query[0].match?(/[_a-zA-Z0-9:]/)
158
- result.push(next_key)
159
-
160
- elsif query&.[](0) == '{'
161
- query = query[1..-1]
162
- obj, query = parse_it(query)
163
- result.push(next_key => obj)
164
-
165
- elsif query[0] == '}'
166
- result.push(next_key)
167
- return result, query[1..-1]
168
-
169
- else
170
- raise "unsupported character '#{query[0]}'"
171
-
172
- end
173
- end
174
- return result, nil
175
- end
176
-
177
-
178
- end
1
+ require "gql_serializer/version"
2
+ require "gql_serializer/extensions"
3
+ require "gql_serializer/configuration"
4
+
5
+
6
+ module GqlSerializer
7
+
8
+ def self.configuration
9
+ @configuration ||= Configuration.new
10
+ end
11
+
12
+ def self.configure
13
+ yield configuration
14
+ end
15
+
16
+
17
+ def self.parse_query(input)
18
+ query = input.dup
19
+ query.strip!
20
+ query.gsub!(/[\r\n\t ]+/, ' ')
21
+ query.gsub!(/\{ /, '{')
22
+ query.gsub!(/ }/, '}')
23
+
24
+ result, _ = self.parse_it(query)
25
+ result
26
+ end
27
+
28
+ def self.query_include(model, hasharray)
29
+ return [] if !model.respond_to? :reflections
30
+ include_array = []
31
+ relations = model.reflections
32
+ hasharray.each do |element|
33
+ if element.is_a? String
34
+ key = element.split(':')[0]
35
+ include_array.push(key) if relations[key]
36
+ elsif element.is_a? Hash
37
+ key = element.keys.first.split(':')[0]
38
+ relation_model = model.reflections&.[](key)&.klass
39
+ next if relation_model.nil?
40
+ relation_hasharray = self.query_include(relation_model, element.values.first)
41
+ if relation_hasharray.empty?
42
+ include_array.push(key)
43
+ else
44
+ include_array.push({key => relation_hasharray})
45
+ end
46
+ end
47
+ end
48
+ include_array
49
+ end
50
+
51
+ # example hasharray = ["id", "name:real_name", "tags", { "panels" => ["id", { "cards" => ["content"] }] }]
52
+ def self.serialize(records, hasharray, options, instructions = {})
53
+
54
+ if records.nil?
55
+ return nil
56
+ end
57
+
58
+ if records.is_a?(Hash)
59
+ return self.serialize_hash(records, hasharray, options, instructions)
60
+ end
61
+
62
+ if records.respond_to? :map
63
+ return records.map do |record|
64
+ self.serialize(record, hasharray, options, instructions)
65
+ end
66
+ end
67
+
68
+ if records.class.respond_to? :reflections
69
+ return self.serialize_active_record(records, hasharray, options, instructions)
70
+ end
71
+
72
+ if !hasharray.empty?
73
+ return self.serialize_object(records, hasharray, options, instructions)
74
+ end
75
+
76
+ return coerce_value(records)
77
+ end
78
+
79
+ def self.serialize_active_record(record, hasharray, options, instructions = {})
80
+ id = "#{record.class}, #{hasharray}"
81
+ instruction = instructions[id]
82
+ if (!instruction)
83
+ instruction = {klass: record.class, hasharray: hasharray, attributes: []}
84
+ instructions[id] = instruction
85
+
86
+ model = record.class
87
+ all_relations = model.reflections.keys
88
+ relations = hasharray.filter do |relation|
89
+ key, _ = self.get_keys(relation, options)
90
+ all_relations.include?(key)
91
+ end
92
+
93
+ if (hasharray - relations).empty?
94
+ attributes = model.attribute_names + relations
95
+ else
96
+ attributes = hasharray
97
+ end
98
+
99
+ attributes.each do |attribute|
100
+ key, alias_key, sub_hasharray = self.get_keys(attribute, options)
101
+ instruction[:attributes].push({key: key, alias_key: alias_key, hasharray: sub_hasharray})
102
+ end
103
+ end
104
+
105
+ hash = {}
106
+ instruction[:attributes].each do |attribute|
107
+ value = record.public_send(attribute[:key])
108
+ hash[attribute[:alias_key]] = self.serialize(value, attribute[:hasharray], options, instructions)
109
+ end
110
+
111
+ hash
112
+ end
113
+
114
+ def self.serialize_object(record, hasharray, options, instructions = {})
115
+ hash = {}
116
+
117
+ hasharray.each do |attribute|
118
+ key, alias_key, sub_hasharray = self.get_keys(attribute, options)
119
+ value = record.public_send(key)
120
+ hash[alias_key] = self.serialize(value, sub_hasharray, options, instructions)
121
+ end
122
+
123
+ hash
124
+ end
125
+
126
+ def self.serialize_hash(record, hasharray, options, instructions = {})
127
+ hash = {}
128
+ attributes = hasharray.empty? ? record.keys : hasharray
129
+
130
+ attributes.each do |attribute|
131
+ key, alias_key, sub_hasharray = self.get_keys(attribute, options)
132
+ value = self.get_hash_value(record, key)
133
+ hash[alias_key] = self.serialize(value, sub_hasharray, options, instructions)
134
+ end
135
+
136
+ hash
137
+ end
138
+
139
+ def self.coerce_value(value)
140
+ return value.to_f if value.is_a? BigDecimal
141
+ return value.new_offset(0).strftime("%FT%TZ") if value.is_a? DateTime
142
+ return value.utc.iso8601 if value.is_a? Time
143
+ value
144
+ end
145
+
146
+
147
+ private
148
+
149
+ def self.apply_case(key, key_case)
150
+ case key_case
151
+ when Configuration::CAMEL_CASE
152
+ result = key.camelize
153
+ result[0] = result[0].downcase
154
+ when Configuration::SNAKE_CASE
155
+ result = key.underscore
156
+ else
157
+ result = key
158
+ end
159
+
160
+ result
161
+ end
162
+
163
+ def self.get_hash_value(hash, key)
164
+ return hash[key] if hash.key?(key)
165
+ return hash[key.to_sym] if hash.key?(key.to_sym)
166
+ raise NoMethodError, "undefined field '#{key.to_s}' for #{hash}"
167
+ end
168
+
169
+ def self.get_keys(attribute, options)
170
+ if attribute.is_a?(Symbol)
171
+ key, alias_key = attribute.to_s.split(':')
172
+ hasharray = []
173
+ elsif attribute.is_a?(String)
174
+ key, alias_key = attribute.split(':')
175
+ hasharray = []
176
+ else
177
+ key, alias_key = attribute.keys.first.split(':')
178
+ hasharray = attribute.values.first
179
+ end
180
+ alias_key = apply_case(alias_key || key, options[:case])
181
+ [key, alias_key, hasharray]
182
+ end
183
+
184
+ def self.parse_it(query)
185
+ result = []
186
+ while query&.length&.> 0
187
+ if query[0] == ' '
188
+ query.strip!
189
+ next
190
+ elsif query[0] == '}'
191
+ return result, query[1..-1]
192
+ end
193
+
194
+ next_key = query[/[_a-zA-Z0-9:]+/]
195
+ query = query[next_key.length..-1]
196
+ query.strip!
197
+
198
+ if query.nil? || query.empty? || query[0].match?(/[_a-zA-Z0-9:]/)
199
+ result.push(next_key)
200
+
201
+ elsif query&.[](0) == '{'
202
+ query = query[1..-1]
203
+ obj, query = parse_it(query)
204
+ result.push(next_key => obj)
205
+
206
+ elsif query[0] == '}'
207
+ result.push(next_key)
208
+ return result, query[1..-1]
209
+
210
+ else
211
+ raise "unsupported character '#{query[0]}'"
212
+
213
+ end
214
+ end
215
+ return result, nil
216
+ end
217
+
218
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gql_serializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Scullion
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-24 00:00:00.000000000 Z
11
+ date: 2025-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -44,48 +44,48 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.13.1
47
+ version: '0.15'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.13.1
54
+ version: '0.15'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sqlite3
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.4.2
61
+ version: '2.7'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.4.2
68
+ version: '2.7'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: activerecord
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '5.2'
75
+ version: '7.0'
76
76
  - - "<"
77
77
  - !ruby/object:Gem::Version
78
- version: '8.0'
78
+ version: '8.1'
79
79
  type: :runtime
80
80
  prerelease: false
81
81
  version_requirements: !ruby/object:Gem::Requirement
82
82
  requirements:
83
83
  - - ">="
84
84
  - !ruby/object:Gem::Version
85
- version: '5.2'
85
+ version: '7.0'
86
86
  - - "<"
87
87
  - !ruby/object:Gem::Version
88
- version: '8.0'
88
+ version: '8.1'
89
89
  description: A gem that adds `as_gql` to easily serialize ActiveRecord objects
90
90
  email:
91
91
  - andrewsc32@protonmail.com
@@ -125,14 +125,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
- version: 2.6.1
128
+ version: 2.7.0
129
129
  required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  requirements:
131
131
  - - ">="
132
132
  - !ruby/object:Gem::Version
133
133
  version: '0'
134
134
  requirements: []
135
- rubygems_version: 3.1.6
135
+ rubygems_version: 3.4.19
136
136
  signing_key:
137
137
  specification_version: 4
138
138
  summary: A gem that adds `as_gql` to easily serialize ActiveRecord objects