a4tools 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bundle/install.log +38 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/a4tools.gemspec +38 -0
- data/bin/deploy_latest_clients +32 -0
- data/bin/devsite_config_server +48 -0
- data/bin/netshell +23 -0
- data/bin/update_server +101 -0
- data/bin/usher +54 -0
- data/lib/a4tools.rb +61 -0
- data/lib/a4tools/version.rb +3 -0
- data/lib/acres_client.rb +376 -0
- data/lib/clients/caching_client.rb +151 -0
- data/lib/clients/deployment_client.rb +53 -0
- data/lib/clients/kai_config_client.rb +39 -0
- data/lib/clients/usher_client.rb +72 -0
- data/lib/clients/usher_mgmt_client.rb +201 -0
- data/lib/event_manager.rb +24 -0
- data/lib/events.json +1 -0
- data/lib/net_shell/builtin_command.rb +312 -0
- data/lib/net_shell/builtin_commands/build.rb +251 -0
- data/lib/net_shell/builtin_commands/cd.rb +12 -0
- data/lib/net_shell/builtin_commands/connect.rb +122 -0
- data/lib/net_shell/builtin_commands/deploy.rb +280 -0
- data/lib/net_shell/builtin_commands/disconnect.rb +15 -0
- data/lib/net_shell/builtin_commands/excerpt.rb +97 -0
- data/lib/net_shell/builtin_commands/exit.rb +7 -0
- data/lib/net_shell/builtin_commands/get.rb +38 -0
- data/lib/net_shell/builtin_commands/help.rb +40 -0
- data/lib/net_shell/builtin_commands/host.rb +126 -0
- data/lib/net_shell/builtin_commands/inject.rb +42 -0
- data/lib/net_shell/builtin_commands/jsoncache.rb +80 -0
- data/lib/net_shell/builtin_commands/kai_event.rb +151 -0
- data/lib/net_shell/builtin_commands/persist.rb +24 -0
- data/lib/net_shell/builtin_commands/pwd.rb +6 -0
- data/lib/net_shell/builtin_commands/recap.rb +188 -0
- data/lib/net_shell/builtin_commands/references.rb +63 -0
- data/lib/net_shell/builtin_commands/select.rb +36 -0
- data/lib/net_shell/builtin_commands/send.rb +74 -0
- data/lib/net_shell/builtin_commands/set.rb +29 -0
- data/lib/net_shell/builtin_commands/show.rb +183 -0
- data/lib/net_shell/builtin_commands/site.rb +122 -0
- data/lib/net_shell/builtin_commands/ssh.rb +62 -0
- data/lib/net_shell/builtin_commands/talk.rb +90 -0
- data/lib/net_shell/builtin_commands/translate.rb +45 -0
- data/lib/net_shell/builtin_commands/unset.rb +14 -0
- data/lib/net_shell/builtin_commands/usher.rb +55 -0
- data/lib/net_shell/builtin_commands/usher_device.rb +39 -0
- data/lib/net_shell/builtin_commands/usher_site.rb +245 -0
- data/lib/net_shell/builtin_commands/usherm_connect.rb +21 -0
- data/lib/net_shell/colors.rb +149 -0
- data/lib/net_shell/command.rb +97 -0
- data/lib/net_shell/io.rb +132 -0
- data/lib/net_shell/net_shell.rb +396 -0
- data/lib/net_shell/prompt.rb +335 -0
- data/lib/object_builder/definitions/app_info_for_script.rb +83 -0
- data/lib/object_builder/definitions/connection_request.rb +28 -0
- data/lib/object_builder/definitions/device_info_for_system.rb +37 -0
- data/lib/object_builder/object_builder.rb +145 -0
- data/lib/talk.json +1 -0
- data/lib/talk_consumer.rb +235 -0
- metadata +279 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'talk'
|
2
|
+
|
3
|
+
category "Talk"
|
4
|
+
description "Select a given version of Talk"
|
5
|
+
usage "[branch_name]"
|
6
|
+
help <<-EOS
|
7
|
+
Selects a given version of Talk for use in netshell.
|
8
|
+
EOS
|
9
|
+
|
10
|
+
opt :repo, "Use the indicated local repository instead of default"
|
11
|
+
opt :commit, "Select a Talk version in terms of a git commit hash instead of a branch", :type => :string
|
12
|
+
opt :update, "Ensure that the acres4/talk repository is up-to-date"
|
13
|
+
opt :jeb, "Select a Talk version in terms of a jeb instead of a branch", :type => :string
|
14
|
+
opt :talkversion, "Use a specific version of maketalk", :type => :string
|
15
|
+
|
16
|
+
tab :commit do
|
17
|
+
return list_commits.map { |commit| commit[:hash] }
|
18
|
+
end
|
19
|
+
|
20
|
+
tab :jeb do
|
21
|
+
return list_commits.map { |commit| commit[:jeb] }
|
22
|
+
end
|
23
|
+
|
24
|
+
tab 0 do
|
25
|
+
return list_branches
|
26
|
+
end
|
27
|
+
|
28
|
+
validate do
|
29
|
+
return true if args.length == 2 and !params[:jeb] and !params[:commit]
|
30
|
+
return true if args.length == 1 and (params[:jeb] or params[:commit])
|
31
|
+
end
|
32
|
+
|
33
|
+
run do
|
34
|
+
clone_repo unless repo_exists?
|
35
|
+
fetch if params[:update]
|
36
|
+
target = params[:jeb] || params[:commit] || args[1]
|
37
|
+
checkout(target)
|
38
|
+
make_talk
|
39
|
+
""
|
40
|
+
end
|
41
|
+
|
42
|
+
###
|
43
|
+
|
44
|
+
def repo
|
45
|
+
params[:repo] || @shell.shelldir("talk")
|
46
|
+
end
|
47
|
+
|
48
|
+
def talk_json_path
|
49
|
+
$localtalk
|
50
|
+
end
|
51
|
+
|
52
|
+
def repo_exists?
|
53
|
+
File.exist?(File.join(repo, ".git"))
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch
|
57
|
+
`cd #{repo} && git fetch`
|
58
|
+
end
|
59
|
+
|
60
|
+
def checkout(id)
|
61
|
+
id = Jebediah.new.process(id)[:result] if params[:jeb]
|
62
|
+
`cd #{repo} && git checkout origin/#{id}`
|
63
|
+
end
|
64
|
+
|
65
|
+
def make_talk
|
66
|
+
version = params[:talkversion] ? " _#{params[:talkversion]}_" : ""
|
67
|
+
`cd #{repo} && maketalk#{version} --upgrade --json info > #{talk_json_path}`
|
68
|
+
refresh_talk
|
69
|
+
end
|
70
|
+
|
71
|
+
def clone_repo
|
72
|
+
puts "Cloning respository into #{repo}"
|
73
|
+
`cd #{File.join(repo, "..")} && git clone git@github.com:acres4/talk`
|
74
|
+
end
|
75
|
+
|
76
|
+
def list_commits
|
77
|
+
return [] unless repo_exists?
|
78
|
+
jeb = Jebediah.new
|
79
|
+
`cd #{repo} && git log --pretty=format:"%an:%h:%s"`.split("\n").map do |commit|
|
80
|
+
components = commit.split(":")
|
81
|
+
{ author:components[0], hash:components[1], jeb:jeb.process(components[1])[:result].join(" "), notes:components[2] }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def list_branches
|
86
|
+
return [] unless repo_exists?
|
87
|
+
branches = `cd #{repo} && git branch -a --no-color`.split("\n").map { |x| x.gsub(/^\*? */,"").gsub(/^.*-> /,"").gsub(/^(remotes\/)?origin\//,"") }
|
88
|
+
branches &= branches # remove dupes
|
89
|
+
branches.sort
|
90
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
category "Talk"
|
2
|
+
description "Find talk entities matching a constant value or string"
|
3
|
+
usage "value"
|
4
|
+
|
5
|
+
help <<-EOS
|
6
|
+
Attempts to find a string or number in a Talk glossary or enumeration. All matching constants are displayed. Constants are case-sensitive.
|
7
|
+
|
8
|
+
Example:
|
9
|
+
Show all constants equal to 10
|
10
|
+
#{command} 10
|
11
|
+
|
12
|
+
Show all terms equal to Mercury
|
13
|
+
#{command} Mercury
|
14
|
+
|
15
|
+
EOS
|
16
|
+
|
17
|
+
validate { args.length == 2 }
|
18
|
+
|
19
|
+
tab 0 do
|
20
|
+
(talk.definition[:glossary].inject([]) { |terms, glossary| terms + glossary[:term] }).map { |t| t[:value] }
|
21
|
+
end
|
22
|
+
|
23
|
+
run { translate(args[1]) }
|
24
|
+
|
25
|
+
###
|
26
|
+
|
27
|
+
def translate(value)
|
28
|
+
is_numeric = (not(value.is_a?(String)) or value.match(/^[-+]?[0-9]+(.[0-9]+)?$/) != nil)
|
29
|
+
|
30
|
+
candidates = case is_numeric
|
31
|
+
when true
|
32
|
+
talk.definition[:enumeration].inject([]) do |c, enum|
|
33
|
+
c + (enum[:constant].select { |constant| constant[:value] == value.to_f }).map { |x| { collection:enum, constant:x } }
|
34
|
+
end
|
35
|
+
when false
|
36
|
+
talk.definition[:glossary].inject([]) do |c, glossary|
|
37
|
+
c + (glossary[:term].select { |term| term[:value] == value }).map { |x| { collection:glossary, constant:x } }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
return "No matches for #{value}" if candidates.empty?
|
42
|
+
|
43
|
+
(candidates.map { |item| "#{item[:constant][:name].style(:constant_name)} (#{talk.truncated_name item[:collection][:name].style(:collection_name)}): #{item[:constant][:description]}" }).join("\n")
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
category "Usher"
|
2
|
+
description "Determine usher information for current IP and serial number"
|
3
|
+
usage "[serial_number]"
|
4
|
+
|
5
|
+
help <<-EOS
|
6
|
+
Prints usher information for clients requesting from the specified IP address and serial number.
|
7
|
+
|
8
|
+
If no IP or serial number are supplied, #{command.style(:command)} uses the host's actual IP address and the "#{"serial".style(:environment)}" environment variable.
|
9
|
+
EOS
|
10
|
+
|
11
|
+
opt :full, "Instruct usher to give all possible hosts instead of trying to guess which host is appropriate. (e.g. use \"#{"usherAlt".style(:method_name)}\" method)"
|
12
|
+
opt :json, "Print result as JSON"
|
13
|
+
opt :url, "Use usher mirror at specified URL", :type => :string
|
14
|
+
opt :ip, "Use usher as if coming from IP address", :type => :string
|
15
|
+
|
16
|
+
tab 0 do
|
17
|
+
(usherm[:devSiteInfoSync][:devSiteInfoArray].map { |info| info[:serialNumber] }) rescue []
|
18
|
+
end
|
19
|
+
|
20
|
+
run do
|
21
|
+
params[:serial] = args[1] if args.length > 0
|
22
|
+
result = A4Tools::UsherClient.new(params[:url]).list_servers_raw(params)
|
23
|
+
return params[:json] ? result.body : render_result(result)
|
24
|
+
end
|
25
|
+
|
26
|
+
###
|
27
|
+
|
28
|
+
def render_result(response)
|
29
|
+
return ("Unable to contact usher") if response.nil?
|
30
|
+
return "Usher returned status #{response.code}" if response.code.to_i < 200 or response.code.to_i >= 300
|
31
|
+
begin
|
32
|
+
payload = JSON.parse(response.body)
|
33
|
+
return ("Usher returned non-JSON RPC response") unless payload["jsonrpc"] == "2.0"
|
34
|
+
return ("Usher returned error: \"#{payload["error"]}\"") unless payload["error"].nil?
|
35
|
+
return ("Usher did not include result") if payload["result"].nil?
|
36
|
+
return ("Usher server returned non-usher response") unless payload["result"]["className"] == "com.acres4.common.info.usher.UsherResponse"
|
37
|
+
return ("Usher response did not include body") if payload["result"]["body"].nil?
|
38
|
+
return render_success(payload["result"]["body"])
|
39
|
+
rescue JSON::ParserError
|
40
|
+
return ("Usher returned non-JSON response")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_success(result)
|
45
|
+
s = " Site ID: #{result["siteID"]}\n"
|
46
|
+
s += " Site name: #{result["siteName"]}\n"
|
47
|
+
s += "Remote address: #{result["remoteAddr"]}\n\n"
|
48
|
+
|
49
|
+
result["hosts"].each do |host|
|
50
|
+
s += "Product: #{host["product"]} (#{host["host"]}:#{host["port"]})\n\t#{host["url"]}, #{host["ctx"]}, System #{host["systemId"]}\n\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
s
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
category "Usher"
|
2
|
+
description "Manage development devices in Usher"
|
3
|
+
usage [
|
4
|
+
"[siteId] -- list development devices in usher",
|
5
|
+
"serial siteId [comment] -- update development device with specified serial number"
|
6
|
+
]
|
7
|
+
|
8
|
+
help <<-EOS
|
9
|
+
Updates the development device with the specified serial in UsherM to use the specified site ID. If a comment supplied, the device's comment field is updated to the supplied comment.
|
10
|
+
|
11
|
+
If no such serial is registered, a new registration is created.
|
12
|
+
|
13
|
+
Must have previously connected to UsherM using #{"usherm_connect".style(:environment)}.
|
14
|
+
EOS
|
15
|
+
|
16
|
+
validate do
|
17
|
+
return show_error("must connect usherm first") if usherm.nil?
|
18
|
+
args.length <= 4
|
19
|
+
end
|
20
|
+
|
21
|
+
tab 0 do
|
22
|
+
usherm[:devSiteInfoSync][:devSiteInfoArray].map { |info| info[:serialNumber] } rescue []
|
23
|
+
end
|
24
|
+
|
25
|
+
run do
|
26
|
+
if args.length == 1 or args.length == 2 then
|
27
|
+
usherm.ensure_fresh
|
28
|
+
devices = usherm[:devSiteInfoSync][:devSiteInfoArray]
|
29
|
+
devices = devices.reject { |info| info[:idSite].to_i != args[1].to_i } if args.length == 2
|
30
|
+
(devices.map do |info|
|
31
|
+
"#{sprintf("%3d", info[:idDevSiteInfo]).to_s.light_blue} #{info[:serialNumber].red} -> Site ##{info[:idSite].to_s.bold}, #{info[:description]}"
|
32
|
+
end).join("\n")
|
33
|
+
else
|
34
|
+
description = args[3] rescue nil
|
35
|
+
usherm.update_device(args[1], description, args[2].to_i, "Added via netshell by #{`whoami`.chomp}@#{`hostname`.chomp}")
|
36
|
+
""
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,245 @@
|
|
1
|
+
<<-EOS
|
2
|
+
|
3
|
+
net> site --add
|
4
|
+
|
5
|
+
What kind of site are we adding?
|
6
|
+
1) Development
|
7
|
+
2) Pulltab
|
8
|
+
3) Arcade
|
9
|
+
4) Production
|
10
|
+
|
11
|
+
Property name (e.g. "Casino del Sol")
|
12
|
+
> Casino del Sol
|
13
|
+
|
14
|
+
Geographic State
|
15
|
+
> AZ
|
16
|
+
> Arizona
|
17
|
+
|
18
|
+
Business day start (24-hour clock, e.g. 1300) # (actually supports H, HH, HMM, HH:MM)
|
19
|
+
> 12:35
|
20
|
+
> 0500
|
21
|
+
> 500
|
22
|
+
> 12
|
23
|
+
> 0
|
24
|
+
|
25
|
+
Host
|
26
|
+
> dev02.acres4.netx
|
27
|
+
That doesn't seem to be a valid usher host.
|
28
|
+
> dev02.acres4.net
|
29
|
+
|
30
|
+
Casino del Sol will accept the following CIDR blocks:
|
31
|
+
No CIDR blocks registered
|
32
|
+
|
33
|
+
You can A)dd or D)elete CIDR blocks, or C)ontinue with configuration
|
34
|
+
> A
|
35
|
+
|
36
|
+
CIDR block (e.g. 1.2.3.4/16):
|
37
|
+
> 1.2.3.4/16
|
38
|
+
Description for 1.2.3.4/16:
|
39
|
+
> Local wireless
|
40
|
+
|
41
|
+
Casino del Sol will accept the following CIDR blocks:
|
42
|
+
1.2.3.4/16 (Local wireless)
|
43
|
+
|
44
|
+
You can A)dd or D)elete CIDR blocks, or C)ontinue with configuration
|
45
|
+
> C
|
46
|
+
|
47
|
+
Casino del Sol will advertise the following servers:
|
48
|
+
1) Mercury (dev02.acres4.net)
|
49
|
+
2) Central (dev02.acres4.net)
|
50
|
+
3) Deployment (deployments.acres4.net)
|
51
|
+
C) Cancel
|
52
|
+
|
53
|
+
You can A)dd or D)elete servers, or C)ontinue with configuration
|
54
|
+
> D
|
55
|
+
|
56
|
+
Which server would you like Casino del Sol to stop advertising?
|
57
|
+
> 2
|
58
|
+
|
59
|
+
Casino del Sol will advertise the following servers:
|
60
|
+
1) Mercury (dev02.acres4.net)
|
61
|
+
2) Deployment (deployments.acres4.net)
|
62
|
+
|
63
|
+
You can A)dd or D)elete servers, or C)ontinue with configuration
|
64
|
+
> A
|
65
|
+
|
66
|
+
Which server would you like to advertise?
|
67
|
+
1) Central. Central Database Server.
|
68
|
+
2) Monitor.
|
69
|
+
3) Promo. Promotional contest server.
|
70
|
+
4) Switchboard. Switchboard server.
|
71
|
+
5) Kaicheck. Kaicheck diagnostic server.
|
72
|
+
6) Kaivoip. Voice server.
|
73
|
+
7) PickPromo.
|
74
|
+
8) Kailite.
|
75
|
+
C) Cancel
|
76
|
+
|
77
|
+
> 6
|
78
|
+
|
79
|
+
Which host would you like to use as a Kaivoip server?
|
80
|
+
1) dev02.acres4.net
|
81
|
+
2) example.com
|
82
|
+
C) Cancel
|
83
|
+
|
84
|
+
> 2
|
85
|
+
|
86
|
+
Casino del Sol will advertise the following servers:
|
87
|
+
1) Mercury (dev02.acres4.net)
|
88
|
+
2) Deployment (deployments.acres4.net)
|
89
|
+
3) Kaivoip (example.com)
|
90
|
+
|
91
|
+
You can A)dd or D)elete servers, or C)ontinue with configuration
|
92
|
+
> C
|
93
|
+
|
94
|
+
Casino del Sol has the following applications deployed:
|
95
|
+
No applications deployed
|
96
|
+
|
97
|
+
You can A)dd an application, D)elete an application, or C)ontinue with configuration
|
98
|
+
> A
|
99
|
+
|
100
|
+
What application would you like to deploy to Casino del Sol?
|
101
|
+
1) FrontLine
|
102
|
+
2) Supervisor
|
103
|
+
3) Kaicheck
|
104
|
+
...
|
105
|
+
> 3
|
106
|
+
|
107
|
+
Here are recent versions of Kaicheck:
|
108
|
+
1) [rc/lima] strangely stroked slug
|
109
|
+
2) [rc/juliet] gently torched dog
|
110
|
+
...
|
111
|
+
|
112
|
+
Enter the number of the build you want to deploy, or the name of another build if you know it:
|
113
|
+
> unsettlingly aroused llama
|
114
|
+
|
115
|
+
There doesn't seem to a build named that for Front Line.
|
116
|
+
> 2
|
117
|
+
|
118
|
+
Casino del Sol has the following applications deployed:
|
119
|
+
1) Supervisor (rc/juliet): gently torched dog
|
120
|
+
2) FrontLine (rc/juliet): gently torched dog
|
121
|
+
|
122
|
+
You can A)dd an application, D)elete an application, or C)ontinue with configuration
|
123
|
+
> C
|
124
|
+
|
125
|
+
CONGRATULATIONS!
|
126
|
+
You've finished defining a new site. Here's what we've wound up with:
|
127
|
+
|
128
|
+
Production site
|
129
|
+
example ("example")
|
130
|
+
Business day start: 02:00, US/Nevada
|
131
|
+
Base host: ip-10-1-0-24.acres4.net
|
132
|
+
|
133
|
+
CIDR blocks:
|
134
|
+
1.2.3.4/16 Local wifi
|
135
|
+
|
136
|
+
Advertised servers:
|
137
|
+
Mercury dev02.acres4.net
|
138
|
+
Deployment deployments.acres4.net
|
139
|
+
Kaivoip example.com
|
140
|
+
|
141
|
+
Applications:
|
142
|
+
FrontLine [rc/juliet] gently stroked dog
|
143
|
+
Supervisor [rc/juliet] gently stroked dog
|
144
|
+
|
145
|
+
Are these settings correct? [y/n]
|
146
|
+
>
|
147
|
+
Are these settings correct? [y/n]
|
148
|
+
> n
|
149
|
+
|
150
|
+
Pick a section to edit:
|
151
|
+
1) Basic site info
|
152
|
+
2) CIDR blocks
|
153
|
+
3) Advertised servers
|
154
|
+
4) Applications
|
155
|
+
C) Cancel
|
156
|
+
|
157
|
+
> C
|
158
|
+
|
159
|
+
CONGRATULATIONS!
|
160
|
+
You've finished defining a new site. Here's what we've wound up with:
|
161
|
+
|
162
|
+
Production site
|
163
|
+
casino-del-sol ("Casino del Sol")
|
164
|
+
Business day start: 04:00, US/Arizona
|
165
|
+
Base host: dev02.acres4.net
|
166
|
+
|
167
|
+
CIDR blocks:
|
168
|
+
10.1.0.128/30 Home
|
169
|
+
|
170
|
+
Advertised servers:
|
171
|
+
Mercury dev02.acres4.net
|
172
|
+
Deployment deployments.acres4.net
|
173
|
+
|
174
|
+
Applications:
|
175
|
+
FrontLine [rc/juliet] gently stroked dog
|
176
|
+
Supervisor [rc/juliet] gently stroked dog
|
177
|
+
|
178
|
+
Are these settings correct? [y/n]
|
179
|
+
> y
|
180
|
+
|
181
|
+
Creating site record...
|
182
|
+
Updating CIDR preferences...
|
183
|
+
Updating advertised server preferences...
|
184
|
+
Publishing usher changes...
|
185
|
+
Deploying applications...
|
186
|
+
Done!
|
187
|
+
|
188
|
+
NEW SITE ISSUED: example, site #36, system #1234
|
189
|
+
|
190
|
+
EOS
|
191
|
+
|
192
|
+
category "Usher"
|
193
|
+
description "Add, update and delete sites in usher"
|
194
|
+
usage [
|
195
|
+
"add",
|
196
|
+
"edit site_id",
|
197
|
+
"delete site_id"
|
198
|
+
]
|
199
|
+
|
200
|
+
tab 0 do
|
201
|
+
["add", "edit", "delete"]
|
202
|
+
end
|
203
|
+
|
204
|
+
tab 1 do
|
205
|
+
usherm[:usherSystemId][:usherSites].map { |site| site[:idSite] } +
|
206
|
+
usherm[:usherSystemId][:usherSites].map { |site| site[:name] }
|
207
|
+
end
|
208
|
+
|
209
|
+
validate do
|
210
|
+
return show_error("must connect usherm first") if usherm.nil?
|
211
|
+
return false if args.length == 1
|
212
|
+
case args[1]
|
213
|
+
when "add"
|
214
|
+
args.length == 2
|
215
|
+
when "edit", "delete"
|
216
|
+
args.length == 3
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
run do
|
221
|
+
case args[1]
|
222
|
+
when "add"
|
223
|
+
prompt_add
|
224
|
+
when "edit"
|
225
|
+
when "delete"
|
226
|
+
end
|
227
|
+
|
228
|
+
""
|
229
|
+
end
|
230
|
+
|
231
|
+
###
|
232
|
+
|
233
|
+
def prompt_add
|
234
|
+
end
|
235
|
+
|
236
|
+
def prompt_edit
|
237
|
+
end
|
238
|
+
|
239
|
+
def prompt_delete
|
240
|
+
end
|
241
|
+
|
242
|
+
###
|
243
|
+
|
244
|
+
def prompt(basis={})
|
245
|
+
end
|