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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d9163af655c3a81a95497b5b85dbba4806d38a8
4
- data.tar.gz: 94d66998484a454134c8c41041e593f4b458b458
3
+ metadata.gz: 90ae3ee8f84b1d47140d8ebd28943835875550be
4
+ data.tar.gz: 8a7bdc5de0df596be2e309ee950112e89ff94166
5
5
  SHA512:
6
- metadata.gz: e4593100bd8419742be0174883536e40f7628211411f124259eef776481968486a387a2916dc958510239a7be27b266ce004f094a8b5fc87d059095d435f51cf
7
- data.tar.gz: 2263f4beddadacbc497b4cc1c47a076b34c755c8ef9e8cda003b7e8ad55d7eef423fdbd427bfe014a5314bbf456feb905fd52e5fb49e76d894162221e2fc7ae6
6
+ metadata.gz: f6ffdc7048653dc766511b66272c730a1f37ac04a1aa7efe35efa4a834916f758d5766ad5c07da75954cb5a63c4a067788014514ca2edcd94756654f5b13dc75
7
+ data.tar.gz: d32b0ca28b0520cf773aa1f1f22c678168cdf6211dcac657796d1dc73fd85819db714df49e7e2ca8c4a9cd8fa78b1a62f80818afbddd3fb9c786c44649ef55e3
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ruby-head
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/peeek CHANGED
@@ -1,16 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
- require 'stringio'
3
- require 'peeek'
2
+ require 'peeek/cli'
4
3
 
5
- path = ARGV.first
6
-
7
- original_stdout = $stdout
8
- $stdout = StringIO.new
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
@@ -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
- # object_and_method_specs an object and method specification(s) that be
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(object_and_method_specs)
53
+ def self.capture(hook_targets)
53
54
  raise ArgumentError, 'block not supplied' unless block_given?
54
55
 
55
56
  local do
56
- object_and_method_specs.each { |object, method_specs| current.hook(object, *method_specs) }
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 specifications of
84
- # the object. see also examples of {Peeek::Hook.create}
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 specifications
134
- # of the object. see also examples of {Peeek::Hook.create}
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
  #
@@ -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
@@ -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 specification of the object
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
@@ -6,6 +6,7 @@ class Peeek
6
6
  class << self
7
7
 
8
8
  # @attribute [r] classes
9
+ # @scope class
9
10
  # @return [Array<Class>] classes valid as linker
10
11
  attr_reader :classes
11
12
 
@@ -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