vli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vli.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tyler Flint
2
+
3
+ MIT License
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.
@@ -0,0 +1,29 @@
1
+ # Vli
2
+
3
+ Vagrant Like Interface is a library of components extracted from the vagrant project to aid in building command line interfaces. If you've ever used or extended the vagrant project you can appreciate the brilliance in the architecture. Special thanks to the vagrant project: https://github.com/mitchellh/vagrant
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'vli'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install vli
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,10 @@
1
+ require "vli/version"
2
+
3
+ module Vli
4
+ autoload :Action, 'vli/action'
5
+ autoload :Command, 'vli/command'
6
+ autoload :Error, 'vli/error'
7
+ autoload :Registry, 'vli/registry'
8
+ autoload :UI, 'vli/ui'
9
+ autoload :Util, 'vli/util'
10
+ end
@@ -0,0 +1,9 @@
1
+ module Vli
2
+ module Action
3
+ autoload :Builder, 'vli/action/builder'
4
+ autoload :Env, 'vli/action/env'
5
+ autoload :Environment, 'vli/action/environment'
6
+ autoload :Runner, 'vli/action/runner'
7
+ autoload :Warden, 'vli/action/warden'
8
+ end
9
+ end
@@ -0,0 +1,128 @@
1
+ module Vli
2
+ module Action
3
+ # Action builder which provides a nice DSL for building up
4
+ # a middleware sequence for Vli actions. This code is based
5
+ # heavily off of `Rack::Builder` and `ActionDispatch::MiddlewareStack`
6
+ # in Rack and Rails, respectively.
7
+ #
8
+ # Usage
9
+ #
10
+ # Building an action sequence is very easy:
11
+ #
12
+ # app = Vli::Action::Builder.new do
13
+ # use MiddlewareA
14
+ # use MiddlewareB
15
+ # end
16
+ #
17
+ # Vli::Action.run(app)
18
+ #
19
+ class Builder
20
+ # Initializes the builder. An optional block can be passed which
21
+ # will be evaluated in the context of the instance.
22
+ def initialize(&block)
23
+ instance_eval(&block) if block_given?
24
+ end
25
+
26
+ # Returns a mergeable version of the builder. If `use` is called with
27
+ # the return value of this method, then the stack will merge, instead
28
+ # of being treated as a separate single middleware.
29
+ def flatten
30
+ lambda do |env|
31
+ self.call(env)
32
+ end
33
+ end
34
+
35
+ # Adds a middleware class to the middleware stack. Any additional
36
+ # args and a block, if given, are saved and passed to the initializer
37
+ # of the middleware.
38
+ #
39
+ # @param [Class] middleware The middleware class
40
+ def use(middleware, *args, &block)
41
+ # Prepend with a environment setter if args are given
42
+ if !args.empty? && args.first.is_a?(Hash) && middleware != Env::Set
43
+ self.use(Env::Set, args.shift, &block)
44
+ end
45
+
46
+ if middleware.kind_of?(Builder)
47
+ # Merge in the other builder's stack into our own
48
+ self.stack.concat(middleware.stack)
49
+ else
50
+ self.stack << [middleware, args, block]
51
+ end
52
+
53
+ self
54
+ end
55
+
56
+ # Inserts a middleware at the given index or directly before the
57
+ # given middleware object.
58
+ def insert(index, middleware, *args, &block)
59
+ index = self.index(index) unless index.is_a?(Integer)
60
+ stack.insert(index, [middleware, args, block])
61
+ end
62
+
63
+ alias_method :insert_before, :insert
64
+
65
+ # Inserts a middleware after the given index or middleware object.
66
+ def insert_after(index, middleware, *args, &block)
67
+ index = self.index(index) unless index.is_a?(Integer)
68
+ raise "no such middleware to insert after: #{index.inspect}" unless index
69
+ insert(index + 1, middleware, *args, &block)
70
+ end
71
+
72
+ # Replaces the given middlware object or index with the new
73
+ # middleware.
74
+ def replace(index, middleware, *args, &block)
75
+ if index.is_a?(Integer)
76
+ delete(index)
77
+ insert(index, middleware, *args, &block)
78
+ else
79
+ insert_before(index, middleware, *args, &block)
80
+ delete(index)
81
+ end
82
+ end
83
+
84
+ # Deletes the given middleware object or index
85
+ def delete(index)
86
+ index = self.index(index) unless index.is_a?(Integer)
87
+ stack.delete_at(index)
88
+ end
89
+
90
+ # Runs the builder stack with the given environment.
91
+ def call(env)
92
+ to_app(env).call(env)
93
+ end
94
+
95
+ protected
96
+
97
+ # Returns the numeric index for the given middleware object.
98
+ #
99
+ # @param [Object] object The item to find the index for
100
+ # @return [Integer]
101
+ def index(object)
102
+ stack.each_with_index do |item, i|
103
+ return i if item[0] == object
104
+ end
105
+
106
+ nil
107
+ end
108
+
109
+ # Returns the current stack of middlewares. You probably won't
110
+ # need to use this directly, and it's recommended that you don't.
111
+ #
112
+ # @return [Array]
113
+ def stack
114
+ @stack ||= []
115
+ end
116
+
117
+ # Converts the builder stack to a runnable action sequence.
118
+ #
119
+ # @param [Vli::Action::Environment] env The action environment
120
+ # @return [Object] A callable object
121
+ def to_app(env)
122
+ # Wrap the middleware stack with the Warden to provide a consistent
123
+ # and predictable behavior upon exceptions.
124
+ Warden.new(stack.dup, env)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,5 @@
1
+ module Vli
2
+ module Env
3
+ autoload :Set, 'vli/action/env/set'
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Vli
2
+ module Action
3
+ module Env
4
+ # A middleware which just sets up the environment with some
5
+ # options which are passed to it.
6
+ class Set
7
+ def initialize(app, env, options=nil)
8
+ @app = app
9
+ @options = options || {}
10
+ end
11
+
12
+ def call(env)
13
+ # Merge the options that were given to us
14
+ env.merge!(@options)
15
+
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module Vli
2
+ module Action
3
+ # Represents an action environment which is what is passed
4
+ # to the `call` method of each action. This environment contains
5
+ # some helper methods for accessing the environment as well
6
+ # as being a hash, to store any additional options.
7
+ class Environment < Util::HashWithIndifferentAccess
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+ # TODO:
2
+ # * env.lock
3
+
4
+ module Vli
5
+ module Action
6
+ class Runner
7
+ @@reported_interrupt = false
8
+
9
+ def initialize(registry, globals=nil, &block)
10
+ @registry = registry
11
+ @globals = globals || {}
12
+ @lazy_globals = block
13
+ end
14
+
15
+ def run(callable_id, options=nil)
16
+ callable = callable_id
17
+ callable = Builder.new.use(callable_id) if callable_id.kind_of?(Class)
18
+ callable = @registry.get(callable_id) if callable_id.kind_of?(Symbol)
19
+ raise ArgumentError, "Argument to run must be a callable object or registered action." if !callable || !callable.respond_to?(:call)
20
+
21
+ # Create the initial environment with the options given
22
+ environment = Environment.new
23
+ environment.merge!(@globals)
24
+ environment.merge!(@lazy_globals.call) if @lazy_globals
25
+ environment.merge!(options || {})
26
+
27
+ # Run the action chain in a busy block, marking the environment as
28
+ # interrupted if a SIGINT occurs, and exiting cleanly once the
29
+ # chain has been run.
30
+ ui = environment[:ui] if environment.has_key?(:ui)
31
+ int_callback = lambda do
32
+ if environment[:interrupted]
33
+ ui.error exit_immediately_message if ui
34
+ abort
35
+ end
36
+
37
+ ui.warn waiting_cleanup_message if ui && !@@reported_interrupt
38
+ environment[:interrupted] = true
39
+ @@reported_interrupt = true
40
+ end
41
+
42
+ # We place a process lock around every action that is called
43
+ Util::Busy.busy(int_callback) { callable.call(environment) }
44
+ end
45
+
46
+ def exit_immediately_message
47
+ "Exiting immediately, without cleanup!"
48
+ end
49
+
50
+ def waiting_cleanup_message
51
+ "Waiting for cleanup before exiting..."
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,86 @@
1
+ module Vli
2
+ module Action
3
+ # The action warden is a middleware which injects itself between
4
+ # every other middleware, watching for exceptions which are raised
5
+ # and performing proper cleanup on every action by calling the `recover`
6
+ # method. The warden therefore allows middlewares to not worry about
7
+ # exceptional events, and by providing a simple callback, can clean up
8
+ # in any erroneous case.
9
+ #
10
+ # Warden will "just work" behind the scenes, and is not of particular
11
+ # interest except to those who are curious about the internal workings
12
+ # of Vli.
13
+ class Warden
14
+ attr_accessor :actions, :stack
15
+
16
+ def initialize(actions, env)
17
+ @stack = []
18
+ @actions = actions.map { |m| finalize_action(m, env) }
19
+ end
20
+
21
+ def call(env)
22
+ return if @actions.empty?
23
+
24
+ begin
25
+ # Call the next middleware in the sequence, appending to the stack
26
+ # of "recoverable" middlewares in case something goes wrong!
27
+ raise Error::VliInterrupt if env[:interrupted]
28
+ action = @actions.shift
29
+ @stack.unshift(action).first.call(env)
30
+ raise Error::VliInterrupt if env[:interrupted]
31
+ rescue SystemExit
32
+ # This means that an "exit" or "abort" was called. In these cases,
33
+ # we just exit immediately.
34
+ raise
35
+ rescue Exception => e
36
+ env["Vli.error"] = e
37
+
38
+ # Something went horribly wrong. Start the rescue chain then
39
+ # reraise the exception to properly kick us out of limbo here.
40
+ begin_rescue(env)
41
+ raise
42
+ end
43
+ end
44
+
45
+ # Begins the recovery sequence for all middlewares which have run.
46
+ # It does this by calling `recover` (if it exists) on each middleware
47
+ # which has already run, in reverse order.
48
+ def begin_rescue(env)
49
+ @stack.each do |act|
50
+ if act.respond_to?(:recover)
51
+ act.recover(env)
52
+ end
53
+ end
54
+
55
+ # Clear stack so that warden down the middleware chain doesn't
56
+ # rescue again.
57
+ @stack.clear
58
+ end
59
+
60
+ # A somewhat confusing function which simply initializes each
61
+ # middleware properly to call the next middleware in the sequence.
62
+ def finalize_action(action, env)
63
+ klass, args, block = action
64
+
65
+ # Default the arguments to an empty array. Otherwise in Ruby 1.8
66
+ # a `nil` args will actually pass `nil` into the class.
67
+ args ||= []
68
+
69
+ if klass.is_a?(Class)
70
+ # A action klass which is to be instantiated with the
71
+ # app, env, and any arguments given
72
+ klass.new(self, env, *args, &block)
73
+ elsif klass.respond_to?(:call)
74
+ # Make it a lambda which calls the item then forwards
75
+ # up the chain
76
+ lambda do |e|
77
+ klass.call(e)
78
+ self.call(e)
79
+ end
80
+ else
81
+ raise "Invalid action: #{action.inspect}"
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,5 @@
1
+ module Vli
2
+ module Command
3
+ autoload :Base, 'vli/command/base'
4
+ end
5
+ end
@@ -0,0 +1,64 @@
1
+ # Credit:
2
+ # special thanks to Vagrant project: https://github.com/mitchellh/vagrant
3
+ # where the source of this module was originally extracted.
4
+
5
+ module Vli
6
+ module Command
7
+ class Base
8
+
9
+ def initialize(argv, env)
10
+ @argv = argv
11
+ @env = env
12
+ end
13
+
14
+ def execute; end
15
+
16
+ # This method will split the argv given into three parts: the
17
+ # flags to this command, the subcommand, and the flags to the
18
+ # subcommand. For example:
19
+ #
20
+ # -v status -h -v
21
+ #
22
+ # The above would yield 3 parts:
23
+ #
24
+ # ["-v"]
25
+ # "status"
26
+ # ["-h", "-v"]
27
+ #
28
+ # These parts are useful because the first is a list of arguments
29
+ # given to the current command, the second is a subcommand, and the
30
+ # third are the commands given to the subcommand.
31
+ #
32
+ # @return [Array] The three parts.
33
+ def split_main_and_subcommand(argv)
34
+ # Initialize return variables
35
+ main_args = nil
36
+ sub_command = nil
37
+ sub_args = []
38
+
39
+ # We split the arguments into two: One set containing any
40
+ # flags before a word, and then the rest. The rest are what
41
+ # get actually sent on to the subcommand.
42
+ argv.each_index do |i|
43
+ if !argv[i].start_with?("-")
44
+ # We found the beginning of the sub command. Split the
45
+ # args up.
46
+ main_args = argv[0, i]
47
+ sub_command = argv[i]
48
+ sub_args = argv[i + 1, argv.length - i + 1]
49
+
50
+ # Break so we don't find the next non flag and shift our
51
+ # main args.
52
+ break
53
+ end
54
+ end
55
+
56
+ # Handle the case that argv was empty or didn't contain any subcommand
57
+ main_args = argv.dup if main_args.nil?
58
+
59
+ return [main_args, sub_command, sub_args]
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,7 @@
1
+ module Vli
2
+ module Error
3
+
4
+ class VliInterrupt < StandardError; end
5
+
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ # Credit:
2
+ # special thanks to Vagrant project: https://github.com/mitchellh/vagrant
3
+ # where the source of this module was originally extracted.
4
+
5
+ module Vli
6
+ # Register components in a single location that can be queried.
7
+ #
8
+ # This allows certain components (such as guest systems, configuration
9
+ # pieces, etc.) to be registered and queried.
10
+ class Registry
11
+ def initialize
12
+ @actions = {}
13
+ @results_cache = {}
14
+ end
15
+
16
+ # Register a callable by key.
17
+ #
18
+ # The callable should be given in a block which will be lazily evaluated
19
+ # when the action is needed.
20
+ #
21
+ # If an action by the given name already exists then it will be
22
+ # overwritten.
23
+ def register(key, value=nil, &block)
24
+ block = lambda { value } if value
25
+ @actions[key] = block
26
+ end
27
+
28
+ # Get an action by the given key.
29
+ #
30
+ # This will evaluate the block given to `register` and return the resulting
31
+ # action stack.
32
+ def get(key)
33
+ return nil if !@actions.has_key?(key)
34
+ return @results_cache[key] if @results_cache.has_key?(key)
35
+ @results_cache[key] = @actions[key].call
36
+ end
37
+ alias :[] :get
38
+
39
+ # Iterate over the keyspace.
40
+ def each(&block)
41
+ @actions.each do |key, _|
42
+ yield key, get(key)
43
+ end
44
+ end
45
+
46
+ # Converts this registry to a hash
47
+ def to_hash
48
+ result = {}
49
+ self.each do |key, value|
50
+ result[key] = value
51
+ end
52
+
53
+ result
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,161 @@
1
+ module Vli
2
+ module UI
3
+ # Vli UIs handle communication with the outside world (typically
4
+ # through a shell). They must respond to the following methods:
5
+ #
6
+ # * `info`
7
+ # * `warn`
8
+ # * `error`
9
+ # * `success`
10
+ class Interface
11
+ attr_accessor :resource
12
+
13
+ def initialize(resource)
14
+ @resource = resource
15
+ end
16
+
17
+ [:ask, :warn, :error, :info, :success].each do |method|
18
+ define_method(method) do |message, *opts|
19
+ define_method(method) { |*args| }
20
+ end
21
+ end
22
+
23
+ [:clear_line, :report_progress].each do |method|
24
+ define_method(method) { |*args| }
25
+ end
26
+ end
27
+
28
+ # This is a UI implementation that does nothing.
29
+ class Silent < Interface
30
+ def ask(*args)
31
+ super
32
+
33
+ # Silent can't do this, obviously.
34
+ raise Errors::UIExpectsTTY
35
+ end
36
+ end
37
+
38
+ # This is a UI implementation that outputs the text as is. It
39
+ # doesn't add any color.
40
+ class Basic < Interface
41
+ include Util::SafePuts
42
+
43
+ # Use some light meta-programming to create the various methods to
44
+ # output text to the UI. These all delegate the real functionality
45
+ # to `say`.
46
+ [:info, :warn, :error, :success].each do |method|
47
+ class_eval <<-CODE
48
+ def #{method}(message, *args)
49
+ super(message)
50
+ say(#{method.inspect}, message, *args)
51
+ end
52
+ CODE
53
+ end
54
+
55
+ def ask(message, opts=nil)
56
+ super(message)
57
+
58
+ # We can't ask questions when the output isn't a TTY.
59
+ raise Errors::UIExpectsTTY if !$stdin.tty?
60
+
61
+ # Setup the options so that the new line is suppressed
62
+ opts ||= {}
63
+ opts[:new_line] = false if !opts.has_key?(:new_line)
64
+ opts[:prefix] = false if !opts.has_key?(:prefix)
65
+
66
+ # Output the data
67
+ say(:info, message, opts)
68
+
69
+ # Get the results and chomp off the newline
70
+ $stdin.gets.chomp
71
+ end
72
+
73
+ # This is used to output progress reports to the UI.
74
+ # Send this method progress/total and it will output it
75
+ # to the UI. Send `clear_line` to clear the line to show
76
+ # a continuous progress meter.
77
+ def report_progress(progress, total, show_parts=true)
78
+ if total && total > 0
79
+ percent = (progress.to_f / total.to_f) * 100
80
+ line = "Progress: #{percent.to_i}%"
81
+ line << " (#{progress} / #{total})" if show_parts
82
+ else
83
+ line = "Progress: #{progress}"
84
+ end
85
+
86
+ info(line, :new_line => false)
87
+ end
88
+
89
+ def clear_line
90
+ reset = "\r"
91
+ reset += "\e[0K" unless Util::Platform.windows?
92
+ reset
93
+
94
+ info(reset, :new_line => false)
95
+ end
96
+
97
+ # This method handles actually outputting a message of a given type
98
+ # to the console.
99
+ def say(type, message, opts=nil)
100
+ defaults = { :new_line => true, :prefix => true }
101
+ opts = defaults.merge(opts || {})
102
+
103
+ # Determine whether we're expecting to output our
104
+ # own new line or not.
105
+ printer = opts[:new_line] ? :puts : :print
106
+
107
+ # Determine the proper IO channel to send this message
108
+ # to based on the type of the message
109
+ channel = type == :error || opts[:channel] == :error ? $stderr : $stdout
110
+
111
+ # Output!
112
+ safe_puts(format_message(type, message, opts),
113
+ :io => channel, :printer => printer)
114
+ end
115
+
116
+ # This is called by `say` to format the message for output.
117
+ def format_message(type, message, opts=nil)
118
+ opts ||= {}
119
+ message = "[#{@resource}] #{message}" if opts[:prefix]
120
+ message
121
+ end
122
+ end
123
+
124
+ # This is a UI implementation that outputs color for various types
125
+ # of messages. This should only be used with a TTY that supports color,
126
+ # but is up to the user of the class to verify this is the case.
127
+ class Colored < Basic
128
+ # Terminal colors
129
+ COLORS = {
130
+ :clear => "\e[0m",
131
+ :red => "\e[31m",
132
+ :green => "\e[32m",
133
+ :yellow => "\e[33m"
134
+ }
135
+
136
+ # Mapping between type of message and the color to output
137
+ COLOR_MAP = {
138
+ :warn => COLORS[:yellow],
139
+ :error => COLORS[:red],
140
+ :success => COLORS[:green]
141
+ }
142
+
143
+ # This is called by `say` to format the message for output.
144
+ def format_message(type, message, opts=nil)
145
+ # Get the format of the message before adding color.
146
+ message = super
147
+
148
+ # Colorize the message if there is a color for this type of message,
149
+ # either specified by the options or via the default color map.
150
+ if opts.has_key?(:color)
151
+ color = COLORS[opts[:color]]
152
+ message = "#{color}#{message}#{COLORS[:clear]}"
153
+ else
154
+ message = "#{COLOR_MAP[type]}#{message}#{COLORS[:clear]}" if COLOR_MAP[type]
155
+ end
156
+
157
+ message
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,8 @@
1
+ module Vli
2
+ module Util
3
+ autoload :Busy, 'vli/util/busy'
4
+ autoload :HashWithIndifferentAccess, 'vli/util/hash_with_indifferent_access'
5
+ autoload :Platform, 'vli/util/platform'
6
+ autoload :SafePuts, 'vli/util/safe_puts'
7
+ end
8
+ end
@@ -0,0 +1,59 @@
1
+ module Vli
2
+ module Util
3
+ # Utility class which allows blocks of code to be marked as "busy"
4
+ # with a specified interrupt handler. During busy areas of code, it
5
+ # is often undesirable for SIGINTs to immediately kill the application.
6
+ # This class is a helper to cleanly register callbacks to handle this
7
+ # situation.
8
+ class Busy
9
+ @@registered = []
10
+ @@mutex = Mutex.new
11
+
12
+ class << self
13
+ # Mark a given block of code as a "busy" block of code, which will
14
+ # register a SIGINT handler for the duration of the block. When a
15
+ # SIGINT occurs, the `sig_callback` proc will be called. It is up
16
+ # to the callback to behave properly and exit the application.
17
+ def busy(sig_callback)
18
+ register(sig_callback)
19
+ yield
20
+ ensure
21
+ unregister(sig_callback)
22
+ end
23
+
24
+ # Registers a SIGINT handler. This typically is called from {busy}.
25
+ # Callbacks are only registered once, so calling this multiple times
26
+ # with the same callback has no consequence.
27
+ def register(sig_callback)
28
+ @@mutex.synchronize do
29
+ registered << sig_callback
30
+ registered.uniq!
31
+
32
+ # Register the handler if this is our first callback.
33
+ Signal.trap("INT") { fire_callbacks } if registered.length == 1
34
+ end
35
+ end
36
+
37
+ # Unregisters a SIGINT handler.
38
+ def unregister(sig_callback)
39
+ @@mutex.synchronize do
40
+ registered.delete(sig_callback)
41
+
42
+ # Remove the signal trap if no more registered callbacks exist
43
+ Signal.trap("INT", "DEFAULT") if registered.empty?
44
+ end
45
+ end
46
+
47
+ # Fires all the registered callbacks.
48
+ def fire_callbacks
49
+ registered.reverse.each { |r| r.call }
50
+ end
51
+
52
+ # Helper method to get access to the class variable. This is mostly
53
+ # exposed for tests. This shouldn't be mucked with directly, since it's
54
+ # structure may change at any time.
55
+ def registered; @@registered; end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,63 @@
1
+ module Vli
2
+ module Util
3
+ # A hash with indifferent access. Mostly taken from Thor/Rails (thanks).
4
+ # Normally I'm not a fan of using an indifferent access hash since Symbols
5
+ # are basically memory leaks in Ruby, but since Vli is typically a quick
6
+ # one-off binary run and it doesn't use too many hash keys where this is
7
+ # used, the effect should be minimal.
8
+ #
9
+ # hash[:foo] #=> 'bar'
10
+ # hash['foo'] #=> 'bar'
11
+ #
12
+ class HashWithIndifferentAccess < ::Hash
13
+ def initialize(hash={}, &block)
14
+ super(&block)
15
+
16
+ hash.each do |key, value|
17
+ self[convert_key(key)] = value
18
+ end
19
+ end
20
+
21
+ def [](key)
22
+ super(convert_key(key))
23
+ end
24
+
25
+ def []=(key, value)
26
+ super(convert_key(key), value)
27
+ end
28
+
29
+ def delete(key)
30
+ super(convert_key(key))
31
+ end
32
+
33
+ def values_at(*indices)
34
+ indices.collect { |key| self[convert_key(key)] }
35
+ end
36
+
37
+ def merge(other)
38
+ dup.merge!(other)
39
+ end
40
+
41
+ def merge!(other)
42
+ other.each do |key, value|
43
+ self[convert_key(key)] = value
44
+ end
45
+ self
46
+ end
47
+
48
+ def key?(key)
49
+ super(convert_key(key))
50
+ end
51
+
52
+ alias_method :include?, :key?
53
+ alias_method :has_key?, :key?
54
+ alias_method :member?, :key?
55
+
56
+ protected
57
+
58
+ def convert_key(key)
59
+ key.is_a?(Symbol) ? key.to_s : key
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,68 @@
1
+ require 'rbconfig'
2
+ require 'tempfile'
3
+
4
+ module Vli
5
+ module Util
6
+ # This class just contains some platform checking code.
7
+ class Platform
8
+ class << self
9
+ def tiger?
10
+ platform.include?("darwin8")
11
+ end
12
+
13
+ def leopard?
14
+ platform.include?("darwin9")
15
+ end
16
+
17
+ [:darwin, :bsd, :freebsd, :linux, :solaris].each do |type|
18
+ define_method("#{type}?") do
19
+ platform.include?(type.to_s)
20
+ end
21
+ end
22
+
23
+ def windows?
24
+ %W[mingw mswin].each do |text|
25
+ return true if platform.include?(text)
26
+ end
27
+
28
+ false
29
+ end
30
+
31
+ # Returns boolean noting whether this is a 64-bit CPU. This
32
+ # is not 100% accurate and there could easily be false negatives.
33
+ #
34
+ # @return [Boolean]
35
+ def bit64?
36
+ ["x86_64", "amd64"].include?(RbConfig::CONFIG["host_cpu"])
37
+ end
38
+
39
+ # Returns boolean noting whether this is a 32-bit CPU. This
40
+ # can easily throw false positives since it relies on {#bit64?}.
41
+ #
42
+ # @return [Boolean]
43
+ def bit32?
44
+ !bit64?
45
+ end
46
+
47
+ # Returns a boolean noting whether the terminal supports color.
48
+ # output.
49
+ def terminal_supports_colors?
50
+ if windows?
51
+ return ENV.has_key?("ANSICON")
52
+ end
53
+
54
+ true
55
+ end
56
+
57
+ def tar_file_options
58
+ # create, write only, fail if the file exists, binary if windows
59
+ File::WRONLY | File::EXCL | File::CREAT | (windows? ? File::BINARY : 0)
60
+ end
61
+
62
+ def platform
63
+ RbConfig::CONFIG["host_os"].downcase
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ module Vli
2
+ module Util
3
+ # This module provides a `safe_puts` method which outputs to
4
+ # the given IO object, and rescues any broken pipe errors and
5
+ # ignores them. This is useful in cases where you're outputting
6
+ # to stdout, for example, and the stdout is closed, but you want to
7
+ # keep running.
8
+ module SafePuts
9
+ # Uses `puts` on the given IO object and safely ignores any
10
+ # Errno::EPIPE.
11
+ #
12
+ # @param [String] message Message to output.
13
+ # @param [Hash] opts Options hash.
14
+ def safe_puts(message=nil, opts=nil)
15
+ message ||= ""
16
+ opts = {
17
+ :io => $stdout,
18
+ :printer => :puts
19
+ }.merge(opts || {})
20
+
21
+ begin
22
+ opts[:io].send(opts[:printer], message)
23
+ rescue Errno::EPIPE
24
+ # This is what makes this a `safe` puts.
25
+ return
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,3 @@
1
+ module Vli
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vli/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "vli"
8
+ gem.version = Vli::VERSION
9
+ gem.authors = ["Tyler Flint"]
10
+ gem.email = ["tylerflint@gmail.com"]
11
+ gem.description = %q{Vagrant Like Interface is a library of components extracted from the vagrant project to aid in building command line interfaces.}
12
+ gem.summary = %q{library of components extracted from the vagrant project}
13
+ gem.homepage = "http://github.com/tylerflint/vli"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tyler Flint
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-05 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Vagrant Like Interface is a library of components extracted from the
15
+ vagrant project to aid in building command line interfaces.
16
+ email:
17
+ - tylerflint@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - lib/vli.rb
28
+ - lib/vli/action.rb
29
+ - lib/vli/action/builder.rb
30
+ - lib/vli/action/env.rb
31
+ - lib/vli/action/env/set.rb
32
+ - lib/vli/action/environment.rb
33
+ - lib/vli/action/runner.rb
34
+ - lib/vli/action/warden.rb
35
+ - lib/vli/command.rb
36
+ - lib/vli/command/base.rb
37
+ - lib/vli/error.rb
38
+ - lib/vli/registry.rb
39
+ - lib/vli/ui.rb
40
+ - lib/vli/util.rb
41
+ - lib/vli/util/busy.rb
42
+ - lib/vli/util/hash_with_indifferent_access.rb
43
+ - lib/vli/util/platform.rb
44
+ - lib/vli/util/safe_puts.rb
45
+ - lib/vli/version.rb
46
+ - vli.gemspec
47
+ homepage: http://github.com/tylerflint/vli
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.24
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: library of components extracted from the vagrant project
71
+ test_files: []
72
+ has_rdoc: