Gibbler - v0.9
Git-like hashes and history for Ruby objects for Ruby 1.8, 1.9 and JRuby.
Check out the screencast created by Alex Peuchert.
Some things to keep in mind
-
Digest calculation may change between minor releases (as it did between 0.6 and 0.7)
-
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.
-
Don’t forget to enjoy your life!
Example 1 – Standalone Usage
require 'gibbler'
g = Gibbler.new 'id', 1001 # => f4fb3796ababa3788d1bded8fdc589ab1ccb1c3d
g.base(36) # => sm71s7eam4hm5jlsuzlqkbuktwpe5h9
g == 'f4fb3796ababa3788d1bded8fdc589ab1ccb1c3d' # => true
g === 'f4fb379' # => true
Example 2 – Mixins Usage
require 'gibbler/mixins'
"kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
config = {}
config.gibbler # => 4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf
config.gibbled? # => false
config[:server] = {
:users => [:dave, :ali],
:ports => [22, 80, 443]
}
config.gibbled? # => true
config.gibbler # => ef23d605f8c4fc80a8e580f9a0e8dab8426454a8
config[:server][:users] << :yanni
config.gibbler # => 4c558a56bc2abf5f8a845a69e47ceb5e0003683f
config.gibbler.short # => 4c558a56
config.gibbler.base36 # => 8x00l83jov4j80i9vfzpaxr9jag23wf
config.gibbler.base36.short # => 8x00l83j
Example 3 – Object History
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 gibbler_commit
method creates a clone of the current object and stores in an instance variable using the current hash digest as the key.
require 'gibbler/mixins'
require 'gibbler/history'
a = { :magic => :original }
a.gibbler_commit # => d7049916ddb25e6cc438b1028fb957e5139f9910
a[:magic] = :updated
a.gibbler_commit # => b668098e16d08898532bf3aa33ce2253a3a4150e
a[:magic] = :changed
a.gibbler_commit # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
a.gibbler_history # => d7049916, b668098e, 0b11c377
a.gibbler_revert! 'd7049916' # Return to a specific commit
a.gibbler # => d7049916ddb25e6cc438b1028fb957e5139f9910
a # => { :magic => :original }
a.delete :magic
a.gibbler_revert! # Return to the previous commit
a.gibbler # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
a # => { :magic => :changed }
a.gibbler_object 'b668098e' # => { :magic => :updated }
a.gibbler_stamp # => 2009-07-01 18:56:52 -0400
Example 4 – Method Aliases
If you have control over the namespaces of your objects, you can use the method aliases to tighten up your code a bit. The “gibbler” and “gibbled?” methods can be accessed via “digest” and “changed?”, respectively. (The reason they’re not enabled by default is to avoid conflicts.)
require 'gibbler/aliases'
"kimmy".digest # => c8027100ecc54945ab15ddac529230e38b1ba6a1
:kimmy.digest # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
a = [:a, :b, :c]
a.digest # => e554061823b8f06367555d1ee4c25b4ffee61944
a << :d
a.changed? # => true
The history methods also have aliases which remove the “gibbler_” prefix.
require 'gibbler/aliases'
require 'gibbler/history'
a = { :magic => :original }
a.commit
a.history
a.revert!
# etc...
Example 5 – Different Digest types
By default Gibbler creates SHA1 hashes. You can change this globally or per instance.
require 'gibbler/mixins'
Gibbler.digest_type = Digest::MD5
:kimmy.gibbler # => 0c61ff17f46223f355759934154d5dcb
:kimmy.gibbler(Digest::SHA1) # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
In Jruby, you can grab the digest types from the openssl library.
require 'openssl'
Gibbler.digest_type = OpenSSL::Digest::SHA256
:kimmy.gibbler # => 1069428e6273cf329436c3dce9b680d4d4e229d7b7...
Example 6 – All your base
require 'gibbler/mixins'
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
:kimmy.gibbler.base(16) # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
:kimmy.gibbler.base(36) # => 9nydr6mpv6w4k8ngo3jtx0jz1n97h7j
:kimmy.gibbler.base(10) # => 472384540402900668368761869477227308873774630879
:kimmy.gibbler.to_i # => 472384540402900668368761869477227308873774630879
Example 7 – Global secret
Gibbler can prepend all digest inputs with a global secret. You can set this once per project to ensure your project’s digests are unique.
require 'gibbler/mixins'
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
Gibbler.secret = "sUp0r5ekRu7"
:kimmy.gibbler # => 6c5f5aff4d809cec7e7da091214a35a2698489f8
Supported Classes
Gibbler methods are available only to the classes which explicitly include them (see RDocs for details on which classes are supported by default). You can also extend custom objects:
class FullHouse
include Gibbler::Complex
attr_accessor :roles
end
a = FullHouse.new
a.gibbler # => 4192d4cb59975813f117a51dcd4454ac16df6703
a.roles = [:jesse, :joey, :danny, :kimmy, :michelle, :dj, :stephanie]
a.gibbler # => 6ea546919dc4caa2bab69799b71d48810a1b48fa
Gibbler::Complex creates a digest based on the name of the class and the names and values of the instance variables. See the RDocs for other Gibbler::* types.
If you want to support all Ruby objects, add the following to your application:
class Object
include Gibbler::String
end
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).
As of 0.7 all Proc objects have the same digest: 12075835e94be34438376cd7a54c8db7e746f15d
.
Known Issues
-
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)
-
Digests for Bignum objects are different between Ruby and JRuby. Why?
-
Digests for Proc objects are different between Ruby 1.8 and 1.9 because Proc.arity returns different values and 1.8 has no lambda? method.
Installation
Via Rubygems:
$ gem install gibbler
or via download:
What People Are Saying
-
“nice approach - everything is an object, every object is ‘gittish’” – @olgen_morten
-
“gibbler is just awesome” – @TomK32
-
“wie cool ist Gibbler eigentlich?” – @we5
-
“it’s nice idea and implementation!” – HristoHristov
More Info
Thanks
-
Kalin Harvey (krrh) for the early feedback and artistic direction.
-
Alex Peuchert (aaalex) for creating the screencast.
Credits
-
[Delano Mandelbaum](delanotes.com/) (@solutious.com)
License
See: LICENSE.txt