break 0.7.0 → 0.8.0

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
  SHA256:
3
- metadata.gz: cb3884c4c9ee2235449260f2ee2395c356ab4c1db2fb7b4b052617df40d1f03a
4
- data.tar.gz: 46bbb28dadb0c3fc3beffa6af4bd12481a3c9c409d5bd409804347bf05923ef7
3
+ metadata.gz: e14cd966917b91b925d085e986916164b42e5a3dd1986818b0e45e291e8177d5
4
+ data.tar.gz: 596fc1d57d319b23303ec89832c3e93cfbc62a6d040406f5ce9cad4dc8103735
5
5
  SHA512:
6
- metadata.gz: 5109915a60c4f32575eceb3a2fc25e8ea58164e0fab643876ff61ce76943d497df197168d0e9c7c6b1bf2b3339801883a000637ccfbfa9d31f3405db2cf19cfe
7
- data.tar.gz: 37aeec66fe323a44be5b7a2cbd957c163458752f2352d7c146dcde4cc06eecd363bf573cfb662857b1b36b585d804fa93baf67936221dc87676fdceca1e4cde1
6
+ metadata.gz: 8c9bf9ea4a4ddb23c69c27f536e1f54b87dd785346dd70e7f13c5dcc6fe51248ec3987f87e35f4dd6dd03016de4360e94a744d5218b5c33c48ce96d1e443e78a
7
+ data.tar.gz: 5f4f020f420ed3adb3db0db53960fa68c0b2824802dd3a718923ae151d71ae46eabeb037414ac3753131f329eccd46305b04429e1c77705eff4b7e61e5d4a718
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "break/version"
4
+ require_relative "break/session"
5
+ require_relative "break/context"
6
+ require_relative "break/command"
7
+ require_relative "break/commands"
8
+ require_relative "break/filter"
9
+ require_relative "break/binding_ext"
10
+
11
+ require_relative "break/irb"
12
+ require_relative "break/pry" if defined?(Pry)
13
+
14
+ Break::Filter.register_internal __dir__
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Binding
4
+ unless method_defined?(:source_location)
5
+ def source_location
6
+ eval "[__FILE__, __LINE__.to_i]"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Break
6
+ class Command
7
+ extend Forwardable
8
+
9
+ def initialize(session)
10
+ @session = session
11
+ end
12
+
13
+ def execute
14
+ raise NotImplementedError
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :session
20
+ def_delegators :session, :context, :context!, :frontend
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "commands/tracepoint_command"
2
+ require_relative "commands/continue_command"
3
+ require_relative "commands/list_command"
4
+ require_relative "commands/next_command"
5
+ require_relative "commands/step_command"
6
+ require_relative "commands/up_command"
7
+ require_relative "commands/down_command"
8
+
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class ContinueCommand < Command
5
+ def execute(*)
6
+ session.leave
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class DownCommand < TracePointCommand
5
+ def execute(*)
6
+ if context.depth >= 0
7
+ frontend.notify("Cannot go further down the stack")
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def execute_trace(trace, *)
14
+ trace.disable
15
+
16
+ context!(*context.bindings, depth: context.depth + 1)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class ListCommand < Command
5
+ def execute(*)
6
+ frontend.where
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class NextCommand < TracePointCommand
5
+ trace :line, :call, :return, :class, :end, :b_call, :b_return
6
+
7
+ def execute_trace(trace, *)
8
+ case trace.event
9
+ when :call, :class, :b_call
10
+ context.bindings << trace.binding
11
+ context.depth += 1
12
+ when :return, :end, :b_return
13
+ context.bindings.pop
14
+ context.depth -= 1
15
+ when :line
16
+ return if context.depth.positive?
17
+
18
+ trace.disable
19
+
20
+ context!(*context.bindings[0...-1], trace.binding)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class StepCommand < TracePointCommand
5
+ trace :line, :call, :return, :class, :end, :b_call, :b_return
6
+
7
+ def execute_trace(trace, *)
8
+ case trace.event
9
+ when :call, :class, :b_call
10
+ trace.disable
11
+
12
+ context!(*context.bindings, trace.binding)
13
+ when :return, :end, :b_return
14
+ context.bindings.pop
15
+ context.depth -= 1
16
+ when :line
17
+ return if context.depth.positive?
18
+
19
+ trace.disable
20
+
21
+ context!(*context.bindings[0...-1], trace.binding)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class TracePointCommand < Command
5
+ class << self
6
+ attr_reader :trace_events
7
+
8
+ def trace(*events)
9
+ @trace_events = events
10
+ end
11
+ end
12
+
13
+ def execute(*args)
14
+ TracePoint.trace(*trace_events) do |trace|
15
+ next if Filter.internal?(trace.path)
16
+
17
+ execute_trace(trace, *args)
18
+ end
19
+
20
+ session.leave
21
+ end
22
+
23
+ def execute_trace
24
+ raise NotImplementedError
25
+ end
26
+
27
+ private
28
+
29
+ def trace_events
30
+ self.class.trace_events
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class UpCommand < TracePointCommand
5
+ def execute(*)
6
+ if context.bindings[context.depth - 2]
7
+ super
8
+ else
9
+ frontend.notify("Cannot go further up the stack")
10
+ end
11
+ end
12
+
13
+ def execute_trace(trace, *)
14
+ trace.disable
15
+
16
+ context!(*context.bindings, depth: context.depth - 1)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class Context
5
+ attr_accessor :bindings
6
+ attr_accessor :depth
7
+
8
+ def initialize(*bindings, depth: 0)
9
+ @bindings = bindings
10
+ @depth = depth
11
+ end
12
+
13
+ def binding
14
+ @bindings[@depth - 1]
15
+ end
16
+
17
+ def inspect
18
+ "#{path}:#{lineno}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ module Filter
5
+ extend self
6
+
7
+ attr_reader :internal
8
+
9
+ def register_internal(*paths)
10
+ (@internal ||= []).concat(paths)
11
+ end
12
+
13
+ def internal?(path)
14
+ @internal.any? { |location| path.include?(location) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "irb/frontend"
4
+ require_relative "irb/commands"
5
+ require_relative "irb/overrides"
6
+
7
+ Break::Filter.register_internal IRB.method(:start).source_location.first.chomp(".rb")
8
+ Break::Filter.register_internal "(irb)"
9
+
10
+ Binding.prepend Break::IRB::Overrides
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Break::IRB
6
+ class Commands < Module
7
+ def initialize(session)
8
+ define_command session, :next, Break::NextCommand
9
+ define_command session, :step, Break::StepCommand
10
+ define_command session, :up, Break::UpCommand
11
+ define_command session, :down, Break::DownCommand
12
+ define_command session, :continue, Break::ContinueCommand, preserve: false
13
+ end
14
+
15
+ private
16
+
17
+ def define_command(session, name, cls, preserve: true)
18
+ define_method(name) do |*args|
19
+ cls.new(session).execute(*args)
20
+ ensure
21
+ # We don't have a clear guideline of when an IRB session starts and
22
+ # when it ends. If we're excuting commands, we have to quit the IRB
23
+ # session which naturally marks it as stopped. If we're executing a
24
+ # `break` command though we actually want to keep marking it as started
25
+ # so we don't step over `binding.irb` calls.
26
+ Break::Session.start! if preserve
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "irb"
4
+
5
+ module Break::IRB
6
+ class Frontend
7
+ def initialize
8
+ IRB.setup caller_locations.first.path, argv: []
9
+ end
10
+
11
+ def attach(session)
12
+ @workspace = IRB::WorkSpace.new(session.context.binding)
13
+
14
+ begin
15
+ @irb = IRB::Irb.new(@workspace)
16
+ @irb.context.main.extend Commands.new(session)
17
+ rescue TypeError
18
+ return
19
+ end
20
+
21
+ where
22
+
23
+ @irb.suspend_context special_case_next_eval(@irb.context) do
24
+ @irb.run(IRB.conf)
25
+ end
26
+ end
27
+
28
+ def detach
29
+ @irb&.context&.exit
30
+ end
31
+
32
+ def where
33
+ puts @workspace.code_around_binding if @workspace
34
+ end
35
+
36
+ def notify(message)
37
+ puts message
38
+ end
39
+
40
+ private
41
+
42
+ # Evaling `next` is a `SyntaxError` in Ruby. Since IRB does not have
43
+ # commands support in the lexer level, we need to call the `next` command
44
+ # in syntactically correct way.
45
+ def special_case_next_eval(irb_context)
46
+ def irb_context.evaluate(line, line_no, exception: nil)
47
+ line = "self.next\n" if line == "next\n"
48
+ super(line, line_no, exception: exception)
49
+ end
50
+
51
+ irb_context
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::IRB
4
+ module Overrides
5
+ def irb
6
+ return if Break::Session.active?
7
+
8
+ Break::Session.start!
9
+
10
+ begin
11
+ session = Break::Session.new(self, frontend: Frontend.new)
12
+ session.enter
13
+ ensure
14
+ Break::Session.stop!
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pry"
4
+ require "break"
5
+
6
+ require_relative "pry/frontend"
7
+ require_relative "pry/commands"
8
+ require_relative "pry/overrides"
9
+
10
+ Break::Filter.register_internal MethodSource.method(:source_helper).source_location.first.chomp(".rb")
11
+ Break::Filter.register_internal CodeRay.method(:scan).source_location.first.chomp(".rb")
12
+ Break::Filter.register_internal Pry.method(:start).source_location.first.chomp("/pry_class.rb")
13
+ Break::Filter.register_internal "(pry)"
14
+ Break::Filter.register_internal __dir__
15
+
16
+ Object.prepend Break::Pry::Overrides
17
+
18
+ Pry.config.commands.import Break::Pry::Commands
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::Pry
4
+ Commands = Pry::CommandSet.new do
5
+ create_command "next", "Go to the next line." do
6
+ group "Break"
7
+
8
+ banner <<-BANNER
9
+ Usage: next
10
+
11
+ Step over within the same frame.
12
+
13
+ Examples:
14
+ next #=> Move a line forward.
15
+ BANNER
16
+
17
+ def process
18
+ session = Break::Session.new(_pry_.binding_stack.first, frontend: Frontend.new(_pry_))
19
+
20
+ command = Break::NextCommand.new(session)
21
+ command.execute
22
+ ensure
23
+ Break::Session.start!
24
+ end
25
+ end
26
+
27
+ create_command "step", "Step into the current line invocation." do
28
+ group "Break"
29
+
30
+ banner <<-BANNER
31
+ Usage: step
32
+
33
+ Step into a method call.
34
+
35
+ Examples:
36
+ step #=> Step into the method invocation.
37
+ BANNER
38
+
39
+ def process
40
+ session = Break::Session.new(_pry_.binding_stack.first, frontend: Frontend.new(_pry_))
41
+
42
+ command = Break::StepCommand.new(session)
43
+ command.execute
44
+ ensure
45
+ Break::Session.start!
46
+ end
47
+ end
48
+
49
+ create_command "up", "Go up a frame." do
50
+ group "Break"
51
+
52
+ banner <<-BANNER
53
+ Usage: up
54
+
55
+ Go to the frame that called the current one. Can be used only if the
56
+ command `step` was issued before.
57
+
58
+ Examples:
59
+ up #=> Step into the method invocation.
60
+ BANNER
61
+
62
+ def process
63
+ session = Break::Session.new(_pry_.binding_stack.first, frontend: Frontend.new(_pry_))
64
+
65
+ command = Break::UpCommand.new(session)
66
+ command.execute
67
+ ensure
68
+ Break::Session.start!
69
+ end
70
+ end
71
+
72
+ create_command "down", "Go down a frame." do
73
+ group "Break"
74
+
75
+ banner <<-BANNER
76
+ Usage: down
77
+
78
+ Go to the frame called from the current one. Can be used only if the
79
+ command `step` was issued before.
80
+
81
+ Examples:
82
+ down #=> Step to the previous frame.
83
+ BANNER
84
+
85
+ def process
86
+ session = Break::Session.new(_pry_.binding_stack.first, frontend: Frontend.new(_pry_))
87
+
88
+ command = Break::DownCommand.new(session)
89
+ command.execute
90
+ ensure
91
+ Break::Session.start!
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::Pry
4
+ class Frontend
5
+ def initialize(pry)
6
+ @pry = pry
7
+ end
8
+
9
+ def attach(session)
10
+ @pry = Pry.start(session.context.binding)
11
+ where
12
+ end
13
+
14
+ def detach
15
+ throw :breakout
16
+ end
17
+
18
+ def where
19
+ @pry&.run_command "whereami".dup
20
+ rescue MethodSource::SourceNotFoundError
21
+ puts "Cannot find method code."
22
+ end
23
+
24
+ def notify(message)
25
+ puts message
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::Pry
4
+ # Overrides is a module that is supposed to be prepended into ::Object's
5
+ # ancestor chain and hook into the #pry method and prevent double break
6
+ # sessions.
7
+ module Overrides
8
+ def pry(*)
9
+ return if Break::Session.active?
10
+
11
+ Break::Session.start!
12
+
13
+ begin
14
+ super
15
+ ensure
16
+ Break::Session.stop!
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class Session
5
+ class << self
6
+ def start!
7
+ Thread.current[:__break_active_session__] = true
8
+ end
9
+
10
+ def stop!
11
+ Thread.current[:__break_active_session__] = nil
12
+ end
13
+
14
+ def active?
15
+ Thread.current[:__break_active_session__]
16
+ end
17
+ end
18
+
19
+ attr_reader :contexts
20
+ attr_reader :frontend
21
+
22
+ def initialize(binding, frontend:)
23
+ @contexts = [Context.new(binding)]
24
+ @frontend = frontend
25
+ end
26
+
27
+ def enter
28
+ frontend.attach(self)
29
+ end
30
+
31
+ def leave
32
+ frontend.detach
33
+ end
34
+
35
+ def context
36
+ contexts.last
37
+ end
38
+
39
+ def context!(*bindings, depth: 0)
40
+ contexts << Context.new(*bindings, depth: depth)
41
+ enter
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest"
4
+
5
+ module Break
6
+ class TestingFrontend
7
+ class << self
8
+ def build(&block)
9
+ Class.new(self, &block)
10
+ end
11
+
12
+ def stack
13
+ @stack ||= []
14
+ end
15
+
16
+ def [](index)
17
+ stack[index]
18
+ end
19
+
20
+ def last
21
+ stack.last
22
+ end
23
+
24
+ def clear
25
+ stack.clear
26
+ end
27
+ end
28
+
29
+ attr_reader :session
30
+
31
+ def initialize
32
+ self.class.stack << self
33
+ end
34
+
35
+ def attach(session)
36
+ @session = session
37
+ end
38
+
39
+ def detach; end
40
+
41
+ def where; end
42
+
43
+ def notify(_message); end
44
+ end
45
+
46
+ class Test < Minitest::Test
47
+ def self.test(name, &block)
48
+ define_method("test_#{name}", &block)
49
+ end
50
+
51
+ def pass
52
+ Kernel.binding.tap do |binding|
53
+ yield binding if block_given?
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ VERSION = "0.8.0"
5
+ end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: break
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Genadi Samokovarov
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2019-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: minitest
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.11'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.11'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '5.11'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '5.11'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,7 +58,31 @@ email:
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
- files: []
61
+ files:
62
+ - lib/break.rb
63
+ - lib/break/binding_ext.rb
64
+ - lib/break/command.rb
65
+ - lib/break/commands.rb
66
+ - lib/break/commands/continue_command.rb
67
+ - lib/break/commands/down_command.rb
68
+ - lib/break/commands/list_command.rb
69
+ - lib/break/commands/next_command.rb
70
+ - lib/break/commands/step_command.rb
71
+ - lib/break/commands/tracepoint_command.rb
72
+ - lib/break/commands/up_command.rb
73
+ - lib/break/context.rb
74
+ - lib/break/filter.rb
75
+ - lib/break/irb.rb
76
+ - lib/break/irb/commands.rb
77
+ - lib/break/irb/frontend.rb
78
+ - lib/break/irb/overrides.rb
79
+ - lib/break/pry.rb
80
+ - lib/break/pry/commands.rb
81
+ - lib/break/pry/frontend.rb
82
+ - lib/break/pry/overrides.rb
83
+ - lib/break/session.rb
84
+ - lib/break/testing.rb
85
+ - lib/break/version.rb
62
86
  homepage: https://github.com/gsamokovarov/break
63
87
  licenses:
64
88
  - MIT