{"id":28630981,"url":"https://github.com/piotrmurach/tty-file","last_synced_at":"2025-09-21T11:39:14.681Z","repository":{"id":54663330,"uuid":"61445899","full_name":"piotrmurach/tty-file","owner":"piotrmurach","description":"File manipulation utility methods","archived":false,"fork":false,"pushed_at":"2024-02-09T22:11:32.000Z","size":236,"stargazers_count":67,"open_issues_count":5,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-02T13:00:55.336Z","etag":null,"topics":["file","file-operations","ruby","rubygem","terminal","tty","tty-components"],"latest_commit_sha":null,"homepage":"https://ttytoolkit.org","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/piotrmurach.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"piotrmurach"}},"created_at":"2016-06-18T17:54:12.000Z","updated_at":"2025-03-15T00:53:43.000Z","dependencies_parsed_at":"2024-02-09T00:45:21.341Z","dependency_job_id":"2bc0d5f1-0f4b-42e6-bc0f-01e077cad35c","html_url":"https://github.com/piotrmurach/tty-file","commit_stats":{"total_commits":285,"total_committers":5,"mean_commits":57.0,"dds":"0.028070175438596467","last_synced_commit":"b17803409039c69c19cf0e5684adf2819701ea18"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/piotrmurach/tty-file","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Ftty-file","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Ftty-file/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Ftty-file/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Ftty-file/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/piotrmurach","download_url":"https://codeload.github.com/piotrmurach/tty-file/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Ftty-file/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259470950,"owners_count":22862999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["file","file-operations","ruby","rubygem","terminal","tty","tty-components"],"created_at":"2025-06-12T13:09:31.466Z","updated_at":"2025-09-21T11:39:09.632Z","avatar_url":"https://github.com/piotrmurach.png","language":"Ruby","funding_links":["https://github.com/sponsors/piotrmurach"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://ttytoolkit.org\"\u003e\u003cimg width=\"130\" src=\"https://github.com/piotrmurach/tty/raw/master/images/tty.png\" alt=\"TTY Toolkit logo\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n# TTY::File [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]\n\n[![Gem Version](https://badge.fury.io/rb/tty-file.svg)][gem]\n[![Actions CI](https://github.com/piotrmurach/tty-file/workflows/CI/badge.svg?branch=master)][gh_actions_ci]\n[![Build status](https://ci.appveyor.com/api/projects/status/og69rn550s4mt1q3?svg=true)][appveyor]\n[![Maintainability](https://api.codeclimate.com/v1/badges/9ce2d164ea4835901ccd/maintainability)][codeclimate]\n[![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-file/badge.svg)][coverage]\n[![Inline docs](http://inch-ci.org/github/piotrmurach/tty-file.svg?branch=master)][inchpages]\n\n[gitter]: https://gitter.im/piotrmurach/tty\n[gem]: http://badge.fury.io/rb/tty-file\n[gh_actions_ci]: https://github.com/piotrmurach/tty-file/actions?query=workflow%3ACI\n[appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-file\n[codeclimate]: https://codeclimate.com/github/piotrmurach/tty-file/maintainability\n[coverage]: https://coveralls.io/github/piotrmurach/tty-file\n[inchpages]: http://inch-ci.org/github/piotrmurach/tty-file\n\n\u003e File manipulation utility methods.\n\n**TTY::File** provides independent file utilities component for [TTY](https://github.com/piotrmurach/tty) toolkit.\n\n## Motivation\n\nThough 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.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem \"tty-file\"\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install tty-file\n\n## Contents\n\n* [1. Usage](#1-usage)\n* [2. Interface](#2-interface)\n  * [2.1. binary?](#21-binary)\n  * [2.2. checksum_file](#22-checksum_file)\n  * [2.3. chmod](#23-chmod)\n  * [2.4. copy_file](#24-copy_file)\n  * [2.5. create_file](#25-create_file)\n  * [2.6. copy_dir](#26-copy_dir)\n  * [2.7. create_dir](#27-create_dir)\n  * [2.8. diff_files](#28-diff_files)\n  * [2.9. download_file](#29-download_file)\n  * [2.10. inject_into_file](#210-inject_into_file)\n  * [2.11. replace_in_file](#211-replace_in_file)\n  * [2.12. append_to_file](#212-append_to_file)\n  * [2.13. prepend_to_file](#213-prepend_to_file)\n  * [2.14. remove_file](#214-remove_file)\n  * [2.15. tail_file](#215-tail_file)\n\n## 1. Usage\n\n```ruby\nTTY::File.replace_in_file(\"Gemfile\", /gem 'rails'/, \"gem 'hanami'\")\n```\n\n## 2. Interface\n\nThe following methods are available for creating and manipulating files.\n\nIf you wish to silence verbose output use `verbose: false`. Similarly if you wish to run action without actually triggering any action use `noop: true`.\n\n### 2.1. binary?\n\nTo check whether a file is a binary file, i.e. image, executable etc. do:\n\n```ruby\nTTY::File.binary?(\"image.png\") # =\u003e true\n```\n\n### 2.2. checksum_file\n\nTo 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.\n\nAmong the supported message digest algorithms are:\n\n* `sha`, `sha1`, `sha224`, `sha256`, `sha384`, `sha512`\n* `md2`, `md4`, `md5`\n\nFor example, to create a digest for a string using `SHA1` do:\n\n```ruby\nTTY::File.checksum_file(\"Some content\\nThe end\", \"sha1\")\n# =\u003e \"289388f187404135e6c15b21460442cf867180dd\"\n```\n\n### 2.3. chmod\n\nTo change file modes use `chmod`, like so:\n\n```ruby\nTTY::File.chmod(\"filename.rb\", 0777)\n```\n\nThere 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:\n\n```ruby\nTTY::File.chmod(\"filename.rb\", TTY::File::U_R | TTY::File::O_X)\n```\n\nApart from traditional octal number definition for file permissions, you can use the more convenient permission notation used by the Unix `chmod` command:\n\n```ruby\nTTY::File.chmod(\"filename.rb\", \"u=wrx,g+x\")\n```\n\nThe `u`, `g`, and `o` specify the user, group, and other parts of the mode bits. The `a` symbol is equivalent to `ugo`.\n\n### 2.4. copy_file\n\nCopies a file's contents from a relative source to a relative destination.\n\n```ruby\nTTY::File.copy_file \"Gemfile\", \"Gemfile.bak\"\n```\n\nIf you provide a block then the file content is yielded:\n\n```ruby\nTTY::File.copy_file(\"Gemfile\", \"app/Gemfile\") do |content|\n  \"https://rubygems.org\\n\" + content\nend\n```\n\nIf 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:\n\n```ruby\nvariables = OpenStruct.new\nvariables[:foo] = \"bar\"\n\nTTY::File.copy_file(\"templates/application.html.erb\", context: variables)\n```\n\nYou can also specify the template name surrounding any dynamic variables with `%` to be evaluated:\n\n```ruby\nvariables = OpenStruct.new\nvariables[:file_name] = \"foo\"\n\nTTY::File.copy_file(\"templates/%file_name%.rb\", context: variables)\n# =\u003e Creates templates/foo.rb\n```\n\nIf the destination is a directory, then copies source inside that directory.\n\n```ruby\nTTY::File.copy_file \"docs/README.md\", \"app\"\n```\n\nIf the destination file already exists, a prompt menu will be displayed to enquire about action:\n\nIf you wish to preserve original owner, group, permission and modified time use `:preserve` option:\n\n```ruby\nTTY::File.copy_file \"docs/README.md\", \"app\", preserve: true\n```\n\n### 2.5. create_file\n\nTo create a file at a given destination with some content use `create_file`:\n\n```ruby\nTTY::File.create_file \"file-a/README.md\", content\n```\n\nOn collision with already existing file, a menu gets displayed:\n\n```\n   collision  examples/file-a\nOverwrite examples/file-a? (enter \"h\" for help) [y,d,n,q,h]\n```\n\nThe `d` option allows to compare the changes:\n\n```\n--- a/examples/file-a\n+++ b/examples/file-a\n@@ -1,8 +1,9 @@\n aaaaa\n bbbbb\n-ccccc\n+xxxxx\n+\n ddddd\n eeeee\n fffff\n-ggggg\n+yyyyy\nOverwrite examples/file-a? (enter \"h\" for help) [y,d,n,q,h]\n````\n\nYou can force to always overwrite file with `:force` option or always skip by providing `:skip`.\n\nThere is [examples/overwrite.rb](examples/overwrite.rb) that demonstrates diffing file with new content.\n\n### 2.6. copy_dir\n\nTo recursively copy a directory of files from source to destination location use `copy_directory` or its alias `copy_dir`.\n\nAssuming you have the following directory structure:\n\n```ruby\n# doc/\n#   subcommands/\n#     command.rb.erb\n#   README.md\n#   %name%.rb\n```\n\nYou can copy `doc` folder to `docs` by invoking:\n\n```ruby\nTTY::File.copy_directory(\"doc\", \"docs\", context: ...)\n```\n\nThe `context` needs to respond to `name` message and given it returns `foo` value the following directory gets created:\n\n```ruby\n# docs/\n#   subcommands/\n#     command.rb\n#   README.md\n#   foo.rb\n```\n\nIf you only need to copy top level files use option `recursive: false`:\n\n```ruby\nTTY::File.copy_directory(\"doc\", \"docs\", recursive: false)\n```\n\nBy passing `:exclude` option you can instruct the method to ignore any files including the given pattern:\n\n```ruby\nTTY::File.copy_directory(\"doc\", \"docs\", exclude: \"subcommands\")\n```\n\n### 2.7. create_dir\n\nTo create directory use `create_directory` or its alias `create_dir` passing as a first argument file path:\n\n```ruby\nTTY::File.create_dir(\"/path/to/directory\")\n```\n\nOr a data structure describing the directory tree including any files with or without content:\n\n```ruby\ntree =\n  \"app\" =\u003e [\n    \"README.md\",\n    [\"Gemfile\", \"gem 'tty-file'\"],\n    \"lib\" =\u003e [\n      \"cli.rb\",\n      [\"file_utils.rb\", \"require 'tty-file'\"]\n    ]\n    \"spec\" =\u003e []\n  ]\n```\n\n```ruby\nTTY::File.create_dir(tree)\n# =\u003e\n# app\n# app/README.md\n# app/Gemfile\n# app/lib\n# app/lib/cli.rb\n# app/lib/file_utils.rb\n# app/spec\n```\n\nAs a second argument you can provide a parent directory, otherwise current directory will be assumed:\n\n```ruby\nTTY::File.create_dir(tree, \"/path/to/parent/dir\")\n```\n\n### 2.8. diff_files\n\nTo compare files line by line in a system independent way use `diff`, or `diff_files`:\n\n```ruby\nprint TTY::File.diff_files(\"file-a\", \"file-b\")\n```\n\nPrinting output to console would result in:\n\n```\n        diff  examples/file-a and examples/file-b\n--- examples/file-a\n+++ examples/file-b\n@@ -1,8 +1,9 @@\n aaaaa\n bbbbb\n-ccccc\n+xxxxx\n+\n ddddd\n eeeee\n fffff\n-ggggg\n+yyyyy\n```\n\nYou can also pass additional parameters such as:\n\n* `:format` - accepted values are `:unified`, `:old`, `:context` and `:ed`. Defaults to `:unified` as seen in the output above - similar to git tool.\n* `:lines` - how many extra lines to include in the output around the compared lines. Defaults to `3` lines.\n* `:threshold` - set maximum file size in bytes. By default files larger than `10Mb` are no processed.\n* `:header` - controls display of two-line files comparison. By default `true`.\n\nChanging format to `:old`, removing context lines and skipping log output:\n\n```ruby\nTTY::File.diff_files(\"file_a\", \"file_b\", format: :old, lines: 0, verbose: false)\n```\n\nResults in the following output:\n\n```\n\u003c\u003c\u003c examples/file-a\n\u003e\u003e\u003e examples/file-b\n3c3,4\n\u003c ccccc\n---\n\u003e xxxxx\n\u003e\n\n7c8\n\u003c ggggg\n---\n\u003e yyyyy\n```\n\nIn addition, you can perform a comparison between a file and a string or between two strings. For example, comparing file with content:\n\n```ruby\nTTY::File.diff_files(\"file-a\", \"new\\nlong\\ntext\")\n```\n\nWill output:\n\n```\n        diff  a/examples/file-a and b/examples/file-a\n--- a/examples/file-a\n+++ b/examples/file-a\n@@ -1,8 +1,4 @@\n-aaaaa\n-bbbbb\n-ccccc\n-ddddd\n-eeeee\n-fffff\n-ggggg\n+new\n+long\n+text\n````\n\nPlease run [examples/diff.rb](examples/diff.rb) to see how output works.\n\n### 2.9. download_file\n\nTo download a content from a given address and to save at a given relative location do:\n\n```ruby\nTTY::File.download_file(\"https://gist.github.com/4701967\", \"doc/README.md\")\n```\n\nIf you pass a block then the content will be yielded to allow modification:\n\n```ruby\nTTY::File.download_file(\"https://gist.github.com/4701967\", \"doc/README.md\") do |content|\n  content.gsub(\"\\n\", \" \")\nend\n```\n\nBy default `download_file` will follow maximum 3 redirects. This can be changed by passing `:limit` option:\n\n```ruby\nTTY::File.download_file(\"https://gist.github.com/4701967\", \"doc/README.md\", limit: 5)\n# =\u003e raises TTY::File::DownloadError\n```\n\n### 2.10. inject_into_file\n\nInject content into a file at a given location and return `true` when performed successfully, `false` otherwise.\n\n```ruby\nTTY::File.inject_into_file \"filename.rb\", \"text to add\", after: \"Code below this line\\n\"\n```\n\nOr using a block:\n\n```ruby\nTTY::File.inject_into_file \"filename.rb\", after: \"Code below this line\\n\" do\n  \"text to add\"\nend\n```\n\nYou can also use Regular Expressions in `:after` or `:before` to match file location.\n\nBy 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:\n\n```ruby\nTTY::File.inject_into_file(\"filename.rb\", \"text to add\", after: \"Code below this line\\n\"\n```\n\nAlternatively, use `safe_inject_into_file` to check if the text can be safely inserted.\n\n```ruby\nTTY::File.safe_inject_into_file(\"Gemfile\", \"gem 'tty'\")\n```\n\nThe [append_to_file](#212-append_to_file) and [prepend_to_file](#213-prepend_to_file) allow you to add content at the end and the begging of a file.\n\n### 2.11. replace_in_file\n\nReplace content of a file matching condition by calling `replace_in_file` or `gsub_file`, which returns `true` when substitutions are performed successfully, `false` otherwise.\n\n```ruby\nTTY::File.replace_in_file \"filename.rb\", /matching condition/, \"replacement\"\n```\n\nThe replacement content can be provided in a block\n\n```ruby\nTTY::File.gsub_file \"filename.rb\", /matching condition/ do\n  \"replacement\"\nend\n```\n\n### 2.12. append_to_file\n\nAppends text to a file and returns `true` when performed successfully, `false` otherwise. You can provide the text as a second argument:\n\n```ruby\nTTY::File.append_to_file(\"Gemfile\", \"gem 'tty'\")\n```\n\nOr inside a block:\n\n```ruby\nTTY::File.append_to_file(\"Gemfile\") do\n  \"gem 'tty'\"\nend\n```\n\nBy 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:\n\n```ruby\nTTY::File.append_to_file(\"Gemfile\", \"gem 'tty'\", force: false)\n```\n\nAlternatively, use `safe_append_to_file` to check if the text can be safely appended.\n\n```ruby\nTTY::File.safe_append_to_file(\"Gemfile\", \"gem 'tty'\")\n```\n\n### 2.13. prepend_to_file\n\nPrepends text to a file and returns `true` when performed successfully, `false` otherwise. You can provide the text as a second argument:\n\n```ruby\nTTY::File.prepend_to_file(\"Gemfile\", \"gem 'tty'\")\n```\n\nOr inside a block:\n\n```ruby\nTTY::File.prepend_to_file(\"Gemfile\") do\n  \"gem 'tty'\"\nend\n```\n\nBy 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:\n\n```ruby\nTTY::File.prepend_to_file(\"Gemfile\", \"gem 'tty'\", force: false)\n```\n\nAlternatively, use `safe_prepend_to_file` to check if the text can be safely appended.\n\n```ruby\nTTY::File.safe_prepend_to_file(\"Gemfile\", \"gem 'tty'\")\n```\n\n### 2.14. remove_file\n\nTo remove a file do:\n\n```ruby\nTTY::File.remove_file \"doc/README.md\"\n```\n\nYou can also pass in `:force` to remove file ignoring any errors:\n\n```ruby\nTTY::File.remove_file \"doc/README.md\", force: true\n```\n\n### 2.15. tail_file\n\nTo read the last 10 lines from a file do:\n\n```ruby\nTTY::File.tail_file \"doc/README.md\"\n# =\u003e [\"## Copyright\", \"Copyright (c) 2016-2017\", ...]\n```\n\nYou can also pass a block:\n\n```ruby\nTTY::File.tail_file(\"doc/README.md\") do |line|\n  puts line\nend\n```\n\nTo change how many lines are read pass a second argument:\n\n```ruby\nTTY::File.tail_file(\"doc/README.md\", 15)\n```\n\n## Development\n\nAfter 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.\n\nTo 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](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://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](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n## Copyright\n\nCopyright (c) 2016 Piotr Murach. See LICENSE for further details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrmurach%2Ftty-file","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpiotrmurach%2Ftty-file","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrmurach%2Ftty-file/lists"}