lsd-rb 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: db20983b3ee9507cf1db9a2c19d0bbfb639b556d42d006f1617f45072bac6d1b
4
+ data.tar.gz: 4c64aa7c2d32343d0893229f8d9f5db6cfb455c199da820636d1aef92cd88728
5
+ SHA512:
6
+ metadata.gz: 3c6f4d86f2e406afc083bfea5c34898e76f6e633cc619ad888f7a323cdaf652dadae4938bdb7d295e4ecf683f65fcd41ebf87699721d8a0a911932afdca43996
7
+ data.tar.gz: 2801208c3964bc84feed96b04402c14158206a9866e10e0e17868df7e22af6e36ad96a36be516407f681d4af8b53ee1eff3a48e3cb2277a57876e08dffecc5c7
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Aristóteles
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # LSD
2
+
3
+ A modern alternative to the `ls` command inspired by Nushell's output, written in Ruby.
4
+
5
+ <p align="center">
6
+ <img src="./lsd.png" alt="LSD" width="600">
7
+ </p>
8
+
9
+ ## Features
10
+
11
+ - Tabular output with colors
12
+ - Column filtering via command line
13
+ - High performance through lazy loading
14
+ - Mac and Linux support
15
+
16
+ ## Installation
17
+
18
+ 1. Install gem (recommended)
19
+
20
+ ```bash
21
+ gem install lsd
22
+ ```
23
+
24
+ 2. Use the installation script
25
+
26
+ ```bash
27
+ git clone https://github.com/your-username/lsd
28
+ cd lsd
29
+ ./install.sh
30
+ ```
31
+
32
+ 3. Manual installation
33
+
34
+ The installation script will:
35
+
36
+ 1. Check if Ruby is installed (>= 3.0)
37
+ 2. Install necessary dependencies
38
+ ```bash
39
+ $ bundle install
40
+ ```
41
+ 3. Compile the program if needed
42
+ ```bash
43
+ $ chmod +x bin/lsd
44
+ ```
45
+ 4. Move the binary to `/usr/local/bin`
46
+
47
+ ## Usage
48
+
49
+ ```bash
50
+ # List current directory
51
+ lsd
52
+
53
+ # List a specific directory
54
+ lsd ~/Downloads
55
+
56
+ # Filter by name and type only
57
+ lsd --filter-by "name,type"
58
+
59
+ # Show help
60
+ lsd --help
61
+ ```
62
+
63
+ ## Available Columns
64
+
65
+ - name: File/directory name
66
+ - size: Size in human format (KB, MB, etc)
67
+ - type: File type (file, directory, symlink)
68
+ - perms: Unix permissions format
69
+ - owner: Owner
70
+ - modified: Modification date
71
+
72
+ The index column (#) is always shown.
73
+
74
+ ## Project Structure
75
+
76
+ ```
77
+ .
78
+ ├── bin/
79
+ │ └── lsd # Main executable
80
+ ├── lib/
81
+ │ ├── lsd.rb # Main code
82
+ │ ├── entry.rb # Directory entry class
83
+ │ └── formatter.rb # Table formatting
84
+ ├── install.sh # Installation script
85
+ └── README.md # This documentation
86
+ ```
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork the project
91
+ 2. Create your feature branch (`git checkout -b feature/new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add new feature'`)
93
+ 4. Push to the branch (`git push origin feature/new-feature`)
94
+ 5. Create a Pull Request
95
+
96
+ ## License
97
+
98
+ This project is licensed under the MIT License - see the LICENSE file for details.
99
+
100
+ ## Development
101
+
102
+ 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.
103
+
104
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
105
+
106
+ ## Contributing
107
+
108
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lsd. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/lsd/blob/master/CODE_OF_CONDUCT.md).
109
+
110
+ ## License
111
+
112
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
113
+
114
+ ## Code of Conduct
115
+
116
+ Everyone interacting in the Lsd project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lsd/blob/master/CODE_OF_CONDUCT.md).
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "lsd"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/lsd ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "lsd"
5
+ require "optparse"
6
+
7
+ options = {columns: Lsd::TableFormatter::DEFAULT_COLUMNS}
8
+
9
+ parser =
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: lsd [options] [directory]"
12
+
13
+ opts.on("--filter-by COLUMNS", Array, "Show only specific columns") do |columns|
14
+ options[:columns] = columns.map(&:strip)
15
+ end
16
+
17
+ opts.on("-h", "--help", "Show this help message") do
18
+ puts opts
19
+ available_columns = Lsd::TableFormatter::AVAILABLE_COLUMNS.keys - Lsd::TableFormatter::REQUIRED_COLUMNS
20
+ puts "\nAvailable columns for filtering: #{available_columns.join(", ")}"
21
+ puts "Note: The index (#) column is always shown"
22
+ puts "\nExamples:"
23
+ puts " lsd --filter-by name,size"
24
+ puts " lsd --filter-by 'name, size'"
25
+ exit
26
+ end
27
+ end
28
+
29
+ parser.parse!
30
+
31
+ begin
32
+ path = ARGV[0] || "."
33
+ entries = Dir.children(path)
34
+ .reject { |e| e.start_with?(".") }
35
+ .sort
36
+ .map { |name| Lsd::Entry.new(File.join(path, name)) }
37
+
38
+ puts Lsd::TableFormatter.format(entries, options[:columns])
39
+ rescue Errno::ENOENT
40
+ puts "Error: Directory '#{path}' not found".red
41
+ exit 1
42
+ rescue Interrupt
43
+ puts "\nOperation cancelled".yellow
44
+ exit 0
45
+ rescue => e
46
+ puts "Error: #{e.message}".red
47
+ exit 1
48
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "terminal-table"
4
+ require "colorize"
5
+
6
+ require_relative "entry"
7
+ require_relative "table_formatter"
8
+
9
+ module Lsd
10
+ module DirectoryLister
11
+ class Error < StandardError; end
12
+
13
+ def self.list(path = ".")
14
+ entries = Dir.entries(path)
15
+ .reject { |e| e.start_with?(".") }
16
+ .sort_by { |name| [File.directory?(name) ? 0 : 1, name.downcase] }
17
+ .map { |name| Entry.new(name) }
18
+
19
+ puts TableFormatter.format(entries)
20
+ rescue Errno::EACCES => e
21
+ raise Error, "Permission denied: #{path}"
22
+ end
23
+ end
24
+ end
data/lib/lsd/entry.rb ADDED
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lsd
4
+ class Entry
5
+ attr_reader :stat
6
+
7
+ def initialize(name)
8
+ @full_path = name
9
+ @stat = File.stat(name)
10
+ end
11
+
12
+ def name
13
+ @name ||= File.basename(@full_path)
14
+ end
15
+
16
+ def type
17
+ @type ||= directory? ? "dir".light_blue : "file".light_black
18
+ end
19
+
20
+ def size
21
+ @size ||= format_size(raw_size)
22
+ end
23
+
24
+ def raw_size
25
+ @raw_size ||= calculate_size
26
+ end
27
+
28
+ def modified
29
+ @modified ||= format_time_ago(stat.mtime)
30
+ end
31
+
32
+ private
33
+
34
+ def directory?
35
+ @is_directory ||= File.directory?(@full_path)
36
+ end
37
+
38
+ def calculate_size
39
+ if directory?
40
+ dir_size = 0
41
+ begin
42
+ Dir.glob("#{@full_path}/**/*", File::FNM_DOTMATCH).each do |f|
43
+ next if File.directory?(f)
44
+ next if f.end_with?("/.", "/..")
45
+
46
+ dir_size += File.size(f)
47
+ rescue Errno::ENOENT, Errno::EACCES
48
+ next
49
+ end
50
+ rescue Errno::EACCES
51
+ return 0
52
+ end
53
+ dir_size
54
+ else
55
+ stat.size
56
+ end
57
+ end
58
+
59
+ def format_size(bytes)
60
+ return "0 B" if bytes == 0
61
+
62
+ {
63
+ 1024**3 => "GiB",
64
+ 1024**2 => "MiB",
65
+ 1024 => "KiB"
66
+ }.each do |threshold, unit|
67
+ if bytes >= threshold
68
+ value = bytes.to_f / threshold
69
+ return (value < 10) ? format("%.1f %s", value, unit) : format("%d %s", value.round, unit)
70
+ end
71
+ end
72
+ "#{bytes} B"
73
+ end
74
+
75
+ TIME_INTERVALS = {
76
+ 86_400 => ->(diff) { "#{(diff / 86_400).round} days ago" },
77
+ 3600 => ->(diff) { "#{(diff / 3600).round} hours ago" },
78
+ 60 => ->(diff) { "#{(diff / 60).round} minutes ago" },
79
+ 0 => ->(diff) { "#{diff.round} seconds ago" }
80
+ }.freeze
81
+
82
+ def format_time_ago(time)
83
+ diff = Time.now - time
84
+ TIME_INTERVALS.each do |threshold, formatter|
85
+ return formatter.call(diff) if diff >= threshold
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lsd
4
+ class TableFormatter
5
+ MAX_NAME_LENGTH = 30
6
+ AVAILABLE_COLUMNS = {
7
+ "index" => {header: "#", value: ->(entry, idx) { idx.to_s.light_green }},
8
+ "name" => {header: "name", value: ->(entry, _) { entry.name.light_cyan }},
9
+ "type" => {header: "type", value: ->(entry, _) { entry.type }},
10
+ "size" => {header: "size", value: ->(entry, _) { entry.size.light_green }},
11
+ "modified" => {header: "modified", value: ->(entry, _) { entry.modified.light_magenta }}
12
+ }.freeze
13
+
14
+ DEFAULT_COLUMNS = %w[index name type size modified]
15
+ REQUIRED_COLUMNS = %w[index]
16
+
17
+ def self.format(entries, columns = DEFAULT_COLUMNS)
18
+ new(entries, columns).format
19
+ end
20
+
21
+ def initialize(entries, columns = DEFAULT_COLUMNS)
22
+ @entries = entries
23
+ @columns = process_columns(columns)
24
+ end
25
+
26
+ def format
27
+ table = Terminal::Table.new(headings: headers, rows: formatted_rows, style: {border: :unicode})
28
+
29
+ table.to_s.gsub(/[┌┐└┘]/, {"┌" => "╭", "┐" => "╮", "└" => "╰", "┘" => "╯"})
30
+ end
31
+
32
+ private
33
+
34
+ def process_columns(columns)
35
+ filtered_columns = columns.reject { |col| REQUIRED_COLUMNS.include?(col) }
36
+
37
+ valid_columns =
38
+ filtered_columns.select do |col|
39
+ if AVAILABLE_COLUMNS.key?(col)
40
+ true
41
+ else
42
+ warn "Warning: Unknown column '#{col}'. Available columns: #{(AVAILABLE_COLUMNS.keys - REQUIRED_COLUMNS).join(", ")}".yellow
43
+ false
44
+ end
45
+ end
46
+
47
+ REQUIRED_COLUMNS + valid_columns
48
+ end
49
+
50
+ def headers
51
+ @columns.map { |col| AVAILABLE_COLUMNS[col][:header].light_green }
52
+ end
53
+
54
+ def formatted_rows
55
+ @entries.map.with_index do |entry, index|
56
+ @columns.map { |col| AVAILABLE_COLUMNS[col][:value].call(entry, index) }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lsd
4
+ VERSION = "0.1.0"
5
+ end
data/lib/lsd.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lsd/version"
4
+ require_relative "lsd/directory_lister"
5
+ require_relative "lsd/entry"
6
+ require_relative "lsd/table_formatter"
7
+
8
+ module Lsd
9
+ class Error < StandardError; end
10
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lsd-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aristóteles Coutinho
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-01-27 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: colorize
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: terminal-table
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: optparse
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.6.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.6.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: bundler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: standard
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.3'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.3'
110
+ description: List directory contents with a modern, colorful interface
111
+ email:
112
+ - contato@aristoteles.dev
113
+ executables:
114
+ - lsd
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - LICENSE.txt
119
+ - README.md
120
+ - bin/console
121
+ - bin/setup
122
+ - exe/lsd
123
+ - lib/lsd.rb
124
+ - lib/lsd/directory_lister.rb
125
+ - lib/lsd/entry.rb
126
+ - lib/lsd/table_formatter.rb
127
+ - lib/lsd/version.rb
128
+ homepage: https://github.com/aristotelesbr/lsd
129
+ licenses:
130
+ - MIT
131
+ metadata:
132
+ homepage_uri: https://github.com/aristotelesbr/lsd
133
+ source_code_uri: https://github.com/aristotelesbr/lsd
134
+ changelog_uri: https://github.com/aristotelesbr/lsd/blob/main/CHANGELOG.md
135
+ rubygems_mfa_required: 'true'
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 3.1.0
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.6.2
151
+ specification_version: 4
152
+ summary: A modern replacement for ls command
153
+ test_files: []