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 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
+ [![Build Status](https://github.com/ahmedmelhady7/dirfy/actions/workflows/ruby.yml/badge.svg)](https://github.com/ahmedmelhady7/dirfy/actions)
4
+ [![Gem Version](https://badge.fury.io/rb/dirfy.svg)](https://badge.fury.io/rb/dirfy)
5
+ [![License](https://img.shields.io/github/license/ahmedmelhady7/dirfy.svg)](https://github.com/ahmedmelhady7/dirfy/blob/main/LICENSE)
6
+ [![Ruby β‰₯2.6](https://img.shields.io/badge/ruby-%3E%3D2.6-blue.svg)](https://www.ruby-lang.org/)
7
+ [![Issues](https://img.shields.io/github/issues/ahmedmelhady7/dirfy.svg)](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
@@ -0,0 +1,6 @@
1
+ # Rakefile
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
data/bin/dirfy ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ require "dirfy/cli"
4
+ Dirfy::CLI.start(ARGV)
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
@@ -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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Dirfy
3
+ VERSION = "0.1.4"
4
+ end
data/lib/dirfy.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ require "dirfy/version"
3
+ require "dirfy/parser"
4
+ require "dirfy/io"
5
+ require "dirfy/cli"
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: []