bosonson 0.304.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +108 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +181 -0
- data/bin/bss +6 -0
- data/bosonson.gemspec +24 -0
- data/deps.rip +2 -0
- data/lib/boson.rb +96 -0
- data/lib/boson/command.rb +196 -0
- data/lib/boson/commands.rb +7 -0
- data/lib/boson/commands/core.rb +77 -0
- data/lib/boson/commands/web_core.rb +153 -0
- data/lib/boson/index.rb +48 -0
- data/lib/boson/inspector.rb +120 -0
- data/lib/boson/inspectors/argument_inspector.rb +97 -0
- data/lib/boson/inspectors/comment_inspector.rb +100 -0
- data/lib/boson/inspectors/method_inspector.rb +98 -0
- data/lib/boson/libraries/file_library.rb +144 -0
- data/lib/boson/libraries/gem_library.rb +30 -0
- data/lib/boson/libraries/local_file_library.rb +30 -0
- data/lib/boson/libraries/module_library.rb +37 -0
- data/lib/boson/libraries/require_library.rb +23 -0
- data/lib/boson/library.rb +179 -0
- data/lib/boson/loader.rb +118 -0
- data/lib/boson/manager.rb +169 -0
- data/lib/boson/namespace.rb +31 -0
- data/lib/boson/option_command.rb +222 -0
- data/lib/boson/option_parser.rb +475 -0
- data/lib/boson/options.rb +146 -0
- data/lib/boson/pipe.rb +147 -0
- data/lib/boson/pipes.rb +75 -0
- data/lib/boson/repo.rb +107 -0
- data/lib/boson/repo_index.rb +124 -0
- data/lib/boson/runner.rb +81 -0
- data/lib/boson/runners/bin_runner.rb +208 -0
- data/lib/boson/runners/console_runner.rb +58 -0
- data/lib/boson/scientist.rb +182 -0
- data/lib/boson/util.rb +129 -0
- data/lib/boson/version.rb +3 -0
- data/lib/boson/view.rb +95 -0
- data/test/argument_inspector_test.rb +62 -0
- data/test/bin_runner_test.rb +223 -0
- data/test/command_test.rb +22 -0
- data/test/commands_test.rb +22 -0
- data/test/comment_inspector_test.rb +126 -0
- data/test/deps.rip +4 -0
- data/test/file_library_test.rb +42 -0
- data/test/loader_test.rb +235 -0
- data/test/manager_test.rb +114 -0
- data/test/method_inspector_test.rb +90 -0
- data/test/option_parser_test.rb +367 -0
- data/test/options_test.rb +189 -0
- data/test/pipes_test.rb +65 -0
- data/test/repo_index_test.rb +122 -0
- data/test/repo_test.rb +23 -0
- data/test/runner_test.rb +40 -0
- data/test/scientist_test.rb +341 -0
- data/test/test_helper.rb +130 -0
- data/test/util_test.rb +56 -0
- data/vendor/bundle/gems/bacon-bits-0.1.0/deps.rip +1 -0
- data/vendor/bundle/gems/hirb-0.6.0/test/deps.rip +4 -0
- 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
|
data/lib/boson/pipe.rb
ADDED
@@ -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
|
data/lib/boson/pipes.rb
ADDED
@@ -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
|
data/lib/boson/repo.rb
ADDED
@@ -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
|