1# 2# irb/extend-command.rb - irb extend command 3# $Release Version: 0.9.6$ 4# $Revision: 38515 $ 5# by Keiju ISHITSUKA(keiju@ruby-lang.org) 6# 7# -- 8# 9# 10# 11module IRB # :nodoc: 12 # Installs the default irb extensions command bundle. 13 module ExtendCommandBundle 14 EXCB = ExtendCommandBundle # :nodoc: 15 16 # See #install_alias_method. 17 NO_OVERRIDE = 0 18 # See #install_alias_method. 19 OVERRIDE_PRIVATE_ONLY = 0x01 20 # See #install_alias_method. 21 OVERRIDE_ALL = 0x02 22 23 # Quits the current irb context 24 # 25 # +ret+ is the optional signal or message to send to Context#exit 26 # 27 # Same as <code>IRB.CurrentContext.exit</code>. 28 def irb_exit(ret = 0) 29 irb_context.exit(ret) 30 end 31 32 # Displays current configuration. 33 # 34 # Modifing the configuration is achieved by sending a message to IRB.conf. 35 def irb_context 36 IRB.CurrentContext 37 end 38 39 @ALIASES = [ 40 [:context, :irb_context, NO_OVERRIDE], 41 [:conf, :irb_context, NO_OVERRIDE], 42 [:irb_quit, :irb_exit, OVERRIDE_PRIVATE_ONLY], 43 [:exit, :irb_exit, OVERRIDE_PRIVATE_ONLY], 44 [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY], 45 ] 46 47 @EXTEND_COMMANDS = [ 48 [:irb_current_working_workspace, :CurrentWorkingWorkspace, "irb/cmd/chws", 49 [:irb_print_working_workspace, OVERRIDE_ALL], 50 [:irb_cwws, OVERRIDE_ALL], 51 [:irb_pwws, OVERRIDE_ALL], 52# [:irb_cww, OVERRIDE_ALL], 53# [:irb_pww, OVERRIDE_ALL], 54 [:cwws, NO_OVERRIDE], 55 [:pwws, NO_OVERRIDE], 56# [:cww, NO_OVERRIDE], 57# [:pww, NO_OVERRIDE], 58 [:irb_current_working_binding, OVERRIDE_ALL], 59 [:irb_print_working_binding, OVERRIDE_ALL], 60 [:irb_cwb, OVERRIDE_ALL], 61 [:irb_pwb, OVERRIDE_ALL], 62# [:cwb, NO_OVERRIDE], 63# [:pwb, NO_OVERRIDE] 64 ], 65 [:irb_change_workspace, :ChangeWorkspace, "irb/cmd/chws", 66 [:irb_chws, OVERRIDE_ALL], 67# [:irb_chw, OVERRIDE_ALL], 68 [:irb_cws, OVERRIDE_ALL], 69# [:irb_cw, OVERRIDE_ALL], 70 [:chws, NO_OVERRIDE], 71# [:chw, NO_OVERRIDE], 72 [:cws, NO_OVERRIDE], 73# [:cw, NO_OVERRIDE], 74 [:irb_change_binding, OVERRIDE_ALL], 75 [:irb_cb, OVERRIDE_ALL], 76 [:cb, NO_OVERRIDE]], 77 78 [:irb_workspaces, :Workspaces, "irb/cmd/pushws", 79 [:workspaces, NO_OVERRIDE], 80 [:irb_bindings, OVERRIDE_ALL], 81 [:bindings, NO_OVERRIDE]], 82 [:irb_push_workspace, :PushWorkspace, "irb/cmd/pushws", 83 [:irb_pushws, OVERRIDE_ALL], 84# [:irb_pushw, OVERRIDE_ALL], 85 [:pushws, NO_OVERRIDE], 86# [:pushw, NO_OVERRIDE], 87 [:irb_push_binding, OVERRIDE_ALL], 88 [:irb_pushb, OVERRIDE_ALL], 89 [:pushb, NO_OVERRIDE]], 90 [:irb_pop_workspace, :PopWorkspace, "irb/cmd/pushws", 91 [:irb_popws, OVERRIDE_ALL], 92# [:irb_popw, OVERRIDE_ALL], 93 [:popws, NO_OVERRIDE], 94# [:popw, NO_OVERRIDE], 95 [:irb_pop_binding, OVERRIDE_ALL], 96 [:irb_popb, OVERRIDE_ALL], 97 [:popb, NO_OVERRIDE]], 98 99 [:irb_load, :Load, "irb/cmd/load"], 100 [:irb_require, :Require, "irb/cmd/load"], 101 [:irb_source, :Source, "irb/cmd/load", 102 [:source, NO_OVERRIDE]], 103 104 [:irb, :IrbCommand, "irb/cmd/subirb"], 105 [:irb_jobs, :Jobs, "irb/cmd/subirb", 106 [:jobs, NO_OVERRIDE]], 107 [:irb_fg, :Foreground, "irb/cmd/subirb", 108 [:fg, NO_OVERRIDE]], 109 [:irb_kill, :Kill, "irb/cmd/subirb", 110 [:kill, OVERRIDE_PRIVATE_ONLY]], 111 112 [:irb_help, :Help, "irb/cmd/help", 113 [:help, NO_OVERRIDE]], 114 115 ] 116 117 # Installs the default irb commands: 118 # 119 # +irb_current_working_workspace+:: Context#main 120 # +irb_change_workspace+:: Context#change_workspace 121 # +irb_workspaces+:: Context#workspaces 122 # +irb_push_workspace+:: Context#push_workspace 123 # +irb_pop_workspace+:: Context#pop_workspace 124 # +irb_load+:: #irb_load 125 # +irb_require+:: #irb_require 126 # +irb_source+:: IrbLoader#source_file 127 # +irb+:: IRB.irb 128 # +irb_jobs+:: JobManager 129 # +irb_fg+:: JobManager#switch 130 # +irb_kill+:: JobManager#kill 131 # +irb_help+:: IRB@Command+line+options 132 def self.install_extend_commands 133 for args in @EXTEND_COMMANDS 134 def_extend_command(*args) 135 end 136 end 137 138 # Evaluate the given +cmd_name+ on the given +cmd_class+ Class. 139 # 140 # Will also define any given +aliases+ for the method. 141 # 142 # The optional +load_file+ parameter will be required within the method 143 # definition. 144 def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases) 145 case cmd_class 146 when Symbol 147 cmd_class = cmd_class.id2name 148 when String 149 when Class 150 cmd_class = cmd_class.name 151 end 152 153 if load_file 154 line = __LINE__; eval %[ 155 def #{cmd_name}(*opts, &b) 156 require "#{load_file}" 157 arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity 158 args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s } 159 args << "*opts" if arity < 0 160 args << "&block" 161 args = args.join(", ") 162 line = __LINE__; eval %[ 163 def #{cmd_name}(\#{args}) 164 ExtendCommand::#{cmd_class}.execute(irb_context, \#{args}) 165 end 166 ], nil, __FILE__, line 167 send :#{cmd_name}, *opts, &b 168 end 169 ], nil, __FILE__, line 170 else 171 line = __LINE__; eval %[ 172 def #{cmd_name}(*opts, &b) 173 ExtendCommand::#{cmd_class}.execute(irb_context, *opts, &b) 174 end 175 ], nil, __FILE__, line 176 end 177 178 for ali, flag in aliases 179 @ALIASES.push [ali, cmd_name, flag] 180 end 181 end 182 183 # Installs alias methods for the default irb commands, see 184 # ::install_extend_commands. 185 def install_alias_method(to, from, override = NO_OVERRIDE) 186 to = to.id2name unless to.kind_of?(String) 187 from = from.id2name unless from.kind_of?(String) 188 189 if override == OVERRIDE_ALL or 190 (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or 191 (override == NO_OVERRIDE) && !respond_to?(to, true) 192 target = self 193 (class << self; self; end).instance_eval{ 194 if target.respond_to?(to, true) && 195 !target.respond_to?(EXCB.irb_original_method_name(to), true) 196 alias_method(EXCB.irb_original_method_name(to), to) 197 end 198 alias_method to, from 199 } 200 else 201 print "irb: warn: can't alias #{to} from #{from}.\n" 202 end 203 end 204 205 def self.irb_original_method_name(method_name) # :nodoc: 206 "irb_" + method_name + "_org" 207 end 208 209 # Installs alias methods for the default irb commands on the given object 210 # using #install_alias_method. 211 def self.extend_object(obj) 212 unless (class << obj; ancestors; end).include?(EXCB) 213 super 214 for ali, com, flg in @ALIASES 215 obj.install_alias_method(ali, com, flg) 216 end 217 end 218 end 219 220 install_extend_commands 221 end 222 223 # Extends methods for the Context module 224 module ContextExtender 225 CE = ContextExtender # :nodoc: 226 227 @EXTEND_COMMANDS = [ 228 [:eval_history=, "irb/ext/history.rb"], 229 [:use_tracer=, "irb/ext/tracer.rb"], 230 [:math_mode=, "irb/ext/math-mode.rb"], 231 [:use_loader=, "irb/ext/use-loader.rb"], 232 [:save_history=, "irb/ext/save-history.rb"], 233 ] 234 235 # Installs the default context extensions as irb commands: 236 # 237 # Context#eval_history=:: +irb/ext/history.rb+ 238 # Context#use_tracer=:: +irb/ext/tracer.rb+ 239 # Context#math_mode=:: +irb/ext/math-mode.rb+ 240 # Context#use_loader=:: +irb/ext/use-loader.rb+ 241 # Context#save_history=:: +irb/ext/save-history.rb+ 242 def self.install_extend_commands 243 for args in @EXTEND_COMMANDS 244 def_extend_command(*args) 245 end 246 end 247 248 # Evaluate the given +command+ from the given +load_file+ on the Context 249 # module. 250 # 251 # Will also define any given +aliases+ for the method. 252 def self.def_extend_command(cmd_name, load_file, *aliases) 253 line = __LINE__; Context.module_eval %[ 254 def #{cmd_name}(*opts, &b) 255 Context.module_eval {remove_method(:#{cmd_name})} 256 require "#{load_file}" 257 send :#{cmd_name}, *opts, &b 258 end 259 for ali in aliases 260 alias_method ali, cmd_name 261 end 262 ], __FILE__, line 263 end 264 265 CE.install_extend_commands 266 end 267 268 # A convenience module for extending Ruby methods. 269 module MethodExtender 270 # Extends the given +base_method+ with a prefix call to the given 271 # +extend_method+. 272 def def_pre_proc(base_method, extend_method) 273 base_method = base_method.to_s 274 extend_method = extend_method.to_s 275 276 alias_name = new_alias_name(base_method) 277 module_eval %[ 278 alias_method alias_name, base_method 279 def #{base_method}(*opts) 280 send :#{extend_method}, *opts 281 send :#{alias_name}, *opts 282 end 283 ] 284 end 285 286 # Extends the given +base_method+ with a postfix call to the given 287 # +extend_method+. 288 def def_post_proc(base_method, extend_method) 289 base_method = base_method.to_s 290 extend_method = extend_method.to_s 291 292 alias_name = new_alias_name(base_method) 293 module_eval %[ 294 alias_method alias_name, base_method 295 def #{base_method}(*opts) 296 send :#{alias_name}, *opts 297 send :#{extend_method}, *opts 298 end 299 ] 300 end 301 302 # Returns a unique method name to use as an alias for the given +name+. 303 # 304 # Usually returns <code>#{prefix}#{name}#{postfix}<num></code>, example: 305 # 306 # new_alias_name('foo') #=> __alias_of__foo__ 307 # def bar; end 308 # new_alias_name('bar') #=> __alias_of__bar__2 309 def new_alias_name(name, prefix = "__alias_of__", postfix = "__") 310 base_name = "#{prefix}#{name}#{postfix}" 311 all_methods = instance_methods(true) + private_instance_methods(true) 312 same_methods = all_methods.grep(/^#{Regexp.quote(base_name)}[0-9]*$/) 313 return base_name if same_methods.empty? 314 no = same_methods.size 315 while !same_methods.include?(alias_name = base_name + no) 316 no += 1 317 end 318 alias_name 319 end 320 end 321end 322 323