peeek 1.0.1 → 1.0.2

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.
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