boson 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gemspec +6 -7
  2. data/.rspec +2 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.rdoc +1 -1
  5. data/README.md +144 -0
  6. data/README.rdoc +2 -2
  7. data/Upgrading.md +23 -0
  8. data/bin/boson +2 -2
  9. data/lib/boson.rb +44 -52
  10. data/lib/boson/bare_runner.rb +83 -0
  11. data/lib/boson/bin_runner.rb +114 -0
  12. data/lib/boson/command.rb +92 -132
  13. data/lib/boson/inspector.rb +49 -48
  14. data/lib/boson/library.rb +71 -120
  15. data/lib/boson/loader.rb +73 -84
  16. data/lib/boson/manager.rb +131 -135
  17. data/lib/boson/method_inspector.rb +112 -0
  18. data/lib/boson/option_command.rb +71 -154
  19. data/lib/boson/option_parser.rb +178 -173
  20. data/lib/boson/options.rb +46 -32
  21. data/lib/boson/runner.rb +58 -66
  22. data/lib/boson/runner_library.rb +31 -0
  23. data/lib/boson/scientist.rb +48 -81
  24. data/lib/boson/util.rb +46 -61
  25. data/lib/boson/version.rb +1 -1
  26. data/test/bin_runner_test.rb +53 -191
  27. data/test/command_test.rb +5 -9
  28. data/test/deps.rip +2 -2
  29. data/test/loader_test.rb +18 -216
  30. data/test/manager_test.rb +69 -79
  31. data/test/method_inspector_test.rb +12 -36
  32. data/test/option_parser_test.rb +45 -32
  33. data/test/runner_library_test.rb +10 -0
  34. data/test/runner_test.rb +158 -28
  35. data/test/scientist_test.rb +9 -147
  36. data/test/test_helper.rb +87 -52
  37. metadata +30 -72
  38. data/deps.rip +0 -2
  39. data/lib/boson/commands.rb +0 -7
  40. data/lib/boson/commands/core.rb +0 -77
  41. data/lib/boson/commands/web_core.rb +0 -153
  42. data/lib/boson/index.rb +0 -48
  43. data/lib/boson/inspectors/argument_inspector.rb +0 -97
  44. data/lib/boson/inspectors/comment_inspector.rb +0 -100
  45. data/lib/boson/inspectors/method_inspector.rb +0 -98
  46. data/lib/boson/libraries/file_library.rb +0 -144
  47. data/lib/boson/libraries/gem_library.rb +0 -30
  48. data/lib/boson/libraries/local_file_library.rb +0 -30
  49. data/lib/boson/libraries/module_library.rb +0 -37
  50. data/lib/boson/libraries/require_library.rb +0 -23
  51. data/lib/boson/namespace.rb +0 -31
  52. data/lib/boson/pipe.rb +0 -147
  53. data/lib/boson/pipes.rb +0 -75
  54. data/lib/boson/repo.rb +0 -107
  55. data/lib/boson/runners/bin_runner.rb +0 -208
  56. data/lib/boson/runners/console_runner.rb +0 -58
  57. data/lib/boson/view.rb +0 -95
  58. data/test/argument_inspector_test.rb +0 -62
  59. data/test/commands_test.rb +0 -22
  60. data/test/comment_inspector_test.rb +0 -126
  61. data/test/file_library_test.rb +0 -42
  62. data/test/pipes_test.rb +0 -65
  63. data/test/repo_index_test.rb +0 -122
  64. data/test/repo_test.rb +0 -23
data/.gemspec CHANGED
@@ -9,17 +9,16 @@ Gem::Specification.new do |s|
9
9
  s.email = "gabriel.horner@gmail.com"
10
10
  s.homepage = "http://tagaholic.me/boson/"
11
11
  s.summary = "A command/task framework similar to rake and thor that opens your ruby universe to the commandline and irb."
12
- s.description = "Boson is a command/task framework with the power to turn any ruby method into a full-fledged executable with options. Some unique features that differentiate it from rake and thor include being usable from irb and the commandline, optional automated views generated by hirb and allowing libraries to be written as plain ruby. For my libraries that use this, see irbfiles. Works with all major ruby versions."
12
+ s.description = "Boson is a modular command/task framework. Thanks to its rich set of plugins, it differentiates itself from rake and thor by being usable from irb and the commandline, having optional automated views generated by hirb and allowing libraries to be written as plain ruby. Works with ruby >= 1.9.2"
13
13
  s.required_rubygems_version = ">= 1.3.6"
14
14
  s.executables = ['boson']
15
- s.add_dependency 'hirb', '>= 0.5.0'
16
- s.add_dependency 'alias', '>= 0.2.2'
17
- s.add_development_dependency 'mocha', '= 0.9.8'
15
+ s.add_development_dependency 'mocha', '~> 0.10.4'
18
16
  s.add_development_dependency 'bacon', '>= 1.1.0'
19
17
  s.add_development_dependency 'mocha-on-bacon'
20
18
  s.add_development_dependency 'bacon-bits'
21
- s.add_development_dependency 'rake'
22
- s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec .travis.yml}
23
- s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
19
+ s.add_development_dependency 'bahia', '>= 0.4.0'
20
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc,md} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec .travis.yml}
21
+ s.files += ['.rspec']
22
+ s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
24
23
  s.license = 'MIT'
25
24
  end
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --default_path test
2
+ --pattern test/**_test.rb
data/.travis.yml CHANGED
@@ -1,4 +1,11 @@
1
1
  before_install: bundle init --gemspec=.gemspec
2
+ script: bacon -q -Ilib -I. test/*_test.rb
2
3
  rvm:
3
4
  - 1.9.2
4
5
  - 1.9.3
6
+ - rbx-19mode
7
+ - jruby-19mode
8
+ matrix:
9
+ # until jruby + bacon issue fixed
10
+ allow_failures:
11
+ - rvm: jruby-19mode
data/CHANGELOG.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = 0.4.0
1
+ == 0.4.0
2
2
  * Add file lock for concurrent processes
3
3
 
4
4
  == 0.3.4
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ ## Description
2
+
3
+ Boson is a modular command/task framework. Thanks to its rich set of plugins,
4
+ it differentiates itself from rake and thor by being usable from irb and the
5
+ commandline, having automated views generated by hirb and allowing libraries to
6
+ be written as plain ruby. Works with on all major rubies for ruby >= 1.9.2
7
+
8
+ ## New Boson
9
+
10
+ Starting with 1.0, boson has changed significantly. Please read [the upgrading
11
+ doc](http://rdoc.info/gems/boson/file/Upgrading.md) if you have an older version
12
+ or if your [reading about boson](http://tagaholic.me/blog.html#gem:name=boson)
13
+ predates 2012.
14
+
15
+ Boson has been rewritten to have a smaller core (no dependencies) with optional
16
+ plugins to hook into its various features. The major focus of 1.0 has been to
17
+ provide an easy way for third-party gems to create their executable and define
18
+ subcommands with options.
19
+
20
+ ## Docs
21
+
22
+ Nicely formatted docs are available
23
+ [here](http://rdoc.info/gems/boson/file/README.md).
24
+
25
+ ## Example Executable
26
+
27
+ For a gem with an executable, bin/cow:
28
+
29
+ ```ruby
30
+ #!/usr/bin/env ruby
31
+ require 'boson/runner'
32
+
33
+ class CowRunner < Boson::Runner
34
+ option :urgent, type: :boolean
35
+ def say(text, options={})
36
+ text.capitalize! if options[:urgent]
37
+ puts text
38
+ end
39
+
40
+ def moo
41
+ puts "MOOOO"
42
+ end
43
+ end
44
+
45
+ CowRunner.start
46
+ ```
47
+
48
+ You can now execute cow with say and moo subcommands:
49
+
50
+ $ cow say hungry
51
+ hungry
52
+ $ cow moo
53
+ MOOOO
54
+ # use say's urgent option
55
+ $ cow say hungry -urgent
56
+ HUNGRY
57
+
58
+ You'll notice that this syntax is powerful and concise and is very similar to
59
+ thor's API. Subcommands map to ruby methods and the class represents the executable.
60
+
61
+ ## Comparison to Thor
62
+
63
+ Since boson and it's rewrite are both heavily inspired by [thor](http://github.com/wycats/thor), it
64
+ makes sense to compare them.
65
+
66
+ First, what I consider pros boson has over thor. Boson
67
+
68
+ * is designed to handle plugins. This means it core parts are extendable by
69
+ modules and core components like commands can have arbitrary metadata
70
+ associated with them.
71
+ * has a rich set of plugins. See [boson-more](http://github.com/cldwalker/boson-more).
72
+ * has commands that are easily testable. Whereas thor has options that automagically
73
+ appear in command methods, boson explicitly passes options to its command
74
+ method as a hash i.e. `MyRunner.new.subcommand(arg, verbose: true)`. This
75
+ also allows commands to just be called as ruby, with no magic to consider.
76
+ * supports custom-user option types i.e. creating a Date option type. See
77
+ Boson::Options.
78
+ * supports custom method decorators i.e. methods like desc that add functionality
79
+ to a command. While boson supports option, options, desc and config out of the box,
80
+ users can create their own.
81
+ * automatically creates usage for your subcommand. With thor you need to
82
+ manually define your usage with desc: `desc "SOME USAGE", "SOME DESCRIPTION"`
83
+ * is lenient about descriptions. Describe commands at your leisure. With thor
84
+ you must define a desc.
85
+ * has a smaller blacklist for command names i.e. just Kernel + Object method
86
+ names. Thor has a bigger
87
+ [blacklist](https://github.com/wycats/thor/blob/a24b6697a37d9bc0c0ea94ef9bf2cdbb33b8abb9/lib/thor/base.rb#L18-19) due to its design.
88
+
89
+ Now for pros thor has over boson. Thor
90
+
91
+ * is widely used and thus has been community QAed thoroughly.
92
+ * supports generators as a major feature.
93
+ * is more stable as its feature set is mostly frozen.
94
+ * is used by rails and thus is guaranteed support for some time.
95
+ * supports ruby 1.8.7.
96
+ * TODO: I'm sure there's more
97
+
98
+ ## Writing Plugins
99
+
100
+ The most common way to write a plugin is to extend one of the many method hooks
101
+ available. Any methods that are defined in an API or APIClassMethods module
102
+ are extendable. For example, if you want to extend what any boson-based
103
+ executable does first, extend BareRunner.start:
104
+
105
+ ```ruby
106
+ module CustomStartUp
107
+ def start(*)
108
+ super
109
+ # additional startup
110
+ end
111
+ end
112
+
113
+ BareRunner.extend CustomStartUp
114
+ ```
115
+
116
+ Notice that `extend` was used to extend a class method. To extend an instance
117
+ method you would use `include`. Also notice that you use `super` in an
118
+ overridden method to call original functionality. If you don't, you're
119
+ possibly overridden existing functionality, which is fine as long as you know
120
+ what you are overriding.
121
+
122
+ For many plugin examples, see
123
+ [boson-more](http://github.com/cldwalker/boson-more).
124
+
125
+ ## Bugs/Issues
126
+
127
+ Please report them [on github](http://github.com/cldwalker/boson/issues).
128
+ If the issue is about upgrading from old boson, please file it in
129
+ [boson-more](http://github.com/cldwalker/boson-more/issues).
130
+
131
+ ## Contributing
132
+ [See here](http://tagaholic.me/contributing.html)
133
+
134
+ ## Motiviation
135
+
136
+ Motivation for the new boson is all the damn executables I'm making.
137
+
138
+ ## Credits
139
+ Boson stands on the shoulders of these people and their ideas:
140
+
141
+ * Contributors: @mirell, @martinos
142
+ * Yehuda Katz for Thor and its awesome option parser (Boson::OptionParser).
143
+ * Daniel Berger for his original work on thor's option parser.
144
+ * Chris Wanstrath for inspiring Boson's libraries with Rip's packages.
data/README.rdoc CHANGED
@@ -9,9 +9,9 @@ libraries to be written as plain ruby. For my libraries that use this, see
9
9
 
10
10
  == UPDATE
11
11
  Boson 0.4.x will be the last 1.8 compatible version.
12
- Boson 0.5 will be a rewrite compatible with only 1.9. Work is going on in {boson2
12
+ Boson 1.0 will be a rewrite compatible with only 1.9. Work is going on in {boson2
13
13
  branch}[https://github.com/cldwalker/boson/tree/boson2]. The goal of boson2 is to have a slimmer
14
- core, move everything else {to plugins}[https://github.com/cldwalker/boson-all]
14
+ core, move everything else {to plugins}[https://github.com/cldwalker/boson-more]
15
15
  and to allow gems to use boson to make executables like thor.
16
16
 
17
17
  == Features
data/Upgrading.md ADDED
@@ -0,0 +1,23 @@
1
+ ## Using Old Boson
2
+
3
+ Any version before 1.0 is considered the old boson. Although I will accept bug
4
+ fixes for it (branched from
5
+ [old_boson](http://github.com/cldwalker/boson/tree/old_boson)), I will *not*
6
+ accept any new features. Since the new boson supports almost all of [boson's
7
+ origin functionality](http://tagaholic.me/blog.html#gem:name=boson) via plugins,
8
+ there is little reason to hang onto this version.
9
+
10
+ ## Using New Boson
11
+
12
+ To enjoy the same experience you've had with the old boson, you'll need to
13
+ also install boson-more and create a ~/.bosonrc:
14
+
15
+ $ gem install boson boson-more
16
+ $ echo "require 'boson/more'" > ~/.bosonrc
17
+
18
+ Your old boson config and libraries should just work. Please file issues with
19
+ your libraries or any upgrade issues at
20
+ [boson-more](http://github.com/cldwalker/boson-more).
21
+
22
+ If you've written custom plugins for the old Boson, you most likely have to
23
+ upgrade to the new API.
data/bin/boson CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path(File.join(File.dirname(__FILE__), "..","lib","boson"))
4
- require 'boson/runners/bin_runner'
3
+ require 'boson'
4
+ require 'boson/bin_runner'
5
5
 
6
6
  Boson::BinRunner.start
data/lib/boson.rb CHANGED
@@ -1,31 +1,49 @@
1
- %w{hirb alias}.each {|e| require e }
2
- %w{runner runners/console_runner repo manager loader inspector library}.each {|e| require "boson/#{e}" }
3
- %w{argument method comment}.each {|e| require "boson/inspectors/#{e}_inspector" }
4
- # order of library subclasses matters
5
- %w{module file gem require local_file}.each {|e| require "boson/libraries/#{e}_library" }
6
- (%w{namespace view command util commands option_parser options} +
7
- %w{index repo_index scientist option_command pipe pipes version}).each {|e| require "boson/#{e}" }
1
+ require 'boson/bare_runner'
2
+ require 'boson/manager'
3
+ require 'boson/loader'
4
+ require 'boson/inspector'
5
+ require 'boson/library'
6
+ require 'boson/method_inspector'
7
+ require 'boson/runner_library'
8
+ require 'boson/command'
9
+ require 'boson/util'
10
+ require 'boson/option_parser'
11
+ require 'boson/options'
12
+ require 'boson/scientist'
13
+ require 'boson/option_command'
14
+ require 'boson/version'
8
15
 
9
- # This module stores the libraries, commands, repos and main object used throughout Boson.
16
+ # This module stores the libraries, commands and the main_object.
10
17
  #
11
18
  # Useful documentation links:
12
- # * Boson::BinRunner - Runs the boson executable
13
- # * Boson::ConsoleRunner - Runs Boson from the ruby console
14
- # * Boson::Repo.config - Explains main config file
15
19
  # * Boson::Library - All about libraries
16
- # * Boson::FileLibrary - Explains creating libraries as files
17
20
  # * Boson::Loader - Explains library module callbacks
18
21
  # * Boson::OptionParser - All about options
19
22
  module Boson
20
- # Module which is extended by Boson.main_object to give it command functionality.
21
- module Universe; include Commands::Namespace; end
22
- NAMESPACE = '.' # Delimits namespace from command
23
23
  extend self
24
+
25
+ # Module which is extended by Boson.main_object to give it command functionality.
26
+ module Universe; end
27
+ # Module under which most library modules are evaluated.
28
+ module Commands; end
29
+
30
+ # Default config
31
+ CONFIG = {libraries: {}, command_aliases: {}, option_underscore_search: true}
32
+
24
33
  # The object which holds and executes all command functionality
25
34
  attr_accessor :main_object
26
- attr_accessor :commands, :libraries
27
35
  alias_method :higgs, :main_object
28
36
 
37
+ attr_accessor :commands, :libraries, :config
38
+ # Prints debugging info when set
39
+ attr_accessor :debug
40
+ # Returns true if commands are being executed from a non-ruby shell i.e. bash
41
+ # Returns nil/false if in a ruby shell i.e. irb.
42
+ attr_accessor :in_shell
43
+ # Returns true if in commandline with verbose flag or if set explicitly.
44
+ # Plugins should use this to display more info.
45
+ attr_accessor :verbose
46
+
29
47
  # Array of loaded Boson::Library objects.
30
48
  def libraries
31
49
  @libraries ||= Array.new
@@ -36,55 +54,29 @@ module Boson
36
54
  @commands ||= Array.new
37
55
  end
38
56
 
39
- # The main required repository which defaults to ~/.boson.
40
- def repo
41
- @repo ||= Repo.new("#{Util.find_home}/.boson")
42
- end
43
-
44
- # An optional local repository which defaults to ./lib/boson or ./.boson.
45
- def local_repo
46
- @local_repo ||= begin
47
- ignored_dirs = (repo.config[:ignore_directories] || []).map {|e| File.expand_path(e) }
48
- dir = ["lib/boson", ".boson"].find {|e| File.directory?(e) &&
49
- File.expand_path(e) != repo.dir && !ignored_dirs.include?(File.expand_path('.')) }
50
- Repo.new(dir) if dir
51
- end
52
- end
53
-
54
- # The array of loaded repositories containing the main repo and possible local and global repos
55
- def repos
56
- @repos ||= [repo, local_repo, global_repo].compact
57
+ # Global config used by most classes
58
+ def config
59
+ @config ||= CONFIG
57
60
  end
58
61
 
59
- # Optional global repository at /etc/boson
60
- def global_repo
61
- File.exists?('/etc/boson') ? Repo.new('/etc/boson') : nil
62
- end
63
-
64
- def main_object=(value) #:nodoc:
62
+ # Sets main_object and extends it with commands from Universe
63
+ def main_object=(value)
65
64
  @main_object = value.extend(Universe)
66
65
  end
67
66
 
68
- def library(query, attribute='name') #:nodoc:
67
+ # Finds first library that has a value of attribute
68
+ def library(query, attribute='name')
69
69
  libraries.find {|e| e.send(attribute) == query }
70
70
  end
71
71
 
72
- # Start Boson by loading repositories and their configured libraries.
73
- # See ConsoleRunner.start for its options.
74
- def start(options={})
75
- ConsoleRunner.start(options)
76
- end
77
-
78
72
  # Invoke an action on the main object.
79
73
  def invoke(*args, &block)
80
74
  main_object.send(*args, &block)
81
75
  end
82
76
 
83
- # Invoke command string even with namespaces
84
- def full_invoke(cmd, args) #:nodoc:
85
- command, subcommand = cmd.include?(NAMESPACE) ? cmd.split(NAMESPACE, 2) : [cmd, nil]
86
- dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
87
- dispatcher.send(subcommand || command, *args)
77
+ # Similar to invoke but accepts args as an array
78
+ def full_invoke(cmd, args)
79
+ main_object.send(cmd, *args)
88
80
  end
89
81
 
90
82
  # Boolean indicating if the main object can invoke the given method/command.
@@ -0,0 +1,83 @@
1
+ module Boson
2
+ # Base class for runners.
3
+ class BareRunner
4
+ DEFAULT_LIBRARIES = []
5
+ # Default options for parse_args
6
+ GLOBAL_OPTIONS = {
7
+ help: { type: :boolean, desc: "Displays this help message" }
8
+ }
9
+
10
+ module API
11
+ # Loads rc
12
+ def start(*)
13
+ @options ||= {}
14
+ load_rc
15
+ end
16
+
17
+ # Default libraries loaded by init
18
+ def default_libraries
19
+ DEFAULT_LIBRARIES
20
+ end
21
+
22
+ def all_libraries
23
+ default_libraries
24
+ end
25
+
26
+ # Loads default libraries
27
+ def init
28
+ Manager.load default_libraries, load_options
29
+ end
30
+
31
+ # Wrapper around abort
32
+ def abort_with(message)
33
+ abort message
34
+ end
35
+ end
36
+
37
+ class<<self
38
+ include API
39
+
40
+ # Executes a command and handles invalid args
41
+ def execute_command(cmd, args)
42
+ Boson.full_invoke(cmd, args)
43
+ rescue ArgumentError
44
+ raise if !allowed_argument_error?($!, cmd, args)
45
+ abort_with "'#{cmd}' was called incorrectly.\nUsage: " + Command.usage(cmd)
46
+ rescue NoMethodError => err
47
+ raise if !err.backtrace.first.include?('`full_invoke')
48
+ no_command_error cmd
49
+ end
50
+
51
+ # Use to abort when no command found
52
+ def no_command_error(cmd)
53
+ abort_with %[Could not find command "#{cmd}"]
54
+ end
55
+
56
+ # Determines if a user command argument error or an internal Boson one
57
+ def allowed_argument_error?(err, cmd, args)
58
+ (err.message[/wrong number of arguments/] &&
59
+ (cmd_obj = Command.find(cmd)) && cmd_obj.arg_size != args.size)
60
+ end
61
+
62
+ private
63
+ def parse_args(args)
64
+ @option_parser = OptionParser.new(self::GLOBAL_OPTIONS)
65
+ options = @option_parser.parse(args.dup, :opts_before_args=>true)
66
+ new_args = @option_parser.non_opts
67
+ [new_args[0], options, new_args[1..-1]]
68
+ end
69
+
70
+ def load_rc
71
+ rc = ENV['BOSONRC'] || '~/.bosonrc'
72
+ load(rc) if !rc.empty? && File.exists?(File.expand_path(rc))
73
+ rescue StandardError, SyntaxError, LoadError => err
74
+ warn "Error while loading #{rc}:\n"+
75
+ "#{err.class}: #{err.message}\n #{err.backtrace.join("\n ")}"
76
+ end
77
+
78
+ def load_options
79
+ {:verbose=>@options[:verbose]}
80
+ end
81
+ end
82
+ end
83
+ end