boson 0.4.0 → 1.0.0
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.
- data/.gemspec +6 -7
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.rdoc +1 -1
- data/README.md +144 -0
- data/README.rdoc +2 -2
- data/Upgrading.md +23 -0
- data/bin/boson +2 -2
- data/lib/boson.rb +44 -52
- data/lib/boson/bare_runner.rb +83 -0
- data/lib/boson/bin_runner.rb +114 -0
- data/lib/boson/command.rb +92 -132
- data/lib/boson/inspector.rb +49 -48
- data/lib/boson/library.rb +71 -120
- data/lib/boson/loader.rb +73 -84
- data/lib/boson/manager.rb +131 -135
- data/lib/boson/method_inspector.rb +112 -0
- data/lib/boson/option_command.rb +71 -154
- data/lib/boson/option_parser.rb +178 -173
- data/lib/boson/options.rb +46 -32
- data/lib/boson/runner.rb +58 -66
- data/lib/boson/runner_library.rb +31 -0
- data/lib/boson/scientist.rb +48 -81
- data/lib/boson/util.rb +46 -61
- data/lib/boson/version.rb +1 -1
- data/test/bin_runner_test.rb +53 -191
- data/test/command_test.rb +5 -9
- data/test/deps.rip +2 -2
- data/test/loader_test.rb +18 -216
- data/test/manager_test.rb +69 -79
- data/test/method_inspector_test.rb +12 -36
- data/test/option_parser_test.rb +45 -32
- data/test/runner_library_test.rb +10 -0
- data/test/runner_test.rb +158 -28
- data/test/scientist_test.rb +9 -147
- data/test/test_helper.rb +87 -52
- metadata +30 -72
- data/deps.rip +0 -2
- data/lib/boson/commands.rb +0 -7
- data/lib/boson/commands/core.rb +0 -77
- data/lib/boson/commands/web_core.rb +0 -153
- data/lib/boson/index.rb +0 -48
- data/lib/boson/inspectors/argument_inspector.rb +0 -97
- data/lib/boson/inspectors/comment_inspector.rb +0 -100
- data/lib/boson/inspectors/method_inspector.rb +0 -98
- data/lib/boson/libraries/file_library.rb +0 -144
- data/lib/boson/libraries/gem_library.rb +0 -30
- data/lib/boson/libraries/local_file_library.rb +0 -30
- data/lib/boson/libraries/module_library.rb +0 -37
- data/lib/boson/libraries/require_library.rb +0 -23
- data/lib/boson/namespace.rb +0 -31
- data/lib/boson/pipe.rb +0 -147
- data/lib/boson/pipes.rb +0 -75
- data/lib/boson/repo.rb +0 -107
- data/lib/boson/runners/bin_runner.rb +0 -208
- data/lib/boson/runners/console_runner.rb +0 -58
- data/lib/boson/view.rb +0 -95
- data/test/argument_inspector_test.rb +0 -62
- data/test/commands_test.rb +0 -22
- data/test/comment_inspector_test.rb +0 -126
- data/test/file_library_test.rb +0 -42
- data/test/pipes_test.rb +0 -65
- data/test/repo_index_test.rb +0 -122
- data/test/repo_test.rb +0 -23
data/deps.rip
DELETED
data/lib/boson/commands.rb
DELETED
data/lib/boson/commands/core.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
module Boson::Commands::Core #:nodoc:
|
2
|
-
extend self
|
3
|
-
|
4
|
-
def config
|
5
|
-
command_attributes = Boson::Command::ATTRIBUTES + [:usage, :full_name, :render_options]
|
6
|
-
library_attributes = Boson::Library::ATTRIBUTES + [:library_type]
|
7
|
-
|
8
|
-
commands = {
|
9
|
-
'render'=>{:desc=>"Render any object using Hirb"},
|
10
|
-
'menu'=>{:desc=>"Provide a menu to multi-select elements from a given array"},
|
11
|
-
'usage'=>{:desc=>"Print a command's usage", :options=>{
|
12
|
-
:verbose=>{:desc=>"Display global options", :type=>:boolean},
|
13
|
-
:render_options=>{:desc=>"Render options for option tables", :default=>{},
|
14
|
-
:keys=>[:vertical, :fields, :hide_empty]} } },
|
15
|
-
'commands'=>{
|
16
|
-
:desc=>"List or search commands. Query must come before any options.", :default_option=>'query',
|
17
|
-
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
|
18
|
-
:local=>{:type=>:boolean, :desc=>"Local commands only" } },
|
19
|
-
:render_options=>{
|
20
|
-
[:headers,:H]=>{:default=>{:desc=>'description'}},
|
21
|
-
:query=>{:keys=>command_attributes, :default_keys=>'full_name'},
|
22
|
-
:fields=>{:default=>[:full_name, :lib, :alias, :usage, :desc], :values=>command_attributes, :enum=>false},
|
23
|
-
:filters=>{:default=>{:render_options=>:inspect, :options=>:inspect, :args=>:inspect, :config=>:inspect}}
|
24
|
-
}
|
25
|
-
},
|
26
|
-
'libraries'=>{
|
27
|
-
:desc=>"List or search libraries. Query must come before any options.", :default_option=>'query',
|
28
|
-
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
|
29
|
-
:local=>{:type=>:boolean, :desc=>"Local libraries only" } },
|
30
|
-
:render_options=>{
|
31
|
-
:query=>{:keys=>library_attributes, :default_keys=>'name'},
|
32
|
-
:fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes, :enum=>false},
|
33
|
-
:filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}, :desc=>"Filters to apply to library fields" }}
|
34
|
-
},
|
35
|
-
'load_library'=>{:desc=>"Load a library", :options=>{[:verbose,:V]=>true}}
|
36
|
-
}
|
37
|
-
|
38
|
-
{:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
|
39
|
-
end
|
40
|
-
|
41
|
-
def commands(options={})
|
42
|
-
cmds = options[:index] ? (Boson::Index.read || true) && Boson::Index.commands : Boson.commands
|
43
|
-
options[:local] ? cmds.select {|e| e.library && e.library.local? } : cmds
|
44
|
-
end
|
45
|
-
|
46
|
-
def libraries(options={})
|
47
|
-
libs = options[:index] ? (Boson::Index.read || true) && Boson::Index.libraries : Boson.libraries
|
48
|
-
options[:local] ? libs.select {|e| e.local? } : libs
|
49
|
-
end
|
50
|
-
|
51
|
-
def load_library(library, options={})
|
52
|
-
Boson::Manager.load(library, options)
|
53
|
-
end
|
54
|
-
|
55
|
-
def render(object, options={})
|
56
|
-
Boson::View.render(object, options)
|
57
|
-
end
|
58
|
-
|
59
|
-
def menu(arr, options={}, &block)
|
60
|
-
Hirb::Console.format_output(arr, options.merge(:class=>"Hirb::Menu"), &block)
|
61
|
-
end
|
62
|
-
|
63
|
-
def usage(command, options={})
|
64
|
-
puts Boson::Command.usage(command)
|
65
|
-
|
66
|
-
if (cmd = Boson::Command.find(command))
|
67
|
-
if cmd.options && !cmd.options.empty?
|
68
|
-
puts "\nLOCAL OPTIONS"
|
69
|
-
cmd.option_parser.print_usage_table options[:render_options].dup.merge(:local=>true)
|
70
|
-
end
|
71
|
-
if options[:verbose] && cmd.render_option_parser
|
72
|
-
puts "\nGLOBAL OPTIONS"
|
73
|
-
cmd.render_option_parser.print_usage_table options[:render_options].dup
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,153 +0,0 @@
|
|
1
|
-
module Boson::Commands::WebCore
|
2
|
-
extend self
|
3
|
-
|
4
|
-
def config #:nodoc:
|
5
|
-
commands = {
|
6
|
-
'get'=>{ :desc=>"Gets the body of a url", :args=>[['url'],['options', {}]]},
|
7
|
-
'post'=>{ :desc=>'Posts to a url', :args=>[['url'],['options', {}]]},
|
8
|
-
'build_url'=>{ :desc=>"Builds a url, escaping the given params", :args=>[['url'],['params']]},
|
9
|
-
'browser'=>{ :desc=>"Opens urls in a browser on a Mac"},
|
10
|
-
'install'=>{ :desc=>"Installs a library by url. Library should then be loaded with load_library.",
|
11
|
-
:args=>[['url'],['options', {}]],
|
12
|
-
:options=> { :name=>{:type=>:string, :desc=>"Library name to save to"},
|
13
|
-
:force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
|
14
|
-
:default=>{:type=>:boolean, :desc=>'Adds library as a default library to main config file'},
|
15
|
-
:module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
|
16
|
-
:method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
|
17
|
-
}
|
18
|
-
}
|
19
|
-
|
20
|
-
{:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
|
21
|
-
end
|
22
|
-
|
23
|
-
# Requires libraries only once before defining method with given block
|
24
|
-
def self.def_which_requires(meth, *libs, &block)
|
25
|
-
define_method(meth) do |*args|
|
26
|
-
libs.each {|e| require e }
|
27
|
-
define_method(meth, block).call(*args)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def_which_requires(:get, 'net/https') do |*args|
|
32
|
-
url, options = args[0], args[1] || {}
|
33
|
-
url = build_url(url, options[:params]) if options[:params]
|
34
|
-
Get.new(url).request(options)
|
35
|
-
end
|
36
|
-
|
37
|
-
def_which_requires(:build_url, 'cgi') do |url, params|
|
38
|
-
url + (url[/\?/] ? '&' : "?") + params.map {|k,v|
|
39
|
-
v = v.is_a?(Array) ? v.join(' ') : v.to_s
|
40
|
-
"#{k}=#{CGI.escape(v)}"
|
41
|
-
}.join("&")
|
42
|
-
end
|
43
|
-
|
44
|
-
def_which_requires(:post, 'uri', 'net/http') do |*args|
|
45
|
-
url, options = args[0], args[1] || {}
|
46
|
-
(res = Net::HTTP.post_form(URI.parse(url), options)) && res.body
|
47
|
-
end
|
48
|
-
|
49
|
-
def install(url, options={}) #:nodoc:
|
50
|
-
options[:name] ||= strip_name_from_url(url)
|
51
|
-
return puts("Please give a library name for this url.") if options[:name].empty?
|
52
|
-
filename = File.join ::Boson.repo.commands_dir, "#{options[:name]}.rb"
|
53
|
-
return puts("Library name #{options[:name]} already exists. Try a different name.") if File.exists?(filename) && !options[:force]
|
54
|
-
|
55
|
-
file_string = get(url) or raise "Unable to fetch url"
|
56
|
-
file_string = "# Originally from #{url}\n"+file_string
|
57
|
-
file_string = wrap_install(file_string, options) if options[:method_wrap] || options[:module_wrap]
|
58
|
-
|
59
|
-
File.open(filename, 'w') {|f| f.write file_string }
|
60
|
-
Boson.repo.update_config {|c| (c[:defaults] ||= []) << options[:name] } if options[:default]
|
61
|
-
puts "Saved to #{filename}."
|
62
|
-
end
|
63
|
-
|
64
|
-
# non-mac users should override this with the launchy gem
|
65
|
-
def browser(*urls)
|
66
|
-
system('open', *urls)
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
def wrap_install(file_string, options)
|
71
|
-
indent = " "
|
72
|
-
unless (mod_name = ::Boson::Util.camelize(options[:name]))
|
73
|
-
return puts("Can't wrap install with name #{options[:name]}")
|
74
|
-
end
|
75
|
-
|
76
|
-
file_string.gsub!(/(^)/,'\1'+indent)
|
77
|
-
file_string = "def #{options[:name]}\n#{file_string}\nend".gsub(/(^)/,'\1'+indent) if options[:method_wrap]
|
78
|
-
"module #{mod_name}\n#{file_string}\nend"
|
79
|
-
end
|
80
|
-
|
81
|
-
def strip_name_from_url(url)
|
82
|
-
url[/\/([^\/.]+)(\.[a-z]+)?$/, 1].to_s.gsub('-', '_').gsub(/[^a-zA-Z_]/, '')
|
83
|
-
end
|
84
|
-
|
85
|
-
# Used by the get command to make get requests and optionally parse json and yaml.
|
86
|
-
# Ruby 1.8.x is dependent on json gem for parsing json.
|
87
|
-
# See Get.request for options a request can take.
|
88
|
-
class Get
|
89
|
-
FORMAT_HEADERS = {
|
90
|
-
:json=>%w{application/json text/json application/javascript text/javascript},
|
91
|
-
:yaml=>%w{application/x-yaml text/yaml}
|
92
|
-
} #:nodoc:
|
93
|
-
|
94
|
-
def initialize(url, options={})
|
95
|
-
@url, @options = url, options
|
96
|
-
end
|
97
|
-
|
98
|
-
# Returns the response body string or a parsed data structure. Returns nil if request fails. By default expects response
|
99
|
-
# to be 200.
|
100
|
-
# ==== Options:
|
101
|
-
# [:any_response] Returns body string for any response code. Default is false.
|
102
|
-
# [:parse] Parse the body into either json or yaml. Expects a valid format or if true autodetects one.
|
103
|
-
# Default is false.
|
104
|
-
# [:raise_error] Raises any original errors when parsing or fetching url instead of handling errors silently.
|
105
|
-
def request(options={})
|
106
|
-
@options.merge! options
|
107
|
-
body = get_body
|
108
|
-
body && @options[:parse] ? parse_body(body) : body
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
# Returns body string if successful or nil if not.
|
113
|
-
def get_body
|
114
|
-
uri = URI.parse(@url)
|
115
|
-
@response = get_response(uri)
|
116
|
-
(@options[:any_response] || @response.code == '200') ? @response.body : nil
|
117
|
-
rescue
|
118
|
-
@options[:raise_error] ? raise : puts("Error: GET '#{@url}' -> #{$!.class}: #{$!.message}")
|
119
|
-
end
|
120
|
-
|
121
|
-
def get_response(uri)
|
122
|
-
net = Net::HTTP.new(uri.host, uri.port)
|
123
|
-
net.verify_mode = OpenSSL::SSL::VERIFY_NONE if uri.scheme == 'https'
|
124
|
-
net.use_ssl = true if uri.scheme == 'https'
|
125
|
-
net.start {|http| http.request_get(uri.request_uri) }
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns nil if dependencies or parsing fails
|
129
|
-
def parse_body(body)
|
130
|
-
format = determine_format(@options[:parse])
|
131
|
-
case format
|
132
|
-
when :json
|
133
|
-
unless ::Boson::Util.safe_require 'json'
|
134
|
-
return puts("Install the json gem to parse json: sudo gem install json")
|
135
|
-
end
|
136
|
-
JSON.parse body
|
137
|
-
when :yaml
|
138
|
-
YAML::load body
|
139
|
-
else
|
140
|
-
puts "Can't parse this format."
|
141
|
-
end
|
142
|
-
rescue
|
143
|
-
@options[:raise_error] ? raise : puts("Error while parsing #{format} response of '#{@url}': #{$!.class}")
|
144
|
-
end
|
145
|
-
|
146
|
-
def determine_format(format)
|
147
|
-
return format.to_sym if %w{json yaml}.include?(format.to_s)
|
148
|
-
return :json if FORMAT_HEADERS[:json].include?(@response.content_type)
|
149
|
-
return :yaml if FORMAT_HEADERS[:yaml].include?(@response.content_type)
|
150
|
-
nil
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
data/lib/boson/index.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Boson
|
2
|
-
# This class manages indexing/storing all commands and libraries. See RepoIndex for details
|
3
|
-
# about the index created for each Repo.
|
4
|
-
module Index
|
5
|
-
extend self
|
6
|
-
# Array of indexes, one per repo in Boson.repos.
|
7
|
-
def indexes
|
8
|
-
@indexes ||= Boson.repos.map {|e| RepoIndex.new(e) }
|
9
|
-
end
|
10
|
-
|
11
|
-
# Updates all repo indexes.
|
12
|
-
def update(options={})
|
13
|
-
indexes.each {|e| e.update(options) }
|
14
|
-
end
|
15
|
-
|
16
|
-
#:stopdoc:
|
17
|
-
def read
|
18
|
-
indexes.each {|e| e.read }
|
19
|
-
end
|
20
|
-
|
21
|
-
def find_library(command, object=false)
|
22
|
-
indexes.each {|e|
|
23
|
-
(lib = e.find_library(command, object)) and return lib
|
24
|
-
}
|
25
|
-
nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def find_command(command)
|
29
|
-
indexes.each {|e|
|
30
|
-
(cmd = Command.find(command, e.commands)) and return(cmd)
|
31
|
-
}
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def commands
|
36
|
-
indexes.map {|e| e.commands}.flatten
|
37
|
-
end
|
38
|
-
|
39
|
-
def libraries
|
40
|
-
indexes.map {|e| e.libraries}.flatten
|
41
|
-
end
|
42
|
-
|
43
|
-
def all_main_methods
|
44
|
-
indexes.map {|e| e.all_main_methods}.flatten
|
45
|
-
end
|
46
|
-
#:startdoc:
|
47
|
-
end
|
48
|
-
end
|
@@ -1,97 +0,0 @@
|
|
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}+(?:\w+\.)?#{Regexp.quote(meth)}#{tabspace}*($|(?:\(|\s+)([^\n\)]+)\s*\)?\s*$)/.match(file_string)
|
10
|
-
(match.to_a[2] || '').strip.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).map {|e| e.to_s } 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
|
-
print_debug_message(klass, meth) if Boson::Runner.debug
|
30
|
-
ensure
|
31
|
-
set_trace_func(nil)
|
32
|
-
end
|
33
|
-
|
34
|
-
def print_debug_message(klass, meth) #:nodoc:
|
35
|
-
warn "DEBUG: Error while scraping arguments from #{klass.to_s[/\w+$/]}##{meth}: #{$!.message}"
|
36
|
-
end
|
37
|
-
|
38
|
-
# process params + values to return array of argument arrays
|
39
|
-
def format_arguments(params, values, arity, num_args) #:nodoc:
|
40
|
-
params ||= []
|
41
|
-
params = params[0,num_args]
|
42
|
-
params.inject([[], 0]) do |(a, i), x|
|
43
|
-
if Array === values[i]
|
44
|
-
[a << ["*#{x}"], i+1]
|
45
|
-
else
|
46
|
-
if arity < 0 && i >= arity.abs - 1
|
47
|
-
[a << [x.to_s, values[i]], i + 1]
|
48
|
-
else
|
49
|
-
[a << [x.to_s], i+1]
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end.first
|
53
|
-
end
|
54
|
-
|
55
|
-
def trace_method_args(meth, klass, object) #:nodoc:
|
56
|
-
file = line = params = values = nil
|
57
|
-
arity = klass.instance_method(meth).arity
|
58
|
-
set_trace_func lambda{|event, file, line, id, binding, classname|
|
59
|
-
begin
|
60
|
-
if event[/call/] && classname == klass && id == meth
|
61
|
-
params = eval("local_variables", binding)
|
62
|
-
values = eval("local_variables.map{|x| eval(x.to_s)}", binding)
|
63
|
-
throw :done
|
64
|
-
end
|
65
|
-
rescue Exception
|
66
|
-
print_debug_message(klass, meth) if Boson::Runner.debug
|
67
|
-
end
|
68
|
-
}
|
69
|
-
if arity >= 0
|
70
|
-
num_args = arity
|
71
|
-
catch(:done){ object.send(meth, *(0...arity)) }
|
72
|
-
else
|
73
|
-
num_args = 0
|
74
|
-
# determine number of args (including splat & block)
|
75
|
-
MAX_ARGS.downto(arity.abs - 1) do |i|
|
76
|
-
catch(:done) do
|
77
|
-
begin
|
78
|
-
object.send(meth, *(0...i))
|
79
|
-
rescue Exception
|
80
|
-
end
|
81
|
-
end
|
82
|
-
# all nils if there's no splat and we gave too many args
|
83
|
-
next if !values || values.compact.empty?
|
84
|
-
k = nil
|
85
|
-
values.each_with_index{|x,j| break (k = j) if Array === x}
|
86
|
-
num_args = k ? k+1 : i
|
87
|
-
break
|
88
|
-
end
|
89
|
-
args = (0...arity.abs-1).to_a
|
90
|
-
catch(:done) do
|
91
|
-
args.empty? ? object.send(meth) : object.send(meth, *args)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
set_trace_func(nil)
|
95
|
-
return [params, values, arity, num_args]
|
96
|
-
end
|
97
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
module Boson
|
2
|
-
# Scrapes comments right before a method for its attributes. Method attributes must begin with '@' i.e.:
|
3
|
-
# # @desc Does foo
|
4
|
-
# # @options :verbose=>true
|
5
|
-
# def foo(options={})
|
6
|
-
#
|
7
|
-
# Some rules about these attributes:
|
8
|
-
# * Attribute definitions can span multiple lines. When a new attribute starts a line or the comments end,
|
9
|
-
# then a definition ends.
|
10
|
-
# * If no @desc is found in the comment block, then the first comment line directly above the method
|
11
|
-
# is assumed to be the value for @desc. This means that no multi-line attribute definitions can occur
|
12
|
-
# without a description since the last line is assumed to be a description.
|
13
|
-
# * options, option, config and render_options attributes can take any valid ruby since they're evaled in
|
14
|
-
# their module's context.
|
15
|
-
# * desc attribute is not evaled and is simply text to be set as a string.
|
16
|
-
#
|
17
|
-
# This module was inspired by
|
18
|
-
# {pragdave}[http://github.com/pragdavespc/rake/commit/45231ac094854da9f4f2ac93465ed9b9ca67b2da].
|
19
|
-
module CommentInspector
|
20
|
-
extend self
|
21
|
-
EVAL_ATTRIBUTES = [:options, :render_options, :config]
|
22
|
-
|
23
|
-
# Given a method's file string, line number and defining module, returns a hash
|
24
|
-
# of attributes defined for that method.
|
25
|
-
def scrape(file_string, line, mod, attribute=nil)
|
26
|
-
hash = scrape_file(file_string, line) || {}
|
27
|
-
options = (arr = hash.delete(:option)) ? parse_option_comments(arr, mod) : {}
|
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, k) : v.join(' ')
|
30
|
-
end
|
31
|
-
(hash[:options] ||= {}).merge!(options) if !options.empty?
|
32
|
-
attribute ? hash[attribute] : hash
|
33
|
-
end
|
34
|
-
|
35
|
-
#:stopdoc:
|
36
|
-
def parse_option_comments(arr, mod)
|
37
|
-
arr.inject({}) {|t,e|
|
38
|
-
key, val = e.join(' ').split(/\s*,\s*/, 2)
|
39
|
-
if val
|
40
|
-
key = key.sub(/^\s*:/, '').to_sym
|
41
|
-
t[key] = eval_comment(val, mod, 'option')
|
42
|
-
end
|
43
|
-
t
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
def eval_comment(value, mod, mattr)
|
48
|
-
value = "{#{value}}" if !value[/^\s*\{/] && value[/=>/]
|
49
|
-
mod.module_eval(value)
|
50
|
-
rescue Exception
|
51
|
-
if Runner.debug
|
52
|
-
warn "DEBUG: Error while evaluating @#{mattr} in module #{mod.to_s[/\w+$/]}:\n " +
|
53
|
-
$!.message.gsub(/\n/, "\n ")
|
54
|
-
end
|
55
|
-
nil
|
56
|
-
end
|
57
|
-
|
58
|
-
# Scrapes a given string for commented @keywords, starting with the line above the given line
|
59
|
-
def scrape_file(file_string, line)
|
60
|
-
lines = file_string.split("\n")
|
61
|
-
saved = []
|
62
|
-
i = line -2
|
63
|
-
while lines[i] =~ /^\s*#\s*(\S+)/ && i >= 0
|
64
|
-
saved << lines[i]
|
65
|
-
i -= 1
|
66
|
-
end
|
67
|
-
|
68
|
-
saved.empty? ? {} : splitter(saved.reverse)
|
69
|
-
end
|
70
|
-
|
71
|
-
def splitter(lines)
|
72
|
-
hash = {}
|
73
|
-
i = 0
|
74
|
-
# to magically make the last comment a description
|
75
|
-
unless lines.any? {|e| e =~ /^\s*#\s*@desc/ }
|
76
|
-
last_line = lines.pop
|
77
|
-
hash[:desc] = (last_line =~ /^\s*#\s*([^@\s].*)/) ? [$1] : nil
|
78
|
-
lines << last_line unless hash[:desc]
|
79
|
-
end
|
80
|
-
|
81
|
-
option = []
|
82
|
-
while i < lines.size
|
83
|
-
while lines[i] =~ /^\s*#\s*@(\w+)\s*(.*)/
|
84
|
-
key = $1.to_sym
|
85
|
-
hash[key] = [$2]
|
86
|
-
i += 1
|
87
|
-
while lines[i] =~ /^\s*#\s*([^@\s].*)/
|
88
|
-
hash[key] << $1
|
89
|
-
i+= 1
|
90
|
-
end
|
91
|
-
option << hash.delete(:option) if key == :option
|
92
|
-
end
|
93
|
-
i += 1
|
94
|
-
end
|
95
|
-
hash[:option] = option if !option.empty?
|
96
|
-
hash
|
97
|
-
end
|
98
|
-
#:startdoc:
|
99
|
-
end
|
100
|
-
end
|