gibbler 0.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +15 -1
- data/README.rdoc +72 -21
- data/gibbler.gemspec +18 -2
- data/lib/gibbler.rb +54 -31
- data/lib/gibbler/digest.rb +27 -0
- data/lib/gibbler/history.rb +170 -0
- data/lib/gibbler/mixins.rb +4 -0
- data/lib/gibbler/mixins/string.rb +9 -0
- data/tryouts/01_mixins_tryouts.rb +13 -0
- data/tryouts/05_gibbler_digest_tryouts.rb +26 -0
- data/tryouts/10_basic_tryouts.rb +92 -0
- data/tryouts/11_basic_sha256_tryouts.rb +40 -0
- data/tryouts/50_history_tryouts.rb +42 -0
- data/tryouts/51_hash_history_tryouts.rb +91 -0
- data/tryouts/52_array_history_tryouts.rb +44 -0
- data/tryouts/53_string_history_tryouts.rb +43 -0
- data/tryouts/57_arbitrary_history_tryouts.rb +45 -0
- data/tryouts/59_history_exceptions_tryouts.rb +30 -0
- data/tryouts/80_performance_tryouts.rb +80 -0
- data/tryouts/object_hash_demo.rb +30 -0
- metadata +19 -3
data/CHANGES.txt
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
GIBBLER, CHANGES
|
2
2
|
|
3
3
|
|
4
|
+
#### 0.5 (2009-07-01) #################################
|
5
|
+
|
6
|
+
NOTE: This is a significant change from 0.4. Many method names
|
7
|
+
have been modified so this release is not backwards compatible.
|
8
|
+
|
9
|
+
* CHANGE: Now refer to "gibble" as "digest" in all docs and methods.
|
10
|
+
* CHANGE: Gibbler#gibble -> Gibbler#gibbler
|
11
|
+
* CHANGE: Gibble is now Gibbler::Digest
|
12
|
+
* ADDED: Gibbler::History, supporting gibbler_snapshots and gibbler_revert
|
13
|
+
for the following objects: Array, Hash
|
14
|
+
* ADDED: Support for short, 8-character digests
|
15
|
+
* ADDED: Expanded test coverage
|
16
|
+
|
17
|
+
|
4
18
|
#### 0.4 (2009-06-30) #################################
|
5
19
|
|
6
|
-
NOTE: Calculated
|
20
|
+
NOTE: Calculated digests have changed since 0.3. Most digests created with
|
7
21
|
0.3 and earlier will not match those created in 0.4 for the same object
|
8
22
|
|
9
23
|
* FIXED: Hash and Array now use the class of the value for hashing
|
data/README.rdoc
CHANGED
@@ -1,25 +1,64 @@
|
|
1
|
-
= Gibbler - v0.
|
1
|
+
= Gibbler - v0.5 ALPHA
|
2
2
|
|
3
|
-
Git-like hashes for Ruby objects.
|
3
|
+
Git-like hashes and history for Ruby objects.
|
4
4
|
|
5
|
-
==
|
6
|
-
|
7
|
-
config = {}
|
8
|
-
config.gibble # => 4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf
|
9
|
-
config.gibbled? # => false
|
5
|
+
== Example 1 -- Basic Usage
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
config.gibbled?
|
16
|
-
|
7
|
+
require 'gibbler'
|
8
|
+
|
9
|
+
config = {}
|
10
|
+
config.gibbler # => 4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf
|
11
|
+
config.gibbled? # => false
|
12
|
+
|
13
|
+
config[:server] = {
|
14
|
+
:users => [:dave, :ali],
|
15
|
+
:ports => [22, 80, 443]
|
16
|
+
}
|
17
|
+
config.gibbled? # => true
|
18
|
+
config.gibbler # => ef23d605f8c4fc80a8e580f9a0e8dab8426454a8
|
17
19
|
|
18
20
|
config[:server][:users] << :yanni
|
19
21
|
|
20
|
-
config.
|
22
|
+
config.gibbler # => 4c558a56bc2abf5f8a845a69e47ceb5e0003683f
|
23
|
+
|
24
|
+
config.gibbler.short # => 4c558a56
|
25
|
+
|
21
26
|
|
27
|
+
== Example 2 -- Object History
|
28
|
+
|
29
|
+
Gibbler can also keep track of the history of changes to an object. By default Gibbler supports history for Hash, Array, and String objects. The <tt>gibbler_commit</tt> method creates a clone of the current object and stores in an instance variable using the current hash digest as the key.
|
22
30
|
|
31
|
+
require 'gibbler'
|
32
|
+
require 'gibbler/history'
|
33
|
+
|
34
|
+
a = { :magic => :original }
|
35
|
+
a.gibbler_commit # => d7049916ddb25e6cc438b1028fb957e5139f9910
|
36
|
+
|
37
|
+
a[:magic] = :updated
|
38
|
+
a.gibbler_commit # => b668098e16d08898532bf3aa33ce2253a3a4150e
|
39
|
+
|
40
|
+
a[:magic] = :changed
|
41
|
+
a.gibbler_commit # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
|
42
|
+
|
43
|
+
a.gibbler_history # => d7049916, b668098e, 0b11c377
|
44
|
+
|
45
|
+
a.gibbler_revert 'd7049916' # Return to a specific commit
|
46
|
+
a.gibbler # => d7049916ddb25e6cc438b1028fb957e5139f9910
|
47
|
+
a # => { :magic => :original }
|
48
|
+
|
49
|
+
a.delete :magic
|
50
|
+
|
51
|
+
a.gibbler_revert # Return to the previous commit
|
52
|
+
a.gibbler # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
|
53
|
+
a # => { :magic => :changed }
|
54
|
+
|
55
|
+
|
56
|
+
a.gibbler_object 'b668098e' # => { :magic => :updated }
|
57
|
+
a.gibbler_stamp # => 2009-07-01 18:56:52 -0400
|
58
|
+
|
59
|
+
http://delano.github.com/gibbler/img/whoababy.gif
|
60
|
+
|
61
|
+
|
23
62
|
== Supported Classes
|
24
63
|
|
25
64
|
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:
|
@@ -30,10 +69,10 @@ Gibbler methods are available only to the classes which explicitly include them
|
|
30
69
|
end
|
31
70
|
|
32
71
|
a = FullHouse.new
|
33
|
-
a.
|
72
|
+
a.gibbler # => 4192d4cb59975813f117a51dcd4454ac16df6703
|
34
73
|
|
35
74
|
a.roles = [:jesse, :joey, :danny, :kimmy, :michelle, :dj, :stephanie]
|
36
|
-
a.
|
75
|
+
a.gibbler # => 6ea546919dc4caa2bab69799b71d48810a1b48fa
|
37
76
|
|
38
77
|
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
78
|
|
@@ -43,19 +82,31 @@ If you want to support all Ruby objects, add the following to your application:
|
|
43
82
|
include Gibbler::String
|
44
83
|
end
|
45
84
|
|
46
|
-
Gibbler::String creates a digest based on the output of the to_s method. This is a reasonable default for
|
85
|
+
Gibbler::String creates a digest based on the name of the class and the output of the to_s method. This is a reasonable default for most objects however any object that includes the object address in to_s (e.g. "<Object:0x0x4ac9f0...") will produce unreliable digests (because the address can change).
|
86
|
+
|
87
|
+
|
88
|
+
== ALPHA Notice
|
89
|
+
|
90
|
+
This code is hella fresh (est 2009-06-25). It's barely tested and the interface may change, but it's fun to play with. I'll happily accept patches.
|
91
|
+
|
92
|
+
NOTE: Gibbler history is not suitable for very large objects since it keeps complete copies of the object in memory. This is a very early implementation of this feature so don't rely on it for production code.
|
93
|
+
|
94
|
+
|
95
|
+
== News
|
96
|
+
|
97
|
+
=== 2009-07-01: Major namespace reorganization
|
47
98
|
|
99
|
+
Many method names have been modified so this release is not backwards compatible with previous releases. This clears up ambiguity regarding "gibbles". They're now rightly referred to as digests.
|
48
100
|
|
49
|
-
== ALPHA NOTICE (2009-06-30)
|
50
101
|
|
51
|
-
|
102
|
+
=== 2009-06-30: Digests have changed in 0.4
|
52
103
|
|
53
|
-
|
104
|
+
Digests 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
105
|
|
55
106
|
|
56
107
|
== Known Issues
|
57
108
|
|
58
|
-
*
|
109
|
+
* gibbler or gibbled? must be called at least once before gibbled? will be able to return a useful value (otherwise there is no previous digest value to compare to)
|
59
110
|
|
60
111
|
|
61
112
|
== More Info
|
data/gibbler.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = "gibbler"
|
3
3
|
s.rubyforge_project = "gibbler"
|
4
|
-
s.version = "0.
|
4
|
+
s.version = "0.5.0"
|
5
5
|
s.summary = "Gibbler: Git-like hashes for Ruby objects"
|
6
6
|
s.description = s.summary
|
7
7
|
s.author = "Delano Mandelbaum"
|
@@ -25,12 +25,28 @@
|
|
25
25
|
|
26
26
|
# = MANIFEST =
|
27
27
|
s.files = %w(
|
28
|
-
README.rdoc
|
29
28
|
CHANGES.txt
|
30
29
|
LICENSE.txt
|
30
|
+
README.rdoc
|
31
31
|
Rakefile
|
32
32
|
gibbler.gemspec
|
33
33
|
lib/gibbler.rb
|
34
|
+
lib/gibbler/digest.rb
|
35
|
+
lib/gibbler/history.rb
|
36
|
+
lib/gibbler/mixins.rb
|
37
|
+
lib/gibbler/mixins/string.rb
|
38
|
+
tryouts/01_mixins_tryouts.rb
|
39
|
+
tryouts/05_gibbler_digest_tryouts.rb
|
40
|
+
tryouts/10_basic_tryouts.rb
|
41
|
+
tryouts/11_basic_sha256_tryouts.rb
|
42
|
+
tryouts/50_history_tryouts.rb
|
43
|
+
tryouts/51_hash_history_tryouts.rb
|
44
|
+
tryouts/52_array_history_tryouts.rb
|
45
|
+
tryouts/53_string_history_tryouts.rb
|
46
|
+
tryouts/57_arbitrary_history_tryouts.rb
|
47
|
+
tryouts/59_history_exceptions_tryouts.rb
|
48
|
+
tryouts/80_performance_tryouts.rb
|
49
|
+
tryouts/object_hash_demo.rb
|
34
50
|
)
|
35
51
|
|
36
52
|
s.has_rdoc = true
|
data/lib/gibbler.rb
CHANGED
@@ -6,10 +6,17 @@ require 'digest/sha1'
|
|
6
6
|
# "Hola, Tanneritos"
|
7
7
|
#
|
8
8
|
module Gibbler
|
9
|
-
VERSION = "0.
|
9
|
+
VERSION = "0.5.0"
|
10
|
+
|
11
|
+
require 'gibbler/digest'
|
12
|
+
require 'gibbler/mixins'
|
13
|
+
|
14
|
+
class Error < RuntimeError
|
15
|
+
def initialize(obj); @obj = obj; end
|
16
|
+
end
|
10
17
|
|
11
18
|
@@gibbler_debug = false
|
12
|
-
@@gibbler_digest_type = Digest::SHA1
|
19
|
+
@@gibbler_digest_type = ::Digest::SHA1
|
13
20
|
|
14
21
|
# Specify a different digest class. The default is +Digest::SHA1+. You
|
15
22
|
# could try +Digest::SHA256+ by doing this:
|
@@ -30,7 +37,7 @@ module Gibbler
|
|
30
37
|
# Objects that are a kind of Hash or Array are processed
|
31
38
|
# recursively. The length of the returned String depends
|
32
39
|
# on the digest type.
|
33
|
-
def
|
40
|
+
def gibbler
|
34
41
|
#if h.respond_to? :__custom_gibbler
|
35
42
|
# d = h.__custom_gibbler
|
36
43
|
# a = __gibbler '%s:%s:%s' % [klass, d.size, d]
|
@@ -38,7 +45,8 @@ module Gibbler
|
|
38
45
|
# a
|
39
46
|
#end
|
40
47
|
gibbler_debug :GIBBLER, self.class, self
|
41
|
-
@
|
48
|
+
@__gibbler_digest__ = Gibbler::Digest.new self.__gibbler
|
49
|
+
@__gibbler_digest__
|
42
50
|
end
|
43
51
|
|
44
52
|
# Sends +str+ to Digest::SHA1.hexdigest. If another digest class
|
@@ -50,12 +58,12 @@ module Gibbler
|
|
50
58
|
|
51
59
|
# Has this object been modified?
|
52
60
|
#
|
53
|
-
# This method compares the return value from
|
54
|
-
# previous value returned by
|
55
|
-
# <tt>@
|
61
|
+
# This method compares the return value from digest with the
|
62
|
+
# previous value returned by gibbler (the value is stored in
|
63
|
+
# <tt>@__gibbler_digest__</tt>)
|
56
64
|
def gibbled?
|
57
|
-
@
|
58
|
-
was, now = @
|
65
|
+
@__gibbler_digest__ ||= self.gibbler
|
66
|
+
was, now = @__gibbler_digest__.clone, self.gibbler
|
59
67
|
gibbler_debug :gibbled?, was, now
|
60
68
|
was != now
|
61
69
|
end
|
@@ -70,28 +78,34 @@ module Gibbler
|
|
70
78
|
end
|
71
79
|
|
72
80
|
# Gets the list of instance variables from the standard implementation
|
73
|
-
# of the instance_variables method and removes
|
81
|
+
# of the instance_variables method and removes all that
|
82
|
+
# begin with <tt>@__gibbler</tt>.
|
74
83
|
# Any class the includes Gibbler or Gibbler::* will use this version of
|
75
84
|
# instance_variables. It's important because we don't want the current
|
76
|
-
#
|
85
|
+
# digest value to affect the next gibble.
|
86
|
+
#
|
87
|
+
# This is also critical for objects that include Gibbler::Complex b/c
|
88
|
+
# it deals explicitly with instance variables. If it sees the __gibbler
|
89
|
+
# variables it will go bananas.
|
90
|
+
#
|
77
91
|
def instance_variables
|
78
92
|
vars = super
|
79
|
-
vars.reject! { |
|
93
|
+
vars.reject! { |e| e.to_s =~ /^@__gibble/ }
|
80
94
|
vars
|
81
95
|
end
|
82
96
|
|
83
97
|
module Complex
|
84
98
|
include Gibbler
|
85
|
-
# Creates a
|
99
|
+
# Creates a digest based on:
|
86
100
|
# * An Array of instance variable names and values in the format: <tt>CLASS:LENGTH:VALUE</tt>
|
87
|
-
# * The
|
101
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
88
102
|
# will be parsed recursively according to the gibbler method for that class type.
|
89
|
-
# *
|
103
|
+
# * Digest the Array of digests
|
90
104
|
# * Return the digest for <tt>class:length:value</tt> where:
|
91
105
|
# * "class" is equal to the current object class (e.g. FullHouse).
|
92
|
-
# * "length" is the size of the Array of
|
106
|
+
# * "length" is the size of the Array of digests (which should equal
|
93
107
|
# the number of instance variables in the object).
|
94
|
-
# * "value" is the Array of
|
108
|
+
# * "value" is the Array of digests joined with a colon (":").
|
95
109
|
#
|
96
110
|
# This method can be used by any class which stores values in instance variables.
|
97
111
|
#
|
@@ -107,11 +121,20 @@ module Gibbler
|
|
107
121
|
gibbler_debug klass, a, [klass, d.size, d]
|
108
122
|
a
|
109
123
|
end
|
124
|
+
|
125
|
+
def __gibbler_revert
|
126
|
+
state = self.gibbler_object @__gibbler_digest__
|
127
|
+
state.instance_variables do |n|
|
128
|
+
v = state.instance_variable_get n
|
129
|
+
self.instance_variable_set v
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
110
133
|
end
|
111
134
|
|
112
135
|
module String
|
113
136
|
include Gibbler
|
114
|
-
# Creates a
|
137
|
+
# Creates a digest based on: <tt>CLASS:LENGTH:VALUE</tt>.
|
115
138
|
# This method can be used for any class where the <tt>to_s</tt>
|
116
139
|
# method returns an appropriate unique value for this instance.
|
117
140
|
# It's used by default for Symbol, Class, Fixnum, and Bignum.
|
@@ -138,16 +161,16 @@ module Gibbler
|
|
138
161
|
module Hash
|
139
162
|
include Gibbler
|
140
163
|
|
141
|
-
# Creates a
|
164
|
+
# Creates a digest based on:
|
142
165
|
# * parse each key, value pair into an Array containing keys: <tt>CLASS:KEY:VALUE.__gibbler</tt>
|
143
|
-
# * The
|
166
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
144
167
|
# will be parsed recursively according to the gibbler method for that class type.
|
145
|
-
# *
|
168
|
+
# * Digest the Array of digests
|
146
169
|
# * Return the digest for <tt>class:length:value</tt> where:
|
147
170
|
# * "class" is equal to the current object class (e.g. Hash).
|
148
|
-
# * "length" is the size of the Array of
|
171
|
+
# * "length" is the size of the Array of digests (which should equal
|
149
172
|
# the number of keys in the original Hash object).
|
150
|
-
# * "value" is the Array of
|
173
|
+
# * "value" is the Array of digests joined with a colon (":").
|
151
174
|
#
|
152
175
|
# This method can be used by any class with a <tt>keys</tt> method.
|
153
176
|
#
|
@@ -174,16 +197,16 @@ module Gibbler
|
|
174
197
|
module Array
|
175
198
|
include Gibbler
|
176
199
|
|
177
|
-
# Creates a
|
178
|
-
# * parse each element into an Array of
|
179
|
-
# * The
|
200
|
+
# Creates a digest based on:
|
201
|
+
# * parse each element into an Array of digests like: <tt>CLASS:INDEX:VALUE.__gibbler</tt>
|
202
|
+
# * The gibbler method is called on each element so if it is a Hash or Array etc it
|
180
203
|
# will be parsed recursively according to the gibbler method for that class type.
|
181
|
-
# *
|
204
|
+
# * Digest the Array of digests
|
182
205
|
# * Return the digest for <tt>class:length:value</tt> where:
|
183
206
|
# * "class" is equal to the current object class (e.g. Array).
|
184
|
-
# * "length" is the size of the Array of
|
207
|
+
# * "length" is the size of the Array of digests (which should equal
|
185
208
|
# the number of elements in the original Array object).
|
186
|
-
# * "value" is the Array of
|
209
|
+
# * "value" is the Array of digests joined with a colon (":").
|
187
210
|
#
|
188
211
|
# This method can be used by any class with an <tt>each</tt> method.
|
189
212
|
#
|
@@ -228,8 +251,8 @@ module Gibbler
|
|
228
251
|
##
|
229
252
|
##end
|
230
253
|
##++
|
231
|
-
|
232
|
-
|
254
|
+
|
255
|
+
|
233
256
|
end
|
234
257
|
|
235
258
|
class Hash
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
# = Gibbler::Digest
|
3
|
+
#
|
4
|
+
# A tiny subclass of String which adds a
|
5
|
+
# few digest related convenience methods.
|
6
|
+
#
|
7
|
+
class Gibbler::Digest < String
|
8
|
+
|
9
|
+
# Returns the first 8 characters of itself (the digest).
|
10
|
+
#
|
11
|
+
# e.g.
|
12
|
+
#
|
13
|
+
# "kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
14
|
+
# "kimmy".gibbler.short # => c8027100
|
15
|
+
#
|
16
|
+
def short
|
17
|
+
self[0..7]
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def ==(g)
|
22
|
+
return true if self.to_s == g.to_s
|
23
|
+
return true if self.short.to_s == g.to_s
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Gibbler
|
5
|
+
class NoRevert < Gibbler::Error
|
6
|
+
def message; "Revert not implemented for #{@obj}" end
|
7
|
+
end
|
8
|
+
class NoHistory < Gibbler::Error
|
9
|
+
def message; "No history for #{@obj}"; end
|
10
|
+
end
|
11
|
+
class BadDigest < Gibbler::Error
|
12
|
+
def message; "Unknown digest: #{@obj}"; end
|
13
|
+
end
|
14
|
+
|
15
|
+
module History
|
16
|
+
|
17
|
+
@@mutex = Mutex.new
|
18
|
+
|
19
|
+
def self.mutex; @@mutex; end
|
20
|
+
|
21
|
+
# Returns an Array of digests in the order they were committed.
|
22
|
+
# If +short+ is anything but false, the digests will be converted
|
23
|
+
# to the short 8 character digests.
|
24
|
+
def gibbler_history(short=false)
|
25
|
+
# Only a single thread should attempt to initialize the store.
|
26
|
+
if @__gibbler_history__.nil?
|
27
|
+
@@mutex.synchronize {
|
28
|
+
@__gibbler_history__ ||= { :history => [], :objects => {}, :stamp => {} }
|
29
|
+
}
|
30
|
+
end
|
31
|
+
if short == false
|
32
|
+
@__gibbler_history__[:history]
|
33
|
+
else
|
34
|
+
@__gibbler_history__[:history].collect { |g| g.short }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the object stored under the given digest +g+.
|
39
|
+
# If +g+ is not a valid digest, returns nil.
|
40
|
+
def gibbler_object(g=nil)
|
41
|
+
g = gibbler_find_long g
|
42
|
+
g = self.gibbler_history.last if g.nil?
|
43
|
+
|
44
|
+
return unless gibbler_valid? g
|
45
|
+
@__gibbler_history__[:objects][ g ]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the timestamp (a Time object) when the digest +g+ was committed.
|
49
|
+
# If +g+ is not a valid gibble, returns nil.
|
50
|
+
def gibbler_stamp(g=nil)
|
51
|
+
g = gibbler_find_long g
|
52
|
+
g = self.gibbler_history.last if g.nil?
|
53
|
+
return unless gibbler_valid? g
|
54
|
+
@__gibbler_history__[:stamp][ g ]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Stores a clone of the current object instance using the current
|
58
|
+
# digest value. If the object was not changed, this method does
|
59
|
+
# nothing but return the gibble.
|
60
|
+
#
|
61
|
+
# NOTE: This method is not fully thread safe. It uses a Mutex.synchronize
|
62
|
+
# but there's a race condition where two threads can attempt to commit at
|
63
|
+
# near the same time. The first will get the lock and create the commit.
|
64
|
+
# The second will get the lock and create another commit immediately
|
65
|
+
# after. What we probably want is for the second thread to return the
|
66
|
+
# digest for that first snapshot, but how do we know this was a result
|
67
|
+
# of the race conditioon rather than two legitimate calls for a snapshot?
|
68
|
+
def gibbler_commit
|
69
|
+
now, digest, point = nil,nil,nil
|
70
|
+
|
71
|
+
if @__gibbler_history__.nil?
|
72
|
+
@@mutex.synchronize {
|
73
|
+
@__gibbler_history__ ||= { :history => [], :objects => {}, :stamp => {} }
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
@@mutex.synchronize {
|
78
|
+
now, digest, point = Time.now, self.gibbler, self.clone
|
79
|
+
@__gibbler_history__[:history] << digest
|
80
|
+
@__gibbler_history__[:stamp][digest] = now
|
81
|
+
@__gibbler_history__[:objects][digest] = point
|
82
|
+
}
|
83
|
+
|
84
|
+
digest
|
85
|
+
end
|
86
|
+
|
87
|
+
# Revert this object to a previously known state. If called without arguments
|
88
|
+
# it will revert to the most recent commit. If a digest is specified +g+, it
|
89
|
+
# will revert to that point.
|
90
|
+
#
|
91
|
+
# Ruby does not support replacing self (<tt>self = previous_self</tt>) so each
|
92
|
+
# object type needs to implement its own __gibbler_revert method. This default
|
93
|
+
# run some common checks and then defers to self.__gibbler_revert.
|
94
|
+
#
|
95
|
+
# Raise the following exceptions:
|
96
|
+
# * NoRevert: if this object doesn't have a __gibbler_revert method
|
97
|
+
# * NoHistory: This object has no commits
|
98
|
+
# * BadDigest: The given digest is not in the history for this object
|
99
|
+
#
|
100
|
+
# If +g+ matches the current digest value this method does nothing.
|
101
|
+
#
|
102
|
+
# Returns the new digest (+g+).
|
103
|
+
def gibbler_revert(g=nil)
|
104
|
+
raise NoRevert unless self.respond_to?(:__gibbler_revert)
|
105
|
+
raise NoHistory, self.class unless gibbler_history?
|
106
|
+
raise BadDigest, g if !g.nil? && !gibbler_valid?(g)
|
107
|
+
|
108
|
+
g = self.gibbler_history.last if g.nil?
|
109
|
+
g = gibbler_find_long g
|
110
|
+
|
111
|
+
# Do nothing if the given digest matches the current gibble.
|
112
|
+
# NOTE: We use __gibbler b/c it doesn't update @@__gibbler_digest__.
|
113
|
+
unless self.__gibbler == g
|
114
|
+
@@mutex.synchronize {
|
115
|
+
# Always make sure @__gibbler_digest__ is a Gibbler::Digest
|
116
|
+
@__gibbler_digest__ = g.is_a?(Gibbler::Digest) ? g : Gibbler::Digest.new(g)
|
117
|
+
self.__gibbler_revert
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
@__gibbler_digest__
|
122
|
+
end
|
123
|
+
|
124
|
+
# Is the given digest +g+ contained in the history for this object?
|
125
|
+
def gibbler_valid?(g)
|
126
|
+
return false unless gibbler_history?
|
127
|
+
gibbler_history.member? gibbler_find_long(g)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Does the current object have any history?
|
131
|
+
def gibbler_history?
|
132
|
+
!gibbler_history.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the long digest associated to the short digest +g+.
|
136
|
+
# If g is longer than 8 characters it returns the value of +g+.
|
137
|
+
def gibbler_find_long(g)
|
138
|
+
return if g.nil?
|
139
|
+
return g if g.size > 8
|
140
|
+
gibbler_history.select { |d| d.match /\A#{g}/ }.first
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
class Hash
|
147
|
+
include Gibbler::History
|
148
|
+
def __gibbler_revert
|
149
|
+
self.clear
|
150
|
+
self.merge! self.gibbler_object @__gibbler_digest__
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class Array
|
155
|
+
include Gibbler::History
|
156
|
+
def __gibbler_revert
|
157
|
+
self.clear
|
158
|
+
self.push *(self.gibbler_object @__gibbler_digest__)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class String
|
163
|
+
include Gibbler::History
|
164
|
+
def __gibbler_revert
|
165
|
+
self.clear
|
166
|
+
self << (self.gibbler_object @__gibbler_digest__)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
library :gibbler, 'lib'
|
3
|
+
|
4
|
+
group "Gibbler::Digest"
|
5
|
+
|
6
|
+
tryouts "All methods" do
|
7
|
+
|
8
|
+
|
9
|
+
dream :class, Gibbler::Digest
|
10
|
+
dream 'c8027100'
|
11
|
+
drill "has short method" do
|
12
|
+
"kimmy".gibbler.short
|
13
|
+
end
|
14
|
+
|
15
|
+
dream :class, Gibbler::Digest
|
16
|
+
dream "12345678"
|
17
|
+
drill "can Gibbler::Digest#short" do
|
18
|
+
Gibbler::Digest.new("1234567890").short
|
19
|
+
end
|
20
|
+
|
21
|
+
drill "can return true if compared with short", true do
|
22
|
+
Gibbler::Digest.new("1234567890") == "12345678"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
group "Gibbler Gazette"
|
4
|
+
|
5
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
6
|
+
|
7
|
+
tryouts "Basic syntax with SHA1" do
|
8
|
+
|
9
|
+
dream :respond_to?, :gibbler
|
10
|
+
dream :gibbler, '52be7494a602d85ff5d8a8ab4ffe7f1b171587df'
|
11
|
+
drill "Symbol can gibbler", :kimmy
|
12
|
+
|
13
|
+
dream :respond_to?, :gibbler
|
14
|
+
dream :gibbler, 'c8027100ecc54945ab15ddac529230e38b1ba6a1'
|
15
|
+
drill "String can gibbler" do
|
16
|
+
"kimmy"
|
17
|
+
end
|
18
|
+
|
19
|
+
drill "String and Symbol return different digests", true do
|
20
|
+
:kimmy.gibbler != "kimmy"
|
21
|
+
end
|
22
|
+
|
23
|
+
dream :respond_to?, :gibbler
|
24
|
+
dream :gibbler, '25ac269ae3ef18cdb4143ad02ca315afb5026de9'
|
25
|
+
drill "Class can gibbler", Class
|
26
|
+
|
27
|
+
dream :respond_to?, :gibbler
|
28
|
+
dream :gibbler, '4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf'
|
29
|
+
drill "Empty Hash instance", Hash.new
|
30
|
+
|
31
|
+
dream :gibbler, 'a9cad665549bd22a4346fcf602d9d3c3b0482bbe'
|
32
|
+
drill "Fixnum instance" do
|
33
|
+
1
|
34
|
+
end
|
35
|
+
|
36
|
+
dream :gibbler, '259afadb4ef8abaeb367db97d0c3015c8a4a504a'
|
37
|
+
drill "Bignum instance" do
|
38
|
+
100000000000
|
39
|
+
end
|
40
|
+
|
41
|
+
dream :gibbler, "1d4b62e1e9f2c097b0cefb6877bf47c2015cdd21"
|
42
|
+
drill "Populated Hash instance" do
|
43
|
+
{
|
44
|
+
:a => [1,2,3, [4,5,6]],
|
45
|
+
:b => { :c => Class }
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
dream :respond_to?, :gibbler
|
50
|
+
dream :gibbler, '48fda57c05684c9e5c3259557851943572183a21'
|
51
|
+
drill "Empty Array instance", Array.new
|
52
|
+
|
53
|
+
dream :gibbler, "884e5713aa70468333459f80aea1bb05394ca4ba"
|
54
|
+
drill "Populated Array instance" do
|
55
|
+
[1, 22222222222, :runtime, [2, "three", [Object, true]]]
|
56
|
+
end
|
57
|
+
|
58
|
+
drill "Knows when a Hash has not changed", false do
|
59
|
+
a = { :magic => true }
|
60
|
+
a.gibbler
|
61
|
+
a[:magic] = true
|
62
|
+
a.gibbled?
|
63
|
+
end
|
64
|
+
|
65
|
+
drill "Knows when a Hash has changed", true do
|
66
|
+
a = { :magic => true }
|
67
|
+
a.gibbler
|
68
|
+
a[:magic] = false
|
69
|
+
a.gibbled?
|
70
|
+
end
|
71
|
+
|
72
|
+
dream :gibbler, "6ea546919dc4caa2bab69799b71d48810a1b48fa"
|
73
|
+
drill "works on arbitrary objects" do
|
74
|
+
class ::FullHouse
|
75
|
+
include Gibbler::Complex
|
76
|
+
attr_accessor :roles
|
77
|
+
end
|
78
|
+
a = FullHouse.new
|
79
|
+
a.roles = [:jesse, :joey, :danny, :kimmy, :michelle, :dj, :stephanie]
|
80
|
+
a
|
81
|
+
end
|
82
|
+
|
83
|
+
drill "doesn't reveal @__gibbler_digest__ instance variable", false do
|
84
|
+
a = {}
|
85
|
+
a.gibbler # We need to gibbler first so it sets a value to the instance var
|
86
|
+
val = Tryouts.sysinfo.ruby[1] == 9 ? :'@__gibbler_digest__' : '@__gibbler_digest__'
|
87
|
+
a.instance_variables.member? val
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
group "Gibbler Gazette"
|
4
|
+
|
5
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
6
|
+
|
7
|
+
|
8
|
+
tryouts "Basic syntax with SHA256" do
|
9
|
+
|
10
|
+
# NOTE: JRuby require that we use OpenSSL::Digest::SHA256
|
11
|
+
if Tryouts.sysinfo.vm == :java
|
12
|
+
drill "Can change Digest type", OpenSSL::Digest::SHA256 do
|
13
|
+
Gibbler.digest_type = OpenSSL::Digest::SHA256
|
14
|
+
end
|
15
|
+
else
|
16
|
+
drill "Can change Digest type", Digest::SHA256 do
|
17
|
+
Gibbler.digest_type = Digest::SHA256
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
dream :respond_to?, :gibbler
|
22
|
+
dream :gibbler, '754f87ca720ec256633a286d9270d68478850b2abd7b0ae65021cb769ae70c08'
|
23
|
+
drill "A Symbol can gibbler", :anything
|
24
|
+
|
25
|
+
dream :respond_to?, :gibbler
|
26
|
+
dream :gibbler, 'd345c0afb4e8da0133a3946d3bd9b2622b0acdd8d6cc1237470cc637a9e4777f'
|
27
|
+
drill "Class can gibbler", Class
|
28
|
+
|
29
|
+
dream :respond_to?, :gibbler
|
30
|
+
dream :gibbler, '88d2bcbd68ce593fd2e0e06f276f7301357516291b95c0c53038e61a9bf091e5'
|
31
|
+
drill "Empty Hash instance", {}
|
32
|
+
|
33
|
+
drill "Can return Digest type", Digest::SHA1 do
|
34
|
+
Gibbler.digest_type = Digest::SHA1
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
4
|
+
|
5
|
+
group "History"
|
6
|
+
|
7
|
+
tryouts "Basics" do
|
8
|
+
|
9
|
+
dream "d7049916ddb25e6cc438b1028fb957e5139f9910"
|
10
|
+
drill "can convert short digest into long" do
|
11
|
+
a = { :magic => :original }
|
12
|
+
g = a.gibbler_commit.short
|
13
|
+
stash :short, g
|
14
|
+
a.gibbler_find_long g
|
15
|
+
end
|
16
|
+
|
17
|
+
dream :class, Time
|
18
|
+
drill "can return most recent stamp" do
|
19
|
+
a = { :magic => :original }
|
20
|
+
a.gibbler_commit
|
21
|
+
stash :hist, a.gibbler_history
|
22
|
+
a.gibbler_stamp
|
23
|
+
end
|
24
|
+
|
25
|
+
dream ["d7049916ddb25e6cc438b1028fb957e5139f9910", "0b11c377fccd44554a601e5d2b135c46dc1c4cb1"]
|
26
|
+
drill "can return history" do
|
27
|
+
a = { :magic => :original }
|
28
|
+
a.gibbler_commit
|
29
|
+
a[:magic] = :changed
|
30
|
+
a.gibbler_commit
|
31
|
+
a.gibbler_history
|
32
|
+
end
|
33
|
+
|
34
|
+
dream ["d7049916", "0b11c377"]
|
35
|
+
drill "can return history (short)" do
|
36
|
+
a = { :magic => :original }
|
37
|
+
a.gibbler_commit
|
38
|
+
a[:magic] = :changed
|
39
|
+
a.gibbler_commit
|
40
|
+
a.gibbler_history(:short)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
4
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
5
|
+
|
6
|
+
group "History"
|
7
|
+
|
8
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
9
|
+
|
10
|
+
|
11
|
+
tryouts "Hash History" do
|
12
|
+
|
13
|
+
drill "Setup Hash class", Hash do
|
14
|
+
class ::Hash
|
15
|
+
include Gibbler::History
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
drill "doesn't reveal @__gibbler_history__ instance variable", false do
|
20
|
+
a = {}
|
21
|
+
a.gibbler # We need to gibbler first so it sets a value to the instance var
|
22
|
+
val = Tryouts.sysinfo.ruby[1] == 9 ? :'@__gibbler_history__' : '@__gibbler_history__'
|
23
|
+
a.instance_variables.member? val
|
24
|
+
end
|
25
|
+
|
26
|
+
drill "can take a Hash snapshot", 'd7049916ddb25e6cc438b1028fb957e5139f9910' do
|
27
|
+
a = { :magic => :original }
|
28
|
+
a.gibbler_commit
|
29
|
+
end
|
30
|
+
|
31
|
+
dream :class, Array
|
32
|
+
dream :size, 2
|
33
|
+
dream ['d7049916ddb25e6cc438b1028fb957e5139f9910', 'b668098e16d08898532bf3aa33ce2253a3a4150e']
|
34
|
+
drill "return a Hash history" do
|
35
|
+
a = { :magic => :original }
|
36
|
+
a.gibbler_commit
|
37
|
+
a[:magic] = :updated
|
38
|
+
a.gibbler_commit
|
39
|
+
a.gibbler_history
|
40
|
+
end
|
41
|
+
|
42
|
+
dream 'd7049916ddb25e6cc438b1028fb957e5139f9910'
|
43
|
+
drill "can revert Hash" do
|
44
|
+
a = { :magic => :original }
|
45
|
+
a.gibbler_commit
|
46
|
+
a[:magic] = :updated
|
47
|
+
a.gibbler_revert
|
48
|
+
end
|
49
|
+
|
50
|
+
drill "knows a valid gibble", true do
|
51
|
+
a = { :magic => :original }
|
52
|
+
a.gibbler_commit
|
53
|
+
a.gibbler_valid? 'd7049916ddb25e6cc438b1028fb957e5139f9910'
|
54
|
+
end
|
55
|
+
|
56
|
+
drill "knows an invalid gibble", false do
|
57
|
+
a = { :magic => :original }
|
58
|
+
a.gibbler_commit
|
59
|
+
a.gibbler_valid? '2222222222222222222222222222222222222222'
|
60
|
+
end
|
61
|
+
|
62
|
+
dream Hash[:magic => :original]
|
63
|
+
drill "can revert to any valid gibble" do
|
64
|
+
a = { :magic => :original }
|
65
|
+
a.gibbler_commit
|
66
|
+
a[:magic] = :updated
|
67
|
+
a.gibbler_commit
|
68
|
+
a[:magic] = :changed
|
69
|
+
a.gibbler_commit
|
70
|
+
a.gibbler_revert 'd7049916ddb25e6cc438b1028fb957e5139f9910'
|
71
|
+
a
|
72
|
+
end
|
73
|
+
|
74
|
+
dream Hash[:magic => :original]
|
75
|
+
dream :gibbler, 'd7049916ddb25e6cc438b1028fb957e5139f9910'
|
76
|
+
drill "revert does nothing if digest is the same as current one" do
|
77
|
+
a = { :magic => :original }
|
78
|
+
a.gibbler_commit
|
79
|
+
a.gibbler_revert
|
80
|
+
a
|
81
|
+
end
|
82
|
+
|
83
|
+
dream 'd7049916ddb25e6cc438b1028fb957e5139f9910'
|
84
|
+
drill "can revert using short gibble" do
|
85
|
+
a = { :magic => :original }
|
86
|
+
a.gibbler_commit
|
87
|
+
a[:magic] = :updated
|
88
|
+
a.gibbler_revert 'd7049916'
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
4
|
+
|
5
|
+
group "History"
|
6
|
+
|
7
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
8
|
+
|
9
|
+
|
10
|
+
tryouts "Array History" do
|
11
|
+
|
12
|
+
drill "Setup Array class", Array do
|
13
|
+
class ::Array
|
14
|
+
include Gibbler::History
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
drill "can take a Array snapshot", 'd95fcabb498ae282f356eba63da541e4f72c6efa' do
|
19
|
+
a = [:jesse]
|
20
|
+
a.gibbler_commit
|
21
|
+
end
|
22
|
+
|
23
|
+
dream :class, Array
|
24
|
+
dream :size, 2
|
25
|
+
dream ['d95fcabb498ae282f356eba63da541e4f72c6efa', 'eebcb2e84e828b1a7207af4d588cf41fd4c6393a']
|
26
|
+
drill "return an Array history" do
|
27
|
+
a = [:jesse]
|
28
|
+
a.gibbler_commit
|
29
|
+
a << :joey
|
30
|
+
a.gibbler_commit
|
31
|
+
a.gibbler_history
|
32
|
+
end
|
33
|
+
|
34
|
+
dream 'd95fcabb498ae282f356eba63da541e4f72c6efa'
|
35
|
+
drill "can revert Array" do
|
36
|
+
a = [:jesse]
|
37
|
+
stash :original, a.gibbler_commit
|
38
|
+
a << :joey
|
39
|
+
stash :updated, a.gibbler
|
40
|
+
a.gibbler_revert
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
2
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
3
|
+
|
4
|
+
group "History"
|
5
|
+
|
6
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
7
|
+
|
8
|
+
|
9
|
+
tryouts "String History" do
|
10
|
+
|
11
|
+
drill "Setup String class", String do
|
12
|
+
class ::String
|
13
|
+
include Gibbler::History
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
drill "can take a String snapshot", 'c8027100ecc54945ab15ddac529230e38b1ba6a1' do
|
18
|
+
a = "kimmy"
|
19
|
+
a.gibbler_commit
|
20
|
+
end
|
21
|
+
|
22
|
+
dream :class, Array
|
23
|
+
dream :size, 2
|
24
|
+
dream ['c8027100ecc54945ab15ddac529230e38b1ba6a1', '692c05d3186baf2da36e87b7bc5fe53ef13b902e']
|
25
|
+
drill "return a String history" do
|
26
|
+
a = "kimmy"
|
27
|
+
a.gibbler_commit
|
28
|
+
a << " gibbler"
|
29
|
+
a.gibbler_commit
|
30
|
+
a.gibbler_history
|
31
|
+
end
|
32
|
+
|
33
|
+
dream 'c8027100ecc54945ab15ddac529230e38b1ba6a1'
|
34
|
+
drill "can revert String" do
|
35
|
+
a = "kimmy"
|
36
|
+
stash :original, a.gibbler_commit
|
37
|
+
a << " gibbler"
|
38
|
+
stash :updated, a.gibbler
|
39
|
+
a.gibbler_revert
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
2
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
3
|
+
|
4
|
+
group "History"
|
5
|
+
|
6
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
tryouts "Arbitrary Object History" do
|
11
|
+
|
12
|
+
drill "Setup String class", 'FullHouse' do
|
13
|
+
class ::FullHouse
|
14
|
+
include Gibbler::Complex
|
15
|
+
include Gibbler::History
|
16
|
+
attr_accessor :roles
|
17
|
+
end
|
18
|
+
FullHouse.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
drill "can take a FullHouse snapshot", '4192d4cb59975813f117a51dcd4454ac16df6703' do
|
22
|
+
a = FullHouse.new
|
23
|
+
a.gibbler_commit
|
24
|
+
end
|
25
|
+
|
26
|
+
dream ['4192d4cb59975813f117a51dcd4454ac16df6703', '2c6957aa1e734d2a3a71caf569a7461a3bf26f11']
|
27
|
+
drill "return a FullHouse history" do
|
28
|
+
a = FullHouse.new
|
29
|
+
a.gibbler_commit
|
30
|
+
a.roles = [:jesse]
|
31
|
+
a.gibbler_commit
|
32
|
+
a.gibbler_history
|
33
|
+
end
|
34
|
+
|
35
|
+
dream '4192d4cb59975813f117a51dcd4454ac16df6703'
|
36
|
+
drill "can revert FullHouse" do
|
37
|
+
a = FullHouse.new
|
38
|
+
stash :original, a.gibbler_commit
|
39
|
+
a.roles = [:jesse]
|
40
|
+
stash :updated, a.gibbler
|
41
|
+
a.gibbler_revert
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
library 'gibbler/history', File.dirname(__FILE__), '..', 'lib'
|
4
|
+
|
5
|
+
group "History"
|
6
|
+
|
7
|
+
Gibbler.enable_debug if Tryouts.verbose > 3
|
8
|
+
|
9
|
+
|
10
|
+
tryouts "Exceptions" do
|
11
|
+
|
12
|
+
dream :exception, Gibbler::BadDigest
|
13
|
+
drill "raises exception when reverting to unknown gibble" do
|
14
|
+
a = {}
|
15
|
+
a.gibbler_commit
|
16
|
+
a.gibbler_revert '2222222222222222222222222222222222222222'
|
17
|
+
end
|
18
|
+
|
19
|
+
dream :exception, Gibbler::NoHistory
|
20
|
+
drill "raises exception when reverting and there's no history" do
|
21
|
+
a = []
|
22
|
+
a.gibbler_revert
|
23
|
+
end
|
24
|
+
|
25
|
+
dream :exception, NoMethodError
|
26
|
+
drill "raises exception when reverting an unsupported object" do
|
27
|
+
:kimmy.gibbler_revert
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
group "Performance"
|
4
|
+
|
5
|
+
tryouts "Speed", :benchmark do
|
6
|
+
# NOTE: gibbler is slower when history is enabled.
|
7
|
+
drill "Setup variables" do
|
8
|
+
@@array = (1..10000).map { rand }
|
9
|
+
values = (1..10000).map { rand }
|
10
|
+
zipped = @@array.zip(values)
|
11
|
+
@@hash = Hash[*zipped]
|
12
|
+
end
|
13
|
+
|
14
|
+
drill "Array#hash", 5 do
|
15
|
+
@@array.hash
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
dream :mean, 0.21
|
20
|
+
drill "Array#gibbler", 5 do
|
21
|
+
@@array.gibbler
|
22
|
+
end
|
23
|
+
|
24
|
+
drill "Hash#hash", 5 do
|
25
|
+
@@hash.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
dream :mean, 1.1
|
29
|
+
drill "Hash#gibbler", 5 do
|
30
|
+
@@hash.gibbler
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
repetitions = 100 # at 100_000 hash shows errors
|
36
|
+
sample_size = 1..100
|
37
|
+
|
38
|
+
tryouts "Uniqueness", :api do
|
39
|
+
|
40
|
+
drill "Array#hash, all unique", 0 do
|
41
|
+
seen = []
|
42
|
+
repetitions.times do
|
43
|
+
srand
|
44
|
+
seen << ((sample_size).map { rand }).hash
|
45
|
+
end
|
46
|
+
seen.size - seen.uniq.size
|
47
|
+
end
|
48
|
+
|
49
|
+
drill "Hash#hash, all unique", 0 do
|
50
|
+
seen = []
|
51
|
+
repetitions.times do
|
52
|
+
srand
|
53
|
+
seen << Hash[*(sample_size).map { rand }.zip((sample_size).map { rand })].hash
|
54
|
+
end
|
55
|
+
seen.size - seen.uniq.size
|
56
|
+
end
|
57
|
+
|
58
|
+
drill "Array#gibbler, all unique", 0 do
|
59
|
+
seen = []
|
60
|
+
repetitions.times do
|
61
|
+
srand
|
62
|
+
seen << ((sample_size).map { rand }).gibbler
|
63
|
+
end
|
64
|
+
seen.size - seen.uniq.size
|
65
|
+
end
|
66
|
+
|
67
|
+
drill "Hash#gibbler, all unique", 0 do
|
68
|
+
seen = []
|
69
|
+
repetitions.times do
|
70
|
+
srand
|
71
|
+
seen << Hash[*(sample_size).map { rand }.zip((sample_size).map { rand })].gibbler
|
72
|
+
end
|
73
|
+
seen.size - seen.uniq.size
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
library :gibbler, File.dirname(__FILE__), '..', 'lib'
|
3
|
+
group "Object#hash"
|
4
|
+
|
5
|
+
tryout "Object#hash (Ruby 1.9 only)", :api do
|
6
|
+
|
7
|
+
drill "Object", Object.hash, 1892070
|
8
|
+
drill "Class", Class.hash, 1892030
|
9
|
+
drill "Array", Array.hash, 1866770
|
10
|
+
drill "Hash", Hash.hash, 1863840
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
tryouts "Object#hash (Ruby 1.8 only)", :api do
|
15
|
+
|
16
|
+
drill "Object", Object.hash, 118420
|
17
|
+
drill "Class", Class.hash, 118400
|
18
|
+
drill "Array", Array.hash, 104100
|
19
|
+
drill "Hash", Hash.hash, 102590
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
tryouts "Object#hash (JRuby only)", :api do
|
24
|
+
|
25
|
+
drill "Object", Object.hash, 1
|
26
|
+
drill "Class", Class.hash, 3
|
27
|
+
drill "Array", Array.hash, 46
|
28
|
+
drill "Hash", Hash.hash, 43
|
29
|
+
|
30
|
+
end
|
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:
|
4
|
+
version: 0.5.0
|
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-
|
12
|
+
date: 2009-07-01 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,12 +23,28 @@ extra_rdoc_files:
|
|
23
23
|
- README.rdoc
|
24
24
|
- LICENSE.txt
|
25
25
|
files:
|
26
|
-
- README.rdoc
|
27
26
|
- CHANGES.txt
|
28
27
|
- LICENSE.txt
|
28
|
+
- README.rdoc
|
29
29
|
- Rakefile
|
30
30
|
- gibbler.gemspec
|
31
31
|
- lib/gibbler.rb
|
32
|
+
- lib/gibbler/digest.rb
|
33
|
+
- lib/gibbler/history.rb
|
34
|
+
- lib/gibbler/mixins.rb
|
35
|
+
- lib/gibbler/mixins/string.rb
|
36
|
+
- tryouts/01_mixins_tryouts.rb
|
37
|
+
- tryouts/05_gibbler_digest_tryouts.rb
|
38
|
+
- tryouts/10_basic_tryouts.rb
|
39
|
+
- tryouts/11_basic_sha256_tryouts.rb
|
40
|
+
- tryouts/50_history_tryouts.rb
|
41
|
+
- tryouts/51_hash_history_tryouts.rb
|
42
|
+
- tryouts/52_array_history_tryouts.rb
|
43
|
+
- tryouts/53_string_history_tryouts.rb
|
44
|
+
- tryouts/57_arbitrary_history_tryouts.rb
|
45
|
+
- tryouts/59_history_exceptions_tryouts.rb
|
46
|
+
- tryouts/80_performance_tryouts.rb
|
47
|
+
- tryouts/object_hash_demo.rb
|
32
48
|
has_rdoc: true
|
33
49
|
homepage: http://github.com/delano/gibbler
|
34
50
|
licenses: []
|