universalid 0.1.2 → 0.1.4
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 +387 -1087
- 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_unsaved_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
|