Class: Crochet::Hook

Inherits:
Object
  • Object
show all
Defined in:
lib/crochet.rb

Constant Summary collapse

TYPE_SPECS =

Map symbols to their class- or instance-relevant methods

{
	:instance => {
		:accessor => :instance_method,
		:definer => :define_method,
		:evaluator => :instance_exec
	},
	:class => {
		:accessor => :method,
		:definer => :define_singleton_method,
		:evaluator => :class_exec
	}
}

Instance Method Summary collapse

Constructor Details

#initialize(klass, &block) ⇒ Hook

Returns a new instance of Hook.



17
18
19
20
21
# File 'lib/crochet.rb', line 17

def initialize(klass, &block)
	@klass = klass
	@methods = []
	self.instance_eval(&block) if block
end

Instance Method Details

#_attach(timing, destructive, method, method_type = :instance, &block) ⇒ Object



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
# File 'lib/crochet.rb', line 22

def _attach(timing, destructive, method, method_type=:instance, &block)
	specs = TYPE_SPECS[method_type]

	# Capture the original method
	orig_method = @klass.method(specs[:accessor]).(method)

	# Redefine the method
	@klass.send(specs[:definer], method) do |*args|
		# Pass the original arguments "before" hook methods
		if timing == :before
			hook_result = self.send(specs[:evaluator], *args, &block)
			args = hook_result if destructive
		end

		# Call the original method and assign the result
		method_result = (method_type == :instance ? orig_method.bind(self) : orig_method).call(*args)

		# Pass method result, followed by the args it received, to "after" hook methods
		if timing == :after
			hook_result = self.send(specs[:evaluator], method_result, *args, &block)
			method_result = hook_result if destructive
		end

		# Finally, return the (optionally overridden) method result
		method_result
	end
	@methods << { :orig => orig_method, :name => method, :type => method_type }
	nil
end

#destroyObject

When ‘destroy` is called, it should restore all affected methods



63
64
65
66
67
68
69
70
71
# File 'lib/crochet.rb', line 63

def destroy
	@methods.each do |method|
		specs = TYPE_SPECS[method[:type]]
		@klass.send(specs[:definer], method[:name]) do |*args|
			(method[:type] == :instance ? method[:orig].bind(self) : method[:orig]).call(*args)
		end
	end
	@methods = []
end