method_extensions 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
|
+
|