Module: Gibbler::History

Extended by:
Attic
Included in:
Array, Hash, String
Defined in:
lib/gibbler/aliases.rb,
lib/gibbler/history.rb

Overview

– Aliases for Gibbler::History methods

NOTE: we explicitly define the methods rather than use “alias” in the event that the require ‘gibbler/aliases’ appears before require ‘gibbler/history’ (alias complains about unknown methods) ++

Constant Summary collapse

@@mutex =
Mutex.new

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.mutexObject



22
# File 'lib/gibbler/history.rb', line 22

def self.mutex; @@mutex; end

Instance Method Details

#commitObject



24
# File 'lib/gibbler/aliases.rb', line 24

def commit; gibbler_commit; end

#find_long(*args, &b) ⇒ Object



30
# File 'lib/gibbler/aliases.rb', line 30

def find_long(*args, &b); gibbler_find_long(*args, &b); end

#gibbler_commitObject

Stores a clone of the current object instance using the current digest value. If the object was not changed, this method does nothing but return the gibble.

NOTE: This method is not fully thread safe. It uses a Mutex.synchronize but there’s a race condition where two threads can attempt to commit at near the same time. The first will get the lock and create the commit. The second will get the lock and create another commit immediately after. What we probably want is for the second thread to return the digest for that first snapshot, but how do we know this was a result of the race conditioon rather than two legitimate calls for a snapshot?



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/gibbler/history.rb', line 71

def gibbler_commit
  now, digest, point = nil,nil,nil
  
  if self.__gibbler_history.nil?
    @@mutex.synchronize {
      self.__gibbler_history ||= { :history => [], :objects => {}, :stamp => {} }
    }
  end
  
  @@mutex.synchronize {
    now, digest, point = ::Time.now, self.gibbler, self.clone
    self.__gibbler_history[:history] << digest
    self.__gibbler_history[:stamp][digest] = now
    self.__gibbler_history[:objects][digest] = point
  }
  
  digest
end

#gibbler_find_long(g) ⇒ Object

Returns the long digest associated to the short digest g. If g is longer than 8 characters it returns the value of g.



140
141
142
143
144
# File 'lib/gibbler/history.rb', line 140

def gibbler_find_long(g)
  return if g.nil?
  return g if g.size > 8
  gibbler_history.select { |d| d.match /\A#{g}/ }.first
end

#gibbler_history(short = false) ⇒ Object

Returns an Array of digests in the order they were committed. If short is anything but false, the digests will be converted to the short 8 character digests.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/gibbler/history.rb', line 27

def gibbler_history(short=false)
  # Only a single thread should attempt to initialize the store.
  if self.__gibbler_history.nil?
    @@mutex.synchronize {
      self.__gibbler_history ||= { :history => [], :objects => {}, :stamp => {} }
    }
  end
  if short == false
    self.__gibbler_history[:history]
  else
    self.__gibbler_history[:history].collect { |g| g.short }
  end
end

#gibbler_history?Boolean

Does the current object have any history?

Returns:

  • (Boolean)


134
135
136
# File 'lib/gibbler/history.rb', line 134

def gibbler_history?
  !gibbler_history.empty?
end

#gibbler_object(g = nil) ⇒ Object

Returns the object stored under the given digest g. If g is not a valid digest, returns nil.



43
44
45
46
47
48
49
# File 'lib/gibbler/history.rb', line 43

def gibbler_object(g=nil) 
  g = gibbler_find_long g
  g = self.gibbler_history.last if g.nil?

  return unless gibbler_valid? g
  self.__gibbler_history[:objects][ g ]
end

#gibbler_revert!(g = nil) ⇒ Object

Revert this object to a previously known state. If called without arguments it will revert to the most recent commit. If a digest is specified g, it will revert to that point.

Ruby does not support replacing self (self = previous_self) so each object type needs to implement its own __gibbler_revert! method. This default run some common checks and then defers to self.__gibbler_revert!.

Raise the following exceptions:

  • NoRevert: if this object doesn’t have a __gibbler_revert! method

  • NoHistory: This object has no commits

  • BadDigest: The given digest is not in the history for this object

If g matches the current digest value this method does nothing.

Returns the new digest (g).

Raises:



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/gibbler/history.rb', line 106

def gibbler_revert!(g=nil)
  raise NoRevert unless self.respond_to? :__gibbler_revert!
  raise NoHistory, self.class unless gibbler_history?
  raise BadDigest, g if !g.nil? && !gibbler_valid?(g)
  
  g = self.gibbler_history.last if g.nil?
  g = gibbler_find_long g 
  
  # Do nothing if the given digest matches the current gibble. 
  # NOTE: We use __gibbler b/c it doesn't update self.gibbler_cache.
  unless self.__gibbler == g
    @@mutex.synchronize {
      # Always make sure self.gibbler_digest is a Gibbler::Digest 
      self.gibbler_cache = g.is_a?(Gibbler::Digest) ? g : Gibbler::Digest.new(g)
      self.__gibbler_revert!
    }
  end
  
  self.gibbler_cache
end

#gibbler_stamp(g = nil) ⇒ Object

Returns the timestamp (a Time object) when the digest g was committed. If g is not a valid gibble, returns nil.



53
54
55
56
57
58
# File 'lib/gibbler/history.rb', line 53

def gibbler_stamp(g=nil)
  g = gibbler_find_long g
  g = self.gibbler_history.last if g.nil?
  return unless gibbler_valid? g
  self.__gibbler_history[:stamp][ g ]
end

#gibbler_valid?(g) ⇒ Boolean

Is the given digest g contained in the history for this object?

Returns:

  • (Boolean)


128
129
130
131
# File 'lib/gibbler/history.rb', line 128

def gibbler_valid?(g)
  return false unless gibbler_history?
  gibbler_history.member? gibbler_find_long(g)
end

#history(*args, &b) ⇒ Object



23
# File 'lib/gibbler/aliases.rb', line 23

def history(*args, &b); gibbler_history(*args, &b); end

#history?Boolean

Returns:

  • (Boolean)


28
# File 'lib/gibbler/aliases.rb', line 28

def history?; gibbler_history?; end

#object(*args, &b) ⇒ Object



25
# File 'lib/gibbler/aliases.rb', line 25

def object(*args, &b); gibbler_object(*args, &b); end

#revert!(*args, &b) ⇒ Object



27
# File 'lib/gibbler/aliases.rb', line 27

def revert!(*args, &b); gibbler_revert!(*args, &b); end

#stamp(*args, &b) ⇒ Object



26
# File 'lib/gibbler/aliases.rb', line 26

def stamp(*args, &b); gibbler_stamp(*args, &b); end

#valid?(*args, &b) ⇒ Boolean

Returns:

  • (Boolean)


29
# File 'lib/gibbler/aliases.rb', line 29

def valid?(*args, &b); gibbler_valid?(*args, &b); end