TODO:
multi-line statements in irb w/o ruby2ruby? (=> extract_source)
polishing!
# File lib/added_methods.rb, line 113 def all_methods init_all_methods ALL_METHODS end
# File lib/added_methods.rb, line 58 def callback(*args, &inner_block) callback_args = [identify_added_method(*args << caller), caller, inner_block] callbacks.each { |name, callback| callback[*callback_args] } end
# File lib/added_methods.rb, line 53 def callbacks init_callbacks CALLBACKS end
# File lib/added_methods.rb, line 63 def define_callback(name, regexp = //, klasses = [], &outer_block) raise TypeError, "wrong argument type #{name.class} (expected Symbol)" unless name.is_a?(Symbol) raise "callback with name #{name} already exists" if callbacks.assoc(name) raise TypeError, "wrong argument type #{regexp.class} (expected Regexp)" unless regexp.is_a?(Regexp) raise TypeError, "wrong argument type #{klasses.class} (expected container object)" unless klasses.respond_to?(:empty?) && klasses.respond_to?(:include?) callbacks << [name, lambda { |am, callstack, inner_block| method, klass = am.name, am.klass return if %w[method_added singleton_method_added].include?(method) return unless klasses.empty? || klasses.include?(klass.to_s) return unless method =~ regexp if outer_block || inner_block outer_block[am] if outer_block inner_block[am] if inner_block else msg = "[#{am.base}] Adding #{'singleton ' if am.singleton?}method #{klass}##{method}" msg << if irb?(callstack) " in (irb:#{IRB.conf[:MAIN_CONTEXT].instance_variable_get(:@line_no)})" else " at #{where(callstack)}" end puts msg end }] end
# File lib/added_methods.rb, line 118 def find(conditions = {}) conditions = conditions.dup class_condition = conditions.delete(:class) file_condition = conditions.delete(:file) results = [] all_methods.each { |klass, files| if class_condition next unless class_condition.is_a?(Array) ? class_condition.include?(klass) : klass == class_condition end files.each { |file, entries| if file_condition next unless file_condition.is_a?(Regexp) ? file =~ file_condition : file == file_condition end entries.each { |am| results << am if conditions.all? { |key, value| case value when Array, Range then value.include?(am[key]) when Regexp then value =~ am[key].to_s else value == am[key] end } } } } results end
# File lib/added_methods.rb, line 151 def find_by_class(*classes) conditions = classes.last.is_a?(Hash) ? classes.pop : {} find(conditions.merge(:class => classes)) end
# File lib/added_methods.rb, line 156 def find_by_name(*names) conditions = names.last.is_a?(Hash) ? names.pop : {} find(conditions.merge(:name => names.map { |m| m.to_s })) end
# File lib/added_methods.rb, line 161 def find_one_by_name_or_class(name_or_class, conditions = {}) (name_or_class.is_a?(Class) ? find_by_class(name_or_class) : find_by_name(name_or_class) ).last end
# File lib/added_methods.rb, line 45 def init(regexp = nil, klasses = [], &block) init_script_lines patch_readline_history define_callback(:__init, regexp, klasses, &block) if regexp install_callbacks end
# File lib/added_methods.rb, line 104 def install_callbacks(bases = [Object, Class, Module, Kernel]) bases.each { |base| [base, singleton_class(base)].each { |b| b.send(:define_method, :method_added) { |id| AddedMethods.callback(b, self, id, false) } b.send(:define_method, :singleton_method_added) { |id| AddedMethods.callback(b, self, id, true) } } } end
# File lib/added_methods.rb, line 95 def remove_callback(name) callbacks.delete_if { |n, _| n == name } end
# File lib/added_methods.rb, line 99 def replace_callback(name, regexp = nil, klasses = [], &outer_block) remove_callback(name) define_callback(name, regexp, klasses, &outer_block) end
# File lib/added_methods.rb, line 239 def add_method(am) am = AddedMethod.new(am) unless am.is_a?(AddedMethod) all_methods[am.klass][am.file] << am end
# File lib/added_methods.rb, line 235 def added_method_callback lambda { |am| add_method(am) } end
# File lib/added_methods.rb, line 217 def defined_in_irb?(callstack) callstack = callstack.dup callstack.shift # ignore immediate caller callstack.reject! { |c| c =~ /\(irb\):|:in `irb_binding'/ } callstack.pop if callstack.last =~ %r{/irb/workspace\.rb:} callstack.empty? end
# File lib/added_methods.rb, line 213 def have_readline_history? Object.const_defined?(:Readline) && Readline.const_defined?(:HISTORY) end
# File lib/added_methods.rb, line 244 def identify_added_method(base, klass, id, singleton, callstack) am = { :base => base, :class => klass, :name => id.id2name, :singleton => singleton } if irb?(callstack) am.update( :file => HISTFILENAME, :line => Readline::HISTORY.size, :def => begin Readline::HISTORY[-1] rescue IndexError end ) else file, line, _ = where(callstack).split(':') line = line.to_i am.update( :file => file, :line => line, :def => (SCRIPT_LINES__[file] || [])[line - 1] ) end AddedMethod.new(am) end end
# File lib/added_methods.rb, line 189 def init_all_methods unless const_defined?(:ALL_METHODS) const_set(:ALL_METHODS, Hash.new { |h, k| h[k] = Hash.new { |i, j| i[j] = [] } }) end end
# File lib/added_methods.rb, line 182 def init_callbacks unless const_defined?(:CALLBACKS) const_set(:CALLBACKS, []) define_callback(:__default, //, [], &added_method_callback) end end
# File lib/added_methods.rb, line 176 def init_script_lines unless Object.const_defined?(:SCRIPT_LINES__) Object.const_set(:SCRIPT_LINES__, {}) end end
# File lib/added_methods.rb, line 227 def irb?(callstack) have_readline_history? && defined_in_irb?(callstack) end
# File lib/added_methods.rb, line 197 def patch_readline_history return unless have_readline_history? return if Readline::HISTORY.respond_to?(:_added_methods_original_push) class << Readline::HISTORY alias_method :_added_methods_original_push, :push def push(l) (SCRIPT_LINES__[HISTFILENAME] ||= Readline::HISTORY.to_a) << l _added_methods_original_push(l) end alias_method :<<, :push end end
# File lib/added_methods.rb, line 172 def singleton_class(klass = self) class << klass; self; end end
# File lib/added_methods.rb, line 231 def where(callstack, default = '(none):0') callstack.find { |i| i !~ /:in `.*'/ } || callstack[1] || default end