universalid 0.1.5 → 0.1.7
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 +45 -2
- data/lib/universalid/extensions/active_record/base_unpacker.rb +5 -1
- data/lib/universalid/extensions/active_support/cache/entry_message_pack_type.rb +20 -0
- data/lib/universalid/extensions/active_support/cache/store_message_pack_type.rb +22 -0
- data/lib/universalid/extensions/active_support/time_with_zone_message_pack_type.rb +5 -5
- data/lib/universalid/extensions/global_id/global_id_model.rb +4 -6
- data/lib/universalid/message_pack_types/{ruby/composites → composites}/struct.rb +2 -1
- data/lib/universalid/message_pack_types.rb +12 -12
- data/lib/universalid/version.rb +1 -1
- data/lib/uri/uid.rb +24 -4
- metadata +30 -14
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/module.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/open_struct.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/set.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/bigdecimal.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/complex.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/date.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/date_time.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/range.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/rational.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/regexp.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8adc71d500029efbc4c0c388042413a4676c101da9f2390a300134a6bde37386
|
4
|
+
data.tar.gz: be284fbc808de8624d2ae7b622439380785af1bce3c50d5bb2f80e229472a1c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b40d14b7e93f5559fb5b24f9f47be634a03e083f7d335e951ac9ade937ceca6fc5e382c7e208dcf04d4cd7ab7bb8f0c6ff16748900bca67b60d534afea25651
|
7
|
+
data.tar.gz: 5a15af009bb6a4ea4a148c0787d46b94e795a3debed2af7e9d986a0c0b00b48177fd178653c075a6f28874cb8fc91a3d6446b7a3b891294c396ad0a2538449e4
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<p>
|
4
4
|
<a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
|
5
|
-
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-
|
5
|
+
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-811-47d299.svg" />
|
6
6
|
</a>
|
7
7
|
<a href="https://codeclimate.com/github/hopsoft/universalid/maintainability">
|
8
8
|
<img src="https://api.codeclimate.com/v1/badges/567624cbe733fafc2330/maintainability" />
|
@@ -38,7 +38,7 @@
|
|
38
38
|
Universal ID leverages both [MessagePack](https://msgpack.org/) and [Brotli](https://github.com/google/brotli) _(a combo built for speed and best-in-class data compression)_.
|
39
39
|
When combined, these libraries are up to 30% faster and within 2-5% compression rates compared to Protobuf. <a title="Source" href="https://g.co/bard/share/e5bdb17aee91">↗</a>
|
40
40
|
|
41
|
-
Universal ID
|
41
|
+
Universal ID introduces a paradigm shift that enables straightforward simple [**solutions** ↗](docs/use_cases.md) for a variety of complex problem domains.
|
42
42
|
|
43
43
|
> [!TIP]
|
44
44
|
> All the code examples below can be tested on your local machine. Just clone the repo _(↑or use Gitpod above↑)_ and run `bin/console` to begin exploring.
|
@@ -48,6 +48,7 @@ Universal ID opens the flood gates with a deluge of powerful yet easily implemen
|
|
48
48
|
|
49
49
|
## Table of Contents
|
50
50
|
|
51
|
+
- [URI::UID](#uriuid)
|
51
52
|
- [Supported Data Types](#supported-data-types)
|
52
53
|
- [Primitive Types](#primitive-types)
|
53
54
|
- [Composite Types](#composite-types)
|
@@ -64,6 +65,46 @@ Universal ID opens the flood gates with a deluge of powerful yet easily implemen
|
|
64
65
|
|
65
66
|
<!-- Tocer[finish]: Auto-generated, don't remove. -->
|
66
67
|
|
68
|
+
## URI::UID
|
69
|
+
|
70
|
+
Universal ID introduces a new URI defintion that can recursively serialize any Ruby object into an URL-safe string
|
71
|
+
which can be safely transported via HTTP.
|
72
|
+
|
73
|
+
> [!NOTE]
|
74
|
+
> The payload is optimized to be as small as possible... _especially notable with large objects._
|
75
|
+
|
76
|
+
The best part: **The API is simple.**
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
data = :ANY_OBJECT_YOU_CAN_IMAGINE
|
80
|
+
|
81
|
+
uid = URI::UID.build(data)
|
82
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
83
|
+
|
84
|
+
uid.payload
|
85
|
+
"Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQUdJTkUD"
|
86
|
+
|
87
|
+
uid.fingerprint
|
88
|
+
"CwWAkccHf6ZTeW1ib2wD"
|
89
|
+
|
90
|
+
uri = uid.to_s
|
91
|
+
"uid://universalid/Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQUdJTkUD#CwWAkccHf6ZTeW1ib2wD"
|
92
|
+
|
93
|
+
parsed = URI::UID.parse(uri)
|
94
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
95
|
+
|
96
|
+
parsed.decode
|
97
|
+
:ANY_OBJECT_YOU_CAN_IMAGINE
|
98
|
+
|
99
|
+
# it's also possible to parse the payload by itself
|
100
|
+
|
101
|
+
parsed = URI::UID.from_payload(uid.payload)
|
102
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
103
|
+
|
104
|
+
parsed.decode
|
105
|
+
:ANY_OBJECT_YOU_CAN_IMAGINE
|
106
|
+
```
|
107
|
+
|
67
108
|
## Supported Data Types
|
68
109
|
|
69
110
|
### Primitive Types
|
@@ -165,6 +206,8 @@ The following extension datatypes ship with Universal ID:
|
|
165
206
|
|
166
207
|
- `ActiveRecord::Base`
|
167
208
|
- `ActiveRecord::Relation`
|
209
|
+
- `ActiveSupport::Cache::Entry`
|
210
|
+
- `ActiveSupport::Cache::Store`
|
168
211
|
- `ActiveSupport::TimeWithZone`
|
169
212
|
- `GlobalID`
|
170
213
|
- `SignedGlobalID`
|
@@ -45,6 +45,7 @@ if defined? ActiveRecord
|
|
45
45
|
|
46
46
|
new_models = models.select(&:new_record?)
|
47
47
|
models -= new_models
|
48
|
+
association_collection = record.public_send(name)
|
48
49
|
|
49
50
|
# restore persisted models
|
50
51
|
# NOTE: ActiveRecord is smart enough to not re-create or re-add
|
@@ -52,7 +53,10 @@ if defined? ActiveRecord
|
|
52
53
|
record.public_send :"#{name}=", models if models.any?
|
53
54
|
|
54
55
|
# restore new unsaved models
|
55
|
-
|
56
|
+
association_collection.target.concat new_models if new_models.any?
|
57
|
+
|
58
|
+
# mark association relation as loaded
|
59
|
+
association_collection.proxy_association.instance_variable_set :@loaded, true
|
56
60
|
end
|
57
61
|
end
|
58
62
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveSupport::Cache::Entry
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: ActiveSupport::Cache::Entry,
|
7
|
+
packer: ->(obj, packer) do
|
8
|
+
packer.write obj.value
|
9
|
+
packer.write obj.version
|
10
|
+
packer.write obj.expires_at
|
11
|
+
end,
|
12
|
+
unpacker: ->(unpacker) do
|
13
|
+
value = unpacker.read
|
14
|
+
version = unpacker.read
|
15
|
+
expires_at = unpacker.read
|
16
|
+
ActiveSupport::Cache::Entry.new value, version: version, expires_at: expires_at
|
17
|
+
end
|
18
|
+
)
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? ActiveSupport::Cache::Store
|
4
|
+
|
5
|
+
UniversalID::MessagePackFactory.register(
|
6
|
+
type: ActiveSupport::Cache::Store,
|
7
|
+
packer: ->(obj, packer) do
|
8
|
+
packer.write obj.class.name
|
9
|
+
packer.write obj.options
|
10
|
+
packer.write obj.instance_variable_get(:@data)
|
11
|
+
end,
|
12
|
+
unpacker: ->(unpacker) do
|
13
|
+
class_name = unpacker.read
|
14
|
+
options = unpacker.read
|
15
|
+
data = unpacker.read
|
16
|
+
Object.const_get(class_name).new(options).tap do |store|
|
17
|
+
store.instance_variable_set :@data, data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
)
|
21
|
+
|
22
|
+
end
|
@@ -5,13 +5,13 @@ if defined? ActiveSupport::TimeWithZone
|
|
5
5
|
UniversalID::MessagePackFactory.register(
|
6
6
|
type: ActiveSupport::TimeWithZone,
|
7
7
|
packer: ->(obj, packer) do
|
8
|
-
packer.write obj.
|
9
|
-
packer.write obj.
|
8
|
+
packer.write obj.to_time.utc
|
9
|
+
packer.write obj.time_zone.tzinfo.identifier
|
10
10
|
end,
|
11
11
|
unpacker: ->(unpacker) do
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
utc = unpacker.read
|
13
|
+
tz = unpacker.read
|
14
|
+
utc.in_time_zone ActiveSupport::TimeZone[tz]
|
15
15
|
end
|
16
16
|
)
|
17
17
|
|
@@ -14,14 +14,12 @@ if defined? GlobalID::Identification
|
|
14
14
|
def initialize(universal_id)
|
15
15
|
@uid = case universal_id
|
16
16
|
when URI::UID then universal_id
|
17
|
-
when String
|
18
|
-
|
19
|
-
|
20
|
-
else URI::UID.parse(URI::UID.build_string(universal_id, self))
|
21
|
-
end
|
17
|
+
when String then URI::UID.match?(universal_id) ?
|
18
|
+
URI::UID.parse(universal_id) :
|
19
|
+
URI::UID.from_payload(universal_id)
|
22
20
|
end
|
23
21
|
|
24
|
-
@id =
|
22
|
+
@id = uid&.payload
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
@@ -11,7 +11,8 @@ UniversalID::MessagePackFactory.register(
|
|
11
11
|
unpacker: ->(unpacker) do
|
12
12
|
class_name = unpacker.read
|
13
13
|
hash = unpacker.read
|
14
|
-
klass = Object.const_get(class_name) if Object.const_defined?(class_name)
|
14
|
+
klass = Object.const_get(class_name) if class_name && Object.const_defined?(class_name)
|
15
|
+
klass ||= Struct.new(*hash.keys)
|
15
16
|
|
16
17
|
if klass
|
17
18
|
# shenanigans to support ::Ruby 3.0.X and 3.1.X
|
@@ -3,21 +3,21 @@
|
|
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/
|
7
|
-
require_relative "message_pack_types/
|
8
|
-
require_relative "message_pack_types/
|
9
|
-
require_relative "message_pack_types/
|
10
|
-
require_relative "message_pack_types/
|
11
|
-
require_relative "message_pack_types/
|
12
|
-
require_relative "message_pack_types/
|
6
|
+
require_relative "message_pack_types/scalars/bigdecimal"
|
7
|
+
require_relative "message_pack_types/scalars/complex"
|
8
|
+
require_relative "message_pack_types/scalars/rational"
|
9
|
+
require_relative "message_pack_types/scalars/date_time"
|
10
|
+
require_relative "message_pack_types/scalars/date"
|
11
|
+
require_relative "message_pack_types/scalars/range"
|
12
|
+
require_relative "message_pack_types/scalars/regexp"
|
13
13
|
|
14
14
|
# composites
|
15
|
-
require_relative "message_pack_types/
|
16
|
-
require_relative "message_pack_types/
|
17
|
-
require_relative "message_pack_types/
|
18
|
-
require_relative "message_pack_types/
|
15
|
+
require_relative "message_pack_types/composites/module"
|
16
|
+
require_relative "message_pack_types/composites/open_struct"
|
17
|
+
require_relative "message_pack_types/composites/struct"
|
18
|
+
require_relative "message_pack_types/composites/set"
|
19
19
|
|
20
20
|
# extensions
|
21
|
-
Dir["#{__dir__}/extensions/**/*.rb"].each { |f| require f }
|
21
|
+
Dir["#{__dir__}/extensions/**/*.rb"].sort.each { |f| require f }
|
22
22
|
|
23
23
|
UniversalID::MessagePackFactory.create_msgpack_pool
|
data/lib/universalid/version.rb
CHANGED
data/lib/uri/uid.rb
CHANGED
@@ -9,6 +9,7 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
9
9
|
VERSION = UniversalID::VERSION
|
10
10
|
SCHEME = "uid"
|
11
11
|
HOST = "universalid"
|
12
|
+
PATTERN = /\A#{SCHEME}:\/\/#{HOST}\/[-_0-9A-Z]+#[-_0-9A-Z]+\z/io
|
12
13
|
|
13
14
|
class << self
|
14
15
|
def encoder
|
@@ -29,6 +30,11 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
29
30
|
new(*::URI.split(value))
|
30
31
|
end
|
31
32
|
|
33
|
+
def match?(uri)
|
34
|
+
return true if uri.is_a?(self)
|
35
|
+
uri.to_s.match? PATTERN
|
36
|
+
end
|
37
|
+
|
32
38
|
def build_string(payload, object = nil)
|
33
39
|
"#{SCHEME}://#{HOST}/#{payload}##{fingerprint(object)}"
|
34
40
|
end
|
@@ -38,6 +44,17 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
38
44
|
parse "#{SCHEME}://#{HOST}#{path}##{fingerprint(object)}"
|
39
45
|
end
|
40
46
|
|
47
|
+
def from_payload(payload, object = nil)
|
48
|
+
parse(build_string(payload, object)).tap do |uid|
|
49
|
+
# NOTE: fingerprint mismatch can happen when building from a UID payload
|
50
|
+
# ensure the fingerprint is correct
|
51
|
+
if uid&.valid? && URI::UID.fingerprint(uid.decode) != uid.fingerprint
|
52
|
+
remove_instance_variable :@decoded_fingerprint if instance_variable_defined?(:@decoded_fingerprint)
|
53
|
+
uid.instance_variable_set :@fragment, URI::UID.build(uid.decode).fingerprint
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
41
58
|
def encode(object, options = {})
|
42
59
|
return yield(object, options) if block_given?
|
43
60
|
encoder.encode object, options
|
@@ -99,7 +116,7 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
99
116
|
end
|
100
117
|
|
101
118
|
def fingerprint(decode: false)
|
102
|
-
return decode_fingerprint if decode
|
119
|
+
return @decoded_fingerprint ||= decode_fingerprint if decode
|
103
120
|
fragment
|
104
121
|
end
|
105
122
|
|
@@ -114,11 +131,14 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
114
131
|
!valid?
|
115
132
|
end
|
116
133
|
|
117
|
-
def decode
|
134
|
+
def decode(force: false)
|
118
135
|
return nil unless valid?
|
119
|
-
return yield(decode_payload, *decode_fingerprint) if block_given?
|
120
136
|
|
121
|
-
|
137
|
+
remove_instance_variable :@decoded if force && instance_variable_defined?(:@decoded)
|
138
|
+
return @decoded if defined?(@decoded)
|
139
|
+
|
140
|
+
@decoded ||= yield(decode_payload, *decode_fingerprint) if block_given?
|
141
|
+
@decoded ||= decode_payload
|
122
142
|
end
|
123
143
|
|
124
144
|
def deconstruct_keys(_keys)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: universalid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Hopkins (hopsoft)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -332,6 +332,20 @@ dependencies:
|
|
332
332
|
- - ">="
|
333
333
|
- !ruby/object:Gem::Version
|
334
334
|
version: '1.32'
|
335
|
+
- !ruby/object:Gem::Dependency
|
336
|
+
name: timecop
|
337
|
+
requirement: !ruby/object:Gem::Requirement
|
338
|
+
requirements:
|
339
|
+
- - ">="
|
340
|
+
- !ruby/object:Gem::Version
|
341
|
+
version: '0'
|
342
|
+
type: :development
|
343
|
+
prerelease: false
|
344
|
+
version_requirements: !ruby/object:Gem::Requirement
|
345
|
+
requirements:
|
346
|
+
- - ">="
|
347
|
+
- !ruby/object:Gem::Version
|
348
|
+
version: '0'
|
335
349
|
description: |
|
336
350
|
Universal ID opens the flood gates with a deluge of profoundly powerful
|
337
351
|
yet easily implemented new use-cases for your apps and scripts.
|
@@ -351,6 +365,8 @@ files:
|
|
351
365
|
- lib/universalid/extensions/active_record/base_packer.rb
|
352
366
|
- lib/universalid/extensions/active_record/base_unpacker.rb
|
353
367
|
- lib/universalid/extensions/active_record/relation_message_pack_type.rb
|
368
|
+
- lib/universalid/extensions/active_support/cache/entry_message_pack_type.rb
|
369
|
+
- lib/universalid/extensions/active_support/cache/store_message_pack_type.rb
|
354
370
|
- lib/universalid/extensions/active_support/time_with_zone_message_pack_type.rb
|
355
371
|
- lib/universalid/extensions/global_id/global_id_model.rb
|
356
372
|
- lib/universalid/extensions/global_id/global_id_uid_extension.rb
|
@@ -358,17 +374,17 @@ files:
|
|
358
374
|
- lib/universalid/extensions/signed_global_id/message_pack_type.rb
|
359
375
|
- lib/universalid/message_pack_factory.rb
|
360
376
|
- lib/universalid/message_pack_types.rb
|
361
|
-
- lib/universalid/message_pack_types/
|
362
|
-
- lib/universalid/message_pack_types/
|
363
|
-
- lib/universalid/message_pack_types/
|
364
|
-
- lib/universalid/message_pack_types/
|
365
|
-
- lib/universalid/message_pack_types/
|
366
|
-
- lib/universalid/message_pack_types/
|
367
|
-
- lib/universalid/message_pack_types/
|
368
|
-
- lib/universalid/message_pack_types/
|
369
|
-
- lib/universalid/message_pack_types/
|
370
|
-
- lib/universalid/message_pack_types/
|
371
|
-
- lib/universalid/message_pack_types/
|
377
|
+
- lib/universalid/message_pack_types/composites/module.rb
|
378
|
+
- lib/universalid/message_pack_types/composites/open_struct.rb
|
379
|
+
- lib/universalid/message_pack_types/composites/set.rb
|
380
|
+
- lib/universalid/message_pack_types/composites/struct.rb
|
381
|
+
- lib/universalid/message_pack_types/scalars/bigdecimal.rb
|
382
|
+
- lib/universalid/message_pack_types/scalars/complex.rb
|
383
|
+
- lib/universalid/message_pack_types/scalars/date.rb
|
384
|
+
- lib/universalid/message_pack_types/scalars/date_time.rb
|
385
|
+
- lib/universalid/message_pack_types/scalars/range.rb
|
386
|
+
- lib/universalid/message_pack_types/scalars/rational.rb
|
387
|
+
- lib/universalid/message_pack_types/scalars/regexp.rb
|
372
388
|
- lib/universalid/message_pack_types/uri/uid/type.rb
|
373
389
|
- lib/universalid/packer.rb
|
374
390
|
- lib/universalid/prepack_database_options.rb
|
@@ -404,7 +420,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
404
420
|
- !ruby/object:Gem::Version
|
405
421
|
version: '0'
|
406
422
|
requirements: []
|
407
|
-
rubygems_version: 3.
|
423
|
+
rubygems_version: 3.2.32
|
408
424
|
signing_key:
|
409
425
|
specification_version: 4
|
410
426
|
summary: Fast, recursive, optimized, URL-Safe serialization for any Ruby object
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|