TTY::File
¶ ↑
File manipulation utility methods
Motivation¶ ↑
Though Ruby's File
and FileUtils
libraries provide very robust apis for dealing with files, this library aims to provide a level of abstraction that is much more convenient, with useful logging capabilities.
Installation¶ ↑
Add this line to your application's Gemfile:
gem "tty-file"
And then execute:
$ bundle
Or install it yourself as:
$ gem install tty-file
Contents¶ ↑
1. Usage¶ ↑
TTY::File.replace_in_file("Gemfile", /gem 'rails'/, "gem 'hanami'")
2. Interface¶ ↑
The following methods are available for creating and manipulating files.
If you wish to silence verbose output use verbose: false
. Similarly if you wish to run action without actually triggering any action use noop: true
.
2.1. binary?¶ ↑
To check whether a file is a binary file, i.e. image, executable etc. do:
TTY::File.binary?("image.png") # => true
2.2. checksum_file¶ ↑
To generate a checksum for a file, IO object, or String, use checksum_file
. By default the MD5
algorithm is used, which can be changed by passing a second argument.
Among the supported message digest algorithms are:
-
sha
,sha1
,sha224
,sha256
,sha384
,sha512
-
md2
,md4
,md5
For example, to create a digest for a string using SHA1
do:
TTY::File.checksum_file("Some content\nThe end", "sha1") # => "289388f187404135e6c15b21460442cf867180dd"
2.3. chmod¶ ↑
To change file modes use chmod
, like so:
TTY::File.chmod("filename.rb", 0777)
There are a number of constants available to represent common mode bits such as TTY::File::U_R
and TTY::File::O_X
, and they can be used as follows:
TTY::File.chmod("filename.rb", TTY::File::U_R | TTY::File::O_X)
Apart from traditional octal number definition for file permissions, you can use the more convenient permission notation used by the Unix chmod
command:
TTY::File.chmod("filename.rb", "u=wrx,g+x")
The u
, g
, and o
specify the user, group, and other parts of the mode bits. The a
symbol is equivalent to ugo
.
2.4. copy_file¶ ↑
Copies a file's contents from a relative source to a relative destination.
TTY::File.copy_file "Gemfile", "Gemfile.bak"
If you provide a block then the file content is yielded:
TTY::File.copy_file("Gemfile", "app/Gemfile") do |content| "https://rubygems.org\n" + content end
If the source file is an ERB
template then you can provide a :context
in which the file gets evaluated, or if TTY::File
gets included as a module then appropriate object context will be used by default. To use :context
do:
variables = OpenStruct.new variables[:foo] = "bar" TTY::File.copy_file("templates/application.html.erb", context: variables)
You can also specify the template name surrounding any dynamic variables with %
to be evaluated:
variables = OpenStruct.new variables[:file_name] = "foo" TTY::File.copy_file("templates/%file_name%.rb", context: variables) # => Creates templates/foo.rb
If the destination is a directory, then copies source inside that directory.
TTY::File.copy_file "docs/README.md", "app"
If the destination file already exists, a prompt menu will be displayed to enquire about action:
If you wish to preserve original owner, group, permission and modified time use :preserve
option:
TTY::File.copy_file "docs/README.md", "app", preserve: true
2.5. create_file¶ ↑
To create a file at a given destination with some content use create_file
:
TTY::File.create_file "file-a/README.md", content
On collision with already existing file, a menu gets displayed:
collision examples/file-a Overwrite examples/file-a? (enter "h" for help) [y,d,n,q,h]
The d
option allows to compare the changes:
--- a/examples/file-a +++ b/examples/file-a @@ -1,8 +1,9 @@ aaaaa bbbbb -ccccc +xxxxx + ddddd eeeee fffff -ggggg +yyyyy Overwrite examples/file-a? (enter "h" for help) [y,d,n,q,h] ```` You can force to always overwrite file with `:force` option or always skip by providing `:skip`. There is [examples/overwrite.rb](examples/overwrite.rb) that demonstrates diffing file with new content. ### 2.6. copy_dir To recursively copy a directory of files from source to destination location use `copy_directory` or its alias `copy_dir`. Assuming you have the following directory structure:
ruby
doc/¶ ↑
subcommands/¶ ↑
command.rb.erb¶ ↑
README.md¶ ↑
%name%.rb¶ ↑
You can copy `doc` folder to `docs` by invoking:
ruby TTY::File.copy_directory
(“doc”, “docs”, context: …)
The `context` needs to respond to `name` message and given it returns `foo` value the following directory gets created:
ruby
docs/¶ ↑
subcommands/¶ ↑
command.rb¶ ↑
README.md¶ ↑
foo.rb¶ ↑
If you only need to copy top level files use option `recursive: false`:
ruby TTY::File.copy_directory
(“doc”, “docs”, recursive: false)
By passing `:exclude` option you can instruct the method to ignore any files including the given pattern:
ruby TTY::File.copy_directory
(“doc”, “docs”, exclude: “subcommands”)
### 2.7. create_dir To create directory use `create_directory` or its alias `create_dir` passing as a first argument file path:
ruby TTY::File.create_dir
(“/path/to/directory”)
Or a data structure describing the directory tree including any files with or without content:
ruby tree = “app” => [ “README.md”, [“Gemfile”, “gem 'tty-file'”], “lib” => [ “cli.rb”, [“file_utils.rb”, “require 'tty-file'”] ] “spec” => [] ]
ruby TTY::File.create_dir(tree)
=>¶ ↑
app¶ ↑
app/README.md¶ ↑
app/Gemfile¶ ↑
app/lib¶ ↑
app/lib/cli.rb¶ ↑
app/lib/file_utils.rb¶ ↑
app/spec¶ ↑
As a second argument you can provide a parent directory, otherwise current directory will be assumed:
ruby TTY::File.create_dir
(tree, “/path/to/parent/dir”)
### 2.8. diff_files To compare files line by line in a system independent way use `diff`, or `diff_files`:
ruby print TTY::File.diff_files
(“file-a”, “file-b”)
Printing output to console would result in:
diff examples/file-a and examples/file-b
— examples/file-a +++ examples/file-b @@ -1,8 +1,9 @@ aaaaa bbbbb -ccccc +xxxxx + ddddd eeeee fffff -ggggg +yyyyy
You can also pass additional parameters such as: * `:format` - accepted values are `:unified`, `:old`, `:context` and `:ed`. Defaults to `:unified` as seen in the output above - similar to git tool. * `:lines` - how many extra lines to include in the output around the compared lines. Defaults to `3` lines. * `:threshold` - set maximum file size in bytes. By default files larger than `10Mb` are no processed. * `:header` - controls display of two-line files comparison. By default `true`. Changing format to `:old`, removing context lines and skipping log output:
ruby TTY::File.diff_files
(“file_a”, “file_b”, format: :old, lines: 0, verbose: false)
Results in the following output:
<<< examples/file-a
examples/file-b 3c3,4
< ccccc¶ ↑
xxxxx
7c8
< ggggg — > yyyyy
In addition, you can perform a comparison between a file and a string or between two strings. For example, comparing file with content:
ruby TTY::File.diff_files
(“file-a”, “newnlongntext”)
Will output:
diff a/examples/file-a and b/examples/file-a
— a/examples/file-a +++ b/examples/file-a @@ -1,8 +1,4 @@ -aaaaa -bbbbb -ccccc -ddddd -eeeee -fffff -ggggg +new +long +text ““
Please run examples/diff.rb to see how output works.
2.9. download_file¶ ↑
To download a content from a given address and to save at a given relative location do:
TTY::File.download_file("https://gist.github.com/4701967", "doc/README.md")
If you pass a block then the content will be yielded to allow modification:
TTY::File.download_file("https://gist.github.com/4701967", "doc/README.md") do |content| content.gsub("\n", " ") end
By default download_file
will follow maximum 3 redirects. This can be changed by passing :limit
option:
TTY::File.download_file("https://gist.github.com/4701967", "doc/README.md", limit: 5) # => raises TTY::File::DownloadError
2.10. inject_into_file¶ ↑
Inject content into a file at a given location and return true
when performed successfully, false
otherwise.
TTY::File.inject_into_file "filename.rb", "text to add", after: "Code below this line\n"
Or using a block:
TTY::File.inject_into_file "filename.rb", after: "Code below this line\n" do "text to add" end
You can also use Regular Expressions in :after
or :before
to match file location.
By default, this method will always inject content into file, regardless whether it is already present or not. To change this pass :force
set to false
to perform check before actually inserting text:
TTY::File.inject_into_file("filename.rb", "text to add", after: "Code below this line\n"
Alternatively, use safe_inject_into_file
to check if the text can be safely inserted.
TTY::File.safe_inject_into_file("Gemfile", "gem 'tty'")
The append_to_file and prepend_to_file allow you to add content at the end and the begging of a file.
2.11. replace_in_file¶ ↑
Replace content of a file matching condition by calling replace_in_file
or gsub_file
, which returns true
when substitutions are performed successfully, false
otherwise.
TTY::File.replace_in_file "filename.rb", /matching condition/, "replacement"
The replacement content can be provided in a block
TTY::File.gsub_file "filename.rb", /matching condition/ do "replacement" end
2.12. append_to_file¶ ↑
Appends text to a file and returns true
when performed successfully, false
otherwise. You can provide the text as a second argument:
TTY::File.append_to_file("Gemfile", "gem 'tty'")
Or inside a block:
TTY::File.append_to_file("Gemfile") do "gem 'tty'" end
By default, this method will always append content regardless whether it is already present or not. To change this pass :force
set to false
to perform check before actually appending:
TTY::File.append_to_file("Gemfile", "gem 'tty'", force: false)
Alternatively, use safe_append_to_file
to check if the text can be safely appended.
TTY::File.safe_append_to_file("Gemfile", "gem 'tty'")
2.13. prepend_to_file¶ ↑
Prepends text to a file and returns true
when performed successfully, false
otherwise. You can provide the text as a second argument:
TTY::File.prepend_to_file("Gemfile", "gem 'tty'")
Or inside a block:
TTY::File.prepend_to_file("Gemfile") do "gem 'tty'" end
By default, this method will always prepend content regardless whether it is already present or not. To change this pass :force
set to false
to perform check before actually prepending:
TTY::File.prepend_to_file("Gemfile", "gem 'tty'", force: false)
Alternatively, use safe_prepend_to_file
to check if the text can be safely appended.
TTY::File.safe_prepend_to_file("Gemfile", "gem 'tty'")
2.14. remove_file¶ ↑
To remove a file do:
TTY::File.remove_file "doc/README.md"
You can also pass in :force
to remove file ignoring any errors:
TTY::File.remove_file "doc/README.md", force: true
2.15. tail_file¶ ↑
To read the last 10 lines from a file do:
TTY::File.tail_file "doc/README.md" # => ["## Copyright", "Copyright (c) 2016-2017", ...]
You can also pass a block:
TTY::File.tail_file("doc/README.md") do |line| puts line end
To change how many lines are read pass a second argument:
TTY::File.tail_file("doc/README.md", 15)
Development¶ ↑
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing¶ ↑
Bug reports and pull requests are welcome on GitHub at github.com/piotrmurach/tty-file. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License¶ ↑
The gem is available as open source under the terms of the MIT License.
Copyright¶ ↑
Copyright © 2016 Piotr Murach. See LICENSE for further details.