boson 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.rdoc +133 -0
- data/Rakefile +52 -0
- data/VERSION.yml +4 -0
- data/bin/boson +6 -0
- data/lib/boson.rb +72 -0
- data/lib/boson/command.rb +117 -0
- data/lib/boson/commands.rb +7 -0
- data/lib/boson/commands/core.rb +66 -0
- data/lib/boson/commands/web_core.rb +36 -0
- data/lib/boson/index.rb +95 -0
- data/lib/boson/inspector.rb +80 -0
- data/lib/boson/inspectors/argument_inspector.rb +92 -0
- data/lib/boson/inspectors/comment_inspector.rb +79 -0
- data/lib/boson/inspectors/method_inspector.rb +94 -0
- data/lib/boson/libraries/file_library.rb +76 -0
- data/lib/boson/libraries/gem_library.rb +21 -0
- data/lib/boson/libraries/module_library.rb +17 -0
- data/lib/boson/libraries/require_library.rb +11 -0
- data/lib/boson/library.rb +108 -0
- data/lib/boson/loader.rb +103 -0
- data/lib/boson/manager.rb +184 -0
- data/lib/boson/namespace.rb +45 -0
- data/lib/boson/option_parser.rb +318 -0
- data/lib/boson/repo.rb +38 -0
- data/lib/boson/runner.rb +51 -0
- data/lib/boson/runners/bin_runner.rb +100 -0
- data/lib/boson/runners/repl_runner.rb +40 -0
- data/lib/boson/scientist.rb +168 -0
- data/lib/boson/util.rb +93 -0
- data/lib/boson/view.rb +31 -0
- data/test/argument_inspector_test.rb +62 -0
- data/test/bin_runner_test.rb +136 -0
- data/test/commands_test.rb +51 -0
- data/test/comment_inspector_test.rb +99 -0
- data/test/config/index.marshal +0 -0
- data/test/file_library_test.rb +50 -0
- data/test/index_test.rb +117 -0
- data/test/loader_test.rb +181 -0
- data/test/manager_test.rb +110 -0
- data/test/method_inspector_test.rb +64 -0
- data/test/option_parser_test.rb +365 -0
- data/test/repo_test.rb +22 -0
- data/test/runner_test.rb +43 -0
- data/test/scientist_test.rb +291 -0
- data/test/test_helper.rb +119 -0
- metadata +133 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module Boson::Commands::WebCore #:nodoc:
|
2
|
+
def self.config
|
3
|
+
descriptions = {
|
4
|
+
:install=>"Installs a library by url. Library should then be loaded with load_library.",
|
5
|
+
:browser=>"Opens urls in a browser", :get=>"Gets the body of a url" }
|
6
|
+
commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:description=>v}; h}
|
7
|
+
commands['install'][:options] = {:name=>:string, :force=>:boolean}
|
8
|
+
{:library_file=>File.expand_path(__FILE__), :commands=>commands}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(url)
|
12
|
+
%w{uri net/http}.each {|e| require e }
|
13
|
+
Net::HTTP.get(URI.parse(url))
|
14
|
+
rescue
|
15
|
+
raise "Error opening #{url}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def install(url, options={})
|
19
|
+
options[:name] ||= strip_name_from_url(url)
|
20
|
+
return puts("Please give a library name with this url.") unless options[:name]
|
21
|
+
filename = File.join Boson.repo.commands_dir, "#{options[:name]}.rb"
|
22
|
+
return puts("Library name #{options[:name]} already exists. Try a different name.") if File.exists?(filename) && !options[:force]
|
23
|
+
File.open(filename, 'w') {|f| f.write get(url) }
|
24
|
+
puts "Saved to #{filename}."
|
25
|
+
end
|
26
|
+
|
27
|
+
# non-mac users should override this with the launchy gem
|
28
|
+
def browser(*urls)
|
29
|
+
system('open', *urls)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def strip_name_from_url(url)
|
34
|
+
url[/\/([^\/.]+)(\.[a-z]+)?$/, 1]
|
35
|
+
end
|
36
|
+
end
|
data/lib/boson/index.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
module Boson
|
3
|
+
module Index
|
4
|
+
extend self
|
5
|
+
attr_reader :libraries, :commands
|
6
|
+
|
7
|
+
def read_and_transfer(ignored_libraries=[])
|
8
|
+
read
|
9
|
+
existing_libraries = (Boson.libraries.map {|e| e.name} + ignored_libraries).uniq
|
10
|
+
Boson.libraries += @libraries.select {|e| !existing_libraries.include?(e.name)}
|
11
|
+
existing_commands = Boson.commands.map {|e| e.name} #td: consider namespace
|
12
|
+
Boson.commands += @commands.select {|e| !existing_commands.include?(e.name) && !ignored_libraries.include?(e.lib)}
|
13
|
+
end
|
14
|
+
|
15
|
+
def update(options={})
|
16
|
+
options[:all] = true if !exists? && !options.key?(:all)
|
17
|
+
libraries_to_update = options[:all] ? Runner.all_libraries : changed_libraries
|
18
|
+
read_and_transfer(libraries_to_update)
|
19
|
+
if options[:verbose]
|
20
|
+
puts options[:all] ? "Generating index for all #{libraries_to_update.size} libraries. Patience ... is a bitch" :
|
21
|
+
(libraries_to_update.empty? ? "No libraries indexed" :
|
22
|
+
"Indexing the following libraries: #{libraries_to_update.join(', ')}")
|
23
|
+
end
|
24
|
+
Manager.load(libraries_to_update, options.merge(:index=>true)) unless libraries_to_update.empty?
|
25
|
+
write
|
26
|
+
end
|
27
|
+
|
28
|
+
def exists?
|
29
|
+
File.exists? marshal_file
|
30
|
+
end
|
31
|
+
|
32
|
+
def write
|
33
|
+
save_marshal_index Marshal.dump([Boson.libraries, Boson.commands, latest_hashes])
|
34
|
+
end
|
35
|
+
|
36
|
+
def save_marshal_index(marshal_string)
|
37
|
+
File.open(marshal_file, 'w') {|f| f.write marshal_string }
|
38
|
+
end
|
39
|
+
|
40
|
+
def read
|
41
|
+
return if @read
|
42
|
+
@libraries, @commands, @lib_hashes = exists? ? Marshal.load(File.read(marshal_file)) : [[], [], {}]
|
43
|
+
set_latest_namespaces
|
44
|
+
@read = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# get latest namespaces from config files
|
48
|
+
def set_latest_namespaces
|
49
|
+
namespace_libs = Boson.repo.config[:auto_namespace] ? @libraries.map {|e| [e.name, {:namespace=>true}]} :
|
50
|
+
Boson.repo.config[:libraries].select {|k,v| v && v[:namespace] }
|
51
|
+
lib_commands = @commands.inject({}) {|t,e| (t[e.lib] ||= []) << e; t }
|
52
|
+
namespace_libs.each {|name, hash|
|
53
|
+
if (lib = @libraries.find {|l| l.name == name})
|
54
|
+
lib.namespace = (hash[:namespace] == true) ? lib.clean_name : hash[:namespace]
|
55
|
+
(lib_commands[lib.name] || []).each {|e| e.namespace = lib.namespace }
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def namespaces
|
61
|
+
@libraries.map {|e| e.namespace }.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
def all_main_methods
|
65
|
+
@commands.reject {|e| e.namespace }.map {|e| [e.name, e.alias]}.flatten.compact + namespaces
|
66
|
+
end
|
67
|
+
|
68
|
+
def marshal_file
|
69
|
+
File.join(Boson.repo.config_dir, 'index.marshal')
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_library(command)
|
73
|
+
read
|
74
|
+
namespace_command = command.split('.')[0]
|
75
|
+
if (lib = @libraries.find {|e| e.namespace == namespace_command })
|
76
|
+
lib.name
|
77
|
+
elsif (cmd = Command.find(command, @commands))
|
78
|
+
cmd.lib
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def changed_libraries
|
83
|
+
read
|
84
|
+
latest_hashes.select {|lib, hash| @lib_hashes[lib] != hash}.map {|e| e[0]}
|
85
|
+
end
|
86
|
+
|
87
|
+
def latest_hashes
|
88
|
+
Runner.all_libraries.inject({}) {|h, e|
|
89
|
+
lib_file = FileLibrary.library_file(e, Boson.repo.dir)
|
90
|
+
h[e] = Digest::MD5.hexdigest(File.read(lib_file)) if File.exists?(lib_file)
|
91
|
+
h
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Boson
|
2
|
+
# Scrapes and processes method metadata with the inspectors (MethodInspector, CommentInspector
|
3
|
+
# and ArgumentInspector) and hands off the usable data to FileLibrary objects.
|
4
|
+
module Inspector
|
5
|
+
extend self
|
6
|
+
attr_reader :enabled
|
7
|
+
|
8
|
+
# Enable scraping by overridding method_added to snoop on a library while it's
|
9
|
+
# loading its methods.
|
10
|
+
def enable
|
11
|
+
@enabled = true
|
12
|
+
body = MethodInspector::METHODS.map {|e|
|
13
|
+
%[def #{e}(val)
|
14
|
+
Boson::MethodInspector.#{e}(self, val)
|
15
|
+
end]
|
16
|
+
}.join("\n") +
|
17
|
+
%[
|
18
|
+
def new_method_added(method)
|
19
|
+
Boson::MethodInspector.new_method_added(self, method)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :_old_method_added, :method_added
|
23
|
+
alias_method :method_added, :new_method_added
|
24
|
+
]
|
25
|
+
::Module.module_eval body
|
26
|
+
end
|
27
|
+
|
28
|
+
# Disable scraping method data.
|
29
|
+
def disable
|
30
|
+
::Module.module_eval %[
|
31
|
+
Boson::MethodInspector::METHODS.each {|e| remove_method e }
|
32
|
+
alias_method :method_added, :_old_method_added
|
33
|
+
]
|
34
|
+
@enabled = false
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds method data scraped for the library's module to the library's commands.
|
38
|
+
def add_method_data_to_library(library)
|
39
|
+
@commands_hash = library.commands_hash
|
40
|
+
@library_file = library.library_file
|
41
|
+
MethodInspector.current_module = library.module
|
42
|
+
@store = MethodInspector.store
|
43
|
+
add_method_scraped_data
|
44
|
+
add_comment_scraped_data
|
45
|
+
end
|
46
|
+
|
47
|
+
#:stopdoc:
|
48
|
+
def add_method_scraped_data
|
49
|
+
(MethodInspector::METHODS + [:method_args]).each do |e|
|
50
|
+
key = command_key(e)
|
51
|
+
(@store[e] || []).each do |cmd, val|
|
52
|
+
if no_command_config_for(cmd, key)
|
53
|
+
(@commands_hash[cmd] ||= {})[key] = val
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_comment_scraped_data
|
60
|
+
(@store[:method_locations] || []).select {|k,(f,l)| f == @library_file }.each do |cmd, (file, lineno)|
|
61
|
+
scraped = CommentInspector.scrape(FileLibrary.read_library_file(file), lineno, MethodInspector.current_module)
|
62
|
+
MethodInspector::METHODS.each do |e|
|
63
|
+
if no_command_config_for(cmd, e)
|
64
|
+
(@commands_hash[cmd] ||= {})[command_key(e)] = scraped[e]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# translates from inspector attribute name to command attribute name
|
71
|
+
def command_key(key)
|
72
|
+
{:method_args=>:args, :desc=>:description}[key] || key
|
73
|
+
end
|
74
|
+
|
75
|
+
def no_command_config_for(cmd, attribute)
|
76
|
+
!@commands_hash[cmd] || (@commands_hash[cmd] && !@commands_hash[cmd].key?(attribute))
|
77
|
+
end
|
78
|
+
#:startdoc:
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# Extracts arguments and their default values from methods either by
|
2
|
+
# by scraping a method's text or with method_added and brute force eval (thanks to
|
3
|
+
# {eigenclass}[http://eigenclass.org/hiki/method+arguments+via+introspection]).
|
4
|
+
module Boson::ArgumentInspector
|
5
|
+
extend self
|
6
|
+
# Returns same argument arrays as scrape_with_eval but argument defaults haven't been evaluated.
|
7
|
+
def scrape_with_text(file_string, meth)
|
8
|
+
tabspace = "[ \t]"
|
9
|
+
if match = /^#{tabspace}*def#{tabspace}+#{meth}#{tabspace}*($|\(?\s*([^\)]+)\s*\)?\s*$)/.match(file_string)
|
10
|
+
(match.to_a[2] || '').split(/\s*,\s*/).map {|e| e.split(/\s*=\s*/)}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Max number of arguments extracted per method with scrape_with_eval
|
15
|
+
MAX_ARGS = 10
|
16
|
+
# Scrapes non-private methods for argument names and default values.
|
17
|
+
# Returns arguments as array of argument arrays with optional default value as a second element.
|
18
|
+
# ====Examples:
|
19
|
+
# def meth1(arg1, arg2='val', options={}) -> [['arg1'], ['arg2', 'val'], ['options', {}]]
|
20
|
+
# def meth2(*args) -> [['*args']]
|
21
|
+
def scrape_with_eval(meth, klass, object)
|
22
|
+
unless %w[initialize].include?(meth.to_s)
|
23
|
+
return if class << object; private_instance_methods(true) end.include?(meth.to_s)
|
24
|
+
end
|
25
|
+
params, values, arity, num_args = trace_method_args(meth, klass, object)
|
26
|
+
return if local_variables == params # nothing new found
|
27
|
+
format_arguments(params, values, arity, num_args)
|
28
|
+
rescue Exception
|
29
|
+
# puts "#{klass}.#{meth}: #{$!.message}"
|
30
|
+
ensure
|
31
|
+
set_trace_func(nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
# process params + values to return array of argument arrays
|
35
|
+
def format_arguments(params, values, arity, num_args) #:nodoc:
|
36
|
+
params ||= []
|
37
|
+
params = params[0,num_args]
|
38
|
+
params.inject([[], 0]) do |(a, i), x|
|
39
|
+
if Array === values[i]
|
40
|
+
[a << ["*#{x}"], i+1]
|
41
|
+
else
|
42
|
+
if arity < 0 && i >= arity.abs - 1
|
43
|
+
[a << [x, values[i]], i + 1]
|
44
|
+
else
|
45
|
+
[a << [x], i+1]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end.first
|
49
|
+
end
|
50
|
+
|
51
|
+
def trace_method_args(meth, klass, object) #:nodoc:
|
52
|
+
file = line = params = values = nil
|
53
|
+
arity = klass.instance_method(meth).arity
|
54
|
+
set_trace_func lambda{|event, file, line, id, binding, classname|
|
55
|
+
begin
|
56
|
+
if event[/call/] && classname == klass && id == meth
|
57
|
+
params = eval("local_variables", binding)
|
58
|
+
values = eval("local_variables.map{|x| eval(x)}", binding)
|
59
|
+
throw :done
|
60
|
+
end
|
61
|
+
rescue Exception
|
62
|
+
end
|
63
|
+
}
|
64
|
+
if arity >= 0
|
65
|
+
num_args = arity
|
66
|
+
catch(:done){ object.send(meth, *(0...arity)) }
|
67
|
+
else
|
68
|
+
num_args = 0
|
69
|
+
# determine number of args (including splat & block)
|
70
|
+
MAX_ARGS.downto(arity.abs - 1) do |i|
|
71
|
+
catch(:done) do
|
72
|
+
begin
|
73
|
+
object.send(meth, *(0...i))
|
74
|
+
rescue Exception
|
75
|
+
end
|
76
|
+
end
|
77
|
+
# all nils if there's no splat and we gave too many args
|
78
|
+
next if !values || values.compact.empty?
|
79
|
+
k = nil
|
80
|
+
values.each_with_index{|x,j| break (k = j) if Array === x}
|
81
|
+
num_args = k ? k+1 : i
|
82
|
+
break
|
83
|
+
end
|
84
|
+
args = (0...arity.abs-1).to_a
|
85
|
+
catch(:done) do
|
86
|
+
args.empty? ? object.send(meth) : object.send(meth, *args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
set_trace_func(nil)
|
90
|
+
return [params, values, arity, num_args]
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Boson
|
2
|
+
# Scrapes comments right before a method for its metadata. Metadata attributes are the
|
3
|
+
# same as MethodInspector: desc, options, render_options. Attributes must begin with '@' i.e.:
|
4
|
+
#
|
5
|
+
# # @desc Does foo
|
6
|
+
# # @options :verbose=>true
|
7
|
+
# def foo(options={})
|
8
|
+
#
|
9
|
+
# Some rules about comment attributes:
|
10
|
+
# * Attribute definitions can span multiple lines. When a new attribute starts a line or the comments end
|
11
|
+
# then a definition ends.
|
12
|
+
# * If no @desc is found in the comment block, then the first comment line directly above the method
|
13
|
+
# is assumed to be the value for @desc. This means that no multi-line attribute definitions can occur
|
14
|
+
# without a description since the last line is assumed to be a description.
|
15
|
+
# * options and render_options attributes can take any valid ruby since they're evaled in their module's context.
|
16
|
+
# * desc attribute is not evaled and is simply text to be set as a string.
|
17
|
+
#
|
18
|
+
# This module was inspired by
|
19
|
+
# {pragdave}[http://github.com/pragdavespc/rake/commit/45231ac094854da9f4f2ac93465ed9b9ca67b2da].
|
20
|
+
module CommentInspector
|
21
|
+
extend self
|
22
|
+
EVAL_ATTRIBUTES = [:options, :render_options]
|
23
|
+
|
24
|
+
# Given a method's file string, line number and defining module, returns a hash
|
25
|
+
# of attributes defined for that method.
|
26
|
+
def scrape(file_string, line, mod, attribute=nil)
|
27
|
+
hash = scrape_file(file_string, line) || {}
|
28
|
+
hash.select {|k,v| v && (attribute.nil? || attribute == k) }.each do |k,v|
|
29
|
+
hash[k] = EVAL_ATTRIBUTES.include?(k) ? eval_comment(v.join(' '), mod) : v.join(' ')
|
30
|
+
end
|
31
|
+
attribute ? hash[attribute] : hash
|
32
|
+
end
|
33
|
+
|
34
|
+
#:stopdoc:
|
35
|
+
def eval_comment(value, mod)
|
36
|
+
value = "{#{value}}" if !value[/^\s*\{/] && value[/=>/]
|
37
|
+
begin mod.module_eval(value); rescue(Exception); nil end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Scrapes a given string for commented @keywords, starting with the line above the given line
|
41
|
+
def scrape_file(file_string, line)
|
42
|
+
lines = file_string.split("\n")
|
43
|
+
saved = []
|
44
|
+
i = line -2
|
45
|
+
while lines[i] =~ /^\s*#\s*(\S+)/ && i >= 0
|
46
|
+
saved << lines[i]
|
47
|
+
i -= 1
|
48
|
+
end
|
49
|
+
|
50
|
+
saved.empty? ? {} : splitter(saved.reverse)
|
51
|
+
end
|
52
|
+
|
53
|
+
def splitter(lines)
|
54
|
+
hash = {}
|
55
|
+
i = 0
|
56
|
+
# to magically make the last comment a description
|
57
|
+
unless lines.any? {|e| e =~ /^\s*#\s*@desc/ }
|
58
|
+
last_line = lines.pop
|
59
|
+
hash[:desc] = (last_line =~ /^\s*#\s*([^@\s].*)/) ? [$1] : nil
|
60
|
+
lines << last_line unless hash[:desc]
|
61
|
+
end
|
62
|
+
|
63
|
+
while i < lines.size
|
64
|
+
while lines[i] =~ /^\s*#\s*@(\w+)\s*(.*)/
|
65
|
+
key = $1.to_sym
|
66
|
+
hash[key] = [$2]
|
67
|
+
i += 1
|
68
|
+
while lines[i] =~ /^\s*#\s*([^@\s].*)/
|
69
|
+
hash[key] << $1
|
70
|
+
i+= 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
i += 1
|
74
|
+
end
|
75
|
+
hash
|
76
|
+
end
|
77
|
+
#:startdoc:
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Boson
|
2
|
+
# Allows for defining method metadata with new_method_added and the
|
3
|
+
# following Module methods:
|
4
|
+
# * desc: Defines a description for a method/command
|
5
|
+
# * options: Defines an OptionParser object for a method's options.
|
6
|
+
# * render_options: Defines an OptionParser object for a method's rendering options.
|
7
|
+
#
|
8
|
+
# These method calls must come immediately before a method i.e.:
|
9
|
+
#
|
10
|
+
# desc "Does foo"
|
11
|
+
# options :verbose=>:boolean
|
12
|
+
# def foo(options={})
|
13
|
+
#
|
14
|
+
# This module also allows for defining method metadata as comments. Although the actual
|
15
|
+
# scraping of comments is handled by CommentInspector, MethodInspector gather's the method
|
16
|
+
# location it needs with with find_method_locations().
|
17
|
+
module MethodInspector
|
18
|
+
extend self
|
19
|
+
attr_accessor :current_module
|
20
|
+
attr_reader :mod_store
|
21
|
+
@mod_store ||= {}
|
22
|
+
METHODS = [:desc, :options, :render_options]
|
23
|
+
|
24
|
+
# The method_added used while scraping method metadata.
|
25
|
+
def new_method_added(mod, meth)
|
26
|
+
return unless mod.name[/^Boson::Commands::/]
|
27
|
+
self.current_module = mod
|
28
|
+
store[:temp] ||= {}
|
29
|
+
METHODS.each do |e|
|
30
|
+
store[e][meth.to_s] = store[:temp][e] if store[:temp][e]
|
31
|
+
end
|
32
|
+
|
33
|
+
if store[:temp].size < METHODS.size
|
34
|
+
store[:method_locations] ||= {}
|
35
|
+
if (result = find_method_locations(caller))
|
36
|
+
store[:method_locations][meth.to_s] = result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
store[:temp] = {}
|
40
|
+
scrape_arguments(meth) if has_inspector_method?(meth, :options) || has_inspector_method?(meth,:render_options)
|
41
|
+
end
|
42
|
+
|
43
|
+
METHODS.each do |e|
|
44
|
+
define_method(e) do |mod, val|
|
45
|
+
store(mod)[e] ||= {}
|
46
|
+
(store(mod)[:temp] ||= {})[e] = val
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Scrapes a method's arguments using ArgumentInspector.
|
51
|
+
def scrape_arguments(meth)
|
52
|
+
store[:method_args] ||= {}
|
53
|
+
|
54
|
+
o = Object.new
|
55
|
+
o.extend(@current_module)
|
56
|
+
# private methods return nil
|
57
|
+
if (val = ArgumentInspector.scrape_with_eval(meth, @current_module, o))
|
58
|
+
store[:method_args][meth.to_s] = val
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns an array of the file and line number at which a method starts using
|
63
|
+
# a caller array. Necessary information for CommentInspector to function.
|
64
|
+
def find_method_locations(stack)
|
65
|
+
if (line = stack.find {|e| e =~ /in `load_source'/ })
|
66
|
+
(line =~ /^(.*):(\d+)/) ? [$1, $2.to_i] : nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#:stopdoc:
|
71
|
+
# Hash of a module's method attributes i.e. descriptions, options by method and then attribute
|
72
|
+
def store(mod=@current_module)
|
73
|
+
@mod_store[mod]
|
74
|
+
end
|
75
|
+
|
76
|
+
def current_module=(mod)
|
77
|
+
@current_module = mod
|
78
|
+
@mod_store[mod] ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_inspector_method?(meth, inspector)
|
82
|
+
(store[inspector] && store[inspector].key?(meth.to_s)) || inspector_in_file?(meth.to_s, inspector)
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspector_in_file?(meth, inspector_method)
|
86
|
+
return false if !(file_line = store[:method_locations] && store[:method_locations][meth])
|
87
|
+
if File.exists?(file_line[0]) && (options = CommentInspector.scrape(
|
88
|
+
FileLibrary.read_library_file(file_line[0]), file_line[1], @current_module, inspector_method) )
|
89
|
+
(store[inspector_method] ||= {})[meth] = options
|
90
|
+
end
|
91
|
+
end
|
92
|
+
#:startdoc:
|
93
|
+
end
|
94
|
+
end
|