universalid 0.0.1 → 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 +4 -4
- data/README.md +929 -188
- data/Rakefile +1 -5
- data/config/default.yml +12 -0
- data/config/example.yml +45 -0
- data/contrib/active_record/base_message_pack_type.rb +11 -0
- data/contrib/active_record/base_packer.rb +130 -0
- data/contrib/active_record/base_unpacker.rb +52 -0
- data/contrib/active_record/relation_message_pack_type.rb +16 -0
- data/contrib/active_record.rb +8 -0
- data/contrib/active_support/time_with_zone_message_pack_type.rb +14 -0
- data/contrib/active_support.rb +7 -0
- data/contrib/global_id/global_id_model.rb +24 -0
- data/contrib/global_id/global_id_uid_extension.rb +36 -0
- data/contrib/global_id/message_pack_type.rb +15 -0
- data/contrib/global_id.rb +7 -0
- data/contrib/signed_global_id/message_pack_type.rb +8 -0
- data/contrib/signed_global_id.rb +7 -0
- data/contrib/tags +75 -0
- data/lib/universal_id/contrib.rb +14 -0
- data/lib/universal_id/encoder.rb +27 -0
- data/lib/universal_id/message_pack_factory.rb +37 -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 +26 -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 +23 -10
- data/lib/uri/uid.rb +95 -0
- metadata +106 -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
|
+
)
|
data/contrib/tags
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
!_TAG_EXTRA_DESCRIPTION anonymous /Include tags for non-named objects like lambda/
|
2
|
+
!_TAG_EXTRA_DESCRIPTION fileScope /Include tags of file scope/
|
3
|
+
!_TAG_EXTRA_DESCRIPTION pseudo /Include pseudo tags/
|
4
|
+
!_TAG_EXTRA_DESCRIPTION subparser /Include tags generated by subparsers/
|
5
|
+
!_TAG_FIELD_DESCRIPTION epoch /the last modified time of the input file (only for F\/file kind tag)/
|
6
|
+
!_TAG_FIELD_DESCRIPTION file /File-restricted scoping/
|
7
|
+
!_TAG_FIELD_DESCRIPTION input /input file/
|
8
|
+
!_TAG_FIELD_DESCRIPTION name /tag name/
|
9
|
+
!_TAG_FIELD_DESCRIPTION pattern /pattern/
|
10
|
+
!_TAG_FIELD_DESCRIPTION typeref /Type and name of a variable or typedef/
|
11
|
+
!_TAG_FIELD_DESCRIPTION!Ruby mixin /how the class or module is mixed in (mixin:HOW:MODULE)/
|
12
|
+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
13
|
+
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
14
|
+
!_TAG_KIND_DESCRIPTION!Ruby A,accessor /accessors/
|
15
|
+
!_TAG_KIND_DESCRIPTION!Ruby C,constant /constants/
|
16
|
+
!_TAG_KIND_DESCRIPTION!Ruby L,library /libraries/
|
17
|
+
!_TAG_KIND_DESCRIPTION!Ruby S,singletonMethod /singleton methods/
|
18
|
+
!_TAG_KIND_DESCRIPTION!Ruby a,alias /aliases/
|
19
|
+
!_TAG_KIND_DESCRIPTION!Ruby c,class /classes/
|
20
|
+
!_TAG_KIND_DESCRIPTION!Ruby f,method /methods/
|
21
|
+
!_TAG_KIND_DESCRIPTION!Ruby m,module /modules/
|
22
|
+
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
|
23
|
+
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
|
24
|
+
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
|
25
|
+
!_TAG_OUTPUT_VERSION 0.0 /current.age/
|
26
|
+
!_TAG_PARSER_VERSION!Ruby 0.0 /current.age/
|
27
|
+
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
|
28
|
+
!_TAG_PROC_CWD /Users/nathan/work/hopsoft/universalid/contrib/ //
|
29
|
+
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
|
30
|
+
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
|
31
|
+
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
|
32
|
+
!_TAG_PROGRAM_VERSION 6.0.0 //
|
33
|
+
!_TAG_ROLE_DESCRIPTION!Ruby!library loaded /loaded by "load" method/
|
34
|
+
!_TAG_ROLE_DESCRIPTION!Ruby!library required /loaded by "require" method/
|
35
|
+
!_TAG_ROLE_DESCRIPTION!Ruby!library requiredRel /loaded by "require_relative" method/
|
36
|
+
ActiveRecordPacker active_record/active_record_packer.rb /^class UniversalID::Contrib::ActiveRecordPacker$/;" c module:UniversalID.Contrib
|
37
|
+
ActiveRecordUnpacker active_record/active_record_unpacker.rb /^class UniversalID::Contrib::ActiveRecordUnpacker$/;" c module:UniversalID.Contrib
|
38
|
+
ClassMethods global_id/global_id_uid_extension.rb /^ module ClassMethods$/;" m module:GlobalIDUIDExtension
|
39
|
+
DESCENDANTS_KEY active_record/active_record_packer.rb /^ DESCENDANTS_KEY = "uid:descendants"$/;" C class:ActiveRecordPacker
|
40
|
+
GlobalID global_id/message_pack_type.rb /^module UniversalID::Contrib::GlobalID; end$/;" m module:UniversalID.Contrib
|
41
|
+
GlobalIDModel global_id/global_id_model.rb /^class UniversalID::Contrib::GlobalIDModel$/;" c module:UniversalID.Contrib mixin:include:GlobalID.Identification
|
42
|
+
GlobalIDUIDExtension global_id/global_id_uid_extension.rb /^module UniversalID::Contrib::GlobalIDUIDExtension$/;" m module:UniversalID.Contrib mixin:extend:Forwardable
|
43
|
+
HAS_MANY_ASSOCIATIONS active_record/active_record_packer.rb /^ HAS_MANY_ASSOCIATIONS = [$/;" C class:ActiveRecordPacker
|
44
|
+
add_descendants! active_record/active_record_packer.rb /^ def add_descendants!(hash)$/;" f class:ActiveRecordPacker
|
45
|
+
assign_attributes active_record/active_record_unpacker.rb /^ def assign_attributes(record, attributes)$/;" S class:ActiveRecordUnpacker
|
46
|
+
assign_descendants active_record/active_record_unpacker.rb /^ def assign_descendants(record, attributes)$/;" S class:ActiveRecordUnpacker
|
47
|
+
associations active_record/active_record_packer.rb /^ def associations$/;" f class:ActiveRecordPacker
|
48
|
+
create_instance active_record/active_record_unpacker.rb /^ def create_instance(class_name, attributes)$/;" S class:ActiveRecordUnpacker
|
49
|
+
find global_id/global_id_model.rb /^ def self.find(value)$/;" S class:GlobalIDModel
|
50
|
+
foreign_key_column_names active_record/active_record_packer.rb /^ def foreign_key_column_names$/;" f class:ActiveRecordPacker
|
51
|
+
from_gid global_id/global_id_uid_extension.rb /^ alias_method :from_gid, :from_global_id$/;" a module:GlobalIDUIDExtension.ClassMethods
|
52
|
+
from_global_id global_id/global_id_uid_extension.rb /^ def from_global_id(gid, options = {})$/;" f module:GlobalIDUIDExtension.ClassMethods
|
53
|
+
from_global_id_record global_id/global_id_uid_extension.rb /^ def from_global_id_record(gid_record)$/;" f module:GlobalIDUIDExtension.ClassMethods
|
54
|
+
from_sgid global_id/global_id_uid_extension.rb /^ alias_method :from_sgid, :from_signed_global_id$/;" a module:GlobalIDUIDExtension.ClassMethods
|
55
|
+
from_signed_global_id global_id/global_id_uid_extension.rb /^ def from_signed_global_id(sgid, options = {})$/;" f module:GlobalIDUIDExtension.ClassMethods
|
56
|
+
has_many_associations active_record/active_record_packer.rb /^ def has_many_associations$/;" f class:ActiveRecordPacker
|
57
|
+
id global_id/global_id_model.rb /^ attr_reader :id, :uid$/;" A class:GlobalIDModel
|
58
|
+
id_only? active_record/active_record_packer.rb /^ def id_only?(prepack_database_options)$/;" f class:ActiveRecordPacker
|
59
|
+
include_descendants? active_record/active_record_packer.rb /^ def include_descendants?$/;" f class:ActiveRecordPacker
|
60
|
+
included global_id/global_id_uid_extension.rb /^ def self.included(mixer)$/;" S module:GlobalIDUIDExtension
|
61
|
+
initialize active_record/active_record_packer.rb /^ def initialize(record)$/;" f class:ActiveRecordPacker
|
62
|
+
initialize global_id/global_id_model.rb /^ def initialize(universal_id)$/;" f class:GlobalIDModel
|
63
|
+
loaded_has_many_relations_by_name active_record/active_record_packer.rb /^ def loaded_has_many_relations_by_name$/;" f class:ActiveRecordPacker
|
64
|
+
pack_with active_record/active_record_packer.rb /^ def pack_with(packer)$/;" f class:ActiveRecordPacker
|
65
|
+
packable_attributes active_record/active_record_packer.rb /^ def packable_attributes$/;" f class:ActiveRecordPacker
|
66
|
+
prepack_database_options active_record/active_record_packer.rb /^ def prepack_database_options$/;" f class:ActiveRecordPacker
|
67
|
+
prepack_options active_record/active_record_packer.rb /^ def prepack_options$/;" f class:ActiveRecordPacker
|
68
|
+
record active_record/active_record_packer.rb /^ attr_reader :record$/;" A class:ActiveRecordPacker
|
69
|
+
reject_keys! active_record/active_record_packer.rb /^ def reject_keys!(hash)$/;" f class:ActiveRecordPacker
|
70
|
+
reject_timestamps! active_record/active_record_packer.rb /^ def reject_timestamps!(hash)$/;" f class:ActiveRecordPacker
|
71
|
+
reject_unsaved_changes! active_record/active_record_packer.rb /^ def reject_unsaved_changes!(hash)$/;" f class:ActiveRecordPacker
|
72
|
+
timestamp_column_names active_record/active_record_packer.rb /^ def timestamp_column_names$/;" f class:ActiveRecordPacker
|
73
|
+
to_global_id_model global_id/global_id_uid_extension.rb /^ def to_global_id_model$/;" f module:GlobalIDUIDExtension
|
74
|
+
uid global_id/global_id_model.rb /^ attr_reader :id, :uid$/;" A class:GlobalIDModel
|
75
|
+
unpack_with active_record/active_record_unpacker.rb /^ def unpack_with(unpacker)$/;" S class:ActiveRecordUnpacker
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UniversalID::Contrib; end
|
4
|
+
|
5
|
+
# A list of all MessagePack types in the preferred registration order
|
6
|
+
#
|
7
|
+
# IMPORTANT: More specific types should be registered before more general types
|
8
|
+
# because MessagePack will use the first registered type that matches
|
9
|
+
# MessagePack scans registered type in linear order and first match wins
|
10
|
+
|
11
|
+
require_relative "../../contrib/signed_global_id"
|
12
|
+
require_relative "../../contrib/global_id"
|
13
|
+
require_relative "../../contrib/active_support"
|
14
|
+
require_relative "../../contrib/active_record"
|
@@ -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,37 @@
|
|
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(type:, type_id: nil, recreate_pool: true, **options)
|
15
|
+
id = type_id || next_type_id
|
16
|
+
options[:recursive] = true unless options.key?(:recursive)
|
17
|
+
register_type(id, type, options)
|
18
|
+
create_msgpack_pool if recreate_pool
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_type_id
|
22
|
+
max_type_id = registered_types.map { |type| type[:type].to_i }.max
|
23
|
+
max_type_id.nil? ? 0 : max_type_id + 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Register MessagePack built-in types
|
29
|
+
UniversalID::MessagePackFactory.register_type MessagePack::Timestamp::TYPE, ::Time,
|
30
|
+
packer: MessagePack::Time::Packer,
|
31
|
+
unpacker: MessagePack::Time::Unpacker
|
32
|
+
|
33
|
+
# Register MessagePack built-in extensions
|
34
|
+
UniversalID::MessagePackFactory.register_type 0x00, ::Symbol
|
35
|
+
|
36
|
+
# Register UniversalID types/extensions
|
37
|
+
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
|
+
)
|