noms-command 0.5.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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE +191 -0
- data/README.rst +376 -0
- data/ROADMAP.rst +127 -0
- data/Rakefile +49 -0
- data/TODO.rst +7 -0
- data/bin/noms2 +20 -0
- data/fixture/dnc.rb +120 -0
- data/fixture/identity +5 -0
- data/fixture/public/dnc.json +22 -0
- data/fixture/public/echo.json +7 -0
- data/fixture/public/files/data.json +12 -0
- data/fixture/public/files/foo.json +2 -0
- data/fixture/public/lib/dnc.js +81 -0
- data/fixture/public/lib/noms-args.js +13 -0
- data/fixture/public/lib/showopt.js +18 -0
- data/fixture/public/location.json +8 -0
- data/fixture/public/showopt.json +15 -0
- data/fixture/rig2json +21 -0
- data/lib/noms/command/application.rb +204 -0
- data/lib/noms/command/auth/identity.rb +62 -0
- data/lib/noms/command/auth.rb +117 -0
- data/lib/noms/command/base.rb +22 -0
- data/lib/noms/command/document.rb +59 -0
- data/lib/noms/command/error.rb +11 -0
- data/lib/noms/command/formatter.rb +178 -0
- data/lib/noms/command/urinion/data.rb +63 -0
- data/lib/noms/command/urinion.rb +29 -0
- data/lib/noms/command/useragent.rb +134 -0
- data/lib/noms/command/version.rb +7 -0
- data/lib/noms/command/window.rb +95 -0
- data/lib/noms/command/xmlhttprequest.rb +181 -0
- data/lib/noms/command.rb +107 -0
- data/noms-command.gemspec +30 -0
- data/spec/01noms-command_spec.rb +30 -0
- data/spec/02noms-command.sh +31 -0
- data/spec/03application_spec.rb +47 -0
- data/spec/04application_spec.rb +61 -0
- data/spec/05formatter_spec.rb +195 -0
- data/spec/06urinion_data.rb +20 -0
- data/spec/07js_spec.rb +87 -0
- data/spec/08xhr_spec.rb +209 -0
- data/spec/09bookmarks_spec.rb +60 -0
- data/spec/10auth_spec.rb +33 -0
- data/spec/spec_helper.rb +40 -0
- metadata +228 -0
data/ROADMAP.rst
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
noms-command Roadmap
|
2
|
+
====================
|
3
|
+
|
4
|
+
Roadmap
|
5
|
+
-------
|
6
|
+
|
7
|
+
These items are necessary and a fairly well-identified goal.
|
8
|
+
|
9
|
+
Authentication
|
10
|
+
~~~~~~~~~~~~~~
|
11
|
+
|
12
|
+
Authentication needs to be handled in a rich and correct way. One of
|
13
|
+
the original inspirations for replacing the **noms** v1 thick client
|
14
|
+
was the daunting nature of adding authentication code to all the different
|
15
|
+
client libraries for the NOMS_ components and then exposing an interface
|
16
|
+
to dealing with them to the command-line.
|
17
|
+
|
18
|
+
Instead, all the web plumbing and dealing with things like how to
|
19
|
+
present persistent web sessions to a user are going to be handled by the
|
20
|
+
general purpose web application client **noms**, and services like the
|
21
|
+
NOMS CMDB, NCC-API, NagUI-API, etc. can provide their own command-line
|
22
|
+
implementations. The NOMS_ components are likely to use standard Javascript
|
23
|
+
libraries to present a consistent look, feel and implementation, but
|
24
|
+
**noms** will provide the ability for a service provider to define a CLI
|
25
|
+
that looks and feels any way they want.
|
26
|
+
|
27
|
+
That means **noms** should be good at command-line web authentication. The
|
28
|
+
old v1 client could not do more than Basic authentication, and this necessitated
|
29
|
+
the storage of personal credentials in plaintext on the system, mixed with
|
30
|
+
other configuration like the ReST enpoint URLs and usage conveniences like
|
31
|
+
default values.
|
32
|
+
|
33
|
+
Instead **noms** will handle many authentication flavors in a way that is
|
34
|
+
as secure as possible:
|
35
|
+
|
36
|
+
============================ =====================================================
|
37
|
+
Authentication Type Description
|
38
|
+
============================ =====================================================
|
39
|
+
Username/password (basic, **noms** will prompt the user for credentials when
|
40
|
+
digest) receives the authorization required HTTP status, and
|
41
|
+
persist the result in an obfuscated, expiring form
|
42
|
+
for that origin.
|
43
|
+
---------------------------- -----------------------------------------------------
|
44
|
+
Login URL with cookie-based **noms** honors redirects to a login URL in the
|
45
|
+
sessions same way as a web browser and stores cookies
|
46
|
+
using proper cookie expiration. **noms** doesn't
|
47
|
+
do HTML parsing, so authentication on the login
|
48
|
+
URL must be of some other type or scriptable
|
49
|
+
with Javascript.
|
50
|
+
|
51
|
+
This implies that Javascript must be able to
|
52
|
+
script the authentication dialog, which means
|
53
|
+
there must be some way to do prompting; possibly
|
54
|
+
with the ``window.prompt()`` interface.
|
55
|
+
---------------------------- -----------------------------------------------------
|
56
|
+
Login URL with special token **noms** will offer a way for javascripts to persist
|
57
|
+
to be included in request state across requests. This mechanism will allow
|
58
|
+
headers a javascript to set special headers that it will
|
59
|
+
have access to for later requests.
|
60
|
+
---------------------------- -----------------------------------------------------
|
61
|
+
Login URL with special token Similar to above, with a somewhat different request
|
62
|
+
to be included in request implementation.
|
63
|
+
bodies
|
64
|
+
---------------------------- -----------------------------------------------------
|
65
|
+
OAuth **noms** will use httpclient's built-in OAuth
|
66
|
+
and prompt the user to authenticate with OAuth, and
|
67
|
+
use the OAuth token for subsequent requests.
|
68
|
+
---------------------------- -----------------------------------------------------
|
69
|
+
Client certificate Prompt for passphrase or use authentication agent
|
70
|
+
to provide client certificate.
|
71
|
+
============================ =====================================================
|
72
|
+
|
73
|
+
Storage
|
74
|
+
~~~~~~~
|
75
|
+
|
76
|
+
In order to persist state across requests, **noms** should probably
|
77
|
+
have a `Web Storage`_ implementation.
|
78
|
+
|
79
|
+
.. _`Web Storage`: http://dev.w3.org/html5/webstorage/
|
80
|
+
|
81
|
+
Caching
|
82
|
+
~~~~~~~
|
83
|
+
|
84
|
+
HTTP provides a rich way to control caching of resources, and **noms** should
|
85
|
+
honor these strictly for efficiency. It will honor ``Cache-control`` headers
|
86
|
+
and use ``ETags`` and ``If-Modified`` appropriately to avoid loading
|
87
|
+
application documents, scripts and even data unnecessarily. This should reduce
|
88
|
+
many of the inefficiencies associated with serving an interface from the server.
|
89
|
+
|
90
|
+
Crossroads
|
91
|
+
----------
|
92
|
+
|
93
|
+
This is not a roadmap, but a series of ideas of how **noms** could be enhanced as
|
94
|
+
well as unanswered questions about how it should work.
|
95
|
+
|
96
|
+
I/O
|
97
|
+
~~~
|
98
|
+
|
99
|
+
Its prototype does no local I/O. Outside of restricted situations like
|
100
|
+
`Web Storage`_ this is probably desirable. It's extremely typical of CLIs, even
|
101
|
+
those for "remote" data stores, to be able to do some I/O, and here are a couple
|
102
|
+
of ideas how:
|
103
|
+
|
104
|
+
* stdin - Right now there's no way to even read stdin. So if you want to make a
|
105
|
+
CLI for uploading batch data you can't even do it--your scripts only have access
|
106
|
+
to command-line arguments.
|
107
|
+
|
108
|
+
Use node.js-style `Readable Stream`_ implementation to have access to stdin.
|
109
|
+
|
110
|
+
.. _`Readable Stream`: https://nodejs.org/api/stream.html
|
111
|
+
|
112
|
+
Another thing is the possibility of doing I/O on select named files. **noms** could
|
113
|
+
perhaps pre-parse the command line and add the files mentioned on the command line
|
114
|
+
to an ACL which would allow downloaded scripts to access them. For example::
|
115
|
+
|
116
|
+
noms http://cmdb/cmdb.json generate-ansible-inventory --output=inventory.json
|
117
|
+
|
118
|
+
**noms** could scan the command line and guess at what files the user intends the
|
119
|
+
application to have access to, and allow the Javascript to open them using a node.js-like
|
120
|
+
stream implementation. Exactly how to do this safely would be a challenge. Are only
|
121
|
+
certain options allowed? Files that already exist (otherwise it would be easy to allow
|
122
|
+
the script to write to unintended files). No files with '..'? What about -oFile.json
|
123
|
+
vs. -onf?
|
124
|
+
|
125
|
+
Another I/O-related subject is that **noms** is currently completely request/response-
|
126
|
+
oriented. It might be nice to be able to stream output data, or input data for file-
|
127
|
+
or batch-upload type operations, or wait for events on a websocket.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new
|
5
|
+
|
6
|
+
task :test => [:spec, :testcmd]
|
7
|
+
|
8
|
+
task :testcmd do
|
9
|
+
ENV['RUBYLIB'] = ['lib', ENV['RUBYLIB']].join(':')
|
10
|
+
ENV['PATH'] = ['bin', ENV['PATH']].join(':')
|
11
|
+
Dir.new('spec').each do |script|
|
12
|
+
next unless script =~ /\.sh$/
|
13
|
+
puts script
|
14
|
+
system(File.join('spec', script))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Start the DNC application web server on port 8787
|
19
|
+
task :start do
|
20
|
+
FileUtils.rm_r 'test' if File.directory? 'test'
|
21
|
+
system 'cp -R fixture test'
|
22
|
+
system("sh -c '#{RbConfig.ruby} test/dnc.rb >test/dnc.out 2>&1 &'")
|
23
|
+
end
|
24
|
+
|
25
|
+
task :status do
|
26
|
+
begin
|
27
|
+
pid = File.read('test/dnc.pid').to_i
|
28
|
+
Process.kill 0, File.read('test/dnc.pid').to_i
|
29
|
+
puts "Test server running (PID #{pid})"
|
30
|
+
rescue Errno::ESRCH
|
31
|
+
puts "Test server not running on PID #{pid}"
|
32
|
+
rescue Errno::ENOENT
|
33
|
+
puts "Test server not running (no pidfile)"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
task :sync do
|
38
|
+
system 'cp -R fixture/* test'
|
39
|
+
end
|
40
|
+
|
41
|
+
task :stop do
|
42
|
+
Process.kill 'TERM', File.read('test/dnc.pid').to_i
|
43
|
+
FileUtils.rm 'test/dnc.pid'
|
44
|
+
end
|
45
|
+
|
46
|
+
task :clean do
|
47
|
+
rm_rf ['pkg', 'test']
|
48
|
+
end
|
49
|
+
|
data/TODO.rst
ADDED
data/bin/noms2
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2015 Evernote Corporation, all rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'noms/command'
|
19
|
+
|
20
|
+
Process.exit NOMS::Command.run(ARGV)
|
data/fixture/dnc.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# Implement Do Not Call List Example REST application
|
5
|
+
# and static file server
|
6
|
+
class DNC < Sinatra::Application
|
7
|
+
|
8
|
+
set :port, 8787
|
9
|
+
set :root, File.expand_path("#{File.dirname(__FILE__)}")
|
10
|
+
enable :static
|
11
|
+
|
12
|
+
File.open(File.join(settings.root, 'dnc.pid'), 'w') {|f| f.puts Process.pid }
|
13
|
+
|
14
|
+
def load_data
|
15
|
+
JSON.load(File.open(File.join(settings.root, 'public', 'files', 'data.json')))
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_data(data)
|
19
|
+
File.open(File.join(settings.root, 'public', 'files', 'data.json'), 'w') { |fh| fh << data.to_json }
|
20
|
+
end
|
21
|
+
|
22
|
+
helpers do
|
23
|
+
def require_auth
|
24
|
+
return if authorized?
|
25
|
+
headers['WWW-Authenticate'] = 'Basic realm="Authorization Required"'
|
26
|
+
halt 401, "Not authorized\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
def authorized?
|
30
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
31
|
+
@auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == ['testuser', 'testpass']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
get '/readme' do
|
36
|
+
redirect 'https://raw.githubusercontent.com/en-jbrinkley/noms-command/master/README.rst', 'README'
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/dnc' do
|
40
|
+
data = load_data
|
41
|
+
if request.query_string.empty?
|
42
|
+
[ 200, { 'Content-type' => 'application/json'},
|
43
|
+
JSON.pretty_generate(data) ]
|
44
|
+
else
|
45
|
+
[ 200, { 'Content-type' => 'application/json' },
|
46
|
+
JSON.pretty_generate(
|
47
|
+
data.select do |item|
|
48
|
+
params.keys.all? { |k| item[k.to_s] && item[k.to_s].to_s === params[k] }
|
49
|
+
end)
|
50
|
+
]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/dnc/:id' do
|
55
|
+
data = load_data
|
56
|
+
object = data.find { |e| e['id'] == params[:id].to_i }
|
57
|
+
if object
|
58
|
+
[ 200, { 'Content-type' => 'application/json' },
|
59
|
+
JSON.pretty_generate(object) ]
|
60
|
+
else
|
61
|
+
404
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
post '/dnc' do
|
66
|
+
request.body.rewind
|
67
|
+
new_object = JSON.parse request.body.read
|
68
|
+
|
69
|
+
data = load_data
|
70
|
+
# How unsafe is this?
|
71
|
+
new_object['id'] = data.map { |e| e['id'] }.max + 1
|
72
|
+
data << new_object
|
73
|
+
write_data data
|
74
|
+
|
75
|
+
[ 201, { 'Content-type' => 'application/json' },
|
76
|
+
JSON.pretty_generate(new_object) ]
|
77
|
+
end
|
78
|
+
|
79
|
+
put '/dnc/:id' do
|
80
|
+
request.body.rewind
|
81
|
+
new_object = JSON.parse request.body.read
|
82
|
+
|
83
|
+
data = load_data
|
84
|
+
new_data = data.reject { |e| e['id'] == params[:id].to_i }
|
85
|
+
if new_data.size == data.size
|
86
|
+
404
|
87
|
+
else
|
88
|
+
new_object['id'] = params[:id].to_i
|
89
|
+
new_data << new_object
|
90
|
+
write_data new_data
|
91
|
+
|
92
|
+
[ 200, { 'Content-type' => 'application/json' },
|
93
|
+
JSON.pretty_generate(new_object) ]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
delete '/dnc/:id' do
|
98
|
+
data = load_data
|
99
|
+
new_data = data.reject { |e| e['id'] == params[:id].to_i }
|
100
|
+
|
101
|
+
if new_data.size == data.size
|
102
|
+
404
|
103
|
+
else
|
104
|
+
write_data new_data
|
105
|
+
204
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
get '/alt/dnc.json' do
|
110
|
+
redirect to('/dnc.json')
|
111
|
+
end
|
112
|
+
|
113
|
+
get '/auth/dnc.json' do
|
114
|
+
require_auth
|
115
|
+
redirect to('/dnc.json')
|
116
|
+
end
|
117
|
+
|
118
|
+
run! if app_file = $0
|
119
|
+
|
120
|
+
end
|
data/fixture/identity
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
{ "$doctype": "noms-v2",
|
2
|
+
"$comment": [
|
3
|
+
"noms2 http://localhost:8787/dnc.json",
|
4
|
+
"noms2 http://localhost:8787/dnc.json list"
|
5
|
+
],
|
6
|
+
"$script": [
|
7
|
+
{ "$source": "https://rawgit.com/jfd/optparse-js/v1.0.5/lib/optparse.js",
|
8
|
+
"$comment": "Optparse.js 1.0.3 - https://github.com/jfd/optparse-js; via rawgit.com" },
|
9
|
+
{ "$source": "https://rawgit.com/douglascrockford/JSON-js/master/json2.js",
|
10
|
+
"$comment": "JSON in JavaScript - https://github.com/douglascrockford/JSON-js" },
|
11
|
+
{ "$source": "lib/noms-args.js" },
|
12
|
+
{ "$source": "lib/dnc.js" }
|
13
|
+
],
|
14
|
+
"$body": [
|
15
|
+
"Usage:",
|
16
|
+
" noms dnc query <field>=<value>",
|
17
|
+
" noms dnc add <field>=<value> [<field>=<value> [...]]",
|
18
|
+
" noms dnc remove <id>",
|
19
|
+
" noms dnc check { <phone> | <name> }",
|
20
|
+
" noms dnc list"
|
21
|
+
]
|
22
|
+
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{ "$doctype": "noms-v2",
|
2
|
+
"$comment": "noms2 http://localhost:8787/echo.json one two three",
|
3
|
+
"$body": [],
|
4
|
+
"$script": [
|
5
|
+
"if (document.argv.length > 1) { document.body = document.argv.slice(1).join(\" \"); } else { line1 = prompt('String to echo: '); line2 = prompt(\"Password to echo (don't use real one): \", false); document.body = [line1, line2]; }"
|
6
|
+
]
|
7
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
[
|
2
|
+
{"id":1,"name":"Manuela Irwin","street":"427 Maple Ln","city":"Arlington, TX 76010","phone":"(817) 555-0427"},
|
3
|
+
{"id":2,"name":"Ronda Sheppard","street":"801 New First Rd","city":"Providence, RI 02940","phone":"(401) 555-0801"},
|
4
|
+
{"id":3,"name":"Leonor Foreman","street":"428 Willow Rd","city":"Providence, RI 02940","phone":"(401) 555-0428"},
|
5
|
+
{"id":4,"name":"Emma Roman","street":"589 Flanty Terr","city":"Anderson, IN 46018","phone":"(317) 555-0589"},
|
6
|
+
{"id":5,"name":"Frieda English","street":"930 Stonehedge Blvd","city":"Chicago, IL 60607","phone":"(312) 555-0930"},
|
7
|
+
{"id":6,"name":"Kitty Morton","street":"618 Manchester St","city":"Richmond, VA 23232","phone":"(804) 555-0618"},
|
8
|
+
{"id":7,"name":"Kathy Mcleod","street":"52 Wommert Ln","city":"Binghamton, NY 13902","phone":"(607) 555-0052"},
|
9
|
+
{"id":8,"name":"Bettie Wolfe","street":"523 Sharon Rd","city":"Coward, SC 29530","phone":"(843) 555-0523"},
|
10
|
+
{"id":9,"name":"Vanessa Conway","street":"885 Old Pinbrick Dr","city":"Athens, GA 30601","phone":"(404) 555-0885"},
|
11
|
+
{"id":10,"name":"Ian Welch","street":"555 Hamlet St","city":"Arlington, TX 76010","phone":"(817) 555-0555"}
|
12
|
+
]
|
@@ -0,0 +1,81 @@
|
|
1
|
+
if (document.argv.length > 1) {
|
2
|
+
var argv = document.argv;
|
3
|
+
var me = argv.shift();
|
4
|
+
var command;
|
5
|
+
var format;
|
6
|
+
var xmlhttp = new XMLHttpRequest();
|
7
|
+
|
8
|
+
// document attributes can be set
|
9
|
+
// but are immutable
|
10
|
+
document.body = [ ];
|
11
|
+
var output = [ ];
|
12
|
+
|
13
|
+
var optspec = [
|
14
|
+
["-J", "--json", "Display JSON"],
|
15
|
+
["-Y", "--yaml", "Display YAML"],
|
16
|
+
["-C", "--csv", "Display CSV"],
|
17
|
+
["-v", "--verbose", "Enable verbose output"],
|
18
|
+
["--nofeedback", "Don't print feedback"]
|
19
|
+
];
|
20
|
+
|
21
|
+
var parser = new optparse.OptionParser(optspec);
|
22
|
+
var options = {
|
23
|
+
"feedback": true,
|
24
|
+
"format": "default",
|
25
|
+
"verbose": false
|
26
|
+
};
|
27
|
+
var args = [ ];
|
28
|
+
|
29
|
+
parser.on("verbose", function() { options["verbose"] = true });
|
30
|
+
parser.on("json", function() {
|
31
|
+
options["format"] = "json";
|
32
|
+
options["feedback"] = false;
|
33
|
+
});
|
34
|
+
parser.on("yaml", function() {
|
35
|
+
options["format"] = "yaml";
|
36
|
+
options["feedback"] = false;
|
37
|
+
});
|
38
|
+
parser.on("csv", function() {
|
39
|
+
options["format"] = "csv";
|
40
|
+
options["feedback"] = false;
|
41
|
+
});
|
42
|
+
parser.on("nofeedback", function() { options["feedback"] = false; });
|
43
|
+
parser.on(0, function(arg) { command = arg });
|
44
|
+
parser.on(function(arg) { args.push(arg); });
|
45
|
+
|
46
|
+
parser.parse(argv);
|
47
|
+
|
48
|
+
switch(command) {
|
49
|
+
case "list":
|
50
|
+
if (options["format"] === "default") {
|
51
|
+
format = "lines";
|
52
|
+
} else {
|
53
|
+
format = options["format"];
|
54
|
+
}
|
55
|
+
xmlhttp.open("GET", "/dnc", false);
|
56
|
+
xmlhttp.send();
|
57
|
+
var records = eval('(' + xmlhttp.responseText + ')');
|
58
|
+
output.push(
|
59
|
+
{
|
60
|
+
'$type': 'object-list',
|
61
|
+
'$format': format,
|
62
|
+
'$columns': [
|
63
|
+
{ 'field': 'id', 'width': 3, 'align': 'right' },
|
64
|
+
{ 'field': 'name', 'width': 20 },
|
65
|
+
{ 'field': 'phone', 'width': 20 }
|
66
|
+
],
|
67
|
+
'$data': records
|
68
|
+
});
|
69
|
+
if (options["feedback"]) {
|
70
|
+
output.push(records.length + " objects");
|
71
|
+
}
|
72
|
+
break;
|
73
|
+
default:
|
74
|
+
document.exitcode = 8;
|
75
|
+
window.alert(
|
76
|
+
me + " error: Unknown command '" + command + "'"
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
document.body = output;
|
81
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
var optspec = [
|
2
|
+
["-n", "--dry-run", "Don't change anything"],
|
3
|
+
["-d", "--debug", "Enable debugging output"],
|
4
|
+
["-c", "--config FILE", "Specify configuration file"]
|
5
|
+
];
|
6
|
+
|
7
|
+
var parser = new optparse.OptionParser(optspec);
|
8
|
+
var options = { "dry-run": false,
|
9
|
+
"debug": false,
|
10
|
+
"config": null }
|
11
|
+
|
12
|
+
parser.on("dry-run", function () { options["dry-run"] = true })
|
13
|
+
parser.on("debug", function() { options["debug"] = true })
|
14
|
+
parser.on("config", function(opt, file) { options["config"] = file })
|
15
|
+
|
16
|
+
parser.parse(document.argv)
|
17
|
+
|
18
|
+
document.body = options
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{ "$doctype": "noms-v2",
|
2
|
+
"$comment": [
|
3
|
+
"noms2 http://localhost:8787/showopt.json",
|
4
|
+
"noms2 http://localhost:8787/showopt.json --debug --config /dev/null"
|
5
|
+
],
|
6
|
+
"$body": [
|
7
|
+
"Usage:",
|
8
|
+
" showopt [--dry-run] [--config <s>] [--verbose]"
|
9
|
+
],
|
10
|
+
"$script": [
|
11
|
+
{ "$source": "https://rawgit.com/jfd/optparse-js/v1.0.5/lib/optparse.js",
|
12
|
+
"$comment": "Optparse.js 1.0.3 - https://github.com/jfd/optparse-js; via rawgit.com" },
|
13
|
+
{ "$source": "lib/showopt.js" }
|
14
|
+
]
|
15
|
+
}
|
data/fixture/rig2json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
echo '['
|
4
|
+
id=0
|
5
|
+
|
6
|
+
while read name
|
7
|
+
do
|
8
|
+
let id=id+1
|
9
|
+
read street
|
10
|
+
read city
|
11
|
+
read phone
|
12
|
+
number=$(echo $street | cut -f1 -d\ )
|
13
|
+
number=555-$(printf '%04d' $number)
|
14
|
+
phone=$(echo $phone | sed "s/xxx-xxxx/$number/")
|
15
|
+
echo -n "{'id':$id,'name':'$name','street':'$street','city':'$city','phone':'$phone'}" | tr "'" '"'
|
16
|
+
read blank || break
|
17
|
+
echo ,
|
18
|
+
done
|
19
|
+
|
20
|
+
echo
|
21
|
+
echo ']'
|