acclimate 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c626e3bd29a1754fcd54ef6d510992fb6f839954
4
- data.tar.gz: 939c681995bc048e0d7588584dfeec286d8df4ea
3
+ metadata.gz: afef825e7d4cfbca7f8904fa47a080fd83180df6
4
+ data.tar.gz: e3ded2d6fb6abcf8ee0f1f452f9ce75666bb8dd7
5
5
  SHA512:
6
- metadata.gz: fba0333e080bc8bb26016b793b25420812f77fc99643a2a38944d689985297241f42203e887fbf9251deb1279ce1c04207be8fd1ca02f37c1ab58417de241a12
7
- data.tar.gz: 5f06b381f3390d53f9059aff5411485b987fb9d193658bfab2df033f792b078eb5b105ce87ca16b73ef1ebdc2b28c7ca6f87511a95e926502bb17b134211af71
6
+ metadata.gz: 266b1c62853e24fef875cd348ed9721948b613b4766edb2857b20c85b4f219b882521e0b6126429d2cd6e7cd4c48c19b9dbb0be1047d57ad804e033c3ca9eef9
7
+ data.tar.gz: 419bd4a87a130f610cd03793b276ce91b18f773a460313dd4da80b1e8ae4604f1bf72ccbf494d53b502d9cf2546702da0bd9a9d451259ade74f1712a1f41a936
data/.ruby-gemset CHANGED
@@ -1 +1 @@
1
- phil_columns
1
+ acclimate
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Acclimate
2
2
 
3
- A CLI building toolkit
3
+ A CLI building toolkit. Pronounced A CLI Mate.
4
4
 
5
5
 
6
6
  ## Installation
@@ -17,6 +17,242 @@ Or install it yourself as:
17
17
 
18
18
  $ gem install acclimate
19
19
 
20
+
20
21
  ## Usage
21
22
 
22
- This release is simply a placeholder for the gem.
23
+ Acclimate provides a toolkit to aid in the boilerplate pieces of a CLI project including: configuration, commands,
24
+ helpers for the CLI class(es), error handling and some output helpers. Acclimate assumes the usage of
25
+ [Thor](http://whatisthor.com/) for creating the CLI portion of your CLI project. Please refer to Thor's documentation
26
+ for further help.
27
+
28
+ ### CliHelper
29
+
30
+ Acclimate's CliHelper module provides convenience methods as well as some sane defaults for [Thor](http://whatisthor.com/).
31
+
32
+ #### The #execute Method
33
+
34
+ The execute method provides a common API to direct your CLI commands to actual command class instances to do the work.
35
+
36
+ module AwesomeCli
37
+ class Cli < Thor
38
+ include Acclimate::CliHelper
39
+
40
+ desc "some-command", "Does something useful"
41
+ option :do_it_good, type: :boolean
42
+ def some_command( filepath )
43
+ execute AwesomeCli::Command::SomeCommand, filepath: filepath
44
+ end
45
+ end
46
+ end
47
+
48
+ The call to `#execute` instantiates the command class, and merges the filepath argument into the options arguments, passing
49
+ it into the command class' initializer.
50
+
51
+
52
+ ### Configuration
53
+
54
+ Acclimate's configuration class provides a base from which to build a custom configuration class in your
55
+ CLI project. The simplest implementation of a configuration class for your project is to inherit from
56
+ `Acclimate::Configuration` and do nothing else.
57
+
58
+ module AwesomeCli
59
+ class Configuration < Acclimate::Configuration
60
+ end
61
+ end
62
+
63
+ Now you can use the configuration class simply by passing a hash of values into it.
64
+
65
+ conf = AwesomeCli::Configuration.new( env: 'development', filepath: 'some/file/path' )
66
+
67
+ conf[:env] #=> 'development'
68
+ conf['env'] #=> 'development'
69
+ conf.env #=> 'development'
70
+ conf.slice( :env ) #=> { :env => 'development }
71
+ conf.slice( :env ).class #=> AwesomeCli::Configuration
72
+
73
+ Pretty much any method that works on a standard `Hash` will work on the configuration. This magic is a result
74
+ of `Acclimate::Configuration` inheriting from [Hashie::Mash](https://github.com/intridea/hashie#mash).
75
+
76
+ In addition, any commands (see the commands section below) that inherit from `Acclimate::Command` accept the
77
+ command line options in their initializer and expose a `#config` method that wraps the options in a configuration
78
+ object for you.
79
+
80
+ ### Commands
81
+
82
+ Acclimate's command class provides a base from which to build the commands for your CLI. Whn building an Acclimate
83
+ CLI, we use commands to encapsulate the actual behavior of each CLI defined command or sub-command. This serves multiple
84
+ purposes. First, it keeps the command logic out of the CLI class(es), which is basically the view or GUI of a CLI project.
85
+ Second, having the actual logic encapsulated in a command means including our CLI gem into another project and borrowing
86
+ the behavior is possible through direct utilization of the command class(es), without interacting with the CLI classes.
87
+
88
+ Building a command class is as simple as inheriting from `Acclimate::Command` and implementing an `#execute` method.
89
+
90
+ module AwesomeCli
91
+ class SomeCommand < Acclimate::Command
92
+ def execute
93
+ # do something useful ...
94
+ end
95
+ end
96
+ end
97
+
98
+ Due to the fact that your commands will certainly have some additional common behavior that Acclimate does not provide,
99
+ it is a good idea to have a base class for your commands. For instance, in order to use your custom configuration class
100
+ as opposed to an instance of `Acclimate::Configuration`, you must override the `#config_klass` method in your command(s).
101
+ You command base class is the ideal place to do so.
102
+
103
+ module AwesomeCli
104
+ class CommandBase < Acclimate::Command
105
+ protected
106
+ def a_helper
107
+ # do something helpful
108
+ end
109
+
110
+ def config_klass
111
+ AwesomeCli::Configuration
112
+ end
113
+ end
114
+
115
+ class SomeCommand < CommandBase
116
+ def execute
117
+ # do something useful ...
118
+ end
119
+ end
120
+ end
121
+
122
+ ### Output
123
+
124
+ Acclimate's output module can be included in any class in your CLI and is already included in any class that inherits from
125
+ `Acclimate::Command` or includes `Acclimate::CliHelper`. In addition to it own [output helpers](https://github.com/midas/acclimate/blob/master/lib/acclimate/output.rb),
126
+ the module also include [Thor's shell module](http://rdoc.info/github/wycats/thor/Thor/Shell/Basic).
127
+
128
+ #### The #confirm Method
129
+
130
+ Acclimate's `#confirm` method is the accepted strategy for outputting command status throughout the execution of the
131
+ command. The `#confirm` method outputs a statement, executes a provided block, then outputs either OK or ERROR depending
132
+ on the results of the block.
133
+
134
+ module AwesomeCli
135
+ module SomeCommand < CommandBase
136
+ def execute
137
+ confirm "Doing something useful" do
138
+ # something useful here
139
+ end
140
+
141
+ confirm "Doing something helpful" do
142
+ # something helpful here
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ Executing the previous command
149
+
150
+ command = AwesomeCli::SomeCommand.new
151
+ command.execute
152
+
153
+ Results in the following output
154
+
155
+ Doing something useful ... OK
156
+ Doing something helpful ... OK
157
+
158
+
159
+ ## Error Handling
160
+
161
+ Acclimate can handle known error conditions gracefully for you. In the cases wher eyou would like to output a useful
162
+ error message but not overwhelm your user with a Ruby backtrace, simply raise the `Acclimate::Error`.
163
+
164
+ raise Acclimate::error, 'External service unavailable, please try the command again later'
165
+
166
+ ### Errors and the #confirm Method
167
+
168
+ If an error is encountered within the block that should be gracefully handled by Acclimate as opposed to an ugly Ruby
169
+ stacktrace, you may raise Acclimate::Error.
170
+
171
+ module AwesomeCli
172
+ module SomeCommand < CommandBase
173
+ def execute
174
+ confirm "Doing something useful" do
175
+ raise Acclimate::Error, 'A known error description'
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ Executing the previous command
182
+
183
+ command = AwesomeCli::SomeCommand.new
184
+ command.execute
185
+
186
+ Results in the following output
187
+
188
+ Doing something useful ... ERROR
189
+ Error: A known error condition
190
+
191
+ By default, the exit code for an error is 1. This can be overridden when raising an error.
192
+
193
+ raise Acclimate::Error.new( 'A known error description', exit_code: 35 )
194
+
195
+ In some cases it is not enough to raise an `Acclimate::Error` and let the graceful error handling ensue. You may need
196
+ to clean something up or want to output additional error details. It is this case the `Acclimate::ConfirmationError` is
197
+ for.
198
+
199
+ Let's assume your command is parsing a text file and wants to output which lines were unparseable along with an error
200
+ message.
201
+
202
+ module AwesomeCli
203
+ module ParseTextFile < CommandBase
204
+ def execute
205
+ confirm "Parsing text file" do
206
+ parse_file
207
+ raise_if_in_error_lines!
208
+ end
209
+ end
210
+
211
+ protected
212
+
213
+ def raise_if_in_error_lines!
214
+ raise Acclimate::ConfirmationError.new( 'Unparseable lines were encountered', exit_code: 12,
215
+ finish_proc: report_in_error_lines )
216
+ end
217
+
218
+ def report_in_error_lines
219
+ -> {
220
+ in_error_lines.each do |line|
221
+ say_stderr( " #{line}" )
222
+ end
223
+ }
224
+ end
225
+
226
+ def parse_file
227
+ File.readlines( config.filepath ).each do |line|
228
+ begin
229
+ parse_line( line )
230
+ rescue
231
+ in_error_lines << line
232
+ end
233
+ end
234
+ end
235
+
236
+ def parse_line( line )
237
+ # some parsing logic ...
238
+ end
239
+
240
+ def in_error_lines
241
+ @in_error_lines ||= []
242
+ end
243
+
244
+ end
245
+ end
246
+
247
+ Executing the previous command
248
+
249
+ command = AwesomeCli::ParseTextFile.new
250
+ command.execute
251
+
252
+ Results in the following output
253
+
254
+ Parsing text file ... ERROR
255
+ Unparseable lines were encountered
256
+ 1 something unparseable
257
+ 7 something else unparseable
258
+
data/acclimate.gemspec CHANGED
@@ -20,5 +20,10 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.5"
22
22
  spec.add_development_dependency "gem-dandy"
23
+ spec.add_development_dependency "pry-debugger"
23
24
  spec.add_development_dependency "rake"
25
+
26
+ spec.add_dependency "hashie"
27
+ spec.add_dependency "rainbow"
28
+ spec.add_dependency "thor"
24
29
  end
@@ -0,0 +1,53 @@
1
+ module Acclimate
2
+ module CliHelper
3
+
4
+ def self.included( base )
5
+ base.class_eval do
6
+ include Acclimate::Output
7
+
8
+ no_commands do
9
+
10
+ def execute( klass, additional_options={} )
11
+ klass.new( options.merge( additional_options )).execute
12
+ rescue Acclimate::Error => e
13
+ handle_error( e ) unless e.handled?
14
+ exit( e.exit_code || 1 )
15
+ end
16
+
17
+ def handle_error( e )
18
+ say "Error: #{e.message}", :red
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ module ClassMethods
28
+
29
+ def handle_argument_error( command, error, _, __ )
30
+ method = "handle_argument_error_for_#{command.name}"
31
+
32
+ if respond_to?( method )
33
+ send( method, command, error )
34
+ else
35
+ handle_argument_error_default( command, error )
36
+ end
37
+ end
38
+
39
+ def handle_argument_error_default( command, error )
40
+ $stdout.puts "Incorrect usage of command: #{command.name}"
41
+ $stdout.puts " #{error.message}", ''
42
+ $stdout.puts "For correct usage:"
43
+ $stdout.puts " acclimate help #{command.name}"
44
+ end
45
+
46
+ def handle_no_command_error( name )
47
+ $stdout.puts "Unrecognized command: #{name}"
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,33 @@
1
+ require 'pathname'
2
+
3
+ module Acclimate
4
+ class Command
5
+
6
+ include Acclimate::Output
7
+
8
+ def initialize( options )
9
+ @options = options
10
+ end
11
+
12
+ def execute
13
+ raise NotImplementedError, "You must implement #{self.class.name}#execute"
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :options
19
+
20
+ def config
21
+ @config = config_klass.new( options )
22
+ end
23
+
24
+ def base_path
25
+ Pathname.new( Dir.pwd )
26
+ end
27
+
28
+ def config_klass
29
+ Acclimate::Configuration
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'hashie'
2
+
3
+ module Acclimate
4
+ class Configuration < Hashie::Mash
5
+
6
+ def initialize( options={} )
7
+ super( options )
8
+ end
9
+
10
+ def slice( *keys )
11
+ klass.new( select { |k,v| keys.map( &:to_s ).include?( k ) } )
12
+ end
13
+
14
+ protected
15
+
16
+ def klass
17
+ self.class
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module Acclimate
2
+ class ConfirmationError < StandardError
3
+
4
+ def initialize( message, options={} )
5
+ @options = options
6
+ super( message )
7
+ end
8
+
9
+ def exit_code
10
+ options[:exit_code] || 1
11
+ end
12
+
13
+ def finish
14
+ return unless finish_proc
15
+ finish_proc.call
16
+ end
17
+
18
+ protected
19
+
20
+ attr_reader :options
21
+
22
+ def finish_proc
23
+ options[:finish_proc]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Acclimate
2
+ class Error < StandardError
3
+
4
+ def initialize( message, options={} )
5
+ @options = options
6
+
7
+ super( message )
8
+ end
9
+
10
+ def exit_code
11
+ options[:exit_code] || 1
12
+ end
13
+
14
+ def handled?
15
+ options[:handled] || false
16
+ end
17
+
18
+ protected
19
+
20
+ attr_reader :options
21
+
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ require 'thor/shell'
2
+
3
+ module Acclimate
4
+ module Output
5
+
6
+ def self.included( base )
7
+ base.send( :include, Thor::Shell )
8
+ end
9
+
10
+ def write( msg, color=:white )
11
+ $stdout.write( Rainbow( msg ).color( color ))
12
+ end
13
+
14
+ def say( msg, color=:white )
15
+ $stdout.puts( Rainbow( msg ).color( color ))
16
+ end
17
+
18
+ def say_stderr( msg, color=:white )
19
+ $stdout.puts( Rainbow( msg ).color( color ))
20
+ end
21
+
22
+ def header( msg )
23
+ say msg, :cyan
24
+ end
25
+
26
+ def say_ok
27
+ say 'OK', :green
28
+ end
29
+
30
+ def say_error
31
+ say_stderr 'ERROR', :red
32
+ end
33
+
34
+ def say_skipping
35
+ say 'SKIPPING', :yellow
36
+ end
37
+
38
+ def confirm( msg, color=:white, &block )
39
+ write "#{msg}... ", color
40
+ block.call
41
+ say_ok
42
+ rescue ConfirmationError => e
43
+ say_error
44
+ say_stderr( e.message, :red )
45
+ e.finish
46
+ raise Acclimate::Error.new( nil, handled: true, exit_code: e.exit_code )
47
+ rescue
48
+ say_error
49
+ raise
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module Acclimate
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/acclimate.rb CHANGED
@@ -1,5 +1,15 @@
1
1
  require "acclimate/version"
2
+ require 'pry-debugger'
3
+ require 'rainbow'
4
+ require 'thor'
2
5
 
3
6
  module Acclimate
4
- # Your code goes here...
7
+
8
+ autoload :CliHelper, 'acclimate/cli_helper'
9
+ autoload :Command, 'acclimate/command'
10
+ autoload :Configuration, 'acclimate/configuration'
11
+ autoload :ConfirmationError, 'acclimate/confirmation_error'
12
+ autoload :Error, 'acclimate/error'
13
+ autoload :Output, 'acclimate/output'
14
+
5
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acclimate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - C. Jason Harrelson (midas )
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-debugger
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,48 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hashie
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rainbow
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: thor
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
55
111
  description: A Cli building toolkit. See README for more details.
56
112
  email:
57
113
  - jason@lookforwardenterprises.com
@@ -68,6 +124,12 @@ files:
68
124
  - Rakefile
69
125
  - acclimate.gemspec
70
126
  - lib/acclimate.rb
127
+ - lib/acclimate/cli_helper.rb
128
+ - lib/acclimate/command.rb
129
+ - lib/acclimate/configuration.rb
130
+ - lib/acclimate/confirmation_error.rb
131
+ - lib/acclimate/error.rb
132
+ - lib/acclimate/output.rb
71
133
  - lib/acclimate/version.rb
72
134
  homepage: ''
73
135
  licenses: