gibbler 0.3 → 0.4
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.
- data/CHANGES.txt +17 -0
- data/README.rdoc +51 -7
- data/gibbler.gemspec +1 -1
- data/lib/gibbler.rb +223 -46
- metadata +2 -2
data/CHANGES.txt
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
GIBBLER, CHANGES
|
2
2
|
|
3
3
|
|
4
|
+
#### 0.4 (2009-06-30) #################################
|
5
|
+
|
6
|
+
NOTE: Calculated gibbles have changed since 0.3. Most gibbles created with
|
7
|
+
0.3 and earlier will not match those created in 0.4 for the same object
|
8
|
+
|
9
|
+
* FIXED: Hash and Array now use the class of the value for hashing
|
10
|
+
rather than Hash or Array (respectively).
|
11
|
+
* FIXED: __gibbler methods now return a digest based on their own class.
|
12
|
+
Previously, all digests were created by String.__gibbler so the class
|
13
|
+
name from the original object got lost.
|
14
|
+
* CHANGE: Gibbler methods are no longer available to all Ruby classes
|
15
|
+
by default. The default list is now: String, Hash, Array, Symbol,
|
16
|
+
Class, Fixnum, Bignum.
|
17
|
+
* CHANGE: Renamed Gibbler.gibbler_digest_type to Gibbler.digest_type
|
18
|
+
* ADDED: Custom objects can now "include Gibbler::Complex"
|
19
|
+
|
20
|
+
|
4
21
|
#### 0.3 (2009-06-29) #################################
|
5
22
|
|
6
23
|
* CHANGE: Renamed to_gibble -> gibble
|
data/README.rdoc
CHANGED
@@ -1,30 +1,74 @@
|
|
1
|
-
= Gibbler - v0.
|
1
|
+
= Gibbler - v0.4
|
2
2
|
|
3
3
|
Git-like hashes for Ruby objects.
|
4
4
|
|
5
|
-
|
5
|
+
== Examples
|
6
|
+
|
6
7
|
config = {}
|
7
|
-
config.gibble # =>
|
8
|
+
config.gibble # => 4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf
|
9
|
+
config.gibbled? # => false
|
8
10
|
|
9
11
|
config[:server] = {
|
10
12
|
:users => [:dave, :ali],
|
11
13
|
:ports => [22, 80, 443]
|
12
14
|
}
|
13
|
-
config.
|
15
|
+
config.gibbled? # => true
|
16
|
+
config.gibble # => ef23d605f8c4fc80a8e580f9a0e8dab8426454a8
|
14
17
|
|
15
18
|
config[:server][:users] << :yanni
|
16
19
|
|
17
|
-
config.gibble # =>
|
20
|
+
config.gibble # => 4c558a56bc2abf5f8a845a69e47ceb5e0003683f
|
21
|
+
|
22
|
+
|
23
|
+
== Supported Classes
|
24
|
+
|
25
|
+
Gibbler methods are available only to the classes which explicitly include them (see RDocs[http://delano.github.com/gibbler] for details on which classes are supported by default). You can also extend custom objects:
|
26
|
+
|
27
|
+
class FullHouse
|
28
|
+
include Gibbler::Complex
|
29
|
+
attr_accessor :roles
|
30
|
+
end
|
31
|
+
|
32
|
+
a = FullHouse.new
|
33
|
+
a.gibble # => 4192d4cb59975813f117a51dcd4454ac16df6703
|
34
|
+
|
35
|
+
a.roles = [:jesse, :joey, :danny, :kimmy, :michelle, :dj, :stephanie]
|
36
|
+
a.gibble # => 6ea546919dc4caa2bab69799b71d48810a1b48fa
|
37
|
+
|
38
|
+
Gibbler::Complex creates a digest based on the name of the class and the names and values of the instance variables. See the RDocs[http://delano.github.com/gibbler] for other Gibbler::* types.
|
39
|
+
|
40
|
+
If you want to support all Ruby objects, add the following to your application:
|
18
41
|
|
42
|
+
class Object
|
43
|
+
include Gibbler::String
|
44
|
+
end
|
19
45
|
|
20
|
-
|
46
|
+
Gibbler::String creates a digest based on the output of the to_s method. This is a reasonable default for more objects however any object that includes the object address in to_s (e.g. "<Object:0x0x4ac9f0...") will produce unreliable gibbles (because the address can change).
|
47
|
+
|
48
|
+
|
49
|
+
== ALPHA NOTICE (2009-06-30)
|
21
50
|
|
22
51
|
This code is hella fresh. It's ugly and barely tested, but it's fun to play with. I'll happily accept patches.
|
23
52
|
|
53
|
+
NOTE: Gibbles have changed between 0.3 and 0.4. Ones created with 0.3 and earlier will not match ones created with 0.4 for the same object.
|
54
|
+
|
55
|
+
|
56
|
+
== Known Issues
|
57
|
+
|
58
|
+
* The gibble method or gibbled? must be called at least once before gibbled? will be able to return a useful value (otherwise there is no previous gibble value to compare to)
|
59
|
+
|
60
|
+
|
61
|
+
== More Info
|
62
|
+
|
63
|
+
* Codes[http://github.com/delano/gibbler]
|
64
|
+
* RDocs[http://delano.github.com/gibbler]
|
65
|
+
* Sponsor[http://solutious.com/]
|
66
|
+
* Inspiration[http://www.youtube.com/watch?v=fipD4DdV48g]
|
67
|
+
|
24
68
|
|
25
69
|
== Credits
|
26
70
|
|
27
|
-
* Delano
|
71
|
+
* Delano (@solutious.com)
|
28
72
|
|
29
73
|
|
30
74
|
== License
|
data/gibbler.gemspec
CHANGED
data/lib/gibbler.rb
CHANGED
@@ -6,7 +6,7 @@ require 'digest/sha1'
|
|
6
6
|
# "Hola, Tanneritos"
|
7
7
|
#
|
8
8
|
module Gibbler
|
9
|
-
VERSION = "0.
|
9
|
+
VERSION = "0.4.0"
|
10
10
|
|
11
11
|
@@gibbler_debug = false
|
12
12
|
@@gibbler_digest_type = Digest::SHA1
|
@@ -16,15 +16,15 @@ module Gibbler
|
|
16
16
|
#
|
17
17
|
# Object.gibbler_digest_type = Digest::SHA256
|
18
18
|
#
|
19
|
-
def self.
|
19
|
+
def self.digest_type=(v)
|
20
20
|
@@gibbler_digest_type = v
|
21
21
|
end
|
22
22
|
# Returns the current debug status (true or false)
|
23
|
-
def self.
|
24
|
-
|
25
|
-
def self.
|
23
|
+
def self.debug?; @@gibbler_debug; end
|
24
|
+
def self.enable_debug; @@gibbler_debug = true; end
|
25
|
+
def self.disable_debug; @@gibbler_debug = false; end
|
26
26
|
# Returns the current digest class.
|
27
|
-
def self.
|
27
|
+
def self.digest_type; @@gibbler_digest_type; end
|
28
28
|
|
29
29
|
# Calculates a digest for the current object instance.
|
30
30
|
# Objects that are a kind of Hash or Array are processed
|
@@ -37,64 +37,241 @@ module Gibbler
|
|
37
37
|
# gibbler_debug [klass, a]
|
38
38
|
# a
|
39
39
|
#end
|
40
|
-
gibbler_debug
|
40
|
+
gibbler_debug :GIBBLER, self.class, self
|
41
41
|
@__gibble__ = self.__gibbler
|
42
42
|
end
|
43
43
|
|
44
|
+
# Sends +str+ to Digest::SHA1.hexdigest. If another digest class
|
45
|
+
# has been specified, that class will be used instead.
|
46
|
+
# See: digest_type
|
47
|
+
def self.digest(str)
|
48
|
+
@@gibbler_digest_type.hexdigest str
|
49
|
+
end
|
50
|
+
|
44
51
|
# Has this object been modified?
|
52
|
+
#
|
53
|
+
# This method compares the return value from gibble with the
|
54
|
+
# previous value returned by gibble (the value is stored in
|
55
|
+
# <tt>@__gibble__</tt>)
|
45
56
|
def gibbled?
|
57
|
+
@__gibble__ ||= self.gibble
|
46
58
|
was, now = @__gibble__.clone, self.gibble
|
47
|
-
gibbler_debug
|
59
|
+
gibbler_debug :gibbled?, was, now
|
48
60
|
was != now
|
49
61
|
end
|
50
62
|
|
51
63
|
def gibbler_debug(*args)
|
52
|
-
return unless
|
53
|
-
p
|
64
|
+
return unless Gibbler.debug?
|
65
|
+
p args
|
66
|
+
end
|
67
|
+
def self.gibbler_debug(*args)
|
68
|
+
return unless Gibbler.debug?
|
69
|
+
p args
|
54
70
|
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class Hash
|
58
|
-
include Gibbler
|
59
71
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
72
|
+
# Gets the list of instance variables from the standard implementation
|
73
|
+
# of the instance_variables method and removes <tt>@__gibble__</tt>.
|
74
|
+
# Any class the includes Gibbler or Gibbler::* will use this version of
|
75
|
+
# instance_variables. It's important because we don't want the current
|
76
|
+
# gibble value to affect the next gibble.
|
77
|
+
def instance_variables
|
78
|
+
vars = super
|
79
|
+
vars.reject! { |x| x.to_s == '@__gibble__' }
|
80
|
+
vars
|
69
81
|
end
|
70
82
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
83
|
+
module Complex
|
84
|
+
include Gibbler
|
85
|
+
# Creates a gibble based on:
|
86
|
+
# * An Array of instance variable names and values in the format: <tt>CLASS:LENGTH:VALUE</tt>
|
87
|
+
# * The gibble method is called on each element so if it is a Hash or Array etc it
|
88
|
+
# will be parsed recursively according to the gibbler method for that class type.
|
89
|
+
# * Gibble the Array of gibbles
|
90
|
+
# * Return the digest for <tt>class:length:value</tt> where:
|
91
|
+
# * "class" is equal to the current object class (e.g. FullHouse).
|
92
|
+
# * "length" is the size of the Array of gibbles (which should equal
|
93
|
+
# the number of instance variables in the object).
|
94
|
+
# * "value" is the Array of gibbles joined with a colon (":").
|
95
|
+
#
|
96
|
+
# This method can be used by any class which stores values in instance variables.
|
97
|
+
#
|
98
|
+
def __gibbler(h=self)
|
99
|
+
klass = h.class
|
100
|
+
d = []
|
101
|
+
instance_variables.each do |n|
|
102
|
+
value = instance_variable_get(n)
|
103
|
+
d << '%s:%s:%s' % [value.class, n, value.__gibbler]
|
104
|
+
end
|
105
|
+
d = d.join(':').__gibbler
|
106
|
+
a = Gibbler.digest "%s:%d:%s" % [klass, d.size, d]
|
107
|
+
gibbler_debug klass, a, [klass, d.size, d]
|
108
|
+
a
|
109
|
+
end
|
110
|
+
end
|
75
111
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
112
|
+
module String
|
113
|
+
include Gibbler
|
114
|
+
# Creates a gibble based on: <tt>CLASS:LENGTH:VALUE</tt>.
|
115
|
+
# This method can be used for any class where the <tt>to_s</tt>
|
116
|
+
# method returns an appropriate unique value for this instance.
|
117
|
+
# It's used by default for Symbol, Class, Fixnum, and Bignum.
|
118
|
+
# e.g.
|
119
|
+
#
|
120
|
+
# "str" => String:3:str => 509a839ca1744c72e37759e7684ff0daa3b61427
|
121
|
+
# :sym => Symbol:3:sym => f3b7b3ca9529002c6826b1ef609d3583c356c8c8
|
122
|
+
#
|
123
|
+
# To use use method in other classes simply:
|
124
|
+
#
|
125
|
+
# class MyClass
|
126
|
+
# include Gibbler::String
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
def __gibbler(h=self)
|
130
|
+
klass = h.class
|
131
|
+
value = h.nil? ? "\0" : h.to_s
|
132
|
+
a = Gibbler.digest "%s:%d:%s" % [klass, value.size, value]
|
133
|
+
gibbler_debug klass, a, [klass, value.size, value]
|
134
|
+
a
|
82
135
|
end
|
83
|
-
a = d.join($/).__gibbler
|
84
|
-
gibbler_debug [klass, a]
|
85
|
-
a
|
86
136
|
end
|
137
|
+
|
138
|
+
module Hash
|
139
|
+
include Gibbler
|
140
|
+
|
141
|
+
# Creates a gibble based on:
|
142
|
+
# * parse each key, value pair into an Array containing keys: <tt>CLASS:KEY:VALUE.__gibbler</tt>
|
143
|
+
# * The gibble method is called on each element so if it is a Hash or Array etc it
|
144
|
+
# will be parsed recursively according to the gibbler method for that class type.
|
145
|
+
# * Gibble the Array of gibbles
|
146
|
+
# * Return the digest for <tt>class:length:value</tt> where:
|
147
|
+
# * "class" is equal to the current object class (e.g. Hash).
|
148
|
+
# * "length" is the size of the Array of gibbles (which should equal
|
149
|
+
# the number of keys in the original Hash object).
|
150
|
+
# * "value" is the Array of gibbles joined with a colon (":").
|
151
|
+
#
|
152
|
+
# This method can be used by any class with a <tt>keys</tt> method.
|
153
|
+
#
|
154
|
+
# e.g.
|
155
|
+
#
|
156
|
+
# class OrderedHash
|
157
|
+
# include Gibbler::Hash
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
def __gibbler(h=self)
|
161
|
+
klass = h.class
|
162
|
+
d = h.keys.sort { |a,b| a.inspect <=> b.inspect }
|
163
|
+
d.collect! do |name|
|
164
|
+
value = h[name]
|
165
|
+
'%s:%s:%s' % [value.class, name, value.__gibbler]
|
166
|
+
end
|
167
|
+
d = d.join(':').__gibbler
|
168
|
+
a = Gibbler.digest '%s:%s:%s' % [klass, d.size, d]
|
169
|
+
gibbler_debug klass, a, [klass, d.size, d]
|
170
|
+
a
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module Array
|
175
|
+
include Gibbler
|
176
|
+
|
177
|
+
# Creates a gibble based on:
|
178
|
+
# * parse each element into an Array of gibbles like: <tt>CLASS:INDEX:VALUE.__gibbler</tt>
|
179
|
+
# * The gibble method is called on each element so if it is a Hash or Array etc it
|
180
|
+
# will be parsed recursively according to the gibbler method for that class type.
|
181
|
+
# * Gibble the Array of gibbles
|
182
|
+
# * Return the digest for <tt>class:length:value</tt> where:
|
183
|
+
# * "class" is equal to the current object class (e.g. Array).
|
184
|
+
# * "length" is the size of the Array of gibbles (which should equal
|
185
|
+
# the number of elements in the original Array object).
|
186
|
+
# * "value" is the Array of gibbles joined with a colon (":").
|
187
|
+
#
|
188
|
+
# This method can be used by any class with an <tt>each</tt> method.
|
189
|
+
#
|
190
|
+
# e.g.
|
191
|
+
#
|
192
|
+
# class NamedArray
|
193
|
+
# include Gibbler::Array
|
194
|
+
# end
|
195
|
+
#
|
196
|
+
def __gibbler(h=self)
|
197
|
+
klass = h.class
|
198
|
+
d, index = [], 0
|
199
|
+
h.each do |value|
|
200
|
+
d << '%s:%s:%s' % [value.class, index, value.__gibbler]
|
201
|
+
index += 1
|
202
|
+
end
|
203
|
+
d = d.join(':').__gibbler
|
204
|
+
a = Gibbler.digest '%s:%s:%s' % [klass, d.size, d]
|
205
|
+
gibbler_debug klass, a, [klass, d.size, d]
|
206
|
+
a
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
##--
|
212
|
+
## NOTE: this was used when Gibbler supported "include Gibbler". We
|
213
|
+
## now recommend the "include Gibbler::String" approach. This was an
|
214
|
+
## interesting approach so I'm keeping the code here for reference.
|
215
|
+
##def self.included(klass)
|
216
|
+
## # Find the appropriate Gibbler::* module for the inheriting class
|
217
|
+
## gibbler_module = Gibbler.const_get("#{klass}") rescue Gibbler::String
|
218
|
+
##
|
219
|
+
## # If a Gibbler module is not defined, const_get bubbles up
|
220
|
+
## # through the stack to find the constant. This will return
|
221
|
+
## # the global class (likely itself) so we enforce a default.
|
222
|
+
## gibbler_module = Gibbler::String if gibbler_module == klass
|
223
|
+
## gibbler_debug :constant, klass, gibbler_module
|
224
|
+
##
|
225
|
+
## klass.module_eval do
|
226
|
+
## include gibbler_module
|
227
|
+
## end
|
228
|
+
##
|
229
|
+
##end
|
230
|
+
##++
|
231
|
+
|
87
232
|
|
88
233
|
end
|
89
234
|
|
90
|
-
class
|
91
|
-
include Gibbler
|
92
|
-
|
93
|
-
def __gibbler(h=self)
|
94
|
-
klass = h.class
|
95
|
-
value = h.nil? ? "\0" : h.to_s
|
96
|
-
a=@@gibbler_digest_type.hexdigest "%s:%d:%s" % [klass, value.size, value]
|
97
|
-
gibbler_debug [klass, value.size, value, a]
|
98
|
-
a
|
99
|
-
end
|
235
|
+
class Hash
|
236
|
+
include Gibbler::Hash
|
100
237
|
end
|
238
|
+
|
239
|
+
class Array
|
240
|
+
include Gibbler::Array
|
241
|
+
end
|
242
|
+
|
243
|
+
class String
|
244
|
+
include Gibbler::String
|
245
|
+
end
|
246
|
+
|
247
|
+
class Symbol
|
248
|
+
include Gibbler::String
|
249
|
+
end
|
250
|
+
|
251
|
+
class Class
|
252
|
+
include Gibbler::String
|
253
|
+
end
|
254
|
+
|
255
|
+
class Fixnum
|
256
|
+
include Gibbler::String
|
257
|
+
end
|
258
|
+
|
259
|
+
class Bignum
|
260
|
+
include Gibbler::String
|
261
|
+
end
|
262
|
+
|
263
|
+
class TrueClass
|
264
|
+
include Gibbler::String
|
265
|
+
end
|
266
|
+
|
267
|
+
class FalseClass
|
268
|
+
include Gibbler::String
|
269
|
+
end
|
270
|
+
|
271
|
+
class Float
|
272
|
+
include Gibbler::String
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gibbler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.4"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-30 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|