scripto 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 682b3b4d477f4c653c2f4f28b650eeb539d7c7fd
4
- data.tar.gz: 1b9e0f393c6d9666f6cb374ca2c485ca95272fc6
2
+ SHA256:
3
+ metadata.gz: e0ea62ce310b383c38d173c5e3cd61b2eb4e7ea345c92f6b2f1874e7c6a3c93f
4
+ data.tar.gz: 0fc744955f01e99d7ee44f53b927d1a93ca6d46e35a7e1b9baf36f62e9939711
5
5
  SHA512:
6
- metadata.gz: 2780520e158ddcf2843a38361c1fd64a9d041c7c215901ad6fe95e5932c43acca75fbda8bf2af8bb6c3928b795e41edf46e2ef3e7cd913d758558d7d8428bcd2
7
- data.tar.gz: de8a0e1eb329fddfe4ff9e86efc65ecbaacc2765c6bbaed3877492b5e58f97b5f89af5d9d5f719fd4743a5171c9c31632c52b2fc63311b460f692bcfb50f5f32
6
+ metadata.gz: fee85c4a32b32d4caf1f54aec0efac6e0634f2217154c1c6367b1c184878d08b14f9f4e035eda2d78b4157bef41d839b7044fbe230957bf81eba2df3bf451f8e
7
+ data.tar.gz: 7664c604a09572d1cecf1e4e7cc1d3b8845e6dfb5ff1ea1e37edf660a58007f19a17a46668786a8014a71f36c373e6eff7b8f811abe5d3b3f14dd854cb068e8f
data/.gitignore CHANGED
@@ -1,14 +1,15 @@
1
- *.gem
2
- *.rbc
1
+ _yardoc
3
2
  .bundle
4
3
  .config
4
+ .ruby-version
5
5
  .vagrant
6
6
  .yardoc
7
- Gemfile.lock
8
- InstalledFiles
9
- _yardoc
7
+ *.gem
8
+ *.rbc
10
9
  coverage
11
10
  doc/
11
+ Gemfile.lock
12
+ InstalledFiles
12
13
  lib/bundler/man
13
14
  pkg
14
15
  rdoc
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
+ before_install:
3
+ - gem install bundler
2
4
  rvm:
3
- - 2.0.0
4
- - 2.1.0
5
- - 2.2.0
6
5
  - 2.3.0
7
6
  - 2.4.0
7
+ - 2.5.0
8
+ - 2.6.0
9
+ - 2.7.0
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Scripto
2
2
 
3
+ [![Build Status](https://travis-ci.org/gurgeous/scripto.svg?branch=master)](https://travis-ci.org/gurgeous/scripto)
4
+
3
5
  Scripto is a framework for writing command line applications. It fills in many of the blanks that Ruby's standard library is missing:
4
6
 
5
- * **print to $stderr** - Colored banners and a verbose mode to make your scripts louder.
6
- * **file operations** - Mkdir, cp, mv, ln. These operations can take care of common edge cases, like creating the target directory before copying a file.
7
- * **run commands** - Run external commands and raise errors on failure.
8
- * **csv** - Read and write CSV files from hashes, Structs, or OpenStructs.
7
+ - **print to \$stderr** - Colored banners and a verbose mode to make your scripts louder.
8
+ - **file operations** - Mkdir, cp, mv, ln. These operations can take care of common edge cases, like creating the target directory before copying a file.
9
+ - **run commands** - Run external commands and raise errors on failure.
10
+ - **csv** - Read and write CSV files from hashes, Structs, or OpenStructs.
9
11
 
10
12
  Rdoc at [rdoc.info](http://rdoc.info/github/gurgeous/scripto/). Thanks!
11
13
 
@@ -65,7 +67,7 @@ Install.new(verbose: true)
65
67
 
66
68
  ## Methods
67
69
 
68
- ### Print to $stderr
70
+ ### Print to \$stderr
69
71
 
70
72
  ```
71
73
  banner(str) - print a banner in green
@@ -151,3 +153,10 @@ md5_string(str) - calculate md5 for a string
151
153
  prompt?(question) - ask the user a question, return true if they say yes
152
154
  random_string(len) - calculate a random alphanumeric string
153
155
  ```
156
+
157
+ # Changelog
158
+
159
+ 0.0.4 (late 2020)
160
+
161
+ - Added support for reading CSV with BOM.
162
+ - Only support Ruby 2.3+, since we moved to modern Bundler
data/Rakefile CHANGED
@@ -1,16 +1,21 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
- require "rdoc/task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
4
+ require 'rubocop/rake_task'
4
5
 
5
6
  Rake::TestTask.new(:test) do |test|
6
- test.libs << "test"
7
+ test.libs << 'test'
7
8
  end
8
9
  task default: :test
9
10
 
10
11
  RDoc::Task.new do |rdoc|
11
- rdoc.rdoc_dir = "rdoc"
12
+ rdoc.rdoc_dir = 'rdoc'
12
13
  rdoc.title = "scripto #{Scripto::VERSION}"
13
- rdoc.main = "README.md"
14
- rdoc.rdoc_files.include("lib/**/*.rb")
15
- rdoc.rdoc_files.include("README.md")
16
- end
14
+ rdoc.main = 'README.md'
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ rdoc.rdoc_files.include('README.md')
17
+ end
18
+
19
+ RuboCop::RakeTask.new do |task|
20
+ task.options << '--display-cop-names'
21
+ end
@@ -1,121 +1,16 @@
1
- # -*- mode: ruby -*-
2
- # vi: set ft=ruby :
3
-
4
- # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5
- VAGRANTFILE_API_VERSION = "2"
6
-
7
- Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8
- # All Vagrant configuration is done here. The most common configuration
9
- # options are documented and commented below. For a complete reference,
10
- # please see the online documentation at vagrantup.com.
11
-
12
- # Every Vagrant virtual environment requires a box to build off of.
13
- config.vm.box = "precise32"
14
-
15
- # The url from where the 'config.vm.box' box will be fetched if it
16
- # doesn't already exist on the user's system.
17
- config.vm.box_url = "http://files.vagrantup.com/precise32.box"
18
-
19
- # amd
20
- config.vm.provision :shell, path: "vagrant_provision", privileged: false
21
-
22
- # Create a forwarded port mapping which allows access to a specific port
23
- # within the machine from a port on the host machine. In the example below,
24
- # accessing "localhost:8080" will access port 80 on the guest machine.
25
- # config.vm.network :forwarded_port, guest: 80, host: 8080
26
-
27
- # Create a private network, which allows host-only access to the machine
28
- # using a specific IP.
29
- # config.vm.network :private_network, ip: "192.168.33.10"
30
-
31
- # Create a public network, which generally matched to bridged network.
32
- # Bridged networks make the machine appear as another physical device on
33
- # your network.
34
- # config.vm.network :public_network
35
-
36
- # If true, then any SSH connections made will enable agent forwarding.
37
- # Default value: false
38
- # config.ssh.forward_agent = true
39
-
40
- # Share an additional folder to the guest VM. The first argument is
41
- # the path on the host to the actual folder. The second argument is
42
- # the path on the guest to mount the folder. And the optional third
43
- # argument is a set of non-required options.
44
- # config.vm.synced_folder "../data", "/vagrant_data"
45
-
46
- # Provider-specific configuration so you can fine-tune various
47
- # backing providers for Vagrant. These expose provider-specific options.
48
- # Example for VirtualBox:
49
- #
50
- # config.vm.provider :virtualbox do |vb|
51
- # # Don't boot with headless mode
52
- # vb.gui = true
53
- #
54
- # # Use VBoxManage to customize the VM. For example to change memory:
55
- # vb.customize ["modifyvm", :id, "--memory", "1024"]
56
- # end
57
- #
58
- # View the documentation for the provider you're using for more
59
- # information on available options.
60
-
61
- # Enable provisioning with Puppet stand alone. Puppet manifests
62
- # are contained in a directory path relative to this Vagrantfile.
63
- # You will need to create the manifests directory and a manifest in
64
- # the file precise32.pp in the manifests_path directory.
65
- #
66
- # An example Puppet manifest to provision the message of the day:
67
- #
68
- # # group { "puppet":
69
- # # ensure => "present",
70
- # # }
71
- # #
72
- # # File { owner => 0, group => 0, mode => 0644 }
73
- # #
74
- # # file { '/etc/motd':
75
- # # content => "Welcome to your Vagrant-built virtual machine!
76
- # # Managed by Puppet.\n"
77
- # # }
78
- #
79
- # config.vm.provision :puppet do |puppet|
80
- # puppet.manifests_path = "manifests"
81
- # puppet.manifest_file = "site.pp"
82
- # end
83
-
84
- # Enable provisioning with chef solo, specifying a cookbooks path, roles
85
- # path, and data_bags path (all relative to this Vagrantfile), and adding
86
- # some recipes and/or roles.
87
- #
88
- # config.vm.provision :chef_solo do |chef|
89
- # chef.cookbooks_path = "../my-recipes/cookbooks"
90
- # chef.roles_path = "../my-recipes/roles"
91
- # chef.data_bags_path = "../my-recipes/data_bags"
92
- # chef.add_recipe "mysql"
93
- # chef.add_role "web"
94
- #
95
- # # You may also specify custom JSON attributes:
96
- # chef.json = { :mysql_password => "foo" }
97
- # end
98
-
99
- # Enable provisioning with chef server, specifying the chef server URL,
100
- # and the path to the validation key (relative to this Vagrantfile).
101
- #
102
- # The Opscode Platform uses HTTPS. Substitute your organization for
103
- # ORGNAME in the URL and validation key.
104
- #
105
- # If you have your own Chef Server, use the appropriate URL, which may be
106
- # HTTP instead of HTTPS depending on your configuration. Also change the
107
- # validation key to validation.pem.
108
- #
109
- # config.vm.provision :chef_client do |chef|
110
- # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
111
- # chef.validation_key_path = "ORGNAME-validator.pem"
112
- # end
113
- #
114
- # If you're using the Opscode platform, your validator client is
115
- # ORGNAME-validator, replacing ORGNAME with your organization name.
116
- #
117
- # If you have your own Chef Server, the default validation client name is
118
- # chef-validator, unless you changed the configuration.
119
- #
120
- # chef.validation_client_name = "ORGNAME-validator"
1
+ Vagrant.configure('2') do |config|
2
+ config.vm.box = 'ubuntu/xenial64'
3
+ config.vm.provision 'shell', privileged: false, inline: <<-EOF
4
+ export DEBIAN_FRONTEND=noninteractive
5
+
6
+ # ruby 2.3
7
+ sudo apt-add-repository ppa:brightbox/ruby-ng
8
+ sudo apt-get update
9
+ sudo apt-get install -y ruby2.3
10
+ sudo gem install bundler
11
+
12
+ # Gemfile
13
+ cd /vagrant
14
+ bundle install
15
+ EOF
121
16
  end
@@ -1,10 +1,10 @@
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"
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
8
 
9
9
  module Scripto
10
10
  extend CsvCommands
@@ -1,17 +1,25 @@
1
- require "csv"
2
- require "zlib"
1
+ require 'csv'
2
+ require 'tempfile'
3
+ require 'zlib'
3
4
 
4
5
  module Scripto
5
6
  module CsvCommands
6
7
  # Read a csv from +path+. Returns an array of Structs, using the keys from
7
8
  # the csv header row.
8
9
  def csv_read(path)
9
- lines = if path =~ /\.gz$/
10
- Zlib::GzipReader.open(path) do |f|
11
- CSV.new(f).read
10
+ lines = begin
11
+ if path =~ /\.gz$/
12
+ Zlib::GzipReader.open(path) do |f|
13
+ CSV.new(f).read
14
+ end
15
+ else
16
+ encoding = 'bom|utf-8'
17
+ if RUBY_VERSION >= "2.6.0"
18
+ CSV.read(path, encoding: encoding)
19
+ else
20
+ CSV.read(path, "r:#{encoding}")
21
+ end
12
22
  end
13
- else
14
- CSV.read(path)
15
23
  end
16
24
  keys = lines.shift.map(&:to_sym)
17
25
  klass = Struct.new(*keys)
@@ -23,12 +31,8 @@ module Scripto
23
31
  # first row are used as the csv header. If +cols+ is specified, it will be
24
32
  # used as the column keys instead.
25
33
  def csv_write(path, rows, cols: nil)
26
- begin
27
- tmp = "/tmp/_scripto_csv.csv"
28
- CSV.open(tmp, "wb") { |f| csv_write0(f, rows, cols: cols) }
29
- mv(tmp, path)
30
- ensure
31
- rm_if_necessary(tmp)
34
+ atomic_write(path) do |tmp|
35
+ CSV.open(tmp.path, 'wb') { |f| csv_write0(f, rows, cols: cols) }
32
36
  end
33
37
  end
34
38
 
@@ -39,7 +43,7 @@ module Scripto
39
43
 
40
44
  # Returns a string containing +rows+ as a csv. Similar to csv_write.
41
45
  def csv_to_s(rows, cols: nil)
42
- string = ""
46
+ string = ''
43
47
  f = CSV.new(StringIO.new(string))
44
48
  csv_write0(f, rows, cols: cols)
45
49
  string
@@ -60,4 +64,4 @@ module Scripto
60
64
  end
61
65
  end
62
66
  end
63
- end
67
+ end
@@ -1,5 +1,5 @@
1
- require "etc"
2
- require "fileutils"
1
+ require 'etc'
2
+ require 'fileutils'
3
3
 
4
4
  module Scripto
5
5
  module FileCommands
@@ -36,13 +36,11 @@ module Scripto
36
36
  # Like ln -sf +src+ +dst. The command will be printed out if
37
37
  # verbose?.
38
38
  def ln(src, dst)
39
- begin
40
- FileUtils.ln_sf(src, dst, verbose: verbose?)
41
- rescue Errno::EEXIST => e
42
- # It's a race - this can occur because ln_sf removes the old
43
- # dst, then creates the symlink. Raise if they don't match.
44
- raise e if !(File.symlink?(dst) && src == File.readlink(dst))
45
- end
39
+ FileUtils.ln_sf(src, dst, verbose: verbose?)
40
+ rescue Errno::EEXIST => e
41
+ # It's a race - this can occur because ln_sf removes the old
42
+ # dst, then creates the symlink. Raise if they don't match.
43
+ raise e if !(File.symlink?(dst) && src == File.readlink(dst))
46
44
  end
47
45
 
48
46
  # Like rm -f +file+. Like all file commands, the operation will be printed
@@ -55,45 +53,43 @@ module Scripto
55
53
  # directory had to be created. This is useful with verbose?, to get an
56
54
  # exact changelog.
57
55
  def mkdir_if_necessary(dir, owner: nil, mode: nil)
58
- if !(File.exist?(dir) || File.symlink?(dir))
59
- mkdir(dir, owner: owner, mode: mode)
60
- true
61
- end
56
+ return if File.exist?(dir) || File.symlink?(dir)
57
+
58
+ mkdir(dir, owner: owner, mode: mode)
59
+ true
62
60
  end
63
61
 
64
62
  # Runs #cp, but ONLY if +dst+ doesn't exist or differs from +src+. Returns
65
63
  # true if the file had to be copied. This is useful with verbose?, to get
66
64
  # an exact changelog.
67
65
  def cp_if_necessary(src, dst, mkdir: false, owner: nil, mode: nil)
68
- if !(File.exist?(dst) && FileUtils.compare_file(src, dst))
69
- cp(src, dst, mkdir: mkdir, owner: owner, mode: mode)
70
- true
71
- end
66
+ return if File.exist?(dst) && FileUtils.compare_file(src, dst)
67
+
68
+ cp(src, dst, mkdir: mkdir, owner: owner, mode: mode)
69
+ true
72
70
  end
73
71
 
74
72
  # Runs #ln, but ONLY if +dst+ isn't a symlink or differs from +src+.
75
73
  # Returns true if the file had to be symlinked. This is useful with
76
74
  # verbose?, to get an exact changelog.
77
75
  def ln_if_necessary(src, dst)
78
- ln = if !File.symlink?(dst)
79
- true
80
- elsif File.readlink(dst) != src
76
+ if File.symlink?(dst)
77
+ return if src == File.readlink(dst)
78
+
81
79
  rm(dst)
82
- true
83
- end
84
- if ln
85
- ln(src, dst)
86
- true
87
80
  end
81
+
82
+ ln(src, dst)
83
+ true
88
84
  end
89
85
 
90
86
  # Runs #rm, but ONLY if +file+ exists. Return true if the file had to be
91
87
  # removed. This is useful with verbose?, to get an exact changelog.
92
88
  def rm_if_necessary(file)
93
- if File.exist?(file)
94
- rm(file)
95
- true
96
- end
89
+ return if !File.exist?(file)
90
+
91
+ rm(file)
92
+ true
97
93
  end
98
94
 
99
95
  # Like chown user:user file. Like all file commands, the operation will be printed
@@ -103,23 +99,24 @@ module Scripto
103
99
  @scripto_uids ||= {}
104
100
  @scripto_uids[user] ||= Etc.getpwnam(user).uid
105
101
  uid = @scripto_uids[user]
106
- if File.stat(file).uid != uid
107
- FileUtils.chown(uid, uid, file, verbose: verbose?)
108
- end
102
+ return if File.stat(file).uid == uid
103
+
104
+ FileUtils.chown(uid, uid, file, verbose: verbose?)
109
105
  end
110
106
 
111
107
  # Like chmod mode file. Like all file commands, the operation will be
112
108
  # printed out if verbose?.
113
109
  def chmod(file, mode)
114
- if File.stat(file).mode != mode
115
- FileUtils.chmod(mode, file, verbose: verbose?)
116
- end
110
+ return if File.stat(file).mode == mode
111
+
112
+ FileUtils.chmod(mode, file, verbose: verbose?)
117
113
  end
118
114
 
119
115
  # Like rm -rf && mkdir -p. Like all file commands, the operation will be
120
116
  # printed out if verbose?.
121
117
  def rm_and_mkdir(dir)
122
- raise "don't do this" if dir == ""
118
+ raise "don't do this" if dir == ''
119
+
123
120
  FileUtils.rm_rf(dir, verbose: verbose?)
124
121
  mkdir(dir)
125
122
  end
@@ -131,5 +128,16 @@ module Scripto
131
128
  File.chmod(stat.mode, dst)
132
129
  File.utime(stat.atime, stat.mtime, dst)
133
130
  end
131
+
132
+ # Atomically write to +path+. An open temp file is yielded.
133
+ def atomic_write(path)
134
+ tmp = Tempfile.new(File.basename(path))
135
+ yield(tmp)
136
+ tmp.close
137
+ chmod(tmp.path, 0o644)
138
+ mv(tmp.path, path)
139
+ ensure
140
+ rm_if_necessary(tmp.path)
141
+ end
134
142
  end
135
- end
143
+ end