joyful_jsonapi 0.0.1

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.
@@ -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: []