plucker_serializer 0.1.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 +7 -0
- data/lib/plucker/attribute.rb +5 -0
- data/lib/plucker/base.rb +99 -0
- data/lib/plucker/belongs_to.rb +12 -0
- data/lib/plucker/caching.rb +60 -0
- data/lib/plucker/collection.rb +77 -0
- data/lib/plucker/descriptor.rb +44 -0
- data/lib/plucker/field.rb +81 -0
- data/lib/plucker/has_many.rb +11 -0
- data/lib/plucker/has_one.rb +12 -0
- data/lib/plucker/relationship.rb +29 -0
- data/lib/plucker/version.rb +4 -0
- data/lib/plucker_serializer.rb +11 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c5ce3615a6b455cef96024d0dd6d9cfadf30e1af519f88e03496927956a378aa
|
4
|
+
data.tar.gz: ba1b98c83f64a135f9043d92e8ec0b9a5ff785144b774f2f6c78e915b589b75c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 11751d1facbd07d422144a931b2e9487495ba294880dd064b754e3526bfa9212da5d73c3f9d65fff061a281ce9b4d63504167d66bfb02c4f0fa8305984823d6a
|
7
|
+
data.tar.gz: c7e5ceabff25a46edac6e3a2c72b79b1f9f2c92c20bb32486b995de4e3d2ae6ebd608818201c6722514363b390b6978e9b1409f2a5606dbf9ac66b440c2e0c45
|
data/lib/plucker/base.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'caching'
|
3
|
+
require_relative 'descriptor'
|
4
|
+
require_relative 'has_many'
|
5
|
+
require_relative 'belongs_to'
|
6
|
+
require_relative 'has_one'
|
7
|
+
require 'active_support/all'
|
8
|
+
|
9
|
+
module Plucker
|
10
|
+
class Base
|
11
|
+
include Caching
|
12
|
+
|
13
|
+
attr_accessor :object
|
14
|
+
|
15
|
+
with_options instance_writer: false, instance_reader: false do |serializer|
|
16
|
+
serializer.class_attribute :_descriptor
|
17
|
+
self._descriptor ||= Plucker::Descriptor.new(self.class)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.inherited(base)
|
21
|
+
super
|
22
|
+
base._descriptor = Plucker::Descriptor.new(base)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(object, options = {})
|
26
|
+
self.object = object
|
27
|
+
end
|
28
|
+
|
29
|
+
def serializable_hash
|
30
|
+
if self.class.cache_enabled?
|
31
|
+
fetch do
|
32
|
+
get_hash
|
33
|
+
end
|
34
|
+
else
|
35
|
+
get_hash
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias to_hash serializable_hash
|
39
|
+
alias to_h serializable_hash
|
40
|
+
|
41
|
+
def get_hash
|
42
|
+
attributes_hash.merge! associations_hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_json(options = nil)
|
46
|
+
to_h.as_json(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def associations_hash
|
50
|
+
hash = {}
|
51
|
+
self.class._descriptor._relationships.each do |(key, relationship)|
|
52
|
+
next if relationship.excluded?(self)
|
53
|
+
hash[key] = relationship.value(self)
|
54
|
+
end
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def attributes_hash
|
59
|
+
self.class._descriptor._attributes.each_with_object({}) do |(key, attr), hash|
|
60
|
+
next if attr.excluded?(self)
|
61
|
+
hash[key] = attr.value(self)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.is_pluckable?
|
66
|
+
self._descriptor.is_pluckable?
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.pluckable_columns
|
70
|
+
self._descriptor._pluckable_columns
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.attributes(*attrs)
|
74
|
+
attrs.each do |attr|
|
75
|
+
attribute(attr)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.attribute(attr, options = {}, &block)
|
80
|
+
self._descriptor.add_attribute(options.fetch(:key, attr), Plucker::Attribute.new(attr, options, block))
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.belongs_to(attr, options = {}, &block)
|
84
|
+
self._descriptor.add_relationship(options.fetch(:key, attr), Plucker::BelongsTo.new(attr, options, block))
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.has_one(attr, options = {}, &block)
|
88
|
+
self._descriptor.add_relationship(options.fetch(:key, attr), Plucker::HasOne.new(attr, options, block))
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.has_many(attr, options = {}, &block)
|
92
|
+
self._descriptor.add_relationship(options.fetch(:key, attr), Plucker::HasMany.new(attr, options, block))
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.model(attr)
|
96
|
+
self._descriptor.set_model(attr)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "relationship"
|
3
|
+
|
4
|
+
module Plucker
|
5
|
+
class BelongsTo < Plucker::Relationship
|
6
|
+
def value(serializer)
|
7
|
+
relationship_object = self.associated_object(serializer)
|
8
|
+
return nil if relationship_object.blank?
|
9
|
+
relationship_serializer(serializer, relationship_object).new(relationship_object).serializable_hash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
module Plucker
|
5
|
+
module Caching
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
with_options instance_writer: false, instance_reader: false do |serializer|
|
10
|
+
serializer.class_attribute :_cache
|
11
|
+
serializer.class_attribute :_cache_options
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def _cache_digest
|
17
|
+
return @_cache_digest if defined?(@_cache_digest)
|
18
|
+
@_cache_digest = Digest::SHA1.hexdigest(self.name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache(options = {})
|
22
|
+
self._cache = true
|
23
|
+
self._cache_options = options.blank? ? {} : options
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache_store
|
27
|
+
defined?(Rails) && Rails.cache
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_enabled?
|
31
|
+
cache_store.present? && _cache.present?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
### INSTANCE METHODS
|
36
|
+
def fetch
|
37
|
+
if serializer_class.cache_enabled?
|
38
|
+
serializer_class.cache_store.fetch(cache_key, version: cache_version, options: serializer_class._cache_options) do
|
39
|
+
yield
|
40
|
+
end
|
41
|
+
else
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def cache_version
|
47
|
+
return @cache_version if defined?(@cache_version)
|
48
|
+
@cache_version = object.cache_version
|
49
|
+
end
|
50
|
+
|
51
|
+
def cache_key
|
52
|
+
return @cache_key if defined?(@cache_key)
|
53
|
+
@cache_key = object.cache_key + "/" + serializer_class._cache_digest
|
54
|
+
end
|
55
|
+
|
56
|
+
def serializer_class
|
57
|
+
@serializer_class ||= self.class
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Plucker
|
3
|
+
class Collection
|
4
|
+
include Enumerable
|
5
|
+
include Caching
|
6
|
+
|
7
|
+
attr_reader :objects, :serializer_class, :options
|
8
|
+
|
9
|
+
def initialize(objects, options = {})
|
10
|
+
@objects = objects
|
11
|
+
@options = options
|
12
|
+
@serializer_class = get_serialized_model(objects)
|
13
|
+
end
|
14
|
+
|
15
|
+
def serializable_hash
|
16
|
+
if (not objects.is_a?(ActiveRecord::Relation))
|
17
|
+
objects.map do |object|
|
18
|
+
serializer_class.new(object).serializable_hash
|
19
|
+
end.compact
|
20
|
+
else
|
21
|
+
if serializer_class.cache_enabled?
|
22
|
+
fetch do
|
23
|
+
if serializer_class.is_pluckable?
|
24
|
+
associated_hash
|
25
|
+
else
|
26
|
+
objects.map do |object|
|
27
|
+
serializer_class.new(object).serializable_hash
|
28
|
+
end.compact
|
29
|
+
end
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if serializer_class.is_pluckable?
|
33
|
+
associated_hash
|
34
|
+
else
|
35
|
+
objects.map do |object|
|
36
|
+
serializer_class.new(object).serializable_hash
|
37
|
+
end.compact
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
alias to_hash serializable_hash
|
43
|
+
alias to_h serializable_hash
|
44
|
+
|
45
|
+
def as_json(options = nil)
|
46
|
+
to_h.as_json(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def cache_version
|
50
|
+
return @cache_version if defined?(@cache_version)
|
51
|
+
@cache_version = objects.cache_version
|
52
|
+
end
|
53
|
+
|
54
|
+
def cache_key
|
55
|
+
return @cache_key if defined?(@cache_key)
|
56
|
+
@cache_key = objects.cache_key + '/' + serializer_class._cache_digest
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def associated_hash
|
61
|
+
pluck_to_hash(objects, serializer_class.pluckable_columns.to_a)
|
62
|
+
end
|
63
|
+
|
64
|
+
def pluck_to_hash(object, attrs)
|
65
|
+
namespaced_attrs = attrs.map { |attr| object.model.table_name.to_s + "." + attr.to_s }
|
66
|
+
object.pluck(Arel.sql(namespaced_attrs.join(','))).map { |el| attrs.zip(el).to_h }
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_serialized_model(objects)
|
70
|
+
if options[:serializer].blank?
|
71
|
+
"#{objects.klass.name.demodulize.camelize}Serializer".constantize
|
72
|
+
else
|
73
|
+
options[:serializer]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Plucker
|
3
|
+
class Descriptor
|
4
|
+
attr_accessor :_serialized_model, :_attributes, :_relationships, :_pluckable_columns, :_is_pluckable
|
5
|
+
|
6
|
+
def initialize(serializer_class)
|
7
|
+
self._serialized_model = get_serialized_model(serializer_class)
|
8
|
+
self._attributes = {}
|
9
|
+
self._relationships = {}
|
10
|
+
self._pluckable_columns = Set.new([:updated_at])
|
11
|
+
self._is_pluckable = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_pluckable?
|
15
|
+
self._is_pluckable && !self._relationships.present?
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_attribute(key, attr)
|
19
|
+
self._attributes[key] = attr
|
20
|
+
if attr.is_pluckable? && self._serialized_model && self._serialized_model.column_names.include?(attr.name.to_s)
|
21
|
+
self._pluckable_columns << attr.name
|
22
|
+
else
|
23
|
+
self._is_pluckable = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_relationship(key, relationship)
|
28
|
+
self._relationships[key] = relationship
|
29
|
+
self._is_pluckable = false
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_model(model)
|
33
|
+
self._serialized_model = model
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_serialized_model(serializer_class)
|
37
|
+
begin
|
38
|
+
serializer_class.name.split(/Serializer/).first.constantize
|
39
|
+
rescue NameError, LoadError => e
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Plucker
|
3
|
+
Field = Struct.new(:name, :options, :block) do
|
4
|
+
def initialize(*)
|
5
|
+
super
|
6
|
+
validate_condition!
|
7
|
+
end
|
8
|
+
|
9
|
+
def value(serializer)
|
10
|
+
block_value = instance_exec(serializer.object, &block) if block
|
11
|
+
if block && block_value != :nil
|
12
|
+
block_value
|
13
|
+
else
|
14
|
+
if serializer.respond_to?(name)
|
15
|
+
serializer.send(name)
|
16
|
+
else
|
17
|
+
serializer.object.send(name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_pluckable?
|
23
|
+
block.blank?
|
24
|
+
end
|
25
|
+
|
26
|
+
def excluded?(serializer)
|
27
|
+
case condition_type
|
28
|
+
when :if
|
29
|
+
!evaluate_condition(serializer)
|
30
|
+
when :unless
|
31
|
+
evaluate_condition(serializer)
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def validate_condition!
|
39
|
+
return if condition_type == :none
|
40
|
+
|
41
|
+
case condition
|
42
|
+
when Symbol, String, Proc
|
43
|
+
# noop
|
44
|
+
else
|
45
|
+
fail TypeError, "#{condition_type.inspect} should be a Symbol, String or Proc"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def evaluate_condition(serializer)
|
50
|
+
case condition
|
51
|
+
when Symbol
|
52
|
+
serializer.public_send(condition)
|
53
|
+
when String
|
54
|
+
serializer.instance_eval(condition)
|
55
|
+
when Proc
|
56
|
+
if condition.arity.zero?
|
57
|
+
serializer.instance_exec(&condition)
|
58
|
+
else
|
59
|
+
serializer.instance_exec(serializer, &condition)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def condition_type
|
67
|
+
@condition_type ||=
|
68
|
+
if options.key?(:if)
|
69
|
+
:if
|
70
|
+
elsif options.key?(:unless)
|
71
|
+
:unless
|
72
|
+
else
|
73
|
+
:none
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def condition
|
78
|
+
options[condition_type]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "relationship"
|
3
|
+
require_relative "collection"
|
4
|
+
|
5
|
+
module Plucker
|
6
|
+
class HasMany < Plucker::Relationship
|
7
|
+
def value(serializer)
|
8
|
+
Plucker::Collection.new(self.associated_object(serializer), serializer: relationship_serializer(serializer)).serializable_hash
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "relationship"
|
3
|
+
|
4
|
+
module Plucker
|
5
|
+
class HasOne < Plucker::Relationship
|
6
|
+
def value(serializer)
|
7
|
+
relationship_object = self.associated_object(serializer)
|
8
|
+
return nil if relationship_object.blank?
|
9
|
+
relationship_serializer(serializer, relationship_object).new(relationship_object).serializable_hash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'collection'
|
3
|
+
require_relative "field"
|
4
|
+
|
5
|
+
module Plucker
|
6
|
+
class Relationship < Plucker::Field
|
7
|
+
def associated_object(serializer)
|
8
|
+
serializer.object.send(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def value(serializer)
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def relationship_serializer(serializer, relationship_object=nil)
|
17
|
+
if self.options[:serializer].blank?
|
18
|
+
if relationship_object.present?
|
19
|
+
association_class = relationship_object.class.name
|
20
|
+
else
|
21
|
+
association_class = serializer.object.class.reflect_on_association(self.name.to_sym).class_name
|
22
|
+
end
|
23
|
+
"#{association_class.demodulize.camelize}Serializer".constantize
|
24
|
+
else
|
25
|
+
self.options[:serializer]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "plucker/version"
|
2
|
+
require "plucker/base"
|
3
|
+
require "plucker/attribute"
|
4
|
+
require "plucker/collection"
|
5
|
+
require "plucker/relationship"
|
6
|
+
require "plucker/belongs_to"
|
7
|
+
require "plucker/has_one"
|
8
|
+
require "plucker/has_many"
|
9
|
+
require "plucker/field"
|
10
|
+
require "plucker/descriptor"
|
11
|
+
require "plucker/caching"
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plucker_serializer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Henry Boisgibault
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: A blazing fast JSON serializer
|
28
|
+
email: henry@logora.fr
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/plucker/attribute.rb
|
34
|
+
- lib/plucker/base.rb
|
35
|
+
- lib/plucker/belongs_to.rb
|
36
|
+
- lib/plucker/caching.rb
|
37
|
+
- lib/plucker/collection.rb
|
38
|
+
- lib/plucker/descriptor.rb
|
39
|
+
- lib/plucker/field.rb
|
40
|
+
- lib/plucker/has_many.rb
|
41
|
+
- lib/plucker/has_one.rb
|
42
|
+
- lib/plucker/relationship.rb
|
43
|
+
- lib/plucker/version.rb
|
44
|
+
- lib/plucker_serializer.rb
|
45
|
+
homepage:
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 2.5.0
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubygems_version: 3.1.2
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: A blazing fast JSON serializer with an easy-to-use API
|
68
|
+
test_files: []
|