method_extensions 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,215 @@
|
|
1
|
+
ripper_available = true
|
2
|
+
begin
|
3
|
+
require "ripper"
|
4
|
+
rescue LoadError
|
5
|
+
ripper_available = false
|
6
|
+
end
|
7
|
+
|
8
|
+
module MethodSourceWithDoc
|
9
|
+
# Returns method source by parsing the file returned by `Method#source_location`.
|
10
|
+
#
|
11
|
+
# If method definition cannot be found `ArgumentError` exception is raised
|
12
|
+
# (this includes methods defined `attr_accessor`, `module_eval` etc.).
|
13
|
+
#
|
14
|
+
# Sample IRB session:
|
15
|
+
#
|
16
|
+
# ruby-1.9.2-head > require 'fileutils'
|
17
|
+
#
|
18
|
+
# ruby-1.9.2-head > puts FileUtils.method(:mkdir).source
|
19
|
+
# def mkdir(list, options = {})
|
20
|
+
# fu_check_options options, OPT_TABLE['mkdir']
|
21
|
+
# list = fu_list(list)
|
22
|
+
# fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
|
23
|
+
# return if options[:noop]
|
24
|
+
#
|
25
|
+
# list.each do |dir|
|
26
|
+
# fu_mkdir dir, options[:mode]
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# => nil
|
30
|
+
def source
|
31
|
+
MethodSourceRipper.source_from_source_location(source_location)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns comment preceding the method definition by parsing the file
|
35
|
+
# returned by `Method#source_location`
|
36
|
+
#
|
37
|
+
# Sample IRB session:
|
38
|
+
#
|
39
|
+
# ruby-1.9.2-head > require 'fileutils'
|
40
|
+
#
|
41
|
+
# ruby-1.9.2-head > puts FileUtils.method(:mkdir).doc
|
42
|
+
# #
|
43
|
+
# # Options: mode noop verbose
|
44
|
+
# #
|
45
|
+
# # Creates one or more directories.
|
46
|
+
# #
|
47
|
+
# # FileUtils.mkdir 'test'
|
48
|
+
# # FileUtils.mkdir %w( tmp data )
|
49
|
+
# # FileUtils.mkdir 'notexist', :noop => true # Does not really create.
|
50
|
+
# # FileUtils.mkdir 'tmp', :mode => 0700
|
51
|
+
# #
|
52
|
+
def doc
|
53
|
+
MethodDocRipper.doc_from_source_location(source_location)
|
54
|
+
end
|
55
|
+
|
56
|
+
# ruby-1.9.2-head > irb_context.inspect_mode = false # turn off inspect mode so that we can view sources
|
57
|
+
#
|
58
|
+
# ruby-1.9.2-head > ActiveRecord::Base.method(:find).source_with_doc
|
59
|
+
# ArgumentError: failed to find method definition around the lines:
|
60
|
+
# delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
|
61
|
+
# delegate :find_each, :find_in_batches, :to => :scoped
|
62
|
+
#
|
63
|
+
# ruby-1.9.2-head > ActiveRecord::Base.method(:scoped).source_with_doc
|
64
|
+
# # Returns an anonymous scope.
|
65
|
+
# #
|
66
|
+
# # posts = Post.scoped
|
67
|
+
# # posts.size # Fires "select count(*) from posts" and returns the count
|
68
|
+
# # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
|
69
|
+
# #
|
70
|
+
# # fruits = Fruit.scoped
|
71
|
+
# # fruits = fruits.where(:colour => 'red') if options[:red_only]
|
72
|
+
# # fruits = fruits.limit(10) if limited?
|
73
|
+
# #
|
74
|
+
# # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
|
75
|
+
# # intermediate values (scopes) around as first-class objects is convenient.
|
76
|
+
# #
|
77
|
+
# # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
|
78
|
+
# def scoped(options = {}, &block)
|
79
|
+
# if options.present?
|
80
|
+
# relation = scoped.apply_finder_options(options)
|
81
|
+
# block_given? ? relation.extending(Module.new(&block)) : relation
|
82
|
+
# else
|
83
|
+
# current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# ruby-1.9.2-head > ActiveRecord::Base.method(:unscoped).source_with_doc
|
88
|
+
# => def unscoped
|
89
|
+
# @unscoped ||= Relation.new(self, arel_table)
|
90
|
+
# finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# ruby-1.9.2-head > ActiveRecord::Relation.instance_method(:find).source_with_doc
|
94
|
+
# => # Find operates with four different retrieval approaches:
|
95
|
+
# ...
|
96
|
+
# def find(*args, &block)
|
97
|
+
# return to_a.find(&block) if block_given?
|
98
|
+
#
|
99
|
+
# options = args.extract_options!
|
100
|
+
#
|
101
|
+
# if options.present?
|
102
|
+
# ...
|
103
|
+
def source_with_doc
|
104
|
+
return unless source_location
|
105
|
+
|
106
|
+
[doc.to_s.chomp, source_unindent(source)].compact.reject(&:empty?).join("\n")
|
107
|
+
end
|
108
|
+
|
109
|
+
def full_inspect
|
110
|
+
"#{ inspect }\n#{ source_location }\n#{ source_with_doc }"
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def source_unindent(src)
|
116
|
+
lines = src.split("\n")
|
117
|
+
indented_lines = lines[1 .. -1] # first line doesn't have proper indentation
|
118
|
+
indent_level = indented_lines.
|
119
|
+
reject { |line| line.strip.empty? }. # exclude empty lines from indent level calculation
|
120
|
+
map { |line| line[/^(\s*)/, 1].size }. # map to indent level of every line
|
121
|
+
min
|
122
|
+
[lines[0], *indented_lines.map { |line| line[indent_level .. -1] }].join("\n")
|
123
|
+
end
|
124
|
+
|
125
|
+
class ::Method
|
126
|
+
include MethodSourceWithDoc
|
127
|
+
end
|
128
|
+
|
129
|
+
class ::UnboundMethod
|
130
|
+
include MethodSourceWithDoc
|
131
|
+
end
|
132
|
+
|
133
|
+
class MethodSourceRipper < Ripper
|
134
|
+
def self.source_from_source_location(source_location)
|
135
|
+
return unless source_location
|
136
|
+
new(*source_location).method_source
|
137
|
+
end
|
138
|
+
|
139
|
+
def initialize(filename, method_definition_lineno)
|
140
|
+
super(IO.read(filename), filename)
|
141
|
+
@src_lines = IO.read(filename).split("\n")
|
142
|
+
@method_definition_lineno = method_definition_lineno
|
143
|
+
end
|
144
|
+
|
145
|
+
def method_source
|
146
|
+
parse
|
147
|
+
if @method_source
|
148
|
+
@method_source
|
149
|
+
else
|
150
|
+
raise ArgumentError.new("failed to find method definition around the lines:\n" <<
|
151
|
+
definition_lines.join("\n"))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def definition_lines
|
156
|
+
@src_lines[@method_definition_lineno - 1 .. @method_definition_lineno + 1]
|
157
|
+
end
|
158
|
+
|
159
|
+
Ripper::SCANNER_EVENTS.each do |meth|
|
160
|
+
define_method("on_#{ meth }") do |*args|
|
161
|
+
[lineno, column]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def on_def(name, params, body)
|
166
|
+
from_lineno, from_column = name
|
167
|
+
return unless @method_definition_lineno == from_lineno
|
168
|
+
|
169
|
+
to_lineno, to_column = lineno, column
|
170
|
+
|
171
|
+
@method_source = @src_lines[from_lineno - 1 .. to_lineno - 1].join("\n").strip
|
172
|
+
end
|
173
|
+
|
174
|
+
def on_defs(target, period, name, params, body)
|
175
|
+
on_def(target, params, body)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class MethodDocRipper < Ripper
|
180
|
+
def self.doc_from_source_location(source_location)
|
181
|
+
return unless source_location
|
182
|
+
new(*source_location).method_doc
|
183
|
+
end
|
184
|
+
|
185
|
+
def initialize(filename, method_definition_lineno)
|
186
|
+
super(IO.read(filename), filename)
|
187
|
+
@method_definition_lineno = method_definition_lineno
|
188
|
+
@last_comment_block = nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def method_doc
|
192
|
+
parse
|
193
|
+
@method_doc
|
194
|
+
end
|
195
|
+
|
196
|
+
Ripper::SCANNER_EVENTS.each do |meth|
|
197
|
+
define_method("on_#{ meth }") do |token|
|
198
|
+
if @last_comment_block &&
|
199
|
+
lineno == @method_definition_lineno
|
200
|
+
@method_doc = @last_comment_block.join.gsub(/^\s*/, "")
|
201
|
+
end
|
202
|
+
|
203
|
+
@last_comment_block = nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def on_comment(token)
|
208
|
+
(@last_comment_block ||= []) << token
|
209
|
+
end
|
210
|
+
|
211
|
+
def on_sp(token)
|
212
|
+
@last_comment_block << token if @last_comment_block
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end if ripper_available
|
@@ -0,0 +1,331 @@
|
|
1
|
+
module MethodSuper
|
2
|
+
# Returns method which will be called if given Method/UnboundMethod would
|
3
|
+
# call `super`.
|
4
|
+
# Implementation is incomplete with regard to modules included in singleton
|
5
|
+
# class (`class C; extend M; end`), but such modules usually don't use `super`
|
6
|
+
# anyway.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# class Base
|
11
|
+
# def meth; end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class Derived < Base
|
15
|
+
# def meth; end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# ruby-1.9.2-head > Derived.instance_method(:meth)
|
19
|
+
# => #<UnboundMethod: Derived#meth>
|
20
|
+
#
|
21
|
+
# ruby-1.9.2-head > Derived.instance_method(:meth).super
|
22
|
+
# => #<UnboundMethod: Base#meth>
|
23
|
+
def super
|
24
|
+
raise ArgumentError, "method doesn't have required @context_for_super instance variable set" unless @context_for_super
|
25
|
+
|
26
|
+
klass, level, name = @context_for_super.values_at(:klass, :level, :name)
|
27
|
+
|
28
|
+
unless @methods_all
|
29
|
+
@methods_all = MethodSuper.methods_all(klass)
|
30
|
+
|
31
|
+
# on first call ignore first found method
|
32
|
+
superclass_index = MethodSuper.superclass_index(@methods_all,
|
33
|
+
level,
|
34
|
+
name)
|
35
|
+
@methods_all = @methods_all[superclass_index + 1 .. -1]
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
superclass_index = MethodSuper.superclass_index(@methods_all,
|
40
|
+
level,
|
41
|
+
name)
|
42
|
+
|
43
|
+
superclass = @methods_all[superclass_index].keys.first
|
44
|
+
rest_methods_all = @methods_all[superclass_index + 1 .. -1]
|
45
|
+
|
46
|
+
super_method = if level == :class && superclass.class == Class
|
47
|
+
superclass.method(name)
|
48
|
+
elsif level == :instance ||
|
49
|
+
(level == :class && superclass.class == Module)
|
50
|
+
superclass.instance_method(name)
|
51
|
+
end
|
52
|
+
|
53
|
+
super_method.instance_variable_set(:@context_for_super, @context_for_super)
|
54
|
+
super_method.instance_variable_set(:@methods_all, rest_methods_all)
|
55
|
+
super_method
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.superclass_index(methods_all, level, name)
|
61
|
+
methods_all.index do |ancestor_with_methods|
|
62
|
+
ancestor, methods =
|
63
|
+
ancestor_with_methods.keys.first, ancestor_with_methods.values.first
|
64
|
+
methods[level] && methods[level].any? do |level, methods|
|
65
|
+
methods.include?(name)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.methods_all(klass)
|
71
|
+
MethodSuper::Methods.new(klass, :ancestor_name_formatter => proc { |ancestor, _| ancestor }).all
|
72
|
+
end
|
73
|
+
|
74
|
+
class Methods
|
75
|
+
def initialize(klass_or_module, options = {})
|
76
|
+
@klass_or_module = klass_or_module
|
77
|
+
@ancestor_name_formatter = options.fetch(:ancestor_name_formatter,
|
78
|
+
default_ancestor_name_formatter)
|
79
|
+
@exclude_trite = options.fetch(:exclude_trite, true)
|
80
|
+
end
|
81
|
+
|
82
|
+
def all
|
83
|
+
@all ||= find_all
|
84
|
+
end
|
85
|
+
|
86
|
+
VISIBILITIES = [ :public, :protected, :private ].freeze
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def default_ancestor_name_formatter
|
91
|
+
proc do |ancestor, singleton|
|
92
|
+
ancestor_name(ancestor, singleton)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_all
|
97
|
+
ancestors = [] # flattened ancestors (both normal and singleton)
|
98
|
+
|
99
|
+
(@klass_or_module.ancestors - trite_ancestors).each do |ancestor|
|
100
|
+
ancestor_singleton = ancestor.singleton_class
|
101
|
+
|
102
|
+
# Modules don't inherit class methods from included modules
|
103
|
+
unless @klass_or_module.instance_of?(Module) && ancestor != @klass_or_module
|
104
|
+
class_methods = collect_instance_methods(ancestor_singleton)
|
105
|
+
end
|
106
|
+
|
107
|
+
instance_methods = collect_instance_methods(ancestor)
|
108
|
+
|
109
|
+
append_ancestor_entry(ancestors, @ancestor_name_formatter[ancestor, false],
|
110
|
+
class_methods, instance_methods)
|
111
|
+
|
112
|
+
(singleton_ancestors(ancestor) || []).each do |singleton_ancestor|
|
113
|
+
class_methods = collect_instance_methods(singleton_ancestor)
|
114
|
+
append_ancestor_entry(ancestors, @ancestor_name_formatter[singleton_ancestor, true],
|
115
|
+
class_methods)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
ancestors
|
120
|
+
end
|
121
|
+
|
122
|
+
# singleton ancestors which ancestor introduced
|
123
|
+
def singleton_ancestors(ancestor)
|
124
|
+
@singleton_ancestors ||= all_singleton_ancestors
|
125
|
+
@singleton_ancestors[ancestor]
|
126
|
+
end
|
127
|
+
|
128
|
+
def all_singleton_ancestors
|
129
|
+
all = {}
|
130
|
+
seen = []
|
131
|
+
(@klass_or_module.ancestors - trite_ancestors).reverse.each do |ancestor|
|
132
|
+
singleton_ancestors = ancestor.singleton_class.ancestors - trite_singleton_ancestors
|
133
|
+
introduces = singleton_ancestors - seen
|
134
|
+
all[ancestor] = introduces unless introduces.empty?
|
135
|
+
seen.concat singleton_ancestors
|
136
|
+
end
|
137
|
+
all
|
138
|
+
end
|
139
|
+
|
140
|
+
def ancestor_name(ancestor, singleton)
|
141
|
+
"#{ singleton ? "S" : ""}[#{ ancestor.is_a?(Class) ? "C" : "M" }] #{ ancestor.name || ancestor.to_s }"
|
142
|
+
end
|
143
|
+
|
144
|
+
# ancestor is included only when contributes some methods
|
145
|
+
def append_ancestor_entry(ancestors, ancestor, class_methods, instance_methods = nil)
|
146
|
+
if class_methods || instance_methods
|
147
|
+
ancestor_entry = {}
|
148
|
+
ancestor_entry[:class] = class_methods if class_methods
|
149
|
+
ancestor_entry[:instance] = instance_methods if instance_methods
|
150
|
+
ancestors << {ancestor => ancestor_entry}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns hash { :public => [...public methods...],
|
155
|
+
# :protected => [...private methods...],
|
156
|
+
# :private => [...private methods...] }
|
157
|
+
# keys with empty values are excluded,
|
158
|
+
# when no methods are found - returns nil
|
159
|
+
def collect_instance_methods(klass)
|
160
|
+
methods_with_visibility = VISIBILITIES.map do |visibility|
|
161
|
+
methods = klass.send("#{ visibility }_instance_methods", false)
|
162
|
+
[visibility, methods] unless methods.empty?
|
163
|
+
end.compact
|
164
|
+
Hash[methods_with_visibility] unless methods_with_visibility.empty?
|
165
|
+
end
|
166
|
+
|
167
|
+
def trite_singleton_ancestors
|
168
|
+
return [] unless @exclude_trite
|
169
|
+
@trite_singleton_ancestors ||= Class.new.singleton_class.ancestors
|
170
|
+
end
|
171
|
+
|
172
|
+
def trite_ancestors
|
173
|
+
return [] unless @exclude_trite
|
174
|
+
@trite_ancestors ||= Class.new.ancestors
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class Method
|
180
|
+
include MethodSuper
|
181
|
+
end
|
182
|
+
|
183
|
+
class UnboundMethod
|
184
|
+
include MethodSuper
|
185
|
+
end
|
186
|
+
|
187
|
+
class Module
|
188
|
+
def instance_method_with_ancestors_for_super(name)
|
189
|
+
method = instance_method_without_ancestors_for_super(name)
|
190
|
+
method.instance_variable_set(:@context_for_super,
|
191
|
+
:klass => self,
|
192
|
+
:level => :instance,
|
193
|
+
:name => name)
|
194
|
+
|
195
|
+
method
|
196
|
+
end
|
197
|
+
|
198
|
+
unless method_defined?(:instance_method_without_ancestors_for_super) ||
|
199
|
+
private_method_defined?(:instance_method_without_ancestors_for_super)
|
200
|
+
alias_method :instance_method_without_ancestors_for_super, :instance_method
|
201
|
+
alias_method :instance_method, :instance_method_with_ancestors_for_super
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
module Kernel
|
206
|
+
def method_with_ancestors_for_super(name)
|
207
|
+
method = method_without_ancestors_for_super(name)
|
208
|
+
|
209
|
+
if respond_to?(:ancestors)
|
210
|
+
method.instance_variable_set(:@context_for_super,
|
211
|
+
:klass => self,
|
212
|
+
:level => :class,
|
213
|
+
:name => name)
|
214
|
+
else
|
215
|
+
method.instance_variable_set(:@context_for_super,
|
216
|
+
:klass => self.class,
|
217
|
+
:level => :instance,
|
218
|
+
:name => name)
|
219
|
+
end
|
220
|
+
|
221
|
+
method
|
222
|
+
end
|
223
|
+
|
224
|
+
unless method_defined?(:method_without_ancestors_for_super) ||
|
225
|
+
private_method_defined?(:method_without_ancestors_for_super)
|
226
|
+
alias_method :method_without_ancestors_for_super, :method
|
227
|
+
alias_method :method, :method_with_ancestors_for_super
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
module BaseIncludedModule
|
232
|
+
def module_meth
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
module BaseExtendedModule
|
237
|
+
def module_meth
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class BaseClass
|
242
|
+
include BaseIncludedModule
|
243
|
+
extend BaseExtendedModule
|
244
|
+
|
245
|
+
def self.singleton_meth
|
246
|
+
end
|
247
|
+
|
248
|
+
def meth
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class DerivedClass < BaseClass
|
253
|
+
def self.singleton_meth
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.module_meth
|
257
|
+
end
|
258
|
+
|
259
|
+
def meth
|
260
|
+
end
|
261
|
+
|
262
|
+
def module_meth
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
if $PROGRAM_NAME == __FILE__
|
267
|
+
require "rspec/core"
|
268
|
+
require "rspec/expectations"
|
269
|
+
require "rspec/matchers"
|
270
|
+
|
271
|
+
|
272
|
+
describe Method do
|
273
|
+
describe "#super" do
|
274
|
+
context "when called on result of DerivedClass.method(:singleton_meth)" do
|
275
|
+
it "returns BaseClass.method(:singleton_meth)" do
|
276
|
+
DerivedClass.method(:singleton_meth).super.should == BaseClass.method(:singleton_meth)
|
277
|
+
end
|
278
|
+
|
279
|
+
context "chained .super calls" do
|
280
|
+
context "when called on result of DerivedClass.method(:module_meth).super" do
|
281
|
+
it "returns BaseModule.instance_method(:module_meth)" do
|
282
|
+
DerivedClass.method(:module_meth).super.should == BaseExtendedModule.instance_method(:module_meth)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context "with class methods coming from extended modules only" do
|
287
|
+
it "returns proper super method" do
|
288
|
+
m1 = Module.new do
|
289
|
+
def m; end
|
290
|
+
end
|
291
|
+
m2 = Module.new do
|
292
|
+
def m; end
|
293
|
+
end
|
294
|
+
c = Class.new do
|
295
|
+
extend m1
|
296
|
+
extend m2
|
297
|
+
end
|
298
|
+
c.method(:m).super.should == m1.instance_method(:m)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context "when called on result of DerivedClass.new.method(:meth)" do
|
305
|
+
it "returns BaseClass.instance_method(:meth)" do
|
306
|
+
derived_instance = DerivedClass.new
|
307
|
+
derived_instance.method(:meth).super.should == BaseClass.instance_method(:meth)
|
308
|
+
end
|
309
|
+
|
310
|
+
context "chained .super calls" do
|
311
|
+
context "when called on result of DerivedClass.new.method(:module_meth).super" do
|
312
|
+
it "returns BaseModule.instance_method(:module_meth)" do
|
313
|
+
DerivedClass.new.method(:module_meth).super.should == BaseIncludedModule.instance_method(:module_meth)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe UnboundMethod do
|
323
|
+
describe "#super" do
|
324
|
+
context "when called on result of DerivedClass.instance_method(:meth)" do
|
325
|
+
it "returns BaseClass.instance_method(:meth)" do
|
326
|
+
DerivedClass.instance_method(:meth).super.should == BaseClass.instance_method(:meth)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "method_extensions"
|
6
|
+
s.version = "0.0.1"
|
7
|
+
s.authors = ["Evgeniy Dolzhenko"]
|
8
|
+
s.email = ["dolzenko@gmail.com"]
|
9
|
+
s.homepage = "http://github.com/dolzenko/method_extensions"
|
10
|
+
s.summary = "Method object extensions for better code navigation"
|
11
|
+
s.files = Dir.glob("lib/**/*") + %w(method_extensions.gemspec)
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: method_extensions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Evgeniy Dolzhenko
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-27 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email:
|
23
|
+
- dolzenko@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/method_extensions/method/source_with_doc.rb
|
32
|
+
- lib/method_extensions/method/super.rb
|
33
|
+
- lib/method_extensions.rb
|
34
|
+
- method_extensions.gemspec
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/dolzenko/method_extensions
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.7
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Method object extensions for better code navigation
|
67
|
+
test_files: []
|
68
|
+
|