stub_a 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/stub_a.rb +154 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 022265f955db24a8dd7a3bb3465243121379ecd8
4
+ data.tar.gz: 86be306baf3ec972fdf4163e7ced3920e7bc4407
5
+ SHA512:
6
+ metadata.gz: aaff64bee34e4ce474b09fe009188c83af14b39128cd44bbaddc55d8055e4e71a7aeb789d097ae45acc8d685f0890571e8603da53178a7223c4a19a0a1a5c639
7
+ data.tar.gz: d156071d484adaf0b2be902a1eb7903c33c9b9d2fd0271371e4b472aca42387d2a3b5120703c3256da55ba17357fbd64b0c80ea0bc402a77f5af9023fcadc4ea
data/lib/stub_a.rb ADDED
@@ -0,0 +1,154 @@
1
+ class StubA
2
+ def initialize(object)
3
+ @target_class = object if object.is_a? Class
4
+ @target_class ||= class << object; self; end
5
+ end
6
+
7
+ [:before,
8
+ :after,
9
+ ].each do |hook_type|
10
+ define_method hook_type, ->(method, &block) {
11
+ make_hook_points(method)
12
+
13
+ method_name = hook_method_name(method, hook_type)
14
+ hook = block if block
15
+ hook ||= default_hook(method, hook_type)
16
+ @target_class.__send__ :define_method, method_name, hook
17
+ self
18
+ }
19
+ end
20
+
21
+ def stub(method, &block)
22
+ raise ArgumentError, "No block" unless block
23
+ make_hook_points(method)
24
+
25
+ method_name = hook_method_name(method, :stub)
26
+ @target_class.__send__ :define_method, method_name, block
27
+ self
28
+ end
29
+
30
+ def restore(method=nil, *types)
31
+ if method.nil?
32
+ hooks = defined_hooks
33
+ method = hooks.keys.first if hooks.keys.size == 1
34
+ end
35
+ raise "Method name to be restored is required." if method.nil?
36
+
37
+ available_types = METHOD_TYPES - [:origin]
38
+ raise "Type error" if (types - available_types).size > 0
39
+ types = available_types if types.empty?
40
+ types.uniq!
41
+
42
+ types.each do |type|
43
+ method_name = hook_method_name(method, type)
44
+ next unless @target_class.method_defined? method_name
45
+ @target_class.__send__ :remove_method, method_name
46
+ end
47
+
48
+ hooks = defined_hooks
49
+ if hooks[method.to_s].size == 1 and
50
+ hooks[method.to_s].first == 'origin'
51
+ origin = hook_method_name(method, :origin)
52
+ @target_class.__send__ :alias_method, method, origin
53
+ @target_class.__send__ :remove_method, origin
54
+ end
55
+ end
56
+
57
+ def restore_all
58
+ defined_hooks.each do |method, types|
59
+ types.each do |type|
60
+ name = hook_method_name(method, type)
61
+ next unless @target_class.method_defined? name
62
+ @target_class.__send__ :alias_method, method, name if type == 'origin'
63
+ @target_class.__send__ :remove_method, name
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+ METHOD_TYPES = [
70
+ :before,
71
+ :after,
72
+ :stub,
73
+ :origin,
74
+ ]
75
+ private_constant :METHOD_TYPES
76
+
77
+ def hook_method_name(method, type)
78
+ "===>(stub_a) #{type}-#{method}"
79
+ end
80
+
81
+ def hook_method_names(method)
82
+ METHOD_TYPES.each_with_object({}) do |t, h|
83
+ h[t] = hook_method_name(method, t)
84
+ end
85
+ end
86
+
87
+ def make_hook_points(method_name)
88
+ methods = hook_method_names(method_name)
89
+ return if @target_class.method_defined? methods[:origin]
90
+
91
+ @target_class.__send__ :alias_method, methods[:origin], method_name
92
+ @target_class.__send__ :define_method, method_name, ->(*args, &block) {
93
+ if respond_to? methods[:before]
94
+ __send__ methods[:before], ARGS[:before].new(method_name, args), &block
95
+ end
96
+
97
+ if respond_to? methods[:stub]
98
+ origin = __send__ :method, methods[:origin]
99
+ val = __send__ methods[:stub], ARGS[:stub].new(origin, args), &block
100
+ else
101
+ val = __send__ methods[:origin], *args, &block
102
+ end
103
+
104
+ if respond_to? methods[:after]
105
+ __send__ methods[:after], ARGS[:after].new(method_name, val)
106
+ end
107
+
108
+ val
109
+ }
110
+ end
111
+
112
+ def defined_hooks
113
+ re = /\A===>\(stub_a\) (before|after|stub|origin)-(.+)\z/
114
+ @target_class.instance_methods.each_with_object({}) {|name, h|
115
+ if name.to_s =~ re
116
+ h[$2] ||= []
117
+ h[$2] << $1
118
+ end
119
+ }
120
+ end
121
+
122
+ def default_hook(method, type)
123
+ case type.to_sym
124
+ when :before
125
+ default_before_hook(method)
126
+ when :after
127
+ default_after_hook(method)
128
+ end
129
+ end
130
+
131
+ def default_before_hook(method)
132
+ ->(method, *args, &block) {
133
+ puts "[#{Time.now}] call `#{method}' (#{self.inspect})"
134
+ puts "args: #{args.inspect}" if args.size > 0
135
+ }
136
+ end
137
+
138
+ def default_after_hook(method)
139
+ before_method = hook_method_name(method, :before)
140
+ ->(method, val) {
141
+ unless respond_to? before_method
142
+ puts "[#{Time.now}] call `#{method}' (#{self.inspect})"
143
+ end
144
+ puts "return: #{val.inspect}"
145
+ }
146
+ end
147
+
148
+ ARGS = {
149
+ before: Struct.new(:method_name, :args),
150
+ stub: Struct.new(:method, :args),
151
+ after: Struct.new(:method_name, :return_value),
152
+ }
153
+ private_constant :ARGS
154
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stub_a
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - 60ml
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: a tool to manipulate objects with hooks and stubs
14
+ email: bd.4975.3110@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/stub_a.rb
20
+ homepage: https://github.com/60ml/stub_a
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.4.5
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: a tool to manipulate objects with hooks and stubs
44
+ test_files: []