dumb_down_viewer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dumb_down_viewer.gemspec +43 -0
- data/exe/ddv +6 -0
- data/lib/dumb_down_viewer/cli.rb +206 -0
- data/lib/dumb_down_viewer/tree_view_builder.rb +180 -0
- data/lib/dumb_down_viewer/version.rb +3 -0
- data/lib/dumb_down_viewer/visitor.rb +246 -0
- data/lib/dumb_down_viewer.rb +86 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 168cf3ff44970f98481dbfa56a9bad5ad019c67b
|
4
|
+
data.tar.gz: 75373160ed335cf58bea04caab40f91d3f36d79b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5fa0f8ba7af6c70b32e10f99d8ec48cf287058b1efde59d2cf4c9742c3513d7cbcaa376275518023e613e01d290715e22b325ffe7f16a53fa4a4a35eacd16d42
|
7
|
+
data.tar.gz: 86a23ddc37f8de3b5dea366aa495b18b3257987d24571aceb60fc2377396b4aa766b6462363f4e9cd819efc54773fb2d78eb30877f6f67f983190a015aa9997a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 HASHIMOTO, Naoki
|
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,112 @@
|
|
1
|
+
# DumbDownViewer
|
2
|
+
|
3
|
+
DumbDownViewer (ddv) is a recursive directory listing command with limited functionality.
|
4
|
+
|
5
|
+
In some case, you can use ddv like "[tree](http://mama.indstate.edu/users/ice/tree/)" command, even though these commands are not compatible:
|
6
|
+
|
7
|
+
Several options of "tree" are missing in ddv, but there are also some options that are available only in ddv (such as --copy-to).
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'dumb_down_viewer'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install dumb_down_viewer
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Suppose you have a directory `spec/data`
|
28
|
+
|
29
|
+
$ find spec/data
|
30
|
+
spec/data
|
31
|
+
spec/data/README
|
32
|
+
spec/data/index.html
|
33
|
+
spec/data/mammalia
|
34
|
+
spec/data/mammalia/can_fly
|
35
|
+
spec/data/mammalia/can_fly/bat.txt
|
36
|
+
spec/data/mammalia/index.html
|
37
|
+
spec/data/mammalia/cannot_fly
|
38
|
+
spec/data/mammalia/cannot_fly/elephant.txt
|
39
|
+
spec/data/aves
|
40
|
+
spec/data/aves/can_fly
|
41
|
+
spec/data/aves/can_fly/sparrow.txt
|
42
|
+
spec/data/aves/index.html
|
43
|
+
spec/data/aves/cannot_fly
|
44
|
+
spec/data/aves/cannot_fly/penguin.jpg
|
45
|
+
spec/data/aves/cannot_fly/penguin.txt
|
46
|
+
spec/data/aves/cannot_fly/ostrich.txt
|
47
|
+
spec/data/aves/cannot_fly/ostrich.jpg
|
48
|
+
|
49
|
+
|
50
|
+
Then type at the command line (or replace `spec/data` with a directory name which is on your system):
|
51
|
+
|
52
|
+
$ ddv spec/data
|
53
|
+
|
54
|
+
And you will get something like the following
|
55
|
+
|
56
|
+
[spec/data]
|
57
|
+
├─ README
|
58
|
+
├─ index.html
|
59
|
+
├─ [aves]
|
60
|
+
│ ├─ index.html
|
61
|
+
│ ├─ [can_fly]
|
62
|
+
│ │ └─ sparrow.txt
|
63
|
+
│ └─ [cannot_fly]
|
64
|
+
│ ├─ ostrich.jpg
|
65
|
+
│ ├─ ostrich.txt
|
66
|
+
│ ├─ penguin.jpg
|
67
|
+
│ └─ penguin.txt
|
68
|
+
└─ [mammalia]
|
69
|
+
├─ index.html
|
70
|
+
├─ [can_fly]
|
71
|
+
│ └─ bat.txt
|
72
|
+
└─ [cannot_fly]
|
73
|
+
└─ elephant.txt
|
74
|
+
|
75
|
+
### Options
|
76
|
+
|
77
|
+
The following is the list of available options:
|
78
|
+
|
79
|
+
|Short |Long |Description |
|
80
|
+
|------|-----|------------|
|
81
|
+
|-s [style_name] |--style [=style_name] |Choose the style of output other than default from ascii_art, list, zenkaku or tree |
|
82
|
+
|-f [format_name] |--format [=format_name] |Choose the output format other than default from csv, tsv or tree_csv |
|
83
|
+
|-d |--directories-only |List directories only |
|
84
|
+
|-L [level] |- |Descend only level directories deep |
|
85
|
+
|-P [pattern] |- |List only those files that match the given pattern |
|
86
|
+
|-I [pattern] |- |Do not list files that match the given pattern |
|
87
|
+
|-a |--all |Show all files including dot files |
|
88
|
+
|- |--ignore-case |Ignore case when pattern matching |
|
89
|
+
|-o [output_file] |--output [=output_file] |Output to file instead of stdout |
|
90
|
+
|- |--filelimit [=number_of_files] |Do not descend dirs with more than the specified number of files in them |
|
91
|
+
|- |--summary [=summary_type] |Add summary information about directories. Available summary_type: default |
|
92
|
+
|-J |- |Print out a JSON representation |
|
93
|
+
|-X |- |Print out an XML representation |
|
94
|
+
|- |--report-total-count |Display file/directory count at the end of directory listing |
|
95
|
+
|- |--copy-to [=dest_dir] |Copy the directory tree to dest_dir |
|
96
|
+
|
97
|
+
|
98
|
+
## Development
|
99
|
+
|
100
|
+
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.
|
101
|
+
|
102
|
+
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nico-hn/DumbDownViewer.
|
107
|
+
|
108
|
+
|
109
|
+
## License
|
110
|
+
|
111
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
112
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "dumb_down_viewer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dumb_down_viewer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dumb_down_viewer"
|
8
|
+
spec.version = DumbDownViewer::VERSION
|
9
|
+
spec.authors = ["HASHIMOTO, Naoki"]
|
10
|
+
spec.email = ["hashimoto.naoki@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{DumbDownViewer (ddv) is a recursive directory listing command with limited functionality.}
|
13
|
+
spec.description = <<-DESCRIPTION
|
14
|
+
DumbDownViewer (ddv) is a recursive directory listing command with limited functionality.
|
15
|
+
In some case, you can use ddv like "tree" command (http://mama.indstate.edu/users/ice/tree/),
|
16
|
+
even though these commands are not compatible: Several options of "tree" are missing in ddv,
|
17
|
+
but there are also some options that are available only in ddv (such as --copy-to).
|
18
|
+
DESCRIPTION
|
19
|
+
spec.homepage = "https://github.com/nico-hn/DumbDownViewer"
|
20
|
+
spec.license = "MIT"
|
21
|
+
|
22
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
23
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
24
|
+
if spec.respond_to?(:metadata)
|
25
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
26
|
+
else
|
27
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
28
|
+
"public gem pushes."
|
29
|
+
end
|
30
|
+
|
31
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
32
|
+
f.match(%r{^(test|spec|features)/})
|
33
|
+
end
|
34
|
+
spec.bindir = "exe"
|
35
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
spec.add_runtime_dependency "nokogiri", "~> 1.6"
|
38
|
+
spec.add_runtime_dependency "optparse_plus"
|
39
|
+
|
40
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
41
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
42
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
43
|
+
end
|
data/exe/ddv
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'dumb_down_viewer'
|
2
|
+
require 'dumb_down_viewer/tree_view_builder'
|
3
|
+
require 'optparse_plus'
|
4
|
+
|
5
|
+
module DumbDownViewer
|
6
|
+
module Cli
|
7
|
+
FORMATTER = {
|
8
|
+
default: DumbDownViewer::TreeViewBuilder::PlainTextFormat,
|
9
|
+
csv: DumbDownViewer::TreeViewBuilder::CSVFormat,
|
10
|
+
tsv: DumbDownViewer::TreeViewBuilder::CSVFormat,
|
11
|
+
tree_csv: DumbDownViewer::TreeViewBuilder::TreeCSVFormat
|
12
|
+
}
|
13
|
+
|
14
|
+
OPTIONS = <<YAML
|
15
|
+
banner: "USAGE: #{File.basename($0)} [OPTION]... DIRECTORY"
|
16
|
+
style:
|
17
|
+
short: "-s [style_name]"
|
18
|
+
long: "--style [=style_name]"
|
19
|
+
description: "Choose the style of output other than default from ascii_art, list, zenkaku or tree"
|
20
|
+
format:
|
21
|
+
short: "-f [format_name]"
|
22
|
+
long: "--format [=format_name]"
|
23
|
+
description: Choose the output format other than default from csv, tsv, tree_csv
|
24
|
+
directories:
|
25
|
+
short: "-d"
|
26
|
+
long: "--directories-only"
|
27
|
+
description: "List directories only"
|
28
|
+
level:
|
29
|
+
short: "-L [level]"
|
30
|
+
description: "Descend only level directories deep"
|
31
|
+
match:
|
32
|
+
short: "-P [pattern]"
|
33
|
+
description: "List only those files that match the pattern given"
|
34
|
+
ignore_match:
|
35
|
+
short: "-I [pattern]"
|
36
|
+
description: "Do not list files that match the given pattern"
|
37
|
+
show_all:
|
38
|
+
short: "-a"
|
39
|
+
long: "--all"
|
40
|
+
description: "Show all files including dot files"
|
41
|
+
ignore_case:
|
42
|
+
long: "--ignore-case"
|
43
|
+
description: "Ignore case when pattern matching"
|
44
|
+
output:
|
45
|
+
short: "-o [output_file]"
|
46
|
+
long: "--output [=output_file]"
|
47
|
+
description: "Output to file instead of stdout"
|
48
|
+
file_limit:
|
49
|
+
long: "--filelimit [=number_of_files]"
|
50
|
+
description: "Do not descend dirs with more than the specified number of files in them"
|
51
|
+
summary:
|
52
|
+
long: "--summary [=summary_type]"
|
53
|
+
description: "Add summary information about directories. Available summary_type: default"
|
54
|
+
json:
|
55
|
+
short: "-J"
|
56
|
+
description: "Print out a JSON representation"
|
57
|
+
xml:
|
58
|
+
short: "-X"
|
59
|
+
description: "Print out an XML representation"
|
60
|
+
total_count:
|
61
|
+
long: "--report-total-count"
|
62
|
+
description: "Display file/directory count at the end of directory listing"
|
63
|
+
copy_to:
|
64
|
+
long: "--copy-to [=dest_dir]"
|
65
|
+
description: "Copy the directory tree to dest_dir"
|
66
|
+
YAML
|
67
|
+
|
68
|
+
def self.parse_command_line_options
|
69
|
+
options = { style: :default, format: :default }
|
70
|
+
OptionParser.new_with_yaml(OPTIONS) do |opt|
|
71
|
+
opt.version = DumbDownViewer::VERSION
|
72
|
+
opt.inherit_ruby_options("E") # -E --encoding
|
73
|
+
|
74
|
+
opt.on(:style) {|style| options[:style] = style.to_sym }
|
75
|
+
opt.on(:format) {|format| options[:format] = format.to_sym }
|
76
|
+
opt.on(:directories) {|directories| options[:directories] = true }
|
77
|
+
opt.on(:level) {|level| options[:level] = level.to_i }
|
78
|
+
opt.on(:match) {|pattern| options[:match] = pattern }
|
79
|
+
opt.on(:ignore_match) {|pattern| options[:ignore_match] = pattern }
|
80
|
+
opt.on(:show_all) { options[:show_all] = true }
|
81
|
+
opt.on(:ignore_case) { options[:ignore_case] = true }
|
82
|
+
opt.on(:output) {|output_file| options[:output] = output_file }
|
83
|
+
opt.on(:file_limit) {|number_of_files| options[:file_limit] = number_of_files.to_i }
|
84
|
+
opt.on(:summary) {|summary_type| options[:summary] = summary_type.to_sym }
|
85
|
+
opt.on(:json) { options[:json] = true }
|
86
|
+
opt.on(:xml) { options[:xml] = true }
|
87
|
+
opt.on(:total_count) { options[:total_count] = true }
|
88
|
+
opt.on(:copy_to) {|dest_dir| options[:copy_to] = dest_dir }
|
89
|
+
opt.parse!
|
90
|
+
end
|
91
|
+
options
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.execute
|
95
|
+
options = parse_command_line_options
|
96
|
+
tree = DumbDownViewer.build_node_tree(ARGV[0])
|
97
|
+
prune_dot_files(tree) unless options[:show_all]
|
98
|
+
prune_dirs_with_more_than(tree, options[:file_limit]) if options[:file_limit]
|
99
|
+
prune_level(tree, options[:level]) if options[:level]
|
100
|
+
select_match(tree, options) if options[:match]
|
101
|
+
ignore_match(tree, options) if options[:ignore_match]
|
102
|
+
prune_files(tree) if options[:directories]
|
103
|
+
copy_to(tree, options) if options[:copy_to]
|
104
|
+
open_output(options[:output]) do |out|
|
105
|
+
out.print format_tree(tree, options)
|
106
|
+
out.puts total_count(tree) if options[:total_count]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.prune_dot_files(tree)
|
111
|
+
pruner = DumbDownViewer::TreePruner.create(false) {|node| node.name.start_with? '.' }
|
112
|
+
pruner.visit(tree, nil)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.prune_files(tree)
|
116
|
+
pruner = DumbDownViewer::TreePruner.create {|node| node.directory? }
|
117
|
+
pruner.visit(tree, nil)
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.prune_level(tree, level)
|
121
|
+
pruner = DumbDownViewer::TreePruner.create {|node| node.depth <= level }
|
122
|
+
pruner.visit(tree, nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.select_match(tree, options)
|
126
|
+
pat = Regexp.compile(options[:match], options[:ignore_case])
|
127
|
+
pruner = DumbDownViewer::TreePruner.create {|node| node.directory? or pat =~ node.name }
|
128
|
+
pruner.visit(tree, nil)
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.ignore_match(tree, options)
|
132
|
+
pat = Regexp.compile(options[:ignore_match], options[:ignore_case])
|
133
|
+
pruner = DumbDownViewer::TreePruner.create(false) {|node| not node.directory? and pat =~ node.name }
|
134
|
+
pruner.visit(tree, nil)
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.prune_dirs_with_more_than(tree, number_of_files)
|
138
|
+
pruner = DumbDownViewer::TreePruner.create(false) do |node|
|
139
|
+
node.directory? and node.sub_nodes.size > number_of_files
|
140
|
+
end
|
141
|
+
if tree.sub_nodes.size > number_of_files
|
142
|
+
tree.directories.clear
|
143
|
+
tree.files.clear
|
144
|
+
end
|
145
|
+
pruner.visit(tree, nil)
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.format_json(tree, options)
|
149
|
+
json = DumbDownViewer::JSONConverter.dump(tree)
|
150
|
+
json + $/
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.format_xml(tree, options)
|
154
|
+
DumbDownViewer::XMLConverter.dump(tree)
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.add_summary(tree, options)
|
158
|
+
if summary_type = options[:summary] and summary_type != :default
|
159
|
+
STDERR.puts "Unknown option #{summary_type} for --summary: default is used instead."
|
160
|
+
end
|
161
|
+
visitor = DumbDownViewer::FileCountSummary.new
|
162
|
+
tree.accept(visitor, nil)
|
163
|
+
DumbDownViewer::FileCountSummary::NodeFormat.new
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.format_tree(tree, options)
|
167
|
+
if options[:file_limit] and tree.sub_nodes.empty?
|
168
|
+
''
|
169
|
+
elsif options[:json]
|
170
|
+
format_json(tree, options)
|
171
|
+
elsif options[:xml]
|
172
|
+
format_xml(tree, options)
|
173
|
+
else
|
174
|
+
format_tree_with_builder(tree, options)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.format_tree_with_builder(tree, options)
|
179
|
+
style = options[:style]
|
180
|
+
col_sep = options[:format] == :tsv ? "\t" : ','
|
181
|
+
node_format = options[:summary] ? add_summary(tree, options) : nil
|
182
|
+
builder = TreeViewBuilder.create(tree)
|
183
|
+
formatter = FORMATTER[options[:format]].new(style, col_sep, node_format)
|
184
|
+
formatter.format_table(builder.tree_table)
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.total_count(tree)
|
188
|
+
count = DumbDownViewer::TotalNodeCount.count(tree)
|
189
|
+
"#{$/}#{count[:directories]} directories, #{count[:files]} files"
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.copy_to(tree, options)
|
193
|
+
DumbDownViewer::TreeDuplicator.duplicate(tree, options[:copy_to])
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.open_output(filename)
|
197
|
+
if filename
|
198
|
+
open(File.expand_path(filename), "wb") do |out|
|
199
|
+
yield out
|
200
|
+
end
|
201
|
+
else
|
202
|
+
yield STDOUT
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'dumb_down_viewer'
|
2
|
+
require 'dumb_down_viewer/visitor'
|
3
|
+
require 'csv'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module DumbDownViewer
|
7
|
+
class TreeViewBuilder < Visitor
|
8
|
+
attr_reader :tree_table
|
9
|
+
|
10
|
+
class PlainTextFormat
|
11
|
+
attr_accessor :line
|
12
|
+
|
13
|
+
LINE_PATTERNS = YAML.load(<<YAML_DATA)
|
14
|
+
:default:
|
15
|
+
:spacer: ' '
|
16
|
+
:h_line: '── '
|
17
|
+
:v_line: '│ '
|
18
|
+
:branch: '├─ '
|
19
|
+
:corner: '└─ '
|
20
|
+
:ascii_art:
|
21
|
+
:spacer: ' '
|
22
|
+
:h_line: '--- '
|
23
|
+
:v_line: '| '
|
24
|
+
:branch: '|-- '
|
25
|
+
:corner: '`-- '
|
26
|
+
:list:
|
27
|
+
:spacer: ' '
|
28
|
+
:h_line: ' '
|
29
|
+
:v_line: ' '
|
30
|
+
:branch: ' * '
|
31
|
+
:corner: ' * '
|
32
|
+
:zenkaku:
|
33
|
+
:spacer: ' '
|
34
|
+
:h_line: '── '
|
35
|
+
:v_line: '│ '
|
36
|
+
:branch: '├─ '
|
37
|
+
:corner: '└─ '
|
38
|
+
:tree:
|
39
|
+
:spacer: ' '
|
40
|
+
:h_line: '─── '
|
41
|
+
:v_line: '│ '
|
42
|
+
:branch: '├── '
|
43
|
+
:corner: '└── '
|
44
|
+
|
45
|
+
YAML_DATA
|
46
|
+
|
47
|
+
def initialize(line_pattern=:default, col_sep=nil, node_format=nil)
|
48
|
+
# col_sep is just for having common interface
|
49
|
+
@line = LINE_PATTERNS[line_pattern]
|
50
|
+
@node_format = node_format || NodeFormat.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def format_table(tree_table)
|
54
|
+
t = tree_table.transpose
|
55
|
+
t.each_cons(2) do |fr, sr|
|
56
|
+
fr.each_with_index do |f, i|
|
57
|
+
next unless f.kind_of? Node
|
58
|
+
draw_lines(fr, sr, f, i)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
draw_last_line(t[-1])
|
63
|
+
update_root_directory_name(tree_table[0][0], t)
|
64
|
+
table_to_output_format(t.transpose)
|
65
|
+
end
|
66
|
+
|
67
|
+
def draw_lines(fr, sr, f_node, i)
|
68
|
+
sub_count = f_node.sub_nodes.size
|
69
|
+
j = i
|
70
|
+
while sub_count > 0
|
71
|
+
j += 1
|
72
|
+
s_node = sr[j]
|
73
|
+
if s_node
|
74
|
+
fr[j] = sub_count == 1 ? @line[:corner] : @line[:branch]
|
75
|
+
sub_count -= 1
|
76
|
+
else
|
77
|
+
fr[j] = @line[:v_line]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
fr[i] = @node_format[f_node]
|
81
|
+
end
|
82
|
+
|
83
|
+
def draw_last_line(line)
|
84
|
+
line.each_index do |i|
|
85
|
+
node = line[i]
|
86
|
+
line[i] = @node_format[node] if node.kind_of?(DirNode)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def fill_spaces(table)
|
91
|
+
table.map do |row|
|
92
|
+
(row.size - 1).downto(0) do |i|
|
93
|
+
row[i] = @line[:spacer] if row[i + 1] and row[i].nil?
|
94
|
+
end
|
95
|
+
row
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def update_root_directory_name(root, table)
|
100
|
+
if root.directory and not root.directory.empty?
|
101
|
+
table[0][0] = "[#{File.join(root.directory, root.name)}]"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def table_to_output_format(table)
|
106
|
+
fill_spaces(table).map {|r| r.join }.join($/) + $/
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class TreeCSVFormat < PlainTextFormat
|
111
|
+
def initialize(line_pattern=:default, col_sep=',', node_format=nil)
|
112
|
+
@line = LINE_PATTERNS[line_pattern]
|
113
|
+
@col_sep = col_sep
|
114
|
+
@node_format = node_format || NodeFormat.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def table_to_output_format(table)
|
118
|
+
CSV.generate('', col_sep: @col_sep) do |csv|
|
119
|
+
table.each {|row| csv << row }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class CSVFormat
|
125
|
+
def initialize(line_pattern=nil, col_sep=',', node_format=nil)
|
126
|
+
@line_pattern = line_pattern
|
127
|
+
@col_sep = col_sep
|
128
|
+
@node_format = node_format || NodeFormat.new
|
129
|
+
end
|
130
|
+
|
131
|
+
def format_table(tree_table)
|
132
|
+
root = tree_table[0][0]
|
133
|
+
tree_table[0][0] = "#{File.join(root.directory, root.name)}"
|
134
|
+
CSV.generate('', col_sep: @col_sep) do |csv|
|
135
|
+
tree_table.each {|row| csv << row }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def format(formatter=PlainTextFormat.new)
|
141
|
+
formatter.format_table(@tree_table)
|
142
|
+
end
|
143
|
+
|
144
|
+
def setup(tree)
|
145
|
+
@tree_table = []
|
146
|
+
determine_depth(tree)
|
147
|
+
tree.accept(self, nil)
|
148
|
+
end
|
149
|
+
|
150
|
+
def determine_depth(tree) # update of test necessary
|
151
|
+
@tree_depth = 0
|
152
|
+
depth_checker = Visitor.new do |node, memo|
|
153
|
+
@tree_depth = node.depth > @tree_depth ? node.depth : @tree_depth
|
154
|
+
end
|
155
|
+
tree.accept(depth_checker, 0)
|
156
|
+
end
|
157
|
+
|
158
|
+
def new_table_row
|
159
|
+
Array.new(@tree_depth + 1)
|
160
|
+
end
|
161
|
+
|
162
|
+
def visit_dir_node(node, memo)
|
163
|
+
add_current_node_row(node)
|
164
|
+
|
165
|
+
[node.files, node.directories].each do |nodes|
|
166
|
+
nodes.sort_by {|n| n.name }.each {|n| n.accept(self, memo) }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def visit_file_node(node, memo)
|
171
|
+
add_current_node_row(node)
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_current_node_row(node)
|
175
|
+
row = new_table_row
|
176
|
+
row[node.depth] = node
|
177
|
+
@tree_table.push row
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'dumb_down_viewer'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module DumbDownViewer
|
5
|
+
class Visitor
|
6
|
+
attr_accessor
|
7
|
+
|
8
|
+
def self.create(*args, &memo_update)
|
9
|
+
new(&memo_update).tap {|visitor| visitor.setup(*args) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(&memo_update)
|
13
|
+
@memo_update = memo_update
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def visit(node, memo)
|
20
|
+
memo = @memo_update.call(node, memo) if @memo_update
|
21
|
+
|
22
|
+
if node.kind_of? DirNode
|
23
|
+
visit_dir_node(node, memo)
|
24
|
+
else
|
25
|
+
visit_file_node(node, memo)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def visit_dir_node(node, memo)
|
30
|
+
visit_sub_nodes(node, memo)
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_file_node(node, memo)
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_sub_nodes(node, memo)
|
37
|
+
node.sub_nodes.each do |node|
|
38
|
+
node.accept(self, memo)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class NodeFormat
|
44
|
+
def [](node)
|
45
|
+
case node
|
46
|
+
when DumbDownViewer::DirNode
|
47
|
+
format_dir(node)
|
48
|
+
when DumbDownViewer::FileNode
|
49
|
+
format_file(node)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def format_dir(node)
|
54
|
+
"[#{node.name}]"
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_file(node)
|
58
|
+
node.name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class TreePruner < Visitor
|
63
|
+
def setup(keep=true)
|
64
|
+
criteria = @memo_update
|
65
|
+
delete_method = keep ? :keep_if : :delete_if
|
66
|
+
@memo_update = proc do |node, memo|
|
67
|
+
unless node.kind_of? FileNode
|
68
|
+
[node.directories, node.files].each do |nodes|
|
69
|
+
nodes.send(delete_method) {|n| criteria.call(n) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class FileCountSummary < Visitor
|
77
|
+
class NodeFormat < DumbDownViewer::NodeFormat
|
78
|
+
def format_dir(node)
|
79
|
+
report = "[#{node.name}]"
|
80
|
+
data = node.summary
|
81
|
+
counts = data.keys.map {|ext| file_count(data, ext) }.join(', ')
|
82
|
+
report << " => #{counts}" unless counts.empty?
|
83
|
+
report
|
84
|
+
end
|
85
|
+
|
86
|
+
def file_count(data, ext)
|
87
|
+
count = data[ext].size
|
88
|
+
unit = count == 1 ? 'file'.freeze : 'files'.freeze
|
89
|
+
ext = '(misc)'.freeze if ext.empty?
|
90
|
+
"#{ext}: #{count} #{unit}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def visit_dir_node(node, memo)
|
95
|
+
node.summary = node.files.group_by {|file| file.extention }
|
96
|
+
visit_sub_nodes(node, memo)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
TotalNodeCount = Visitor.create do |node, memo|
|
101
|
+
memo[node.class] += 1
|
102
|
+
memo
|
103
|
+
end
|
104
|
+
|
105
|
+
def TotalNodeCount.count(tree)
|
106
|
+
counter = Hash.new(0)
|
107
|
+
tree.accept(self, counter)
|
108
|
+
{ directories: counter[DirNode] - 1, files: counter[FileNode] }
|
109
|
+
end
|
110
|
+
|
111
|
+
class JSONConverter
|
112
|
+
def self.dump(tree, with_path=false)
|
113
|
+
JSON.dump(new.visit(tree, with_path))
|
114
|
+
end
|
115
|
+
|
116
|
+
def visit(node, with_path=false)
|
117
|
+
case node
|
118
|
+
when DirNode
|
119
|
+
{
|
120
|
+
type: 'directory', name: name_value(node, with_path),
|
121
|
+
contents: node.sub_nodes.map {|n| n.accept(self, with_path) }
|
122
|
+
}
|
123
|
+
when FileNode
|
124
|
+
{ type: 'file', name: name_value(node, with_path) }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def name_value(node, with_path)
|
131
|
+
if with_path
|
132
|
+
File.join(node.directory, node.name)
|
133
|
+
else
|
134
|
+
node.name
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class XMLConverter
|
140
|
+
attr_reader :doc, :tree_root
|
141
|
+
XML_TEMPLATE = '<?xml version="1.0" encoding="UTF-8"?>'
|
142
|
+
|
143
|
+
def self.dump(tree, with_path=false)
|
144
|
+
visitor = new.tap do |v|
|
145
|
+
v.create_doc
|
146
|
+
v.visit(tree, with_path)
|
147
|
+
end
|
148
|
+
|
149
|
+
visitor.dump(tree, with_path)
|
150
|
+
end
|
151
|
+
|
152
|
+
def visit(node, with_path)
|
153
|
+
case node
|
154
|
+
when DirNode
|
155
|
+
create_dir_element(node, with_path).tap do |elm|
|
156
|
+
node.sub_nodes.each do |n|
|
157
|
+
n.accept(self, with_path).parent = elm
|
158
|
+
end
|
159
|
+
end
|
160
|
+
when FileNode
|
161
|
+
create_file_element(node, with_path)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def create_doc
|
166
|
+
@doc = Nokogiri::XML(XML_TEMPLATE).tap do |doc|
|
167
|
+
@tree_root = Nokogiri::XML::Node.new('tree'.freeze, doc)
|
168
|
+
@tree_root.parent = doc
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def dump(tree, with_path)
|
173
|
+
file_regexp = /#{Regexp.escape("> <\/file>")}/
|
174
|
+
visit(tree, with_path).parent = @tree_root
|
175
|
+
@doc.to_xml.gsub(file_regexp, '></file>')
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def create_dir_element(node, with_path)
|
181
|
+
Nokogiri::XML::Node.new('directory'.freeze, @doc).tap do |elm|
|
182
|
+
elm['name'.freeze] = name_value(node, with_path)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_file_element(node, with_path)
|
187
|
+
Nokogiri::XML::Node.new('file'.freeze, @doc).tap do |elm|
|
188
|
+
elm['name'.freeze] = name_value(node, with_path)
|
189
|
+
elm.content = ' '.freeze
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def name_value(node, with_path)
|
194
|
+
if with_path
|
195
|
+
File.join(node.directory, node.name)
|
196
|
+
else
|
197
|
+
node.name
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class TreeDuplicator < Visitor
|
203
|
+
class DuplicationError < StandardError; end
|
204
|
+
|
205
|
+
def self.duplicate(tree, dest_root)
|
206
|
+
duplicator = new
|
207
|
+
duplicator.setup(tree, dest_root)
|
208
|
+
tree.accept(duplicator, nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
def setup(tree, dest_root)
|
212
|
+
orig_root = File.join(tree.directory, tree.name)
|
213
|
+
@dest_root, @orig_root = [dest_root, orig_root].map do |root|
|
214
|
+
root.sub(/\/\Z/, '')
|
215
|
+
end
|
216
|
+
@orig_root_re = /\A#{Regexp.escape(@orig_root)}/
|
217
|
+
end
|
218
|
+
|
219
|
+
def visit_dir_node(node, memo)
|
220
|
+
dest_dir = replace_root(File.join(node.directory, node.name))
|
221
|
+
check_entry_existence(dest_dir)
|
222
|
+
FileUtils.mkdir(File.expand_path(dest_dir))
|
223
|
+
visit_sub_nodes(node, memo)
|
224
|
+
end
|
225
|
+
|
226
|
+
def visit_file_node(node, memo)
|
227
|
+
orig_file = File.join(node.directory, node.name)
|
228
|
+
dest_file = replace_root(orig_file)
|
229
|
+
check_entry_existence(dest_file)
|
230
|
+
FileUtils.cp(File.expand_path(orig_file),
|
231
|
+
File.expand_path(dest_file))
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def replace_root(path)
|
237
|
+
path.sub(@orig_root_re, @dest_root)
|
238
|
+
end
|
239
|
+
|
240
|
+
def check_entry_existence(entry)
|
241
|
+
if File.exist?(File.expand_path(entry))
|
242
|
+
raise DuplicationError, "#{entry} already exists: #{@orig_root} is not copied."
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'dumb_down_viewer/version'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'find'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
module DumbDownViewer
|
7
|
+
def self.collect_directories_and_files(path)
|
8
|
+
entries = Dir.entries(path) - ['.', '..']
|
9
|
+
entries.partition do |entry|
|
10
|
+
entry_path = File.expand_path(File.join(path, entry))
|
11
|
+
File.directory? entry_path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build_node_tree(dir)
|
16
|
+
dirname, filename = File.split(dir)
|
17
|
+
DirNode.new(dirname, filename, 0).tap {|dir| dir.collect_entries }
|
18
|
+
end
|
19
|
+
|
20
|
+
class Node
|
21
|
+
attr_reader :sub_nodes, :directory, :name, :depth
|
22
|
+
attr_accessor :summary
|
23
|
+
|
24
|
+
def initialize(pwd, name, depth)
|
25
|
+
@directory = pwd
|
26
|
+
@name = name
|
27
|
+
@depth = depth
|
28
|
+
@name_with_path = pwd.empty? ? @name : File.join(pwd, name)
|
29
|
+
setup
|
30
|
+
end
|
31
|
+
|
32
|
+
def setup
|
33
|
+
end
|
34
|
+
|
35
|
+
def accept(visitor, memo)
|
36
|
+
visitor.visit(self, memo)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
@name
|
41
|
+
end
|
42
|
+
|
43
|
+
def directory?
|
44
|
+
kind_of? DirNode
|
45
|
+
end
|
46
|
+
|
47
|
+
def file?
|
48
|
+
kind_of? FileNode
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class DirNode < Node
|
53
|
+
attr_reader :directories, :files
|
54
|
+
|
55
|
+
def sub_nodes
|
56
|
+
(@files + @directories).freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
def collect_entries
|
60
|
+
dirs, files = DumbDownViewer.collect_directories_and_files(@name_with_path)
|
61
|
+
depth = @depth + 1
|
62
|
+
@directories = entry_nodes(dirs, DirNode, depth)
|
63
|
+
@directories.each {|dir| dir.collect_entries }
|
64
|
+
@files = entry_nodes(files, FileNode, depth)
|
65
|
+
end
|
66
|
+
|
67
|
+
def entry_nodes(nodes, node_class, depth)
|
68
|
+
nodes.map {|node| node_class.new(@name_with_path, node, depth) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class FileNode < Node
|
73
|
+
attr_reader :extention
|
74
|
+
def setup
|
75
|
+
extract_extention
|
76
|
+
@sub_nodes = [].freeze
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def extract_extention
|
82
|
+
m = /\.([^.]+)\Z/.match(@name)
|
83
|
+
@extention = m ? m[1] : ''
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dumb_down_viewer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- HASHIMOTO, Naoki
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: optparse_plus
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: |
|
84
|
+
DumbDownViewer (ddv) is a recursive directory listing command with limited functionality.
|
85
|
+
In some case, you can use ddv like "tree" command (http://mama.indstate.edu/users/ice/tree/),
|
86
|
+
even though these commands are not compatible: Several options of "tree" are missing in ddv,
|
87
|
+
but there are also some options that are available only in ddv (such as --copy-to).
|
88
|
+
email:
|
89
|
+
- hashimoto.naoki@gmail.com
|
90
|
+
executables:
|
91
|
+
- ddv
|
92
|
+
extensions: []
|
93
|
+
extra_rdoc_files: []
|
94
|
+
files:
|
95
|
+
- ".gitignore"
|
96
|
+
- ".rspec"
|
97
|
+
- ".travis.yml"
|
98
|
+
- Gemfile
|
99
|
+
- LICENSE.txt
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- bin/console
|
103
|
+
- bin/setup
|
104
|
+
- dumb_down_viewer.gemspec
|
105
|
+
- exe/ddv
|
106
|
+
- lib/dumb_down_viewer.rb
|
107
|
+
- lib/dumb_down_viewer/cli.rb
|
108
|
+
- lib/dumb_down_viewer/tree_view_builder.rb
|
109
|
+
- lib/dumb_down_viewer/version.rb
|
110
|
+
- lib/dumb_down_viewer/visitor.rb
|
111
|
+
homepage: https://github.com/nico-hn/DumbDownViewer
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata:
|
115
|
+
allowed_push_host: https://rubygems.org
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubyforge_project:
|
132
|
+
rubygems_version: 2.4.5.1
|
133
|
+
signing_key:
|
134
|
+
specification_version: 4
|
135
|
+
summary: DumbDownViewer (ddv) is a recursive directory listing command with limited
|
136
|
+
functionality.
|
137
|
+
test_files: []
|