/
change_observer.rb
65 lines (59 loc) · 2.17 KB
/
change_observer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# frozen_string_literal: true
module Bootsnap
module LoadPathCache
module ChangeObserver
module ArrayMixin
# For each method that adds items to one end or another of the array
# (<<, push, unshift, concat), override that method to also notify the
# observer of the change.
def <<(entry)
@lpc_observer.push_paths(self, entry.to_s)
super
end
def push(*entries)
@lpc_observer.push_paths(self, *entries.map(&:to_s))
super
end
alias_method :append, :push
def unshift(*entries)
@lpc_observer.unshift_paths(self, *entries.map(&:to_s))
super
end
alias_method :prepend, :unshift
def concat(entries)
@lpc_observer.push_paths(self, *entries.map(&:to_s))
super
end
# uniq! keeps the first occurrence of each path, otherwise preserving
# order, preserving the effective load path
def uniq!(*args)
ret = super
@lpc_observer.reinitialize if block_given? || !args.empty?
ret
end
# For each method that modifies the array more aggressively, override
# the method to also have the observer completely reconstruct its state
# after the modification. Many of these could be made to modify the
# internal state of the LoadPathCache::Cache more efficiently, but the
# accounting cost would be greater than the hit from these, since we
# actively discourage calling them.
%i(
[]= clear collect! compact! delete delete_at delete_if fill flatten!
insert keep_if map! pop reject! replace reverse! rotate! select!
shift shuffle! slice! sort! sort_by!
).each do |method_name|
define_method(method_name) do |*args, &block|
ret = super(*args, &block)
@lpc_observer.reinitialize
ret
end
end
end
def self.register(observer, arr)
return if arr.frozen? # can't register observer, but no need to.
arr.instance_variable_set(:@lpc_observer, observer)
arr.extend(ArrayMixin)
end
end
end
end