peeek 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -0
- data/Rakefile +6 -1
- data/bin/peeek +6 -14
- data/lib/peeek.rb +10 -9
- data/lib/peeek/cli.rb +99 -0
- data/lib/peeek/cli/options.rb +212 -0
- data/lib/peeek/hook.rb +1 -1
- data/lib/peeek/hook/linker.rb +1 -0
- data/lib/peeek/hook/specifier.rb +99 -0
- data/lib/peeek/version.rb +1 -1
- data/peeek.gemspec +0 -1
- data/spec/peeek/call_spec.rb +27 -48
- data/spec/peeek/cli/options_spec.rb +331 -0
- data/spec/peeek/cli_sample.rb +2 -0
- data/spec/peeek/cli_spec.rb +254 -0
- data/spec/peeek/hook/specifier_spec.rb +107 -0
- data/spec/peeek_spec.rb +1 -1
- metadata +21 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90ae3ee8f84b1d47140d8ebd28943835875550be
|
4
|
+
data.tar.gz: 8a7bdc5de0df596be2e309ee950112e89ff94166
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6ffdc7048653dc766511b66272c730a1f37ac04a1aa7efe35efa4a834916f758d5766ad5c07da75954cb5a63c4a067788014514ca2edcd94756654f5b13dc75
|
7
|
+
data.tar.gz: d32b0ca28b0520cf773aa1f1f22c678168cdf6211dcac657796d1dc73fd85819db714df49e7e2ca8c4a9cd8fa78b1a62f80818afbddd3fb9c786c44649ef55e3
|
data/.travis.yml
ADDED
data/Rakefile
CHANGED
data/bin/peeek
CHANGED
@@ -1,16 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require '
|
3
|
-
require 'peeek'
|
2
|
+
require 'peeek/cli'
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
calls = begin
|
11
|
-
Peeek.capture(String => :%) { load path } # want to be you can specify
|
12
|
-
ensure
|
13
|
-
$stdout = original_stdout
|
14
|
-
end
|
15
|
-
|
16
|
-
puts calls
|
4
|
+
begin
|
5
|
+
puts Peeek::CLI.new($stdin, $stdout, ARGV).run(binding)
|
6
|
+
rescue => e
|
7
|
+
puts "peeek: #{e.message} (#{e.class})"
|
8
|
+
end
|
data/lib/peeek.rb
CHANGED
@@ -7,12 +7,14 @@ require 'peeek/calls'
|
|
7
7
|
class Peeek
|
8
8
|
|
9
9
|
# @attribute [r] global
|
10
|
+
# @scope class
|
10
11
|
# @return [Peeek] the global Peeek object
|
11
12
|
def self.global
|
12
13
|
@global ||= new
|
13
14
|
end
|
14
15
|
|
15
16
|
# @attribute [r] current
|
17
|
+
# @scope class
|
16
18
|
# @return [Peeek] the current Peeek object
|
17
19
|
#
|
18
20
|
# @see Peeek.local
|
@@ -44,16 +46,15 @@ class Peeek
|
|
44
46
|
|
45
47
|
# Capture all calls to hook targets.
|
46
48
|
#
|
47
|
-
# @param [Hash{Module, Class, Object => String, Array<String>, Symbol, Array<Symbol>}]
|
48
|
-
#
|
49
|
-
# target of hook
|
49
|
+
# @param [Hash{Module, Class, Object => String, Array<String>, Symbol, Array<Symbol>}] hook_targets
|
50
|
+
# an object and method specifier(s) that be target of hook
|
50
51
|
# @yield any process that want to run to capture
|
51
52
|
# @return [Peeek::Calls] calls that were captured in the block
|
52
|
-
def self.capture(
|
53
|
+
def self.capture(hook_targets)
|
53
54
|
raise ArgumentError, 'block not supplied' unless block_given?
|
54
55
|
|
55
56
|
local do
|
56
|
-
|
57
|
+
hook_targets.each { |object, method_specs| current.hook(object, *method_specs) }
|
57
58
|
yield
|
58
59
|
current.calls
|
59
60
|
end
|
@@ -80,8 +81,8 @@ class Peeek
|
|
80
81
|
# Register a hook to methods of an object.
|
81
82
|
#
|
82
83
|
# @param [Module, Class, Object] object a target object that hook
|
83
|
-
# @param [Array<String>, Array<Symbol>] method_specs method
|
84
|
-
#
|
84
|
+
# @param [Array<String>, Array<Symbol>] method_specs method specifiers of the
|
85
|
+
# object. see also examples of {Peeek::Hook.create}
|
85
86
|
# @yield [call] process a call to the methods. give optionally
|
86
87
|
# @yieldparam [Peeek::Call] call a call to the methods
|
87
88
|
# @return [Peeek::Hooks] registered hooks at calling
|
@@ -130,8 +131,8 @@ class Peeek
|
|
130
131
|
|
131
132
|
# Register a hook to methods of self to the current Peeek object.
|
132
133
|
#
|
133
|
-
# @param [Array<String>, Array<Symbol>] method_specs method
|
134
|
-
#
|
134
|
+
# @param [Array<String>, Array<Symbol>] method_specs method specifiers of
|
135
|
+
# the object. see also examples of {Peeek::Hook.create}
|
135
136
|
# @yield [call] process a call to the methods. give optionally
|
136
137
|
# @yieldparam [Peeek::Call] call a call to the methods
|
137
138
|
#
|
data/lib/peeek/cli.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'peeek/cli/options'
|
2
|
+
require 'peeek'
|
3
|
+
|
4
|
+
class Peeek
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
# Initialize the CLI object.
|
8
|
+
#
|
9
|
+
# @param [IO] input source to input
|
10
|
+
# @param [IO] output destination to output the execution result
|
11
|
+
# @param [Array<String>] argv arguments that is given from command line
|
12
|
+
def initialize(input, output, argv)
|
13
|
+
@input = input
|
14
|
+
@output = output
|
15
|
+
@options = Options.new(argv)
|
16
|
+
rescue Help => e
|
17
|
+
output.puts(e.message)
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
|
21
|
+
# @attribute [r] options
|
22
|
+
# @return [Peeek::CLI::Options] options to run from CLI
|
23
|
+
attr_reader :options
|
24
|
+
|
25
|
+
# Run the command or the program file with a binding. And capture calls
|
26
|
+
# that raised when running it.
|
27
|
+
#
|
28
|
+
# @param [Binding] binding context that runs the command or the program file
|
29
|
+
# @return [Peeek::Calls] captured calls
|
30
|
+
def run(binding)
|
31
|
+
if Options.encoding_options_enabled?
|
32
|
+
Encoding.default_external = @options.external_encoding
|
33
|
+
Encoding.default_internal = @options.internal_encoding
|
34
|
+
end
|
35
|
+
|
36
|
+
$DEBUG = @options.debug
|
37
|
+
$VERBOSE = @options.verbose
|
38
|
+
|
39
|
+
Dir.chdir(@options.working_directory) if @options.working_directory
|
40
|
+
$LOAD_PATH.unshift(*@options.directories_to_load)
|
41
|
+
@options.required_libraries.each(&method(:require))
|
42
|
+
|
43
|
+
hook_targets = materialize_hook_targets(binding)
|
44
|
+
process = setup_to_execute(binding)
|
45
|
+
|
46
|
+
@output.puts("peeek-#{VERSION}") if @options.version_requested?
|
47
|
+
|
48
|
+
original_stdout = $stdout
|
49
|
+
$stdout = @output
|
50
|
+
|
51
|
+
begin
|
52
|
+
Peeek.capture(hook_targets, &process)
|
53
|
+
rescue => e
|
54
|
+
e.set_backtrace(e.backtrace.reject { |line| line =~ %r{lib/peeek} })
|
55
|
+
raise e
|
56
|
+
ensure
|
57
|
+
$stdout = original_stdout
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def materialize_hook_targets(binding)
|
64
|
+
hook_targets = @options.hook_targets.inject({}) do |hook_targets, hook_spec|
|
65
|
+
hook_targets[hook_spec.object_name] ||= []
|
66
|
+
hook_targets[hook_spec.object_name] << hook_spec.method_specifier
|
67
|
+
hook_targets
|
68
|
+
end
|
69
|
+
|
70
|
+
hook_targets = hook_targets.map do |object_name, method_specs|
|
71
|
+
type = binding.eval("defined? #{object_name}")
|
72
|
+
raise "#{object_name} is undefined" if type.nil?
|
73
|
+
raise "#{object_name} isn't a constant or a global variable" unless type == 'constant' || type == 'global-variable'
|
74
|
+
[binding.eval(object_name), method_specs]
|
75
|
+
end
|
76
|
+
|
77
|
+
Hash[hook_targets]
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_to_execute(binding)
|
81
|
+
if @options.command_given? and @options.continued?
|
82
|
+
process_for { binding.eval(@options.command, '-e', 1) }
|
83
|
+
elsif @options.arguments_given? and @options.continued?
|
84
|
+
path, *argv = @options.arguments
|
85
|
+
process_for(argv) { load path }
|
86
|
+
elsif @options.version_requested?
|
87
|
+
process_for { }
|
88
|
+
else
|
89
|
+
process_for { binding.eval(@input.read, '-', 1) }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def process_for(argv = @options.arguments.dup, &process)
|
94
|
+
ARGV[0, ARGV.length] = argv
|
95
|
+
process
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'stringio'
|
3
|
+
require 'peeek/hook/specifier'
|
4
|
+
|
5
|
+
class Peeek
|
6
|
+
class CLI
|
7
|
+
class Help < StandardError; end
|
8
|
+
|
9
|
+
module EncodingOptions
|
10
|
+
|
11
|
+
# @attribute external_encoding
|
12
|
+
# @return [Encoding] external character encoding
|
13
|
+
attr_reader :external_encoding
|
14
|
+
|
15
|
+
def external_encoding=(encoding)
|
16
|
+
@external_encoding = Encoding.find(encoding)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @attribute internal_encoding
|
20
|
+
# @return [Encoding] internal character encoding
|
21
|
+
attr_reader :internal_encoding
|
22
|
+
|
23
|
+
def internal_encoding=(encoding)
|
24
|
+
@internal_encoding = Encoding.find(encoding)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class Options
|
30
|
+
|
31
|
+
SILENCE = 0
|
32
|
+
MEDIUM = 1
|
33
|
+
VERBOSE = 2
|
34
|
+
|
35
|
+
include EncodingOptions if defined? Encoding
|
36
|
+
|
37
|
+
# Determine if CLI options class has enable encoding options.
|
38
|
+
#
|
39
|
+
# @return whether CLI options class has enable encoding options
|
40
|
+
def self.encoding_options_enabled?
|
41
|
+
include?(EncodingOptions)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Initialize the CLI options.
|
45
|
+
#
|
46
|
+
# @param [Array<String>] argv arguments that is given from command line
|
47
|
+
def initialize(argv = [])
|
48
|
+
@debug = $DEBUG
|
49
|
+
@verbose = $VERBOSE
|
50
|
+
@version_requested = false
|
51
|
+
|
52
|
+
opt = OptionParser.new
|
53
|
+
opt.banner = 'Usage: peeek [switches] [--] [programfile] [arguments]'
|
54
|
+
opt.summary_indent = ' ' * 2
|
55
|
+
opt.summary_width = 15
|
56
|
+
|
57
|
+
@working_directory = nil
|
58
|
+
|
59
|
+
opt.on('-Cdirectory', 'cd to directory before executing your script') do |directory|
|
60
|
+
@working_directory = directory
|
61
|
+
end
|
62
|
+
|
63
|
+
opt.on('-d', '--debug', 'set debugging flags (set $DEBUG to true)') do
|
64
|
+
@debug = true
|
65
|
+
@verbose = verbose_by(VERBOSE)
|
66
|
+
end
|
67
|
+
|
68
|
+
commands = []
|
69
|
+
|
70
|
+
opt.on("-e 'command'", "one line of script. Several -e's allowed. Omit [programfile]") do |command|
|
71
|
+
commands << command
|
72
|
+
end
|
73
|
+
|
74
|
+
if self.class.encoding_options_enabled?
|
75
|
+
@external_encoding = Encoding.default_external
|
76
|
+
@internal_encoding = Encoding.default_internal
|
77
|
+
|
78
|
+
opt.on('-Eex[:in]', '--encoding=ex[:in]', 'specify the default external and internal character encodings') do |encodings|
|
79
|
+
external_encoding, internal_encoding = parse_encodings(encodings)
|
80
|
+
@external_encoding = external_encoding if external_encoding
|
81
|
+
@internal_encoding = internal_encoding
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@hook_targets = []
|
86
|
+
|
87
|
+
opt.on('-Hstring', 'object and method name that is target of hook') do |string|
|
88
|
+
hook_spec = Hook::Specifier.parse(string)
|
89
|
+
@hook_targets << hook_spec unless @hook_targets.include?(hook_spec)
|
90
|
+
end
|
91
|
+
|
92
|
+
@directories_to_load = []
|
93
|
+
|
94
|
+
opt.on('-Idirectory', 'specify $LOAD_PATH directory (may be used more than once)') do |directory|
|
95
|
+
@directories_to_load << directory unless @directories_to_load.include?(directory)
|
96
|
+
end
|
97
|
+
|
98
|
+
@required_libraries = []
|
99
|
+
|
100
|
+
opt.on('-rlibrary', 'require the library before executing your script') do |library|
|
101
|
+
@required_libraries << library unless @required_libraries.include?(library)
|
102
|
+
end
|
103
|
+
|
104
|
+
opt.on('-v', 'print version number, then turn on verbose mode') do
|
105
|
+
@version_requested = true
|
106
|
+
@verbose = verbose_by(VERBOSE)
|
107
|
+
end
|
108
|
+
|
109
|
+
opt.on('-w', '--verbose', 'turn warnings on for your script') do
|
110
|
+
@verbose = verbose_by(VERBOSE)
|
111
|
+
end
|
112
|
+
|
113
|
+
level_strings = [:SILENCE, :MEDIUM, :VERBOSE].map do |const_name|
|
114
|
+
"#{self.class.const_get(const_name)}=#{const_name.to_s.downcase}"
|
115
|
+
end
|
116
|
+
|
117
|
+
opt.on("-W[level=#{VERBOSE}]", "set warning level; #{level_strings * ', '}", Integer) do |level|
|
118
|
+
@verbose = verbose_by(level || VERBOSE)
|
119
|
+
end
|
120
|
+
|
121
|
+
@continued = true
|
122
|
+
|
123
|
+
opt.on('--version', 'print the version') do
|
124
|
+
@version_requested = true
|
125
|
+
@continued = false
|
126
|
+
end
|
127
|
+
|
128
|
+
opt.on_tail('-h', '--help', 'show this message') do
|
129
|
+
raise Help, opt.help
|
130
|
+
end
|
131
|
+
|
132
|
+
@arguments = opt.order(argv)
|
133
|
+
@command = commands * '; '
|
134
|
+
end
|
135
|
+
|
136
|
+
# @attribute debug
|
137
|
+
# @return [Boolean] debugging flags
|
138
|
+
attr_accessor :debug
|
139
|
+
|
140
|
+
# @attribute verbose
|
141
|
+
# @return [Boolean, nil] verbose mode
|
142
|
+
attr_accessor :verbose
|
143
|
+
|
144
|
+
# @attribute working_directory
|
145
|
+
# @return [String] current directory at executing
|
146
|
+
attr_accessor :working_directory
|
147
|
+
|
148
|
+
# @attribute directories_to_load
|
149
|
+
# @return [Array<String>] directories that adds to $LOAD_PATH
|
150
|
+
attr_accessor :directories_to_load
|
151
|
+
|
152
|
+
# @attribute required_libraries
|
153
|
+
# @return [Array<String>] libraries to require
|
154
|
+
attr_accessor :required_libraries
|
155
|
+
|
156
|
+
# @attribute hook_targets
|
157
|
+
# @return [Array<Peeek::Hook::Specifier>] targets to hook
|
158
|
+
attr_accessor :hook_targets
|
159
|
+
|
160
|
+
# @attribute command
|
161
|
+
# @return [String] Ruby code to execute
|
162
|
+
attr_accessor :command
|
163
|
+
|
164
|
+
# @attribute arguments
|
165
|
+
# @return [Array<String>] arguments at executing
|
166
|
+
attr_accessor :arguments
|
167
|
+
|
168
|
+
# Determine if print of the version requested.
|
169
|
+
#
|
170
|
+
# @return whether print of the version requested
|
171
|
+
def version_requested?
|
172
|
+
@version_requested
|
173
|
+
end
|
174
|
+
|
175
|
+
# Determine if a command given.
|
176
|
+
#
|
177
|
+
# @return whether a command given
|
178
|
+
def command_given?
|
179
|
+
!@command.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
# Determine if arguments given.
|
183
|
+
#
|
184
|
+
# @return whether arguments given
|
185
|
+
def arguments_given?
|
186
|
+
!@arguments.empty?
|
187
|
+
end
|
188
|
+
|
189
|
+
# Determine if continue process.
|
190
|
+
#
|
191
|
+
# @return whether continue process
|
192
|
+
def continued?
|
193
|
+
@continued
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def parse_encodings(encodings)
|
199
|
+
encodings = encodings.split(':')
|
200
|
+
encodings.map { |encoding| encoding.empty? ? nil : Encoding.find(encoding) }
|
201
|
+
end
|
202
|
+
|
203
|
+
def verbose_by(level)
|
204
|
+
verbose = {0 => nil, 1 => false, 2 => true}
|
205
|
+
raise ArgumentError, "invalid warning level - #{level}" unless verbose.key?(level)
|
206
|
+
verbose[level]
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
end
|
data/lib/peeek/hook.rb
CHANGED
@@ -33,7 +33,7 @@ class Peeek
|
|
33
33
|
# # => #<Peeek::Hook #<IO:<STDOUT>>.puts>
|
34
34
|
#
|
35
35
|
# @param [Module, Class, Object] object a target object that hook
|
36
|
-
# @param [String, Symbol] method_spec method
|
36
|
+
# @param [String, Symbol] method_spec method specifier of the object
|
37
37
|
# @yield [call] process a call to the method. give optionally
|
38
38
|
# @yieldparam [Peeek::Call] call a call to the method
|
39
39
|
# @return [Peeek::Hook] a hook to the method of the object
|
data/lib/peeek/hook/linker.rb
CHANGED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'peeek/hook'
|
2
|
+
|
3
|
+
class Peeek
|
4
|
+
class Hook
|
5
|
+
class Specifier
|
6
|
+
|
7
|
+
# Parse a string as hook specifier.
|
8
|
+
#
|
9
|
+
# @param [String] string string to parse as hook specifier
|
10
|
+
# @return [Peeek::Hook::Specifier] a hook specifier
|
11
|
+
def self.parse(string)
|
12
|
+
method_prefixes = METHOD_PREFIXES.sort_by(&:length).reverse.map do |method_prefix|
|
13
|
+
index = string.rindex(method_prefix)
|
14
|
+
priority = index ? [index + method_prefix.length, method_prefix.length] : nil
|
15
|
+
[method_prefix, index, priority]
|
16
|
+
end
|
17
|
+
|
18
|
+
method_prefixes = method_prefixes.select(&:last)
|
19
|
+
raise ArgumentError, "method name that is target of hook isn't specified in #{string.inspect}" if method_prefixes.empty?
|
20
|
+
method_prefix, index = method_prefixes.max_by(&:last)
|
21
|
+
method_prefix_range = index..(index + method_prefix.length - 1)
|
22
|
+
string_range = 0..(string.length - 1)
|
23
|
+
raise ArgumentError, "object name should not be empty for #{string.inspect}" unless string_range.begin < method_prefix_range.begin
|
24
|
+
raise ArgumentError, "method name should not be empty for #{string.inspect}" unless method_prefix_range.end < string_range.end
|
25
|
+
|
26
|
+
object_name = string[string_range.begin..(method_prefix_range.begin - 1)]
|
27
|
+
method_name = string[(method_prefix_range.end + 1)..string_range.end].to_sym
|
28
|
+
new(object_name, method_prefix, method_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Initialize the hook specifier.
|
32
|
+
#
|
33
|
+
# @param [String] object_name object name
|
34
|
+
# @param [String] method_prefix method prefix
|
35
|
+
# @param [Symbol] method_name method name in the object
|
36
|
+
def initialize(object_name, method_prefix, method_name)
|
37
|
+
@object_name = object_name
|
38
|
+
@method_prefix = normalize_method_prefix(method_prefix)
|
39
|
+
@method_name = method_name
|
40
|
+
end
|
41
|
+
|
42
|
+
# @attribute [r] object_name
|
43
|
+
# @return [String] object name
|
44
|
+
attr_reader :object_name
|
45
|
+
|
46
|
+
# @attribute [r] method_prefix
|
47
|
+
# @return [String] method prefix
|
48
|
+
attr_reader :method_prefix
|
49
|
+
|
50
|
+
# @attribute [r] method_name
|
51
|
+
# @return [Symbol] method name in the object
|
52
|
+
attr_reader :method_name
|
53
|
+
|
54
|
+
# @attribute [r] method_specifier
|
55
|
+
# @return [String] method specifier in the object
|
56
|
+
def method_specifier
|
57
|
+
@method_prefix + @method_name.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
@object_name + method_specifier
|
62
|
+
end
|
63
|
+
|
64
|
+
def inspect
|
65
|
+
"#<#{self.class} #{self}>"
|
66
|
+
end
|
67
|
+
|
68
|
+
def ==(other)
|
69
|
+
self.class == other.class &&
|
70
|
+
@object_name == other.object_name &&
|
71
|
+
@method_prefix == other.method_prefix &&
|
72
|
+
@method_name == other.method_name
|
73
|
+
end
|
74
|
+
alias eql? ==
|
75
|
+
|
76
|
+
def hash
|
77
|
+
values = [@object_name, @method_prefix, @method_name]
|
78
|
+
values.inject(self.class.hash) { |hash, value| (hash << 32) + value.hash }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def normalize_method_prefix(method_prefix)
|
84
|
+
case method_prefix
|
85
|
+
when *INSTANCE_METHOD_PREFIXES
|
86
|
+
Instance::METHOD_PREFIX
|
87
|
+
when *SINGLETON_METHOD_PREFIXES
|
88
|
+
Singleton::METHOD_PREFIX
|
89
|
+
else
|
90
|
+
init = METHOD_PREFIXES.map(&:inspect)
|
91
|
+
last = init.pop
|
92
|
+
method_prefixes = [init * ', ', last] * ' or '
|
93
|
+
raise ArgumentError, "invalid method prefix, #{method_prefixes} are valid"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|