universalid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '043986f0e3474969c1d0ff0cbf97749d10a4fbb78ccd49296347b9d9e49935b6'
4
+ data.tar.gz: b43b1fa649dd682d12d8cb8e3fc373aaf2dcd7d98f64c7e36ad91747a130200d
5
+ SHA512:
6
+ metadata.gz: 94768a2128d5b16c7eb2e4989b64c9a9bab603c33f1cf696429c67e8ad4321a888f8df1c68d19f2c74941265a6fa9a6081527970c52a87a1ba325fd7e9558d3a
7
+ data.tar.gz: fddaae3b8892726c533ea1527cb6040c215057df342ea6542f4ba669878badf270b13a6369b490abc021fe77da4871a0773cdaff093777c511a79c84a4ac3539
data/README.md ADDED
@@ -0,0 +1,278 @@
1
+ <p align="center">
2
+ <h1 align="center">Universal ID 🌌</h1>
3
+ <p align="center">
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-165-47d299.svg" />
6
+ </a>
7
+ <a href="https://codeclimate.com/github/hopsoft/universalid/maintainability">
8
+ <img src="https://api.codeclimate.com/v1/badges/80bcd3acced072534a3a/maintainability" />
9
+ </a>
10
+ <a href="https://rubygems.org/gems/universalid">
11
+ <img alt="GEM Version" src="https://img.shields.io/gem/v/universalid?color=168AFE&include_prereleases&logo=ruby&logoColor=FE1616">
12
+ </a>
13
+ <a href="https://rubygems.org/gems/universalid">
14
+ <img alt="GEM Downloads" src="https://img.shields.io/gem/dt/universalid?color=168AFE&logo=ruby&logoColor=FE1616">
15
+ </a>
16
+ <a href="https://github.com/testdouble/standard">
17
+ <img alt="Ruby Style" src="https://img.shields.io/badge/style-standard-168AFE?logo=ruby&logoColor=FE1616" />
18
+ </a>
19
+ <a href="https://github.com/hopsoft/universalid/actions/workflows/tests.yml">
20
+ <img alt="Tests" src="https://github.com/hopsoft/universalid/actions/workflows/tests.yml/badge.svg" />
21
+ </a>
22
+ <a href="https://github.com/sponsors/hopsoft">
23
+ <img alt="Sponsors" src="https://img.shields.io/github/sponsors/hopsoft?color=eb4aaa&logo=GitHub%20Sponsors" />
24
+ </a>
25
+ <br>
26
+ <a href="https://ruby.social/@hopsoft">
27
+ <img alt="Ruby.Social Follow" src="https://img.shields.io/mastodon/follow/000008274?domain=https%3A%2F%2Fruby.social&label=%40hopsoft&style=social">
28
+ </a>
29
+ <a href="https://twitter.com/hopsoft">
30
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/url?label=%40hopsoft&style=social&url=https%3A%2F%2Ftwitter.com%2Fhopsoft">
31
+ </a>
32
+ </p>
33
+ <h2 align="center">GlobalID support for Array, Hash, ActiveRecord::Relation, and more.</h2>
34
+ <h3 align="center">Simple, standardized, secure marshaling for peace-of-mind object portability</h3>
35
+ </p>
36
+
37
+ Portability has never been easier.
38
+ UniversalID simplifies marshaling by bringing [`GlobalID`'s](https://github.com/rails/globalid) powerful features to more Ruby objects...
39
+ including unsaved ActiveModels. 🤯
40
+
41
+ ## Sponsors
42
+
43
+ <p align="center">
44
+ <em>Proudly sponsored by</em>
45
+ </p>
46
+ <p align="center">
47
+ <a href="https://www.clickfunnels.com?utm_source=hopsoft&utm_medium=open-source&utm_campaign=universalid">
48
+ <img src="https://images.clickfunnel.com/uploads/digital_asset/file/176632/clickfunnels-dark-logo.svg" width="575" />
49
+ </a>
50
+ </p>
51
+
52
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
53
+
54
+ ## Table of Contents
55
+
56
+ - [What is Global ID?](#what-is-global-id)
57
+ - [Global ID Examples](#global-id-examples)
58
+ - [What is Universal ID?](#what-is-universal-id)
59
+ - [Why Expand Global ID?](#why-expand-global-id)
60
+ - [Summary of Benefits](#summary-of-benefits)
61
+ - [GlobalID](#globalid)
62
+ - [SignedGlobalID](#signedglobalid)
63
+ - [Hash](#hash)
64
+ - [ActiveModel](#activemodel)
65
+ - [Benchmarks](#benchmarks)
66
+ - [License](#license)
67
+
68
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
69
+
70
+ ## What is Global ID?
71
+
72
+ A GlobalID is an URI that uniquely identifies a model instance.
73
+ It was designed to make ActiveRecord models portable across process boundaries.
74
+ For example, passing a model instance as an argument _(from the web server)_ to a background job.
75
+ They also facilitate use-cases like interleaved search results that mix multiple classes into a single unified result.
76
+
77
+ GlobalIDs can also be [signed](https://github.com/rails/globalid#signed-global-ids) and dedicated to a
78
+ [purpose](https://github.com/rails/globalid#signed-global-ids) with an [expiration](https://github.com/rails/globalid#signed-global-ids) policy.
79
+
80
+ ### Global ID Examples
81
+
82
+ ```ruby
83
+ # Basic GlobalIDs
84
+ campaign = Campaign.create(name: "Example")
85
+ campaign.to_gid #............ #<GlobalID:0x000... @uri=#<URI::GID gid://UniversalID/Campaign/1>>
86
+ campaign.to_gid.uri #........ #<URI::GID gid://UniversalID/Campaign/1>
87
+ campaign.to_gid.to_s #....... gid://UniversalID/Campaign/1
88
+ campaign.to_gid_param #...... Z2lkOi8vVW5pdmVyc2FsSUQvQ2FtcGFpZ24vMQ
89
+
90
+ gid = GlobalID.parse("Z2lkOi8vVW5pdmVyc2FsSUQvQ2FtcGFpZ24vMQ") # #<GlobalID:0x000... @uri=#<URI::GID gid://UniversalID/Campaign/1>>
91
+ campaign == gid.find # true
92
+ ```
93
+
94
+ ```ruby
95
+ # Signed GlobalIDs
96
+ campaign = Campaign.create(name: "Example")
97
+ campaign.to_sgid #......... #<SignedGlobalID:0x000... @expires_at=nil, @purpose="...", @uri=#<URI::GID gid://UniversalID/Campaign/1>, ...>
98
+ campaign.to_sgid_param #... BAh7CEkiCGdpZAY6BkVUSSIhZ2lkOi8vVW...
99
+
100
+ sgid = SignedGlobalID.parse("BAh7CEkiCGdpZAY6BkVUSSIhZ2lkOi8vVW...") # #<SignedGlobalID:0x000... @expires_at=nil, @purpose="...", @uri=#<URI::GID gid://UniversalID/Campaign/1>
101
+ campaign == sgid.find # true
102
+ ```
103
+
104
+ ## What is Universal ID?
105
+
106
+ UniversalID extends GlobalID functionality to more objects.
107
+
108
+ - Array
109
+ - Hash
110
+ - ActiveModel _(unsaved)_
111
+ - ActiveRecord::Relation
112
+ - etc.
113
+
114
+ ### Why Expand Global ID?
115
+
116
+ A variety of additional use-cases can be handled easily _(with minimal code)_ by extending GlobalID to objects like Hash.
117
+ Consider a multi-step form or wizard where users incrementally build up a complex set of related ActiveRecord instances.
118
+
119
+ - When do we save to the database?
120
+ - What about validations? Will they be a problem if we save early?
121
+ - Should we persist the data in cache instead?
122
+ - What if the user abandons the process?
123
+ - How do we cleanup abandoned data?
124
+ - Should we consider full-stack-frontend to manage state client side before saving? 😱
125
+
126
+ **Don't fret!** UniversalID supports safely marshaling unsaved ActiveModels between steps.
127
+
128
+ ```ruby
129
+ # 1. Start multi-step form (partial data)
130
+ campaign = Campaign.new(name: "Example") #....... unsaved data
131
+ param = campaign.to_portable_hash_sgid_param #... make it portable (assign this to a hidden field, querystrig etc.)
132
+
133
+ # HTTP request / crossing a process boundary / etc.
134
+
135
+ # Step 2. Continue multi-step form (enrich partial data)
136
+ campaign = Campaign.new_from_portable_hash(param) #................................... unsaved data
137
+ campaign.emails << campaign.emails.build(subject: "First Email", body: "Welcome") #... unsaved data
138
+ param = campaign.to_portable_hash_sgid_param #........................................ make it portable
139
+
140
+ # HTTP request / crossing a process boundary / etc.
141
+
142
+ # Step 3-N. Continue multi-step form (enrich partial data)
143
+ # ...
144
+
145
+ # Final Step. Save the data
146
+ campaign = Campaign.new_from_portable_hash(param)
147
+ campaign.save!
148
+ ```
149
+
150
+ **And... this is just one use-case!**
151
+
152
+ UniversalID can be used to solve a multitude of problems with minimal effort.
153
+
154
+ - Sharable artifacts (reports, configurations, etc.)
155
+ - Data versioning
156
+ - Digital products
157
+ - etc. *the only limit is your imagination*
158
+
159
+ ### Summary of Benefits
160
+
161
+ #### GlobalID
162
+
163
+ - Standardizes marshaling _(fewer bespoke solutions)_
164
+ - Reduces complexity and lines-of-code
165
+ - Simplifies derivative works _(works with existing data-models/object-structures)_
166
+ - Encapsulates portability across processes and systems _(self-contained)_
167
+ - Creates opportunity for generic solutions _(meta-programming)_
168
+ - Minimizes data storage needs for incomplete or ephemeral data _(URL safe string)_
169
+ - Facilitates easy rollback when incomplete or ephemeral data is abandoned
170
+
171
+ #### SignedGlobalID
172
+
173
+ - Enhances security _(can't be tampered with, prevents MITM attacks, etc.)_
174
+ - Provides scoping for a specific purpose _(via `for`)_
175
+ - Supports versioning _(via purpose)_
176
+ - Includes scarcity _(via expiration)_
177
+ - Enables productization _(an SGID string is a digital "product")_
178
+
179
+ ### Hash
180
+
181
+ ```ruby
182
+ hash = {name: "Example", list: [1,2,3], object: {nested: true}}
183
+ portable = UniversalID::PortableHash.new(hash)
184
+ gid_param = portable.to_gid_param #..... Z2lkOi8vVW5pdmVyc2FsSUQvVW5pdmVyc2FsSUQ6OlBvcnRhYmxlSGFzaC9lTnFyVnNwTHpFMVZzbEp5clVqTUxj...
185
+ sgid_param = portable.to_sgid_param #... BAh7CEkiCGdpZAY6BkVUSSIBg2dpZDovL1VuaXZlcnNhbElEL1VuaXZlcnNhbElEOjpQb3J0YWJsZUhhc2gvZU5x...
186
+
187
+
188
+ UniversalID::PortableHash.parse_gid(gid_param).find
189
+ {"name"=>"Example", "list"=>[1, 2, 3], "object"=>{"nested"=>true}}
190
+
191
+
192
+ UniversalID::PortableHash.parse_gid(sgid_param).find
193
+ {"name"=>"Example", "list"=>[1, 2, 3], "object"=>{"nested"=>true}}
194
+ ```
195
+
196
+ ### ActiveModel
197
+
198
+ ```ruby
199
+ email = Email.new(subject: "Example", body: "Hi there...") # unsaved
200
+ gid = email.to_portable_hash_gid_param #..... Z2lkOi8vVW5pdmVyc2FsSUQvVW5pdmVyc2FsSUQ6OlBvcnRhYmxlSGFzaC9lTnFyVmlvdVRjcEtUUz...
201
+ sgid = email.to_portable_hash_sgid_param #... BAh7CEkiCGdpZAY6BkVUSSJzZ2lkOi8vVW5pdmVyc2FsSUQvVW5pdmVyc2FsSUQ6OlBvcnRhYmxlSG...
202
+
203
+ copy = Email.new_from_portable_hash(gid)
204
+ signed_copy = Email.new_from_portable_hash(sgid)
205
+
206
+ email.save!
207
+
208
+ options = {portable_hash_options: {except: [:id, :created_at, :updated_at]}}
209
+ # NOTE: Options can be configured globally via UniversalID.config.portable_hash
210
+
211
+ gid = email.to_portable_hash_gid_param(options)
212
+ sgid = email.to_portable_hash_gid_param(options)
213
+
214
+ # Copies are new records and don't include values for id, created_at, or updated_at
215
+ copy = Email.new_from_portable_hash(gid)
216
+ signed_copy = Email.new_from_portable_hash(sgid)
217
+ ```
218
+
219
+ ### Benchmarks
220
+
221
+ ```
222
+ # Simple Campaign with 3 associated Email records (nested attributes)
223
+ ==================================================================================================
224
+ Benchmarking 10000 iterations
225
+ ==================================================================================================
226
+ user system total real
227
+ PortableHash.new 0.194428 0.000358 0.194786 ( 0.194788)
228
+ Average 0.000019 0.000000 0.000019 ( 0.000019)
229
+ ..................................................................................................
230
+ user system total real
231
+ PortableHash.new w/ options 0.191249 0.000421 0.191670 ( 0.191677)
232
+ Average 0.000019 0.000000 0.000019 ( 0.000019)
233
+ ..................................................................................................
234
+ user system total real
235
+ PortableHash.find 0.061181 0.002219 0.063400 ( 0.063401)
236
+ Average 0.000006 0.000000 0.000006 ( 0.000006)
237
+ ..................................................................................................
238
+ user system total real
239
+ PortableHash#id 0.342809 0.001674 0.344483 ( 0.344494)
240
+ Average 0.000034 0.000000 0.000034 ( 0.000034)
241
+ ..................................................................................................
242
+ user system total real
243
+ PortableHash#to_gid 0.422914 0.001586 0.424500 ( 0.424498)
244
+ Average 0.000042 0.000000 0.000042 ( 0.000042)
245
+ ..................................................................................................
246
+ user system total real
247
+ PortableHash#to_gid_param 0.437669 0.001826 0.439495 ( 0.439509)
248
+ Average 0.000044 0.000000 0.000044 ( 0.000044)
249
+ ..................................................................................................
250
+ user system total real
251
+ PortableHash#to_sgid 0.430941 0.002055 0.432996 ( 0.433020)
252
+ Average 0.000043 0.000000 0.000043 ( 0.000043)
253
+ ..................................................................................................
254
+ user system total real
255
+ PortableHash#to_sgid_param 0.492965 0.001968 0.494933 ( 0.494978)
256
+ Average 0.000049 0.000000 0.000049 ( 0.000049)
257
+ ..................................................................................................
258
+ user system total real
259
+ ActiveModelSerializer.new_from_portable_hash 3.009667 0.006939 3.016606 ( 3.017388)
260
+ Average 0.000301 0.000001 0.000302 ( 0.000302)
261
+ ..................................................................................................
262
+ user system total real
263
+ ActiveModelSerializer.new_from_portable_hash (signed) 3.189282 0.006650 3.195932 ( 3.196076)
264
+ Average 0.000319 0.000001 0.000320 ( 0.000320)
265
+ ..................................................................................................
266
+ user system total real
267
+ ActiveModelSerializer#to_portable_hash_gid_param 0.923773 0.002653 0.926426 ( 0.926427)
268
+ Average 0.000092 0.000000 0.000093 ( 0.000093)
269
+ ..................................................................................................
270
+ user system total real
271
+ ActiveModelSerializer#to_portable_hash_sgid_param 0.991336 0.002782 0.994118 ( 0.994133)
272
+ Average 0.000099 0.000000 0.000099 ( 0.000099)
273
+ ..................................................................................................
274
+ ```
275
+
276
+ ## License
277
+
278
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ task default: :test
7
+
8
+ Minitest::TestTask.create(:test) do |t|
9
+ t.test_globs = if ARGV.size > 1
10
+ ARGV[1..]
11
+ else
12
+ ["test/**/*_test.rb"]
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UniversalID::ActiveModelSerializer
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ class_methods do
8
+ def new_from_portable_hash(value, options = {})
9
+ hash = if value.is_a?(UniversalID::PortableHash)
10
+ value
11
+ else
12
+ gid = UniversalID::PortableHash.parse_gid(value.to_s, options)
13
+ gid&.find || {portable_hash_error: "Invalid or expired UniversalID::PortableHash! #{value.to_s.inspect}"}
14
+ end
15
+ new hash
16
+ rescue => error
17
+ new(portable_hash_error: "Invalid or expired UniversalID::PortableHash! #{error.inspect}")
18
+ end
19
+ end
20
+
21
+ included do
22
+ validate { errors.add(:base, portable_hash_error) if portable_hash_error }
23
+ end
24
+
25
+ attr_accessor :portable_hash_error
26
+
27
+ def to_portable_hash(options = {})
28
+ portable_hash_options = options.delete(:portable_hash_options)
29
+ UniversalID::PortableHash.new as_json(options).merge(portable_hash_options: portable_hash_options)
30
+ end
31
+
32
+ def to_portable_hash_global_id(options = {})
33
+ gid_options = options.delete(:gid_options) || {}
34
+ to_portable_hash(options).to_gid(gid_options)
35
+ end
36
+ alias_method :to_portable_hash_gid, :to_portable_hash_global_id
37
+
38
+ def to_portable_hash_global_id_param(options = {})
39
+ to_portable_hash_gid(options).to_param
40
+ end
41
+ alias_method :to_portable_hash_gid_param, :to_portable_hash_global_id_param
42
+
43
+ def to_portable_hash_signed_global_id(options = {})
44
+ gid_options = options.delete(:gid_options) || {}
45
+ to_portable_hash(options).to_sgid(gid_options)
46
+ end
47
+ alias_method :to_portable_hash_sgid, :to_portable_hash_signed_global_id
48
+
49
+ def to_portable_hash_signed_global_id_param(options = {})
50
+ to_portable_hash_sgid(options).to_param
51
+ end
52
+ alias_method :to_portable_hash_sgid_param, :to_portable_hash_signed_global_id_param
53
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UniversalID
4
+ def self.config
5
+ @config ||= begin
6
+ options = ActiveSupport::OrderedOptions.new
7
+
8
+ # Default options for UniversalID::PortableHash ........................................................
9
+ options.portable_hash = {
10
+ allow_blank: false,
11
+ only: [], # keys to include (trumps except)
12
+ except: [] # keys to exclude
13
+ }
14
+
15
+ options
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UniversalID::LocatorError < StandardError
4
+ attr_reader :id, :cause
5
+
6
+ def initialize(id = nil, cause = nil)
7
+ super "Failed to locate the id #{id.inspect} Cause: #{cause.inspect}"
8
+ @id = id
9
+ @cause = cause
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UniversalID::Portable
4
+ extend ActiveSupport::Concern
5
+ include GlobalID::Identification
6
+
7
+ class_methods do
8
+ def config
9
+ UniversalID.config
10
+ end
11
+
12
+ def parse_gid(gid, options = {})
13
+ GlobalID.parse(gid, options) || SignedGlobalID.parse(gid, options)
14
+ end
15
+
16
+ def find(id)
17
+ raise NotImplementedError
18
+ end
19
+ end
20
+
21
+ def id
22
+ raise NotImplementedError
23
+ end
24
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UniversalID::PortableHash < Hash
4
+ include UniversalID::Portable
5
+
6
+ class << self
7
+ def config
8
+ super.portable_hash.with_indifferent_access
9
+ end
10
+
11
+ def find(id)
12
+ compressed_json = Base64.urlsafe_decode64(id)
13
+ JSON.parse Zlib::Inflate.inflate(compressed_json)
14
+ rescue => error
15
+ raise UniversalID::LocatorError.new(id, error)
16
+ end
17
+
18
+ def deep_transform(hash, options)
19
+ include_list = options[:only]
20
+ exclude_list = options[:except]
21
+ hash.each_with_object({}) do |(key, value), memo|
22
+ key = key.to_s
23
+ next if include_list.any? && incldue_list.none?(key)
24
+ next if exclude_list.any?(key)
25
+ transform(value, options: options) { |val| memo[key] = val }
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def transform(value, options:)
32
+ value = case value
33
+ when Hash then deep_transform(value, options)
34
+ when Array then value.map { |val| transform(val, options: options) }
35
+ else value
36
+ end
37
+
38
+ if block_given?
39
+ yield value if value.present? || options[:allow_blank]
40
+ end
41
+
42
+ value
43
+ end
44
+ end
45
+
46
+ delegate :config, to: :"self.class"
47
+ attr_reader :options
48
+
49
+ def initialize(hash)
50
+ @options = merge_options!(extract_options!(hash))
51
+ merge! self.class.deep_transform(hash, options)
52
+ end
53
+
54
+ def id
55
+ compressed_json = Zlib::Deflate.deflate(to_json, Zlib::BEST_COMPRESSION)
56
+ Base64.urlsafe_encode64 compressed_json, padding: false
57
+ end
58
+
59
+ private
60
+
61
+ def extract_options!(hash)
62
+ options = hash.delete(:portable_hash_options) || hash.delete("portable_hash_options") || {}
63
+ options = options.each_with_object({}) do |(key, val), memo|
64
+ memo[key.to_sym] = val.is_a?(Array) ? val.map(&:to_s) : val
65
+ end
66
+ options.with_indifferent_access
67
+ end
68
+
69
+ def merge_options!(options)
70
+ config.each do |key, val|
71
+ default = config[key]
72
+ custom = options[key]
73
+
74
+ if default.is_a?(Array)
75
+ custom = [] if custom.nil?
76
+ custom = custom.is_a?(Array) ? custom : [custom]
77
+ options[key] = (default + custom).uniq
78
+ else
79
+ options[key] = custom || default
80
+ end
81
+ end
82
+
83
+ options
84
+ end
85
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UniversalID
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "zlib"
5
+ require "globalid"
6
+ require "active_model"
7
+ require "active_support/all"
8
+ require_relative "universal_id/version"
9
+ require_relative "universal_id/errors"
10
+ require_relative "universal_id/config"
11
+ require_relative "universal_id/portable"
12
+ require_relative "universal_id/portable_hash"
13
+ require_relative "universal_id/active_model_serializer"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "universal_id"
metadata ADDED
@@ -0,0 +1,256 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: universalid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nate Hopkins (hopsoft)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: globalid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faker
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: magic_frozen_string_literal
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest-reporters
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry-byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry-doc
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: sqlite3
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: standard
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: tocer
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: 'UniversalID expands GlobalID support to objects like Array, Hash, ActiveRecord::Relation,
210
+ and more.
211
+
212
+ '
213
+ email:
214
+ - natehop@gmail.com
215
+ executables: []
216
+ extensions: []
217
+ extra_rdoc_files: []
218
+ files:
219
+ - README.md
220
+ - Rakefile
221
+ - lib/universal_id.rb
222
+ - lib/universal_id/active_model_serializer.rb
223
+ - lib/universal_id/config.rb
224
+ - lib/universal_id/errors.rb
225
+ - lib/universal_id/portable.rb
226
+ - lib/universal_id/portable_hash.rb
227
+ - lib/universal_id/version.rb
228
+ - lib/universalid.rb
229
+ homepage: https://github.com/hopsoft/universalid
230
+ licenses:
231
+ - MIT
232
+ metadata:
233
+ homepage_uri: https://github.com/hopsoft/universalid
234
+ source_code_uri: https://github.com/hopsoft/universalid
235
+ changelog_uri: https://github.com/hopsoft/universalid/blob/main/CHANGELOG.md
236
+ post_install_message:
237
+ rdoc_options: []
238
+ require_paths:
239
+ - lib
240
+ required_ruby_version: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: 2.7.5
245
+ required_rubygems_version: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - ">="
248
+ - !ruby/object:Gem::Version
249
+ version: '0'
250
+ requirements: []
251
+ rubygems_version: 3.3.21
252
+ signing_key:
253
+ specification_version: 4
254
+ summary: GlobalID for for Arrays, Hashes, and objects like ActiveRecord::Relation,
255
+ etc.
256
+ test_files: []