mushy 0.21.2 → 0.24.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.
- checksums.yaml +4 -4
- data/bin/mushy +5 -9
- data/lib/mushy/builder/api.rb +146 -152
- data/lib/mushy/builder/index.rb +1 -0
- data/lib/mushy/flux.rb +2 -1
- data/lib/mushy/fluxs/bash.rb +50 -58
- data/lib/mushy/fluxs/browser.rb +232 -240
- data/lib/mushy/fluxs/build_csv.rb +60 -68
- data/lib/mushy/fluxs/cli.rb +22 -28
- data/lib/mushy/fluxs/document.rb +33 -39
- data/lib/mushy/fluxs/environment.rb +23 -36
- data/lib/mushy/fluxs/file_watch.rb +114 -77
- data/lib/mushy/fluxs/write_json.rb +30 -40
- data/lib/mushy.rb +6 -6
- data/mushy.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e76c4fac239938383546eb9dcec5faa82b28dc94663343c3c16345ddf45ee5f
|
4
|
+
data.tar.gz: d5a6e435fa286e0cba5e52bc43752c7a2723d629c15566c214dfab368de00d06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06ed9957d991a0158ddd30b62cf5c2652ec1271f117479e0f4f240bb8b24f0d735e921b9be32f373e5d96f614e8623a1d8e924c52e919173a7e4c8da60446bac
|
7
|
+
data.tar.gz: e1d42f2ed6a29433a0c802d46a840e13f5516d607436a2044c8da8265fcc465db29a8aa081c787409ab296f590f5eb045e32126e9050c8f4a08485a19408052e
|
data/bin/mushy
CHANGED
@@ -4,7 +4,6 @@ require 'thor'
|
|
4
4
|
require 'mushy'
|
5
5
|
|
6
6
|
class MushyCLI < Thor
|
7
|
-
|
8
7
|
argument :file, optional: true, type: :string
|
9
8
|
argument :values, optional: true, type: :hash
|
10
9
|
|
@@ -16,29 +15,26 @@ class MushyCLI < Thor
|
|
16
15
|
|
17
16
|
desc "build FILE", 'Build a flow.'
|
18
17
|
def build
|
19
|
-
|
20
|
-
MushyCLI.set_special( { method: 'build', file: file } )
|
21
|
-
|
18
|
+
MushyCLI.set_special_values( { method: 'build', file: file } )
|
22
19
|
end
|
23
20
|
|
24
|
-
def self.
|
21
|
+
def self.set_special_values data
|
25
22
|
@special = data
|
26
23
|
end
|
27
24
|
|
28
|
-
def self.
|
25
|
+
def self.get_special_values
|
29
26
|
@special
|
30
27
|
end
|
31
|
-
|
32
28
|
end
|
33
29
|
|
34
30
|
MushyCLI.start(ARGV)
|
35
31
|
|
36
|
-
exit unless MushyCLI.
|
32
|
+
exit unless MushyCLI.get_special_values
|
37
33
|
|
38
34
|
require 'sinatra'
|
39
35
|
enable :run
|
40
36
|
|
41
|
-
the_file = MushyCLI.
|
37
|
+
the_file = MushyCLI.get_special_values[:file]
|
42
38
|
|
43
39
|
get '/' do
|
44
40
|
Mushy::Builder::Index.file
|
data/lib/mushy/builder/api.rb
CHANGED
@@ -1,191 +1,185 @@
|
|
1
1
|
require 'daemons'
|
2
2
|
|
3
|
-
module Mushy
|
3
|
+
module Mushy::Builder
|
4
|
+
end
|
4
5
|
|
5
|
-
|
6
|
+
module Mushy::Builder::Api
|
7
|
+
def self.run(data)
|
8
|
+
data = SymbolizedHash.new JSON.parse(data)
|
6
9
|
|
7
|
-
|
10
|
+
event = SymbolizedHash.new JSON.parse(data[:setup][:event].to_json)
|
8
11
|
|
9
|
-
|
12
|
+
config = SymbolizedHash.new data[:config]
|
10
13
|
|
11
|
-
|
14
|
+
flux = Mushy::Flow.build_flux({ type: data[:setup][:flux], config: config })
|
12
15
|
|
13
|
-
|
16
|
+
result = flux.execute event
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
flux = Mushy::Flow.build_flux( { type: data[:setup][:flux], config: config } )
|
18
|
-
|
19
|
-
result = flux.execute event
|
20
|
-
|
21
|
-
[result].flatten
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.save file, data
|
25
|
-
|
26
|
-
file = "#{file}.mushy" unless file.downcase.end_with?('.mushy')
|
27
|
-
|
28
|
-
data = SymbolizedHash.new JSON.parse(data)
|
29
|
-
Mushy::WriteFile.new.process( {}, { name: file, data: JSON.pretty_generate(data) })
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.start file, event
|
34
|
-
original_file = file
|
35
|
-
file = [file, "#{Dir.home}/.mushy/#{file}"]
|
36
|
-
.map { |x| (x.downcase.end_with?('.mushy') ? x : "#{x}.mushy") }
|
37
|
-
.select { |x| File.exist?(x) }
|
38
|
-
.first
|
39
|
-
|
40
|
-
unless file
|
41
|
-
puts "#{original_file} does not exist."
|
42
|
-
return
|
43
|
-
end
|
44
|
-
|
45
|
-
flow = File.open(file).read
|
46
|
-
flow = Mushy::Flow.parse flow
|
18
|
+
[result].flatten
|
19
|
+
end
|
47
20
|
|
48
|
-
|
21
|
+
def self.save(file, data)
|
22
|
+
file = "#{file}.mushy" unless file.downcase.end_with?('.mushy')
|
49
23
|
|
50
|
-
|
24
|
+
data = SymbolizedHash.new JSON.parse(data)
|
25
|
+
Mushy::WriteFile.new.process({}, { name: file, data: JSON.pretty_generate(data) })
|
26
|
+
end
|
51
27
|
|
52
|
-
|
28
|
+
def self.start(file, event)
|
29
|
+
original_file = file
|
30
|
+
file = [file, "#{Dir.home}/.mushy/#{file}"]
|
31
|
+
.map { |x| (x.downcase.end_with?('.mushy') ? x : "#{x}.mushy") }
|
32
|
+
.select { |x| File.exist?(x) }
|
33
|
+
.first
|
53
34
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end,
|
59
|
-
run_method: (s.config[:run_strategy] == 'daemon' ? :run_this_as_a_daemon : :run_this_inline),
|
60
|
-
}
|
61
|
-
}.group_by { |x| x[:run_method] }
|
35
|
+
unless file
|
36
|
+
puts "#{original_file} does not exist."
|
37
|
+
return
|
38
|
+
end
|
62
39
|
|
63
|
-
|
64
|
-
|
65
|
-
.map { |x| ->() { loop &x } }
|
66
|
-
.map { |x| run_this_as_a_daemon &x }
|
40
|
+
flow = File.open(file).read
|
41
|
+
flow = Mushy::Flow.parse flow
|
67
42
|
|
68
|
-
|
69
|
-
.map { |p| ->() { p[:flux].loop &p[:proc] } }
|
70
|
-
.map { |x| ->() { loop &x } }
|
71
|
-
.map { |x| run_this_inline &x }
|
43
|
+
service_fluxes = flow.fluxs.select { |x| x.respond_to? :loop }
|
72
44
|
|
73
|
-
|
74
|
-
end
|
45
|
+
pwd = Dir.pwd
|
75
46
|
|
76
|
-
|
47
|
+
if service_fluxes.any?
|
77
48
|
|
78
|
-
|
79
|
-
|
49
|
+
things = service_fluxes
|
50
|
+
.map do |s|
|
51
|
+
{
|
52
|
+
flux: s,
|
53
|
+
proc: lambda do |e|
|
54
|
+
Dir.chdir(pwd)
|
55
|
+
Mushy::Runner.new.start(e, s, flow)
|
56
|
+
end,
|
57
|
+
run_method: (s.config[:run_strategy] == 'daemon' ? :run_this_as_a_daemon : :run_this_inline)
|
58
|
+
}
|
59
|
+
end.group_by { |x| x[:run_method] }
|
80
60
|
|
81
|
-
|
82
|
-
|
83
|
-
|
61
|
+
(things[:run_this_as_a_daemon] || [])
|
62
|
+
.map { |p| -> { p[:flux].loop(&p[:proc]) } }
|
63
|
+
.map { |x| -> { loop(&x) } }
|
64
|
+
.map { |x| run_this_as_a_daemon(&x) }
|
84
65
|
|
85
|
-
|
86
|
-
|
87
|
-
|
66
|
+
(things[:run_this_inline] || [])
|
67
|
+
.map { |p| -> { p[:flux].loop(&p[:proc]) } }
|
68
|
+
.map { |x| -> { loop(&x) } }
|
69
|
+
.map { |x| run_this_inline(&x) }
|
88
70
|
|
89
|
-
|
90
|
-
|
91
|
-
data = JSON.parse File.open(file).read
|
71
|
+
exit
|
72
|
+
end
|
92
73
|
|
93
|
-
|
94
|
-
data['fluxs'] = organize_as_a_flattened_tree_based_on_parents data['fluxs']
|
74
|
+
cli_flux = flow.fluxs.select { |x| x.is_a?(Mushy::Cli) }.first
|
95
75
|
|
96
|
-
|
97
|
-
|
98
|
-
{ fluxs: [] }
|
99
|
-
end
|
76
|
+
Mushy::Runner.new.start event, cli_flux, flow
|
77
|
+
end
|
100
78
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
.each { |x| x['parents'] = [x['parent']].select { |y| y } }
|
105
|
-
fluxs
|
106
|
-
.select { |x| x['parent'] }
|
107
|
-
.each { |x| x.delete 'parent' }
|
108
|
-
fluxs
|
109
|
-
.select { |x| x['parents'] }
|
110
|
-
.each { |x| x['parents'] = x['parents'].select { |y| y } }
|
111
|
-
|
112
|
-
fluxs
|
113
|
-
end
|
79
|
+
def self.run_this_inline(&block)
|
80
|
+
block.call
|
81
|
+
end
|
114
82
|
|
115
|
-
|
116
|
-
|
83
|
+
def self.run_this_as_a_daemon(&block)
|
84
|
+
Daemons.call(&block).pid.pid
|
85
|
+
end
|
117
86
|
|
118
|
-
|
87
|
+
def self.get_flow(file)
|
88
|
+
file = "#{file}.mushy" unless file.downcase.end_with?('.mushy')
|
89
|
+
data = JSON.parse File.open(file).read
|
119
90
|
|
120
|
-
|
91
|
+
data['fluxs'] = standardize_these data['fluxs']
|
92
|
+
data['fluxs'] = organize_as_a_flattened_tree_based_on_parents data['fluxs']
|
121
93
|
|
122
|
-
|
94
|
+
data
|
95
|
+
rescue
|
96
|
+
{ fluxs: [] }
|
97
|
+
end
|
123
98
|
|
124
|
-
|
125
|
-
|
126
|
-
|
99
|
+
def self.standardize_these(fluxs)
|
100
|
+
fluxs
|
101
|
+
.reject { |x| x['parents'] }
|
102
|
+
.each { |x| x['parents'] = [x['parent']].select { |y| y } }
|
103
|
+
fluxs
|
104
|
+
.select { |x| x['parent'] }
|
105
|
+
.each { |x| x.delete 'parent' }
|
106
|
+
fluxs
|
107
|
+
.select { |x| x['parents'] }
|
108
|
+
.each { |x| x['parents'] = x['parents'].select { |y| y } }
|
109
|
+
|
110
|
+
fluxs
|
111
|
+
end
|
127
112
|
|
128
|
-
|
113
|
+
def self.organize_as_a_flattened_tree_based_on_parents(fluxs)
|
114
|
+
fluxs = fluxs.sort_by { |x| x['parents'].count }
|
129
115
|
|
130
|
-
|
116
|
+
new_fluxs = [fluxs.first]
|
131
117
|
|
132
|
-
|
118
|
+
loop do
|
119
|
+
next_fluxs = fluxs.select { |x| x['parents'].include? new_fluxs[-1]['id'] }
|
133
120
|
|
134
|
-
|
121
|
+
unless next_fluxs.any?
|
122
|
+
next_fluxs = [fluxs.reject { |x| new_fluxs.map { |y| y['id'] }.include?(x['id']) }[0]].select { |x| x }
|
135
123
|
end
|
136
124
|
|
137
|
-
|
138
|
-
{
|
139
|
-
fluxs: Mushy::Flux.all.select { |x| x.respond_to? :details }.select { |x| x.details }.map do |flux|
|
140
|
-
details = flux.details
|
141
|
-
|
142
|
-
details[:documentation] = Documentation.build_from details
|
143
|
-
|
144
|
-
details[:config][:incoming_split] = { type: 'text', shrink: true, description: 'Split an incoming event into multiple events by this key, an each event will be processed independently.', default: '' }
|
145
|
-
details[:config][:outgoing_split] = { type: 'text', shrink: true, description: 'Split an outgoing event into multiple events by this key.', default: '' }
|
146
|
-
details[:config][:merge] = { type: 'text', shrink: true, description: 'A comma-delimited list of fields from the event to carry through. Use * to merge all fields.', default: '' }
|
147
|
-
details[:config][:group] = { type: 'text', shrink: true, description: 'Group events by this key, with the value as the key. If a group key is provided like group_by|group_key, then multiple events with the results under group_key will be returned.', default: '' }
|
148
|
-
details[:config][:limit] = { type: 'integer', shrink: true, description: 'Limit the number of events to this number.', default: '' }
|
149
|
-
details[:config][:join] = { type: 'text', shrink: true, description: 'Join all of the events from this flux into one event, under this name.', default: '' }
|
150
|
-
details[:config][:sort] = { type: 'text', shrink: true, description: 'Sort by this key.', default: '' }
|
151
|
-
details[:config][:ignore] = { type: 'text', shrink: true, description: 'Ignore these keys.', value: '', default: '' }
|
152
|
-
details[:config][:model] = { type: 'keyvalue', shrink: true, description: 'Reshape the outgoing events.', value: {}, default: {} }
|
153
|
-
|
154
|
-
details[:config][:error_strategy] = {
|
155
|
-
description: 'Error strategy. (return to return an event with "exception" returning the error, or ignore to ignore the exception)',
|
156
|
-
type: 'select',
|
157
|
-
options: ['', 'return', 'ignore'],
|
158
|
-
value: '',
|
159
|
-
shrink: true,
|
160
|
-
}
|
161
|
-
|
162
|
-
if flux.new.respond_to? :loop
|
163
|
-
details[:config][:run_strategy] = {
|
164
|
-
description: 'Run this using this strategy. (select "daemon" if this should be run in the background)',
|
165
|
-
type: 'select',
|
166
|
-
options: ['', 'inline', 'daemon'],
|
167
|
-
value: '',
|
168
|
-
shrink: true,
|
169
|
-
}
|
170
|
-
end
|
171
|
-
|
172
|
-
details[:config]
|
173
|
-
.select { |_, v| v[:type] == 'keyvalue' }
|
174
|
-
.select { |_, v| v[:editors].nil? }
|
175
|
-
.each do |_, v|
|
176
|
-
v[:editors] = [
|
177
|
-
{ id: 'new_key', target: 'key', field: { type: 'text', value: '', default: '' } },
|
178
|
-
{ id: 'new_value', target: 'value', field: { type: 'text', value: '', default: '' } }
|
179
|
-
]
|
180
|
-
end
|
181
|
-
|
182
|
-
details
|
183
|
-
end.sort_by { |x| x[:name] }
|
184
|
-
}
|
185
|
-
end
|
125
|
+
new_fluxs = [new_fluxs, next_fluxs].flatten
|
186
126
|
|
127
|
+
break unless next_fluxs.any?
|
187
128
|
end
|
188
129
|
|
130
|
+
new_fluxs
|
189
131
|
end
|
190
132
|
|
191
|
-
|
133
|
+
def self.get_fluxs
|
134
|
+
{
|
135
|
+
fluxs: Mushy::Flux.all
|
136
|
+
.select { |x| x.respond_to? :details }
|
137
|
+
.select(&:details)
|
138
|
+
.map do |flux|
|
139
|
+
details = flux.details
|
140
|
+
|
141
|
+
details[:documentation] = Mushy::Builder::Documentation.build_from details
|
142
|
+
|
143
|
+
details[:config][:incoming_split] = { type: 'text', shrink: true, description: 'Split an incoming event into multiple events by this key, an each event will be processed independently.', default: '' }
|
144
|
+
details[:config][:outgoing_split] = { type: 'text', shrink: true, description: 'Split an outgoing event into multiple events by this key.', default: '' }
|
145
|
+
details[:config][:merge] = { type: 'text', shrink: true, description: 'A comma-delimited list of fields from the event to carry through. Use * to merge all fields.', default: '' }
|
146
|
+
details[:config][:group] = { type: 'text', shrink: true, description: 'Group events by this key, with the value as the key. If a group key is provided like group_by|group_key, then multiple events with the results under group_key will be returned.', default: '' }
|
147
|
+
details[:config][:limit] = { type: 'integer', shrink: true, description: 'Limit the number of events to this number.', default: '' }
|
148
|
+
details[:config][:join] = { type: 'text', shrink: true, description: 'Join all of the events from this flux into one event, under this name.', default: '' }
|
149
|
+
details[:config][:sort] = { type: 'text', shrink: true, description: 'Sort by this key.', default: '' }
|
150
|
+
details[:config][:ignore] = { type: 'text', shrink: true, description: 'Ignore these keys.', value: '', default: '' }
|
151
|
+
details[:config][:model] = { type: 'keyvalue', shrink: true, description: 'Reshape the outgoing events.', value: {}, default: {} }
|
152
|
+
|
153
|
+
details[:config][:error_strategy] = {
|
154
|
+
description: 'Error strategy. (return to return an event with "exception" returning the error, or ignore to ignore the exception)',
|
155
|
+
type: 'select',
|
156
|
+
options: ['', 'return', 'ignore'],
|
157
|
+
value: '',
|
158
|
+
shrink: true
|
159
|
+
}
|
160
|
+
|
161
|
+
if flux.new.respond_to? :loop
|
162
|
+
details[:config][:run_strategy] = {
|
163
|
+
description: 'Run this using this strategy. (select "daemon" if this should be run in the background)',
|
164
|
+
type: 'select',
|
165
|
+
options: ['', 'inline', 'daemon'],
|
166
|
+
value: '',
|
167
|
+
shrink: true
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
details[:config]
|
172
|
+
.select { |_, v| v[:type] == 'keyvalue' }
|
173
|
+
.select { |_, v| v[:editors].nil? }
|
174
|
+
.each do |_, v|
|
175
|
+
v[:editors] = [
|
176
|
+
{ id: 'new_key', target: 'key', field: { type: 'text', value: '', default: '' } },
|
177
|
+
{ id: 'new_value', target: 'value', field: { type: 'text', value: '', default: '' } }
|
178
|
+
]
|
179
|
+
end
|
180
|
+
|
181
|
+
details
|
182
|
+
end.sort_by { |x| x[:name] }
|
183
|
+
}
|
184
|
+
end
|
185
|
+
end
|
data/lib/mushy/builder/index.rb
CHANGED
@@ -479,6 +479,7 @@ module Mushy
|
|
479
479
|
Vue.set(app.results, 'errorMessage', '');
|
480
480
|
var the_setup = thingToData(app.setup);
|
481
481
|
the_setup.event = c.test_event;
|
482
|
+
c['_test_mode'] = true;
|
482
483
|
axios.post('/run', { config: c, setup: the_setup })
|
483
484
|
.then(function(r){
|
484
485
|
Vue.set(app.setup.testResultModal, 'is-active', true);
|
data/lib/mushy/flux.rb
CHANGED
@@ -63,7 +63,8 @@ module Mushy
|
|
63
63
|
the_original_join = mashed_config[:join]
|
64
64
|
mashed_config[:join] = nil if mashed_config[:incoming_split]
|
65
65
|
|
66
|
-
|
66
|
+
method = config[:_test_mode] && respond_to?(:test) ? :test : :process
|
67
|
+
results = send(method, event, mashed_config)
|
67
68
|
|
68
69
|
returned_one_result = results.is_a?(Hash)
|
69
70
|
|
data/lib/mushy/fluxs/bash.rb
CHANGED
@@ -1,66 +1,58 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
1
|
+
class Mushy::Bash < Mushy::Flux
|
2
|
+
def self.details
|
3
|
+
{
|
4
|
+
name: 'Bash',
|
5
|
+
title: 'Execute a command via bash',
|
6
|
+
description: 'Run a bash command.',
|
7
|
+
fluxGroup: { name: 'Execute' },
|
8
|
+
config: {
|
9
|
+
command: {
|
10
|
+
description: 'The command to run in bash.',
|
11
|
+
type: 'text',
|
12
|
+
value: '{{command}}'
|
13
|
+
},
|
14
|
+
directory: {
|
15
|
+
description: 'The working directory in which the command will be run.',
|
16
|
+
type: 'text',
|
17
|
+
shrink: true,
|
18
|
+
value: ''
|
19
|
+
}
|
20
|
+
},
|
21
|
+
examples: {
|
22
|
+
'Successful Call' => {
|
23
|
+
description: 'This will run the ls command and return the full bash result.',
|
24
|
+
input: { command: 'ls' },
|
25
|
+
result: {
|
26
|
+
text: "bin\nblue_heart.png\nthe_output.txt\n",
|
27
|
+
success: true,
|
28
|
+
exit_code: 0
|
29
|
+
}
|
23
30
|
},
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"text": "bin\nblue_heart.png\nthe_output.txt\n",
|
32
|
-
"success": true,
|
33
|
-
"exit_code": 0
|
34
|
-
}
|
35
|
-
},
|
36
|
-
"Failed Call" => {
|
37
|
-
description: 'This is an example of what happens when the command fails.',
|
38
|
-
input: { command: 'rm file_that_does_not_exist.txt' },
|
39
|
-
result: {
|
40
|
-
"text": "",
|
41
|
-
"success": false,
|
42
|
-
"exit_code": 256
|
43
|
-
}
|
44
|
-
},
|
31
|
+
'Failed Call' => {
|
32
|
+
description: 'This is an example of what happens when the command fails.',
|
33
|
+
input: { command: 'rm file_that_does_not_exist.txt' },
|
34
|
+
result: {
|
35
|
+
text: '',
|
36
|
+
success: false,
|
37
|
+
exit_code: 256
|
45
38
|
}
|
39
|
+
}
|
46
40
|
}
|
47
|
-
|
48
|
-
|
49
|
-
def process event, config
|
50
|
-
command = config[:command]
|
41
|
+
}
|
42
|
+
end
|
51
43
|
|
52
|
-
|
44
|
+
def process(_, config)
|
45
|
+
command = config[:command]
|
53
46
|
|
54
|
-
|
47
|
+
command = "cd #{config[:directory]};#{command}" if config[:directory]
|
55
48
|
|
56
|
-
|
57
|
-
{
|
58
|
-
text: text,
|
59
|
-
success: result.success?,
|
60
|
-
exit_code: result.to_i,
|
61
|
-
}
|
62
|
-
end
|
49
|
+
text = `#{command}`
|
63
50
|
|
51
|
+
result = $?
|
52
|
+
{
|
53
|
+
text: text,
|
54
|
+
success: result.success?,
|
55
|
+
exit_code: result.to_i
|
56
|
+
}
|
64
57
|
end
|
65
|
-
|
66
|
-
end
|
58
|
+
end
|