boson-more 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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/.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'rubygems' unless Object.const_defined?(:Gem)
|
3
|
+
require File.dirname(__FILE__) + "/lib/boson/more/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "boson-more"
|
7
|
+
s.version = Boson::More::VERSION
|
8
|
+
s.authors = ["Gabriel Horner"]
|
9
|
+
s.email = "gabriel.horner@gmail.com"
|
10
|
+
s.homepage = "http://github.com/cldwalker/boson-more"
|
11
|
+
s.summary = "boson2 plugins"
|
12
|
+
s.description = "A collection of boson plugins that can be mixed and matched"
|
13
|
+
s.required_rubygems_version = ">= 1.3.6"
|
14
|
+
s.add_dependency 'boson', '>= 1.0.0'
|
15
|
+
s.add_development_dependency 'mocha'
|
16
|
+
s.add_development_dependency 'bacon', '>= 1.1.0'
|
17
|
+
s.add_development_dependency 'mocha-on-bacon'
|
18
|
+
s.add_development_dependency 'bacon-bits'
|
19
|
+
s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec}
|
20
|
+
s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
21
|
+
s.license = 'MIT'
|
22
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT LICENSE
|
2
|
+
|
3
|
+
Copyright (c) 2010 Gabriel Horner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
## Description
|
2
|
+
|
3
|
+
boson-more is a collection of useful boson plugins. These plugins add features
|
4
|
+
such as allowing boson to be used from irb, optional automated views generated
|
5
|
+
by hirb and allowing libraries to be written as plain ruby. For my libraries
|
6
|
+
that use this, see [irbfiles](http://github.com/cldwalker/irbfiles).
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
To use all of the plugins, add this to ~/.bosonrc:
|
11
|
+
|
12
|
+
require 'boson/more'
|
13
|
+
|
14
|
+
To only use certain plugins, require those plugins in ~/.bosonrc.
|
15
|
+
|
16
|
+
When using all plugins, you can use boson in irb/ripl by dropping this in ~/.irbrc:
|
17
|
+
|
18
|
+
require 'boson'
|
19
|
+
Boson.start
|
20
|
+
|
21
|
+
|
22
|
+
## List of Plugins
|
23
|
+
|
24
|
+
* boson/alias - Adds aliasing to commands. Requires alias gem.
|
25
|
+
* boson/console - Allows for boson to be used in a ruby console.
|
26
|
+
* boson/libraries - Adds several libraries. Necessary for using old boson.
|
27
|
+
* boson/more\_commands - Adds set of default commands
|
28
|
+
* boson/more\_inspector - Adds commenting-based command configuration.
|
29
|
+
* boson/more\_manager - Adds ability for libraries to have dependencies.
|
30
|
+
* boson/more\_method\_inspector - Adds ability to scrape argument names and
|
31
|
+
default values from commands.
|
32
|
+
* boson/more\_scientist - Adds a few global options to option commands.
|
33
|
+
* boson/runner\_options - Adds additional options to the boson executable.
|
34
|
+
* boson/science - Adds pipes and several global options to option commands.
|
35
|
+
Requires hirb.
|
36
|
+
* boson/namespacer - Adds namespaces to commands.
|
37
|
+
* boson/save - Allows libraries and commands to be saved and loaded quickly.
|
38
|
+
Necessary for using old boson.
|
39
|
+
* boson/viewable - Adds rendering to commands. Requires hirb.
|
40
|
+
|
41
|
+
## Features
|
42
|
+
|
43
|
+
When using all these plugins, they have the following features:
|
44
|
+
|
45
|
+
* Simple organization: Commands are just methods on an object (default is main)
|
46
|
+
and command libraries are just modules.
|
47
|
+
* Commands are accessible from the commandline (Boson::BinRunner) or irb
|
48
|
+
(Boson::ConsoleRunner).
|
49
|
+
* Libraries
|
50
|
+
* can be written in plain ruby which allows for easy testing and use
|
51
|
+
independent of boson (Boson::FileLibrary).
|
52
|
+
* can exist locally as a Bosonfile (Boson::LocalFileLibrary) and under
|
53
|
+
lib/boson/commands or .boson/commands.
|
54
|
+
* can be made from gems (Boson::GemLibrary) or any require-able file
|
55
|
+
(Boson::RequireLibrary).
|
56
|
+
* are encouraged to be shared. Libraries can be installed with a given url.
|
57
|
+
Users can customize any aspect of a third-party library without modifying it
|
58
|
+
(Boson::Library).
|
59
|
+
* Commands
|
60
|
+
* can have any number of local and global options (Boson::OptionCommand).
|
61
|
+
Options are defined with Boson::OptionParser.
|
62
|
+
* can have any view associated to it (via Hirb) without adding view code to
|
63
|
+
the command's method. These views can be toggled on and manipulated via
|
64
|
+
global render options (Boson::View and Boson::OptionCommand).
|
65
|
+
* can pipe their return value into custom pipe options (Boson::Pipe).
|
66
|
+
* has default pipe options to search and sort an array of any objects
|
67
|
+
(Boson::Pipes).
|
68
|
+
* Option parser (Boson::OptionParser)
|
69
|
+
* provides option types that map to objects i.e. :array type creates Array
|
70
|
+
objects.
|
71
|
+
* come with 5 default option types: boolean, array, string, hash and numeric.
|
72
|
+
* can have have custom option types defined by users (Boson::Options).
|
73
|
+
* Comes with default commands to load, search, list and install commands and
|
74
|
+
libraries (Boson::Commands::Core).
|
75
|
+
* Namespaces are optional and when used are methods which allow for
|
76
|
+
method\_missing magic.
|
77
|
+
|
78
|
+
|
79
|
+
## Bugs/Issues
|
80
|
+
|
81
|
+
Please report them [on github](http://github.com/cldwalker/boson-more/issues).
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
[See here](http://tagaholic.me/contributing.html)
|
86
|
+
|
87
|
+
## Links
|
88
|
+
|
89
|
+
* http://tagaholic.me/2009/10/14/boson-command-your-ruby-universe.html
|
90
|
+
* http://tagaholic.me/2009/10/15/boson-and-hirb-interactions.html
|
91
|
+
* http://tagaholic.me/2009/10/19/how-boson-enhances-your-irb-experience.html
|
92
|
+
## TODO
|
93
|
+
|
94
|
+
* Actually have working tests
|
95
|
+
* Clean up plugins and move their files into separates directories
|
96
|
+
* Clean up plugins that unintentionally depend on each other
|
97
|
+
* Clean up docs which are currently strewn across plugins
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
def gemspec
|
5
|
+
@gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Build the gem"
|
9
|
+
task :gem=>:gemspec do
|
10
|
+
sh "gem build .gemspec"
|
11
|
+
FileUtils.mkdir_p 'pkg'
|
12
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Install the gem locally"
|
16
|
+
task :install => :gem do
|
17
|
+
sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Generate the gemspec"
|
21
|
+
task :generate do
|
22
|
+
puts gemspec.to_ruby
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Validate the gemspec"
|
26
|
+
task :gemspec do
|
27
|
+
gemspec.validate
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Run tests'
|
31
|
+
task :test do |t|
|
32
|
+
sh 'bacon -q -Ilib -I. test/*_test.rb'
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
data/deps.rip
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
boson >=1.0.0
|
data/lib/boson/alias.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'alias'
|
2
|
+
|
3
|
+
module Boson
|
4
|
+
class Library
|
5
|
+
# [*:class_commands*] A hash of commands to create. A hash key-pair can map command names to any string of ruby code
|
6
|
+
# that ends with a method call. Or a key-pair can map a class to an array of its class methods
|
7
|
+
# to create commands of the same name. Example:
|
8
|
+
# :class_commands=>{'spy'=>'Bond.spy', 'create'=>'Alias.manager.create',
|
9
|
+
# 'Boson::Util'=>['detect', 'any_const_get']}
|
10
|
+
# [*:no_alias_creation*] Boolean which doesn't create aliases for a library. Useful for libraries that configure command
|
11
|
+
# aliases outside of Boson's control. Default is false.
|
12
|
+
module Alias
|
13
|
+
attr_reader :class_commands, :no_alias_creation
|
14
|
+
end
|
15
|
+
include Alias
|
16
|
+
|
17
|
+
module AliasLoader
|
18
|
+
def load_commands?
|
19
|
+
super || @class_commands
|
20
|
+
end
|
21
|
+
|
22
|
+
def before_load_commands
|
23
|
+
unless @class_commands.nil? || @class_commands.empty? || @method_conflict
|
24
|
+
Boson::Manager.create_class_aliases(@module, @class_commands)
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
include AliasLoader
|
30
|
+
end
|
31
|
+
|
32
|
+
class Manager
|
33
|
+
def self.create_class_aliases(mod, class_commands)
|
34
|
+
class_commands.dup.each {|k,v|
|
35
|
+
if v.is_a?(Array)
|
36
|
+
class_commands.delete(k).each {|e| class_commands[e] = "#{k}.#{e}"}
|
37
|
+
end
|
38
|
+
}
|
39
|
+
Alias.manager.create_aliases(:any_to_instance_method, mod.to_s=>class_commands.invert)
|
40
|
+
end
|
41
|
+
|
42
|
+
module AliasLib
|
43
|
+
def after_create_commands(lib, commands)
|
44
|
+
create_command_aliases(lib, commands) if commands.size > 0 && !lib.no_alias_creation
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_command_aliases(lib, commands)
|
48
|
+
lib.module ? prep_and_create_instance_aliases(commands, lib.module) : check_for_uncreated_aliases(lib, commands)
|
49
|
+
end
|
50
|
+
|
51
|
+
def prep_and_create_instance_aliases(commands, lib_module)
|
52
|
+
aliases_hash = {}
|
53
|
+
select_commands = Boson.commands.select {|e| commands.include?(e.name)}
|
54
|
+
select_commands.each do |e|
|
55
|
+
if e.alias
|
56
|
+
aliases_hash[lib_module.to_s] ||= {}
|
57
|
+
aliases_hash[lib_module.to_s][e.name] = e.alias
|
58
|
+
end
|
59
|
+
end
|
60
|
+
create_instance_aliases(aliases_hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_instance_aliases(aliases_hash)
|
64
|
+
Alias.manager.create_aliases(:instance_method, aliases_hash)
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_for_uncreated_aliases(lib, commands)
|
68
|
+
if (found_commands = Boson.commands.select {|e| commands.include?(e.name)}) && found_commands.find {|e| e.alias }
|
69
|
+
$stderr.puts "No aliases created for library #{lib.name} because it has no module"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
include AliasLib
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Extracts arguments and their default values from methods either by
|
2
|
+
# by scraping a method's text or with method_added and brute force eval (thanks to
|
3
|
+
# {eigenclass}[http://eigenclass.org/hiki/method+arguments+via+introspection]).
|
4
|
+
module Boson::ArgumentInspector
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Max number of arguments extracted per method with scrape_with_eval
|
8
|
+
MAX_ARGS = 10
|
9
|
+
# Scrapes non-private methods for argument names and default values.
|
10
|
+
# Returns arguments as array of argument arrays with optional default value as a second element.
|
11
|
+
# ====Examples:
|
12
|
+
# def meth1(arg1, arg2='val', options={}) -> [['arg1'], ['arg2', 'val'], ['options', {}]]
|
13
|
+
# def meth2(*args) -> [['*args']]
|
14
|
+
def scrape_with_eval(meth, klass, object)
|
15
|
+
unless %w[initialize].include?(meth.to_s)
|
16
|
+
return if class << object; private_instance_methods(true).map {|e| e.to_s } end.include?(meth.to_s)
|
17
|
+
end
|
18
|
+
params, values, arity, num_args = trace_method_args(meth, klass, object)
|
19
|
+
return if local_variables == params # nothing new found
|
20
|
+
format_arguments(params, values, arity, num_args)
|
21
|
+
rescue Exception
|
22
|
+
print_debug_message(klass, meth) if Boson.debug
|
23
|
+
ensure
|
24
|
+
set_trace_func(nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
def print_debug_message(klass, meth) #:nodoc:
|
28
|
+
warn "DEBUG: Error while scraping arguments from #{klass.to_s[/\w+$/]}##{meth}: #{$!.message}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# process params + values to return array of argument arrays
|
32
|
+
def format_arguments(params, values, arity, num_args) #:nodoc:
|
33
|
+
params ||= []
|
34
|
+
params = params[0,num_args]
|
35
|
+
params.inject([[], 0]) do |(a, i), x|
|
36
|
+
if Array === values[i]
|
37
|
+
[a << ["*#{x}"], i+1]
|
38
|
+
else
|
39
|
+
if arity < 0 && i >= arity.abs - 1
|
40
|
+
[a << [x.to_s, values[i]], i + 1]
|
41
|
+
else
|
42
|
+
[a << [x.to_s], i+1]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def trace_method_args(meth, klass, object) #:nodoc:
|
49
|
+
file = line = params = values = nil
|
50
|
+
arity = klass.instance_method(meth).arity
|
51
|
+
set_trace_func lambda{|event, file, line, id, binding, classname|
|
52
|
+
begin
|
53
|
+
if event[/call/] && classname == klass && id == meth
|
54
|
+
params = eval("local_variables", binding)
|
55
|
+
values = eval("local_variables.map{|x| eval(x.to_s)}", binding)
|
56
|
+
throw :done
|
57
|
+
end
|
58
|
+
rescue Exception
|
59
|
+
print_debug_message(klass, meth) if Boson.debug
|
60
|
+
end
|
61
|
+
}
|
62
|
+
if arity >= 0
|
63
|
+
num_args = arity
|
64
|
+
catch(:done){ object.send(meth, *(0...arity)) }
|
65
|
+
else
|
66
|
+
num_args = 0
|
67
|
+
# determine number of args (including splat & block)
|
68
|
+
MAX_ARGS.downto(arity.abs - 1) do |i|
|
69
|
+
catch(:done) do
|
70
|
+
begin
|
71
|
+
object.send(meth, *(0...i))
|
72
|
+
rescue Exception
|
73
|
+
end
|
74
|
+
end
|
75
|
+
# all nils if there's no splat and we gave too many args
|
76
|
+
next if !values || values.compact.empty?
|
77
|
+
k = nil
|
78
|
+
values.each_with_index{|x,j| break (k = j) if Array === x}
|
79
|
+
num_args = k ? k+1 : i
|
80
|
+
break
|
81
|
+
end
|
82
|
+
args = (0...arity.abs-1).to_a
|
83
|
+
catch(:done) do
|
84
|
+
args.empty? ? object.send(meth) : object.send(meth, *args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
set_trace_func(nil)
|
88
|
+
return [params, values, arity, num_args]
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Boson::Commands::Core #:nodoc:
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def config
|
5
|
+
command_attributes = Boson::Command::ATTRIBUTES + [:usage, :full_name, :render_options]
|
6
|
+
library_attributes = Boson::Library::ATTRIBUTES + [:library_type]
|
7
|
+
|
8
|
+
commands = {
|
9
|
+
'usage'=>{:desc=>"Print a command's usage", :options=>{
|
10
|
+
:verbose=>{:desc=>"Display global options", :type=>:boolean},
|
11
|
+
:render_options=>{:desc=>"Render options for option tables", :default=>{},
|
12
|
+
:keys=>[:vertical, :fields, :hide_empty]} } },
|
13
|
+
'commands'=>{
|
14
|
+
:desc=>"List or search commands. Query must come before any options.", :default_option=>'query',
|
15
|
+
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
|
16
|
+
:local=>{:type=>:boolean, :desc=>"Local commands only" } },
|
17
|
+
:render_options=>{
|
18
|
+
[:headers,:H]=>{:default=>{:desc=>'description'}},
|
19
|
+
:query=>{:keys=>command_attributes, :default_keys=>'full_name'},
|
20
|
+
:fields=>{:default=>[:full_name, :lib, :alias, :usage, :desc], :values=>command_attributes, :enum=>false},
|
21
|
+
:filters=>{:default=>{:render_options=>:inspect, :options=>:inspect, :args=>:inspect, :config=>:inspect}}
|
22
|
+
}
|
23
|
+
},
|
24
|
+
'libraries'=>{
|
25
|
+
:desc=>"List or search libraries. Query must come before any options.", :default_option=>'query',
|
26
|
+
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
|
27
|
+
:local=>{:type=>:boolean, :desc=>"Local libraries only" } },
|
28
|
+
:render_options=>{
|
29
|
+
:query=>{:keys=>library_attributes, :default_keys=>'name'},
|
30
|
+
:fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes, :enum=>false},
|
31
|
+
:filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}, :desc=>"Filters to apply to library fields" }}
|
32
|
+
},
|
33
|
+
'load_library'=>{:desc=>"Load a library", :options=>{[:verbose,:V]=>true}}
|
34
|
+
}
|
35
|
+
|
36
|
+
{:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
|
37
|
+
end
|
38
|
+
|
39
|
+
def commands(options={})
|
40
|
+
cmds = options[:index] ? (Boson::Index.read || true) && Boson::Index.commands : Boson.commands
|
41
|
+
options[:local] ? cmds.select {|e| e.library && e.library.local? } : cmds
|
42
|
+
end
|
43
|
+
|
44
|
+
def libraries(options={})
|
45
|
+
libs = options[:index] ? (Boson::Index.read || true) && Boson::Index.libraries : Boson.libraries
|
46
|
+
options[:local] ? libs.select {|e| e.local? } : libs
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_library(library, options={})
|
50
|
+
Boson::Manager.load(library, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def usage(command, options={})
|
54
|
+
puts Boson::Command.usage(command)
|
55
|
+
|
56
|
+
if (cmd = Boson::Command.find(command))
|
57
|
+
if cmd.options && !cmd.options.empty?
|
58
|
+
puts "\nLOCAL OPTIONS"
|
59
|
+
cmd.option_parser.print_usage_table options[:render_options].dup.merge(:local=>true)
|
60
|
+
end
|
61
|
+
if options[:verbose] && cmd.render_option_parser
|
62
|
+
puts "\nGLOBAL OPTIONS"
|
63
|
+
cmd.render_option_parser.print_usage_table options[:render_options].dup
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Boson::Commands::ViewCore
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def config
|
5
|
+
commands = {
|
6
|
+
'render'=>{:desc=>"Render any object using Hirb"},
|
7
|
+
'menu'=>{:desc=>"Provide a menu to multi-select elements from a given array"}
|
8
|
+
}
|
9
|
+
{:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(object, options={})
|
13
|
+
Boson::View.render(object, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def menu(arr, options={}, &block)
|
17
|
+
Hirb::Console.format_output(arr, options.merge(:class=>"Hirb::Menu"), &block)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Boson::Commands::WebCore
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def config #:nodoc:
|
5
|
+
commands = {
|
6
|
+
'get'=>{ :desc=>"Gets the body of a url", :args=>[['url'],['options', {}]]},
|
7
|
+
'post'=>{ :desc=>'Posts to a url', :args=>[['url'],['options', {}]]},
|
8
|
+
'build_url'=>{ :desc=>"Builds a url, escaping the given params", :args=>[['url'],['params']]},
|
9
|
+
'browser'=>{ :desc=>"Opens urls in a browser on a Mac"},
|
10
|
+
'install'=>{ :desc=>"Installs a library by url. Library should then be loaded with load_library.",
|
11
|
+
:args=>[['url'],['options', {}]],
|
12
|
+
:options=> { :name=>{:type=>:string, :desc=>"Library name to save to"},
|
13
|
+
:force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
|
14
|
+
:default=>{:type=>:boolean, :desc=>'Adds library as a default library to main config file'},
|
15
|
+
:module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
|
16
|
+
:method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
{:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Requires libraries only once before defining method with given block
|
24
|
+
def self.def_which_requires(meth, *libs, &block)
|
25
|
+
define_method(meth) do |*args|
|
26
|
+
libs.each {|e| require e }
|
27
|
+
define_method(meth, block).call(*args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def_which_requires(:get, 'net/https') do |*args|
|
32
|
+
url, options = args[0], args[1] || {}
|
33
|
+
url = build_url(url, options[:params]) if options[:params]
|
34
|
+
Get.new(url).request(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def_which_requires(:build_url, 'cgi') do |url, params|
|
38
|
+
url + (url[/\?/] ? '&' : "?") + params.map {|k,v|
|
39
|
+
v = v.is_a?(Array) ? v.join(' ') : v.to_s
|
40
|
+
"#{k}=#{CGI.escape(v)}"
|
41
|
+
}.join("&")
|
42
|
+
end
|
43
|
+
|
44
|
+
def_which_requires(:post, 'uri', 'net/http') do |*args|
|
45
|
+
url, options = args[0], args[1] || {}
|
46
|
+
(res = Net::HTTP.post_form(URI.parse(url), options)) && res.body
|
47
|
+
end
|
48
|
+
|
49
|
+
def install(url, options={}) #:nodoc:
|
50
|
+
options[:name] ||= strip_name_from_url(url)
|
51
|
+
return puts("Please give a library name for this url.") if options[:name].empty?
|
52
|
+
filename = File.join ::Boson.repo.commands_dir, "#{options[:name]}.rb"
|
53
|
+
return puts("Library name #{options[:name]} already exists. Try a different name.") if File.exists?(filename) && !options[:force]
|
54
|
+
|
55
|
+
file_string = get(url) or raise "Unable to fetch url"
|
56
|
+
file_string = "# Originally from #{url}\n"+file_string
|
57
|
+
file_string = wrap_install(file_string, options) if options[:method_wrap] || options[:module_wrap]
|
58
|
+
|
59
|
+
File.open(filename, 'w') {|f| f.write file_string }
|
60
|
+
Boson.repo.update_config {|c| (c[:defaults] ||= []) << options[:name] } if options[:default]
|
61
|
+
puts "Saved to #{filename}."
|
62
|
+
end
|
63
|
+
|
64
|
+
# non-mac users should override this with the launchy gem
|
65
|
+
def browser(*urls)
|
66
|
+
system('open', *urls)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def wrap_install(file_string, options)
|
71
|
+
indent = " "
|
72
|
+
unless (mod_name = ::Boson::Util.camelize(options[:name]))
|
73
|
+
return puts("Can't wrap install with name #{options[:name]}")
|
74
|
+
end
|
75
|
+
|
76
|
+
file_string.gsub!(/(^)/,'\1'+indent)
|
77
|
+
file_string = "def #{options[:name]}\n#{file_string}\nend".gsub(/(^)/,'\1'+indent) if options[:method_wrap]
|
78
|
+
"module #{mod_name}\n#{file_string}\nend"
|
79
|
+
end
|
80
|
+
|
81
|
+
def strip_name_from_url(url)
|
82
|
+
url[/\/([^\/.]+)(\.[a-z]+)?$/, 1].to_s.gsub('-', '_').gsub(/[^a-zA-Z_]/, '')
|
83
|
+
end
|
84
|
+
|
85
|
+
# Used by the get command to make get requests and optionally parse json and yaml.
|
86
|
+
# Ruby 1.8.x is dependent on json gem for parsing json.
|
87
|
+
# See Get.request for options a request can take.
|
88
|
+
class Get
|
89
|
+
FORMAT_HEADERS = {
|
90
|
+
:json=>%w{application/json text/json application/javascript text/javascript},
|
91
|
+
:yaml=>%w{application/x-yaml text/yaml}
|
92
|
+
} #:nodoc:
|
93
|
+
|
94
|
+
def initialize(url, options={})
|
95
|
+
@url, @options = url, options
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the response body string or a parsed data structure. Returns nil if request fails. By default expects response
|
99
|
+
# to be 200.
|
100
|
+
# ==== Options:
|
101
|
+
# [:any_response] Returns body string for any response code. Default is false.
|
102
|
+
# [:parse] Parse the body into either json or yaml. Expects a valid format or if true autodetects one.
|
103
|
+
# Default is false.
|
104
|
+
# [:raise_error] Raises any original errors when parsing or fetching url instead of handling errors silently.
|
105
|
+
def request(options={})
|
106
|
+
@options.merge! options
|
107
|
+
body = get_body
|
108
|
+
body && @options[:parse] ? parse_body(body) : body
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
# Returns body string if successful or nil if not.
|
113
|
+
def get_body
|
114
|
+
uri = URI.parse(@url)
|
115
|
+
@response = get_response(uri)
|
116
|
+
(@options[:any_response] || @response.code == '200') ? @response.body : nil
|
117
|
+
rescue
|
118
|
+
@options[:raise_error] ? raise : puts("Error: GET '#{@url}' -> #{$!.class}: #{$!.message}")
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_response(uri)
|
122
|
+
net = Net::HTTP.new(uri.host, uri.port)
|
123
|
+
net.verify_mode = OpenSSL::SSL::VERIFY_NONE if uri.scheme == 'https'
|
124
|
+
net.use_ssl = true if uri.scheme == 'https'
|
125
|
+
net.start {|http| http.request_get(uri.request_uri) }
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns nil if dependencies or parsing fails
|
129
|
+
def parse_body(body)
|
130
|
+
format = determine_format(@options[:parse])
|
131
|
+
case format
|
132
|
+
when :json
|
133
|
+
unless ::Boson::Util.safe_require 'json'
|
134
|
+
return puts("Install the json gem to parse json: sudo gem install json")
|
135
|
+
end
|
136
|
+
JSON.parse body
|
137
|
+
when :yaml
|
138
|
+
YAML::load body
|
139
|
+
else
|
140
|
+
puts "Can't parse this format."
|
141
|
+
end
|
142
|
+
rescue
|
143
|
+
@options[:raise_error] ? raise : puts("Error while parsing #{format} response of '#{@url}': #{$!.class}")
|
144
|
+
end
|
145
|
+
|
146
|
+
def determine_format(format)
|
147
|
+
return format.to_sym if %w{json yaml}.include?(format.to_s)
|
148
|
+
return :json if FORMAT_HEADERS[:json].include?(@response.content_type)
|
149
|
+
return :yaml if FORMAT_HEADERS[:yaml].include?(@response.content_type)
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|