Module: Aversion
- Defined in:
- lib/aversion.rb,
lib/aversion/version.rb
Overview
Public: Aversion makes your Ruby objects versionable. It also makes them immutable, so the only way to obtain transformed copies is to explicitly mutate state in #transform calls, which will return the modified copy, leaving the original intact.
Examples
class Person
include Aversion
def initialize(hunger)
@hunger = hunger
end
def eat
transform do
@hunger -= 5
end
end
end
# Objects are immutable. Calls to mutate state will return new modified
# copies (thanks to #transform):
john = Person.new
new_john = john.eat
newer_john = new_john.eat
# You can roll back to a previous state:
new_john_again = newer_john.rollback
# Calculate deltas between objects, and replay the differences to get to the
# desired state:
difference = newer_john - john
newer_john_again = john.replay(difference)
Constant Summary collapse
- VERSION =
"0.0.1"
Class Method Summary collapse
-
.included(base) ⇒ Object
Public: When we include Aversion, we override .new with an immutable constructor and provide a .new_mutable version.
Instance Method Summary collapse
-
#-(other) ⇒ Object
Public: Returns the difference between two versioned objects, which is an array of the transformations one lacks from the other.
-
#==(other) ⇒ Object
Public: Returns whether two versionable objects are equal.
-
#history ⇒ Object
Internal: Returns the history of this object.
-
#history=(transformations) ⇒ Object
Internal: Sets the history of this object to a specific array fo transformations.
-
#mutable ⇒ Object
Public: Returns a mutable version of the object, in case anyone needs it.
-
#replay(transformations) ⇒ Object
Public: Replays an array of transformations (procs).
-
#rollback ⇒ Object
Public: Rolls back to a previous version of the state.
-
#transform(&block) ⇒ Object
Public: The only way to transform state.
Class Method Details
.included(base) ⇒ Object
Public: When we include Aversion, we override .new with an immutable constructor and provide a .new_mutable version.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/aversion.rb', line 41 def self.included(base) base.class_eval do # Public: Initializes an immutable instance. def self.new(*args) new_mutable(*args).freeze end # Public: Initializes a mutable instance. def self.new_mutable(*args) allocate.tap do |instance| instance.send :initialize, *args instance.instance_eval do @transformations = [] @initial_args = args end end end end end |
Instance Method Details
#-(other) ⇒ Object
Public: Returns the difference between two versioned objects, which is an array of the transformations one lacks from the other.
117 118 119 120 121 |
# File 'lib/aversion.rb', line 117 def -(other) younger, older = [history, other.history].sort { |a,b| a.length <=> b.length } difference = (older.length - younger.length) - 1 older[difference..-1] end |
#==(other) ⇒ Object
Public: Returns whether two versionable objects are equal.
They will be equal as long as they have the same initial args with they were constructed with and their history is the same.
127 128 129 130 |
# File 'lib/aversion.rb', line 127 def ==(other) @initial_args == other.instance_variable_get(:@initial_args) && history == other.history end |
#history ⇒ Object
Internal: Returns the history of this object.
105 106 107 |
# File 'lib/aversion.rb', line 105 def history @transformations end |
#history=(transformations) ⇒ Object
Internal: Sets the history of this object to a specific array fo transformations.
111 112 113 |
# File 'lib/aversion.rb', line 111 def history=(transformations) @transformations = transformations end |
#mutable ⇒ Object
Public: Returns a mutable version of the object, in case anyone needs it. We do need it internally to perform transformations.
63 64 65 66 67 68 69 70 |
# File 'lib/aversion.rb', line 63 def mutable self.class.new_mutable(*@initial_args).tap do |mutable| instance_variables.each do |ivar| mutable.instance_variable_set(ivar, instance_variable_get(ivar)) mutable.instance_variable_set(:@transformations, @transformations.dup) end end end |
#replay(transformations) ⇒ Object
Public: Replays an array of transformations (procs).
transformations - the Array of Procs to apply.
Returns a new, immutable copy with those transformations applied.
95 96 97 98 99 100 101 102 |
# File 'lib/aversion.rb', line 95 def replay(transformations) (frozen? ? mutable : self).tap do |object| transformations.each do |transformation| object.history << transformation object.instance_eval(&transformation) end end.freeze end |
#rollback ⇒ Object
Public: Rolls back to a previous version of the state.
Returns a new, immutable copy with the previous state.
84 85 86 87 88 |
# File 'lib/aversion.rb', line 84 def rollback self.class.new_mutable(*@initial_args).tap do |instance| instance.replay(history[0..-2]) end.freeze end |
#transform(&block) ⇒ Object
Public: The only way to transform state.
Returns a new, immutable copy with the transformation applied.
75 76 77 78 79 |
# File 'lib/aversion.rb', line 75 def transform(&block) mutable.tap do |new_instance| new_instance.replay([block.dup]) end.freeze end |