Module: CoreDocs::MethodInfo

Defined in:
lib/core_docs.rb

Class Method Summary collapse

Class Method Details

.aliases(meth) ⇒ Array

Retrives aliases of a method



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/core_docs.rb', line 48

def self.aliases(meth)
  # host        = is_singleton?(meth) ? meth.receiver : meth.owner
  # method_type = is_singleton?(meth) ? :method : :instance_method

  # methods = Pry::Method.send(:all_from_common, host, method_type, false).
  #                       map { |m| m.instance_variable_get(:@method) }

  # methods.select { |m| host.send(method_type,m.name) == host.send(method_type,meth.name) }.
  #         reject { |m| m.name == meth.name }.
  #         map    { |m| host.send(method_type,m.name) }
  []
end

.c_files_found?(gem_dir) ⇒ Boolean



123
124
125
# File 'lib/core_docs.rb', line 123

def self.c_files_found?(gem_dir)
  Dir.glob("#{gem_dir}/**/*.c").count > 0
end

.cache(meth) ⇒ Object

Cache the file that holds the method or return immediately if file is already cached. Return if the method cannot be cached - i.e is a C stdlib method.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/core_docs.rb', line 211

def self.cache(meth)
  file, _ = meth.source_location

  return if is_eval_method?(meth)
  return if cached?(meth)

  if !file
    parse_and_cache_if_gem_cext(meth)
    return
  end

  log.enter_level(Logger::FATAL) do
    YARD.parse(file)
  end
end

.cached?(meth) ⇒ Boolean

Check whether the file containing the method is already cached.



71
72
73
# File 'lib/core_docs.rb', line 71

def self.cached?(meth)
  !!registry_lookup(meth)
end

.find_gem_dir(meth) ⇒ String



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/core_docs.rb', line 143

def self.find_gem_dir(meth)
  # host = method_host(meth)

  # begin
  #   host_source_location, _ =  WrappedModule.new(host).source_location
  #   break if host_source_location != nil
  #   return unless host.name
  #   host = eval(host.namespace_name)
  # end while host

  # # we want to exclude all source_locations that aren't gems (i.e
  # # stdlib)
  # if host_source_location && host_source_location =~ %r{/gems/}
  #   gem_root(host_source_location)
  # else

    # the WrappedModule approach failed, so try our backup approach
    gem_dir_from_method(meth)
  # end
end

.gem_dir_from_method(meth) ⇒ String?

Try to recover the gem directory of a gem based on a method object.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/core_docs.rb', line 189

def self.gem_dir_from_method(meth)
  guess = 0

  host = method_host(meth)
  return unless host.name
  root_module_name = host.name.split("::").first
  while gem_name = guess_gem_name_from_module_name(root_module_name, guess)
    matches = $LOAD_PATH.grep %r{/gems/#{gem_name}} if !gem_name.empty?
    if matches && matches.any?
      return gem_root(matches.first)
    else
      guess += 1
    end
  end

  nil
end

.gem_root(dir) ⇒ String

FIXME: this is unnecessarily limited to ext/ and lib/ folders



134
135
136
137
138
# File 'lib/core_docs.rb', line 134

def self.gem_root(dir)
  if index = dir.rindex(/\/(?:lib|ext)(?:\/|$)/)
    dir[0..index-1]
  end
end

.guess_gem_name_from_module_name(name, guess) ⇒ String?

Try to guess what the gem name will be based on the name of the module. We try a few approaches here depending on the guess parameter.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/core_docs.rb', line 169

def self.guess_gem_name_from_module_name(name, guess)
  case guess
  when 0
    name.downcase
  when 1
    name.scan(/[A-Z][a-z]+/).map(&:downcase).join('_')
  when 2
    name.scan(/[A-Z][a-z]+/).map(&:downcase).join('_').sub("_", "-")
  when 3
    name.scan(/[A-Z][a-z]+/).map(&:downcase).join('-')
  when 4
    name
  else
    nil
  end
end

.info_for(meth) ⇒ YARD::CodeObjects::MethodObject

Retrieve the YARD object that contains the method data.



92
93
94
95
# File 'lib/core_docs.rb', line 92

def self.info_for(meth)
  cache(meth)
  registry_lookup(meth)
end

.is_eval_method?(meth) ⇒ Boolean

Determine whether a method is an eval method.



99
100
101
102
103
104
105
106
# File 'lib/core_docs.rb', line 99

def self.is_eval_method?(meth)
  file, _ = meth.source_location
  if file =~ /(\(.*\))|<.*>/
    true
  else
    false
  end
end

.is_singleton?(meth) ⇒ Boolean

Checks whether method is a singleton (i.e class method)



64
65
66
# File 'lib/core_docs.rb', line 64

def self.is_singleton?(meth)
  receiver_notation_for(meth).include?('.')
end

.method_host(meth) ⇒ Object



128
129
130
# File 'lib/core_docs.rb', line 128

def self.method_host(meth)
  is_singleton?(meth) && Module === meth.receiver ? meth.receiver : meth.owner
end

.parse_and_cache_if_gem_cext(meth) ⇒ Object

Attempts to find the c source files if method belongs to a gem and use YARD to parse and cache the source files for display



112
113
114
115
116
117
118
119
# File 'lib/core_docs.rb', line 112

def self.parse_and_cache_if_gem_cext(meth)
  if gem_dir = find_gem_dir(meth)
    if c_files_found?(gem_dir)
      warn "Scanning and caching *.c files..."
      YARD.parse("#{gem_dir}/**/*.c")
    end
  end
end

.receiver_notation_for(meth) ⇒ String

Note:

This mess is needed in order to support all the modern Rubies. YOU must figure out a better way to distinguish between class methods and instance methods.

Convert a method object into the Class#method string notation.



36
37
38
39
40
41
42
# File 'lib/core_docs.rb', line 36

def self.receiver_notation_for(meth)
  match = meth.inspect.match(/\A#<(?:Unbound)?Method: (.+)([#\.].+)>\z/)
  owner = meth.owner.to_s.sub(/#<.+?:(.+?)>/, '\1')
  name = match[2]
  name.sub!('#', '.') if match[1] =~ /\A#<Class:/
  owner + name
end

.registry_lookup(meth) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/core_docs.rb', line 75

def self.registry_lookup(meth)
  obj = YARD::Registry.at(receiver_notation_for(meth))
  if obj.nil?
    if !(aliases = aliases(meth)).empty?
      obj = YARD::Registry.at(receiver_notation_for(aliases.first))
    elsif meth.owner == Kernel
      # YARD thinks that some methods are on Object when
      # they're actually on Kernel; so try again on Object if Kernel fails.
      obj = YARD::Registry.at("Object##{meth.name}")
    end
  end
  obj
end