cmds 0.0.1
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 +19 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +333 -0
- data/Rakefile +1 -0
- data/ansible/dev.yml +25 -0
- data/ansible/hosts +6 -0
- data/cmds.gemspec +27 -0
- data/lib/cmds/version.rb +3 -0
- data/lib/cmds.rb +322 -0
- data/scratch/blah.rb +6 -0
- data/scratch/erb.rb +53 -0
- data/spec/cmds/call_spec.rb +16 -0
- data/spec/cmds/curry_spec.rb +14 -0
- data/spec/cmds/erb_context_spec.rb +23 -0
- data/spec/cmds/expand_option_hash_spec.rb +61 -0
- data/spec/cmds/replace_shortcuts_spec.rb +78 -0
- data/spec/cmds/run_spec.rb +49 -0
- data/spec/cmds/sub_spec.rb +159 -0
- data/spec/cmds_spec.rb +7 -0
- data/spec/spec_helper.rb +21 -0
- data/test/echo_cmd.rb +15 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5caaa2f3116bf7e3a3d0d177334117a3877a0c30
|
4
|
+
data.tar.gz: a9c58cb91f4f58eec0317d6dadf19d5b645d9d74
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1386ff56087d5031e83ec65e26e0582c551f0df1a7df48661448a046a4b04d5e1391f4133804762a17783b3daa982594227d48702a73e28567ac42c91fd7901
|
7
|
+
data.tar.gz: d8f87632411dca1a6195eafaf66f45ab0636190e4771a19a2e30bb6859a5d2725c860dd2c44be9867404efc0df0f9c5e808877eac019f3f43a0b6eae61846052
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 nrser
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
# cmds
|
2
|
+
|
3
|
+
cmds tries to make it easier to read, write and remember using shell commands in Ruby.
|
4
|
+
|
5
|
+
it treats generating shell the in a similar fashion to generating SQL or HTML.
|
6
|
+
|
7
|
+
|
8
|
+
## status
|
9
|
+
|
10
|
+
eh, it's kinda starting to work... i'll be using it for stuff and seeing how it goes, but no promises until `1.0` of course.
|
11
|
+
|
12
|
+
|
13
|
+
## installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```
|
18
|
+
gem 'cmds'
|
19
|
+
```
|
20
|
+
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
```
|
25
|
+
$ bundle
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```
|
32
|
+
$ gem install cmds
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
## real-world examples
|
38
|
+
|
39
|
+
instead of
|
40
|
+
|
41
|
+
```
|
42
|
+
`psql -U #{ db_config['username'] || ENV['USER'] } #{ db_config['database']} < #{ filepath.shellescape }`
|
43
|
+
```
|
44
|
+
|
45
|
+
write
|
46
|
+
|
47
|
+
```
|
48
|
+
Cmds 'psql %{opts} %{db} < %{dump}',
|
49
|
+
db: db_config['database'],
|
50
|
+
dump: filepath,
|
51
|
+
opts: {
|
52
|
+
username: db_config['username'] || ENV['USER']
|
53
|
+
}
|
54
|
+
```
|
55
|
+
|
56
|
+
instead of
|
57
|
+
|
58
|
+
```
|
59
|
+
`aws s3 sync s3://#{ PROD_APP_NAME } #{ s3_path.shellescape }`
|
60
|
+
```
|
61
|
+
|
62
|
+
write
|
63
|
+
|
64
|
+
```
|
65
|
+
Cmds 'aws s3 sync %{uri} %{path}', uri: "s3://#{ PROD_APP_NAME }"
|
66
|
+
path: s3_path
|
67
|
+
```
|
68
|
+
|
69
|
+
instead of
|
70
|
+
|
71
|
+
```
|
72
|
+
`PGPASSWORD=#{ config[:password].shellescape } pg_dump -U #{ config[:username].shellescape } -h #{ config[:host].shellescape } -p #{ config[:port] } #{ config[:database].shellescape } > #{ filepath.shellescape }`
|
73
|
+
```
|
74
|
+
|
75
|
+
write
|
76
|
+
|
77
|
+
```
|
78
|
+
Cmds 'PGPASSWORD=%{password} pg_dump %{opts} %{database} > %{filepath}',
|
79
|
+
password: config[:password],
|
80
|
+
database: config[:database],
|
81
|
+
filepath: filepath,
|
82
|
+
opts: {
|
83
|
+
username: config[:username],
|
84
|
+
host: config[:host],
|
85
|
+
port: config[:port],
|
86
|
+
}
|
87
|
+
```
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
## substitutions
|
92
|
+
|
93
|
+
substitutions can be positional, keyword, or both.
|
94
|
+
|
95
|
+
### positional
|
96
|
+
|
97
|
+
positional arguments can be substituted in order using the `arg` method call:
|
98
|
+
|
99
|
+
```
|
100
|
+
Cmds.sub "psql <%= arg %> <%= arg %> < <%= arg %>", [
|
101
|
+
{
|
102
|
+
username: "bingo bob",
|
103
|
+
host: "localhost",
|
104
|
+
port: 12345,
|
105
|
+
},
|
106
|
+
"blah",
|
107
|
+
"/where ever/it/is.psql",
|
108
|
+
]
|
109
|
+
# => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
|
110
|
+
```
|
111
|
+
|
112
|
+
internally this translates to calling `@args.fetch(@arg_index)` and increments `@arg_index` by 1.
|
113
|
+
|
114
|
+
this will raise an error if it's called after using the last positional argument, but will not complain if all positional arguments are not used. this prevents using a keyword arguments named `arg` without accessing the keywords hash directly.
|
115
|
+
|
116
|
+
the arguments may also be accessed directly though the bound class's `@args` instance variable:
|
117
|
+
|
118
|
+
```
|
119
|
+
Cmds.sub "psql <%= @args[2] %> <%= @args[0] %> < <%= @args[1] %>", [
|
120
|
+
"blah",
|
121
|
+
"/where ever/it/is.psql",
|
122
|
+
{
|
123
|
+
username: "bingo bob",
|
124
|
+
host: "localhost",
|
125
|
+
port: 12345,
|
126
|
+
},
|
127
|
+
]
|
128
|
+
# => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
|
129
|
+
```
|
130
|
+
|
131
|
+
note that `@args` is a standard Ruby array and will simply return `nil` if there is no value at that index (though you can use `args.fetch(i)` to get the same behavior as the `arg` method with a specific index `i`).
|
132
|
+
|
133
|
+
### keyword
|
134
|
+
|
135
|
+
keyword arguments can be accessed by making a method call with their key:
|
136
|
+
|
137
|
+
```
|
138
|
+
Cmds.sub "psql <%= opts %> <%= database %> < <%= filepath %>",
|
139
|
+
[],
|
140
|
+
database: "blah",
|
141
|
+
filepath: "/where ever/it/is.psql",
|
142
|
+
opts: {
|
143
|
+
username: "bingo bob",
|
144
|
+
host: "localhost",
|
145
|
+
port: 12345,
|
146
|
+
}
|
147
|
+
# => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
|
148
|
+
```
|
149
|
+
|
150
|
+
this translates to a call of `@kwds.fetch(key)`, which will raise an error if `key` isn't present.
|
151
|
+
|
152
|
+
there are four key names that may not be accessed this way due to method definition on the context object:
|
153
|
+
|
154
|
+
* `arg` (see above)
|
155
|
+
* `initialize`
|
156
|
+
* `get_binding`
|
157
|
+
* `method_missing`
|
158
|
+
|
159
|
+
though keys with those names may be accessed directly via `@kwds.fetch(key)` and the like.
|
160
|
+
|
161
|
+
to test for a key's presence or optionally include a value, append `?` to the method name:
|
162
|
+
|
163
|
+
```
|
164
|
+
c = Cmds.new <<-BLOCK
|
165
|
+
defaults
|
166
|
+
<% if current_host? %>
|
167
|
+
-currentHost <%= current_host %>
|
168
|
+
<% end %>
|
169
|
+
export <%= domain %> <%= filepath %>
|
170
|
+
BLOCK
|
171
|
+
|
172
|
+
c.call domain: 'com.nrser.blah', filepath: '/tmp/export.plist'
|
173
|
+
# defaults export com.nrser.blah /tmp/export.plist
|
174
|
+
|
175
|
+
c.call current_host: 'xyz', domain: 'com.nrser.blah', filepath: '/tmp/export.plist'
|
176
|
+
# defaults -currentHost xyz export com.nrser.blah /tmp/export.plist
|
177
|
+
```
|
178
|
+
|
179
|
+
### both
|
180
|
+
|
181
|
+
both positional and keyword substitutions may be provided:
|
182
|
+
|
183
|
+
```
|
184
|
+
Cmds.sub "psql <%= opts %> <%= arg %> < <%= filepath %>",
|
185
|
+
["blah"],
|
186
|
+
filepath: "/where ever/it/is.psql",
|
187
|
+
opts: {
|
188
|
+
username: "bingo bob",
|
189
|
+
host: "localhost",
|
190
|
+
port: 12345,
|
191
|
+
}
|
192
|
+
# => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
|
193
|
+
```
|
194
|
+
|
195
|
+
this might be useful if you have a simple positional command like
|
196
|
+
|
197
|
+
```
|
198
|
+
Cmds "blah <%= arg %>", ["value"]
|
199
|
+
```
|
200
|
+
|
201
|
+
and you want to quickly add in some optional value
|
202
|
+
|
203
|
+
```
|
204
|
+
Cmds "blah <%= maybe? %> <%= arg %>", ["value"]
|
205
|
+
Cmds "blah <%= maybe? %> <%= arg %>", ["value"], maybe: "yes!"
|
206
|
+
```
|
207
|
+
|
208
|
+
### shortcuts
|
209
|
+
|
210
|
+
there are support for `sprintf`-style shortcuts.
|
211
|
+
|
212
|
+
**positional**
|
213
|
+
|
214
|
+
`%s` is replaced with `<%= arg %>`.
|
215
|
+
|
216
|
+
so
|
217
|
+
|
218
|
+
```
|
219
|
+
Cmds.sub "./test/echo_cmd.rb %s", ["hello world!"]
|
220
|
+
```
|
221
|
+
|
222
|
+
is the same as
|
223
|
+
|
224
|
+
```
|
225
|
+
Cmds "./test/echo_cmd.rb <%= arg %>", ["hello world!"]
|
226
|
+
```
|
227
|
+
|
228
|
+
**keyword**
|
229
|
+
|
230
|
+
`%{key}` and `%<key>s` are replaced with `<%= key %>`, and `%{key?}` and `%<key?>s` are replaced with `<%= key? %>` for optional keywords.
|
231
|
+
|
232
|
+
so
|
233
|
+
|
234
|
+
```
|
235
|
+
Cmds "./test/echo_cmd.rb %{key}", key: "hello world!"
|
236
|
+
```
|
237
|
+
|
238
|
+
and
|
239
|
+
|
240
|
+
```
|
241
|
+
Cmds "./test/echo_cmd.rb %<key>s", key: "hello world!"
|
242
|
+
```
|
243
|
+
|
244
|
+
are the same is
|
245
|
+
|
246
|
+
```
|
247
|
+
Cmds "./test/echo_cmd.rb <%= key %>", key: "hello world!"
|
248
|
+
```
|
249
|
+
|
250
|
+
**escaping**
|
251
|
+
|
252
|
+
strings that would be replaced as shortcuts can be escaped by adding one more `%` to the front of them:
|
253
|
+
|
254
|
+
```
|
255
|
+
Cmds.sub "%%s" # => "%s"
|
256
|
+
Cmds.sub "%%%<key>s" # => "%%<key>s"
|
257
|
+
```
|
258
|
+
|
259
|
+
note that unlike `sprintf`, which has a much more general syntax, this is only necessary for patterns that exactly match a shortcut, not `%` in general:
|
260
|
+
|
261
|
+
```
|
262
|
+
Cmds.sub "50%" # => "50%"
|
263
|
+
```
|
264
|
+
|
265
|
+
|
266
|
+
|
267
|
+
## reuse commands
|
268
|
+
|
269
|
+
```
|
270
|
+
playbook = Cmds.new "ansible-playbook -i %{inventory} %{playbook}"
|
271
|
+
playbook.call inventory: "./hosts", playbook: "blah.yml"
|
272
|
+
```
|
273
|
+
|
274
|
+
currying
|
275
|
+
|
276
|
+
```
|
277
|
+
dev_playbook = playbook.curry inventory: "inventory/dev"
|
278
|
+
prod_playbook = playbook.curry inventory: "inventory/prod"
|
279
|
+
|
280
|
+
# run setup.yml on the development hosts
|
281
|
+
dev_playbook.call playbook: "setup.yml"
|
282
|
+
|
283
|
+
# run setup.yml on the production hosts
|
284
|
+
prod_playbook.call playbook: "setup.yml"
|
285
|
+
```
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
## defaults
|
290
|
+
|
291
|
+
NEEDS TEST
|
292
|
+
|
293
|
+
can be accomplished with reuse and currying stuff
|
294
|
+
|
295
|
+
```
|
296
|
+
playbook = Cmds.new "ansible-playbook -i %{inventory} %{playbook}", inventory: "inventory/dev"
|
297
|
+
|
298
|
+
# run setup.yml on the development hosts
|
299
|
+
playbook.call playbook: "setup.yml"
|
300
|
+
|
301
|
+
# run setup.yml on the production hosts
|
302
|
+
prod_playbook.call playbook: "setup.yml", inventory: "inventory/prod"
|
303
|
+
```
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
## future..?
|
308
|
+
|
309
|
+
### formatters
|
310
|
+
|
311
|
+
kinda like `sprintf` formatters or string escape helpers in Rails, they would be exposed as functions in ERB and as format characters in the shorthand versions:
|
312
|
+
|
313
|
+
```
|
314
|
+
Cmds "blah <%= j obj %>", obj: {x: 1}
|
315
|
+
# => blah \{\"x\":1\}
|
316
|
+
|
317
|
+
Cmds "blah %j", [{x: 1}]
|
318
|
+
# => blah \{\"x\":1\}
|
319
|
+
|
320
|
+
Cmds "blah %<obj>j", obj: {x: 1}
|
321
|
+
# => blah \{\"x\":1\}
|
322
|
+
```
|
323
|
+
|
324
|
+
the `s` formatter would just format as an escaped string (no different from `<%= %>`).
|
325
|
+
|
326
|
+
other formatters could include
|
327
|
+
|
328
|
+
* `j` for JSON (as shown above)
|
329
|
+
* `r` for raw (unescaped)
|
330
|
+
* `l` or `,` for comma-separated list (which some commands like as input)
|
331
|
+
* `y` for YAML
|
332
|
+
* `p` for path, joining with `File.join`
|
333
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/ansible/dev.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
- hosts: localhost
|
3
|
+
|
4
|
+
vars:
|
5
|
+
|
6
|
+
ref_repos:
|
7
|
+
github:
|
8
|
+
- owner: rails
|
9
|
+
name: rails
|
10
|
+
version: master
|
11
|
+
dir_name: rails
|
12
|
+
|
13
|
+
tasks:
|
14
|
+
|
15
|
+
- name: install gems
|
16
|
+
command: bundle install --path=.bundle
|
17
|
+
args:
|
18
|
+
chdir: ..
|
19
|
+
|
20
|
+
- name: clone reference repos
|
21
|
+
git: repo=git@github.com:{{ item.owner }}/{{ item.name }}
|
22
|
+
dest="../ref/repos/{{ item.dir_name }}"
|
23
|
+
update=no
|
24
|
+
version={{ item.version }}
|
25
|
+
with_items: ref_repos.github
|
data/ansible/hosts
ADDED
data/cmds.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cmds/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cmds"
|
8
|
+
spec.version = Cmds::VERSION
|
9
|
+
spec.authors = ["nrser"]
|
10
|
+
spec.email = ["neil@ztkae.com"]
|
11
|
+
spec.summary = %q{helps read, write and remember commands.}
|
12
|
+
# spec.description = %q{TODO: Write a longer description. Optional.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'nrser', '~> 0.0.11'
|
22
|
+
spec.add_dependency 'erubis', '~> 2.7.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
end
|
data/lib/cmds/version.rb
ADDED