bosonson 0.304.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.
Files changed (61) hide show
  1. data/CHANGELOG.rdoc +108 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.rdoc +181 -0
  4. data/bin/bss +6 -0
  5. data/bosonson.gemspec +24 -0
  6. data/deps.rip +2 -0
  7. data/lib/boson.rb +96 -0
  8. data/lib/boson/command.rb +196 -0
  9. data/lib/boson/commands.rb +7 -0
  10. data/lib/boson/commands/core.rb +77 -0
  11. data/lib/boson/commands/web_core.rb +153 -0
  12. data/lib/boson/index.rb +48 -0
  13. data/lib/boson/inspector.rb +120 -0
  14. data/lib/boson/inspectors/argument_inspector.rb +97 -0
  15. data/lib/boson/inspectors/comment_inspector.rb +100 -0
  16. data/lib/boson/inspectors/method_inspector.rb +98 -0
  17. data/lib/boson/libraries/file_library.rb +144 -0
  18. data/lib/boson/libraries/gem_library.rb +30 -0
  19. data/lib/boson/libraries/local_file_library.rb +30 -0
  20. data/lib/boson/libraries/module_library.rb +37 -0
  21. data/lib/boson/libraries/require_library.rb +23 -0
  22. data/lib/boson/library.rb +179 -0
  23. data/lib/boson/loader.rb +118 -0
  24. data/lib/boson/manager.rb +169 -0
  25. data/lib/boson/namespace.rb +31 -0
  26. data/lib/boson/option_command.rb +222 -0
  27. data/lib/boson/option_parser.rb +475 -0
  28. data/lib/boson/options.rb +146 -0
  29. data/lib/boson/pipe.rb +147 -0
  30. data/lib/boson/pipes.rb +75 -0
  31. data/lib/boson/repo.rb +107 -0
  32. data/lib/boson/repo_index.rb +124 -0
  33. data/lib/boson/runner.rb +81 -0
  34. data/lib/boson/runners/bin_runner.rb +208 -0
  35. data/lib/boson/runners/console_runner.rb +58 -0
  36. data/lib/boson/scientist.rb +182 -0
  37. data/lib/boson/util.rb +129 -0
  38. data/lib/boson/version.rb +3 -0
  39. data/lib/boson/view.rb +95 -0
  40. data/test/argument_inspector_test.rb +62 -0
  41. data/test/bin_runner_test.rb +223 -0
  42. data/test/command_test.rb +22 -0
  43. data/test/commands_test.rb +22 -0
  44. data/test/comment_inspector_test.rb +126 -0
  45. data/test/deps.rip +4 -0
  46. data/test/file_library_test.rb +42 -0
  47. data/test/loader_test.rb +235 -0
  48. data/test/manager_test.rb +114 -0
  49. data/test/method_inspector_test.rb +90 -0
  50. data/test/option_parser_test.rb +367 -0
  51. data/test/options_test.rb +189 -0
  52. data/test/pipes_test.rb +65 -0
  53. data/test/repo_index_test.rb +122 -0
  54. data/test/repo_test.rb +23 -0
  55. data/test/runner_test.rb +40 -0
  56. data/test/scientist_test.rb +341 -0
  57. data/test/test_helper.rb +130 -0
  58. data/test/util_test.rb +56 -0
  59. data/vendor/bundle/gems/bacon-bits-0.1.0/deps.rip +1 -0
  60. data/vendor/bundle/gems/hirb-0.6.0/test/deps.rip +4 -0
  61. metadata +217 -0
@@ -0,0 +1,7 @@
1
+ # Module under which most library modules are evaluated.
2
+ module Boson::Commands
3
+ # Used for defining namespaces.
4
+ module Namespace; end
5
+ end
6
+ require 'boson/commands/core'
7
+ require 'boson/commands/web_core'
@@ -0,0 +1,77 @@
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
@@ -0,0 +1,153 @@
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
@@ -0,0 +1,48 @@
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
@@ -0,0 +1,120 @@
1
+ module Boson
2
+ # Scrapes and processes method attributes with the inspectors (MethodInspector, CommentInspector
3
+ # and ArgumentInspector) and hands off the data to FileLibrary objects.
4
+ #
5
+ # === Method Attributes
6
+ # Method attributes refer to (commented) Module methods placed before a command's method
7
+ # in a FileLibrary module:
8
+ # module SomeMod
9
+ # # @render_options :fields=>%w{one two}
10
+ # # @config :alias=>'so'
11
+ # options :verbose=>:boolean
12
+ # option :count, :numeric
13
+ # # Something descriptive perhaps
14
+ # def some_method(opts)
15
+ # # ...
16
+ # end
17
+ # end
18
+ #
19
+ # Method attributes serve as configuration for a method's command. All attributes should only be called once per
20
+ # method except for option. Available method attributes:
21
+ # * config: Hash to define any command attributes (see Command.new).
22
+ # * desc: String to define a command's description for a command. Defaults to first commented line above a method.
23
+ # * options: Hash to define an OptionParser object for a command's options.
24
+ # * option: Option name and value to be merged in with options. See OptionParser for what an option value can be.
25
+ # * render_options: Hash to define an OptionParser object for a command's local/global render options (see View).
26
+ #
27
+ # When deciding whether to use commented or normal Module methods, remember that commented Module methods allow
28
+ # independence from Boson (useful for testing). See CommentInspector for more about commented method attributes.
29
+ module Inspector
30
+ extend self
31
+ attr_reader :enabled
32
+
33
+ # Enable scraping by overridding method_added to snoop on a library while it's
34
+ # loading its methods.
35
+ def enable
36
+ @enabled = true
37
+ body = MethodInspector::ALL_METHODS.map {|e|
38
+ %[def #{e}(*args)
39
+ Boson::MethodInspector.#{e}(self, *args)
40
+ end]
41
+ }.join("\n") +
42
+ %[
43
+ def new_method_added(method)
44
+ Boson::MethodInspector.new_method_added(self, method)
45
+ end
46
+
47
+ alias_method :_old_method_added, :method_added
48
+ alias_method :method_added, :new_method_added
49
+ ]
50
+ ::Module.module_eval body
51
+ end
52
+
53
+ # Disable scraping method data.
54
+ def disable
55
+ ::Module.module_eval %[
56
+ Boson::MethodInspector::ALL_METHODS.each {|e| remove_method e }
57
+ alias_method :method_added, :_old_method_added
58
+ ]
59
+ @enabled = false
60
+ end
61
+
62
+ # Adds method attributes scraped for the library's module to the library's commands.
63
+ def add_method_data_to_library(library)
64
+ @commands_hash = library.commands_hash
65
+ @library_file = library.library_file
66
+ MethodInspector.current_module = library.module
67
+ @store = MethodInspector.store
68
+ add_method_scraped_data
69
+ add_comment_scraped_data
70
+ end
71
+
72
+ #:stopdoc:
73
+ def add_method_scraped_data
74
+ (MethodInspector::METHODS + [:args]).each do |key|
75
+ (@store[key] || []).each do |cmd, val|
76
+ @commands_hash[cmd] ||= {}
77
+ add_valid_data_to_config(key, val, cmd)
78
+ end
79
+ end
80
+ end
81
+
82
+ def add_valid_data_to_config(key, value, cmd)
83
+ if valid_attr_value?(key, value)
84
+ add_scraped_data_to_config(key, value, cmd)
85
+ else
86
+ if Runner.debug
87
+ warn "DEBUG: Command '#{cmd}' has #{key.inspect} attribute with invalid value '#{value.inspect}'"
88
+ end
89
+ end
90
+ end
91
+
92
+ def add_scraped_data_to_config(key, value, cmd)
93
+ if value.is_a?(Hash)
94
+ if key == :config
95
+ @commands_hash[cmd] = Util.recursive_hash_merge value, @commands_hash[cmd]
96
+ else
97
+ @commands_hash[cmd][key] = Util.recursive_hash_merge value, @commands_hash[cmd][key] || {}
98
+ end
99
+ else
100
+ @commands_hash[cmd][key] ||= value
101
+ end
102
+ end
103
+
104
+ def valid_attr_value?(key, value)
105
+ return true if (klass = MethodInspector::METHOD_CLASSES[key]).nil?
106
+ value.is_a?(klass) || value.nil?
107
+ end
108
+
109
+ def add_comment_scraped_data
110
+ (@store[:method_locations] || []).select {|k,(f,l)| f == @library_file }.each do |cmd, (file, lineno)|
111
+ scraped = CommentInspector.scrape(FileLibrary.read_library_file(file), lineno, MethodInspector.current_module)
112
+ @commands_hash[cmd] ||= {}
113
+ MethodInspector::METHODS.each do |e|
114
+ add_valid_data_to_config(e, scraped[e], cmd)
115
+ end
116
+ end
117
+ end
118
+ #:startdoc:
119
+ end
120
+ end
@@ -0,0 +1,97 @@
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