boson 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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