globalid 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52c4368f15fc8952c9d79189ed9a78e649e0142421721aef338625c19b24c3b9
4
- data.tar.gz: 4d6d1c379d45ccaf5761488c540d19cac9d0488b578564964edd88c52073c511
3
+ metadata.gz: 15140d3d09fd236ea096c178a473440eb3d2fab55503978d646f3f13a863589c
4
+ data.tar.gz: 65351562c30c6c4e68d5b21ac8a502a7f7f5c8995cf538844395d123c685afba
5
5
  SHA512:
6
- metadata.gz: 49ff5a4b4d4f68afdffa67a85b5aebfb3b2f518257ffec22be8a60a29ac5596a67552aae46bebab28a4fe6b37a5802ac520b0f567098617110b2f54c4a74cfcb
7
- data.tar.gz: f6ba35dc2c080b0c996bd4529cec1438a35545c693cc0271e25c6b7bdaa8f759de61f26ae6ecf16487d89c7e6e5afc41caab7114541cd8a54ebdf9e2a3ce1f60
6
+ metadata.gz: b2a642776d9233ad70854e49b1dde8287397b94321a158747abc6738c4a976a4bb93a5de9fa72d8e3f7792a5667cc01eb938200b0fb8da675bce28af8488c93d
7
+ data.tar.gz: c390cbaf842ce68dfd97e4a6d94ae40f9b83fa694ee5931975d96d1b8bd4c8795fbb308a12d3e0ba54f5e8b87d16229a665abe86aa9d00ec0e5e98bb5777ad23
data/README.md CHANGED
@@ -161,6 +161,25 @@ GlobalID::Locator.locate_many gids
161
161
 
162
162
  Note the order is maintained in the returned results.
163
163
 
164
+ ### Options
165
+
166
+ Either `GlobalID::Locator.locate` or `GlobalID::Locator.locate_many` supports a hash of options as second parameter. The supported options are:
167
+
168
+ * :includes - A Symbol, Array, Hash or combination of them
169
+ The same structure you would pass into a `includes` method of Active Record.
170
+ See [Active Record eager loading associations](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)
171
+ If present, `locate` or `locate_many` will eager load all the relationships specified here.
172
+ Note: It only works if all the gids models have that relationships.
173
+ * :only - A class, module or Array of classes and/or modules that are
174
+ allowed to be located. Passing one or more classes limits instances of returned
175
+ classes to those classes or their subclasses. Passing one or more modules in limits
176
+ instances of returned classes to those including that module. If no classes or
177
+ modules match, +nil+ is returned.
178
+ * :ignore_missing (Only for `locate_many`) - By default, `locate_many` will call `#find` on the model to locate the
179
+ ids extracted from the GIDs. In Active Record (and other data stores following the same pattern),
180
+ `#find` will raise an exception if a named ID can't be found. When you set this option to true,
181
+ we will use `#where(id: ids)` instead, which does not raise on missing records.
182
+
164
183
  ### Custom App Locator
165
184
 
166
185
  A custom locator can be set for an app by calling `GlobalID::Locator.use` and providing an app locator to use for that app.
@@ -172,7 +191,7 @@ A custom locator can either be a block or a class.
172
191
  Using a block:
173
192
 
174
193
  ```ruby
175
- GlobalID::Locator.use :foo do |gid|
194
+ GlobalID::Locator.use :foo do |gid, options|
176
195
  FooRemote.const_get(gid.model_name).find(gid.model_id)
177
196
  end
178
197
  ```
@@ -182,7 +201,7 @@ Using a class:
182
201
  ```ruby
183
202
  GlobalID::Locator.use :bar, BarLocator.new
184
203
  class BarLocator
185
- def locate(gid)
204
+ def locate(gid, options = {})
186
205
  @search_client.search name: gid.model_name, id: gid.model_id
187
206
  end
188
207
  end
@@ -50,12 +50,13 @@ class GlobalID
50
50
  end
51
51
 
52
52
  def model_class
53
- model = model_name.constantize
53
+ @model_class ||= begin
54
+ model = model_name.constantize
54
55
 
55
- unless model <= GlobalID
56
+ if model <= GlobalID
57
+ raise ArgumentError, "GlobalID and SignedGlobalID cannot be used as model_class."
58
+ end
56
59
  model
57
- else
58
- raise ArgumentError, "GlobalID and SignedGlobalID cannot be used as model_class."
59
60
  end
60
61
  end
61
62
 
@@ -1,19 +1,118 @@
1
1
  class GlobalID
2
+ # Mix `GlobalID::Identification` into any model with a `#find(id)` class
3
+ # method. Support is automatically included in Active Record.
4
+ #
5
+ # class Person
6
+ # include ActiveModel::Model
7
+ # include GlobalID::Identification
8
+ #
9
+ # attr_accessor :id
10
+ #
11
+ # def self.find(id)
12
+ # new id: id
13
+ # end
14
+ #
15
+ # def ==(other)
16
+ # id == other.try(:id)
17
+ # end
18
+ # end
19
+ #
20
+ # person_gid = Person.find(1).to_global_id
21
+ # # => #<GlobalID ...
22
+ # person_gid.uri
23
+ # # => #<URI ...
24
+ # person_gid.to_s
25
+ # # => "gid://app/Person/1"
26
+ # GlobalID::Locator.locate person_gid
27
+ # # => #<Person:0x007fae94bf6298 @id="1">
2
28
  module Identification
29
+
30
+ # Returns the Global ID of the model.
31
+ #
32
+ # model = Person.new id: 1
33
+ # global_id = model.to_global_id
34
+ # global_id.modal_class # => Person
35
+ # global_id.modal_id # => "1"
36
+ # global_id.to_param # => "Z2lkOi8vYm9yZGZvbGlvL1BlcnNvbi8x"
3
37
  def to_global_id(options = {})
4
38
  GlobalID.create(self, options)
5
39
  end
6
40
  alias to_gid to_global_id
7
41
 
42
+ # Returns the Global ID parameter of the model.
43
+ #
44
+ # model = Person.new id: 1
45
+ # model.to_gid_param # => ""Z2lkOi8vYm9yZGZvbGlvL1BlcnNvbi8x"
8
46
  def to_gid_param(options = {})
9
47
  to_global_id(options).to_param
10
48
  end
11
49
 
50
+ # Returns the Signed Global ID of the model.
51
+ # Signed Global IDs ensure that the data hasn't been tampered with.
52
+ #
53
+ # model = Person.new id: 1
54
+ # signed_global_id = model.to_signed_global_id
55
+ # signed_global_id.modal_class # => Person
56
+ # signed_global_id.modal_id # => "1"
57
+ # signed_global_id.to_param # => "BAh7CEkiCGdpZAY6BkVUSSIiZ2..."
58
+ #
59
+ # ==== Expiration
60
+ #
61
+ # Signed Global IDs can expire some time in the future. This is useful if
62
+ # there's a resource people shouldn't have indefinite access to, like a
63
+ # share link.
64
+ #
65
+ # expiring_sgid = Document.find(5).to_sgid(expires_in: 2.hours, for: 'sharing')
66
+ # # => #<SignedGlobalID:0x008fde45df8937 ...>
67
+ # # Within 2 hours...
68
+ # GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
69
+ # # => #<Document:0x007fae94bf6298 @id="5">
70
+ # # More than 2 hours later...
71
+ # GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
72
+ # # => nil
73
+ #
74
+ # In Rails, an auto-expiry of 1 month is set by default.
75
+ #
76
+ # You need to explicitly pass `expires_in: nil` to generate a permanent
77
+ # SGID that will not expire,
78
+ #
79
+ # never_expiring_sgid = Document.find(5).to_sgid(expires_in: nil)
80
+ # # => #<SignedGlobalID:0x008fde45df8937 ...>
81
+ #
82
+ # # Any time later...
83
+ # GlobalID::Locator.locate_signed never_expiring_sgid
84
+ # # => #<Document:0x007fae94bf6298 @id="5">
85
+ #
86
+ # It's also possible to pass a specific expiry time
87
+ #
88
+ # explicit_expiring_sgid = SecretAgentMessage.find(5).to_sgid(expires_at: Time.now.advance(hours: 1))
89
+ # # => #<SignedGlobalID:0x008fde45df8937 ...>
90
+ #
91
+ # # 1 hour later...
92
+ # GlobalID::Locator.locate_signed explicit_expiring_sgid.to_s
93
+ # # => nil
94
+ #
95
+ # Note that an explicit `:expires_at` takes precedence over a relative `:expires_in`.
96
+ #
97
+ # ==== Purpose
98
+ #
99
+ # You can even bump the security up some more by explaining what purpose a
100
+ # Signed Global ID is for. In this way evildoers can't reuse a sign-up
101
+ # form's SGID on the login page. For example.
102
+ #
103
+ # signup_person_sgid = Person.find(1).to_sgid(for: 'signup_form')
104
+ # # => #<SignedGlobalID:0x007fea1984b520
105
+ # GlobalID::Locator.locate_signed(signup_person_sgid.to_s, for: 'signup_form')
106
+ # => #<Person:0x007fae94bf6298 @id="1">
12
107
  def to_signed_global_id(options = {})
13
108
  SignedGlobalID.create(self, options)
14
109
  end
15
110
  alias to_sgid to_signed_global_id
16
111
 
112
+ # Returns the Signed Global ID parameter.
113
+ #
114
+ # model = Person.new id: 1
115
+ # model.to_sgid_param # => "BAh7CEkiCGdpZAY6BkVUSSIiZ2..."
17
116
  def to_sgid_param(options = {})
18
117
  to_signed_global_id(options).to_param
19
118
  end
@@ -2,18 +2,33 @@ require 'active_support/core_ext/enumerable' # For Enumerable#index_by
2
2
 
3
3
  class GlobalID
4
4
  module Locator
5
+ class InvalidModelIdError < StandardError; end
6
+
5
7
  class << self
6
8
  # Takes either a GlobalID or a string that can be turned into a GlobalID
7
9
  #
8
10
  # Options:
11
+ # * <tt>:includes</tt> - A Symbol, Array, Hash or combination of them.
12
+ # The same structure you would pass into a +includes+ method of Active Record.
13
+ # If present, locate will load all the relationships specified here.
14
+ # See https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations.
9
15
  # * <tt>:only</tt> - A class, module or Array of classes and/or modules that are
10
16
  # allowed to be located. Passing one or more classes limits instances of returned
11
17
  # classes to those classes or their subclasses. Passing one or more modules in limits
12
18
  # instances of returned classes to those including that module. If no classes or
13
19
  # modules match, +nil+ is returned.
14
20
  def locate(gid, options = {})
15
- if gid = GlobalID.parse(gid)
16
- locator_for(gid).locate gid if find_allowed?(gid.model_class, options[:only])
21
+ gid = GlobalID.parse(gid)
22
+
23
+ return unless gid && find_allowed?(gid.model_class, options[:only])
24
+
25
+ locator = locator_for(gid)
26
+
27
+ if locator.method(:locate).arity == 1
28
+ GlobalID.deprecator.warn "It seems your locator is defining the `locate` method only with one argument. Please make sure your locator is receiving the options argument as well, like `locate(gid, options = {})`."
29
+ locator.locate(gid)
30
+ else
31
+ locator.locate(gid, options.except(:only))
17
32
  end
18
33
  end
19
34
 
@@ -28,6 +43,11 @@ class GlobalID
28
43
  # per model class, but still interpolate the results to match the order in which the gids were passed.
29
44
  #
30
45
  # Options:
46
+ # * <tt>:includes</tt> - A Symbol, Array, Hash or combination of them
47
+ # The same structure you would pass into a includes method of Active Record.
48
+ # @see https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
49
+ # If present, locate_many will load all the relationships specified here.
50
+ # Note: It only works if all the gids models have that relationships.
31
51
  # * <tt>:only</tt> - A class, module or Array of classes and/or modules that are
32
52
  # allowed to be located. Passing one or more classes limits instances of returned
33
53
  # classes to those classes or their subclasses. Passing one or more modules in limits
@@ -49,6 +69,10 @@ class GlobalID
49
69
  # Takes either a SignedGlobalID or a string that can be turned into a SignedGlobalID
50
70
  #
51
71
  # Options:
72
+ # * <tt>:includes</tt> - A Symbol, Array, Hash or combination of them
73
+ # The same structure you would pass into a includes method of Active Record.
74
+ # @see https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
75
+ # If present, locate_signed will load all the relationships specified here.
52
76
  # * <tt>:only</tt> - A class, module or Array of classes and/or modules that are
53
77
  # allowed to be located. Passing one or more classes limits instances of returned
54
78
  # classes to those classes or their subclasses. Passing one or more modules in limits
@@ -66,6 +90,11 @@ class GlobalID
66
90
  # the results to match the order in which the gids were passed.
67
91
  #
68
92
  # Options:
93
+ # * <tt>:includes</tt> - A Symbol, Array, Hash or combination of them
94
+ # The same structure you would pass into a includes method of Active Record.
95
+ # @see https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
96
+ # If present, locate_many_signed will load all the relationships specified here.
97
+ # Note: It only works if all the gids models have that relationships.
69
98
  # * <tt>:only</tt> - A class, module or Array of classes and/or modules that are
70
99
  # allowed to be located. Passing one or more classes limits instances of returned
71
100
  # classes to those classes or their subclasses. Passing one or more modules in limits
@@ -82,7 +111,7 @@ class GlobalID
82
111
  #
83
112
  # Using a block:
84
113
  #
85
- # GlobalID::Locator.use :foo do |gid|
114
+ # GlobalID::Locator.use :foo do |gid, options|
86
115
  # FooRemote.const_get(gid.model_name).find(gid.model_id)
87
116
  # end
88
117
  #
@@ -91,7 +120,7 @@ class GlobalID
91
120
  # GlobalID::Locator.use :bar, BarLocator.new
92
121
  #
93
122
  # class BarLocator
94
- # def locate(gid)
123
+ # def locate(gid, options = {})
95
124
  # @search_client.search name: gid.model_name, id: gid.model_id
96
125
  # end
97
126
  # end
@@ -125,32 +154,55 @@ class GlobalID
125
154
  @locators = {}
126
155
 
127
156
  class BaseLocator
128
- def locate(gid)
129
- gid.model_class.find gid.model_id
157
+ def locate(gid, options = {})
158
+ return unless model_id_is_valid?(gid)
159
+ model_class = gid.model_class
160
+ model_class = model_class.includes(options[:includes]) if options[:includes]
161
+
162
+ model_class.find gid.model_id
130
163
  end
131
164
 
132
165
  def locate_many(gids, options = {})
133
- models_and_ids = gids.collect { |gid| [ gid.model_class, gid.model_id ] }
134
- ids_by_model = models_and_ids.group_by(&:first)
135
- loaded_by_model = Hash[ids_by_model.map { |model, ids|
136
- [ model, find_records(model, ids.map(&:last), ignore_missing: options[:ignore_missing]).index_by { |record| record.id.to_s } ]
137
- }]
166
+ ids_by_model = Hash.new { |hash, key| hash[key] = [] }
167
+
168
+ gids.each do |gid|
169
+ next unless model_id_is_valid?(gid)
170
+ ids_by_model[gid.model_class] << gid.model_id
171
+ end
172
+
173
+ records_by_model_name_and_id = {}
138
174
 
139
- models_and_ids.collect { |(model, id)| loaded_by_model[model][id] }.compact
175
+ ids_by_model.each do |model, ids|
176
+ records = find_records(model, ids, ignore_missing: options[:ignore_missing], includes: options[:includes])
177
+
178
+ records_by_id = records.index_by do |record|
179
+ record.id.is_a?(Array) ? record.id.map(&:to_s) : record.id.to_s
180
+ end
181
+
182
+ records_by_model_name_and_id[model.name] = records_by_id
183
+ end
184
+
185
+ gids.filter_map { |gid| records_by_model_name_and_id[gid.model_name][gid.model_id] }
140
186
  end
141
187
 
142
188
  private
143
189
  def find_records(model_class, ids, options)
190
+ model_class = model_class.includes(options[:includes]) if options[:includes]
191
+
144
192
  if options[:ignore_missing]
145
- model_class.where(id: ids)
193
+ model_class.where(model_class.primary_key => ids)
146
194
  else
147
195
  model_class.find(ids)
148
196
  end
149
197
  end
198
+
199
+ def model_id_is_valid?(gid)
200
+ Array(gid.model_id).size == Array(gid.model_class.primary_key).size
201
+ end
150
202
  end
151
203
 
152
204
  class UnscopedLocator < BaseLocator
153
- def locate(gid)
205
+ def locate(gid, options = {})
154
206
  unscoped(gid.model_class) { super }
155
207
  end
156
208
 
@@ -174,12 +226,12 @@ class GlobalID
174
226
  @locator = block
175
227
  end
176
228
 
177
- def locate(gid)
178
- @locator.call(gid)
229
+ def locate(gid, options = {})
230
+ @locator.call(gid, options)
179
231
  end
180
232
 
181
233
  def locate_many(gids, options = {})
182
- gids.map { |gid| locate(gid) }
234
+ gids.map { |gid| locate(gid, options) }
183
235
  end
184
236
  end
185
237
  end
@@ -42,6 +42,10 @@ class GlobalID
42
42
  send :extend, GlobalID::FixtureSet
43
43
  end
44
44
  end
45
+
46
+ initializer "web_console.deprecator" do |app|
47
+ app.deprecators[:global_id] = GlobalID.deprecator if app.respond_to?(:deprecators)
48
+ end
45
49
  end
46
50
  end
47
51
 
@@ -5,7 +5,7 @@ class SignedGlobalID < GlobalID
5
5
  class ExpiredMessage < StandardError; end
6
6
 
7
7
  class << self
8
- attr_accessor :verifier
8
+ attr_accessor :verifier, :expires_in
9
9
 
10
10
  def parse(sgid, options = {})
11
11
  super verify(sgid.to_s, options), options
@@ -19,8 +19,6 @@ class SignedGlobalID < GlobalID
19
19
  end
20
20
  end
21
21
 
22
- attr_accessor :expires_in
23
-
24
22
  DEFAULT_PURPOSE = "default"
25
23
 
26
24
  def pick_purpose(options)
@@ -29,11 +27,22 @@ class SignedGlobalID < GlobalID
29
27
 
30
28
  private
31
29
  def verify(sgid, options)
30
+ verify_with_verifier_validated_metadata(sgid, options) ||
31
+ verify_with_legacy_self_validated_metadata(sgid, options)
32
+ end
33
+
34
+ def verify_with_verifier_validated_metadata(sgid, options)
35
+ pick_verifier(options).verify(sgid, purpose: pick_purpose(options))
36
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
37
+ nil
38
+ end
39
+
40
+ def verify_with_legacy_self_validated_metadata(sgid, options)
32
41
  metadata = pick_verifier(options).verify(sgid)
33
42
 
34
43
  raise_if_expired(metadata['expires_at'])
35
44
 
36
- metadata['gid'] if pick_purpose(options) == metadata['purpose']
45
+ metadata['gid'] if pick_purpose(options)&.to_s == metadata['purpose']&.to_s
37
46
  rescue ActiveSupport::MessageVerifier::InvalidSignature, ExpiredMessage
38
47
  nil
39
48
  end
@@ -55,25 +64,19 @@ class SignedGlobalID < GlobalID
55
64
  end
56
65
 
57
66
  def to_s
58
- @sgid ||= @verifier.generate(to_h)
67
+ @sgid ||= @verifier.generate(@uri.to_s, purpose: purpose, expires_at: expires_at)
59
68
  end
60
69
  alias to_param to_s
61
70
 
62
- def to_h
63
- # Some serializers decodes symbol keys to symbols, others to strings.
64
- # Using string keys remedies that.
65
- { 'gid' => @uri.to_s, 'purpose' => purpose, 'expires_at' => encoded_expiration }
66
- end
67
-
68
71
  def ==(other)
69
72
  super && @purpose == other.purpose
70
73
  end
71
74
 
72
- private
73
- def encoded_expiration
74
- expires_at.utc.iso8601(3) if expires_at
75
- end
75
+ def inspect # :nodoc:
76
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
77
+ end
76
78
 
79
+ private
77
80
  def pick_expiration(options)
78
81
  return options[:expires_at] if options.key?(:expires_at)
79
82
 
@@ -30,6 +30,11 @@ module URI
30
30
 
31
31
  # Raised when creating a Global ID for a model without an id
32
32
  class MissingModelIdError < URI::InvalidComponentError; end
33
+ class InvalidModelIdError < URI::InvalidComponentError; end
34
+
35
+ # Maximum size of a model id segment
36
+ COMPOSITE_MODEL_ID_MAX_SIZE = 20
37
+ COMPOSITE_MODEL_ID_DELIMITER = "/"
33
38
 
34
39
  class << self
35
40
  # Validates +app+'s as URI hostnames containing only alphanumeric characters
@@ -83,7 +88,8 @@ module URI
83
88
  def build(args)
84
89
  parts = Util.make_components_hash(self, args)
85
90
  parts[:host] = parts[:app]
86
- parts[:path] = "/#{parts[:model_name]}/#{CGI.escape(parts[:model_id].to_s)}"
91
+ model_id_segment = Array(parts[:model_id]).map { |p| CGI.escape(p.to_s) }.join(COMPOSITE_MODEL_ID_DELIMITER)
92
+ parts[:path] = "/#{parts[:model_name]}/#{model_id_segment}"
87
93
 
88
94
  if parts[:params] && !parts[:params].empty?
89
95
  parts[:query] = URI.encode_www_form(parts[:params])
@@ -147,12 +153,22 @@ module URI
147
153
 
148
154
  def set_model_components(path, validate = false)
149
155
  _, model_name, model_id = path.split('/', 3)
150
- validate_component(model_name) && validate_model_id(model_id, model_name) if validate
151
-
152
- model_id = CGI.unescape(model_id) if model_id
153
156
 
157
+ validate_component(model_name) && validate_model_id_section(model_id, model_name) if validate
154
158
  @model_name = model_name
155
- @model_id = model_id
159
+
160
+ if model_id
161
+ model_id_parts = model_id
162
+ .split(COMPOSITE_MODEL_ID_DELIMITER, COMPOSITE_MODEL_ID_MAX_SIZE)
163
+ .reject(&:blank?)
164
+
165
+ model_id_parts.map! do |id|
166
+ validate_model_id(id)
167
+ CGI.unescape(id)
168
+ end
169
+
170
+ @model_id = model_id_parts.length == 1 ? model_id_parts.first : model_id_parts
171
+ end
156
172
  end
157
173
 
158
174
  def validate_component(component)
@@ -162,13 +178,20 @@ module URI
162
178
  "Expected a URI like gid://app/Person/1234: #{inspect}"
163
179
  end
164
180
 
165
- def validate_model_id(model_id, model_name)
166
- return model_id unless model_id.blank? || model_id.include?('/')
181
+ def validate_model_id_section(model_id, model_name)
182
+ return model_id unless model_id.blank?
167
183
 
168
184
  raise MissingModelIdError, "Unable to create a Global ID for " \
169
185
  "#{model_name} without a model id."
170
186
  end
171
187
 
188
+ def validate_model_id(model_id_part)
189
+ return unless model_id_part.include?('/')
190
+
191
+ raise InvalidModelIdError, "Unable to create a Global ID for " \
192
+ "#{model_name} with a malformed model id."
193
+ end
194
+
172
195
  def parse_query_params(query)
173
196
  Hash[URI.decode_www_form(query)].with_indifferent_access if query
174
197
  end
@@ -3,11 +3,11 @@ require 'active_support/message_verifier'
3
3
  class GlobalID
4
4
  class Verifier < ActiveSupport::MessageVerifier
5
5
  private
6
- def encode(data)
6
+ def encode(data, **)
7
7
  ::Base64.urlsafe_encode64(data)
8
8
  end
9
9
 
10
- def decode(data)
10
+ def decode(data, **)
11
11
  ::Base64.urlsafe_decode64(data)
12
12
  end
13
13
  end
data/lib/global_id.rb CHANGED
@@ -16,4 +16,8 @@ class GlobalID
16
16
  super
17
17
  require 'global_id/signed_global_id'
18
18
  end
19
+
20
+ def self.deprecator # :nodoc:
21
+ @deprecator ||= ActiveSupport::Deprecation.new("2.1", "GlobalID")
22
+ end
19
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: globalid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-25 00:00:00.000000000 Z
11
+ date: 2023-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement