dirfy 0.1.4
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/README.md +131 -0
- data/Rakefile +6 -0
- data/bin/dirfy +4 -0
- data/lib/dirfy/cli.rb +64 -0
- data/lib/dirfy/io.rb +96 -0
- data/lib/dirfy/parser.rb +44 -0
- data/lib/dirfy/version.rb +4 -0
- data/lib/dirfy.rb +5 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 79899b98c10fa13d4c3d6c48a83efd80f3a7d9d7e3c62f58faaa0b8c39a6561f
|
4
|
+
data.tar.gz: d32b90c0829e778539896e4a4386d6a38db3dee915e42e12481b7cc72fa915c9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c956b986856643b569417ff8a355973fc9878757545feef593dd6daab1557c6a60d331bd321685be77e032b97d3ea58b3c8ab56c6eaaa718b5e74b3ce17ecb90
|
7
|
+
data.tar.gz: 79f20f7434fff68854d8ba31774d5976a2e7546866dbdc2240db5ff16df2e6f95c5b9f06b6cb6ab191749e96796f6109fa249f40c59ea7aac70fe84505886544
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Dirfy
|
2
|
+
|
3
|
+
[](https://github.com/ahmedmelhady7/dirfy/actions)
|
4
|
+
[](https://badge.fury.io/rb/dirfy)
|
5
|
+
[](https://github.com/ahmedmelhady7/dirfy/blob/main/LICENSE)
|
6
|
+
[](https://www.ruby-lang.org/)
|
7
|
+
[](https://github.com/ahmedmelhady7/dirfy/issues)
|
8
|
+
|
9
|
+
> **Instantly scaffold directory & file structures from any ASCII/Unicode βtreeβ diagram** π
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
## π Problem Statement
|
14
|
+
|
15
|
+
Modern AI code generators (ChatGPT, Copilot, etc.) excel at producing code snippetsβbut often describe project layouts as ASCII or Unicode βtreeβ diagrams. Manually translating those diagrams into a real folder/file structure is:
|
16
|
+
|
17
|
+
- **Time-consuming & error-prone** when projects are large or deeply nested
|
18
|
+
- **Tedious** to type out dozens or hundreds of `mkdir -p` / `touch` commands
|
19
|
+
- **Prone to typos** and forgotten directories
|
20
|
+
|
21
|
+
**Dirfy** solves this by automating the entire process: feed it any `tree` diagram (text or file), and it will parse and create the exact structureβno matter how bigβwithin seconds.
|
22
|
+
|
23
|
+
---
|
24
|
+
|
25
|
+
## β¨ Features
|
26
|
+
|
27
|
+
- **Parse ASCII & Unicode** tree diagrams
|
28
|
+
- **Dry-run mode** (`-d`) to preview changes without writing
|
29
|
+
- **Verbose logging** (`-v`) for full action reports
|
30
|
+
- **Custom indent** support (`-i N`) for non-standard tree outputs
|
31
|
+
- **Path prefixing** (`-p DIR/`) to scaffold under any base folder
|
32
|
+
- **Live progress bar** and clear success/failure summary
|
33
|
+
- **Zero external dependencies** (pure Ruby β₯2.6)
|
34
|
+
|
35
|
+
---
|
36
|
+
|
37
|
+
## π Installation
|
38
|
+
|
39
|
+
```bash
|
40
|
+
# via RubyGems
|
41
|
+
gem install dirfy
|
42
|
+
|
43
|
+
# or from source
|
44
|
+
git clone https://github.com/ahmedmelhady7/dirfy.git
|
45
|
+
cd dirfy
|
46
|
+
bundle install
|
47
|
+
rake install
|
48
|
+
````
|
49
|
+
|
50
|
+
---
|
51
|
+
|
52
|
+
## π‘ Usage
|
53
|
+
|
54
|
+
### From a file
|
55
|
+
|
56
|
+
```bash
|
57
|
+
dirfy path/to/tree.txt
|
58
|
+
```
|
59
|
+
|
60
|
+
### Via pipe
|
61
|
+
|
62
|
+
```bash
|
63
|
+
cat tree.txt | dirfy
|
64
|
+
```
|
65
|
+
|
66
|
+
### Common Options
|
67
|
+
|
68
|
+
| Flag | Description |
|
69
|
+
| -------------------------- | -------------------------------------------------- |
|
70
|
+
| `-d`, `--dry-run` | Preview actions without creating files/directories |
|
71
|
+
| `-v`, `--verbose` | Show each create/skip/fail action |
|
72
|
+
| `-i N`, `--indent=N` | Set spaces per tree level (default: `4`) |
|
73
|
+
| `-p DIR/`, `--prefix=DIR/` | Prepend `DIR/` to every generated path |
|
74
|
+
| `-h`, `--help` | Display help and exit |
|
75
|
+
|
76
|
+
---
|
77
|
+
|
78
|
+
## π οΈ Examples
|
79
|
+
|
80
|
+
Given a file `myapp_tree.txt`:
|
81
|
+
|
82
|
+
```text
|
83
|
+
my_app/
|
84
|
+
βββ lib/
|
85
|
+
β βββ my_app.rb
|
86
|
+
βββ spec/
|
87
|
+
β βββ my_app_spec.rb
|
88
|
+
βββ README.md
|
89
|
+
```
|
90
|
+
|
91
|
+
Run:
|
92
|
+
|
93
|
+
```bash
|
94
|
+
dirfy -d -v myapp_tree.txt
|
95
|
+
```
|
96
|
+
|
97
|
+
Output (dry-run):
|
98
|
+
|
99
|
+
```
|
100
|
+
π Detected 4 items to create.
|
101
|
+
DRY-RUN Dir: my_app/
|
102
|
+
DRY-RUN Dir: my_app/lib/
|
103
|
+
DRY-RUN File: my_app/lib/my_app.rb
|
104
|
+
DRY-RUN Dir: my_app/spec/
|
105
|
+
DRY-RUN File: my_app/spec/my_app_spec.rb
|
106
|
+
DRY-RUN File: my_app/README.md
|
107
|
+
```
|
108
|
+
|
109
|
+
Remove `-d` to actually scaffold.
|
110
|
+
|
111
|
+
---
|
112
|
+
|
113
|
+
## π Contributing
|
114
|
+
|
115
|
+
1. Fork this repo
|
116
|
+
2. Create a feature branch (`git checkout -b feature/your-feature`)
|
117
|
+
3. Commit your changes (`git commit -m "Add feature"`)
|
118
|
+
4. Run tests (`bundle exec rake spec`)
|
119
|
+
5. Submit a pull request
|
120
|
+
|
121
|
+
Please check [CONTRIBUTING.md](CONTRIBUTING.md) for full guidelines.
|
122
|
+
|
123
|
+
---
|
124
|
+
|
125
|
+
## π License
|
126
|
+
|
127
|
+
This project is released under the **MIT License**. See [LICENSE](LICENSE) for details.
|
128
|
+
|
129
|
+
---
|
130
|
+
|
131
|
+
> Built with β€οΈ to make AI-generated project scaffolding a breeze.
|
data/Rakefile
ADDED
data/bin/dirfy
ADDED
data/lib/dirfy/cli.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "optparse"
|
3
|
+
require_relative "parser"
|
4
|
+
require_relative "io"
|
5
|
+
|
6
|
+
module Dirfy
|
7
|
+
# Command-line interface
|
8
|
+
class CLI
|
9
|
+
def self.start(argv = ARGV)
|
10
|
+
options = {
|
11
|
+
dry_run: false,
|
12
|
+
verbose: false,
|
13
|
+
indent: 4,
|
14
|
+
prefix: ""
|
15
|
+
}
|
16
|
+
|
17
|
+
parser = OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: dirfy [options] [treefile]"
|
19
|
+
|
20
|
+
opts.on("-d", "--dry-run", "Preview only; no files/dirs created") do
|
21
|
+
options[:dry_run] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-v", "--verbose", "Log each action") do
|
25
|
+
options[:verbose] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-iN", "--indent=N", Integer, "Spaces per level (default 4)") do |n|
|
29
|
+
options[:indent] = n
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("-pDIR", "--prefix=DIR", "Prepend DIR/ to every path") do |dir|
|
33
|
+
options[:prefix] = dir.chomp("/") + "/"
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-h", "--help", "Show this help") do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
parser.parse!(argv)
|
43
|
+
|
44
|
+
lines =
|
45
|
+
if argv[0] && File.file?(argv[0])
|
46
|
+
File.readlines(argv[0]).map(&:chomp)
|
47
|
+
else
|
48
|
+
STDIN.read.lines.map(&:chomp)
|
49
|
+
end
|
50
|
+
|
51
|
+
items = Parser.new(indent: options[:indent]).parse(lines)
|
52
|
+
items.map! { |p| options[:prefix] + p }
|
53
|
+
|
54
|
+
puts "π Detected #{items.size} items to create."
|
55
|
+
unless options[:dry_run]
|
56
|
+
print "Proceed? (y/N) "
|
57
|
+
answer = STDIN.gets.to_s.strip.downcase
|
58
|
+
exit(1) unless %w[y yes].include?(answer)
|
59
|
+
end
|
60
|
+
|
61
|
+
IO.new(dry_run: options[:dry_run], verbose: options[:verbose]).create(items)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/dirfy/io.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Dirfy
|
5
|
+
# Creates directories/files with progress bar, dry-run, verbose modes.
|
6
|
+
class IO
|
7
|
+
def initialize(dry_run: false, verbose: false)
|
8
|
+
@dry_run = dry_run
|
9
|
+
@verbose = verbose
|
10
|
+
@dirs = @files = @skipped = @failed = 0
|
11
|
+
@bar_length = 30
|
12
|
+
end
|
13
|
+
|
14
|
+
# items - Array<String>
|
15
|
+
def create(items)
|
16
|
+
total = items.size
|
17
|
+
items.each_with_index do |item, idx|
|
18
|
+
print_progress(idx + 1, total)
|
19
|
+
if item.end_with?("/")
|
20
|
+
create_dir(item)
|
21
|
+
else
|
22
|
+
create_file(item)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
puts
|
26
|
+
print_summary
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def create_dir(path)
|
32
|
+
if @dry_run
|
33
|
+
log "DRY-RUN Dir: #{path}"
|
34
|
+
else
|
35
|
+
if Dir.exist?(path)
|
36
|
+
log "Skipped dir: #{path}"
|
37
|
+
@skipped += 1
|
38
|
+
else
|
39
|
+
FileUtils.mkdir_p(path)
|
40
|
+
log "Created dir: #{path}"
|
41
|
+
@dirs += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue StandardError => e
|
45
|
+
warn "Failed dir: #{path} (#{e.message})"
|
46
|
+
@failed += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_file(path)
|
50
|
+
dir = File.dirname(path)
|
51
|
+
unless Dir.exist?(dir)
|
52
|
+
if @dry_run
|
53
|
+
log "DRY-RUN Mkdir: #{dir}"
|
54
|
+
else
|
55
|
+
FileUtils.mkdir_p(dir)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if @dry_run
|
60
|
+
log "DRY-RUN File: #{path}"
|
61
|
+
else
|
62
|
+
if File.exist?(path)
|
63
|
+
log "Skipped file: #{path}"
|
64
|
+
@skipped += 1
|
65
|
+
else
|
66
|
+
FileUtils.touch(path)
|
67
|
+
log "Created file: #{path}"
|
68
|
+
@files += 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
rescue StandardError => e
|
72
|
+
warn "Failed file: #{path} (#{e.message})"
|
73
|
+
@failed += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def print_progress(current, total)
|
77
|
+
pct = (current * 100.0 / total).floor
|
78
|
+
filled = (pct * @bar_length / 100.0).floor
|
79
|
+
empty = @bar_length - filled
|
80
|
+
bar = "β" * filled + " " * empty
|
81
|
+
print "\rπ€ [#{bar}] #{pct}%"
|
82
|
+
end
|
83
|
+
|
84
|
+
def log(msg)
|
85
|
+
puts msg if @verbose || @dry_run
|
86
|
+
end
|
87
|
+
|
88
|
+
def warn(msg)
|
89
|
+
STDERR.puts "β οΈ #{msg}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def print_summary
|
93
|
+
puts "Summary β dirs=#{@dirs}, files=#{@files}, skipped=#{@skipped}, failed=#{@failed}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/dirfy/parser.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "strscan"
|
3
|
+
|
4
|
+
module Dirfy
|
5
|
+
# Parses ASCII/Unicode tree diagrams into flat path lists.
|
6
|
+
class Parser
|
7
|
+
def initialize(indent: 4)
|
8
|
+
@indent = indent
|
9
|
+
end
|
10
|
+
|
11
|
+
# lines - Array<String>
|
12
|
+
# returns Array<String> of relative paths, dirs ending with '/'
|
13
|
+
def parse(lines)
|
14
|
+
stack = []
|
15
|
+
items = []
|
16
|
+
|
17
|
+
lines.each do |raw|
|
18
|
+
line = raw.rstrip
|
19
|
+
next if line.strip.empty? || line.strip.start_with?("#")
|
20
|
+
|
21
|
+
if line.match?(/[ββ]/)
|
22
|
+
# replace tree chars with spaces to count indent
|
23
|
+
indent_str = line.gsub(/[ββββ]/, " ")
|
24
|
+
lead_spaces = indent_str[/\A */].size
|
25
|
+
depth = lead_spaces / @indent
|
26
|
+
name = line.split("ββ ", 2).last
|
27
|
+
else
|
28
|
+
depth = 0
|
29
|
+
name = line.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
stack[depth] = name
|
33
|
+
stack = stack[0..depth]
|
34
|
+
|
35
|
+
# build full path
|
36
|
+
path = stack.map { |c| c.chomp("/") }.join("/")
|
37
|
+
path << "/" if name.end_with?("/")
|
38
|
+
items << path
|
39
|
+
end
|
40
|
+
|
41
|
+
items
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/dirfy.rb
ADDED
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dirfy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Your Name
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rake
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '13.0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '13.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
email:
|
41
|
+
- you@example.com
|
42
|
+
executables:
|
43
|
+
- dirfy
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- bin/dirfy
|
50
|
+
- lib/dirfy.rb
|
51
|
+
- lib/dirfy/cli.rb
|
52
|
+
- lib/dirfy/io.rb
|
53
|
+
- lib/dirfy/parser.rb
|
54
|
+
- lib/dirfy/version.rb
|
55
|
+
homepage: https://github.com/ahmedmelhady7/dirfy
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubygems_version: 3.6.9
|
74
|
+
specification_version: 4
|
75
|
+
summary: Create directory/file structures from ASCII/Unicode tree diagrams
|
76
|
+
test_files: []
|