universalid 0.1.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +387 -1094
- data/Rakefile +16 -2
- data/config/default.yml +1 -1
- data/config/example.yml +2 -2
- data/lib/universalid/encoder.rb +21 -0
- data/lib/universalid/extensions/active_record/base_message_pack_type.rb +15 -0
- data/lib/universalid/extensions/active_record/base_packer.rb +170 -0
- data/lib/universalid/extensions/active_record/base_unpacker.rb +61 -0
- data/lib/universalid/extensions/active_record/relation_message_pack_type.rb +20 -0
- data/lib/universalid/extensions/active_support/time_with_zone_message_pack_type.rb +18 -0
- data/lib/universalid/extensions/global_id/global_id_model.rb +28 -0
- data/lib/universalid/extensions/global_id/global_id_uid_extension.rb +43 -0
- data/lib/universalid/extensions/global_id/message_pack_type.rb +16 -0
- data/lib/universalid/extensions/signed_global_id/message_pack_type.rb +12 -0
- data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/open_struct.rb +2 -0
- data/lib/universalid/message_pack_types/ruby/scalars/bigdecimal.rb +10 -0
- data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/date.rb +2 -0
- data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/date_time.rb +2 -0
- data/lib/{universal_id → universalid}/message_pack_types.rb +3 -5
- data/lib/universalid/packer.rb +19 -0
- data/lib/{universal_id → universalid}/prepack_database_options.rb +4 -4
- data/lib/{universal_id → universalid}/prepack_options.rb +2 -12
- data/lib/{universal_id → universalid}/prepacker.rb +3 -3
- data/lib/{universal_id → universalid}/refinements/open_struct_refinement.rb +2 -0
- data/lib/universalid/refinements.rb +3 -0
- data/lib/{universal_id → universalid}/settings.rb +3 -2
- data/lib/{universal_id → universalid}/version.rb +1 -1
- data/lib/universalid.rb +28 -1
- data/lib/uri/uid.rb +17 -7
- metadata +128 -50
- data/lib/universal_id/contrib/active_record/base_message_pack_type.rb +0 -11
- data/lib/universal_id/contrib/active_record/base_packer.rb +0 -130
- data/lib/universal_id/contrib/active_record/base_unpacker.rb +0 -50
- data/lib/universal_id/contrib/active_record/relation_message_pack_type.rb +0 -16
- data/lib/universal_id/contrib/active_record.rb +0 -4
- data/lib/universal_id/contrib/active_support/time_with_zone_message_pack_type.rb +0 -14
- data/lib/universal_id/contrib/active_support.rb +0 -3
- data/lib/universal_id/contrib/global_id/global_id_model.rb +0 -24
- data/lib/universal_id/contrib/global_id/global_id_uid_extension.rb +0 -36
- data/lib/universal_id/contrib/global_id/message_pack_type.rb +0 -15
- data/lib/universal_id/contrib/global_id.rb +0 -3
- data/lib/universal_id/contrib/rails.rb +0 -6
- data/lib/universal_id/contrib/signed_global_id/message_pack_type.rb +0 -8
- data/lib/universal_id/contrib/signed_global_id.rb +0 -3
- data/lib/universal_id/encoder.rb +0 -27
- data/lib/universal_id/refinements.rb +0 -8
- data/lib/universal_id.rb +0 -29
- /data/lib/{universal_id → universalid}/message_pack_factory.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/module.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/set.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/struct.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/complex.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/range.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/rational.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/regexp.rb +0 -0
- /data/lib/{universal_id → universalid}/message_pack_types/uri/uid/type.rb +0 -0
- /data/lib/{universal_id → universalid}/refinements/array_refinement.rb +0 -0
- /data/lib/{universal_id → universalid}/refinements/hash_refinement.rb +0 -0
- /data/lib/{universal_id → universalid}/refinements/set_refinement.rb +0 -0
data/Rakefile
CHANGED
@@ -2,9 +2,23 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "minitest/test_task"
|
5
|
+
require "pry-byebug"
|
5
6
|
|
6
7
|
task default: :test
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# take explicit control of test initialization
|
10
|
+
|
11
|
+
task test: [:load_tests, :exec_tests]
|
12
|
+
|
13
|
+
task :load_tests do
|
14
|
+
ENV["TEST_SEED"] ||= ENV.fetch("TEST_SEED", Time.now).to_s
|
15
|
+
require_relative "test/test_extension"
|
16
|
+
|
17
|
+
globs = ENV["GLOBS"] ? ENV["GLOBS"].split(",") : ["test/**/*_test.rb"]
|
18
|
+
files = globs.map { |glob| Dir[glob] }.flatten.shuffle
|
19
|
+
files.each { |file| require_relative file }
|
20
|
+
end
|
21
|
+
|
22
|
+
Minitest::TestTask.create(:exec_tests) do |t|
|
23
|
+
t.test_globs.clear
|
10
24
|
end
|
data/config/default.yml
CHANGED
data/config/example.yml
CHANGED
@@ -13,7 +13,7 @@ prepack:
|
|
13
13
|
include: []
|
14
14
|
|
15
15
|
# ..........................................................................................................
|
16
|
-
# Whether or not to
|
16
|
+
# Whether or not to include blank values when packing (nil, {}, [], "", etc.)
|
17
17
|
include_blank: true
|
18
18
|
|
19
19
|
# ==========================================================================================================
|
@@ -32,7 +32,7 @@ prepack:
|
|
32
32
|
# ......................................................................................................
|
33
33
|
# Whether or not to include unsaved changes
|
34
34
|
# Assign to `true` when packing new records
|
35
|
-
|
35
|
+
include_changes: false
|
36
36
|
|
37
37
|
# ......................................................................................................
|
38
38
|
# Whether or not to include loaded in-memory descendants (i.e. child associations)
|
@@ -0,0 +1,21 @@
|
|
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
|
+
packed = UniversalID::Packer.pack(object, options)
|
11
|
+
deflated = Brotli.deflate(packed)
|
12
|
+
Base64.urlsafe_encode64 deflated, padding: false
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode(string)
|
16
|
+
decoded = Base64.urlsafe_decode64(string)
|
17
|
+
inflated = Brotli.inflate(decoded)
|
18
|
+
UniversalID::Packer.unpack inflated
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveRecord::Base
|
4
|
+
|
5
|
+
require_relative "base_packer"
|
6
|
+
require_relative "base_unpacker"
|
7
|
+
|
8
|
+
UniversalID::MessagePackFactory.register(
|
9
|
+
type: ActiveRecord::Base,
|
10
|
+
recreate_pool: false,
|
11
|
+
packer: ->(obj, packer) { UniversalID::Extensions::ActiveRecordBasePacker.new(obj).pack_with packer },
|
12
|
+
unpacker: ->(unpacker) { UniversalID::Extensions::ActiveRecordBaseUnpacker.unpack_with unpacker }
|
13
|
+
)
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveRecord
|
4
|
+
|
5
|
+
class UniversalID::Extensions::ActiveRecordBasePacker
|
6
|
+
using UniversalID::Refinements::HashRefinement
|
7
|
+
|
8
|
+
# TODO: implement support for has_one
|
9
|
+
# ActiveRecord::Reflection::HasOneReflection
|
10
|
+
#
|
11
|
+
# TODO: implement support for has_and_belongs_to_many
|
12
|
+
# ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
13
|
+
#
|
14
|
+
HAS_MANY_ASSOCIATIONS = [
|
15
|
+
ActiveRecord::Reflection::HasManyReflection
|
16
|
+
]
|
17
|
+
|
18
|
+
DESCENDANTS_KEY = "uid:descendants"
|
19
|
+
|
20
|
+
attr_reader :record
|
21
|
+
|
22
|
+
def initialize(record)
|
23
|
+
@record = record
|
24
|
+
end
|
25
|
+
|
26
|
+
def pack_with(packer)
|
27
|
+
packer.write record.class.name
|
28
|
+
packer.write packable_attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
def prepack_options
|
32
|
+
options = record.instance_variable_get(:@_uid_prepack_options)
|
33
|
+
options = UniversalID::PrepackOptions.new unless options.is_a?(UniversalID::PrepackOptions)
|
34
|
+
options
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepack_database_options
|
38
|
+
prepack_options.database_options
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def packable_attributes
|
44
|
+
hash = if id_only?
|
45
|
+
record.attributes.slice record.class.primary_key
|
46
|
+
else
|
47
|
+
record.attributes.select { |name, _| prepack_options.keep_key? name }.tap do |attrs|
|
48
|
+
reject_keys! attrs if prepack_database_options.exclude_keys?
|
49
|
+
reject_timestamps! attrs if prepack_database_options.exclude_timestamps?
|
50
|
+
reject_unsaved_changes! attrs if prepack_database_options.exclude_changes?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if include_descendants?
|
55
|
+
add_descendants! hash
|
56
|
+
hash.delete DESCENDANTS_KEY if hash[DESCENDANTS_KEY].empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
hash["marked_for_destruction"] = true if record.marked_for_destruction?
|
60
|
+
|
61
|
+
hash.prepack prepack_options
|
62
|
+
end
|
63
|
+
|
64
|
+
# helpers ..................................................................................................
|
65
|
+
def id_only?
|
66
|
+
return false if record.new_record?
|
67
|
+
|
68
|
+
# explicit exclusion of primary key
|
69
|
+
return false if prepack_options.reject_key?(record.class.primary_key)
|
70
|
+
|
71
|
+
# explicit exclusion of all db keys and primary key is not explicitly included
|
72
|
+
return false if prepack_database_options.exclude_keys? && !prepack_options.keep_key?(record.class.primary_key)
|
73
|
+
|
74
|
+
# non-pk attribute names
|
75
|
+
attribute_names = record.attributes.keys - [record.class.primary_key]
|
76
|
+
|
77
|
+
# explicit inclusion of non-pk attributes
|
78
|
+
return false if prepack_options.includes.any? && attribute_names.any? { |attr| prepack_options.includes[attr] }
|
79
|
+
|
80
|
+
# record has unsaved non-pk changes and we want to keep them
|
81
|
+
return false if prepack_database_options.include_changes? && attribute_names.any? { |attr| record.changes[attr] }
|
82
|
+
|
83
|
+
prepack_database_options.include_keys?
|
84
|
+
end
|
85
|
+
|
86
|
+
def include_descendants?
|
87
|
+
return false unless prepack_database_options.include_descendants?
|
88
|
+
|
89
|
+
max_depth = prepack_database_options.descendant_depth.to_i
|
90
|
+
record_depth = record.instance_variable_get(:@_uid_depth).to_i
|
91
|
+
record_depth < max_depth
|
92
|
+
end
|
93
|
+
|
94
|
+
# attribute mutators .......................................................................................
|
95
|
+
|
96
|
+
def reject_keys!(hash)
|
97
|
+
hash.delete record.class.primary_key unless prepack_options.includes[record.class.primary_key]
|
98
|
+
foreign_key_column_names.each { |key| hash.delete(key) unless prepack_options.includes[key] }
|
99
|
+
end
|
100
|
+
|
101
|
+
def reject_timestamps!(hash)
|
102
|
+
timestamp_column_names.each { |key| hash.delete key unless prepack_options.includes[key] }
|
103
|
+
end
|
104
|
+
|
105
|
+
def reject_unsaved_changes!(hash)
|
106
|
+
record.changes_to_save.each do |key, (original_value, _)|
|
107
|
+
hash[key] = original_value if prepack_options.keep_key?(key)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_descendants!(hash)
|
112
|
+
hash[DESCENDANTS_KEY] ||= {}
|
113
|
+
|
114
|
+
has_many_descendant_instances_by_association_name.each do |name, relation|
|
115
|
+
descendants = relation.each_with_object([]) do |descendant, memo|
|
116
|
+
next unless descendant.persisted? || prepack_database_options.include_changes?
|
117
|
+
|
118
|
+
descendant.instance_variable_set(:@_uid_depth, prepack_database_options.current_depth + 1)
|
119
|
+
prepacked = UniversalID::Prepacker.prepack(descendant, prepack_options.to_h)
|
120
|
+
memo << UniversalID::MessagePackFactory.msgpack_pool.dump(prepacked)
|
121
|
+
ensure
|
122
|
+
prepack_database_options.decrement_current_depth!
|
123
|
+
descendant.remove_instance_variable :@_uid_depth
|
124
|
+
end
|
125
|
+
hash[DESCENDANTS_KEY][name.to_s] = descendants
|
126
|
+
end
|
127
|
+
|
128
|
+
prepack_database_options.increment_current_depth!
|
129
|
+
end
|
130
|
+
|
131
|
+
# active record helpers ....................................................................................
|
132
|
+
|
133
|
+
def timestamp_column_names
|
134
|
+
record.class.all_timestamp_attributes_in_model
|
135
|
+
end
|
136
|
+
|
137
|
+
def foreign_key_column_names
|
138
|
+
record.class.reflections
|
139
|
+
.each_with_object([]) do |(name, reflection), memo|
|
140
|
+
memo << reflection.foreign_key if reflection.macro == :belongs_to
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def associations
|
145
|
+
record.class.reflect_on_all_associations
|
146
|
+
end
|
147
|
+
|
148
|
+
def has_many_associations
|
149
|
+
associations.select { |a| HAS_MANY_ASSOCIATIONS.include? a.class }
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns a has of the current in-memory `has_many` associated records keyed by name
|
153
|
+
def has_many_descendant_instances_by_association_name
|
154
|
+
has_many_associations.each_with_object({}) do |association, memo|
|
155
|
+
relation = record.public_send(association.name)
|
156
|
+
|
157
|
+
descendants = Set.new
|
158
|
+
|
159
|
+
# persisted records
|
160
|
+
relation.each { |descendant| descendants << descendant } if relation.loaded?
|
161
|
+
|
162
|
+
# new records
|
163
|
+
relation.target.each { |descendant| descendants << descendant } if relation.target.any?
|
164
|
+
|
165
|
+
memo[association.name] = descendants.to_a if descendants.any?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveRecord
|
4
|
+
|
5
|
+
class UniversalID::Extensions::ActiveRecordBaseUnpacker
|
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 = Object.const_get(class_name) if Object.const_defined?(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
|
+
end
|
22
|
+
record ||= klass.new
|
23
|
+
|
24
|
+
assign_attributes record, attributes.except("marked_for_destruction")
|
25
|
+
record.mark_for_destruction if attributes["marked_for_destruction"]
|
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::Extensions::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 { |packed| UniversalID::MessagePackFactory.msgpack_pool.load(packed) }
|
43
|
+
models.compact!
|
44
|
+
next unless models.any?
|
45
|
+
|
46
|
+
new_models = models.select(&:new_record?)
|
47
|
+
models -= new_models
|
48
|
+
|
49
|
+
# restore persisted models
|
50
|
+
# NOTE: ActiveRecord is smart enough to not re-create or re-add
|
51
|
+
# existing records for has_many associations
|
52
|
+
record.public_send :"#{name}=", models if models.any?
|
53
|
+
|
54
|
+
# restore new unsaved models
|
55
|
+
record.public_send(name).target.concat new_models if new_models.any?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveRecord::Relation
|
4
|
+
|
5
|
+
# TODO: Revisit ActiveRecord::Relation serialization strategy,
|
6
|
+
# and attempt to optimize without falling back to Marshal.dump/load
|
7
|
+
UniversalID::MessagePackFactory.register(
|
8
|
+
type: ActiveRecord::Relation,
|
9
|
+
recreate_pool: false,
|
10
|
+
packer: ->(obj, packer) do
|
11
|
+
# NOTE: packing a relation will reset the loaded state and internal cache of the relation
|
12
|
+
# this ensures minimal payload size
|
13
|
+
obj = obj.dup # clear internal cached state (loaded results, etc.)
|
14
|
+
obj.reset # dup should clear any internal caching, but we call reset just in case
|
15
|
+
packer.write Marshal.dump(obj)
|
16
|
+
end,
|
17
|
+
unpacker: ->(unpacker) { Marshal.load unpacker.read }
|
18
|
+
)
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveSupport::TimeWithZone
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: ActiveSupport::TimeWithZone,
|
7
|
+
packer: ->(obj, packer) do
|
8
|
+
packer.write obj.iso8601(9)
|
9
|
+
packer.write obj.zone
|
10
|
+
end,
|
11
|
+
unpacker: ->(unpacker) do
|
12
|
+
time = Time.parse(unpacker.read)
|
13
|
+
zone = unpacker.read
|
14
|
+
ActiveSupport::TimeWithZone.new time, ActiveSupport::TimeZone[zone]
|
15
|
+
end
|
16
|
+
)
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? GlobalID::Identification
|
4
|
+
|
5
|
+
class UniversalID::Extensions::GlobalIDModel
|
6
|
+
include GlobalID::Identification
|
7
|
+
|
8
|
+
def self.find(value)
|
9
|
+
new value
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :id, :uid
|
13
|
+
|
14
|
+
def initialize(universal_id)
|
15
|
+
@uid = case universal_id
|
16
|
+
when URI::UID then universal_id
|
17
|
+
when String
|
18
|
+
case universal_id
|
19
|
+
when /\A#{URI::UID::SCHEME}/o then URI::UID.parse(universal_id)
|
20
|
+
else URI::UID.parse(URI::UID.build_string(universal_id, self))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@id = @uid&.payload
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined?(GlobalID::Identification) && defined?(SignedGlobalID)
|
4
|
+
|
5
|
+
require "forwardable"
|
6
|
+
require_relative "global_id_model"
|
7
|
+
|
8
|
+
module UniversalID::Extensions::GlobalIDUIDExtension
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def self.included(mixer)
|
12
|
+
mixer.extend ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds all GlobalID::Identification methods
|
16
|
+
def_delegators(:to_global_id_model, *GlobalID::Identification.instance_methods(false))
|
17
|
+
|
18
|
+
# Returns a UniversalID::Extensions::GlobalIDModel instance
|
19
|
+
# which implements the GlobalID::Identification interface/protocol
|
20
|
+
def to_global_id_model
|
21
|
+
UniversalID::Extensions::GlobalIDModel.new self
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def from_global_id_record(gid_record)
|
26
|
+
gid_record&.find&.uid
|
27
|
+
end
|
28
|
+
|
29
|
+
def from_global_id(gid, options = {})
|
30
|
+
from_global_id_record GlobalID.parse(gid, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :from_gid, :from_global_id
|
34
|
+
|
35
|
+
def from_signed_global_id(sgid, options = {})
|
36
|
+
from_global_id_record SignedGlobalID.parse(sgid, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :from_sgid, :from_signed_global_id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? GlobalID
|
4
|
+
|
5
|
+
require_relative "global_id_uid_extension"
|
6
|
+
|
7
|
+
URI::UID.include UniversalID::Extensions::GlobalIDUIDExtension
|
8
|
+
|
9
|
+
UniversalID::MessagePackFactory.register(
|
10
|
+
type: GlobalID,
|
11
|
+
recreate_pool: false,
|
12
|
+
packer: ->(obj, packer) { packer.write obj.to_param },
|
13
|
+
unpacker: ->(unpacker) { GlobalID.parse unpacker.read }
|
14
|
+
)
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? SignedGlobalID
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: SignedGlobalID,
|
7
|
+
recreate_pool: false,
|
8
|
+
packer: ->(obj, packer) { packer.write obj.to_param },
|
9
|
+
unpacker: ->(unpacker) { SignedGlobalID.parse unpacker.read }
|
10
|
+
)
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register_scalar(
|
6
|
+
type: BigDecimal,
|
7
|
+
recreate_pool: false,
|
8
|
+
packer: ->(obj, packer) { packer.write obj.to_s },
|
9
|
+
unpacker: ->(unpacker) { BigDecimal unpacker.read }
|
10
|
+
)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# NOTE: MessagePack scans registered type in linear order and first match wins
|
4
4
|
|
5
5
|
# scalars
|
6
|
+
require_relative "message_pack_types/ruby/scalars/bigdecimal"
|
6
7
|
require_relative "message_pack_types/ruby/scalars/complex"
|
7
8
|
require_relative "message_pack_types/ruby/scalars/rational"
|
8
9
|
require_relative "message_pack_types/ruby/scalars/date_time"
|
@@ -16,10 +17,7 @@ require_relative "message_pack_types/ruby/composites/open_struct"
|
|
16
17
|
require_relative "message_pack_types/ruby/composites/struct"
|
17
18
|
require_relative "message_pack_types/ruby/composites/set"
|
18
19
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
# contribs
|
23
|
-
require_relative "contrib/rails" if defined? Rails
|
20
|
+
# extensions
|
21
|
+
Dir["#{__dir__}/extensions/**/*.rb"].each { |f| require f }
|
24
22
|
|
25
23
|
UniversalID::MessagePackFactory.create_msgpack_pool
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UniversalID::Packer
|
4
|
+
class << self
|
5
|
+
def pack(object, options = {})
|
6
|
+
object = UniversalID::Prepacker.prepack(object, options)
|
7
|
+
|
8
|
+
# This is basically the same call as UniversalID::MessagePackFactory.pack(object),
|
9
|
+
# but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
|
10
|
+
UniversalID::MessagePackFactory.msgpack_pool.dump object
|
11
|
+
end
|
12
|
+
|
13
|
+
def unpack(string)
|
14
|
+
# This is basically the same call as UniversalID::MessagePackFactory.unpack(object),
|
15
|
+
# but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
|
16
|
+
UniversalID::MessagePackFactory.msgpack_pool.load string
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -25,12 +25,12 @@ class UniversalID::PrepackDatabaseOptions
|
|
25
25
|
!include_timestamps?
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
!!@settings.
|
28
|
+
def include_changes?
|
29
|
+
!!@settings.include_changes
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
!
|
32
|
+
def exclude_changes?
|
33
|
+
!include_changes?
|
34
34
|
end
|
35
35
|
|
36
36
|
def descendant_depth
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "prepack_database_options"
|
4
|
-
|
5
3
|
class UniversalID::PrepackOptions
|
6
4
|
attr_reader :excludes, :includes, :database_options
|
7
5
|
|
@@ -37,7 +35,7 @@ class UniversalID::PrepackOptions
|
|
37
35
|
end
|
38
36
|
|
39
37
|
def reject_key?(key)
|
40
|
-
excludes[key.to_s]
|
38
|
+
!!excludes[key.to_s]
|
41
39
|
end
|
42
40
|
|
43
41
|
def keep_value?(value)
|
@@ -57,15 +55,7 @@ class UniversalID::PrepackOptions
|
|
57
55
|
end
|
58
56
|
|
59
57
|
def blank?(value)
|
60
|
-
|
61
|
-
return false if !!value == value # booleans
|
62
|
-
|
63
|
-
result = false
|
64
|
-
result ||= value.empty? if value.respond_to?(:empty?)
|
65
|
-
result ||= value.blank? if value.respond_to?(:blank?)
|
66
|
-
result ||= value.strip.empty? if value.is_a?(String)
|
67
|
-
result ||= value.compact.empty? if value.is_a?(Array) || value.is_a?(Hash)
|
68
|
-
result
|
58
|
+
(value == false) ? false : value.blank?
|
69
59
|
end
|
70
60
|
|
71
61
|
def present?(value)
|
@@ -15,11 +15,11 @@ class UniversalID::Prepacker
|
|
15
15
|
class << self
|
16
16
|
def prepack(object, options = {})
|
17
17
|
options = UniversalID::PrepackOptions.new(options) unless options.is_a?(UniversalID::PrepackOptions)
|
18
|
+
object.instance_variable_set(:@_uid_prepack_options, options) unless object.frozen?
|
18
19
|
|
19
|
-
return object
|
20
|
+
return object unless object.respond_to?(:prepack)
|
20
21
|
|
21
|
-
object.
|
22
|
-
object
|
22
|
+
object.prepack options
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -38,7 +38,8 @@ class UniversalID::Settings
|
|
38
38
|
in database: database then database.each { |k, v| assign k, v, to: to }
|
39
39
|
in include_keys: include_keys then to.prepack.database.include_keys = !!include_keys
|
40
40
|
in include_timestamps: include_timestamps then to.prepack.database.include_timestamps = !!include_timestamps
|
41
|
-
in
|
41
|
+
in include_changes: include_changes then to.prepack.database.include_changes = !!include_changes
|
42
|
+
in include_unsaved_changes: include_changes then to.prepack.database.include_changes = !!include_changes # TODO: Remove in v1.0
|
42
43
|
in include_descendants: include_descendants then to.prepack.database.include_descendants = !!include_descendants
|
43
44
|
in descendant_depth: descendant_depth then to.prepack.database.descendant_depth = descendant_depth
|
44
45
|
else # ignore key
|
@@ -60,7 +61,7 @@ class UniversalID::Settings
|
|
60
61
|
config = self.class.build(**config) unless key == :default
|
61
62
|
registry[key] = config
|
62
63
|
self.class.define_method(key) { config }
|
63
|
-
self.class.define_method("#{key}_copy") { Marshal.load Marshal.dump(config) }
|
64
|
+
self.class.define_method(:"#{key}_copy") { Marshal.load Marshal.dump(config) }
|
64
65
|
self.class.define_singleton_method(key) { instance.public_send key }
|
65
66
|
[key, config]
|
66
67
|
end
|