pry-hack 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/examples/method.rb +16 -0
- data/lib/pry/hack.rb +225 -0
- metadata +126 -0
data/README.md
ADDED
data/examples/method.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "pry/hack"
|
2
|
+
|
3
|
+
Pry.add_hack(:method, :instance_variable_peek,
|
4
|
+
Pry::Hackage::Hack.new(/^@([A-Za-z_][A-Za-z0-9_]*[A-za-z0-9_\?\!])$/) { replace_with "instance_variable_get(:@#{capture 1})" }
|
5
|
+
)
|
6
|
+
|
7
|
+
Pry.config.hack.enabled = true
|
8
|
+
|
9
|
+
#class Test
|
10
|
+
# def initialize
|
11
|
+
# @hello = :world
|
12
|
+
# end
|
13
|
+
#end
|
14
|
+
#
|
15
|
+
#object = Test.new
|
16
|
+
#puts object.@hello
|
data/lib/pry/hack.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
require "pry"
|
2
|
+
require "rubylexer"
|
3
|
+
|
4
|
+
class Pry
|
5
|
+
|
6
|
+
# Configuration block for the plugin
|
7
|
+
# - _s_ A cute way to joke at `hacks', also holds the structures
|
8
|
+
# that contain the {Pry::Hackage::Hack} objects.
|
9
|
+
# - _implications_ Aliases for use in {Pry::Hackage::Hack#initialize}.
|
10
|
+
# - _enabled_ Whether or not the hacks are being used.
|
11
|
+
self.config.hack =
|
12
|
+
OpenStruct.new(
|
13
|
+
:s => OpenStruct.new(
|
14
|
+
:meth => {},
|
15
|
+
:symbol_prefix => {},
|
16
|
+
:modop => {}
|
17
|
+
#:operator => {},
|
18
|
+
#:unary => {},
|
19
|
+
#:identifier => {},
|
20
|
+
#:magic_variable => {},
|
21
|
+
#:unicode => {},
|
22
|
+
),
|
23
|
+
:implications => OpenStruct.new(
|
24
|
+
:method => :meth,
|
25
|
+
:symbol => :symbol_prefix,
|
26
|
+
:% => :modop
|
27
|
+
),
|
28
|
+
:enabled => false
|
29
|
+
)
|
30
|
+
|
31
|
+
# Adds a hack to pry for use in the REPL.
|
32
|
+
#
|
33
|
+
# @param [Symbol] type The type of hack which is being added, this decides the placement
|
34
|
+
# of the object in Pry.config.hack.s
|
35
|
+
#
|
36
|
+
# @param [Object] handle A name to give to the hack, this is needed to remove the hack from
|
37
|
+
# the environment as well.
|
38
|
+
#
|
39
|
+
# @param [Pry::Hackage::Hack] hack The hack that will be added to Pry. See {Pry::Hackage::Hack}
|
40
|
+
#
|
41
|
+
# @return [Pry::Hackage::Hack] The hack that was passed as the last argument of the method.
|
42
|
+
def self.add_hack(type, handle, hack)
|
43
|
+
(eval "self.config.hack.s.#{self.config.hack.implications.send(:method_missing, type)||type}")[handle] = hack
|
44
|
+
return hack
|
45
|
+
end
|
46
|
+
|
47
|
+
# Removes a method from use in the REPL, added by {Pry#add_hack}
|
48
|
+
#
|
49
|
+
# @param [Object] type The name that was assigned to the hack at the time it was added.
|
50
|
+
#
|
51
|
+
# @return [Fixnum] handle The number of hacks with the name given that were removed from
|
52
|
+
# the environment.
|
53
|
+
def self.remove_hack(type, handle)
|
54
|
+
si = self.config.hack.collect(&:size).reduce(0,:+)
|
55
|
+
self.config.hack.each {|hash| hash.delete(handle)}
|
56
|
+
return si - self.config.hack.collect(&:size).reduce(0,:+)
|
57
|
+
end
|
58
|
+
|
59
|
+
# The main module containing the hack code
|
60
|
+
module Hackage
|
61
|
+
|
62
|
+
VERSION = 0.1
|
63
|
+
|
64
|
+
# Regular expressions kept constant for optimization and intended for use to match
|
65
|
+
# patterns that may be used by a hack in the future.
|
66
|
+
REGEX_REGEX = [%r/\A\/(.+?)\/(.+?)*\Z/, %r/%r(.)([^\1\\]|\\.)*\1/]
|
67
|
+
SYMBOL_REGEX = %r/\A\:(.+?)\Z/
|
68
|
+
|
69
|
+
KEYWORDS = %w[BEGIN END alias and begin break case class def defined? do else elsif end ensure for
|
70
|
+
if in module next not or redo rescue retry return super then when while yield]
|
71
|
+
# The base class from which all Hacks extend
|
72
|
+
class Hack
|
73
|
+
|
74
|
+
# Object initialization.
|
75
|
+
#
|
76
|
+
# @param [Regexp] regex The regular expression that matches the hack
|
77
|
+
#
|
78
|
+
# @param [Proc] block The block passed to the method detailing the hack's action.
|
79
|
+
def initialize(regex, &block)
|
80
|
+
@PATTERN = regex
|
81
|
+
@CODE = block
|
82
|
+
define_singleton_method(:capture) do |x|
|
83
|
+
[][x]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Lexical score of a string in relation to the regular expression of the hack.
|
88
|
+
#
|
89
|
+
# @param [String] text The text to be tested for a numerical matching score.
|
90
|
+
#
|
91
|
+
# @return [Fixnum] The score given, or characters matched by the regular expression.
|
92
|
+
def score(text)
|
93
|
+
md = @PATTERN.match(text)
|
94
|
+
return if md.nil?
|
95
|
+
cap = md.captures
|
96
|
+
define_singleton_method(:capture) do |x|
|
97
|
+
cap[x-1]
|
98
|
+
end
|
99
|
+
return cap.join.length
|
100
|
+
end
|
101
|
+
|
102
|
+
def run(where)
|
103
|
+
instance_exec(nil, where, &@CODE)
|
104
|
+
end
|
105
|
+
|
106
|
+
# DSL function that replaces the entire string with the one provided.
|
107
|
+
def replace_with(text)
|
108
|
+
return text
|
109
|
+
end
|
110
|
+
|
111
|
+
# DSL function that acts like String#sub
|
112
|
+
#
|
113
|
+
# @param [String] text The text that will be subject to the sub.
|
114
|
+
#
|
115
|
+
# @param [Hash,String] hash If a hash, you must use the format :with => "text"
|
116
|
+
# and you may also supply :global? => true to apply
|
117
|
+
# a global substitution. See String#gsub
|
118
|
+
#
|
119
|
+
# @return [String] The string post operations.
|
120
|
+
def replace(text, hash)
|
121
|
+
if hash.is_a? Hash
|
122
|
+
if hash[:global?].nil? || hash[:global?]
|
123
|
+
return text.gsub(@PATTERN, hash[:with])
|
124
|
+
else
|
125
|
+
return text.sub(@PATTERN, hash[:with])
|
126
|
+
end
|
127
|
+
else
|
128
|
+
return text.gsub(@PATTERN, hash)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def ignore
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class ModOpHack < Hack
|
139
|
+
def initialize(char, &block)
|
140
|
+
@CHAR = char
|
141
|
+
@CODE = block
|
142
|
+
define_singleton_method(:capture) do |x|
|
143
|
+
[][x]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
PAIR_MATCHES = {'[' => ']', '{' => '}', '(' => ')', '<' => '>'}
|
148
|
+
def score(str)
|
149
|
+
str.strip!
|
150
|
+
char = Regexp.escape(str[1])
|
151
|
+
char_to_match = Regexp.escape(PAIR_MATCHES[str[1]]||str[1])
|
152
|
+
@PATTERN = /^#{@CHAR}(#{char})(.*)(#{char_to_match})$/
|
153
|
+
scr = super str
|
154
|
+
define_singleton_method(:delimiter) { Struct.new(:open,:close).new(capture(1), capture(3)) }
|
155
|
+
define_singleton_method(:content) { capture(2) }
|
156
|
+
return scr
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.hack_line(str)
|
161
|
+
# A dirty syntax hack used to enable and disable pry-hack when a hack goes
|
162
|
+
# wrong and you are unable to fix it because of a side effect. The syntax is as follows
|
163
|
+
# -*- pry-hack: disable -*-
|
164
|
+
# or to enable
|
165
|
+
# -*- pry-hack: enable -*-
|
166
|
+
if str =~ /#\s*-\*-\s*pry-hack:\s*(disable|enable)\s*-\*-\s*/
|
167
|
+
self.config.hack.enabled = ($1 == "enable") ? true : false
|
168
|
+
end
|
169
|
+
return str unless ::Pry.config.hack.enabled
|
170
|
+
stack = []
|
171
|
+
tstack = []
|
172
|
+
state = nil
|
173
|
+
|
174
|
+
lexer = RubyLexer.new("(pry)", str)
|
175
|
+
tstack = lexer.to_set.to_a[1..-2].select {|x| !(x.is_a? RubyLexer::FileAndLineToken) }
|
176
|
+
tstack.map!(&:to_s)
|
177
|
+
while c = tstack.shift
|
178
|
+
if state.nil?
|
179
|
+
state= case c
|
180
|
+
when '.'
|
181
|
+
# self.config.hack.s.meth
|
182
|
+
:meth
|
183
|
+
when '%'
|
184
|
+
# self.config.hack.s.modop
|
185
|
+
:modop
|
186
|
+
when Hackage::SYMBOL_REGEX
|
187
|
+
# self.config.hack.s.symbol_prefix
|
188
|
+
:symbol_prefix
|
189
|
+
when *Hackage::REGEX_REGEX
|
190
|
+
# self.config.hack.s.regex
|
191
|
+
:regex
|
192
|
+
end
|
193
|
+
stack.push c
|
194
|
+
next
|
195
|
+
else
|
196
|
+
if state == :modop && tstack[0] =~ /[^A-Za-z0-9#]/
|
197
|
+
char = Regexp.escape(tstack[0])
|
198
|
+
char_to_match = Regexp.escape(ModOpHack::PAIR_MATCHES[tstack[0]] || char)
|
199
|
+
c += (tstack.shift + " ") until c =~ /^.#{char}.*#{char_to_match}/
|
200
|
+
end
|
201
|
+
hacks = (self.config.hack.s.send(state).values.sort_by {|x| x.score(c) })
|
202
|
+
if hacks.nil?
|
203
|
+
stack.push c
|
204
|
+
next
|
205
|
+
end
|
206
|
+
hacks.compact!
|
207
|
+
hacks.reject! {|x| x.score(c).nil? }
|
208
|
+
if hacks.any? && stack.last == '%' then stack.pop end # Do this so that you can make sure not to have a double modulo occurance
|
209
|
+
c = (hacks.any?) ? hacks.last.run(binding) : c
|
210
|
+
state = nil
|
211
|
+
stack.push c
|
212
|
+
end
|
213
|
+
end
|
214
|
+
return stack.join
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
alias_method :old_retrieve_line, :retrieve_line
|
219
|
+
|
220
|
+
def retrieve_line(eval_string, *args)
|
221
|
+
old_retrieve_line(eval_string, *args)
|
222
|
+
puts eval_string.sub!(/^.+?$/, Hackage.hack_line(eval_string))
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pry-hack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matthew Carey
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rubylexer
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pry
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! 'Using a lexical parser, this gem allows you to add hacks to your REPL
|
63
|
+
session allowing you to
|
64
|
+
|
65
|
+
have shortcut syntax. Things such as
|
66
|
+
|
67
|
+
[0] pry(main)> object.@ivar
|
68
|
+
|
69
|
+
=> :im_the_return_of_an_instance_variable
|
70
|
+
|
71
|
+
|
72
|
+
Or
|
73
|
+
|
74
|
+
|
75
|
+
[0] pry(main)> %S{hello symbol world}
|
76
|
+
|
77
|
+
=> [:hello, :symbol, :world]
|
78
|
+
|
79
|
+
|
80
|
+
And even the most desired ruby syntax of all is planned to come, that''s right.
|
81
|
+
Increment and decrement operators.
|
82
|
+
|
83
|
+
|
84
|
+
[0] pry(main)> i++
|
85
|
+
|
86
|
+
=> 1
|
87
|
+
|
88
|
+
[1] pry(main)> i--
|
89
|
+
|
90
|
+
=> 0
|
91
|
+
|
92
|
+
'
|
93
|
+
email: matthew.b.carey@gmail.com
|
94
|
+
executables: []
|
95
|
+
extensions: []
|
96
|
+
extra_rdoc_files: []
|
97
|
+
files:
|
98
|
+
- lib/pry/hack.rb
|
99
|
+
- examples/method.rb
|
100
|
+
- README.md
|
101
|
+
homepage: https://github.com/swarley/pry-hack
|
102
|
+
licenses: []
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 1.8.23
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Change the syntax of your pry session with minimal side effects
|
125
|
+
test_files: []
|
126
|
+
has_rdoc:
|