boson-more 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +35 -0
- data/deps.rip +1 -0
- data/lib/boson/alias.rb +75 -0
- data/lib/boson/argument_inspector.rb +90 -0
- data/lib/boson/commands/core.rb +67 -0
- data/lib/boson/commands/view_core.rb +19 -0
- data/lib/boson/commands/web_core.rb +153 -0
- data/lib/boson/comment_inspector.rb +100 -0
- data/lib/boson/console.rb +40 -0
- data/lib/boson/console_runner.rb +60 -0
- data/lib/boson/index.rb +48 -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/libraries.rb +183 -0
- data/lib/boson/more/version.rb +5 -0
- data/lib/boson/more.rb +18 -0
- data/lib/boson/more_commands.rb +14 -0
- data/lib/boson/more_inspector.rb +42 -0
- data/lib/boson/more_manager.rb +34 -0
- data/lib/boson/more_method_inspector.rb +74 -0
- data/lib/boson/more_option_parser.rb +28 -0
- data/lib/boson/more_scientist.rb +68 -0
- data/lib/boson/more_util.rb +30 -0
- data/lib/boson/namespace.rb +31 -0
- data/lib/boson/namespacer.rb +117 -0
- data/lib/boson/pipe.rb +156 -0
- data/lib/boson/pipe_runner.rb +44 -0
- data/lib/boson/pipes.rb +75 -0
- data/lib/boson/repo.rb +96 -0
- data/lib/boson/repo_index.rb +135 -0
- data/lib/boson/runner_options.rb +88 -0
- data/lib/boson/save.rb +198 -0
- data/lib/boson/science.rb +273 -0
- data/lib/boson/view.rb +98 -0
- data/lib/boson/viewable.rb +48 -0
- data/test/alias_test.rb +55 -0
- data/test/argument_inspector_test.rb +40 -0
- data/test/command_test.rb +22 -0
- data/test/commands_test.rb +53 -0
- data/test/comment_inspector_test.rb +126 -0
- data/test/console_runner_test.rb +58 -0
- data/test/deps.rip +4 -0
- data/test/file_library_test.rb +41 -0
- data/test/gem_library_test.rb +40 -0
- data/test/libraries_test.rb +55 -0
- data/test/loader_test.rb +38 -0
- data/test/module_library_test.rb +30 -0
- data/test/more_manager_test.rb +29 -0
- data/test/more_method_inspector_test.rb +42 -0
- data/test/more_scientist_test.rb +10 -0
- data/test/namespacer_test.rb +61 -0
- data/test/pipes_test.rb +65 -0
- data/test/repo_index_test.rb +123 -0
- data/test/repo_test.rb +23 -0
- data/test/runner_options_test.rb +29 -0
- data/test/save_test.rb +86 -0
- data/test/science_test.rb +58 -0
- data/test/scientist_test.rb +195 -0
- data/test/test_helper.rb +165 -0
- data/test/web_test.rb +22 -0
- metadata +169 -0
data/lib/boson/save.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'boson/namespacer'
|
2
|
+
require 'boson/repo'
|
3
|
+
require 'boson/index'
|
4
|
+
require 'boson/repo_index'
|
5
|
+
|
6
|
+
module Boson
|
7
|
+
module Save
|
8
|
+
def config
|
9
|
+
repo.config
|
10
|
+
end
|
11
|
+
|
12
|
+
def config=(val)
|
13
|
+
repo.config = val
|
14
|
+
end
|
15
|
+
|
16
|
+
# The main required repository which defaults to ~/.boson.
|
17
|
+
def repo
|
18
|
+
@repo ||= Repo.new("#{ENV['BOSON_HOME'] || Dir.home}/.boson")
|
19
|
+
end
|
20
|
+
|
21
|
+
# An optional local repository which defaults to ./lib/boson or ./.boson.
|
22
|
+
def local_repo
|
23
|
+
@local_repo ||= begin
|
24
|
+
ignored_dirs = (config[:ignore_directories] || []).map {|e| File.expand_path(e) }
|
25
|
+
dir = ["lib/boson", ".boson"].find {|e| File.directory?(e) &&
|
26
|
+
File.expand_path(e) != repo.dir && !ignored_dirs.include?(File.expand_path('.')) }
|
27
|
+
Repo.new(dir) if dir
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# The array of loaded repositories containing the main repo and possible local and global repos
|
32
|
+
def repos
|
33
|
+
@repos ||= [repo, local_repo, global_repo].compact
|
34
|
+
end
|
35
|
+
|
36
|
+
# Optional global repository at /etc/boson
|
37
|
+
def global_repo
|
38
|
+
File.exists?('/etc/boson') ? Repo.new('/etc/boson') : nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
extend Save
|
42
|
+
|
43
|
+
class BareRunner
|
44
|
+
module Save
|
45
|
+
def init
|
46
|
+
add_load_path
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def autoload_command(cmd, opts={verbose: Boson.verbose})
|
51
|
+
Index.read
|
52
|
+
(lib = Index.find_library(cmd)) && Manager.load(lib, opts)
|
53
|
+
lib
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_autoloader
|
57
|
+
class << ::Boson.main_object
|
58
|
+
def method_missing(method, *args, &block)
|
59
|
+
if BareRunner.autoload_command(method.to_s)
|
60
|
+
send(method, *args, &block) if respond_to?(method)
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_libraries
|
69
|
+
Boson.repos.map {|e| e.config[:defaults] || [] }.flatten + super
|
70
|
+
end
|
71
|
+
|
72
|
+
# Libraries detected in repositories
|
73
|
+
def detected_libraries
|
74
|
+
Boson.repos.map {|e| e.detected_libraries }.flatten.uniq
|
75
|
+
end
|
76
|
+
|
77
|
+
# Libraries specified in config files and detected_libraries
|
78
|
+
def all_libraries
|
79
|
+
Boson.repos.map {|e| e.all_libraries }.flatten.uniq
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_load_path
|
83
|
+
Boson.repos.each {|repo|
|
84
|
+
if repo.config[:add_load_path] || File.exists?(File.join(repo.dir, 'lib'))
|
85
|
+
$: << File.join(repo.dir, 'lib') unless $:.include? File.expand_path(File.join(repo.dir, 'lib'))
|
86
|
+
end
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
extend Save
|
91
|
+
end
|
92
|
+
|
93
|
+
# * All libraries can be configured by passing a hash of {library attributes}[link:classes/Boson/Library.html#M000077] under
|
94
|
+
# {the :libraries key}[link:classes/Boson/Repo.html#M000070] to the main config file ~/.boson/config/boson.yml.
|
95
|
+
# For most libraries this may be the only way to configure a library's commands.
|
96
|
+
# An example of a GemLibrary config:
|
97
|
+
# :libraries:
|
98
|
+
# httparty:
|
99
|
+
# :class_commands:
|
100
|
+
# delete: HTTParty.delete
|
101
|
+
# :commands:
|
102
|
+
# delete:
|
103
|
+
# :alias: d
|
104
|
+
# :desc: Http delete a given url
|
105
|
+
#
|
106
|
+
# When installing a third-party library, use the config file as a way to override default library and command attributes
|
107
|
+
# without modifying the library.
|
108
|
+
class Library
|
109
|
+
module Save
|
110
|
+
attr_accessor :repo_dir
|
111
|
+
|
112
|
+
def before_initialize
|
113
|
+
@repo_dir = set_repo.dir
|
114
|
+
end
|
115
|
+
|
116
|
+
def config
|
117
|
+
set_repo.config
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_repo
|
121
|
+
Boson.repo
|
122
|
+
end
|
123
|
+
|
124
|
+
def marshal_dump
|
125
|
+
[@name, @commands, @gems, @module.to_s, @repo_dir, @indexed_namespace]
|
126
|
+
end
|
127
|
+
|
128
|
+
def marshal_load(ary)
|
129
|
+
@name, @commands, @gems, @module, @repo_dir, @indexed_namespace = ary
|
130
|
+
end
|
131
|
+
end
|
132
|
+
include Save
|
133
|
+
end
|
134
|
+
|
135
|
+
class Command
|
136
|
+
module Save
|
137
|
+
def marshal_dump
|
138
|
+
if @args && @args.any? {|e| e[1].is_a?(Module) }
|
139
|
+
@args.map! {|e| e.size == 2 ? [e[0], e[1].inspect] : e }
|
140
|
+
@file_parsed_args = true
|
141
|
+
end
|
142
|
+
[@name, @alias, @lib, @desc, @options, @render_options, @args, @default_option]
|
143
|
+
end
|
144
|
+
|
145
|
+
def marshal_load(ary)
|
146
|
+
@name, @alias, @lib, @desc, @options, @render_options, @args, @default_option = ary
|
147
|
+
end
|
148
|
+
end
|
149
|
+
include Save
|
150
|
+
end
|
151
|
+
|
152
|
+
if defined? BinRunner
|
153
|
+
# Any changes to your commands are immediately available from
|
154
|
+
# the commandline except for changes to the main config file. For those
|
155
|
+
# changes to take effect you need to explicitly load and index the libraries
|
156
|
+
# with --index. See RepoIndex to understand how Boson can immediately detect
|
157
|
+
# the latest commands.
|
158
|
+
class BinRunner
|
159
|
+
module Save
|
160
|
+
def autoload_command(cmd)
|
161
|
+
if !Boson.can_invoke?(cmd, false)
|
162
|
+
update_index
|
163
|
+
super(cmd, load_options)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def update_index
|
168
|
+
Index.update(verbose: verbose)
|
169
|
+
end
|
170
|
+
|
171
|
+
def execute_command(cmd, args)
|
172
|
+
@command = cmd # for external errors
|
173
|
+
autoload_command cmd
|
174
|
+
super
|
175
|
+
end
|
176
|
+
|
177
|
+
def command_not_found?(cmd)
|
178
|
+
super && (!(Index.read && Index.find_command(cmd[/\w+/])) || cmd.include?(NAMESPACE))
|
179
|
+
end
|
180
|
+
|
181
|
+
def command_name(cmd)
|
182
|
+
cmd.split(Boson::NAMESPACE)[-1]
|
183
|
+
end
|
184
|
+
|
185
|
+
def eval_execute_option(str)
|
186
|
+
define_autoloader
|
187
|
+
super
|
188
|
+
end
|
189
|
+
|
190
|
+
def default_libraries
|
191
|
+
super + Boson.repos.map {|e| e.config[:bin_defaults] || [] }.flatten +
|
192
|
+
Dir.glob('Bosonfile')
|
193
|
+
end
|
194
|
+
end
|
195
|
+
extend Save
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
require 'boson/view'
|
2
|
+
require 'boson/pipe'
|
3
|
+
require 'boson/pipes'
|
4
|
+
require 'boson/more_scientist'
|
5
|
+
|
6
|
+
module Boson
|
7
|
+
class OptionCommand
|
8
|
+
BASIC_OPTIONS.update(
|
9
|
+
:delete_options=>{:type=>:array, :desc=>'Deletes global options starting with given strings' },
|
10
|
+
:usage_options=>{:type=>:string, :desc=>"Render options to pass to usage/help"},
|
11
|
+
:render=> {:type=>:boolean, :desc=>"Toggle a command's default rendering behavior"})
|
12
|
+
PIPE_OPTIONS = {
|
13
|
+
:sort=>{:type=>:string, :desc=>"Sort by given field"},
|
14
|
+
:reverse_sort=>{:type=>:boolean, :desc=>"Reverse a given sort"},
|
15
|
+
:query=>{:type=>:hash, :desc=>"Queries fields given field:search pairs"},
|
16
|
+
:pipes=>{:alias=>'P', :type=>:array, :desc=>"Pipe to commands sequentially"}
|
17
|
+
} #:nodoc:
|
18
|
+
|
19
|
+
RENDER_OPTIONS = {
|
20
|
+
:fields=>{:type=>:array, :desc=>"Displays fields in the order given"},
|
21
|
+
:class=>{:type=>:string, :desc=>"Hirb helper class which renders"},
|
22
|
+
:max_width=>{:type=>:numeric, :desc=>"Max width of a table"},
|
23
|
+
:vertical=>{:type=>:boolean, :desc=>"Display a vertical table"},
|
24
|
+
} #:nodoc:
|
25
|
+
|
26
|
+
# Adds render and pipe global options
|
27
|
+
# For more about pipe and render options see Pipe and View respectively.
|
28
|
+
# === Toggling Views With the Basic Global Option --render
|
29
|
+
# One of the more important global options is --render. This option toggles the rendering of a command's
|
30
|
+
# output done with View and Hirb[http://github.com/cldwalker/hirb].
|
31
|
+
#
|
32
|
+
# Here's a simple example of toggling Hirb's table view:
|
33
|
+
# # Defined in a library file:
|
34
|
+
# #@options {}
|
35
|
+
# def list(options={})
|
36
|
+
# [1,2,3]
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Using it in irb:
|
40
|
+
# >> list
|
41
|
+
# => [1,2,3]
|
42
|
+
# >> list '-r' # or list --render
|
43
|
+
# +-------+
|
44
|
+
# | value |
|
45
|
+
# +-------+
|
46
|
+
# | 1 |
|
47
|
+
# | 2 |
|
48
|
+
# | 3 |
|
49
|
+
# +-------+
|
50
|
+
# 3 rows in set
|
51
|
+
# => true
|
52
|
+
# == Additional config keys for the main repo config
|
53
|
+
# [:render_options] Hash of render options available to all option commands to be passed to a Hirb view (see View). Since
|
54
|
+
# this merges with default render options, it's possible to override default render options.
|
55
|
+
# [:no_auto_render] When set, turns off commandline auto-rendering of a command's output. Default is false.
|
56
|
+
module ClassRender
|
57
|
+
|
58
|
+
def default_options
|
59
|
+
default_pipe_options.merge(default_render_options.merge(BASIC_OPTIONS))
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_pipe_options
|
63
|
+
@default_pipe_options ||= PIPE_OPTIONS.merge Pipe.pipe_options
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_render_options
|
67
|
+
@default_render_options ||= RENDER_OPTIONS.merge Boson.repo.config[:render_options] || {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_non_render_options(opt)
|
71
|
+
opt.delete_if {|k,v| BASIC_OPTIONS.keys.include?(k) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
extend ClassRender
|
75
|
+
|
76
|
+
module Render
|
77
|
+
def option_parser
|
78
|
+
@option_parser ||= @command.render_options ? OptionParser.new(all_global_options) :
|
79
|
+
self.class.default_option_parser
|
80
|
+
end
|
81
|
+
|
82
|
+
def all_global_options
|
83
|
+
OptionParser.make_mergeable! @command.render_options
|
84
|
+
render_opts = Util.recursive_hash_merge(@command.render_options, Util.deep_copy(self.class.default_render_options))
|
85
|
+
merged_opts = Util.recursive_hash_merge Util.deep_copy(self.class.default_pipe_options), render_opts
|
86
|
+
opts = Util.recursive_hash_merge merged_opts, Util.deep_copy(BASIC_OPTIONS)
|
87
|
+
set_global_option_defaults opts
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_global_option_defaults(opts)
|
91
|
+
if !opts[:fields].key?(:values)
|
92
|
+
if opts[:fields][:default]
|
93
|
+
opts[:fields][:values] = opts[:fields][:default]
|
94
|
+
else
|
95
|
+
if opts[:change_fields] && (changed = opts[:change_fields][:default])
|
96
|
+
opts[:fields][:values] = changed.is_a?(Array) ? changed : changed.values
|
97
|
+
end
|
98
|
+
opts[:fields][:values] ||= opts[:headers][:default].keys if opts[:headers] && opts[:headers][:default]
|
99
|
+
end
|
100
|
+
opts[:fields][:enum] = false if opts[:fields][:values] && !opts[:fields].key?(:enum)
|
101
|
+
end
|
102
|
+
if opts[:fields][:values]
|
103
|
+
opts[:sort][:values] ||= opts[:fields][:values]
|
104
|
+
opts[:query][:keys] ||= opts[:fields][:values]
|
105
|
+
opts[:query][:default_keys] ||= "*"
|
106
|
+
end
|
107
|
+
opts
|
108
|
+
end
|
109
|
+
end
|
110
|
+
include Render
|
111
|
+
end
|
112
|
+
|
113
|
+
module Scientist
|
114
|
+
# * Before a method returns its value, it pipes its return value through pipe commands if pipe options are specified. See Pipe.
|
115
|
+
# * Methods can have any number of optional views associated with them via global render options (see View). Views can be toggled
|
116
|
+
# on/off with the global option --render (see OptionCommand).
|
117
|
+
module Render
|
118
|
+
attr_accessor :rendered, :render
|
119
|
+
|
120
|
+
def after_parse
|
121
|
+
(@global_options[:delete_options] || []).map {|e|
|
122
|
+
@global_options.keys.map {|k| k.to_s }.grep(/^#{e}/)
|
123
|
+
}.flatten.each {|e| @global_options.delete(e.to_sym) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def process_result(result)
|
127
|
+
if (@rendered = can_render?)
|
128
|
+
if @global_options.key?(:class) || @global_options.key?(:method)
|
129
|
+
result = Pipe.scientist_process(result, @global_options, :config=>@command.config, :args=>@args, :options=>@current_options)
|
130
|
+
end
|
131
|
+
View.render(result, OptionCommand.delete_non_render_options(@global_options.dup), false)
|
132
|
+
else
|
133
|
+
Pipe.scientist_process(result, @global_options, :config=>@command.config, :args=>@args, :options=>@current_options)
|
134
|
+
end
|
135
|
+
rescue StandardError
|
136
|
+
raise Scientist::Error, $!.message, $!.backtrace
|
137
|
+
end
|
138
|
+
|
139
|
+
def can_render?
|
140
|
+
render.nil? ? command_renders? : render
|
141
|
+
end
|
142
|
+
|
143
|
+
def command_renders?
|
144
|
+
(!!@command.render_options ^ @global_options[:render]) && !Pipe.any_no_render_pipes?(@global_options)
|
145
|
+
end
|
146
|
+
|
147
|
+
def run_pretend_option(args)
|
148
|
+
super
|
149
|
+
@rendered = true if @global_options[:pretend]
|
150
|
+
end
|
151
|
+
|
152
|
+
def help_options
|
153
|
+
super.tap do |opts|
|
154
|
+
if @global_options[:usage_options]
|
155
|
+
opts << "--render_options=#{@global_options[:usage_options]}"
|
156
|
+
end
|
157
|
+
opts
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
extend Render
|
162
|
+
end
|
163
|
+
|
164
|
+
class Command
|
165
|
+
module ScienceClassMethods
|
166
|
+
attr_accessor :all_option_commands
|
167
|
+
|
168
|
+
def create(name, library)
|
169
|
+
super.tap do |obj|
|
170
|
+
if @all_option_commands && !%w{get method_missing}.include?(name)
|
171
|
+
obj.make_option_command(library)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
extend ScienceClassMethods
|
177
|
+
|
178
|
+
module Science
|
179
|
+
# Option parser for command as defined by @render_options.
|
180
|
+
def render_option_parser
|
181
|
+
option_command? ? Boson::Scientist.option_command(self).option_parser : nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def make_option_command(lib=library)
|
185
|
+
@option_command = true
|
186
|
+
@args = [['*args']] unless args(lib) || arg_size
|
187
|
+
end
|
188
|
+
|
189
|
+
def option_command?
|
190
|
+
super || render_options
|
191
|
+
end
|
192
|
+
end
|
193
|
+
include Science
|
194
|
+
end
|
195
|
+
|
196
|
+
# [*:render_options*] Hash of rendering options to pass to OptionParser. If the key :output_class is passed,
|
197
|
+
# that class's Hirb config will serve as defaults for this rendering hash.
|
198
|
+
class Command
|
199
|
+
attr_accessor :render_options
|
200
|
+
|
201
|
+
module Science
|
202
|
+
def after_initialize(hash)
|
203
|
+
if hash[:render_options] && (@render_options = hash.delete(:render_options))[:output_class]
|
204
|
+
@render_options = Util.recursive_hash_merge View.class_config(@render_options[:output_class]), @render_options
|
205
|
+
end
|
206
|
+
super
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if defined? BinRunner
|
212
|
+
class BinRunner < BareRunner
|
213
|
+
GLOBAL_OPTIONS.update(
|
214
|
+
option_commands: {
|
215
|
+
:type=>:boolean,
|
216
|
+
:desc=>"Toggles on all commands to be defined as option commands"
|
217
|
+
},
|
218
|
+
render: {:type=>:boolean,
|
219
|
+
:desc=>"Renders a Hirb view from result of command without options"}
|
220
|
+
)
|
221
|
+
|
222
|
+
# [:render] Toggles the auto-rendering done for commands that don't have views. Doesn't affect commands that already have views.
|
223
|
+
# Default is false. Also see Auto Rendering section below.
|
224
|
+
#
|
225
|
+
# ==== Auto Rendering
|
226
|
+
# Commands that don't have views (defined via render_options) have their return value auto-rendered as a view as follows:
|
227
|
+
# * nil,false and true aren't rendered
|
228
|
+
# * arrays are rendered with Hirb's tables
|
229
|
+
# * non-arrays are printed with inspect()
|
230
|
+
# * Any of these cases can be toggled to render/not render with the global option :render
|
231
|
+
# To turn off auto-rendering by default, add a :no_auto_render: true entry to the main config.
|
232
|
+
module Science
|
233
|
+
def init
|
234
|
+
Command.all_option_commands = true if @options[:option_commands]
|
235
|
+
super
|
236
|
+
end
|
237
|
+
|
238
|
+
def render_output(output)
|
239
|
+
if (!Scientist.rendered && !View.silent_object?(output)) ^ @options[:render] ^
|
240
|
+
Boson.repo.config[:no_auto_render]
|
241
|
+
opts = output.is_a?(String) ? {:method=>'puts'} :
|
242
|
+
{:inspect=>!output.is_a?(Array) || (Scientist.global_options || {})[:render] }
|
243
|
+
View.render output, opts
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def allowed_argument_error?(err, cmd, args)
|
248
|
+
err.class == OptionCommand::CommandArgumentError || super
|
249
|
+
end
|
250
|
+
|
251
|
+
def execute_command(cmd, args)
|
252
|
+
render_output super
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class <<self
|
257
|
+
include Science
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Additional method attributes:
|
263
|
+
# * render_options: Hash to define an OptionParser object for a command's local/global render options (see View).
|
264
|
+
class MethodInspector
|
265
|
+
METHODS << :render_options
|
266
|
+
METHOD_CLASSES[:render_options] = Hash
|
267
|
+
SCRAPEABLE_METHODS << :render_options
|
268
|
+
end
|
269
|
+
|
270
|
+
module CommentInspector
|
271
|
+
EVAL_ATTRIBUTES << :render_options
|
272
|
+
end
|
273
|
+
end
|
data/lib/boson/view.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'hirb'
|
2
|
+
require 'boson/more_option_parser'
|
3
|
+
|
4
|
+
module Boson
|
5
|
+
# This module generates views for a command by handing it to {Hirb}[http://tagaholic.me/hirb/]. Since Hirb can be customized
|
6
|
+
# to generate any view, commands can have any views associated with them!
|
7
|
+
#
|
8
|
+
# === Views with Render Options
|
9
|
+
# To pass rendering options to a Hirb helper as command options, a command has to define the options with
|
10
|
+
# the render_options method attribute:
|
11
|
+
#
|
12
|
+
# # @render_options :fields=>[:a,:b]
|
13
|
+
# def list(options={})
|
14
|
+
# [{:a=>1, :b=>2}, {:a=>10,:b=>11}]
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # To see that the render_options method attribute actually passes the :fields option by default:
|
18
|
+
# >> list '-p' # or list '--pretend'
|
19
|
+
# Arguments: []
|
20
|
+
# Global options: {:pretend=>true, :fields=>[:a, :b]}
|
21
|
+
#
|
22
|
+
# >> list
|
23
|
+
# +----+----+
|
24
|
+
# | a | b |
|
25
|
+
# +----+----+
|
26
|
+
# | 1 | 2 |
|
27
|
+
# | 10 | 11 |
|
28
|
+
# +----+----+
|
29
|
+
# 2 rows in set
|
30
|
+
#
|
31
|
+
# # To create a vertical table, we can pass --vertical, one of the default global render options.
|
32
|
+
# >> list '-V' # or list '--vertical'
|
33
|
+
# *** 1. row ***
|
34
|
+
# a: 1
|
35
|
+
# b: 2
|
36
|
+
# ...
|
37
|
+
#
|
38
|
+
# # To get the original return value use the global option --render
|
39
|
+
# >> list '-r' # or list '--render'
|
40
|
+
# => [{:a=>1, :b=>2}, {:a=>10,:b=>11}]
|
41
|
+
#
|
42
|
+
# === Boson and Hirb
|
43
|
+
# Since Boson uses {Hirb's auto table helper}[http://tagaholic.me/hirb/doc/classes/Hirb/Helpers/AutoTable.html]
|
44
|
+
# by default, you may want to read up on its many options. To use any of them in commands, define them locally
|
45
|
+
# with render_options or globally by adding them under the :render_options key of the main config.
|
46
|
+
# What if you want to use your own helper class? No problem. Simply pass it with the global :class option.
|
47
|
+
#
|
48
|
+
# When using the default helper, one of the most important options to define is :fields. Aside from controlling what fields
|
49
|
+
# are displayed, it's used to set :values option attributes for related options i.e. :sort and :query. This provides handy option
|
50
|
+
# value aliasing via OptionParser. If you don't set :fields, Boson will try to set its :values with field-related options i.e.
|
51
|
+
# :change_fields, :filters and :headers.
|
52
|
+
module View
|
53
|
+
extend self
|
54
|
+
|
55
|
+
# Enables hirb and reads a config file from the main repo's config/hirb.yml.
|
56
|
+
def enable
|
57
|
+
unless @enabled
|
58
|
+
Hirb::View.enable(:config_file=>File.join(Boson.repo.config_dir, 'hirb.yml'))
|
59
|
+
Hirb::Helpers::Table.filter_any = true
|
60
|
+
end
|
61
|
+
@enabled = true
|
62
|
+
end
|
63
|
+
|
64
|
+
# Renders any object via Hirb. Options are passed directly to
|
65
|
+
# {Hirb::Console.render_output}[http://tagaholic.me/hirb/doc/classes/Hirb/Console.html#M000011].
|
66
|
+
def render(object, options={}, return_obj=false)
|
67
|
+
if options[:inspect]
|
68
|
+
puts(object.inspect)
|
69
|
+
else
|
70
|
+
render_object(object, options, return_obj) unless silent_object?(object)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#:stopdoc:
|
75
|
+
def class_config(klass)
|
76
|
+
opts = (Hirb::View.formatter_config[klass] || {}).dup
|
77
|
+
opts.delete(:ancestor)
|
78
|
+
opts.merge!((opts.delete(:options) || {}).dup)
|
79
|
+
OptionParser.make_mergeable!(opts)
|
80
|
+
opts
|
81
|
+
end
|
82
|
+
|
83
|
+
def toggle_pager
|
84
|
+
Hirb::View.toggle_pager
|
85
|
+
end
|
86
|
+
|
87
|
+
def silent_object?(obj)
|
88
|
+
[nil,false,true].include?(obj)
|
89
|
+
end
|
90
|
+
|
91
|
+
def render_object(object, options={}, return_obj=false)
|
92
|
+
options[:class] ||= :auto_table
|
93
|
+
render_result = Hirb::Console.render_output(object, options)
|
94
|
+
return_obj ? object : render_result
|
95
|
+
end
|
96
|
+
#:startdoc:
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'boson/view'
|
2
|
+
|
3
|
+
module Boson
|
4
|
+
if defined? BinRunner
|
5
|
+
class BinRunner
|
6
|
+
module Viewable
|
7
|
+
def print_usage_header
|
8
|
+
super
|
9
|
+
puts "GLOBAL OPTIONS"
|
10
|
+
View.enable
|
11
|
+
end
|
12
|
+
end
|
13
|
+
extend Viewable
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BareRunner
|
18
|
+
module Viewable
|
19
|
+
def init
|
20
|
+
View.enable
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
extend Viewable
|
25
|
+
end
|
26
|
+
|
27
|
+
class OptionParser
|
28
|
+
module Viewable
|
29
|
+
def get_fields_and_options(fields, options)
|
30
|
+
(fields << :default).uniq! if options.delete(:local) || options[:fields] == '*'
|
31
|
+
fields, opts = super(fields, options)
|
32
|
+
fields.delete(:default) if fields.include?(:default) && opts.all? {|e| e[:default].nil? }
|
33
|
+
[fields, opts]
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_render_options #:nodoc:
|
37
|
+
{:header_filter=>:capitalize, :description=>false, :filter_any=>true,
|
38
|
+
:filter_classes=>{Array=>[:join, ',']}, :hide_empty=>true}
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_table(fields, arr, options)
|
42
|
+
options = default_render_options.merge(:fields=>fields).merge(options)
|
43
|
+
View.render arr, options
|
44
|
+
end
|
45
|
+
end
|
46
|
+
include Viewable
|
47
|
+
end
|
48
|
+
end
|
data/test/alias_test.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Add library_loaded? and with_config
|
2
|
+
describe "Manager" do
|
3
|
+
def load_library(hash)
|
4
|
+
new_attributes = {:name=>hash[:name], :commands=>[], :created_dependencies=>[], :loaded=>true}
|
5
|
+
[:module, :commands].each {|e| new_attributes[e] = hash.delete(e) if hash[e] }
|
6
|
+
Manager.expects(:call_load_action).returns(Library.new(new_attributes))
|
7
|
+
Manager.load([hash[:name]])
|
8
|
+
end
|
9
|
+
|
10
|
+
before { reset_boson }
|
11
|
+
|
12
|
+
describe "command aliases" do
|
13
|
+
before { eval %[module ::Aquateen; def frylock; end; end] }
|
14
|
+
after { Object.send(:remove_const, "Aquateen") }
|
15
|
+
|
16
|
+
it "created with command specific config" do
|
17
|
+
with_config(:command_aliases=>{'frylock'=>'fr'}) do
|
18
|
+
Manager.expects(:create_instance_aliases).with({"Aquateen"=>{"frylock"=>"fr"}})
|
19
|
+
load_library :name=>'aquateen', :commands=>['frylock'], :module=>Aquateen
|
20
|
+
library_loaded? 'aquateen'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "created with config command_aliases" do
|
25
|
+
with_config(:command_aliases=>{"frylock"=>"fr"}) do
|
26
|
+
Manager.expects(:create_instance_aliases).with({"Aquateen"=>{"frylock"=>"fr"}})
|
27
|
+
load_library :name=>'aquateen', :commands=>['frylock'], :module=>Aquateen
|
28
|
+
library_loaded? 'aquateen'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "not created and warns for commands with no module" do
|
33
|
+
with_config(:command_aliases=>{'frylock'=>'fr'}) do
|
34
|
+
capture_stderr {
|
35
|
+
load_library(:name=>'aquateen', :commands=>['frylock'])
|
36
|
+
}.should =~ /No aliases/
|
37
|
+
library_loaded? 'aquateen'
|
38
|
+
Aquateen.method_defined?(:fr).should == false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "Loader" do
|
45
|
+
describe "load" do
|
46
|
+
before { reset }
|
47
|
+
it "loads a library and creates its class commands" do
|
48
|
+
with_config(:libraries=>{"blah"=>{:class_commands=>{"bling"=>"Blah.bling", "Blah"=>['hmm']}}}) do
|
49
|
+
load :blah, :file_string=>"module Blah; def self.bling; end; def self.hmm; end; end"
|
50
|
+
command_exists? 'bling'
|
51
|
+
command_exists? 'hmm'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|