joyful_jsonapi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/railtie'
4
+
5
+ class Railtie < Rails::Railtie
6
+ initializer 'joyful_jsonapi.active_record' do
7
+ ActiveSupport.on_load :active_record do
8
+ require 'extensions/has_one'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,120 @@
1
+ module JoyfulJsonapi
2
+ class Relationship
3
+ attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method, :links, :lazy_load_data
4
+
5
+ def initialize(
6
+ key:,
7
+ name:,
8
+ id_method_name:,
9
+ record_type:,
10
+ object_method_name:,
11
+ object_block:,
12
+ serializer:,
13
+ relationship_type:,
14
+ cached: false,
15
+ polymorphic:,
16
+ conditional_proc:,
17
+ transform_method:,
18
+ links:,
19
+ lazy_load_data: false
20
+ )
21
+ @key = key
22
+ @name = name
23
+ @id_method_name = id_method_name
24
+ @record_type = record_type
25
+ @object_method_name = object_method_name
26
+ @object_block = object_block
27
+ @serializer = serializer
28
+ @relationship_type = relationship_type
29
+ @cached = cached
30
+ @polymorphic = polymorphic
31
+ @conditional_proc = conditional_proc
32
+ @transform_method = transform_method
33
+ @links = links || {}
34
+ @lazy_load_data = lazy_load_data
35
+ end
36
+
37
+ def serialize(record, serialization_params, output_hash)
38
+ if include_relationship?(record, serialization_params)
39
+ empty_case = relationship_type == :has_many ? [] : nil
40
+
41
+ output_hash[key] = {}
42
+ unless lazy_load_data
43
+ output_hash[key][:data] = ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
44
+ end
45
+ add_links_hash(record, serialization_params, output_hash) if links.present?
46
+ end
47
+ end
48
+
49
+ def fetch_associated_object(record, params)
50
+ return object_block.call(record, params) unless object_block.nil?
51
+ record.send(object_method_name)
52
+ end
53
+
54
+ def include_relationship?(record, serialization_params)
55
+ if conditional_proc.present?
56
+ conditional_proc.call(record, serialization_params)
57
+ else
58
+ true
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def ids_hash_from_record_and_relationship(record, params = {})
65
+ return ids_hash(
66
+ fetch_id(record, params)
67
+ ) unless polymorphic
68
+
69
+ return unless associated_object = fetch_associated_object(record, params)
70
+
71
+ return associated_object.map do |object|
72
+ id_hash_from_record object, polymorphic
73
+ end if associated_object.respond_to? :map
74
+
75
+ id_hash_from_record associated_object, polymorphic
76
+ end
77
+
78
+ def id_hash_from_record(record, record_types)
79
+ # memoize the record type within the record_types dictionary, then assigning to record_type:
80
+ associated_record_type = record_types[record.class] ||= run_key_transform(record.class.name.demodulize.underscore)
81
+ id_hash(record.id, associated_record_type)
82
+ end
83
+
84
+ def ids_hash(ids)
85
+ return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
86
+ id_hash(ids, record_type) # ids variable is just a single id here
87
+ end
88
+
89
+ def id_hash(id, record_type, default_return=false)
90
+ if id.present?
91
+ { id: id.to_s, type: record_type }
92
+ else
93
+ default_return ? { id: nil, type: record_type } : nil
94
+ end
95
+ end
96
+
97
+ def fetch_id(record, params)
98
+ if object_block.present?
99
+ object = object_block.call(record, params)
100
+ return object.map { |item| item.public_send(id_method_name) } if object.respond_to? :map
101
+ return object.try(id_method_name)
102
+ end
103
+ record.public_send(id_method_name)
104
+ end
105
+
106
+ def add_links_hash(record, params, output_hash)
107
+ output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash|
108
+ Link.new(key: key, method: method).serialize(record, params, hash)\
109
+ end
110
+ end
111
+
112
+ def run_key_transform(input)
113
+ if self.transform_method.present?
114
+ input.to_s.send(*self.transform_method).to_sym
115
+ else
116
+ input.to_sym
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'joyful_jsonapi/multi_to_json'
5
+
6
+ module JoyfulJsonapi
7
+ MandatoryField = Class.new(StandardError)
8
+
9
+ module SerializationCore
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ class << self
14
+ attr_accessor :attributes_to_serialize,
15
+ :relationships_to_serialize,
16
+ :cachable_relationships_to_serialize,
17
+ :uncachable_relationships_to_serialize,
18
+ :transform_method,
19
+ :record_type,
20
+ :record_id,
21
+ :cache_length,
22
+ :race_condition_ttl,
23
+ :cached,
24
+ :data_links,
25
+ :meta_to_serialize
26
+ end
27
+ end
28
+
29
+ class_methods do
30
+ def id_hash(id, record_type, default_return=false)
31
+ if id.present?
32
+ { id: id.to_s, type: record_type }
33
+ else
34
+ default_return ? { id: nil, type: record_type } : nil
35
+ end
36
+ end
37
+
38
+ def links_hash(record, params = {})
39
+ data_links.each_with_object({}) do |(_k, link), hash|
40
+ link.serialize(record, params, hash)
41
+ end
42
+ end
43
+
44
+ def attributes_hash(record, fieldset = nil, params = {})
45
+ attributes = attributes_to_serialize
46
+ attributes = attributes.slice(*fieldset) if fieldset.present?
47
+ attributes.each_with_object({}) do |(_k, attribute), hash|
48
+ attribute.serialize(record, params, hash)
49
+ end
50
+ end
51
+
52
+ def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
53
+ relationships = relationships_to_serialize if relationships.nil?
54
+ relationships = relationships.slice(*fieldset) if fieldset.present?
55
+
56
+ relationships.each_with_object({}) do |(_k, relationship), hash|
57
+ relationship.serialize(record, params, hash)
58
+ end
59
+ end
60
+
61
+ def meta_hash(record, params = {})
62
+ meta_to_serialize.call(record, params)
63
+ end
64
+
65
+ def record_hash(record, fieldset, params = {})
66
+ if cached
67
+ record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
68
+ temp_hash = id_hash(id_from_record(record), record_type, true)
69
+ temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
70
+ temp_hash[:relationships] = {}
71
+ temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present?
72
+ temp_hash[:links] = links_hash(record, params) if data_links.present?
73
+ temp_hash
74
+ end
75
+ record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, params)) if uncachable_relationships_to_serialize.present?
76
+ record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present?
77
+ record_hash
78
+ else
79
+ record_hash = id_hash(id_from_record(record), record_type, true)
80
+ record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
81
+ record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
82
+ record_hash[:links] = links_hash(record, params) if data_links.present?
83
+ record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present?
84
+ record_hash
85
+ end
86
+ end
87
+
88
+ def id_from_record(record)
89
+ return record_id.call(record) if record_id.is_a?(Proc)
90
+ return record.send(record_id) if record_id
91
+ raise MandatoryField, 'id is a mandatory field in the jsonapi spec' unless record.respond_to?(:id)
92
+ record.id
93
+ end
94
+
95
+ # Override #to_json for alternative implementation
96
+ def to_json(payload)
97
+ JoyfulJsonapi::MultiToJson.to_json(payload) if payload.present?
98
+ end
99
+
100
+ def parse_include_item(include_item)
101
+ return [include_item.to_sym] unless include_item.to_s.include?('.')
102
+ include_item.to_s.split('.').map { |item| item.to_sym }
103
+ end
104
+
105
+ def remaining_items(items)
106
+ return unless items.size > 1
107
+
108
+ items_copy = items.dup
109
+ items_copy.delete_at(0)
110
+ [items_copy.join('.').to_sym]
111
+ end
112
+
113
+ # includes handler
114
+ def get_included_records(record, includes_list, known_included_objects, fieldsets, params = {})
115
+ return unless includes_list.present?
116
+
117
+ includes_list.sort.each_with_object([]) do |include_item, included_records|
118
+ items = parse_include_item(include_item)
119
+ items.each do |item|
120
+ next unless relationships_to_serialize && relationships_to_serialize[item]
121
+ relationship_item = relationships_to_serialize[item]
122
+ next unless relationship_item.include_relationship?(record, params)
123
+ unless relationship_item.polymorphic.is_a?(Hash)
124
+ record_type = relationship_item.record_type
125
+ serializer = relationship_item.serializer.to_s.constantize
126
+ end
127
+ relationship_type = relationship_item.relationship_type
128
+
129
+ included_objects = relationship_item.fetch_associated_object(record, params)
130
+ next if included_objects.blank?
131
+ included_objects = [included_objects] unless relationship_type == :has_many
132
+
133
+ included_objects.each do |inc_obj|
134
+ if relationship_item.polymorphic.is_a?(Hash)
135
+ record_type = inc_obj.class.name.demodulize.underscore
136
+ serializer = self.compute_serializer_name(inc_obj.class.name.demodulize.to_sym).to_s.constantize
137
+ end
138
+
139
+ if remaining_items(items)
140
+ serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params)
141
+ included_records.concat(serializer_records) unless serializer_records.empty?
142
+ end
143
+
144
+ code = "#{record_type}_#{serializer.id_from_record(inc_obj)}"
145
+ next if known_included_objects.key?(code)
146
+
147
+ known_included_objects[code] = inc_obj
148
+
149
+ included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], params)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,3 @@
1
+ module JoyfulJsonapi
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,254 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: joyful_jsonapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bradley Temple
8
+ - Shishir Kakaraddi
9
+ - Srinivas Raghunathan
10
+ - Adam Gross
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2020-06-02 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '4.2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activerecord
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '4.2'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '4.2'
44
+ - !ruby/object:Gem::Dependency
45
+ name: activemodel
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '4.2'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '4.2'
58
+ - !ruby/object:Gem::Dependency
59
+ name: skylight
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '1.3'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '1.3'
72
+ - !ruby/object:Gem::Dependency
73
+ name: rspec
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: 3.5.0
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: 3.5.0
86
+ - !ruby/object:Gem::Dependency
87
+ name: oj
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '3.3'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '3.3'
100
+ - !ruby/object:Gem::Dependency
101
+ name: rspec-benchmark
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 0.3.0
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: 0.3.0
114
+ - !ruby/object:Gem::Dependency
115
+ name: bundler
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '2.0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '2.0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: byebug
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: active_model_serializers
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 0.10.7
149
+ type: :development
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: 0.10.7
156
+ - !ruby/object:Gem::Dependency
157
+ name: sqlite3
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: '1.3'
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '1.3'
170
+ - !ruby/object:Gem::Dependency
171
+ name: jsonapi-rb
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: 0.5.0
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: 0.5.0
184
+ - !ruby/object:Gem::Dependency
185
+ name: jsonapi-serializers
186
+ requirement: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - "~>"
189
+ - !ruby/object:Gem::Version
190
+ version: 1.0.0
191
+ type: :development
192
+ prerelease: false
193
+ version_requirements: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - "~>"
196
+ - !ruby/object:Gem::Version
197
+ version: 1.0.0
198
+ description: JSON API(jsonapi.org) serializer that works with rails and can be used
199
+ to serialize any kind of ruby objects
200
+ email: ''
201
+ executables: []
202
+ extensions: []
203
+ extra_rdoc_files:
204
+ - LICENSE.txt
205
+ - README.md
206
+ files:
207
+ - LICENSE.txt
208
+ - README.md
209
+ - lib/extensions/has_one.rb
210
+ - lib/generators/serializer/USAGE
211
+ - lib/generators/serializer/serializer_generator.rb
212
+ - lib/generators/serializer/templates/serializer.rb.tt
213
+ - lib/joyful_jsonapi.rb
214
+ - lib/joyful_jsonapi/attribute.rb
215
+ - lib/joyful_jsonapi/error_serializer.rb
216
+ - lib/joyful_jsonapi/instrumentation.rb
217
+ - lib/joyful_jsonapi/instrumentation/serializable_hash.rb
218
+ - lib/joyful_jsonapi/instrumentation/serialized_json.rb
219
+ - lib/joyful_jsonapi/instrumentation/skylight.rb
220
+ - lib/joyful_jsonapi/instrumentation/skylight/normalizers/base.rb
221
+ - lib/joyful_jsonapi/instrumentation/skylight/normalizers/serializable_hash.rb
222
+ - lib/joyful_jsonapi/instrumentation/skylight/normalizers/serialized_json.rb
223
+ - lib/joyful_jsonapi/link.rb
224
+ - lib/joyful_jsonapi/multi_to_json.rb
225
+ - lib/joyful_jsonapi/object_serializer.rb
226
+ - lib/joyful_jsonapi/railtie.rb
227
+ - lib/joyful_jsonapi/relationship.rb
228
+ - lib/joyful_jsonapi/serialization_core.rb
229
+ - lib/joyful_jsonapi/version.rb
230
+ homepage: https://github.com/agile-tools-org/joyful_jsonapi
231
+ licenses:
232
+ - Apache-2.0
233
+ metadata:
234
+ allowed_push_host: https://rubygems.org
235
+ post_install_message:
236
+ rdoc_options: []
237
+ require_paths:
238
+ - lib
239
+ required_ruby_version: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: 2.0.0
244
+ required_rubygems_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ requirements: []
250
+ rubygems_version: 3.1.2
251
+ signing_key:
252
+ specification_version: 4
253
+ summary: JSON API(jsonapi.org) tools for joyful development, forked from Netfliex/fast_jsonapi
254
+ test_files: []