byebug 10.0.2 → 11.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,36 +43,8 @@ abide by its terms.
43
43
 
44
44
  ## Code style
45
45
 
46
- * Byebug uses [codeclimate][] to enforce code style. You can run codeclimate
47
- checks locally using the [codeclimate CLI][] with `codeclimate analyze`.
48
-
49
- * It also uses some extra style checks that are not available in codeclimate.
50
- You can run those using `bin/rake lint`. These tasks are:
51
-
52
- * Linting of c-files using `clang-format`. Configuration is specific to
53
- clang-format 3.8, you may need some extra work to get that installed on macOS,
54
- see below.
55
-
56
- * Checking correct executable bit on repository files.
57
-
58
- [codeclimate]: https://codeclimate.com/github/deivid-rodriguez/byebug
59
- [codeclimate CLI]: https://github.com/codeclimate/codeclimate
60
-
61
- ### Runnning `clang-format` on macOS
62
-
63
- At the moment byebug uses older `clang-format` version to enforce C codestyle than
64
- can be found in Homebrew. If you are planning to change some C source here it is
65
- recommended to use [direnv][] to hook that older version into your shell:
66
-
67
- * Install [direnv][] as described in their README
68
- * Install `clang-format@3.8` with `brew install clang-format@3.8`
69
- * In byebug source code directory do `echo 'export PATH="/usr/local/opt/clang-format@3.8/bin:$PATH"' > .envrc`
70
- * Allow direnv to use that `.envrc` file with `direnv allow`
71
-
72
- With that your `$PATH` will be updated to use older `clang-format` every time you `cd`
73
- into byebug source code folder. It will reverted back when you `cd` out of it as well.
74
-
75
- [direnv]: https://github.com/direnv/direnv/
46
+ * Byebug uses several style checks to check code style consistent. You can run
47
+ those using `bin/rake lint`.
76
48
 
77
49
  ## Byebug as a C-extension
78
50
 
@@ -81,6 +53,6 @@ functionality is implemented in C (the interaction with the TracePoint API).
81
53
  The rest of the gem is implemented in Ruby. Normally you won't need to touch
82
54
  the C-extension, but it will obviously depended on the bug you're trying to fix
83
55
  or the feature you are willing to add. You can learn more about C-extensions
84
- [here](http://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html)
56
+ [here](https://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html)
85
57
  or
86
- [here](http://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html).
58
+ [here](https://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html).
data/GUIDE.md CHANGED
@@ -69,12 +69,12 @@ Now let us step through the program.
69
69
  5: tri = 0
70
70
  6:
71
71
  7: 0.upto(n) { |i| tri += i }
72
- 9: end
73
- 10:
74
- 11: tri
75
- 12: end
76
- 13:
77
- => 14: triangle(3)
72
+ 8:
73
+ 9: tri
74
+ 10: end
75
+ 11:
76
+ => 12: t = triangle(3)
77
+ 13: puts t
78
78
  (byebug) <RET> # hit enter
79
79
 
80
80
  [1, 10] in /path/to/triangle.rb
@@ -613,7 +613,7 @@ We get the same result as if we had run byebug from the outset.
613
613
  ### Debugging Oddities: How debugging Ruby may be different from other languages
614
614
 
615
615
  If you are used to debugging in other languages like C, C++, Perl, Java or even
616
- Bash (see [bashdb](http://bashdb.sf.net)), there may be a number of things that
616
+ Bash (see [bashdb](http://bashdb.sourceforge.net)), there may be a number of things that
617
617
  seem or feel a little bit different and may confuse you. A number of these
618
618
  things aren't oddities of the debugger per se but differences in how Ruby works
619
619
  compared to those other languages. Because Ruby works a little differently from
@@ -1298,7 +1298,7 @@ Running a program from byebug adds a bit of overhead and slows it down a little.
1298
1298
  Furthermore, by necessity, debuggers change the operation of the program they
1299
1299
  are debugging. And this can lead to unexpected and unwanted differences. It has
1300
1300
  happened so often that the term
1301
- [Heisenbugs](http://en.wikipedia.org/wiki/Heisenbug) was coined to describe the
1301
+ [Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug) was coined to describe the
1302
1302
  situation where using a debugger (among other possibilities) changes the
1303
1303
  behavior of the program so that the bug doesn't manifest itself anymore.
1304
1304
 
@@ -1468,6 +1468,7 @@ display a short list of named classes of commands
1468
1468
  save -- Saves current byebug session to a file
1469
1469
  set -- Modifies byebug settings
1470
1470
  show -- Shows byebug settings
1471
+ skip -- Runs until the next breakpoint as long as it is different from the current one
1471
1472
  source -- Restores a previously saved byebug session
1472
1473
  step -- Steps into blocks or methods one or more times
1473
1474
  thread -- Commands to manipulate threads
data/LICENSE CHANGED
@@ -1,23 +1,23 @@
1
- Copyright (c) David Rodríguez <deivid.rodriguez@gmail.com>
1
+ Copyright (c) 2018 David Rodríguez <deivid.rodriguez@riseup.net>
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
5
- modification, are permitted provided that the following conditions
6
- are met:
7
- 1. Redistributions of source code must retain the above copyright
8
- notice, this list of conditions and the following disclaimer.
9
- 2. Redistributions in binary form must reproduce the above copyright
10
- notice, this list of conditions and the following disclaimer in the
11
- documentation and/or other materials provided with the distribution.
5
+ modification, are permitted provided that the following conditions are met:
12
6
 
13
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
14
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17
18
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
- SUCH DAMAGE.
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md CHANGED
@@ -1,27 +1,28 @@
1
1
  # Byebug
2
2
 
3
3
  [![Version][gem]][gem_url]
4
- [![Maintainability][mai]][mai_url]
4
+ [![Tidelift][tid]][tid_url]
5
5
  [![Coverage][cov]][cov_url]
6
6
  [![Gitter][irc]][irc_url]
7
7
 
8
8
  [gem]: https://img.shields.io/gem/v/byebug.svg
9
- [mai]: https://api.codeclimate.com/v1/badges/f1a1bec582752c22da80/maintainability
9
+ [tid]: https://tidelift.com/badges/github/deivid-rodriguez/byebug
10
10
  [cov]: https://api.codeclimate.com/v1/badges/f1a1bec582752c22da80/test_coverage
11
11
  [irc]: https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg
12
12
 
13
13
  [gem_url]: https://rubygems.org/gems/byebug
14
- [mai_url]: https://codeclimate.com/github/deivid-rodriguez/byebug/maintainability
14
+ [tid_url]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_badge
15
15
  [cov_url]: https://codeclimate.com/github/deivid-rodriguez/byebug/test_coverage
16
16
  [irc_url]: https://gitter.im/deivid-rodriguez/byebug
17
17
 
18
- Byebug is a simple to use, feature rich debugger for Ruby. It uses the
18
+ Byebug is a simple to use and feature rich debugger for Ruby. It uses the
19
19
  TracePoint API for execution control and the Debug Inspector API for call stack
20
- navigation, so it doesn't depend on internal core sources. It's developed as a C
21
- extension, so it's fast. And it has a full test suite so it's reliable.
20
+ navigation. Therefore, Byebug doesn't depend on internal core sources. Byebug is also
21
+ fast because it is developed as a C extension and reliable because it is supported
22
+ by a full test suite.
22
23
 
23
- It allows you to see what is going on _inside_ a Ruby program while it executes
24
- and offers many of the traditional debugging features such as:
24
+ The debugger permits the ability to understand what is going on _inside_ a Ruby program
25
+ while it executes and offers many of the traditional debugging features such as:
25
26
 
26
27
  * Stepping: Running your program one line at a time.
27
28
  * Breaking: Pausing the program at some event or specified instruction, to
@@ -34,7 +35,6 @@ and offers many of the traditional debugging features such as:
34
35
  ## Build Status
35
36
 
36
37
  Linux [![Cir][cir]][cir_url]
37
- macOS [![Tra][tra]][tra_url]
38
38
  Windows [![Vey][vey]][vey_url]
39
39
 
40
40
  [cir]: https://circleci.com/gh/deivid-rodriguez/byebug/tree/master.svg?style=svg
@@ -47,9 +47,7 @@ Windows [![Vey][vey]][vey_url]
47
47
 
48
48
  ## Requirements
49
49
 
50
- * Required: MRI 2.2.0 or higher.
51
-
52
- * Recommended: MRI 2.3.0 or higher.
50
+ MRI 2.3.0 or higher.
53
51
 
54
52
  ## Install
55
53
 
@@ -57,7 +55,7 @@ Windows [![Vey][vey]][vey_url]
57
55
  gem install byebug
58
56
  ```
59
57
 
60
- Or if you use `bundler`,
58
+ Alternatively, if you use `bundler`:
61
59
 
62
60
  ```shell
63
61
  bundle add byebug --group "development, test"
@@ -67,12 +65,9 @@ bundle add byebug --group "development, test"
67
65
 
68
66
  ### From within the Ruby code
69
67
 
70
- Simply drop
71
-
72
- byebug
73
-
74
- wherever you want to start debugging and the execution will stop there.
75
- If you were debugging Rails, for example, you would add `byebug` to your code.
68
+ Simply include `byebug` wherever you want to start debugging and the execution will
69
+ stop there. For example, if you were debugging Rails, you would add `byebug` to
70
+ your code:
76
71
 
77
72
  ```ruby
78
73
  def index
@@ -81,13 +76,13 @@ def index
81
76
  end
82
77
  ```
83
78
 
84
- And then start a Rails server.
79
+ And then start a Rails server:
85
80
 
86
81
  ```shell
87
82
  bin/rails s
88
83
  ```
89
84
 
90
- Once the execution gets to your `byebug` command you will get a debugging prompt.
85
+ Once the execution gets to your `byebug` command, you will receive a debugging prompt.
91
86
 
92
87
  ### From the command line
93
88
 
@@ -99,49 +94,50 @@ byebug myscript.rb
99
94
 
100
95
  ## Byebug's commands
101
96
 
102
- Command | Aliases | Subcommands
103
- ------- | ------- | -----------
104
- `backtrace` | `bt` `where` |
105
- `break` | |
106
- `catch` | |
107
- `condition` | |
108
- `continue` | |
109
- `debug` | |
110
- `delete` | |
111
- `disable` | | `breakpoints` `display`
112
- `display` | |
113
- `down` | |
114
- `edit` | |
115
- `enable` | | `breakpoints` `display`
116
- `finish` | |
117
- `frame` | |
118
- `help` | |
119
- `history` | |
120
- `info` | | `args` `breakpoints` `catch` `display` `file` `line` `program`
121
- `interrupt` | |
122
- `irb` | |
123
- `kill` | |
124
- `list` | |
125
- `method` | | `instance`
126
- `next` | |
127
- `pry` | |
128
- `quit` | |
129
- `restart` | |
130
- `save` | |
131
- `set` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width`
132
- `show` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width`
133
- `source` | |
134
- `step` | |
135
- `thread` | | `current` `list` `resume` `stop` `switch`
136
- `tracevar` | |
137
- `undisplay` | |
138
- `untracevar`| |
139
- `up` | |
140
- `var` | | `all` `constant` `global` `instance` `local`
97
+ Command | Aliases | Subcommands
98
+ ------- | ------- | -----------
99
+ `backtrace` | `bt` `w` `where`|
100
+ `break` | `b` |
101
+ `catch` | `cat` |
102
+ `condition` | `cond` |
103
+ `continue` | `c` `cont` |
104
+ `debug` | |
105
+ `delete` | `del` |
106
+ `disable` | `dis` | `breakpoints` `display`
107
+ `display` | `disp` |
108
+ `down` | |
109
+ `edit` | `ed` |
110
+ `enable` | `en` | `breakpoints` `display`
111
+ `finish` | `fin` |
112
+ `frame` | `f` |
113
+ `help` | `h` |
114
+ `history` | `hist` |
115
+ `info` | `i` | `args` `breakpoints` `catch` `display` `file` `line` `program`
116
+ `interrupt` | `int` |
117
+ `irb` | |
118
+ `kill` | |
119
+ `list` | `l` |
120
+ `method` | `m` | `instance`
121
+ `next` | `n` |
122
+ `pry` | |
123
+ `quit` | `q` |
124
+ `restart` | |
125
+ `save` | `sa` |
126
+ `set` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width`
127
+ `show` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width`
128
+ `skip` | `sk` |
129
+ `source` | `so` |
130
+ `step` | `s` |
131
+ `thread` | `th` | `current` `list` `resume` `stop` `switch`
132
+ `tracevar` | `tr` |
133
+ `undisplay` | `undisp` |
134
+ `untracevar`| `untr` |
135
+ `up` | |
136
+ `var` | `v` | `all` `constant` `global` `instance` `local`
141
137
 
142
138
  ## Semantic Versioning
143
139
 
144
- Byebug tries to follow [semantic versioning](http://semver.org) and tries to
140
+ Byebug attempts to follow [semantic versioning](https://semver.org) and
145
141
  bump major version only when backwards incompatible changes are released.
146
142
  Backwards compatibility is targeted to [pry-byebug] and any other plugins
147
143
  relying on `byebug`.
@@ -166,10 +162,18 @@ started. Proper documentation will be eventually written.
166
162
 
167
163
  See [Getting Started with Development](CONTRIBUTING.md).
168
164
 
169
- You can also help `byebug` by leaving a small (or big) tip through
170
- [Liberapay][liberapay.com].
165
+ ## Funding
166
+
167
+ Subscribe to [Tidelift] to ensure byebug stays actively maintained, and at the
168
+ same time get licensing assurances and timely security notifications for your
169
+ open source dependencies.
170
+
171
+ You can also help `byebug` by leaving a small (or big) tip through [Liberapay].
172
+
173
+ ## Security contact information
171
174
 
172
- [![Support via Liberapay][liberapay-button]][liberapay-donate]
175
+ Please use the Tidelift security contact to [report a security vulnerability].
176
+ Tidelift will coordinate the fix and disclosure.
173
177
 
174
178
  ## Credits
175
179
 
@@ -190,6 +194,6 @@ software, especially:
190
194
  [minitest-byebug]: https://github.com/kaspth/minitest-byebug
191
195
  [sublime_debugger]: https://github.com/shuky19/sublime_debugger
192
196
  [atom-byebug]: https://github.com/izaera/atom-byebug
193
- [liberapay.com]: https://liberapay.com
194
- [liberapay-button]: https://liberapay.com/assets/widgets/donate.svg
195
- [liberapay-donate]: https://liberapay.com/byebug/donate
197
+ [Liberapay]: https://liberapay.com/byebug/donate
198
+ [Tidelift]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_text
199
+ [report a security vulnerability]: https://tidelift.com/security
@@ -28,6 +28,7 @@ require "byebug/commands/restart"
28
28
  require "byebug/commands/save"
29
29
  require "byebug/commands/set"
30
30
  require "byebug/commands/show"
31
+ require "byebug/commands/skip"
31
32
  require "byebug/commands/source"
32
33
  require "byebug/commands/step"
33
34
  require "byebug/commands/thread"
@@ -23,8 +23,8 @@ module Byebug
23
23
 
24
24
  def self.description
25
25
  <<-DESCRIPTION
26
- b[reak] [file:]line [if expr]
27
- b[reak] [module::...]class(.|#)method [if expr]
26
+ b[reak] [<file>:]<line> [if <expr>]
27
+ b[reak] [<module>::...]<class>(.|#)<method> [if <expr>]
28
28
 
29
29
  They can be specified by line or method and an expression can be added
30
30
  for conditionally enabled breakpoints.
@@ -43,9 +43,7 @@ module Byebug
43
43
  b = line_breakpoint(@match[1]) || method_breakpoint(@match[1])
44
44
  return errmsg(pr("break.errors.location")) unless b
45
45
 
46
- if syntax_valid?(@match[2])
47
- return puts(pr("break.created", id: b.id, file: b.source, line: b.pos))
48
- end
46
+ return puts(pr("break.created", id: b.id, file: b.source, line: b.pos)) if syntax_valid?(@match[2])
49
47
 
50
48
  errmsg(pr("break.errors.expression", expr: @match[2]))
51
49
  b.enabled = false
@@ -76,7 +74,7 @@ module Byebug
76
74
  def target_object(str)
77
75
  k = error_eval(str)
78
76
 
79
- k && k.is_a?(Module) ? k.name : str
77
+ k&.is_a?(Module) ? k.name : str
80
78
  rescue StandardError
81
79
  errmsg("Warning: breakpoint source is not yet defined")
82
80
  str
@@ -87,9 +85,7 @@ module Byebug
87
85
 
88
86
  fullpath = File.realpath(file)
89
87
 
90
- if line > n_lines(file)
91
- raise(pr("break.errors.far_line", lines: n_lines(file), file: fullpath))
92
- end
88
+ raise(pr("break.errors.far_line", lines: n_lines(file), file: fullpath)) if line > n_lines(file)
93
89
 
94
90
  unless Breakpoint.potential_line?(fullpath, line)
95
91
  msg = pr(
@@ -48,18 +48,14 @@ module Byebug
48
48
  private
49
49
 
50
50
  def remove(exception)
51
- unless Byebug.catchpoints.member?(exception)
52
- return errmsg pr("catch.errors.not_found", exception: exception)
53
- end
51
+ return errmsg pr("catch.errors.not_found", exception: exception) unless Byebug.catchpoints.member?(exception)
54
52
 
55
53
  puts pr("catch.removed", exception: exception)
56
54
  Byebug.catchpoints.delete(exception)
57
55
  end
58
56
 
59
57
  def add(exception)
60
- if warning_eval(exception.is_a?(Class).to_s)
61
- errmsg pr("catch.errors.not_class", class: exception)
62
- end
58
+ errmsg pr("catch.errors.not_class", class: exception) if warning_eval(exception.is_a?(Class).to_s)
63
59
 
64
60
  puts pr("catch.added", exception: exception)
65
61
  Byebug.add_catchpoint(exception)
@@ -47,9 +47,7 @@ module Byebug
47
47
  breakpoint = breakpoints.find { |b| b.id == pos }
48
48
  return errmsg(pr("break.errors.no_breakpoint")) unless breakpoint
49
49
 
50
- unless syntax_valid?(@match[2])
51
- return errmsg(pr("break.errors.not_changed", expr: @match[2]))
52
- end
50
+ return errmsg(pr("break.errors.not_changed", expr: @match[2])) unless syntax_valid?(@match[2])
53
51
 
54
52
  breakpoint.expr = @match[2]
55
53
  end
@@ -14,7 +14,7 @@ module Byebug
14
14
  include Helpers::ParseHelper
15
15
 
16
16
  def self.regexp
17
- /^\s* c(?:ont(?:inue)?)? (?:\s+(\S+))? \s*$/x
17
+ /^\s* c(?:ont(?:inue)?)? (?:(!|\s+unconditionally|\s+\S+))? \s*$/x
18
18
  end
19
19
 
20
20
  def self.description
@@ -22,6 +22,11 @@ module Byebug
22
22
  c[ont[inue]][ <line_number>]
23
23
 
24
24
  #{short_description}
25
+
26
+ Normally the program stops at the next breakpoint. However, if the
27
+ parameter "unconditionally" is given or the command is suffixed with
28
+ "!", the program will run until the end regardless of any enabled
29
+ breakpoints.
25
30
  DESCRIPTION
26
31
  end
27
32
 
@@ -30,21 +35,33 @@ module Byebug
30
35
  end
31
36
 
32
37
  def execute
33
- if @match[1]
34
- num, err = get_int(@match[1], "Continue", 0, nil)
38
+ if until_line?
39
+ num, err = get_int(modifier, "Continue", 0, nil)
35
40
  return errmsg(err) unless num
36
41
 
37
42
  filename = File.expand_path(frame.file)
38
- unless Breakpoint.potential_line?(filename, num)
39
- return errmsg(pr("continue.errors.unstopped_line", line: num))
40
- end
43
+ return errmsg(pr("continue.errors.unstopped_line", line: num)) unless Breakpoint.potential_line?(filename, num)
41
44
 
42
45
  Breakpoint.add(filename, num)
43
46
  end
44
47
 
45
48
  processor.proceed!
46
49
 
47
- Byebug.stop if Byebug.stoppable?
50
+ Byebug.stop if unconditionally? || Byebug.stoppable?
51
+ end
52
+
53
+ private
54
+
55
+ def until_line?
56
+ @match[1] && !["!", "unconditionally"].include?(modifier)
57
+ end
58
+
59
+ def unconditionally?
60
+ @match[1] && ["!", "unconditionally"].include?(modifier)
61
+ end
62
+
63
+ def modifier
64
+ @match[1].lstrip
48
65
  end
49
66
  end
50
67
  end