wip-runner 0.3.4 → 0.4.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: 5bb36ff238b91a06e9cb2788a7c2a5339dc8d2a6
4
- data.tar.gz: 4d839159e03cd18ec8e50e7f97a60b2a4adb3b25
3
+ metadata.gz: 43076f87b14007f06ce003584c7cbe366170e629
4
+ data.tar.gz: ea94ff0e8cd0384b234f6921411178d8bc781875
5
5
  SHA512:
6
- metadata.gz: 14a1d7b3f7c6051b0e07b6fe29954c29cc776bc2dd2f8004eaec9020ed093b9930822d163bfab026393fa2066fa05def72c2a1ece6f0d88783df2671a9230e1c
7
- data.tar.gz: 51026c6b225deac5efab13f9e2578c91aa6070c9aa3f816b9406e33d693d232ce8175f2965378283fddea821dfbb614b9b801b2e263d2b46f182f7a956c02143
6
+ metadata.gz: 64247802ae56cdd2c96294c402e8e6aaf7183362ef0a97fad35b5910b0640855005ad81e29d84e3c68d2345e9beb9f4d5c803b9b2d43de721381773366549b97
7
+ data.tar.gz: bb3b9642aef06a98c988b32504e82ee03f9c79d9e66b8e41dce35a2fbe5525669e17d267452e861a8a40cf89d98a7157cd5441e7a09b8a6b961b03abe769567d
@@ -18,9 +18,9 @@ module WIP
18
18
 
19
19
  def command_parser(params)
20
20
  command = params.command
21
- return CLI::Parser.new(@io) if command.nil?
21
+ return CLI::Parser.new(@ui) if command.nil?
22
22
 
23
- Commands.locate(command).new(@io).parser
23
+ Commands.locate(command).new(@ui).parser
24
24
  end
25
25
  end
26
26
  end
@@ -4,7 +4,9 @@ module WIP
4
4
  overview 'Prints version information'
5
5
 
6
6
  def execute(params, options)
7
- @io.say "#{WIP::Runner::CLI.signature} version #{WIP::Runner::CLI.namespace::VERSION}"
7
+ @ui.out {
8
+ @ui.say("#{WIP::Runner::CLI.signature} version #{WIP::Runner::CLI.namespace::VERSION}")
9
+ }
8
10
  end
9
11
  end
10
12
  end
@@ -1,5 +1,3 @@
1
- require 'highline'
2
-
3
1
  module WIP
4
2
  module Runner
5
3
  class CLI
@@ -40,10 +38,9 @@ module WIP
40
38
  end
41
39
  end
42
40
 
43
- def initialize(io = HighLine.new)
44
- @io = io
45
- @parser = Parser.new(io)
46
-
41
+ def initialize(ui = UI.new)
42
+ @ui = ui
43
+ @parser = Parser.new(ui)
47
44
  trap('INT') { quit }
48
45
  trap('TERM') { quit }
49
46
  end
@@ -57,8 +54,10 @@ module WIP
57
54
  recipient = (command(args) || @parser)
58
55
  recipient.run(args)
59
56
  rescue InvalidCommand => e
60
- @io.say(e.message)
61
- @io.newline
57
+ @ui.err {
58
+ @ui.say(e.message)
59
+ @ui.newline
60
+ }
62
61
  @parser.help
63
62
  end
64
63
 
@@ -66,12 +65,12 @@ module WIP
66
65
 
67
66
  def command(args)
68
67
  handler = Commands.locate(args.shift)
69
- handler.nil? ? nil : handler.new(@io)
68
+ handler.nil? ? nil : handler.new(@ui)
70
69
  end
71
70
 
72
71
  class Parser
73
- def initialize(io)
74
- @io = io
72
+ def initialize(ui)
73
+ @ui = ui
75
74
  end
76
75
 
77
76
  def run(args)
@@ -80,7 +79,9 @@ module WIP
80
79
  end
81
80
 
82
81
  def help
83
- @io.say(options.help)
82
+ @ui.err {
83
+ @ui.say(options.help)
84
+ }
84
85
  end
85
86
 
86
87
  private
@@ -106,7 +107,9 @@ module WIP
106
107
  parser.separator 'Options:'
107
108
 
108
109
  parser.on_tail '-h', '--help', 'Prints help messages' do
109
- @io.say(parser)
110
+ @ui.err {
111
+ @ui.say(parser)
112
+ }
110
113
  end
111
114
  end
112
115
  end
@@ -45,9 +45,9 @@ module WIP
45
45
 
46
46
  attr_reader :parser # TODO(?)... :arguments, :options
47
47
 
48
- def initialize(io)
49
- @io = io
50
- @parser = WIP::Runner::Parser.new(@io, self.class)
48
+ def initialize(ui)
49
+ @ui = ui
50
+ @parser = WIP::Runner::Parser.new(@ui, self.class)
51
51
  end
52
52
 
53
53
  def run(argv = [])
@@ -82,7 +82,7 @@ module WIP
82
82
  def validate!(arguments)
83
83
  unless parser.arguments.empty? || parser.config.no_validate
84
84
  missing = parser.arguments.keys.inject({}) do |memo, key|
85
- memo[key] = nil if arguments[key].nil?
85
+ memo[key] = nil if arguments[key].nil? || arguments[key].empty?
86
86
  memo
87
87
  end
88
88
  raise InvalidArguments, missing unless missing.empty?
@@ -92,13 +92,15 @@ module WIP
92
92
  private
93
93
 
94
94
  def delegate(command, argv)
95
- target = Commands.locate(command, self.class).new(@io)
95
+ target = Commands.locate(command, self.class).new(@ui)
96
96
  target.run(argv)
97
97
  end
98
98
 
99
99
  def print_error(e)
100
- @io.say(e.message)
101
- @io.newline
100
+ @ui.err {
101
+ @ui.say(e.message)
102
+ @ui.newline
103
+ }
102
104
  parser.help
103
105
  end
104
106
  end
@@ -3,6 +3,10 @@ require 'ostruct'
3
3
  module WIP
4
4
  module Runner
5
5
  class Options < OpenStruct
6
+ def to_hash
7
+ self.to_h
8
+ end
9
+
6
10
  def each(&block)
7
11
  self.to_h.each(&block)
8
12
  end
@@ -14,6 +18,10 @@ module WIP
14
18
  def values
15
19
  self.to_h.values
16
20
  end
21
+
22
+ def merge(other)
23
+ Options.new(self.to_h.merge(other.to_h))
24
+ end
17
25
  end
18
26
  end
19
27
  end
@@ -5,8 +5,8 @@ module WIP
5
5
  class Parser
6
6
  attr_reader :config
7
7
 
8
- def initialize(io, command)
9
- @io = io
8
+ def initialize(ui, command)
9
+ @ui = ui
10
10
  @command = command
11
11
  @config = WIP::Runner::Options.new
12
12
  end
@@ -21,7 +21,12 @@ module WIP
21
21
 
22
22
  @args = WIP::Runner::Options.new.tap do |opts|
23
23
  arguments.keys.each_with_index do |key, index|
24
- opts[key] = remaining[index]
24
+ if arguments[key].multiple
25
+ opts[key] = remaining[index..-1]
26
+ break
27
+ else
28
+ opts[key] = remaining[index]
29
+ end
25
30
  end
26
31
  end
27
32
 
@@ -30,7 +35,9 @@ module WIP
30
35
  end
31
36
 
32
37
  def help
33
- @io.say(options.help)
38
+ @ui.err {
39
+ @ui.say(options.help)
40
+ }
34
41
  end
35
42
 
36
43
  def options
@@ -87,6 +94,7 @@ module WIP
87
94
  pairs.each do |name, definition|
88
95
  padding = ' ' * (parser.summary_width - name.length + 1)
89
96
  overview = definition[:overview]
97
+ overview << ' [multiple]' if definition[:multiple]
90
98
  parser.separator [parser.summary_indent, name, padding, overview].join('')
91
99
  end
92
100
  end
@@ -10,8 +10,13 @@ module WIP
10
10
  instance_exec(&block) if block_given?
11
11
  end
12
12
 
13
- def description
14
- raise NotImplementedError
13
+ def content(format = :text)
14
+ case format
15
+ when :markdown
16
+ "```\n#{@content}\n```"
17
+ else
18
+ @content
19
+ end
15
20
  end
16
21
 
17
22
  def execute(io, env, &block)
@@ -15,17 +15,8 @@ module WIP
15
15
  super
16
16
  end
17
17
 
18
- def description(format = :text)
19
- case format
20
- when :markdown
21
- "```\n#{@content}\n```"
22
- else
23
- @content
24
- end
25
- end
26
-
27
- def execute(io, env, &block)
28
- prompts.empty? ? simplex!(io, env, &block) : complex!(io, env, &block)
18
+ def execute(ui, env, &block)
19
+ prompts.empty? ? simplex!(ui, env, &block) : complex!(ui, env, &block)
29
20
  end
30
21
 
31
22
  def name(value)
@@ -46,7 +37,7 @@ module WIP
46
37
  ([@name] + @args).join(' ')
47
38
  end
48
39
 
49
- def simplex!(io, env, &block)
40
+ def simplex!(ui, env, &block)
50
41
  Open3.popen2e(env, "#{executable} #{arguments}") do |stdin, stdoe, thread|
51
42
  while line = stdoe.gets
52
43
  block.call(line)
@@ -56,11 +47,11 @@ module WIP
56
47
  end
57
48
  end
58
49
 
59
- def complex!(io, env, &block)
50
+ def complex!(ui, env, &block)
60
51
  Open3.popen2e(env, "#{executable} #{arguments}") do |stdin, stdoe, thread|
61
52
  prompts.each do |term, options|
62
53
  stdoe.expect(term) do |result|
63
- stdin.puts io.ask(term) do |q|
54
+ stdin.puts ui.ask(term) do |q|
64
55
  options.each { |k, v| q.send(:"#{k}=", v) }
65
56
  end
66
57
  stdoe.gets
@@ -3,11 +3,6 @@ module WIP
3
3
  module Shell
4
4
  module Handlers
5
5
  class System < Base
6
- def description
7
- # @description ||= "```\n#{@content}\n```"
8
- @description ||= @content
9
- end
10
-
11
6
  def execute(io, env, &block)
12
7
  IO.popen(env, executable, 'r') do |pipe|
13
8
  pipe.each(&block)
@@ -6,150 +6,155 @@ module WIP
6
6
  class Runner
7
7
  attr_reader :arguments, :options
8
8
 
9
- def initialize(io, tasks, env = {})
10
- @io = io
11
- @tasks = tasks
12
- @env = env
13
- @io.indent_size = 2
9
+ # TODO: enforce env keys as String OR Symbol
10
+ def initialize(ui, env = {})
11
+ @ui = ui
12
+ @env = env
14
13
  end
15
14
 
16
- def run(arguments, options)
15
+ # TODO: custom proc for UI???
16
+ def run(tasks, arguments, options, &block)
17
17
  @arguments = arguments
18
- @options = options
19
-
20
- if markdown?
21
- @io.indent do
22
- @tasks.each do |task|
23
- evaluate(task)
24
- end
25
- end
18
+ @options = default(:options).merge(options)
19
+
20
+ if format == :markdown
21
+ raise 'TODO'
22
+ # @ui.out {
23
+ # @ui.indent do
24
+ # [tasks].flatted.each do |task|
25
+ # evaluate(task)
26
+ # end
27
+ # end
28
+ # }
26
29
  else
27
- @tasks.each do |task|
28
- evaluate(task)
30
+ [tasks].flatten.each do |task|
31
+ evaluate(task, &block)
29
32
  end
30
33
  end
31
34
  end
32
35
 
33
36
  private
34
37
 
35
- def evaluate(task)
38
+ def default(setting)
39
+ @defaults ||= {
40
+ :options => Options.new({
41
+ :echo => true,
42
+ :format => :text,
43
+ :interactive => true,
44
+ :mode => :execute
45
+ }),
46
+ :procs => {
47
+ :execute => Proc.new { |line| @ui.out { @ui.say("> #{line.rstrip}") } },
48
+ :silent => Proc.new { |line| }
49
+ }
50
+ }
51
+
52
+ @defaults[setting]
53
+ end
54
+
55
+ def evaluate(task, &block)
36
56
  task.build(arguments, options)
37
- prefix = options.preview ? :preview : :execute
38
57
 
39
- section('Config') do
40
- # @io.newline
41
- # @io.say '```'
42
- task.configs.each do |term, options|
43
- send(:"#{prefix}_config", term, options)
58
+ section(task.heading) do
59
+ if interactive? && ! task.configs.empty?
60
+ task.configs.each do |term, options, b|
61
+ evaluate_config(term, options, &b)
62
+ end
63
+ @ui.err { @ui.newline }
44
64
  end
45
- # @io.say '```'
46
- end unless task.configs.empty?
47
65
 
48
- task.shells.each do |shell|
49
- section("Shell #{shell.type.downcase}") do
50
- send(:"#{prefix}_shell", shell)
51
- @io.newline
66
+ task.shells.each do |shell|
67
+ send(:"#{mode}_shell", shell, &block)
52
68
  end
53
- end
54
69
 
55
- # p task.children
56
- task.children.each do |child|
57
- section('Task') do
58
- evaluate(child)
70
+ task.steps.each do |step|
71
+ evaluate(step, &block)
59
72
  end
60
73
  end
61
74
  end
62
75
 
63
- # ---
64
-
65
- # class Execute
66
- # end
67
- #
68
- # class Preview
69
- # end
76
+ def default_config(term, options = {})
77
+ options[:default] || @env[term] || ENV[term]
78
+ end
70
79
 
71
- def execute_config(term, options = {})
80
+ def evaluate_config(term, options = {})
72
81
  query = options[:required] ? "#{term} (*)" : term
73
- answer = @io.ask("- #{query}: ") do |q|
74
- q.default = (options[:default] || ENV[term]) unless options[:password]
75
- q.echo = false if options[:password]
76
- q.validate = /^.+$/ if options[:required]
82
+ answer = @ui.err do
83
+ @ui.ask("- #{query}: ") do |q|
84
+ q.default = default_config(term, options) unless options[:password]
85
+ q.echo = false if options[:password]
86
+ q.validate = /^.+$/ if options[:required]
87
+ end
77
88
  end
78
- @env[term] = answer unless answer.empty?
79
- @env[term] ||= ENV[term]
80
- end
81
-
82
- def execute_shell(shell)
83
- preview_shell(shell)
84
89
 
85
- if approved?
86
- @io.newline
87
- result = shell.execute(@io, @env) do |line|
88
- # @io.say "> `#{line.rstrip}`<br>"
89
- @io.say "> #{line.rstrip}"
90
- # puts "#{@io.indentation}> `#{line.rstrip}` "
91
- # @io.instance_variable_get(:@output).puts "> `#{line.rstrip}` "
92
-
93
- # @io.send((action || :say), line)
94
- end
95
- @io.newline
90
+ if block_given?
91
+ yield answer
92
+ else
93
+ @env[term] = answer unless answer.empty?
94
+ @env[term] ||= ENV[term]
95
+ end
96
+ end
96
97
 
97
- # TODO: raise instead of exit.
98
- exit 1 unless result.success?
98
+ def display_shell(shell, &block)
99
+ if block_given?
100
+ block.call(shell.content)
99
101
  else
100
- @io.newline
101
- @io.say '> (skipped)'
102
+ @ui.out {
103
+ @ui.say(shell.content)
104
+ }
102
105
  end
103
106
  end
104
107
 
105
- def preview_config(term, options = {})
106
- message = options[:required] ? "#{term} (*)" : term
107
- @io.say "- #{message}"
108
+ def execute_shell(shell, &block)
109
+ display_shell(shell) unless (mode == :silent) || ! echo?
110
+
111
+ @ui.out {
112
+ if approved?
113
+ if block_given?
114
+ result = shell.execute(@ui, @env, &block)
115
+ else
116
+ @ui.newline
117
+ result = shell.execute(@ui, @env, &default(:procs)[mode])
118
+ @ui.newline
119
+ end
120
+
121
+ # TODO: raise instead of exit.
122
+ exit 1 unless result.success?
123
+ else
124
+ if block_given?
125
+ block.call('> (skipped)')
126
+ else
127
+ @ui.newline
128
+ @ui.say('> (skipped)')
129
+ end
130
+ end
131
+ }
108
132
  end
109
133
 
110
- def preview_shell(shell)
111
- @io.say shell.description
134
+ def silent_shell(shell)
135
+ execute_shell(shell)
112
136
  end
113
137
 
114
138
  # ---
115
139
 
116
140
  def section(heading, &block)
117
- if markdown?
118
- @io.say "- [ ] #{heading}..."
119
- @io.indent(&block)
141
+ if format == :markdown
142
+ @ui.out {
143
+ @ui.say("- [ ] #{heading}")
144
+ @ui.indent(&block)
145
+ }
120
146
  else
121
- yield
147
+ @ui.err {
148
+ if heading
149
+ @ui.say(heading)
150
+ @ui.newline
151
+ end
152
+ yield
153
+ @ui.newline
154
+ }
122
155
  end
123
156
  end
124
157
 
125
- def format
126
- @options.format ? @options.format.intern : :text
127
- end
128
-
129
- def markdown?
130
- format == :markdown
131
- end
132
-
133
- # $ wip-runner x --format=markdown
134
- # $ wip-runner x --log=out.md # log format determined by extension
135
- #
136
- # @formatter = Formatter::Markdown.new(@io)
137
- #
138
- # def item(text)
139
- # formatter.item(text)
140
- # end
141
- #
142
- # class Formatter::Markdown
143
- # def item(text, check = true)
144
- # @io.say check ? "- [ ] #{text}" : "- #{text}"
145
- # end
146
- # end
147
- #
148
- # class Formatter::Plain
149
- # class Formatter::Color
150
- # class Formatter::HTML
151
- # class Formatter::JSON
152
-
153
158
  # ---
154
159
 
155
160
  def approved?
@@ -159,6 +164,22 @@ module WIP
159
164
  # when 'yes'
160
165
  # end
161
166
  end
167
+
168
+ def echo?
169
+ !! options.echo
170
+ end
171
+
172
+ def format
173
+ options.format
174
+ end
175
+
176
+ def interactive?
177
+ options.interactive
178
+ end
179
+
180
+ def mode
181
+ options.mode
182
+ end
162
183
  end
163
184
  end
164
185
  end
@@ -2,30 +2,35 @@ module WIP
2
2
  module Runner
3
3
  module Shell
4
4
  class Task
5
- attr_reader :configs, :shells, :children
6
-
7
- def initialize(command, &block)
8
- @command = command
9
- @configs = []
10
- @shells = []
11
- @children = []
12
- @block = block
5
+ attr_reader :heading, :configs, :shells, :steps
6
+
7
+ def initialize(command, *args, &block)
8
+ @command = command
9
+ @configs = []
10
+ @shells = []
11
+ @steps = []
12
+ @heading = args.first unless args.empty?
13
+ @block = block
13
14
  end
14
15
 
15
16
  def build(arguments, options)
16
17
  self.instance_exec(arguments, options, &@block) ; self
17
18
  end
18
19
 
19
- def config(term, options = {})
20
- @configs << [term.to_s, options] # Config.new(...)
20
+ def heading?
21
+ !! @heading
22
+ end
23
+
24
+ def config(term, options = {}, &block)
25
+ @configs << [term.to_s, options, block] # Config.new(...)
21
26
  end
22
27
 
23
28
  def shell(handler, content, &block)
24
29
  shells << Handlers.locate(handler).new(content, &block)
25
30
  end
26
31
 
27
- def task(&block)
28
- children << Task.new(@command, &block)
32
+ def task(*args, &block)
33
+ steps << Task.new(@command, *args, &block)
29
34
  end
30
35
 
31
36
  protected
@@ -34,7 +39,6 @@ module WIP
34
39
  if @command.respond_to?(method_name)
35
40
  @command.send(method_name, *args, &block)
36
41
  else
37
- # super
38
42
  @command.instance_eval {
39
43
  method_missing(method_name, *args, &block)
40
44
  }
@@ -13,7 +13,7 @@ module WIP
13
13
  end
14
14
 
15
15
  def example_command(implementation)
16
- ExampleCommands.const_get(implementation).new(io)
16
+ ExampleCommands.const_get(implementation).new(ui)
17
17
  end
18
18
 
19
19
  module ExampleCommands
@@ -56,7 +56,9 @@ module WIP
56
56
 
57
57
  def execute(args, options)
58
58
  super
59
- @io.say 'running nested command...'
59
+ @ui.out {
60
+ @ui.say('running nested command...')
61
+ }
60
62
  end
61
63
  end
62
64
  end