boson 0.2.1 → 0.2.2

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/README.rdoc CHANGED
@@ -145,8 +145,7 @@ Having done that, let's start up irb:
145
145
  == Todo
146
146
  * More tests
147
147
  * Making commands out of existing gems easier and more powerful
148
- * Consider managing extensions to core and standard libraries
149
- * Features based on commands and their argument types i.e. aliasing, completion, piping
148
+ * Features based on commands and their argument types i.e. completing and piping
150
149
  * Consider dropping alias gem dependency if not using its full potential
151
150
 
152
151
  == Bugs/Issues
data/VERSION.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
- :major: 0
3
2
  :minor: 2
4
- :patch: 1
3
+ :patch: 2
4
+ :build:
5
+ :major: 0
data/lib/boson.rb CHANGED
@@ -51,9 +51,14 @@ module Boson
51
51
  end
52
52
  end
53
53
 
54
- # The array of loaded repositories containing the main repo and a possible local repo
54
+ # The array of loaded repositories containing the main repo and possible local and global repos
55
55
  def repos
56
- @repos ||= [repo, local_repo].compact
56
+ @repos ||= [repo, local_repo, global_repo].compact
57
+ end
58
+
59
+ # Optional global repository at /etc/boson
60
+ def global_repo
61
+ File.exists?('/etc/boson') ? Repo.new('/etc/boson') : nil
57
62
  end
58
63
 
59
64
  def main_object=(value) #:nodoc:
data/lib/boson/command.rb CHANGED
@@ -1,9 +1,20 @@
1
1
  module Boson
2
2
  # A command starts with the functionality of a ruby method and adds benefits with options, render_options, etc.
3
3
  class Command
4
+ class <<self; attr_accessor :all_option_commands ; end
5
+
4
6
  # Creates a command given its name and a library.
5
7
  def self.create(name, library)
6
- new (library.commands_hash[name] || {}).merge({:name=>name, :lib=>library.name, :namespace=>library.namespace})
8
+ obj = new(new_attributes(name, library))
9
+ if @all_option_commands && !%w{get method_missing}.include?(name)
10
+ obj.make_option_command(library)
11
+ end
12
+ obj
13
+ end
14
+
15
+ # Used to generate a command's initial attributes when creating a command object
16
+ def self.new_attributes(name, library)
17
+ (library.commands_hash[name] || {}).merge({:name=>name, :lib=>library.name, :namespace=>library.namespace})
7
18
  end
8
19
 
9
20
  # Finds a command, namespaced or not and aliased or not. If found returns the
@@ -18,17 +29,18 @@ module Boson
18
29
  commands.find(&find_lambda)
19
30
  end
20
31
 
21
- ATTRIBUTES = [:name, :lib, :alias, :description, :options, :args]
32
+ ATTRIBUTES = [:name, :lib, :alias, :desc, :options, :args, :config]
22
33
  attr_accessor *(ATTRIBUTES + [:render_options, :namespace, :default_option])
23
34
  # A hash of attributes which map to instance variables and values. :name
24
35
  # and :lib are required keys.
25
36
  #
26
37
  # Attributes that can be configured:
27
- # [*:description*] Description that shows up in command listings
38
+ # [*:desc*] Description that shows up in command listings
28
39
  # [*:alias*] Alternative name for command
29
40
  # [*:options*] Hash of options passed to OptionParser
30
- # [*:render_options*] Hash of rendering options passed to OptionParser
31
- # [*:global_options*] Boolean to enable using global options without having to define render_options or options.
41
+ # [*:render_options*] Hash of rendering options to pass to OptionParser. If the key :output_class is passed,
42
+ # that class's Hirb config will serve as defaults for this rendering hash.
43
+ # [*:option_command*] Boolean to wrap a command with an OptionCommand object i.e. allow commands to have options.
32
44
  # [*:args*] Should only be set if not automatically set. This attribute is only
33
45
  # important for commands that have options/render_options. Its value can be an array
34
46
  # (as ArgumentInspector.scrape_with_eval produces), a number representing
@@ -38,22 +50,29 @@ module Boson
38
50
  # # For a command with default option 'query' and options --query and -v
39
51
  # 'some -v' -> '--query=some -v'
40
52
  # '-v' -> '-v'
41
- def initialize(hash)
42
- @name = hash[:name] or raise ArgumentError
43
- @lib = hash[:lib] or raise ArgumentError
44
- [:alias, :description, :render_options, :options, :namespace, :default_option,
45
- :global_options].each do |e|
46
- instance_variable_set("@#{e}", hash[e]) if hash[e]
53
+ # [*:config*] A hash for third party libraries to get and set custom command attributes.
54
+ def initialize(attributes)
55
+ hash = attributes.dup
56
+ @name = hash.delete(:name) or raise ArgumentError
57
+ @lib = hash.delete(:lib) or raise ArgumentError
58
+ [:alias, :desc, :options, :namespace, :default_option, :option_command].each do |e|
59
+ instance_variable_set("@#{e}", hash.delete(e)) if hash.key?(e)
47
60
  end
48
- if hash[:args]
49
- if hash[:args].is_a?(Array)
50
- @args = hash[:args]
51
- elsif hash[:args].to_s[/^\d+/]
52
- @arg_size = hash[:args].to_i
53
- elsif hash[:args] == '*'
61
+
62
+ if hash[:render_options] && (@render_options = hash.delete(:render_options))[:output_class]
63
+ @render_options = Util.recursive_hash_merge View.class_config(@render_options[:output_class]), @render_options
64
+ end
65
+
66
+ if (args = hash.delete(:args))
67
+ if args.is_a?(Array)
68
+ @args = args
69
+ elsif args.to_s[/^\d+/]
70
+ @arg_size = args.to_i
71
+ elsif args == '*'
54
72
  @args = [['*args']]
55
73
  end
56
74
  end
75
+ @config = (hash.delete(:config) || {}).merge(hash)
57
76
  end
58
77
 
59
78
  # Library object a command belongs to.
@@ -63,12 +82,12 @@ module Boson
63
82
 
64
83
  # Array of array args with optional defaults. Scraped with ArgumentInspector.
65
84
  def args(lib=library)
66
- @args ||= begin
67
- if lib && File.exists?(lib.library_file || '')
68
- @file_parsed_args = true
69
- file_string = Boson::FileLibrary.read_library_file(lib.library_file)
70
- ArgumentInspector.scrape_with_text(file_string, @name)
71
- end
85
+ @args = !@args.nil? ? @args : begin
86
+ if lib
87
+ file_string, meth = file_string_and_method_for_args(lib)
88
+ (file_string && meth && (@file_parsed_args = true) &&
89
+ ArgumentInspector.scrape_with_text(file_string, meth))
90
+ end || false
72
91
  end
73
92
  end
74
93
 
@@ -101,12 +120,31 @@ module Boson
101
120
  end
102
121
 
103
122
  #:stopdoc:
123
+ def file_string_and_method_for_args(lib)
124
+ if !lib.is_a?(ModuleLibrary) && (klass_method = (lib.class_commands || {})[@name])
125
+ if RUBY_VERSION >= '1.9'
126
+ klass, meth = klass_method.split('.', 2)
127
+ if (meth_locations = MethodInspector.find_method_locations_for_19(klass, meth))
128
+ file_string = File.read meth_locations[0]
129
+ end
130
+ end
131
+ elsif File.exists?(lib.library_file || '')
132
+ file_string, meth = FileLibrary.read_library_file(lib.library_file), @name
133
+ end
134
+ [file_string, meth]
135
+ end
136
+
104
137
  def has_splat_args?
105
- @args && @args.any? {|e| e[0][/^\*/] }
138
+ !!(@args && @args[-1] && @args[-1][0][/^\*/])
139
+ end
140
+
141
+ def make_option_command(lib=library)
142
+ @option_command = true
143
+ @args = [['*args']] unless args(lib) || arg_size
106
144
  end
107
145
 
108
146
  def option_command?
109
- options || render_options || @global_options
147
+ options || render_options || @option_command
110
148
  end
111
149
 
112
150
  def arg_size
@@ -118,16 +156,23 @@ module Boson
118
156
  @file_parsed_args
119
157
  end
120
158
 
159
+ # Deprecated method
160
+ def description
161
+ puts "@command.description has been changed to @command.desc. Delete your old " +
162
+ "Boson index at ~/.boson/command/index.marshal for Boson to work from the commandline."
163
+ Kernel.exit
164
+ end
165
+
121
166
  def marshal_dump
122
167
  if @args && @args.any? {|e| e[1].is_a?(Module) }
123
168
  @args.map! {|e| e.size == 2 ? [e[0], e[1].inspect] : e }
124
169
  @file_parsed_args = true
125
170
  end
126
- [@name, @alias, @lib, @description, @options, @render_options, @args, @default_option]
171
+ [@name, @alias, @lib, @desc, @options, @render_options, @args, @default_option]
127
172
  end
128
173
 
129
174
  def marshal_load(ary)
130
- @name, @alias, @lib, @description, @options, @render_options, @args, @default_option = ary
175
+ @name, @alias, @lib, @desc, @options, @render_options, @args, @default_option = ary
131
176
  end
132
177
  #:startdoc:
133
178
  end
@@ -6,36 +6,43 @@ module Boson::Commands::Core #:nodoc:
6
6
  library_attributes = Boson::Library::ATTRIBUTES + [:library_type]
7
7
 
8
8
  commands = {
9
- 'render'=>{:description=>"Render any object using Hirb"},
10
- 'menu'=>{:description=>"Provide a menu to multi-select elements from a given array"},
11
- 'usage'=>{:description=>"Print a command's usage", :options=>{[:verbose, :V]=>:boolean}},
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=>{[:verbose, :V]=>:boolean}},
12
12
  'commands'=>{
13
- :description=>"List or search commands", :default_option=>'query',
14
- :options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"}},
13
+ :desc=>"List or search commands. Query must come before any options.", :default_option=>'query',
14
+ :options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
15
+ :local=>{:type=>:boolean, :desc=>"Local commands only" } },
15
16
  :render_options=>{
17
+ :headers=>{:default=>{:desc=>'description'}},
16
18
  :query=>{:keys=>command_attributes, :default_keys=>'full_name'},
17
- :fields=>{:default=>[:full_name, :lib, :alias, :usage, :description], :values=>command_attributes} }
19
+ :fields=>{:default=>[:full_name, :lib, :alias, :usage, :desc], :values=>command_attributes, :enum=>false},
20
+ :filters=>{:default=>{:render_options=>:inspect, :options=>:inspect, :args=>:inspect, :config=>:inspect}}
21
+ }
18
22
  },
19
23
  'libraries'=>{
20
- :description=>"List or search libraries", :default_option=>'query',
21
- :options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"} },
24
+ :desc=>"List or search libraries. Query must come before any options.", :default_option=>'query',
25
+ :options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
26
+ :local=>{:type=>:boolean, :desc=>"Local libraries only" } },
22
27
  :render_options=>{
23
28
  :query=>{:keys=>library_attributes, :default_keys=>'name'},
24
- :fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes},
29
+ :fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes, :enum=>false},
25
30
  :filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}, :desc=>"Filters to apply to library fields" }}
26
31
  },
27
- 'load_library'=>{:description=>"Load a library", :options=>{[:verbose,:V]=>true}}
32
+ 'load_library'=>{:desc=>"Load a library", :options=>{[:verbose,:V]=>true}}
28
33
  }
29
34
 
30
35
  {:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
31
36
  end
32
37
 
33
38
  def commands(options={})
34
- options[:index] ? (Boson::Index.read || true) && Boson::Index.commands : Boson.commands
39
+ cmds = options[:index] ? (Boson::Index.read || true) && Boson::Index.commands : Boson.commands
40
+ options[:local] ? cmds.select {|e| e.library && e.library.local? } : cmds
35
41
  end
36
42
 
37
43
  def libraries(options={})
38
- options[:index] ? (Boson::Index.read || true) && Boson::Index.libraries : Boson.libraries
44
+ libs = options[:index] ? (Boson::Index.read || true) && Boson::Index.libraries : Boson.libraries
45
+ options[:local] ? libs.select {|e| e.local? } : libs
39
46
  end
40
47
 
41
48
  def load_library(library, options={})
@@ -50,16 +57,16 @@ module Boson::Commands::Core #:nodoc:
50
57
  Hirb::Console.format_output(output, options.merge(:class=>"Hirb::Menu"), &block)
51
58
  end
52
59
 
53
- def usage(name, options={})
54
- msg = (command = Boson::Command.find(name)) ? "#{name} #{command.usage}" : "Command '#{name}' not found"
60
+ def usage(command, options={})
61
+ msg = (cmd = Boson::Command.find(command)) ? "#{command} #{cmd.usage}" : "Command '#{cmd}' not found"
55
62
  puts msg
56
- if command && options[:verbose]
57
- if command.options && !command.options.empty?
63
+ if cmd && options[:verbose]
64
+ if cmd.options && !cmd.options.empty?
58
65
  puts "\nLOCAL OPTIONS"
59
- command.option_parser.print_usage_table
66
+ cmd.option_parser.print_usage_table
60
67
  end
61
68
  puts "\nGLOBAL OPTIONS"
62
- Boson::Scientist.option_command(command).option_parser.print_usage_table
69
+ Boson::Scientist.option_command(cmd).option_parser.print_usage_table
63
70
  end
64
71
  end
65
72
  end
@@ -5,11 +5,11 @@ module Boson::Commands::WebCore #:nodoc:
5
5
  descriptions = {
6
6
  :install=>"Installs a library by url. Library should then be loaded with load_library.",
7
7
  :browser=>"Opens urls in a browser on a Mac", :get=>"Gets the body of a url", :post=>'Posts to url' }
8
- commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:description=>v}; h}
8
+ commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:desc=>v}; h}
9
9
  commands['install'][:options] = {:name=>{:type=>:string, :desc=>"Library name to save to"},
10
10
  :force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
11
11
  :module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
12
- :method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installe library using library name"}}
12
+ :method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
13
13
  {:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
14
14
  end
15
15
 
data/lib/boson/index.rb CHANGED
@@ -18,10 +18,16 @@ module Boson
18
18
  indexes.each {|e| e.read }
19
19
  end
20
20
 
21
- def find_library(command)
21
+ def find_library(command, object=false)
22
22
  indexes.each {|e|
23
- lib = e.find_library(command)
24
- return lib if lib
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)
25
31
  }
26
32
  nil
27
33
  end
@@ -101,7 +101,7 @@ module Boson
101
101
 
102
102
  # translates from inspector attribute name to command attribute name
103
103
  def command_key(key)
104
- {:method_args=>:args, :desc=>:description}[key] || key
104
+ {:method_args=>:args}[key] || key
105
105
  end
106
106
  #:startdoc:
107
107
  end
@@ -6,8 +6,8 @@ module Boson::ArgumentInspector
6
6
  # Returns same argument arrays as scrape_with_eval but argument defaults haven't been evaluated.
7
7
  def scrape_with_text(file_string, meth)
8
8
  tabspace = "[ \t]"
9
- if match = /^#{tabspace}*def#{tabspace}+#{meth}\b#{tabspace}*($|\(?\s*([^\)]+)\s*\)?\s*$)/.match(file_string)
10
- (match.to_a[2] || '').split(/\s*,\s*/).map {|e| e.split(/\s*=\s*/)}
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
11
  end
12
12
  end
13
13
 
@@ -57,6 +57,13 @@ module Boson
57
57
  end
58
58
 
59
59
  #:stopdoc:
60
+ def find_method_locations_for_19(klass, meth)
61
+ if (klass = Util.any_const_get(klass)) && (meth_location = klass.method(meth).source_location) &&
62
+ meth_location[0]
63
+ meth_location
64
+ end
65
+ end
66
+
60
67
  # Hash of a module's method attributes i.e. descriptions, options by method and then attribute
61
68
  def store(mod=@current_module)
62
69
  @mod_store[mod]
@@ -115,8 +115,8 @@ module Boson
115
115
  Inspector.disable
116
116
  end
117
117
 
118
- def create_module_from_path
119
- @name.split('/')[0..-2].inject(Boson::Commands) {|base, e|
118
+ def create_module_from_path(index=-2)
119
+ @name.split('/')[0..index].inject(Boson::Commands) {|base, e|
120
120
  base.const_defined?(sub_mod = Util.camelize(e)) ? base.const_get(sub_mod) :
121
121
  Util.create_module(base, e)
122
122
  }
@@ -125,20 +125,16 @@ module Boson
125
125
  def load_source_and_set_module
126
126
  detected = detect_additions(:modules=>true) { load_source }
127
127
  @module = determine_lib_module(detected[:modules]) unless @module
128
- #without this, module's class methods weren't showing up
129
- @module = Util.constantize(@module) if base_module != Commands
130
128
  end
131
129
 
132
130
  def determine_lib_module(detected_modules)
131
+ detected_modules = detected_modules.select {|e| e.to_s[/^#{base_module}::/] }
133
132
  case detected_modules.size
134
133
  when 1 then lib_module = detected_modules[0]
135
- when 0 then raise LoaderError, "Can't detect module. Make sure at least one module is defined in the library."
134
+ when 0 then lib_module = create_module_from_path(-1)
136
135
  else
137
136
  unless (lib_module = Util.constantize("boson/commands/#{@name}")) && lib_module.to_s[/^Boson::Commands/]
138
- command_modules = detected_modules.map {|e| e.to_s}.grep(/^#{base_module}::/)
139
- unless command_modules.size == 1 && (lib_module = command_modules[0])
140
- raise LoaderError, "Can't detect module. Specify a module in this library's config."
141
- end
137
+ raise LoaderError, "Can't detect module. Specify a module in this library's config."
142
138
  end
143
139
  end
144
140
  lib_module
data/lib/boson/library.rb CHANGED
@@ -29,7 +29,7 @@ module Boson
29
29
  # :commands:
30
30
  # delete:
31
31
  # :alias: d
32
- # :description: Http delete a given url
32
+ # :desc: Http delete a given url
33
33
  #
34
34
  # When installing a third-party library, use the config file as a way to override default library and command attributes
35
35
  # without modifying the library.
@@ -53,7 +53,7 @@ module Boson
53
53
  ATTRIBUTES = [:gems, :dependencies, :commands, :loaded, :module, :name, :namespace, :indexed_namespace]
54
54
  attr_reader *(ATTRIBUTES + [:commands_hash, :library_file, :object_namespace])
55
55
  # Private attribute for use within Boson.
56
- attr_reader :no_alias_creation, :new_module, :new_commands
56
+ attr_reader :no_alias_creation, :new_module, :new_commands, :class_commands, :lib_file, :repo_dir
57
57
  # Optional namespace name for a library. When enabled defaults to a library's name.
58
58
  attr_writer :namespace
59
59
 
@@ -69,7 +69,7 @@ module Boson
69
69
  # for the given commands with command names pointing to their configs. See Command.new for a
70
70
  # command's configurable attributes. If an array, the commands are set for the given library,
71
71
  # overidding default command detection. Example:
72
- # :commands=>{'commands'=>{:description=>'Lists commands', :alias=>'com'}}
72
+ # :commands=>{'commands'=>{:desc=>'Lists commands', :alias=>'com'}}
73
73
  # [*:class_commands*] A hash of commands to create. A hash key-pair can map command names to any string of ruby code
74
74
  # that ends with a method call. Or a key-pair can map a class to an array of its class methods
75
75
  # to create commands of the same name. Example:
@@ -122,6 +122,10 @@ module Boson
122
122
  @name[/\w+$/]
123
123
  end
124
124
 
125
+ def local?
126
+ is_a?(LocalFileLibrary) || (Boson.local_repo && Boson.local_repo.dir == @repo_dir)
127
+ end
128
+
125
129
  def set_name(name)
126
130
  name.to_s
127
131
  end
@@ -8,24 +8,6 @@ module Boson
8
8
 
9
9
  # Creates a namespace given its name and the library it belongs to.
10
10
  def self.create(name, library)
11
- if library.object_namespace && library.module.instance_methods.map {|e| e.to_s}.include?(name)
12
- library.include_in_universe
13
- create_object_namespace(name, library)
14
- else
15
- create_basic_namespace(name, library)
16
- end
17
- end
18
- #:stopdoc:
19
-
20
- def self.create_object_namespace(name, library)
21
- obj = library.namespace_object
22
- obj.instance_eval("class<<self;self;end").send(:define_method, :boson_commands) {
23
- self.class.instance_methods(false) }
24
- obj.instance_eval("class<<self;self;end").send(:define_method, :object_delegate?) { true }
25
- namespaces[name.to_s] = obj
26
- end
27
-
28
- def self.create_basic_namespace(name, library)
29
11
  namespaces[name.to_s] = new(name, library)
30
12
  Commands::Namespace.send(:define_method, name) { Boson::Namespace.namespaces[name.to_s] }
31
13
  end
@@ -36,11 +18,10 @@ module Boson
36
18
  class <<self; self end.send :include, @library.module
37
19
  end
38
20
 
39
- def object_delegate?; false; end
40
-
41
21
  def method_missing(method, *args, &block)
42
22
  Boson.can_invoke?(method) ? Boson.invoke(method, *args, &block) : super
43
23
  end
24
+
44
25
  #:startdoc:
45
26
  # List of subcommands for the namespace.
46
27
  def boson_commands