universalid 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +959 -186
- data/Rakefile +1 -5
- data/config/default.yml +12 -0
- data/config/example.yml +45 -0
- data/lib/universal_id/contrib/active_record/base_message_pack_type.rb +11 -0
- data/lib/universal_id/contrib/active_record/base_packer.rb +130 -0
- data/lib/universal_id/contrib/active_record/base_unpacker.rb +52 -0
- data/lib/universal_id/contrib/active_record/relation_message_pack_type.rb +16 -0
- data/lib/universal_id/contrib/active_record.rb +4 -0
- data/lib/universal_id/contrib/active_support/time_with_zone_message_pack_type.rb +14 -0
- data/lib/universal_id/contrib/active_support.rb +3 -0
- data/lib/universal_id/contrib/global_id/global_id_model.rb +24 -0
- data/lib/universal_id/contrib/global_id/global_id_uid_extension.rb +36 -0
- data/lib/universal_id/contrib/global_id/message_pack_type.rb +15 -0
- data/lib/universal_id/contrib/global_id.rb +3 -0
- data/lib/universal_id/contrib/rails.rb +6 -0
- data/lib/universal_id/contrib/signed_global_id/message_pack_type.rb +8 -0
- data/lib/universal_id/contrib/signed_global_id.rb +3 -0
- data/lib/universal_id/encoder.rb +27 -0
- data/lib/universal_id/message_pack_factory.rb +51 -0
- data/lib/universal_id/message_pack_types/ruby/composites/open_struct.rb +8 -0
- data/lib/universal_id/message_pack_types/ruby/composites/set.rb +9 -0
- data/lib/universal_id/message_pack_types/ruby/composites/struct.rb +23 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/complex.rb +8 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/date.rb +8 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/date_time.rb +8 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/range.rb +20 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/rational.rb +8 -0
- data/lib/universal_id/message_pack_types/ruby/scalars/regexp.rb +15 -0
- data/lib/universal_id/message_pack_types/uri/uid/type.rb +8 -0
- data/lib/universal_id/message_pack_types.rb +24 -0
- data/lib/universal_id/prepack_database_options.rb +63 -0
- data/lib/universal_id/prepack_options.rb +74 -0
- data/lib/universal_id/prepacker.rb +28 -0
- data/lib/universal_id/refinements/array_refinement.rb +17 -0
- data/lib/universal_id/refinements/hash_refinement.rb +19 -0
- data/lib/universal_id/refinements/kernel_refinement.rb +19 -0
- data/lib/universal_id/refinements/open_struct_refinement.rb +12 -0
- data/lib/universal_id/refinements/set_refinement.rb +12 -0
- data/lib/universal_id/refinements.rb +9 -0
- data/lib/universal_id/settings.rb +82 -0
- data/lib/universal_id/version.rb +1 -1
- data/lib/universal_id.rb +25 -10
- data/lib/uri/uid.rb +95 -0
- metadata +105 -28
- data/lib/universal_id/active_model_serializer.rb +0 -53
- data/lib/universal_id/config.rb +0 -18
- data/lib/universal_id/errors.rb +0 -11
- data/lib/universal_id/portable.rb +0 -24
- data/lib/universal_id/portable_hash.rb +0 -85
data/Rakefile
CHANGED
@@ -6,9 +6,5 @@ require "minitest/test_task"
|
|
6
6
|
task default: :test
|
7
7
|
|
8
8
|
Minitest::TestTask.create(:test) do |t|
|
9
|
-
t.test_globs =
|
10
|
-
ARGV[1..]
|
11
|
-
else
|
12
|
-
["test/**/*_test.rb"]
|
13
|
-
end
|
9
|
+
t.test_globs = ENV["GLOBS"] ? ENV["GLOBS"].split(",") : ["test/**/*_test.rb"]
|
14
10
|
end
|
data/config/default.yml
ADDED
data/config/example.yml
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
---
|
2
|
+
##############################################################################################################
|
3
|
+
# Prepack options applied before packing with MessagePack
|
4
|
+
##############################################################################################################
|
5
|
+
prepack:
|
6
|
+
# ..........................................................................................................
|
7
|
+
# A list of attributes to exclude (for objects like Hash, OpenStruct, Struct, etc.)
|
8
|
+
# Takes prescedence over the`include` list
|
9
|
+
exclude: []
|
10
|
+
|
11
|
+
# ..........................................................................................................
|
12
|
+
# A list of attributes to include (for objects like Hash, OpenStruct, Struct, etc.)
|
13
|
+
include: []
|
14
|
+
|
15
|
+
# ..........................................................................................................
|
16
|
+
# Whether or not to omit blank values when packing (nil, {}, [], "", etc.)
|
17
|
+
include_blank: true
|
18
|
+
|
19
|
+
# ==========================================================================================================
|
20
|
+
# Database records
|
21
|
+
database:
|
22
|
+
# ......................................................................................................
|
23
|
+
# Whether or not to include primary/foreign keys
|
24
|
+
# Setting this to `false` can be used to make a copy of an existing record
|
25
|
+
include_keys: true
|
26
|
+
|
27
|
+
# ......................................................................................................
|
28
|
+
# Whether or not to include date/time timestamps (created_at, updated_at, etc.)
|
29
|
+
# Setting this to `false` can be used to make a copy of an existing record
|
30
|
+
include_timestamps: true
|
31
|
+
|
32
|
+
# ......................................................................................................
|
33
|
+
# Whether or not to include unsaved changes
|
34
|
+
# Assign to `true` when packing new records
|
35
|
+
include_unsaved_changes: false
|
36
|
+
|
37
|
+
# ......................................................................................................
|
38
|
+
# Whether or not to include loaded in-memory descendants (i.e. child associations)
|
39
|
+
include_descendants: false
|
40
|
+
|
41
|
+
# ......................................................................................................
|
42
|
+
# The max depth (number) of loaded in-memory descendants to include when `include_descendants == true`
|
43
|
+
# For example, a value of (2) would include the following:
|
44
|
+
# Parent > Child > Grandchild
|
45
|
+
descendant_depth: 0
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_packer"
|
4
|
+
require_relative "base_unpacker"
|
5
|
+
|
6
|
+
UniversalID::MessagePackFactory.register(
|
7
|
+
type: ActiveRecord::Base,
|
8
|
+
recreate_pool: false,
|
9
|
+
packer: ->(obj, packer) { UniversalID::Contrib::ActiveRecordBasePacker.new(obj).pack_with packer },
|
10
|
+
unpacker: ->(unpacker) { UniversalID::Contrib::ActiveRecordBaseUnpacker.unpack_with unpacker }
|
11
|
+
)
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UniversalID::Contrib::ActiveRecordBasePacker
|
4
|
+
using UniversalID::Refinements::HashRefinement
|
5
|
+
|
6
|
+
# TODO: implement support for has_one
|
7
|
+
# ActiveRecord::Reflection::HasOneReflection
|
8
|
+
#
|
9
|
+
# TODO: implement support for has_and_belongs_to_many
|
10
|
+
# ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
11
|
+
#
|
12
|
+
HAS_MANY_ASSOCIATIONS = [
|
13
|
+
ActiveRecord::Reflection::HasManyReflection
|
14
|
+
]
|
15
|
+
|
16
|
+
DESCENDANTS_KEY = "uid:descendants"
|
17
|
+
|
18
|
+
attr_reader :record
|
19
|
+
|
20
|
+
def initialize(record)
|
21
|
+
@record = record
|
22
|
+
end
|
23
|
+
|
24
|
+
def pack_with(packer)
|
25
|
+
packer.write record.class.name
|
26
|
+
packer.write packable_attributes
|
27
|
+
end
|
28
|
+
|
29
|
+
def prepack_options
|
30
|
+
options = record.instance_variable_get(:@_uid_prepack_options)
|
31
|
+
options = UniversalID::PrepackOptions.new unless options.is_a?(UniversalID::PrepackOptions)
|
32
|
+
options
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepack_database_options
|
36
|
+
prepack_options.database_options
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def packable_attributes
|
42
|
+
return record.attributes.slice(record.class.primary_key) if id_only?(prepack_database_options)
|
43
|
+
|
44
|
+
hash = record.attributes
|
45
|
+
reject_keys! hash if prepack_database_options.exclude_keys?
|
46
|
+
reject_timestamps! hash if prepack_database_options.exclude_timestamps?
|
47
|
+
reject_unsaved_changes! hash if prepack_database_options.exclude_unsaved_changes?
|
48
|
+
add_descendants! hash if include_descendants?
|
49
|
+
|
50
|
+
hash.prepack prepack_options
|
51
|
+
end
|
52
|
+
|
53
|
+
# helpers ..................................................................................................
|
54
|
+
def id_only?(prepack_database_options)
|
55
|
+
return false if record.new_record?
|
56
|
+
return false if prepack_database_options.include_descendants?
|
57
|
+
prepack_database_options.exclude_unsaved_changes?
|
58
|
+
end
|
59
|
+
|
60
|
+
def include_descendants?
|
61
|
+
return false unless prepack_database_options.include_descendants?
|
62
|
+
|
63
|
+
max_depth = prepack_database_options.descendant_depth.to_i
|
64
|
+
record_depth = record.instance_variable_get(:@_uid_depth).to_i
|
65
|
+
record_depth < max_depth
|
66
|
+
end
|
67
|
+
|
68
|
+
# attribute mutators .......................................................................................
|
69
|
+
|
70
|
+
def reject_keys!(hash)
|
71
|
+
hash.delete record.class.primary_key
|
72
|
+
foreign_key_column_names.each { |key| hash.delete key }
|
73
|
+
end
|
74
|
+
|
75
|
+
def reject_timestamps!(hash)
|
76
|
+
timestamp_column_names.each { |key| hash.delete key }
|
77
|
+
end
|
78
|
+
|
79
|
+
def reject_unsaved_changes!(hash)
|
80
|
+
record.changes_to_save.each do |key, (original_value, _)|
|
81
|
+
hash[key] = original_value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_descendants!(hash)
|
86
|
+
hash[DESCENDANTS_KEY] ||= {}
|
87
|
+
|
88
|
+
loaded_has_many_relations_by_name.each do |name, relation|
|
89
|
+
descendants = relation.map do |descendant|
|
90
|
+
descendant.instance_variable_set(:@_uid_depth, prepack_database_options.current_depth + 1)
|
91
|
+
UniversalID::Encoder.encode descendant, prepack_options
|
92
|
+
ensure
|
93
|
+
prepack_database_options.decrement_current_depth!
|
94
|
+
descendant.remove_instance_variable :@_uid_depth
|
95
|
+
end
|
96
|
+
hash[DESCENDANTS_KEY][name] = descendants
|
97
|
+
end
|
98
|
+
|
99
|
+
prepack_database_options.increment_current_depth!
|
100
|
+
end
|
101
|
+
|
102
|
+
# active record helpers ....................................................................................
|
103
|
+
|
104
|
+
def timestamp_column_names
|
105
|
+
record.class.all_timestamp_attributes_in_model
|
106
|
+
end
|
107
|
+
|
108
|
+
def foreign_key_column_names
|
109
|
+
record.class.reflections
|
110
|
+
.each_with_object([]) do |(name, reflection), memo|
|
111
|
+
memo << reflection.foreign_key if reflection.macro == :belongs_to
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def associations
|
116
|
+
record.class.reflect_on_all_associations
|
117
|
+
end
|
118
|
+
|
119
|
+
def has_many_associations
|
120
|
+
associations.select { |a| HAS_MANY_ASSOCIATIONS.include? a.class }
|
121
|
+
end
|
122
|
+
|
123
|
+
def loaded_has_many_relations_by_name
|
124
|
+
has_many_associations.each_with_object({}) do |association, memo|
|
125
|
+
relation = record.public_send(association.name)
|
126
|
+
next unless relation.loaded?
|
127
|
+
memo[association.name] = relation
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UniversalID::Contrib::ActiveRecordBaseUnpacker
|
4
|
+
using UniversalID::Refinements::KernelRefinement
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def unpack_with(unpacker)
|
8
|
+
class_name = unpacker.read
|
9
|
+
attributes = unpacker.read || {}
|
10
|
+
create_instance class_name, attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def create_instance(class_name, attributes)
|
16
|
+
klass = const_find(class_name)
|
17
|
+
return nil unless klass
|
18
|
+
|
19
|
+
record = if attributes[klass.primary_key]
|
20
|
+
klass.find_by(klass.primary_key => attributes[klass.primary_key])
|
21
|
+
else
|
22
|
+
klass.new
|
23
|
+
end
|
24
|
+
|
25
|
+
assign_attributes record, attributes
|
26
|
+
assign_descendants record, attributes
|
27
|
+
|
28
|
+
record
|
29
|
+
end
|
30
|
+
|
31
|
+
def assign_attributes(record, attributes)
|
32
|
+
attributes.each do |key, value|
|
33
|
+
record.public_send "#{key}=", value if record.respond_to? "#{key}="
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def assign_descendants(record, attributes)
|
38
|
+
descendants = attributes[UniversalID::Contrib::ActiveRecordBasePacker::DESCENDANTS_KEY] || {}
|
39
|
+
descendants.each do |name, list|
|
40
|
+
next unless record.respond_to?(name) && record.respond_to?("#{name}=")
|
41
|
+
|
42
|
+
models = list.map { |encoded| UniversalID::Encoder.decode encoded }
|
43
|
+
models.compact!
|
44
|
+
next unless models.any?
|
45
|
+
|
46
|
+
# NOTE: ActiveRecord is smart enough to not re-create or re-add
|
47
|
+
# existing records for has_many associations
|
48
|
+
record.public_send "#{name}=", models
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: Revisit ActiveRecord::Relation serialization strategy,
|
4
|
+
# and attempt to optimize without falling back to Marshal.dump/load
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: ActiveRecord::Relation,
|
7
|
+
recreate_pool: false,
|
8
|
+
packer: ->(obj, packer) do
|
9
|
+
# NOTE: packing a relation will reset the loaded state and internal cache of the relation
|
10
|
+
# this ensures minimal payload size
|
11
|
+
obj = obj.dup # clear internal cached state (loaded results, etc.)
|
12
|
+
obj.reset # dup should clear any internal caching, but we call reset just in case
|
13
|
+
packer.write Marshal.dump(obj)
|
14
|
+
end,
|
15
|
+
unpacker: ->(unpacker) { Marshal.load unpacker.read }
|
16
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
UniversalID::MessagePackFactory.register(
|
4
|
+
type: ActiveSupport::TimeWithZone,
|
5
|
+
packer: ->(obj, packer) do
|
6
|
+
packer.write obj.iso8601(9)
|
7
|
+
packer.write obj.zone
|
8
|
+
end,
|
9
|
+
unpacker: ->(unpacker) do
|
10
|
+
time = Time.parse(unpacker.read)
|
11
|
+
zone = unpacker.read
|
12
|
+
ActiveSupport::TimeWithZone.new time, ActiveSupport::TimeZone[zone]
|
13
|
+
end
|
14
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UniversalID::Contrib::GlobalIDModel
|
4
|
+
include GlobalID::Identification
|
5
|
+
|
6
|
+
def self.find(value)
|
7
|
+
new value
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :id, :uid
|
11
|
+
|
12
|
+
def initialize(universal_id)
|
13
|
+
@uid = case universal_id
|
14
|
+
when URI::UID then universal_id
|
15
|
+
when String
|
16
|
+
case universal_id
|
17
|
+
when /\A#{URI::UID::SCHEME}/o then URI::UID.parse(universal_id)
|
18
|
+
else URI::UID.parse(URI::UID.build_string(universal_id))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@id = @uid&.payload
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UniversalID::Contrib::GlobalIDUIDExtension
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def self.included(mixer)
|
7
|
+
mixer.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
# Adds all GlobalID::Identification methods
|
11
|
+
def_delegators(:to_global_id_model, *GlobalID::Identification.instance_methods(false))
|
12
|
+
|
13
|
+
# Returns a UniversalID::Contrib::GlobalIDModel instance
|
14
|
+
# which implements the GlobalID::Identification interface/protocol
|
15
|
+
def to_global_id_model
|
16
|
+
UniversalID::Contrib::GlobalIDModel.new self
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def from_global_id_record(gid_record)
|
21
|
+
gid_record&.find&.uid
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_global_id(gid, options = {})
|
25
|
+
from_global_id_record GlobalID.parse(gid, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :from_gid, :from_global_id
|
29
|
+
|
30
|
+
def from_signed_global_id(sgid, options = {})
|
31
|
+
from_global_id_record SignedGlobalID.parse(sgid, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :from_sgid, :from_signed_global_id
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UniversalID::Contrib::GlobalID; end
|
4
|
+
|
5
|
+
require_relative "global_id_model"
|
6
|
+
require_relative "global_id_uid_extension"
|
7
|
+
|
8
|
+
URI::UID.include UniversalID::Contrib::GlobalIDUIDExtension
|
9
|
+
|
10
|
+
UniversalID::MessagePackFactory.register(
|
11
|
+
type: GlobalID,
|
12
|
+
recreate_pool: false,
|
13
|
+
packer: ->(obj, packer) { packer.write obj.to_param },
|
14
|
+
unpacker: ->(unpacker) { GlobalID.parse unpacker.read }
|
15
|
+
)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "active_record" if defined? ActiveRecord
|
4
|
+
require_relative "active_support" if defined? ActiveSupport
|
5
|
+
require_relative "global_id" if defined? GlobalID
|
6
|
+
require_relative "signed_global_id" if defined? SignedGlobalID
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "brotli"
|
5
|
+
|
6
|
+
# This module provides the ability to encode and decode objects into a compressed, URL-safe string
|
7
|
+
module UniversalID::Encoder
|
8
|
+
class << self
|
9
|
+
def encode(object, options = {})
|
10
|
+
object = UniversalID::Prepacker.prepack(object, options)
|
11
|
+
|
12
|
+
# This is basically the same call as UniversalID::MessagePackFactory.pack(object),
|
13
|
+
# but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
|
14
|
+
packed = UniversalID::MessagePackFactory.msgpack_pool.dump(object)
|
15
|
+
deflated = Brotli.deflate(packed)
|
16
|
+
Base64.urlsafe_encode64 deflated, padding: false
|
17
|
+
end
|
18
|
+
|
19
|
+
def decode(string)
|
20
|
+
decoded = Base64.urlsafe_decode64(string)
|
21
|
+
inflated = Brotli.inflate(decoded)
|
22
|
+
# This is basically the same call as UniversalID::MessagePackFactory.unpack(object),
|
23
|
+
# but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
|
24
|
+
UniversalID::MessagePackFactory.msgpack_pool.load inflated
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "etc"
|
4
|
+
require "msgpack"
|
5
|
+
|
6
|
+
UniversalID::MessagePackFactory = MessagePack::Factory.new.tap do |factory|
|
7
|
+
class << factory
|
8
|
+
attr_reader :msgpack_pool
|
9
|
+
|
10
|
+
def create_msgpack_pool
|
11
|
+
@msgpack_pool = UniversalID::MessagePackFactory.pool([Etc.nprocessors.to_i, 1].max)
|
12
|
+
end
|
13
|
+
|
14
|
+
def register_scalar(type:, recreate_pool: true, **options)
|
15
|
+
register id: next_type_id(order: :asc), type: type, **options
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(type:, id: nil, recreate_pool: true, **options)
|
19
|
+
options[:recursive] = true unless options.key?(:recursive)
|
20
|
+
register_type(id || next_type_id(order: :desc), type, options)
|
21
|
+
create_msgpack_pool if recreate_pool
|
22
|
+
end
|
23
|
+
|
24
|
+
def next_type_id(order:)
|
25
|
+
range = 0..127
|
26
|
+
|
27
|
+
case order
|
28
|
+
when :asc
|
29
|
+
id = range.first
|
30
|
+
id += 1 while type_registered?(id)
|
31
|
+
when :desc
|
32
|
+
id = range.last
|
33
|
+
id -= 1 while type_registered?(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
id = nil unless range.cover?(id)
|
37
|
+
id
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Register MessagePack built-in types
|
43
|
+
UniversalID::MessagePackFactory.register_type MessagePack::Timestamp::TYPE, ::Time,
|
44
|
+
packer: MessagePack::Time::Packer,
|
45
|
+
unpacker: MessagePack::Time::Unpacker
|
46
|
+
|
47
|
+
# Register MessagePack built-in extensions
|
48
|
+
UniversalID::MessagePackFactory.register_type 0x00, ::Symbol
|
49
|
+
|
50
|
+
# Register UniversalID types/extensions
|
51
|
+
require_relative "message_pack_types"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
using UniversalID::Refinements::KernelRefinement
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: Struct,
|
7
|
+
recreate_pool: false,
|
8
|
+
packer: ->(obj, packer) do
|
9
|
+
packer.write obj.class.name
|
10
|
+
packer.write obj.to_h
|
11
|
+
end,
|
12
|
+
|
13
|
+
unpacker: ->(unpacker) do
|
14
|
+
class_name = unpacker.read
|
15
|
+
hash = unpacker.read
|
16
|
+
klass = const_find(class_name)
|
17
|
+
|
18
|
+
# shenanigans to support ::Ruby 3.0.X and 3.1.X
|
19
|
+
RUBY_VERSION.start_with?("3.0", "3.1") ?
|
20
|
+
klass.new.tap { |struct| hash.each { |key, val| struct[key] = hash[key] } } :
|
21
|
+
klass.new(**hash)
|
22
|
+
end
|
23
|
+
)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
UniversalID::MessagePackFactory.register_scalar(
|
4
|
+
type: Range,
|
5
|
+
recreate_pool: false,
|
6
|
+
packer: ->(obj, packer) do
|
7
|
+
packer.write obj.first
|
8
|
+
packer.write obj.to_s.scan(/\.{2,3}/).first
|
9
|
+
packer.write obj.last
|
10
|
+
end,
|
11
|
+
unpacker: ->(unpacker) do
|
12
|
+
first = unpacker.read
|
13
|
+
operator = unpacker.read
|
14
|
+
last = unpacker.read
|
15
|
+
case operator
|
16
|
+
when ".." then first..last
|
17
|
+
when "..." then first...last
|
18
|
+
end
|
19
|
+
end
|
20
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
UniversalID::MessagePackFactory.register_scalar(
|
4
|
+
type: Regexp,
|
5
|
+
recreate_pool: false,
|
6
|
+
packer: ->(obj, packer) do
|
7
|
+
packer.write obj.source
|
8
|
+
packer.write obj.options
|
9
|
+
end,
|
10
|
+
unpacker: ->(unpacker) do
|
11
|
+
source = unpacker.read
|
12
|
+
options = unpacker.read
|
13
|
+
Regexp.new source, options
|
14
|
+
end
|
15
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: MessagePack scans registered type in linear order and first match wins
|
4
|
+
|
5
|
+
# scalars
|
6
|
+
require_relative "message_pack_types/ruby/scalars/complex"
|
7
|
+
require_relative "message_pack_types/ruby/scalars/rational"
|
8
|
+
require_relative "message_pack_types/ruby/scalars/date_time"
|
9
|
+
require_relative "message_pack_types/ruby/scalars/date"
|
10
|
+
require_relative "message_pack_types/ruby/scalars/range"
|
11
|
+
require_relative "message_pack_types/ruby/scalars/regexp"
|
12
|
+
|
13
|
+
# composites
|
14
|
+
require_relative "message_pack_types/ruby/composites/open_struct"
|
15
|
+
require_relative "message_pack_types/ruby/composites/struct"
|
16
|
+
require_relative "message_pack_types/ruby/composites/set"
|
17
|
+
|
18
|
+
# uid
|
19
|
+
require_relative "message_pack_types/uri/uid/type"
|
20
|
+
|
21
|
+
# contribs
|
22
|
+
require_relative "contrib/rails" if defined? Rails
|
23
|
+
|
24
|
+
UniversalID::MessagePackFactory.create_msgpack_pool
|