gibbler 0.4 → 0.5.0
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 +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: []
|