gibbler 0.8.10 → 0.10.0.pre.RC1
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 +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.github/workflows/ruby-rake.yaml +38 -0
- data/.gitignore +16 -0
- data/.pre-commit-config.yaml +60 -0
- data/.rubocop.yml +27 -0
- data/{CHANGES.txt → CHANGELOG.md} +75 -81
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +4 -2
- data/README.md +250 -0
- data/VERSION.yml +2 -2
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/img/whoababy.gif +0 -0
- data/lib/gibbler/aliases.rb +2 -2
- data/lib/gibbler/history.rb +40 -42
- data/lib/gibbler/mixins.rb +31 -7
- data/lib/gibbler/version.rb +17 -0
- data/lib/gibbler.rb +272 -299
- data/sig/gibbler.rbs +4 -0
- data/try/{01_mixins_try.rb → 01_core_ext_try.rb} +1 -1
- data/try/02_compat_try.rb +1 -1
- data/try/05_gibbler_digest_try.rb +1 -1
- data/try/10_standalone_try.rb +49 -0
- data/try/{10_basic_try.rb → 11_basic_try.rb} +13 -14
- data/try/{11_basic_sha256_try.rb → 12_basic_sha256_try.rb} +1 -1
- data/try/14_extended_try.rb +1 -1
- data/try/15_file_try.rb +1 -1
- data/try/16_uri_try.rb +1 -1
- data/try/17_complex_object_try.rb +5 -2
- data/try/18_proc_try.rb +1 -1
- data/try/20_time_try.rb +1 -1
- data/try/30_secret_try.rb +1 -1
- data/try/50_history_try.rb +1 -1
- data/try/51_hash_history_try.rb +1 -1
- data/try/52_array_history_try.rb +1 -1
- data/try/53_string_history_try.rb +1 -1
- data/try/57_arbitrary_history_try.rb +1 -1
- data/try/59_history_exceptions_try.rb +1 -1
- metadata +88 -57
- data/README.rdoc +0 -228
- data/Rakefile +0 -64
- data/gibbler.gemspec +0 -69
data/lib/gibbler.rb
CHANGED
@@ -1,40 +1,24 @@
|
|
1
|
-
|
2
|
-
GIBBLER_LIB_HOME = File.expand_path File.dirname(__FILE__)
|
3
|
-
end
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
unless defined?(GIBBLER_LIB_HOME)
|
4
|
+
GIBBLER_LIB_HOME = File.expand_path File.dirname(__FILE__)
|
7
5
|
end
|
8
|
-
|
9
|
-
require 'thread'
|
10
|
-
require 'attic'
|
11
6
|
require 'digest/sha1'
|
12
7
|
|
13
|
-
|
14
|
-
#
|
8
|
+
|
9
|
+
# = Gibbler
|
10
|
+
#
|
15
11
|
# "Hola, Tanneritos"
|
16
12
|
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
load_config
|
21
|
-
[@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
|
22
|
-
end
|
23
|
-
alias_method :inspect, :to_s
|
24
|
-
def self.load_config
|
25
|
-
require 'yaml'
|
26
|
-
@version ||= YAML.load_file(::File.join(GIBBLER_LIB_HOME, '..', 'VERSION.yml'))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
13
|
+
class Gibbler < String
|
14
|
+
|
15
|
+
|
30
16
|
@default_base = 16
|
31
17
|
@secret = nil
|
32
18
|
class << self
|
33
19
|
attr_accessor :secret, :default_base
|
34
20
|
end
|
35
|
-
|
36
|
-
require 'gibbler/mixins'
|
37
|
-
|
21
|
+
|
38
22
|
class Error < RuntimeError
|
39
23
|
def initialize(obj); @obj = obj; end
|
40
24
|
end
|
@@ -46,115 +30,118 @@ end
|
|
46
30
|
# few digest related convenience methods.
|
47
31
|
#
|
48
32
|
class Gibbler::Digest < String
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
base
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
# Return an integer assuming base is Gibbler.default_base.
|
36
|
+
def to_i(base=nil)
|
37
|
+
base ||= Gibbler.default_base
|
38
|
+
super(base)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a string. Takes an optional base.
|
42
|
+
def to_s(base=nil)
|
43
|
+
base.nil? ? super() : super().to_i(Gibbler.default_base).to_s(base)
|
44
|
+
end
|
45
|
+
|
46
|
+
def base(base=Gibbler.default_base)
|
47
|
+
v = self.to_i(Gibbler.default_base).to_s(base)
|
48
|
+
v.extend Gibbler::Digest::InstanceMethods
|
49
|
+
self.class.new v
|
50
|
+
end
|
51
|
+
|
52
|
+
def base36
|
53
|
+
base(36)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Shorten the digest to the given (optional) length.
|
57
|
+
def shorten(len=20)
|
58
|
+
self[0..len-1]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the first 8 characters of itself (the digest).
|
62
|
+
#
|
63
|
+
# e.g.
|
64
|
+
#
|
65
|
+
# "kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
66
|
+
# "kimmy".gibbler.short # => c8027100
|
67
|
+
#
|
68
|
+
def short
|
69
|
+
shorten(8)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the first 6 characters of itself (the digest).
|
73
|
+
#
|
74
|
+
# e.g.
|
75
|
+
#
|
76
|
+
# "kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
77
|
+
# "kimmy".gibbler.shorter # => c80271
|
78
|
+
#
|
79
|
+
def shorter
|
80
|
+
shorten(6)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the first 4 characters of itself (the digest).
|
84
|
+
#
|
85
|
+
# e.g.
|
86
|
+
#
|
87
|
+
# "kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
88
|
+
# "kimmy".gibbler.tiny # => c802
|
89
|
+
#
|
90
|
+
def tiny
|
91
|
+
shorten(4)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns true when +ali+ matches +self+
|
95
|
+
#
|
96
|
+
# "kimmy".gibbler == "c8027100ecc54945ab15ddac529230e38b1ba6a1" # => true
|
97
|
+
# "kimmy".gibbler == "c8027100" # => false
|
98
|
+
#
|
99
|
+
def ==(ali)
|
100
|
+
return true if self.to_s == ali.to_s
|
101
|
+
false
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns true when +g+ matches one of: +self+, +short+, +shorter+, +tiny+
|
105
|
+
#
|
106
|
+
# "kimmy".gibbler === "c8027100ecc54945ab15ddac529230e38b1ba6a1" # => true
|
107
|
+
# "kimmy".gibbler === "c8027100" # => true
|
108
|
+
# "kimmy".gibbler === "c80271" # => true
|
109
|
+
# "kimmy".gibbler === "c802" # => true
|
110
|
+
#
|
111
|
+
def ===(g)
|
112
|
+
return true if [to_s, short, shorter, tiny].member?(g.to_s)
|
113
|
+
false
|
114
|
+
end
|
127
115
|
end
|
116
|
+
include InstanceMethods
|
128
117
|
end
|
129
118
|
|
130
|
-
|
119
|
+
class Gibbler < String
|
131
120
|
module Object
|
132
|
-
|
121
|
+
|
133
122
|
def self.included(obj)
|
134
123
|
obj.extend Attic
|
135
124
|
obj.attic :gibbler_cache
|
136
|
-
# Backwards compatibility for <= 0.6.2
|
137
|
-
obj.send :alias_method, :__gibbler_cache, :gibbler_cache
|
138
125
|
end
|
139
|
-
|
126
|
+
|
140
127
|
def self.gibbler_fields
|
141
128
|
end
|
142
129
|
def gibbler_fields
|
143
130
|
end
|
144
|
-
|
145
|
-
# Calculates a digest for the current object instance.
|
131
|
+
|
132
|
+
# Calculates a digest for the current object instance.
|
146
133
|
# Objects that are a kind of Hash or Array are processed
|
147
|
-
# recursively. The length of the returned String depends
|
134
|
+
# recursively. The length of the returned String depends
|
148
135
|
# on the digest type. Also stores the value in the attic.
|
149
|
-
#
|
136
|
+
#
|
150
137
|
# obj.gibbler # => a5b1191a
|
151
138
|
# obj.gibbler_cache # => a5b1191a
|
152
|
-
#
|
139
|
+
#
|
153
140
|
# Calling gibbler_cache returns the most recent digest
|
154
141
|
# without calculation.
|
155
142
|
#
|
156
143
|
# If the object is frozen, this will return the value of
|
157
|
-
#
|
144
|
+
# `gibbler_cache`.
|
158
145
|
#
|
159
146
|
def gibbler(digest_type=nil)
|
160
147
|
#gibbler_debug caller[0]
|
@@ -165,9 +152,9 @@ module Gibbler
|
|
165
152
|
|
166
153
|
# Has this object been modified?
|
167
154
|
#
|
168
|
-
# This method compares the return value from digest with the
|
155
|
+
# This method compares the return value from digest with the
|
169
156
|
# previous value returned by gibbler (the value is stored in
|
170
|
-
# the attic as
|
157
|
+
# the attic as `gibbler_cache`).
|
171
158
|
# See Attic[http://github.com/delano/attic]
|
172
159
|
def gibbled?
|
173
160
|
self.gibbler_cache ||= self.gibbler
|
@@ -180,15 +167,15 @@ module Gibbler
|
|
180
167
|
return unless Gibbler.debug?
|
181
168
|
p args
|
182
169
|
end
|
183
|
-
|
170
|
+
|
184
171
|
# Creates a digest for the current state of self based on:
|
185
172
|
# * Object#class
|
186
173
|
# * Length of Object#name || 0
|
187
174
|
# * Object#name || ''
|
188
|
-
#
|
189
|
-
# e.g. Digest::SHA1.hexdigest "Class:6:Object" #=>
|
190
175
|
#
|
191
|
-
#
|
176
|
+
# e.g. Digest::SHA1.hexdigest "Class:6:Object" #=>
|
177
|
+
#
|
178
|
+
# <b>This is a default method appropriate for only the most
|
192
179
|
# basic objects like Class and Module.</b>
|
193
180
|
#
|
194
181
|
def __gibbler(digest_type=nil)
|
@@ -199,10 +186,10 @@ module Gibbler
|
|
199
186
|
gibbler_debug klass, a, [klass, nom.size, nom]
|
200
187
|
a
|
201
188
|
end
|
202
|
-
|
189
|
+
|
203
190
|
# A simple override on Object#freeze to create a digest
|
204
191
|
# before the object is frozen. Once the object is frozen
|
205
|
-
#
|
192
|
+
# `obj.gibbler` will return the cached value with
|
206
193
|
# out calculation.
|
207
194
|
def freeze()
|
208
195
|
gibbler_debug :FREEZE, self.class, caller[0] if Gibbler.debug?
|
@@ -210,52 +197,84 @@ module Gibbler
|
|
210
197
|
super
|
211
198
|
self
|
212
199
|
end
|
213
|
-
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
class Gibbler < String
|
206
|
+
include Gibbler::Digest::InstanceMethods
|
207
|
+
# Modify the digest type for this instance. See Gibbler.digest_type
|
208
|
+
attr_writer :digest_type
|
209
|
+
attr_reader :input
|
210
|
+
# Creates a digest from the given +input+. See Gibbler.digest.
|
211
|
+
#
|
212
|
+
# If only one argument is given and it's a digest, this will
|
213
|
+
# simply create an instance of that digest. In other words,
|
214
|
+
# it won't calculate a new digest based on that input.
|
215
|
+
def initialize *input
|
216
|
+
if input.size == 1 && Gibbler::Digest::InstanceMethods === input.first
|
217
|
+
super input.first
|
218
|
+
else
|
219
|
+
input.collect!(&:to_s)
|
220
|
+
super Gibbler.digest(input) || ''
|
221
|
+
end
|
214
222
|
end
|
215
|
-
|
223
|
+
def digest_type
|
224
|
+
@digest_type || self.class.digest_type
|
225
|
+
end
|
226
|
+
|
227
|
+
def digest *input
|
228
|
+
replace Gibbler.digest(input, digest_type)
|
229
|
+
end
|
230
|
+
|
216
231
|
end
|
217
232
|
|
233
|
+
class Gibbler < String
|
218
234
|
|
219
|
-
|
235
|
+
@debug = false
|
236
|
+
@digest_type = ::Digest::SHA1
|
237
|
+
@delimiter = ':'
|
220
238
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
239
|
+
class << self
|
240
|
+
# Specify a different digest class. The default is +Digest::SHA1+. You
|
241
|
+
# could try +Digest::SHA256+ by doing this:
|
242
|
+
#
|
243
|
+
# Object.digest_type = Digest::SHA256
|
244
|
+
#
|
245
|
+
attr_accessor :digest_type
|
246
|
+
# The delimiter to use when joining Array values before creating a
|
247
|
+
# new digest hash. The default is ":".
|
248
|
+
attr_accessor :delimiter
|
249
|
+
# Set to true for debug output (including all digest inputs)
|
250
|
+
attr_accessor :debug
|
251
|
+
# Returns the current debug status (true or false)
|
252
|
+
def debug?; @debug != false; end
|
231
253
|
end
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
def self.disable_debug; @@gibbler_debug = false; end
|
236
|
-
def self.debug=(v); @@gibbler_debug = v; end
|
237
|
-
# Returns the current digest class.
|
238
|
-
def self.digest_type; @@gibbler_digest_type; end
|
239
|
-
|
240
|
-
# Sends +str+ to Digest::SHA1.hexdigest. If another digest class
|
241
|
-
# has been specified, that class will be used instead.
|
254
|
+
|
255
|
+
# Sends +input+ to Digest::SHA1.hexdigest. If another digest class
|
256
|
+
# has been specified, that class will be used instead.
|
242
257
|
# If Gibbler.secret is set, +str+ will be prepended with the
|
243
|
-
# value.
|
258
|
+
# value.
|
259
|
+
#
|
260
|
+
# If +input+ is an Array, it will be flattened and joined.
|
244
261
|
#
|
245
262
|
# See: digest_type
|
246
|
-
def self.digest(
|
247
|
-
|
248
|
-
|
249
|
-
|
263
|
+
def self.digest(input, digest_type=nil)
|
264
|
+
input = input.flatten.collect(&:to_s).join(delimiter) if ::Array === input
|
265
|
+
return if input.empty?
|
266
|
+
digest_type ||= @digest_type
|
267
|
+
input = [Gibbler.secret, input].join(delimiter) unless Gibbler.secret.nil?
|
268
|
+
dig = digest_type.hexdigest(input)
|
250
269
|
dig = dig.to_i(16).to_s(Gibbler.default_base) if 16 != Gibbler.default_base
|
251
270
|
dig
|
252
271
|
end
|
253
|
-
|
272
|
+
|
254
273
|
def self.gibbler_debug(*args)
|
255
274
|
return unless Gibbler.debug?
|
256
275
|
p args
|
257
276
|
end
|
258
|
-
|
277
|
+
|
259
278
|
# Raises an exception. The correct usage is to include a Gibbler::Object:
|
260
279
|
# * Gibbler::Complex
|
261
280
|
# * Gibbler::String
|
@@ -264,19 +283,20 @@ module Gibbler
|
|
264
283
|
def self.included(obj)
|
265
284
|
raise "You probably want to include Gibbler::Complex or Gibbler::Object"
|
266
285
|
end
|
267
|
-
|
268
|
-
|
269
|
-
#
|
270
|
-
#
|
286
|
+
|
287
|
+
|
288
|
+
# Creates a digest based on:
|
289
|
+
# * An Array of instance variable names or method names and values in the format: `CLASS:LENGTH:VALUE`
|
290
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
271
291
|
# will be parsed recursively according to the gibbler method for that class type.
|
272
|
-
# * Digest the Array of digests
|
273
|
-
# * Return the digest for
|
292
|
+
# * Digest the Array of digests
|
293
|
+
# * Return the digest for `class:length:value` where:
|
274
294
|
# * "class" is equal to the current object class (e.g. FullHouse).
|
275
295
|
# * "length" is the size of the Array of digests (which should equal
|
276
296
|
# the number of instance variables in the object).
|
277
297
|
# * "value" is the Array of digests joined with a colon (":").
|
278
298
|
#
|
279
|
-
# This method can be used by any class which stores values in instance variables.
|
299
|
+
# This method can be used by any class which stores values in instance variables.
|
280
300
|
#
|
281
301
|
# class Episodes
|
282
302
|
# include Gibbler::Complex
|
@@ -285,7 +305,7 @@ module Gibbler
|
|
285
305
|
#
|
286
306
|
module Complex
|
287
307
|
include Gibbler::Object
|
288
|
-
|
308
|
+
|
289
309
|
def self.included(obj)
|
290
310
|
obj.extend Attic
|
291
311
|
obj.attic :gibbler_cache
|
@@ -307,7 +327,7 @@ module Gibbler
|
|
307
327
|
end
|
308
328
|
end
|
309
329
|
end
|
310
|
-
|
330
|
+
|
311
331
|
def gibbler_fields
|
312
332
|
f = [self.class.gibbler_fields].compact.flatten
|
313
333
|
if f.empty?
|
@@ -317,26 +337,26 @@ module Gibbler
|
|
317
337
|
end
|
318
338
|
f
|
319
339
|
end
|
320
|
-
|
321
|
-
# Creates a digest for the current state of self.
|
340
|
+
|
341
|
+
# Creates a digest for the current state of self.
|
322
342
|
def __gibbler(digest_type=nil)
|
323
343
|
klass = self.class
|
324
344
|
d = []
|
325
345
|
gibbler_debug :gibbler_fields, gibbler_fields
|
326
346
|
gibbler_fields.each do |n|
|
327
|
-
value = instance_variable_get("@#{n}")
|
347
|
+
value = respond_to?(n) ? send(n) : instance_variable_get("@#{n}")
|
328
348
|
unless value.respond_to? :__gibbler
|
329
349
|
gibbler_debug klass, :skipping, n
|
330
350
|
next
|
331
351
|
end
|
332
352
|
d << '%s:%s:%s' % [value.class, n, value.__gibbler(digest_type)]
|
333
353
|
end
|
334
|
-
d = d.join(
|
354
|
+
d = d.join(Gibbler.delimiter).__gibbler(digest_type)
|
335
355
|
a = Gibbler.digest "%s:%d:%s" % [klass, d.size, d], digest_type
|
336
356
|
gibbler_debug klass, a, [klass, d.size, d]
|
337
357
|
a
|
338
358
|
end
|
339
|
-
|
359
|
+
|
340
360
|
def __gibbler_revert!
|
341
361
|
state = self.gibbler_object self.gibbler_cache
|
342
362
|
state.instance_variables do |n|
|
@@ -344,33 +364,32 @@ module Gibbler
|
|
344
364
|
self.instance_variable_set v
|
345
365
|
end
|
346
366
|
end
|
347
|
-
|
348
367
|
end
|
349
|
-
|
350
|
-
# Creates a digest based on:
|
351
|
-
# This method can be used for any class where the
|
352
|
-
# method returns an appropriate unique value for this instance.
|
353
|
-
# It's used by default for Symbol, Class,
|
368
|
+
|
369
|
+
# Creates a digest based on: `CLASS:LENGTH:VALUE`.
|
370
|
+
# This method can be used for any class where the `to_s`
|
371
|
+
# method returns an appropriate unique value for this instance.
|
372
|
+
# It's used by default for Symbol, Class, Integer.
|
354
373
|
# e.g.
|
355
|
-
#
|
374
|
+
#
|
356
375
|
# "str" => String:3:str => 509a839ca1744c72e37759e7684ff0daa3b61427
|
357
376
|
# :sym => Symbol:3:sym => f3b7b3ca9529002c6826b1ef609d3583c356c8c8
|
358
377
|
#
|
359
378
|
# To use use method in other classes simply:
|
360
379
|
#
|
361
|
-
# class MyStringLikeClass
|
380
|
+
# class MyStringLikeClass
|
362
381
|
# include Gibbler::String
|
363
382
|
# end
|
364
383
|
#
|
365
384
|
module String
|
366
385
|
include Gibbler::Object
|
367
|
-
|
386
|
+
|
368
387
|
def self.included(obj)
|
369
388
|
obj.extend Attic
|
370
389
|
obj.attic :gibbler_cache
|
371
390
|
end
|
372
|
-
|
373
|
-
# Creates a digest for the current state of self.
|
391
|
+
|
392
|
+
# Creates a digest for the current state of self.
|
374
393
|
def __gibbler(digest_type=nil)
|
375
394
|
klass = self.class
|
376
395
|
value = self.nil? ? "\0" : self.to_s
|
@@ -379,81 +398,81 @@ module Gibbler
|
|
379
398
|
a
|
380
399
|
end
|
381
400
|
end
|
382
|
-
|
383
|
-
# Creates a digest based on:
|
384
|
-
# * parse each key, value pair into an Array containing keys:
|
385
|
-
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
401
|
+
|
402
|
+
# Creates a digest based on:
|
403
|
+
# * parse each key, value pair into an Array containing keys: `CLASS:KEY:VALUE.__gibbler`
|
404
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
386
405
|
# will be parsed recursively according to the gibbler method for that class type.
|
387
|
-
# * Digest the Array of digests
|
388
|
-
# * Return the digest for
|
406
|
+
# * Digest the Array of digests
|
407
|
+
# * Return the digest for `class:length:value` where:
|
389
408
|
# * "class" is equal to the current object class (e.g. Hash).
|
390
409
|
# * "length" is the size of the Array of digests (which should equal
|
391
410
|
# the number of keys in the original Hash object).
|
392
411
|
# * "value" is the Array of digests joined with a colon (":").
|
393
412
|
#
|
394
|
-
# This method can be used by any class with a
|
413
|
+
# This method can be used by any class with a `keys` method.
|
395
414
|
#
|
396
415
|
# class MyOrderedHash
|
397
416
|
# include Gibbler::Hash
|
398
417
|
# end
|
399
418
|
#
|
400
|
-
module Hash
|
419
|
+
module Hash
|
401
420
|
include Gibbler::Object
|
402
|
-
|
421
|
+
|
403
422
|
def self.included(obj)
|
404
423
|
obj.extend Attic
|
405
424
|
obj.attic :gibbler_cache
|
406
425
|
end
|
407
|
-
|
408
|
-
# Creates a digest for the current state of self.
|
426
|
+
|
427
|
+
# Creates a digest for the current state of self.
|
409
428
|
def __gibbler(digest_type=nil)
|
410
429
|
klass = self.class
|
411
430
|
d = self.keys.sort { |a,b| a.inspect <=> b.inspect }
|
412
|
-
d.collect! do |name|
|
431
|
+
d.collect! do |name|
|
413
432
|
value = self[name]
|
414
433
|
unless value.respond_to? :__gibbler
|
415
434
|
gibbler_debug klass, :skipping, name
|
416
435
|
next
|
417
436
|
end
|
418
437
|
'%s:%s:%s' % [value.class, name, value.__gibbler(digest_type)]
|
419
|
-
end
|
420
|
-
d = d.join(
|
438
|
+
end
|
439
|
+
d = d.join(Gibbler.delimiter).__gibbler(digest_type)
|
421
440
|
a = Gibbler.digest '%s:%s:%s' % [klass, d.size, d], digest_type
|
422
441
|
gibbler_debug klass, a, [klass, d.size, d]
|
423
|
-
a
|
442
|
+
a
|
424
443
|
end
|
425
444
|
end
|
426
|
-
|
445
|
+
|
427
446
|
# Creates a digest based on:
|
428
|
-
# * parse each element into an Array of digests like:
|
429
|
-
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
430
|
-
# will be parsed recursively according to the gibbler method for that class type.
|
431
|
-
# * Digest the Array of digests
|
432
|
-
# * Return the digest for
|
447
|
+
# * parse each element into an Array of digests like: `CLASS:INDEX:VALUE.__gibbler`
|
448
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
449
|
+
# will be parsed recursively according to the gibbler method for that class type.
|
450
|
+
# * Digest the Array of digests
|
451
|
+
# * Return the digest for `class:length:value` where:
|
433
452
|
# * "class" is equal to the current object class (e.g. Array).
|
434
453
|
# * "length" is the size of the Array of digests (which should equal
|
435
454
|
# the number of elements in the original Array object).
|
436
455
|
# * "value" is the Array of digests joined with a colon (":").
|
437
456
|
#
|
438
|
-
# This method can be used by any class with an
|
457
|
+
# This method can be used by any class with an `each` method.
|
439
458
|
#
|
440
|
-
# class MyNamedArray
|
459
|
+
# class MyNamedArray
|
441
460
|
# include Gibbler::Array
|
442
461
|
# end
|
443
462
|
#
|
444
463
|
module Array
|
445
464
|
include Gibbler::Object
|
446
|
-
|
465
|
+
|
447
466
|
def self.included(obj)
|
448
467
|
obj.extend Attic
|
449
468
|
obj.attic :gibbler_cache
|
450
469
|
end
|
451
|
-
|
452
|
-
# Creates a digest for the current state of self.
|
470
|
+
|
471
|
+
# Creates a digest for the current state of self.
|
453
472
|
def __gibbler(digest_type=nil)
|
454
473
|
klass = self.class
|
455
474
|
d, index = [], 0
|
456
|
-
self.each_with_index do |value,idx|
|
475
|
+
self.each_with_index do |value,idx|
|
457
476
|
unless value.respond_to? :__gibbler
|
458
477
|
gibbler_debug klass, :skipping, idx
|
459
478
|
next
|
@@ -461,35 +480,35 @@ module Gibbler
|
|
461
480
|
d << '%s:%s:%s' % [value.class, index, value.__gibbler(digest_type)]
|
462
481
|
index += 1
|
463
482
|
end
|
464
|
-
d = d.join(
|
483
|
+
d = d.join(Gibbler.delimiter).__gibbler(digest_type)
|
465
484
|
a = Gibbler.digest '%s:%s:%s' % [klass, d.size, d], digest_type
|
466
485
|
gibbler_debug klass, a, [klass, d.size, d]
|
467
486
|
a
|
468
487
|
end
|
469
488
|
end
|
470
|
-
|
471
|
-
# Creates a digest based on:
|
472
|
-
# Times are calculated based on the equivalent time in UTC.
|
489
|
+
|
490
|
+
# Creates a digest based on: `CLASS:LENGTH:TIME`.
|
491
|
+
# Times are calculated based on the equivalent time in UTC.
|
473
492
|
# e.g.
|
474
|
-
#
|
493
|
+
#
|
475
494
|
# Time.parse('2009-08-25 16:43:53 UTC') => 73b4635f
|
476
495
|
# Time.parse('2009-08-25 12:43:53 -04:00') => 73b4635f
|
477
496
|
#
|
478
497
|
# To use use method in other classes simply:
|
479
498
|
#
|
480
|
-
# class ClassLikeTime
|
499
|
+
# class ClassLikeTime
|
481
500
|
# include Gibbler::Time
|
482
501
|
# end
|
483
502
|
#
|
484
503
|
module Time
|
485
504
|
include Gibbler::Object
|
486
|
-
|
505
|
+
|
487
506
|
def self.included(obj)
|
488
507
|
obj.extend Attic
|
489
508
|
obj.attic :gibbler_cache
|
490
509
|
end
|
491
|
-
|
492
|
-
# Creates a digest for the current state of self.
|
510
|
+
|
511
|
+
# Creates a digest for the current state of self.
|
493
512
|
def __gibbler(digest_type=nil)
|
494
513
|
klass = self.class
|
495
514
|
value = self.nil? ? "\0" : self.utc.strftime('%Y-%m-%d %H:%M:%S UTC')
|
@@ -498,29 +517,29 @@ module Gibbler
|
|
498
517
|
a
|
499
518
|
end
|
500
519
|
end
|
501
|
-
|
502
|
-
# Creates a digest based on:
|
503
|
-
# Dates are calculated based on the equivalent datetime in UTC.
|
520
|
+
|
521
|
+
# Creates a digest based on: `CLASS:LENGTH:DATETIME`.
|
522
|
+
# Dates are calculated based on the equivalent datetime in UTC.
|
504
523
|
# e.g.
|
505
|
-
#
|
524
|
+
#
|
506
525
|
# DateTime.parse('2009-08-25T17:00:40+00:00') => ad64c769
|
507
526
|
# DateTime.parse('2009-08-25T13:00:40-04:00') => ad64c769
|
508
527
|
#
|
509
528
|
# To use use method in other classes simply:
|
510
529
|
#
|
511
|
-
# class ClassLikeTime
|
530
|
+
# class ClassLikeTime
|
512
531
|
# include Gibbler::Time
|
513
532
|
# end
|
514
533
|
#
|
515
534
|
module DateTime
|
516
535
|
include Gibbler::Object
|
517
|
-
|
536
|
+
|
518
537
|
def self.included(obj)
|
519
538
|
obj.extend Attic
|
520
539
|
obj.attic :gibbler_cache
|
521
540
|
end
|
522
|
-
|
523
|
-
# Creates a digest for the current state of self.
|
541
|
+
|
542
|
+
# Creates a digest for the current state of self.
|
524
543
|
def __gibbler(digest_type=nil)
|
525
544
|
klass = self.class
|
526
545
|
value = self.nil? ? "\0" : self.new_offset(0).to_s
|
@@ -528,36 +547,36 @@ module Gibbler
|
|
528
547
|
gibbler_debug klass, a, [klass, value.size, value]
|
529
548
|
a
|
530
549
|
end
|
531
|
-
|
550
|
+
|
532
551
|
end
|
533
|
-
|
534
|
-
# Creates a digest based on:
|
552
|
+
|
553
|
+
# Creates a digest based on: `CLASS:EXCLUDE?:FIRST:LAST`
|
535
554
|
# where EXCLUDE? is a boolean value whether the Range excludes
|
536
555
|
# the last value (i.e. 1...100) and FIRST and LAST are the values
|
537
556
|
# returned by Range#first and Range#last.
|
538
557
|
# e.g.
|
539
|
-
#
|
558
|
+
#
|
540
559
|
# (1..100) => Range:false:1:100 => 54506352
|
541
560
|
# (1...100) => Range:true:1:100 => f0cad8cc
|
542
561
|
#
|
543
562
|
# To use use method in other classes simply:
|
544
563
|
#
|
545
|
-
# class ClassLikeRange
|
564
|
+
# class ClassLikeRange
|
546
565
|
# include Gibbler::Range
|
547
566
|
# end
|
548
567
|
#
|
549
568
|
module Range
|
550
569
|
include Gibbler::Object
|
551
|
-
|
570
|
+
|
552
571
|
def self.included(obj)
|
553
572
|
obj.extend Attic
|
554
573
|
obj.attic :gibbler_cache
|
555
574
|
end
|
556
|
-
|
557
|
-
# Creates a digest for the current state of self.
|
575
|
+
|
576
|
+
# Creates a digest for the current state of self.
|
558
577
|
def __gibbler(digest_type=nil)
|
559
578
|
klass = self.class
|
560
|
-
if self.nil?
|
579
|
+
if self.nil?
|
561
580
|
first, last, exclude = "\0", "\0", "\0"
|
562
581
|
else
|
563
582
|
first, last, exclude = self.first, self.last, self.exclude_end?
|
@@ -566,10 +585,10 @@ module Gibbler
|
|
566
585
|
gibbler_debug klass, a, [klass, exclude, first, last]
|
567
586
|
a
|
568
587
|
end
|
569
|
-
|
588
|
+
|
570
589
|
end
|
571
|
-
|
572
|
-
# Creates a digest based on:
|
590
|
+
|
591
|
+
# Creates a digest based on: `CLASS:\0`
|
573
592
|
#
|
574
593
|
# e.g.
|
575
594
|
#
|
@@ -583,7 +602,7 @@ module Gibbler
|
|
583
602
|
obj.attic :gibbler_cache
|
584
603
|
end
|
585
604
|
|
586
|
-
# Creates a digest for the current state of self.
|
605
|
+
# Creates a digest for the current state of self.
|
587
606
|
def __gibbler(digest_type=nil)
|
588
607
|
klass = self.class
|
589
608
|
a = Gibbler.digest "%s:%s" % [klass, "\0"], digest_type
|
@@ -591,34 +610,34 @@ module Gibbler
|
|
591
610
|
a
|
592
611
|
end
|
593
612
|
end
|
594
|
-
|
595
|
-
# Creates a digest based on:
|
613
|
+
|
614
|
+
# Creates a digest based on: `CLASS:PATHLENGTH:PATH`
|
596
615
|
# where PATHLENGTH is the length of the PATH string. PATH is
|
597
616
|
# not modified in any way (it is not converted to an absolute
|
598
|
-
# path).
|
599
|
-
#
|
617
|
+
# path).
|
618
|
+
#
|
600
619
|
# NOTE: You may expect this method to include other information
|
601
620
|
# like the file contents and modified date (etc...). The reason
|
602
621
|
# we do not is because Gibbler is concerned only about Ruby and
|
603
622
|
# not the outside world. There are many complexities in parsing
|
604
623
|
# file data and attributes which would make it difficult to run
|
605
|
-
# across platforms and Ruby versions / engines. If you want to
|
624
|
+
# across platforms and Ruby versions / engines. If you want to
|
606
625
|
#
|
607
626
|
# e.g.
|
608
|
-
#
|
627
|
+
#
|
609
628
|
# File.new('.') # => c8bc8b3a
|
610
629
|
# File.new('/tmp') # => 3af85a19
|
611
630
|
# File.new('/tmp/') # => 92cbcb7d
|
612
|
-
#
|
631
|
+
#
|
613
632
|
module File
|
614
633
|
include Gibbler::Object
|
615
|
-
|
634
|
+
|
616
635
|
def self.included(obj)
|
617
636
|
obj.extend Attic
|
618
637
|
obj.attic :gibbler_cache
|
619
638
|
end
|
620
|
-
|
621
|
-
# Creates a digest for the current state of self.
|
639
|
+
|
640
|
+
# Creates a digest for the current state of self.
|
622
641
|
def __gibbler(digest_type=nil)
|
623
642
|
klass = self.class
|
624
643
|
value = self.nil? ? "\0" : self.path
|
@@ -627,59 +646,13 @@ module Gibbler
|
|
627
646
|
a
|
628
647
|
end
|
629
648
|
end
|
630
|
-
|
631
|
-
##--
|
632
|
-
## NOTE: this was used when Gibbler supported "include Gibbler". We
|
633
|
-
## now recommend the "include Gibbler::String" approach. This was an
|
634
|
-
## interesting approach so I'm keeping the code here for reference.
|
635
|
-
##def self.included(klass)
|
636
|
-
## # Find the appropriate Gibbler::* module for the inheriting class
|
637
|
-
## gibbler_module = Gibbler.const_get("#{klass}") rescue Gibbler::String
|
638
|
-
##
|
639
|
-
## # If a Gibbler module is not defined, const_get bubbles up
|
640
|
-
## # through the stack to find the constant. This will return
|
641
|
-
## # the global class (likely itself) so we enforce a default.
|
642
|
-
## gibbler_module = Gibbler::String if gibbler_module == klass
|
643
|
-
## gibbler_debug :constant, klass, gibbler_module
|
644
|
-
##
|
645
|
-
## klass.module_eval do
|
646
|
-
## include gibbler_module
|
647
|
-
## end
|
648
|
-
##
|
649
|
-
##end
|
650
|
-
##++
|
651
|
-
|
652
|
-
|
653
|
-
end
|
654
|
-
|
655
|
-
|
656
|
-
class NilClass; include Gibbler::Nil; end
|
657
|
-
class Class; include Gibbler::Object; end
|
658
|
-
class Module; include Gibbler::Object; end
|
659
|
-
class Proc; include Gibbler::Object; end
|
660
|
-
class String; include Gibbler::String; end
|
661
|
-
class Regexp; include Gibbler::String; end
|
662
|
-
class Fixnum; include Gibbler::String; end
|
663
|
-
class Bignum; include Gibbler::String; end
|
664
|
-
class TrueClass; include Gibbler::String; end
|
665
|
-
class FalseClass; include Gibbler::String; end
|
666
|
-
class Float; include Gibbler::String; end
|
667
|
-
class Symbol; include Gibbler::String; end
|
668
|
-
class Date; include Gibbler::String; end
|
669
|
-
class Hash; include Gibbler::Hash; end
|
670
|
-
class Array; include Gibbler::Array; end
|
671
|
-
class Time; include Gibbler::Time; end
|
672
|
-
class DateTime < Date; include Gibbler::DateTime; end
|
673
|
-
class Range; include Gibbler::Range; end
|
674
|
-
class File; include Gibbler::File; end
|
675
|
-
class TempFile; include Gibbler::File; end
|
676
|
-
|
677
|
-
# URI::Generic must be included towards the
|
678
|
-
# end b/c it runs Object#freeze statically.
|
679
|
-
module URI; class Generic; include Gibbler::String; end; end
|
680
|
-
|
681
|
-
# Bundler calls freeze on an instance of Gem::Platform
|
682
|
-
module Gem; class Platform; include Gibbler::Complex; end; end
|
683
|
-
|
684
649
|
|
650
|
+
end
|
685
651
|
|
652
|
+
class String
|
653
|
+
unless method_defined? :clear
|
654
|
+
def clear
|
655
|
+
replace ""
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|