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,146 @@
1
+ module Boson
2
+ # This module contains the methods used to define the default option types.
3
+ #
4
+ # === Creating Your Own Option Type
5
+ # Defining your own option type simply requires one method (create_@type) to parse the option value and create
6
+ # the desired object. To create an option type :date, you could create the following create_date method:
7
+ # # Drop this in ~/.boson/commands/date_option.rb
8
+ # module Boson::Options::Date
9
+ # def create_date(value)
10
+ # # value should be mm/dd
11
+ # Date.parse(value + "/#{Date.today.year}")
12
+ # end
13
+ # end
14
+ # Boson::OptionParser.send :include, Boson::Options::Date
15
+ #
16
+ # Modify your config to load this new library by default:
17
+ # :defaults:
18
+ # - date_option
19
+ #
20
+ # In a FileLibrary, we could then use this new option:
21
+ # module Calendar
22
+ # #@options :day=>:date
23
+ # def appointments(options={})
24
+ # # ...
25
+ # end
26
+ # end
27
+ # # >> appointments '-d 10/10' -> {:day=>#<Date: 4910229/2,0,2299161> }
28
+ # As you can see, a date object is created from the :date option's value and passed into appointments().
29
+ #
30
+ # Some additional tips on the create_* method:
31
+ # * The argument passed to the method is the option value from the user.
32
+ # * To access the current option name use @current_option.
33
+ # * To access the hash of attributes the current option has use OptionParser.current_attributes. See
34
+ # OptionParser.new for more about option attributes.
35
+ #
36
+ # There are two optional methods per option type: validate_@type and usage_for_@type i.e. validate_date and usage_for_date.
37
+ # Like create_@type, validate_@type takes the option's value. If the value validation fails, raise an
38
+ # OptionParser::Error with a proper message. All user-defined option types automatically validate for an option value's existence.
39
+ # The usage_for_* method takes an option's name (i.e. --day) and returns a usage string to be wrapped in '[ ]'. If no usage is defined
40
+ # the default would look like '[--day=:date]'. Consider using the OptionParser.default_usage helper method for your usage.
41
+ module Options
42
+ #:stopdoc:
43
+ # Parse/create methods
44
+ def create_string(value)
45
+ if (values = current_attributes[:values]) && (values = values.sort_by {|e| e.to_s})
46
+ value = auto_alias_value(values, value)
47
+ validate_enum_values(values, value)
48
+ end
49
+ value
50
+ end
51
+
52
+ def create_boolean(value)
53
+ if (!@opt_types.key?(dasherize(@current_option)) && @current_option =~ /^no-(\w+)$/)
54
+ opt = (opt = original_no_opt($1)) ? undasherize(opt) : $1
55
+ (@current_option.replace(opt) && false)
56
+ else
57
+ true
58
+ end
59
+ end
60
+
61
+ def create_numeric(value)
62
+ value.index('.') ? value.to_f : value.to_i
63
+ end
64
+
65
+ def create_array(value)
66
+ splitter = current_attributes[:split] || ','
67
+ array = value.split(splitter)
68
+ if (values = current_attributes[:values]) && (values = values.sort_by {|e| e.to_s })
69
+ if current_attributes[:regexp]
70
+ array = array.map {|e|
71
+ (new_values = values.grep(/#{e}/)).empty? ? e : new_values
72
+ }.compact.flatten.uniq
73
+ else
74
+ array.each {|e| array.delete(e) && array += values if e == '*'}
75
+ array.map! {|e| auto_alias_value(values, e) }
76
+ end
77
+ validate_enum_values(values, array)
78
+ end
79
+ array
80
+ end
81
+
82
+ def create_hash(value)
83
+ (keys = current_attributes[:keys]) && keys = keys.sort_by {|e| e.to_s }
84
+ hash = parse_hash(value, keys)
85
+ if keys
86
+ hash = hash.inject({}) {|h,(k,v)|
87
+ h[auto_alias_value(keys, k)] = v; h
88
+ }
89
+ validate_enum_values(keys, hash.keys)
90
+ end
91
+ hash
92
+ end
93
+
94
+ def parse_hash(value, keys)
95
+ splitter = current_attributes[:split] || ','
96
+ if !value.include?(':') && current_attributes[:default_keys]
97
+ value = current_attributes[:default_keys].to_s + ":#{value}"
98
+ end
99
+
100
+ # Creates array pairs, grouping array of keys with a value
101
+ aoa = Hash[*value.split(/(?::)([^#{Regexp.quote(splitter)}]+)#{Regexp.quote(splitter)}?/)].to_a
102
+ aoa.each_with_index {|(k,v),i| aoa[i][0] = keys.join(splitter) if k == '*' } if keys
103
+ aoa.inject({}) {|t,(k,v)| k.split(splitter).each {|e| t[e] = v }; t }
104
+ end
105
+
106
+ # Validation methods
107
+ def validate_string(value)
108
+ raise OptionParser::Error, "cannot pass '#{value}' as an argument to option '#{@current_option}'" if valid?(value)
109
+ end
110
+
111
+ def validate_numeric(value)
112
+ unless value =~ OptionParser::NUMERIC and $& == value
113
+ raise OptionParser::Error, "expected numeric value for option '#{@current_option}'; got #{value.inspect}"
114
+ end
115
+ end
116
+
117
+ def validate_hash(value)
118
+ if !value.include?(':') && !current_attributes[:default_keys]
119
+ raise(OptionParser::Error, "invalid key:value pair for option '#{@current_option}'")
120
+ end
121
+ end
122
+
123
+ # Usage methods
124
+ def usage_for_boolean(opt)
125
+ opt
126
+ end
127
+
128
+ def usage_for_string(opt)
129
+ default_usage(opt, undasherize(opt).upcase)
130
+ end
131
+
132
+ def usage_for_numeric(opt)
133
+ default_usage opt, "N"
134
+ end
135
+
136
+ def usage_for_array(opt)
137
+ default_usage opt, "A,B,C"
138
+ end
139
+
140
+ def usage_for_hash(opt)
141
+ default_usage opt, "A:B,C:D"
142
+ end
143
+ #:startdoc:
144
+ end
145
+ end
146
+ Boson::OptionParser.send :include, Boson::Options
@@ -0,0 +1,147 @@
1
+ module Boson
2
+ # This module passes an original command's return value through methods/commands specified as pipe options. Pipe options
3
+ # are processed in this order:
4
+ # * A :query option searches an array of objects or hashes using Pipes.query_pipe.
5
+ # * A :sort option sorts an array of objects or hashes using Pipes.sort_pipe.
6
+ # * A :reverse_sort pipe option reverses an array.
7
+ # * A :pipes option takes an array of commands that modify the return value using Pipes.pipes_pipe.
8
+ # * All user-defined pipe options (:pipe_options key in Repo.config) are processed in random order.
9
+ #
10
+ # Some points:
11
+ # * User-defined pipes call a command (the option's name by default). It's the user's responsibility to have this
12
+ # command loaded when used. The easiest way to do this is by adding the pipe command's library to :defaults in main config.
13
+ # * By default, pipe commands do not modify the value their given. This means you can activate multiple pipes using
14
+ # a method's original return value.
15
+ # * A pipe command expects a command's return value as its first argument. If the pipe option takes an argument, it's passed
16
+ # on as a second argument.
17
+ # * When piping occurs in relation to rendering depends on the Hirb view. With the default Hirb view, piping occurs
18
+ # occurs in the middle of the rendering, after Hirb has converted the return value into an array of hashes.
19
+ # If using a custom Hirb view, piping occurs before rendering.
20
+ # * What the pipe command should expect as a return value depends on the type of command. If it's a command rendered with hirb's
21
+ # tables, the return value is a an array of hashes. For everything else, it's the method's original return value.
22
+ #
23
+ # === User Pipes
24
+ # User pipes have the following attributes which alter their behavior:
25
+ # [*:pipe*] Pipe command the pipe executes when called. Default is the pipe's name.
26
+ # [*:env*] Boolean which enables passing an additional hash to the pipe command. This hash contains information from the first
27
+ # command's input with the following keys: :args (command's arguments), :options (command's options),
28
+ # :global_options (command's global options) and :config (a command's configuration hash). Default is false.
29
+ # [*:filter*] Boolean which has the pipe command modify the original command's output with the value it returns. Default is false.
30
+ # [*:no_render*] Boolean to turn off auto-rendering of the original command's final output. Only applicable to :filter enabled
31
+ # pipes. Default is false.
32
+ # [*:solo*] Boolean to indicate this pipe can't run with other user pipes or pipes from :pipes option.
33
+ # If a user calls multiple solo pipes, only the first one detected is called.
34
+ #
35
+ # === User Pipes Example
36
+ # Let's say you want to have two commands, browser and copy, you want to make available as pipe options:
37
+ # # Opens url in browser. This command already ships with Boson.
38
+ # def browser(url)
39
+ # system('open', url)
40
+ # end
41
+ #
42
+ # # Copy to clipboard
43
+ # def copy(str)
44
+ # IO.popen('pbcopy', 'w+') {|clipboard| clipboard.write(str)}
45
+ # end
46
+ #
47
+ # To configure them, drop the following config in ~/.boson/config/boson.yml:
48
+ # :pipe_options:
49
+ # :browser:
50
+ # :type: :boolean
51
+ # :desc: Open in browser
52
+ # :copy:
53
+ # :type: :boolean
54
+ # :desc: Copy to clipboard
55
+ #
56
+ # Now for any command that returns a url string, these pipe options can be turned on to execute the url.
57
+ #
58
+ # Some examples of these options using commands from {my libraries}[http://github.com/cldwalker/irbfiles]:
59
+ # # Creates a gist and then opens url in browser and copies it.
60
+ # $ cat some_file | boson gist -bC # or cat some_file | boson gist --browser --copy
61
+ #
62
+ # # Generates rdoc in current directory and then opens it in browser
63
+ # irb>> rdoc '-b' # or rdoc '--browser'
64
+ module Pipe
65
+ extend self
66
+
67
+ # Process pipes for Scientist
68
+ def scientist_process(object, global_opt, env={})
69
+ @env = env
70
+ [:query, :sort, :reverse_sort].each {|e| global_opt.delete(e) } unless object.is_a?(Array)
71
+ process_pipes(object, global_opt)
72
+ end
73
+
74
+ # Main method which processes all pipe commands, both default and user-defined ones.
75
+ def process_pipes(obj, options)
76
+ internal_pipes(options).each {|pipe|
77
+ obj = Pipes.send("#{pipe}_pipe", obj, options[pipe]) if options[pipe]
78
+ }
79
+ process_user_pipes(obj, options)
80
+ end
81
+
82
+ # A hash that defines user pipes in the same way as the :pipe_options key in Repo.config.
83
+ # This method should be called when a pipe's library is loading.
84
+ def add_pipes(hash)
85
+ pipe_options.merge! setup_pipes(hash)
86
+ end
87
+
88
+ #:stopdoc:
89
+ def internal_pipes(global_opt)
90
+ internals = [:query, :sort, :reverse_sort, :pipes]
91
+ internals.delete(:pipes) if pipes_to_process(global_opt).any? {|e| pipe(e)[:solo] }
92
+ internals
93
+ end
94
+
95
+ def pipe_options
96
+ @pipe_options ||= setup_pipes(Boson.repo.config[:pipe_options] || {})
97
+ end
98
+
99
+ def setup_pipes(hash)
100
+ hash.each {|k,v| v[:pipe] ||= k }
101
+ end
102
+
103
+ def pipe(key)
104
+ pipe_options[key] || {}
105
+ end
106
+
107
+ # global_opt can come from Hirb callback or Scientist
108
+ def process_user_pipes(result, global_opt)
109
+ pipes_to_process(global_opt).each {|e|
110
+ args = [pipe(e)[:pipe], result]
111
+ args << global_opt[e] unless pipe(e)[:type] == :boolean
112
+ args << get_env(e, global_opt) if pipe(e)[:env]
113
+ pipe_result = Boson.invoke(*args)
114
+ result = pipe_result if pipe(e)[:filter]
115
+ }
116
+ result
117
+ end
118
+
119
+ def get_env(key, global_opt)
120
+ { :global_options=>global_opt.merge(:delete_callbacks=>[:z_boson_pipes]),
121
+ :config=>(@env[:config].dup[key] || {}),
122
+ :args=>@env[:args],
123
+ :options=>@env[:options] || {}
124
+ }
125
+ end
126
+
127
+ def any_no_render_pipes?(global_opt)
128
+ !(pipes = pipes_to_process(global_opt)).empty? &&
129
+ pipes.any? {|e| pipe(e)[:no_render] }
130
+ end
131
+
132
+ def pipes_to_process(global_opt)
133
+ pipes = (global_opt.keys & pipe_options.keys)
134
+ (solo_pipe = pipes.find {|e| pipe(e)[:solo] }) ? [solo_pipe] : pipes
135
+ end
136
+ #:startdoc:
137
+
138
+ # Callbacks used by Hirb::Helpers::Table to search,sort and run custom pipe commands on arrays of hashes.
139
+ module TableCallbacks
140
+ # Processes boson's pipes
141
+ def z_boson_pipes_callback(obj, options)
142
+ Pipe.process_pipes(obj, options)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ Hirb::Helpers::Table.send :include, Boson::Pipe::TableCallbacks
@@ -0,0 +1,75 @@
1
+ module Boson
2
+ # === Default Pipes: Search and Sort
3
+ # The default pipe options, :query, :sort and :reverse_sort, are quite useful for searching and sorting arrays:
4
+ # Some examples using default commands:
5
+ # # Searches commands in the full_name field for 'lib' and sorts results by that field.
6
+ # $ boson commands -q=f:lib -s=f # or commands --query=full_name:lib --sort=full_name
7
+ #
8
+ # # Multiple fields can be searched if separated by a ','. This searches the full_name and desc fields.
9
+ # $ boson commands -q=f,d:web # or commands --query=full_name,desc:web
10
+ #
11
+ # # All fields can be queried using a '*'.
12
+ # # Searches all library fields and then reverse sorts on name field
13
+ # $ boson libraries -q=*:core -s=n -R # or libraries --query=*:core --sort=name --reverse_sort
14
+ #
15
+ # # Multiple searches can be joined together by ','
16
+ # # Searches for libraries that have the name matching core or a library_type matching gem
17
+ # $ boson libraries -q=n:core,l:gem # or libraries --query=name:core,library_type:gem
18
+ #
19
+ # In these examples, we queried commands and examples with an explicit --query. However, -q or --query isn't necessary
20
+ # for these commands because they already default to it when not present. This behavior comes from the default_option
21
+ # attribute a command can have.
22
+ module Pipes
23
+ extend self
24
+
25
+ # Case-insensitive search an array of objects or hashes for the :query option.
26
+ # This option is a hash of fields mapped to their search terms. Searches are OR-ed.
27
+ # When searching hashes, numerical string keys in query_hash are converted to actual numbers to
28
+ # interface with Hirb.
29
+ def query_pipe(object, query_hash)
30
+ if object[0].is_a?(Hash)
31
+ query_hash.map {|field,query|
32
+ field = field.to_i if field.to_s[/^\d+$/]
33
+ object.select {|e| e[field].to_s =~ /#{query}/i }
34
+ }.flatten.uniq
35
+ else
36
+ query_hash.map {|field,query| object.select {|e| e.send(field).to_s =~ /#{query}/i } }.flatten.uniq
37
+ end
38
+ rescue NoMethodError
39
+ $stderr.puts "Query failed with nonexistant method '#{$!.message[/`(.*)'/,1]}'"
40
+ end
41
+
42
+ # Sorts an array of objects or hashes using a sort field. Sort is reversed with reverse_sort set to true.
43
+ def sort_pipe(object, sort)
44
+ sort_lambda = lambda {}
45
+ if object[0].is_a?(Hash)
46
+ if sort.to_s[/^\d+$/]
47
+ sort = sort.to_i
48
+ elsif object[0].keys.all? {|e| e.is_a?(Symbol) }
49
+ sort = sort.to_sym
50
+ end
51
+ sort_lambda = untouched_sort?(object.map {|e| e[sort] }) ? lambda {|e| e[sort] } : lambda {|e| e[sort].to_s }
52
+ else
53
+ sort_lambda = untouched_sort?(object.map {|e| e.send(sort) }) ? lambda {|e| e.send(sort) || ''} :
54
+ lambda {|e| e.send(sort).to_s }
55
+ end
56
+ object.sort_by &sort_lambda
57
+ rescue NoMethodError, ArgumentError
58
+ $stderr.puts "Sort failed with nonexistant method '#{sort}'"
59
+ end
60
+
61
+ def untouched_sort?(values) #:nodoc:
62
+ values.all? {|e| e.respond_to?(:<=>) } && values.map {|e| e.class }.uniq.size == 1
63
+ end
64
+
65
+ # Reverse an object
66
+ def reverse_sort_pipe(object, extra=nil)
67
+ object.reverse
68
+ end
69
+
70
+ # Pipes output of multiple commands recursively, given initial object
71
+ def pipes_pipe(obj, arr)
72
+ arr.inject(obj) {|acc,e| Boson.full_invoke(e, [acc]) }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,107 @@
1
+ %w{yaml fileutils}.each {|e| require e }
2
+ module Boson
3
+ # A class for repositories. A repository has a root directory with required subdirectories config/ and
4
+ # commands/ and optional subdirectory lib/. Each repository has a primary config file at config/boson.yml.
5
+ class Repo
6
+ def self.commands_dir(dir) #:nodoc:
7
+ File.join(dir, 'commands')
8
+ end
9
+
10
+ attr_accessor :dir, :config
11
+ # Creates a repository given a root directory.
12
+ def initialize(dir)
13
+ @dir = dir
14
+ end
15
+
16
+ # Points to the config/ subdirectory and is automatically created when called. Used for config files.
17
+ def config_dir
18
+ @config_dir ||= FileUtils.mkdir_p(config_dir_path) && config_dir_path
19
+ end
20
+
21
+ def config_dir_path
22
+ "#{dir}/config"
23
+ end
24
+
25
+ # Path name of main config file. If passed true, parent directory of file is created.
26
+ def config_file(create_dir=false)
27
+ File.join((create_dir ? config_dir : config_dir_path), 'boson.yml')
28
+ end
29
+
30
+ # Points to the commands/ subdirectory and is automatically created when called. Used for command libraries.
31
+ def commands_dir
32
+ @commands_dir ||= (cdir = self.class.commands_dir(@dir)) && FileUtils.mkdir_p(cdir) && cdir
33
+ end
34
+
35
+ # A hash read from the YAML config file at config/boson.yml.
36
+ # {See here}[http://github.com/cldwalker/irbfiles/blob/master/boson/config/boson.yml] for an example config file.
37
+ # Top level config keys, library attributes and config attributes need to be symbols.
38
+ # ==== Config keys for all repositories:
39
+ # [:libraries] Hash of libraries mapping their name to attribute hashes. See Library.new for configurable attributes.
40
+ # Example:
41
+ # :libraries=>{'completion'=>{:namespace=>true}}
42
+ # [:command_aliases] Hash of commands names and their aliases. Since this is global it will be read by _all_ libraries.
43
+ # This is useful for quickly creating aliases without having to worry about placing them under
44
+ # the correct library config. For non-global aliasing, aliases should be placed under the :command_aliases
45
+ # key of a library entry in :libraries.
46
+ # Example:
47
+ # :command_aliases=>{'libraries'=>'lib', 'commands'=>'com'}
48
+ # [:defaults] Array of libraries to load at start up for commandline and irb. This is useful for extending boson i.e. adding your
49
+ # own option types since these are loaded before any other libraries. Default is no libraries.
50
+ # [:console_defaults] Array of libraries to load at start up when used in irb. Default is to load all library files and libraries
51
+ # defined in the config.
52
+ # [:bin_defaults] Array of libraries to load at start up when used from the commandline. Default is no libraries.
53
+ # [:add_load_path] Boolean specifying whether to add a load path pointing to the lib subdirectory/. This is useful in sharing
54
+ # classes between libraries without resorting to packaging them as gems. Defaults to false if the lib
55
+ # subdirectory doesn't exist in the boson directory.
56
+ #
57
+ # ==== Config keys specific to the main repo config ~/.boson/config/boson.yml
58
+ # [:pipe_options] Hash of options available to all option commands for piping (see Pipe). A pipe option has the
59
+ # {normal option attributes}[link:classes/Boson/OptionParser.html#M000081] and these:
60
+ # * :pipe: Specifies the command to call when piping. Defaults to the pipe's option name.
61
+ # * :filter: Boolean which indicates that the pipe command will modify its input with what it returns.
62
+ # Default is false.
63
+ # [:render_options] Hash of render options available to all option commands to be passed to a Hirb view (see View). Since
64
+ # this merges with default render options, it's possible to override default render options.
65
+ # [:error_method_conflicts] Boolean specifying library loading behavior when its methods conflicts with existing methods in
66
+ # the global namespace. When set to false, Boson automatically puts the library in its own namespace.
67
+ # When set to true, the library fails to load explicitly. Default is false.
68
+ # [:console] Console to load when using --console from commandline. Default is irb.
69
+ # [:auto_namespace] Boolean which automatically namespaces all user-defined libraries. Be aware this can break libraries which
70
+ # depend on commands from other libraries. Default is false.
71
+ # [:ignore_directories] Array of directories to ignore when detecting local repositories for Boson.local_repo.
72
+ # [:no_auto_render] When set, turns off commandline auto-rendering of a command's output. Default is false.
73
+ # [:option_underscore_search] When set, OptionParser option values (with :values or :keys) are auto aliased with underscore searching.
74
+ # Default is true. See Util.underscore_search.
75
+ def config(reload=false)
76
+ if reload || @config.nil?
77
+ begin
78
+ @config = {:libraries=>{}, :command_aliases=>{}, :console_defaults=>[], :option_underscore_search=>true}
79
+ @config.merge!(YAML::load_file(config_file(true))) if File.exists?(config_file)
80
+ rescue ArgumentError
81
+ message = $!.message !~ /syntax error on line (\d+)/ ? "Error"+$!.message :
82
+ "Error: Syntax error in line #{$1} of config file '#{config_file}'"
83
+ Kernel.abort message
84
+ end
85
+ end
86
+ @config
87
+ end
88
+
89
+ # Updates main config file by passing config into a block to be modified and then saved
90
+ def update_config
91
+ yield(config)
92
+ write_config_file
93
+ end
94
+
95
+ def write_config_file #:nodoc:
96
+ File.open(config_file, 'w') {|f| f.write config.to_yaml }
97
+ end
98
+
99
+ def detected_libraries #:nodoc:
100
+ Dir[File.join(commands_dir, '**/*.rb')].map {|e| e.gsub(/^#{commands_dir}\/|\.rb$/, '') }
101
+ end
102
+
103
+ def all_libraries #:nodoc:
104
+ (detected_libraries + config[:libraries].keys).uniq
105
+ end
106
+ end
107
+ end