Module: Onceler::Recordable

Defined in:
lib/onceler/recordable.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(instance) ⇒ Object



3
4
5
6
7
8
9
# File 'lib/onceler/recordable.rb', line 3

def self.extended(instance)
  instance.instance_eval do
    @__retvals = {}
    @__inherited_retvals = {}
    @__ignore_ivars = instance_variables
  end
end

Instance Method Details

#__associations_equal?(obj1, obj2) ⇒ Boolean

if a nested once block updates an inherited object’s associations, we want to know about it

Returns:

  • (Boolean)


72
73
74
75
76
77
# File 'lib/onceler/recordable.rb', line 72

def __associations_equal?(obj1, obj2)
  cache1 = obj1.association_cache
  cache2 = obj2.association_cache
  cache1.size == cache2.size &&
  cache1.all? { |k, v| cache2.key?(k) && __values_equal?(v.target, cache2[k].target) }
end

#__data(inherit = false) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/onceler/recordable.rb', line 79

def __data(inherit = false)
  @__data ||= {}
  @__data[inherit] ||= begin
    @__comparison_cache = {}
    data = [__ivars(inherit), __retvals(inherit)]
    begin
      data = Marshal.dump(data)
    rescue TypeError
      data.each do |hash|
        hash.each do |key, val|
          begin
            Marshal.dump(val)
          rescue TypeError
            raise TypeError.new("Unable to dump #{key} in #{self.class.[:location]}: #{$!}")
          end
        end
      end
    end
    @__comparison_cache = nil
    data
  end
end

#__ivars(inherit = false) ⇒ Object



34
35
36
37
38
39
40
41
42
43
# File 'lib/onceler/recordable.rb', line 34

def __ivars(inherit = false)
  ivars = instance_variables - @__ignore_ivars
  ivars.inject({}) do |hash, key|
    if key.to_s !~ /\A@__/
      val = instance_variable_get(key)
      hash[key] = val if __mutated?(key, val) || inherit
    end
    hash
  end
end

#__mutated?(key, val) ⇒ Boolean

we don’t include inherited stuff in __data, because we might need to interleave things from an intermediate before(:each) at run time

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
55
# File 'lib/onceler/recordable.rb', line 47

def __mutated?(key, val)
  # top-level recorders don't inherit anything, so we always want to return true
  return true unless @__inherited_cache
  # need to do both types of comparison, i.e. it's the same object in
  # memory (not reassigned), and nothing about it has been changed
  return true unless @__inherited_values[key].equal?(val)
  return true unless __values_equal?(@__inherited_cache[key], val)
  false
end

#__prepare_recording(recording) ⇒ Object



11
12
13
14
15
16
17
18
19
20
# File 'lib/onceler/recordable.rb', line 11

def __prepare_recording(recording)
  method = recording.name
  define_singleton_method(method) do
    if @__retvals.key?(method)
      @__retvals[method]
    else
      @__retvals[method] = __record(recording)
    end
  end
end

#__record(recording) ⇒ Object



22
23
24
# File 'lib/onceler/recordable.rb', line 22

def __record(recording)
  instance_eval(&recording.block)
end

#__retvals(inherit = false) ⇒ Object



26
27
28
29
30
31
32
# File 'lib/onceler/recordable.rb', line 26

def __retvals(inherit = false)
  retvals = @__inherited_retvals.merge(@__retvals)
  retvals.inject({}) do |hash, (key, val)|
    hash[key] = val if __mutated?(key, val) || inherit
    hash
  end
end

#__values_equal?(obj1, obj2) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/onceler/recordable.rb', line 57

def __values_equal?(obj1, obj2)
  if ActiveRecord::Base === obj1 && ActiveRecord::Base === obj2
    cache_key = [obj1, obj2]
    return @__comparison_cache[cache_key] if @__comparison_cache.key?(cache_key)
    # so as to avoid cycles while traversing AR associations
    @__comparison_cache[cache_key] = true
    @__comparison_cache[cache_key] = obj1.attributes == obj2.attributes &&
                                     __associations_equal?(obj1, obj2)
  else
    obj1 == obj2
  end
end

#copy_from(other) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/onceler/recordable.rb', line 102

def copy_from(other)
  # need two copies of things for __mutated? checks (see above)
  @__inherited_cache = Marshal.load(other.__data(:inherit)).inject(&:merge)
  ivars, retvals = Marshal.load(other.__data(:inherit))
  @__inherited_retvals = retvals
  @__inherited_values = ivars.merge(retvals)
  ivars.each do |key, value|
    instance_variable_set(key, value)
  end
  retvals.each do |key, value|
    define_singleton_method(key) { value }
  end
end