gibbler 0.8.10 → 0.10.0.pre.RC1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|