scripto 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4303784647ebd8ea25f1231f1c457490a15aebb8
4
+ data.tar.gz: 8503790cde1ec50455970da18a6d038ad6699cf1
5
+ SHA512:
6
+ metadata.gz: 5d9162c98b08f8f027d9233f5eca62db3485616dccecbb42a502e2d38db6734296575642a2f31563a39d04f0789a745ce62d5acfe95e1cb86a7c85bc74605b98
7
+ data.tar.gz: d047ea1eca14ef498dc563cb26c20b1864763d509a5649e798390193825a4e1031fd6554d8ab77803847d0e2ed68c301523873db23735c3256d869b3d97a24f2
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scripto.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 amd
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.
@@ -0,0 +1,29 @@
1
+ # Scripto
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'scripto'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install scripto
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/scripto/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rdoc/task"
4
+
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << "test"
7
+ end
8
+ task default: :test
9
+
10
+ RDoc::Task.new do |rdoc|
11
+ rdoc.rdoc_dir = "rdoc"
12
+ rdoc.title = "scripto #{Scripto::VERSION}"
13
+ rdoc.rdoc_files.include("lib/**/*.rb")
14
+ end
@@ -0,0 +1,15 @@
1
+ require "scripto/csv_commands"
2
+ require "scripto/file_commands"
3
+ require "scripto/misc_commands"
4
+ require "scripto/print_commands"
5
+ require "scripto/run_commands"
6
+ require "scripto/version"
7
+ require "scripto/main"
8
+
9
+ module Scripto
10
+ extend CsvCommands
11
+ extend FileCommands
12
+ extend MiscCommands
13
+ extend PrintCommands
14
+ extend RunCommands
15
+ end
@@ -0,0 +1,56 @@
1
+ require "csv"
2
+ require "ostruct"
3
+ require "zlib"
4
+
5
+ module Scripto
6
+ module CsvCommands
7
+ def csv_read(path)
8
+ lines = if path =~ /\.gz$/
9
+ Zlib::GzipReader.open(path) do |f|
10
+ CSV.new(f).read
11
+ end
12
+ else
13
+ CSV.read(path)
14
+ end
15
+ keys = lines.shift.map(&:to_sym)
16
+ klass = Struct.new(*keys)
17
+ lines.map { |i| klass.new(*i) }
18
+ end
19
+
20
+ # write rows to path as CSV
21
+ def csv_write(path, rows, cols: nil)
22
+ begin
23
+ tmp = "/tmp/_scripto_csv.csv"
24
+ CSV.open(tmp, "wb") { |f| csv_write0(f, rows, cols: cols) }
25
+ mv(tmp, path)
26
+ ensure
27
+ rm_if_necessary(tmp)
28
+ end
29
+ end
30
+
31
+ def csv_to_stdout(rows, cols: nil)
32
+ CSV($stdout) { |f| csv_write0(f, rows, cols: cols) }
33
+ end
34
+
35
+ def csv_to_s(rows, cols: nil)
36
+ string = ""
37
+ f = CSV.new(StringIO.new(string))
38
+ csv_write0(f, rows, cols: cols)
39
+ string
40
+ end
41
+
42
+ protected
43
+
44
+ def csv_write0(csv, rows, cols: nil)
45
+ # cols
46
+ cols ||= rows.first.to_h.keys
47
+ csv << cols
48
+
49
+ # rows
50
+ rows.each do |row|
51
+ row = row.to_h
52
+ csv << cols.map { |i| row[i] }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,100 @@
1
+ require "etc"
2
+ require "fileutils"
3
+
4
+ module Scripto
5
+ module FileCommands
6
+ def mkdir(dir, owner: nil, mode: nil)
7
+ FileUtils.mkdir_p(dir, verbose: verbose?)
8
+ chown(dir, owner) if owner
9
+ chmod(dir, mode) if mode
10
+ end
11
+
12
+ def cp(src, dst, mkdir: false, owner: nil, mode: nil)
13
+ mkdir_if_necessary(File.dirname(dst)) if mkdir
14
+ FileUtils.cp_r(src, dst, preserve: true, verbose: verbose?)
15
+ chown(dst, owner) if owner && !File.symlink?(dst)
16
+ chmod(dst, mode) if mode
17
+ end
18
+
19
+ def mv(src, dst, mkdir: false)
20
+ mkdir_if_necessary(File.dirname(dst)) if mkdir
21
+ FileUtils.mv(src, dst, verbose: verbose?)
22
+ end
23
+
24
+ def ln(src, dst)
25
+ begin
26
+ FileUtils.ln_sf(src, dst, verbose: verbose?)
27
+ rescue Errno::EEXIST => e
28
+ # It's a race - this can occur because ln_sf removes the old
29
+ # dst, then creates the symlink. Raise if they don't match.
30
+ raise e if !(File.symlink?(dst) && src == File.readlink(dst))
31
+ end
32
+ end
33
+
34
+ def rm(file)
35
+ FileUtils.rm_f(file, verbose: verbose?)
36
+ end
37
+
38
+ def mkdir_if_necessary(dir, owner: nil, mode: nil)
39
+ if !(File.exists?(dir) || File.symlink?(dir))
40
+ mkdir(dir, owner: owner, mode: mode)
41
+ true
42
+ end
43
+ end
44
+
45
+ def cp_if_necessary(src, dst, mkdir: false, owner: nil, mode: nil)
46
+ if !(File.exists?(dst) && FileUtils.compare_file(src, dst))
47
+ cp(src, dst, mkdir: mkdir, owner: owner, mode: mode)
48
+ true
49
+ end
50
+ end
51
+
52
+ def ln_if_necessary(src, dst)
53
+ ln = if !File.symlink?(dst)
54
+ true
55
+ elsif File.readlink(dst) != src
56
+ rm(dst)
57
+ true
58
+ end
59
+ if ln
60
+ ln(src, dst)
61
+ true
62
+ end
63
+ end
64
+
65
+ def rm_if_necessary(file)
66
+ if File.exists?(file)
67
+ rm(file)
68
+ true
69
+ end
70
+ end
71
+
72
+ def chown(file, user)
73
+ # who is the current owner?
74
+ @scripto_uids ||= {}
75
+ @scripto_uids[user] ||= Etc.getpwnam(user).uid
76
+ uid = @scripto_uids[user]
77
+ if File.stat(file).uid != uid
78
+ FileUtils.chown(uid, uid, file, verbose: verbose?)
79
+ end
80
+ end
81
+
82
+ def chmod(file, mode)
83
+ if File.stat(file).mode != mode
84
+ FileUtils.chmod(mode, file, verbose: verbose?)
85
+ end
86
+ end
87
+
88
+ def rm_and_mkdir(dir)
89
+ raise "don't do this" if dir == ""
90
+ FileUtils.rm_rf(dir, verbose: verbose?)
91
+ mkdir(dir)
92
+ end
93
+
94
+ def copy_metadata(src, dst)
95
+ stat = File.stat(src)
96
+ File.chmod(stat.mode, dst)
97
+ File.utime(stat.atime, stat.mtime, dst)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,16 @@
1
+ module Scripto
2
+ class Main
3
+ include CsvCommands
4
+ include FileCommands
5
+ include MiscCommands
6
+ include PrintCommands
7
+ include RunCommands
8
+
9
+ attr_accessor :options
10
+
11
+ def initialize(options = {})
12
+ self.options = options
13
+ self.verbose = options[:verbose]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ require "digest/md5"
2
+ require "etc"
3
+
4
+ module Scripto
5
+ module MiscCommands
6
+ BASE_62 = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a
7
+
8
+ def whoami
9
+ @scripto_whoami ||= Etc.getpwuid(Process.uid).name
10
+ end
11
+
12
+ def root?
13
+ whoami == "root"
14
+ end
15
+
16
+ def md5_file(path)
17
+ File.open(path) do |f|
18
+ digest, buf = Digest::MD5.new, ""
19
+ while f.read(4096, buf)
20
+ digest.update(buf)
21
+ end
22
+ digest.hexdigest
23
+ end
24
+ end
25
+
26
+ def md5_string(s)
27
+ Digest::MD5.hexdigest(s.to_s)
28
+ end
29
+
30
+ def prompt?(question)
31
+ $stderr.write("#{question} (y/n) ")
32
+ $stderr.flush
33
+ $stdin.gets =~ /^y/i
34
+ end
35
+
36
+ def random_string(len)
37
+ (1..len).map { BASE_62.sample }.join
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,45 @@
1
+ module Scripto
2
+ module PrintCommands
3
+ RESET = "\e[0m"
4
+ GREEN = "\e[1;37;42m"
5
+ YELLOW = "\e[1;37;43m"
6
+ RED = "\e[1;37;41m"
7
+
8
+ attr_accessor :verbose
9
+
10
+ def verbose?
11
+ !!@verbose
12
+ end
13
+
14
+ def verbose!
15
+ @verbose = true
16
+ end
17
+
18
+ def vbanner(s = nil)
19
+ banner(s) if verbose?
20
+ end
21
+
22
+ def vputs(s = nil)
23
+ $stderr.puts(s) if verbose?
24
+ end
25
+
26
+ def vprintf(s, *args)
27
+ $stderr.printf(s, *args) if verbose?
28
+ end
29
+
30
+ def banner(s, color: GREEN)
31
+ now = Time.new.strftime("%H:%M:%S")
32
+ s = "#{s} ".ljust(72, " ")
33
+ $stderr.puts "#{color}[#{now}] #{s}#{RESET}"
34
+ end
35
+
36
+ def warning(msg)
37
+ banner("Warning: #{msg}", color: YELLOW)
38
+ end
39
+
40
+ def fatal(msg)
41
+ banner(msg, color: RED)
42
+ exit(1)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,86 @@
1
+ require "english"
2
+ require "shellwords"
3
+
4
+ module Scripto
5
+ module RunCommands
6
+ class Error < StandardError ; end
7
+
8
+ def run(command, args = nil)
9
+ cmd = CommandLine.new(command, args)
10
+ vputs(cmd)
11
+ cmd.run
12
+ end
13
+
14
+ def run_capture(command, args = nil)
15
+ CommandLine.new(command, args).capture
16
+ end
17
+
18
+ def run_quietly(command, args = nil)
19
+ cmd = CommandLine.new(command, args)
20
+ run("#{cmd} > /dev/null 2> /dev/null")
21
+ end
22
+
23
+ def run_succeeds?(command, args = nil)
24
+ begin
25
+ run_quietly(command, args)
26
+ true
27
+ rescue Error
28
+ false
29
+ end
30
+ end
31
+
32
+ def run_fails?(command, args = nil)
33
+ !run_succeeds?(command, args)
34
+ end
35
+
36
+ def shellescape(s)
37
+ Shellwords.escape(s)
38
+ end
39
+
40
+ protected
41
+
42
+ class CommandLine
43
+ attr_accessor :command, :args
44
+
45
+ def initialize(command, args)
46
+ self.command = command
47
+ self.args = if args
48
+ args.map(&:to_s)
49
+ else
50
+ [ ]
51
+ end
52
+ end
53
+
54
+ def run
55
+ system(command, *args)
56
+ raise!($CHILD_STATUS) if $CHILD_STATUS != 0
57
+ end
58
+
59
+ def capture
60
+ begin
61
+ captured = `#{to_s}`
62
+ rescue Errno::ENOENT
63
+ raise Error, "#{self} failed : ENOENT (No such file or directory)"
64
+ end
65
+ raise!($CHILD_STATUS) if $CHILD_STATUS != 0
66
+ captured
67
+ end
68
+
69
+ def raise!(status)
70
+ if status.termsig == Signal.list["INT"]
71
+ raise "#{self} interrupted"
72
+ end
73
+ raise Error, "#{self} failed : #{status.to_i / 256}"
74
+ end
75
+
76
+ def to_s
77
+ if args.length > 0
78
+ escaped = args.map { |i| Shellwords.escape(i) }
79
+ "#{command} #{escaped.join(" ")}"
80
+ else
81
+ command
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ module Scripto
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scripto/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scripto"
8
+ spec.version = Scripto::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.required_ruby_version = ">= 2.0.0"
11
+ spec.authors = ["Adam Doppelt"]
12
+ spec.email = ["amd@gurge.com"]
13
+ spec.summary = "Helpers for writing command line scripts."
14
+ spec.homepage = "http://github.com/gurgeous/scripto"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1,26 @@
1
+ require "fileutils"
2
+ require "minitest/autorun"
3
+ require "minitest/pride"
4
+
5
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
6
+ require "scripto"
7
+
8
+ module Helper
9
+ TMP_DIR = "/tmp/_scripto_test"
10
+
11
+ # attr_accessor :main
12
+
13
+ def setup
14
+ # self.main = Scripto::Main.new
15
+ FileUtils.rm_rf(TMP_DIR)
16
+ FileUtils.mkdir_p(TMP_DIR)
17
+ @pwd = Dir.pwd
18
+ Dir.chdir(TMP_DIR)
19
+ Scripto.verbose = false
20
+ end
21
+
22
+ def teardown
23
+ FileUtils.rm_rf(TMP_DIR)
24
+ Dir.chdir(@pwd)
25
+ end
26
+ end
@@ -0,0 +1,60 @@
1
+ require_relative "helper"
2
+
3
+ class TestCsv < Minitest::Test
4
+ include Helper
5
+
6
+ FILE = "test.csv"
7
+
8
+ ROWS = [ { a: "1", b: "2", c: "3" }, { a: "4", b: "5", c: "6" } ]
9
+ ROWS_EXP = "a,b,c\n1,2,3\n4,5,6\n"
10
+ ROWS_REVERSED_EXP = "c,b,a\n3,2,1\n6,5,4\n"
11
+
12
+ def test_types
13
+ assert_equal(ROWS_EXP, Scripto.csv_to_s(ROWS))
14
+ assert_equal(ROWS_EXP, Scripto.csv_to_s(structs))
15
+ assert_equal(ROWS_EXP, Scripto.csv_to_s(ostructs))
16
+ end
17
+
18
+ def test_cols
19
+ assert_equal(ROWS_REVERSED_EXP, Scripto.csv_to_s(ROWS, cols: %i(c b a)))
20
+ assert_equal(ROWS_REVERSED_EXP, Scripto.csv_to_s(structs, cols: %i(c b a)))
21
+ assert_equal(ROWS_REVERSED_EXP, Scripto.csv_to_s(ostructs, cols: %i(c b a)))
22
+ end
23
+
24
+ def test_csv_stdout
25
+ assert_output(ROWS_EXP) { Scripto.csv_to_stdout(ROWS) }
26
+ end
27
+
28
+ def test_csv_read_write
29
+ Scripto.csv_write(FILE, ROWS)
30
+ assert_equal(ROWS_EXP, File.read(FILE))
31
+ assert_equal(ROWS, Scripto.csv_read(FILE).map(&:to_h))
32
+ end
33
+
34
+ def test_gz
35
+ Scripto.csv_write(FILE, ROWS)
36
+ Scripto.run("gzip #{FILE}")
37
+ assert_equal(ROWS, Scripto.csv_read("#{FILE}.gz").map(&:to_h))
38
+ end
39
+
40
+ def test_atomic
41
+ magic = "can't touch this"
42
+ File.write(FILE, magic)
43
+ begin
44
+ Util.csv_write(TMP, rows, cols: %i(bad_column))
45
+ rescue
46
+ end
47
+ assert_equal(magic, File.read(FILE))
48
+ end
49
+
50
+ protected
51
+
52
+ def structs
53
+ klass = Struct.new(*ROWS.first.keys)
54
+ ROWS.map { |i| klass.new(*i.values) }
55
+ end
56
+
57
+ def ostructs
58
+ ROWS.map { |i| OpenStruct.new(i) }
59
+ end
60
+ end
@@ -0,0 +1,200 @@
1
+ require_relative "helper"
2
+
3
+ class TestFile < Minitest::Test
4
+ include Helper
5
+
6
+ DIR = "dir"
7
+ SRC, DST = "src.txt", "dst.txt"
8
+ SRC2, DST2 = "src2.txt", "dst2.txt"
9
+
10
+ def setup
11
+ super
12
+ File.write(SRC, "something")
13
+ File.write(SRC2, "another thing")
14
+ end
15
+
16
+ #
17
+ # basics
18
+ #
19
+
20
+ def test_mkdir
21
+ Scripto.mkdir(DIR)
22
+ assert(File.directory?(DIR))
23
+ end
24
+
25
+ def test_mkdir_mode
26
+ Scripto.mkdir(DIR, mode: 0644)
27
+ assert_equal(0644, File.stat(DIR).mode & 0644)
28
+ end
29
+
30
+ def test_cp
31
+ Scripto.cp(SRC, DST)
32
+ assert_equal(File.read(SRC), File.read(DST))
33
+ end
34
+
35
+ def test_cp_mode
36
+ Scripto.cp(SRC, DST, mode: 0644)
37
+ assert_equal(0644, File.stat(DST).mode & 0644)
38
+ end
39
+
40
+ def test_cp_mkdir
41
+ in_dir = "#{DIR}/in_dir"
42
+ Scripto.cp(SRC, in_dir, mkdir: true)
43
+ assert_equal(File.read(SRC), File.read(in_dir))
44
+ end
45
+
46
+ def test_mv
47
+ Scripto.mv(SRC, DST)
48
+ assert_equal("something", File.read(DST))
49
+ end
50
+
51
+ def test_mv_mkdir
52
+ in_dir = "#{DIR}/in_dir"
53
+ Scripto.mv(SRC, in_dir, mkdir: true)
54
+ assert_equal("something", File.read(in_dir))
55
+ end
56
+
57
+ def test_ln
58
+ Scripto.ln(SRC, DST)
59
+ assert_equal(SRC, File.readlink(DST))
60
+ end
61
+
62
+ def test_rm
63
+ Scripto.rm(SRC)
64
+ assert(!File.exists?(SRC))
65
+ Scripto.rm("this_file_doesnt_exist") # shouldn't complain
66
+ end
67
+
68
+ #
69
+ # if necessary (this is useful for printing)
70
+ #
71
+
72
+ def test_if_necessary
73
+ # should return true
74
+ assert Scripto.mkdir_if_necessary(DIR)
75
+ assert Scripto.cp_if_necessary(SRC, DST)
76
+ assert Scripto.ln_if_necessary(SRC, DST2)
77
+
78
+ assert(File.directory?(DIR))
79
+ assert_equal(File.read(SRC), File.read(DST))
80
+ assert_equal(SRC, File.readlink(DST2))
81
+
82
+ # should be silent
83
+ assert_fu_output(nil, "") do
84
+ assert_nil Scripto.mkdir_if_necessary(DIR)
85
+ assert_nil Scripto.cp_if_necessary(SRC, DST)
86
+ assert_nil Scripto.ln_if_necessary(SRC, DST2)
87
+ end
88
+ end
89
+
90
+ def test_cp_if_necessary_differs
91
+ File.write(DST, "this is different")
92
+ assert_fu_output(nil, "cp -rp #{SRC} #{DST}\n") do
93
+ assert Scripto.cp_if_necessary(SRC, DST)
94
+ end
95
+ end
96
+
97
+ def test_ln_if_necessary_differs
98
+ File.symlink(SRC2, DST)
99
+ assert_fu_output(nil, "rm -f #{DST}\nln -sf #{SRC} #{DST}\n") do
100
+ assert Scripto.ln_if_necessary(SRC, DST)
101
+ assert_equal(SRC, File.readlink(DST))
102
+ end
103
+ end
104
+
105
+ def test_chmod
106
+ Scripto.chmod(SRC, 0644)
107
+ assert_equal(0644, File.stat(SRC).mode & 0644)
108
+ end
109
+
110
+ def test_rm_and_mkdir
111
+ Dir.mkdir(DIR)
112
+ File.write("#{DIR}/file", "this is a test")
113
+ assert Dir["#{DIR}/*"].length == 1
114
+ Scripto.rm_and_mkdir(DIR)
115
+ assert Dir["#{DIR}/*"].length == 0
116
+ end
117
+
118
+ def test_copy_metadata
119
+ File.chmod(0644, SRC)
120
+ File.utime(1234, 5678, SRC)
121
+ Scripto.copy_metadata(SRC, SRC2)
122
+ assert_equal(0644, File.stat(SRC2).mode & 0644)
123
+ assert_equal(1234, File.stat(SRC2).atime.to_i)
124
+ assert_equal(5678, File.stat(SRC2).mtime.to_i)
125
+ end
126
+
127
+ #
128
+ # chown rests - must be root
129
+ #
130
+
131
+ def test_mkdir_owner
132
+ skip if !root?
133
+ Scripto.mkdir(DIR, owner: "nobody")
134
+ assert(Etc.getpwnam("nobody").uid, File.stat(DIR).uid)
135
+ end
136
+
137
+ def test_cp_owner
138
+ skip if !root?
139
+ Scripto.cp(SRC, DST, owner: "nobody")
140
+ assert(Etc.getpwnam("nobody").uid, File.stat(DST).uid)
141
+ end
142
+
143
+ def test_chown
144
+ skip if !root?
145
+ Scripto.chown(SRC, "nobody")
146
+ assert(Etc.getpwnam("nobody").uid, File.stat(SRC).uid)
147
+ end
148
+
149
+ #
150
+ # verbosity
151
+ #
152
+
153
+ def test_mkdir_verbose
154
+ assert_fu_output(nil, "mkdir -p #{DIR}\n") do
155
+ Scripto.mkdir(DIR)
156
+ end
157
+ end
158
+
159
+ def test_cp_verbose
160
+ assert_fu_output(nil, "cp -rp #{SRC} #{DST}\n") do
161
+ Scripto.cp(SRC, DST)
162
+ end
163
+ end
164
+
165
+ def test_mv_verbose
166
+ assert_fu_output(nil, "mv #{SRC} #{DST}\n") do
167
+ Scripto.mv(SRC, DST)
168
+ end
169
+ end
170
+
171
+ def test_ln_verbose
172
+ assert_fu_output(nil, "ln -sf #{SRC} #{DST}\n") do
173
+ Scripto.ln(SRC, DST)
174
+ end
175
+ end
176
+
177
+ def test_rm_verbose
178
+ assert_fu_output(nil, "rm -f #{SRC}\n") do
179
+ Scripto.rm(SRC)
180
+ end
181
+ end
182
+
183
+ protected
184
+
185
+ def root?
186
+ if !defined?(@root)
187
+ @root = `whoami`.strip == "root"
188
+ end
189
+ @root
190
+ end
191
+
192
+ def assert_fu_output(stdout = nil, stderr = nil, &block)
193
+ Scripto.verbose!
194
+ assert_output(stdout, stderr) do
195
+ # FileUtils squirrels this away so we have to set it manually
196
+ FileUtils.instance_eval("@fileutils_output = $stderr")
197
+ yield
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,53 @@
1
+ require_relative "helper"
2
+
3
+ class TestMisc < Minitest::Test
4
+ include Helper
5
+
6
+ def test_whoami
7
+ assert_equal(`whoami`.strip, Scripto.whoami)
8
+ end
9
+
10
+ def test_root?
11
+ assert_equal(`whoami`.strip == "root", Scripto.root?)
12
+ end
13
+
14
+ def test_md5_string
15
+ assert_equal("ba73632f801ac2c72d78134722f2cb84", Scripto.md5_string("gub"))
16
+ end
17
+
18
+ def test_md5_file
19
+ File.write("test.txt", "gub")
20
+ assert_equal("ba73632f801ac2c72d78134722f2cb84", Scripto.md5_file("test.txt"))
21
+ end
22
+
23
+ def test_prompt?
24
+ with_fake_stdin("YES") do
25
+ assert_output(nil, "question (y/n) ") do
26
+ assert Scripto.prompt?("question")
27
+ end
28
+ end
29
+ with_fake_stdin("y") do
30
+ assert_output(nil, "question (y/n) ") do
31
+ assert Scripto.prompt?("question")
32
+ end
33
+ end
34
+ with_fake_stdin("no") do
35
+ assert_output(nil, "question (y/n) ") do
36
+ assert !Scripto.prompt?("question")
37
+ end
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def with_fake_stdin(str, &block)
44
+ old_stdin = $stdin
45
+ begin
46
+ $stdin = StringIO.new(str)
47
+ $stdin.rewind
48
+ yield
49
+ ensure
50
+ $stdin = old_stdin
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ require_relative "helper"
2
+
3
+ class TestPrint < Minitest::Test
4
+ include Helper
5
+
6
+ def test_verbose?
7
+ assert(!Scripto.verbose?)
8
+ Scripto.verbose!
9
+ assert(Scripto.verbose?)
10
+ end
11
+
12
+ def test_quiet
13
+ assert_silent { Scripto.vbanner "gub" }
14
+ assert_silent { Scripto.vprintf "gub" }
15
+ assert_silent { Scripto.vputs "gub" }
16
+ end
17
+
18
+ def test_loud
19
+ Scripto.verbose!
20
+ assert_output(nil, /gub/) { Scripto.vbanner "gub" }
21
+ assert_output(nil, /gub/) { Scripto.vprintf "gub" }
22
+ assert_output(nil, /gub/) { Scripto.vputs "gub" }
23
+ end
24
+
25
+ def test_warning
26
+ assert_output(nil, /gub/) { Scripto.warning "gub" }
27
+ end
28
+ end
@@ -0,0 +1,103 @@
1
+ require_relative "helper"
2
+
3
+ class TestRun < Minitest::Test
4
+ include Helper
5
+
6
+ SUCCEEDS = "echo gub"
7
+ FAILS = "cat scripto_bogus_file 2> /dev/null"
8
+ BAD_COMMAND = "this_command_doesnt_exist"
9
+ SRC = "_scripto_src"
10
+ DST = "_scripto with spaces"
11
+ ARGS = [ "-f", SRC, DST ]
12
+
13
+ def setup
14
+ super
15
+ File.write(SRC, "something")
16
+ end
17
+
18
+ #
19
+ # basic methods
20
+ #
21
+
22
+ def test_run
23
+ Scripto.run("#{SUCCEEDS} > #{SRC}")
24
+ assert_equal("gub", File.read(SRC).strip)
25
+ assert("gub", Scripto.run_capture(SUCCEEDS))
26
+ Scripto.run_quietly(SUCCEEDS)
27
+ end
28
+
29
+ def test_run_succeeds?
30
+ assert_succeeds(SUCCEEDS)
31
+ assert_fails(FAILS)
32
+ assert_fails(BAD_COMMAND)
33
+ end
34
+
35
+ def test_shellescape
36
+ assert_equal("gub", Scripto.shellescape("gub"))
37
+ assert_equal("gub\\ zub", Scripto.shellescape("gub zub"))
38
+ end
39
+
40
+ # verbosity
41
+ def test_verbose
42
+ Scripto.verbose!
43
+ cmd = "#{SUCCEEDS} > /dev/null"
44
+ assert_output(nil, "#{cmd}\n") do
45
+ Scripto.run(cmd)
46
+ end
47
+ end
48
+
49
+ # commands that fail
50
+ def test_failures
51
+ assert_raises(Scripto::RunCommands::Error) { Scripto.run(BAD_COMMAND) }
52
+ assert_raises(Scripto::RunCommands::Error) { Scripto.run_capture(BAD_COMMAND) }
53
+ assert_raises(Scripto::RunCommands::Error) { Scripto.run(FAILS) }
54
+ assert_raises(Scripto::RunCommands::Error) { Scripto.run_capture(FAILS) }
55
+ end
56
+
57
+ #
58
+ # args
59
+ #
60
+
61
+ def test_run_args
62
+ # make sure SRC is copied to DST in all cases
63
+ assert_cp { Scripto.run("cp", ARGS) }
64
+ assert_cp { Scripto.run_quietly("cp", ARGS) }
65
+ assert_cp { assert_succeeds("cp", ARGS) }
66
+ end
67
+
68
+ def test_capture_escaping
69
+ tricky = "\"'!tricky!'\""
70
+ assert_equal("#{tricky} #{tricky}\n", Scripto.run_capture("echo", [tricky, tricky]))
71
+ end
72
+
73
+ def test_args_succeeds_fails
74
+ assert_fails(BAD_COMMAND, ARGS)
75
+ end
76
+
77
+ # is output escaped properly with verbose?
78
+ def test_args_verbose
79
+ Scripto.verbose!
80
+ assert_output(nil, "cp -f #{SRC} #{DST.gsub(" ", "\\ ")}\n") do
81
+ Scripto.run("cp", ARGS)
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def assert_cp(&block)
88
+ File.unlink(DST) if File.exists?(DST)
89
+ yield
90
+ assert_equal(File.read(SRC), File.read(DST))
91
+ File.unlink(DST)
92
+ end
93
+
94
+ def assert_succeeds(command, args = nil)
95
+ assert_equal(true, Scripto.run_succeeds?(command, args))
96
+ assert_equal(false, Scripto.run_fails?(command, args))
97
+ end
98
+
99
+ def assert_fails(command, args = nil)
100
+ assert_equal(false, Scripto.run_succeeds?(command, args))
101
+ assert_equal(true, Scripto.run_fails?(command, args))
102
+ end
103
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scripto
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Adam Doppelt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - amd@gurge.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/scripto.rb
68
+ - lib/scripto/csv_commands.rb
69
+ - lib/scripto/file_commands.rb
70
+ - lib/scripto/main.rb
71
+ - lib/scripto/misc_commands.rb
72
+ - lib/scripto/print_commands.rb
73
+ - lib/scripto/run_commands.rb
74
+ - lib/scripto/version.rb
75
+ - scripto.gemspec
76
+ - test/helper.rb
77
+ - test/test_csv.rb
78
+ - test/test_file.rb
79
+ - test/test_misc.rb
80
+ - test/test_print.rb
81
+ - test/test_run.rb
82
+ homepage: http://github.com/gurgeous/scripto
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: 2.0.0
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.2.1
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Helpers for writing command line scripts.
106
+ test_files:
107
+ - test/helper.rb
108
+ - test/test_csv.rb
109
+ - test/test_file.rb
110
+ - test/test_misc.rb
111
+ - test/test_print.rb
112
+ - test/test_run.rb