ori 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.html +158 -0
- data/README.md +160 -0
- data/Rakefile +60 -0
- data/VERSION.yml +5 -0
- data/lib/misc/method_aliases.rb +11 -0
- data/lib/ori.rb +47 -0
- data/lib/ori/auto_config.rb +78 -0
- data/lib/ori/colorize.rb +62 -0
- data/lib/ori/config.rb +27 -0
- data/lib/ori/extensions.rb +4 -0
- data/lib/ori/extensions/object/ri.rb +88 -0
- data/lib/ori/internals.rb +411 -0
- data/lib/ori/library.rb +59 -0
- data/lib/ori/list_method.rb +247 -0
- data/lib/ori/request.rb +118 -0
- data/lib/ori/tools.rb +142 -0
- data/ori.gemspec +82 -0
- data/samples/NOTES +3 -0
- data/samples/basic_extension.rb +27 -0
- data/samples/basic_inheritance.rb +99 -0
- data/samples/self_singletons.rb +36 -0
- data/samples/singleton_class_includes_module.rb +33 -0
- data/spec/auto_config_spec.rb +33 -0
- data/spec/colorize_spec.rb +26 -0
- data/spec/inspector_spec.rb +75 -0
- data/spec/internals_spec.rb +219 -0
- data/spec/list_method_spec.rb +118 -0
- data/spec/request_spec.rb +72 -0
- data/spec/site/NOTES +3 -0
- data/spec/site/library_spec.rb +33 -0
- data/spec/site/spec_helper.rb +1 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/tools_spec.rb +109 -0
- metadata +107 -0
data/lib/ori/library.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module ORI
|
4
|
+
# ri lookup library.
|
5
|
+
class Library #:nodoc:
|
6
|
+
# Mask of ri command to fetch content. Example:
|
7
|
+
#
|
8
|
+
# ri -T -f ansi %s
|
9
|
+
attr_accessor :frontend
|
10
|
+
|
11
|
+
# Shell escape mode. <tt>:unix</tt> or <tt>:windows</tt>.
|
12
|
+
attr_accessor :shell_escape
|
13
|
+
|
14
|
+
def initialize(attrs = {})
|
15
|
+
@cache = {}
|
16
|
+
attrs.each {|k, v| send("#{k}=", v)}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Lookup an article.
|
20
|
+
#
|
21
|
+
# lookup("Kernel#puts") # => content or nil.
|
22
|
+
def lookup(topic)
|
23
|
+
if @cache.has_key? topic
|
24
|
+
@cache[topic]
|
25
|
+
else
|
26
|
+
require_frontend
|
27
|
+
|
28
|
+
etopic = case @shell_escape
|
29
|
+
when :unix
|
30
|
+
Tools.shell_escape(topic)
|
31
|
+
when :windows
|
32
|
+
Tools.win_shell_escape(topic)
|
33
|
+
else
|
34
|
+
topic
|
35
|
+
end
|
36
|
+
|
37
|
+
cmd = @frontend % etopic
|
38
|
+
##p "cmd", cmd
|
39
|
+
content = `#{cmd} 2>&1`
|
40
|
+
##p "content", content
|
41
|
+
|
42
|
+
# NOTES:
|
43
|
+
# * Windows' ri always returns 0 even if article is not found. Work around it with a hack.
|
44
|
+
# * Unix's ri sometimes returns 0 when it offers suggestions. Try `ri Object#is_ax?`.
|
45
|
+
@cache[topic] = if $?.exitstatus != 0 or content.lines.count < 4
|
46
|
+
nil
|
47
|
+
else
|
48
|
+
content
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def require_frontend
|
56
|
+
raise "`frontend` is not set" if not @frontend
|
57
|
+
end
|
58
|
+
end # Library
|
59
|
+
end # ORI
|
@@ -0,0 +1,247 @@
|
|
1
|
+
module ORI
|
2
|
+
# Our method representation suitable for listing.
|
3
|
+
class ListMethod #:nodoc:
|
4
|
+
OWN_MARKER = ["~", " "]
|
5
|
+
|
6
|
+
# Object. Can be anything, including <em>nil</em>.
|
7
|
+
attr_reader :obj
|
8
|
+
|
9
|
+
attr_reader :method_name
|
10
|
+
attr_reader :inspector
|
11
|
+
|
12
|
+
def initialize(attrs = {})
|
13
|
+
attrs.each {|k, v| send("#{k}=", v)}
|
14
|
+
clear_cache
|
15
|
+
end
|
16
|
+
|
17
|
+
#--------------------------------------- Accessors and pseudo accessors
|
18
|
+
|
19
|
+
# Return method access substring: "::" or "#".
|
20
|
+
def access
|
21
|
+
# NOTE: It is *WRONG* to rely on Ruby's `inspect` to handle things because
|
22
|
+
# it doesn't work for cases when singleton methods are included from modules.
|
23
|
+
@cache[:access] ||= (module? and not inspector.match /instance/) ? "::" : "#"
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspector=(s)
|
27
|
+
@inspector = s.to_s
|
28
|
+
clear_cache
|
29
|
+
end
|
30
|
+
|
31
|
+
def instance?
|
32
|
+
access == "#"
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_name=(s)
|
36
|
+
@method_name = s.to_s
|
37
|
+
clear_cache
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fetch method object.
|
41
|
+
def method_object
|
42
|
+
require_valid
|
43
|
+
|
44
|
+
@cache[:method_object] ||= if @inspector.match /instance/
|
45
|
+
@obj._ori_instance_method(@method_name)
|
46
|
+
else
|
47
|
+
@obj._ori_method(@method_name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def module?
|
52
|
+
@cache[:is_module] ||= begin
|
53
|
+
require_obj
|
54
|
+
@obj.is_a? Module
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def obj=(obj)
|
59
|
+
@obj = obj
|
60
|
+
@obj_present = true
|
61
|
+
clear_cache
|
62
|
+
end
|
63
|
+
|
64
|
+
def obj_module
|
65
|
+
@cache[:obj_module] ||= obj.is_a?(Module) ? obj : obj.class
|
66
|
+
end
|
67
|
+
|
68
|
+
def obj_module_name
|
69
|
+
@cache[:obj_module_name] ||= Tools.get_module_name(obj_module)
|
70
|
+
end
|
71
|
+
|
72
|
+
def owner
|
73
|
+
@cache[:owner] ||= method_object.owner
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get, if possible, <tt>obj</tt> singleton class.
|
77
|
+
# Some objects, e.g. <tt>Fixnum</tt> instances, don't have a singleton class.
|
78
|
+
def obj_singleton_class
|
79
|
+
@cache[:obj_singleton] ||= begin
|
80
|
+
class << obj #:nodoc:
|
81
|
+
self
|
82
|
+
end
|
83
|
+
rescue
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return <tt>true</tt> if method is natively owned by <tt>obj</tt> class.
|
89
|
+
def own?
|
90
|
+
@cache[:is_own] ||= begin
|
91
|
+
require_valid
|
92
|
+
owner == obj_module || owner == obj_singleton_class
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def owner_name
|
97
|
+
@cache[:owner_name] ||= Tools.get_module_name(owner)
|
98
|
+
end
|
99
|
+
|
100
|
+
def private?
|
101
|
+
visibility == :private
|
102
|
+
end
|
103
|
+
|
104
|
+
def protected?
|
105
|
+
visibility == :protected
|
106
|
+
end
|
107
|
+
|
108
|
+
def public?
|
109
|
+
visibility == :public
|
110
|
+
end
|
111
|
+
|
112
|
+
def singleton?
|
113
|
+
access == "::"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return visibility: <tt>:public</tt>, <tt>:protected</tt>, <tt>:private</tt>.
|
117
|
+
def visibility
|
118
|
+
@cache[:visibility] ||= begin
|
119
|
+
require_valid
|
120
|
+
|
121
|
+
if @inspector.match /private/
|
122
|
+
:private
|
123
|
+
elsif @inspector.match /protected/
|
124
|
+
:protected
|
125
|
+
else
|
126
|
+
:public
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#---------------------------------------
|
132
|
+
|
133
|
+
# Format self into a string.
|
134
|
+
# Options:
|
135
|
+
#
|
136
|
+
# :color => true|false
|
137
|
+
def format(options = {})
|
138
|
+
options = options.dup
|
139
|
+
o = {}
|
140
|
+
o[k = :color] = (v = options.delete(k)).nil?? true : v
|
141
|
+
raise ArgumentError, "Unknown option(s): #{options.inspect}" if not options.empty?
|
142
|
+
|
143
|
+
require_valid
|
144
|
+
|
145
|
+
Colorize.colorize *[
|
146
|
+
(own?? [[:list_method, :own_marker], OWN_MARKER[0]] : [[:list_method, :not_own_marker], OWN_MARKER[1]]),
|
147
|
+
[[:list_method, :obj_module_name], obj_module_name],
|
148
|
+
([[:list_method, :owner_name], "(#{owner_name})"] if not own?),
|
149
|
+
[[:list_method, :access], access],
|
150
|
+
[[:list_method, :name], method_name],
|
151
|
+
([[:list_method, :visibility], " [#{visibility}]"] if not public?),
|
152
|
+
[[:reset]],
|
153
|
+
].compact.flatten(1).reject {|v| v.is_a? Array and not o[:color]}
|
154
|
+
end
|
155
|
+
|
156
|
+
# Match entire formatted record against RE.
|
157
|
+
def fullmatch(re)
|
158
|
+
format(:color => false).match(re)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Match method name against RE.
|
162
|
+
def match(re)
|
163
|
+
@method_name.match(re)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Quick format. No options, no hashes, no checks.
|
167
|
+
def qformat
|
168
|
+
#"#{owner_name}#{access}#{@method_name} [#{visibility}]" # Before multi-obj support.
|
169
|
+
"#{obj_module_name}#{access}#{@method_name} [#{visibility}]"
|
170
|
+
end
|
171
|
+
|
172
|
+
def ri_topics
|
173
|
+
@cache[:ri_topics] ||= begin
|
174
|
+
require_valid
|
175
|
+
|
176
|
+
# Build "hierarchy methods". Single record is:
|
177
|
+
#
|
178
|
+
# ["Kernel", "#", "dup"]
|
179
|
+
hmethods = []
|
180
|
+
|
181
|
+
# Always stuff self in front of the line regardless of if we have method or not.
|
182
|
+
hmethods << [obj_module_name, access, method_name]
|
183
|
+
|
184
|
+
ancestors = []
|
185
|
+
ancestors += obj_module.ancestors
|
186
|
+
ancestors += obj_singleton_class.ancestors if obj_singleton_class # E.g. when module extends class.
|
187
|
+
|
188
|
+
ancestors.each do |mod|
|
189
|
+
mav = Tools.get_methods(mod, :inspector_arg => false, :to_mav => true)
|
190
|
+
##p "mav", mav
|
191
|
+
found = mav.select {|method_name,| method_name == self.method_name}
|
192
|
+
##p "found", found
|
193
|
+
found.each do |method_name, access|
|
194
|
+
hmethods << [Tools.get_module_name(mod), access, method_name]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Misdoc hack -- stuff Object#meth lookup if Kernel#meth is present. For methods like Kernel#is_a?.
|
199
|
+
if (found = hmethods.find {|mod, access| [mod, access] == ["Kernel", "#"]}) and not hmethods.find {|mod,| mod == "Object"}
|
200
|
+
hmethods << ["Object", "#", found.last]
|
201
|
+
end
|
202
|
+
|
203
|
+
hmethods.uniq
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def valid?
|
208
|
+
[
|
209
|
+
@obj_present,
|
210
|
+
@method_name,
|
211
|
+
@inspector,
|
212
|
+
].all?
|
213
|
+
end
|
214
|
+
|
215
|
+
#---------------------------------------
|
216
|
+
|
217
|
+
# Support <tt>Enumerable#sort</tt>.
|
218
|
+
def <=>(other)
|
219
|
+
[@method_name, access, obj_module_name] <=> [other.method_name, other.access, obj_module_name]
|
220
|
+
end
|
221
|
+
|
222
|
+
# Support <tt>Array#uniq</tt>.
|
223
|
+
def hash
|
224
|
+
@cache[:hash] ||= qformat.hash
|
225
|
+
end
|
226
|
+
|
227
|
+
# Support <tt>Array#uniq</tt>.
|
228
|
+
def eql?(other)
|
229
|
+
hash == other.hash
|
230
|
+
end
|
231
|
+
|
232
|
+
#---------------------------------------
|
233
|
+
private
|
234
|
+
|
235
|
+
def clear_cache
|
236
|
+
@cache = {}
|
237
|
+
end
|
238
|
+
|
239
|
+
def require_obj
|
240
|
+
raise "`obj` is not set" if not @obj_present
|
241
|
+
end
|
242
|
+
|
243
|
+
def require_valid
|
244
|
+
raise "Object is not valid" if not valid?
|
245
|
+
end
|
246
|
+
end # ListMethod
|
247
|
+
end
|
data/lib/ori/request.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module ORI
|
2
|
+
# <tt>something.ri [something]</tt> request logic.
|
3
|
+
#
|
4
|
+
# NOTE: This class DOES NOT validate particular options to be passed to <tt>get_list_methods</tt>.
|
5
|
+
class Request #:nodoc:
|
6
|
+
class ParseError < Exception #:nodoc:
|
7
|
+
end
|
8
|
+
|
9
|
+
# Options for <tt>Internals::get_list_methods</tt>.
|
10
|
+
attr_accessor :glm_options
|
11
|
+
|
12
|
+
# <tt>:self</tt>, <tt>:list</tt>, <tt>:method</tt> or <tt>:error</tt>.
|
13
|
+
attr_accessor :kind
|
14
|
+
|
15
|
+
# Message. E.g. for <tt>:error</tt> kind this is the message for the user.
|
16
|
+
attr_accessor :message
|
17
|
+
|
18
|
+
def initialize(attrs = {})
|
19
|
+
@glm_options = {}
|
20
|
+
attrs.each {|k, v| send("#{k}=", v)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def error?
|
24
|
+
@kind == :error
|
25
|
+
end
|
26
|
+
|
27
|
+
def list?
|
28
|
+
@kind == :list
|
29
|
+
end
|
30
|
+
|
31
|
+
def method?
|
32
|
+
@kind == :method
|
33
|
+
end
|
34
|
+
|
35
|
+
def self?
|
36
|
+
@kind == :self
|
37
|
+
end
|
38
|
+
|
39
|
+
#---------------------------------------
|
40
|
+
|
41
|
+
# Parse arguments into a new <tt>Request</tt> object.
|
42
|
+
#
|
43
|
+
# parse()
|
44
|
+
# parse(//)
|
45
|
+
# parse(//, :all)
|
46
|
+
# parse(//, :all => true, :access => "#")
|
47
|
+
# parse(:puts)
|
48
|
+
# parse("#puts")
|
49
|
+
# parse("::puts")
|
50
|
+
def self.parse(*args)
|
51
|
+
r = new(:glm_options => {:objs => []})
|
52
|
+
|
53
|
+
begin
|
54
|
+
if args.size < 1
|
55
|
+
# Fixnum.ri
|
56
|
+
# 5.ri
|
57
|
+
r.kind = :self
|
58
|
+
else
|
59
|
+
# At least 1 argument is present.
|
60
|
+
arg1 = args.shift
|
61
|
+
|
62
|
+
case arg1
|
63
|
+
when Symbol, String
|
64
|
+
# ri :meth
|
65
|
+
# ri "#meth"
|
66
|
+
# ri "::meth"
|
67
|
+
r.kind = :method
|
68
|
+
if args.size > 0
|
69
|
+
raise ParseError, "Unexpected arguments after #{arg1.inspect}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# This is important -- look through all available methods.
|
73
|
+
r.glm_options[:all] = true
|
74
|
+
|
75
|
+
method_name = if arg1.to_s.match /\A(::|#)(.+)\z/
|
76
|
+
r.glm_options[:access] = $1
|
77
|
+
$2
|
78
|
+
else
|
79
|
+
arg1.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
r.glm_options[:re] = /\A#{Regexp.escape(method_name)}\z/
|
83
|
+
|
84
|
+
when Regexp
|
85
|
+
# ri //
|
86
|
+
# ri //, :all
|
87
|
+
# ri /kk/, :option => value etc.
|
88
|
+
r.kind = :list
|
89
|
+
r.glm_options[:re] = arg1
|
90
|
+
args.each do |arg|
|
91
|
+
if arg.is_a? Hash
|
92
|
+
if arg.has_key?(k = :join)
|
93
|
+
r.glm_options[:objs] += [arg.delete(k)].flatten(1)
|
94
|
+
end
|
95
|
+
|
96
|
+
r.glm_options.merge! arg
|
97
|
+
elsif [String, Symbol].include? arg.class
|
98
|
+
r.glm_options.merge! arg.to_sym => true
|
99
|
+
else
|
100
|
+
raise ParseError, "Unsupported argument #{arg.inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Don't bother making `objs` unique, we're just the request parser.
|
105
|
+
|
106
|
+
else
|
107
|
+
raise ParseError, "Unsupported argument #{arg1.inspect}"
|
108
|
+
end # case arg1
|
109
|
+
end # if args.size < 1
|
110
|
+
rescue ParseError => e
|
111
|
+
r.kind = :error
|
112
|
+
r.message = e.message
|
113
|
+
end
|
114
|
+
|
115
|
+
r
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/ori/tools.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
module ORI
|
2
|
+
# Generic tools.
|
3
|
+
module Tools #:nodoc:
|
4
|
+
ANSI_ATTRS = {
|
5
|
+
:reset => 0,
|
6
|
+
:bold => 1,
|
7
|
+
:underscore => 4,
|
8
|
+
:underline => 4,
|
9
|
+
:blink => 5,
|
10
|
+
:reverse => 7,
|
11
|
+
:concealed => 8,
|
12
|
+
:black => 30,
|
13
|
+
:red => 31,
|
14
|
+
:green => 32,
|
15
|
+
:yellow => 33,
|
16
|
+
:blue => 34,
|
17
|
+
:magenta => 35,
|
18
|
+
:cyan => 36,
|
19
|
+
:white => 37,
|
20
|
+
:on_black => 40,
|
21
|
+
:on_red => 41,
|
22
|
+
:on_green => 42,
|
23
|
+
:on_yellow => 43,
|
24
|
+
:on_blue => 44,
|
25
|
+
:on_magenta => 45,
|
26
|
+
:on_cyan => 46,
|
27
|
+
:on_white => 47,
|
28
|
+
}
|
29
|
+
|
30
|
+
# Default inspectors for <tt>get_methods</tt>.
|
31
|
+
GET_METHODS_INSPECTORS = [
|
32
|
+
:private_instance_methods,
|
33
|
+
:protected_instance_methods,
|
34
|
+
:public_instance_methods,
|
35
|
+
:private_methods,
|
36
|
+
:protected_methods,
|
37
|
+
:public_methods,
|
38
|
+
]
|
39
|
+
|
40
|
+
# Build an ANSI sequence.
|
41
|
+
#
|
42
|
+
# ansi() # => ""
|
43
|
+
# ansi(:red) # => "\e[31m"
|
44
|
+
# ansi(:red, :bold) # => "\e[31;1m"
|
45
|
+
# puts "Hello, #{ansi(:bold)}user#{ansi(:reset)}"
|
46
|
+
def self.ansi(*attrs)
|
47
|
+
codes = attrs.map {|attr| ANSI_ATTRS[attr.to_sym] or raise ArgumentError, "Unknown attribute #{attr.inspect}"}
|
48
|
+
codes.empty?? "" : "\e[#{codes.join(';')}m"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Inspect an object with various inspectors.
|
52
|
+
# Options:
|
53
|
+
#
|
54
|
+
# :inspectors => [] # Array of inspectors, e.g. [:public_instance_methods].
|
55
|
+
# :inspector_arg => T|F # Arg to pass to inspector. Default is <tt>true</tt>
|
56
|
+
# :to_mav => T|F # Post-transform list into [method_name, access, visibility] ("MAV"). Default is <tt>false</tt>.
|
57
|
+
#
|
58
|
+
# Examples:
|
59
|
+
#
|
60
|
+
# get_methods(obj)
|
61
|
+
# # => [[inspector, [methods]], [inspector, [methods]], ...]
|
62
|
+
# get_methods(obj, :to_mav => true)
|
63
|
+
# # => [[method_name, access, visibility], [method_name, access, visibility], ...]
|
64
|
+
def self.get_methods(obj, options = {})
|
65
|
+
options = options.dup
|
66
|
+
o = {}
|
67
|
+
o[k = :inspectors] = (v = options.delete(k)).nil?? GET_METHODS_INSPECTORS : v
|
68
|
+
o[k = :inspector_arg] = (v = options.delete(k)).nil?? true : v
|
69
|
+
o[k = :to_mav] = (v = options.delete(k)).nil?? false : v
|
70
|
+
raise ArgumentError, "Unknown option(s): #{options.inspect}" if not options.empty?
|
71
|
+
|
72
|
+
out = []
|
73
|
+
|
74
|
+
o[:inspectors].each do |inspector|
|
75
|
+
next if not obj.respond_to? inspector
|
76
|
+
out << [inspector.to_s, obj.send(inspector, o[:inspector_arg]).sort.map(&:to_s)]
|
77
|
+
end
|
78
|
+
|
79
|
+
if o[:to_mav]
|
80
|
+
mav = []
|
81
|
+
|
82
|
+
is_module = obj.is_a? Module
|
83
|
+
|
84
|
+
out.each do |inspector, methods|
|
85
|
+
##puts "-- inspector-#{inspector.inspect}"
|
86
|
+
access = (is_module and not inspector.match /instance/) ? "::" : "#"
|
87
|
+
|
88
|
+
visibility = if inspector.match /private/
|
89
|
+
:private
|
90
|
+
elsif inspector.match /protected/
|
91
|
+
:protected
|
92
|
+
else
|
93
|
+
:public
|
94
|
+
end
|
95
|
+
|
96
|
+
methods.each do |method_name|
|
97
|
+
mav << [method_name, access, visibility]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
out = mav.uniq # NOTE: Dupes are possible, e.g. when custom inspectors are given.
|
102
|
+
end
|
103
|
+
|
104
|
+
out
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return name of a module, even a "nameless" one.
|
108
|
+
def self.get_module_name(mod)
|
109
|
+
if mod.name.to_s.empty?
|
110
|
+
if mat = mod.inspect.match(/#<Class:.*?\b(.+?)(?:>|:[0#])/)
|
111
|
+
mat[1]
|
112
|
+
end
|
113
|
+
else
|
114
|
+
mod.name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Escape string for use in Unix shell command.
|
119
|
+
# Credits http://stackoverflow.com/questions/1306680/shellwords-shellescape-implementation-for-ruby-1-8.
|
120
|
+
def self.shell_escape(s)
|
121
|
+
# An empty argument will be skipped, so return empty quotes.
|
122
|
+
return "''" if s.empty?
|
123
|
+
|
124
|
+
s = s.dup
|
125
|
+
|
126
|
+
# Process as a single byte sequence because not all shell
|
127
|
+
# implementations are multibyte aware.
|
128
|
+
s.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
|
129
|
+
|
130
|
+
# A LF cannot be escaped with a backslash because a backslash + LF
|
131
|
+
# combo is regarded as line continuation and simply ignored.
|
132
|
+
s.gsub!(/\n/, "'\n'")
|
133
|
+
|
134
|
+
s
|
135
|
+
end
|
136
|
+
|
137
|
+
# Escape string for use in Windows command. Word "shell" is used for similarity.
|
138
|
+
def self.win_shell_escape(s)
|
139
|
+
s
|
140
|
+
end
|
141
|
+
end # Tools
|
142
|
+
end # ORI
|