philiprehberger-pathname_kit 0.1.0
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/CHANGELOG.md +20 -0
- data/LICENSE +21 -0
- data/README.md +91 -0
- data/lib/philiprehberger/pathname_kit/version.rb +7 -0
- data/lib/philiprehberger/pathname_kit.rb +119 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3d307f3b4309bc786d63332bc60c097aae61cfed960a3a00d0a354435426fdd1
|
|
4
|
+
data.tar.gz: 2a650ca32af727f254b2a8b0bb44016067e10f975f4510098e713c55299e4c13
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bbd7f36bbf1efc5c9e76a5429d448ad4b132ffe93d7bd727bf74fb9f92f8a0c88ea8513a6b2a41c7f0ed2fc257b419c935ab6670d3c14df3e014bf8dc69a2c69
|
|
7
|
+
data.tar.gz: 8b6909b94b63402b60255479bf51aa1e2d0acfaf0f57548f778fb6c408832593fdc9b13049447b9ed2b0cce23535a7b0f91fa35ee4f7a4bc9777cc132be34d4d
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this gem will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-03-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- Atomic file writes with temp file and rename
|
|
15
|
+
- Directory creation with ensure_directory
|
|
16
|
+
- Safe file deletion that returns success/failure
|
|
17
|
+
- Glob-based file finding
|
|
18
|
+
- Tempfile helper with automatic cleanup
|
|
19
|
+
- File touch with parent directory creation
|
|
20
|
+
- Line counting for files
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 philiprehberger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# philiprehberger-pathname_kit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/philiprehberger/rb-pathname-kit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/philiprehberger-pathname_kit)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Pathname extensions for atomic write, safe delete, and common file operations
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Ruby >= 3.1
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'philiprehberger-pathname_kit'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install directly:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gem install philiprehberger-pathname_kit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require 'philiprehberger/pathname_kit'
|
|
31
|
+
|
|
32
|
+
Philiprehberger::PathnameKit.atomic_write('config.yml') do |f|
|
|
33
|
+
f.write('key: value')
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Atomic Write
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
Philiprehberger::PathnameKit.atomic_write('/path/to/file.txt') do |f|
|
|
41
|
+
f.write('safe content')
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Directory and File Operations
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
Philiprehberger::PathnameKit.ensure_directory('/path/to/nested/dir')
|
|
49
|
+
Philiprehberger::PathnameKit.touch('/path/to/file.txt')
|
|
50
|
+
Philiprehberger::PathnameKit.safe_delete('/path/to/old.txt') # => true or false
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Finding and Counting
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
files = Philiprehberger::PathnameKit.find('/src/**/*.rb')
|
|
57
|
+
count = Philiprehberger::PathnameKit.line_count('/path/to/file.rb')
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Tempfile Helper
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
Philiprehberger::PathnameKit.tempfile('.csv') do |path|
|
|
64
|
+
File.write(path, 'a,b,c')
|
|
65
|
+
# temp file is cleaned up automatically
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## API
|
|
70
|
+
|
|
71
|
+
| Method | Description |
|
|
72
|
+
|--------|-------------|
|
|
73
|
+
| `.atomic_write(path) { \|f\| }` | Write to a temp file then rename atomically |
|
|
74
|
+
| `.ensure_directory(path)` | Create directory and all parents if needed |
|
|
75
|
+
| `.safe_delete(path)` | Delete a file, returning true if deleted |
|
|
76
|
+
| `.find(glob)` | Find files matching a glob pattern |
|
|
77
|
+
| `.tempfile(ext) { \|path\| }` | Create a temp file and yield its path |
|
|
78
|
+
| `.touch(path)` | Create or update a file's modification time |
|
|
79
|
+
| `.line_count(path)` | Count the number of lines in a file |
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bundle install
|
|
85
|
+
bundle exec rspec # Run tests
|
|
86
|
+
bundle exec rubocop # Check code style
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'pathname_kit/version'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
require 'pathname'
|
|
7
|
+
|
|
8
|
+
module Philiprehberger
|
|
9
|
+
module PathnameKit
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
# Write content atomically by writing to a temp file then renaming.
|
|
13
|
+
#
|
|
14
|
+
# @param path [String] the file path to write to
|
|
15
|
+
# @yield [IO] the temporary file to write to
|
|
16
|
+
# @return [void]
|
|
17
|
+
# @raise [Error] if path is nil or empty
|
|
18
|
+
def self.atomic_write(path)
|
|
19
|
+
raise Error, 'path cannot be nil' if path.nil?
|
|
20
|
+
raise Error, 'path cannot be empty' if path.to_s.empty?
|
|
21
|
+
|
|
22
|
+
dir = File.dirname(path)
|
|
23
|
+
ensure_directory(dir)
|
|
24
|
+
|
|
25
|
+
temp = Tempfile.new(['atomic', File.extname(path)], dir)
|
|
26
|
+
begin
|
|
27
|
+
yield temp
|
|
28
|
+
temp.close
|
|
29
|
+
FileUtils.mv(temp.path, path)
|
|
30
|
+
rescue StandardError
|
|
31
|
+
temp.close
|
|
32
|
+
temp.unlink
|
|
33
|
+
raise
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Ensure a directory exists, creating it and all parents if needed.
|
|
38
|
+
#
|
|
39
|
+
# @param path [String] the directory path
|
|
40
|
+
# @return [void]
|
|
41
|
+
# @raise [Error] if path is nil or empty
|
|
42
|
+
def self.ensure_directory(path)
|
|
43
|
+
raise Error, 'path cannot be nil' if path.nil?
|
|
44
|
+
raise Error, 'path cannot be empty' if path.to_s.empty?
|
|
45
|
+
|
|
46
|
+
FileUtils.mkdir_p(path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Safely delete a file, returning true if deleted and false if not found.
|
|
50
|
+
#
|
|
51
|
+
# @param path [String] the file path to delete
|
|
52
|
+
# @return [Boolean] true if the file was deleted
|
|
53
|
+
# @raise [Error] if path is nil or empty
|
|
54
|
+
def self.safe_delete(path)
|
|
55
|
+
raise Error, 'path cannot be nil' if path.nil?
|
|
56
|
+
raise Error, 'path cannot be empty' if path.to_s.empty?
|
|
57
|
+
|
|
58
|
+
return false unless File.exist?(path)
|
|
59
|
+
|
|
60
|
+
File.delete(path)
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Find files matching a glob pattern.
|
|
65
|
+
#
|
|
66
|
+
# @param glob [String] the glob pattern
|
|
67
|
+
# @return [Array<String>] matching file paths
|
|
68
|
+
# @raise [Error] if glob is nil or empty
|
|
69
|
+
def self.find(glob)
|
|
70
|
+
raise Error, 'glob cannot be nil' if glob.nil?
|
|
71
|
+
raise Error, 'glob cannot be empty' if glob.to_s.empty?
|
|
72
|
+
|
|
73
|
+
Dir.glob(glob).sort
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Create a temporary file with a given extension and yield its path.
|
|
77
|
+
#
|
|
78
|
+
# @param ext [String] the file extension (e.g. '.txt')
|
|
79
|
+
# @yield [String] the temporary file path
|
|
80
|
+
# @return [Object] the block return value
|
|
81
|
+
def self.tempfile(ext = '.tmp')
|
|
82
|
+
temp = Tempfile.new(['tmp', ext])
|
|
83
|
+
begin
|
|
84
|
+
yield temp.path
|
|
85
|
+
ensure
|
|
86
|
+
temp.close
|
|
87
|
+
temp.unlink
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Touch a file, creating it if it does not exist and updating its mtime.
|
|
92
|
+
#
|
|
93
|
+
# @param path [String] the file path
|
|
94
|
+
# @return [void]
|
|
95
|
+
# @raise [Error] if path is nil or empty
|
|
96
|
+
def self.touch(path)
|
|
97
|
+
raise Error, 'path cannot be nil' if path.nil?
|
|
98
|
+
raise Error, 'path cannot be empty' if path.to_s.empty?
|
|
99
|
+
|
|
100
|
+
dir = File.dirname(path)
|
|
101
|
+
ensure_directory(dir)
|
|
102
|
+
FileUtils.touch(path)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Count the number of lines in a file.
|
|
106
|
+
#
|
|
107
|
+
# @param path [String] the file path
|
|
108
|
+
# @return [Integer] the number of lines
|
|
109
|
+
# @raise [Error] if path is nil or empty
|
|
110
|
+
# @raise [Error] if the file does not exist
|
|
111
|
+
def self.line_count(path)
|
|
112
|
+
raise Error, 'path cannot be nil' if path.nil?
|
|
113
|
+
raise Error, 'path cannot be empty' if path.to_s.empty?
|
|
114
|
+
raise Error, "file not found: #{path}" unless File.exist?(path)
|
|
115
|
+
|
|
116
|
+
File.readlines(path).size
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: philiprehberger-pathname_kit
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Philip Rehberger
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Pathname utility library providing atomic writes, safe deletes, directory
|
|
14
|
+
creation, glob-based file finding, tempfile helpers, touch, and line counting. All
|
|
15
|
+
operations handle edge cases and cleanup gracefully.
|
|
16
|
+
email:
|
|
17
|
+
- me@philiprehberger.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
25
|
+
- lib/philiprehberger/pathname_kit.rb
|
|
26
|
+
- lib/philiprehberger/pathname_kit/version.rb
|
|
27
|
+
homepage: https://github.com/philiprehberger/rb-pathname-kit
|
|
28
|
+
licenses:
|
|
29
|
+
- MIT
|
|
30
|
+
metadata:
|
|
31
|
+
homepage_uri: https://github.com/philiprehberger/rb-pathname-kit
|
|
32
|
+
source_code_uri: https://github.com/philiprehberger/rb-pathname-kit
|
|
33
|
+
changelog_uri: https://github.com/philiprehberger/rb-pathname-kit/blob/main/CHANGELOG.md
|
|
34
|
+
bug_tracker_uri: https://github.com/philiprehberger/rb-pathname-kit/issues
|
|
35
|
+
rubygems_mfa_required: 'true'
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: 3.1.0
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.5.22
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: Pathname extensions for atomic write, safe delete, and common file operations
|
|
55
|
+
test_files: []
|