dle 0.1.7 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 925684c19f7b8b0f4e34406a8d6954e7a6b3c95b
4
- data.tar.gz: 9fc9e15525c47d1d64d9e60a72778fc4583bc61e
3
+ metadata.gz: c1222ce5b458c3140edf473d1505c02fc789edaf
4
+ data.tar.gz: e172707845f3c78cc6d5bcff8a2f4ae84eae6786
5
5
  SHA512:
6
- metadata.gz: 896a00ec5bb4c4c89b49819d0a276df2cbaa19b6faf2230f2a81b537b77d038b5b8633348d891a99f0bcb6317b34714175daa49bdc7fdc097b3e71f4a0123e65
7
- data.tar.gz: 774d08af7e89e476569a4c69d7184bbc5a949ca5a18368d0e6b3d304de99fe7b4455d3796f66cf090114ff2a76722a4aca44047d8b5dc7feb63fa78e92b87430
6
+ metadata.gz: 730402aa582c113b6904b3fd511c396679b4621a8981e1caafc7faa174adcca70be21e92e8993fd632595ec5db49cf8ee19729491cb316df5579b50519003447
7
+ data.tar.gz: 816a8bb37cde6ecae9b49c40392ea89168222495271794d8a7eb3350236231de006232507f116a9aa055a8cafdb7e774c33ff3eb2fe6367b9d0d1d89ec1d2414
data/README.md CHANGED
@@ -5,11 +5,12 @@ You can copy, move, rename, chmod, chown or remove individual files or directori
5
5
 
6
6
  **BETA product, use at your own risk, use `--simulate` to be sure, always have a backup!**
7
7
 
8
- **[» See it in action!](https://www.youtube.com/watch?v=-xfnx3VQvNQ)**
8
+ [![YouTube](http://img.youtube.com/vi/-xfnx3VQvNQ/mqdefault.jpg)](https://www.youtube.com/watch?v=-xfnx3VQvNQ)
9
+ **[▶ See it in action](https://www.youtube.com/watch?v=-xfnx3VQvNQ)**
9
10
 
10
11
  ## Requirements
11
12
 
12
- You will need a UNIX system with a working Ruby installation, sorry Windozer!
13
+ You will need a UNIX system with a working Ruby (>= 1.9.3) installation, sorry Windozer!
13
14
 
14
15
  ## Installation
15
16
 
@@ -28,16 +29,26 @@ Note that you need a blocking call to the editor (for GUI editors). Sublime and
28
29
  To get a list of available options invoke DLE with the `--help` or `-h` option:
29
30
 
30
31
  Usage: dle [options] base_directory
32
+ # Application options
31
33
  -d, --dotfiles Include dotfiles (unix invisible)
32
34
  -r, --skip-review Skip review changes before applying
33
35
  -s, --simulate Don't apply changes, show commands instead
34
36
  -f, --file DLFILE Use input file (be careful)
35
37
  -o, --only pattern files, dirs or regexp (without delimiters)
36
38
  e.g.: dle ~/Movies -o "(mov|mkv|avi)$"
39
+ -a, --apply NAMED,FILTERS Filter collection with saved filters
40
+ -q, --query Filter collection with ruby code
41
+ -e, --edit FILTER Edit/create filter scripts
42
+
43
+ # General options
37
44
  -m, --monochrome Don't colorize output
38
45
  -h, --help Shows this help
39
46
  -v, --version Shows version and other info
40
47
  -z Do not check for updates on GitHub (with -v/--version)
48
+ --console Start console to play around with the collection (requires pry)
49
+
50
+
51
+
41
52
 
42
53
  Change into a directory you want to work with and invoke DLE:
43
54
 
@@ -62,6 +73,23 @@ Your editor will open with a list of your directory structure which you can edit
62
73
  * Copy
63
74
  * Delete
64
75
 
76
+
77
+ ### Filters
78
+
79
+ You can easily filter your movies with Ruby. It's not hard, just look at these examples.
80
+
81
+ ```ruby
82
+ # Filter by name, for regex see http://rubular.com
83
+ @fs.index.reject! {|inode, node| node.basename =~ /whatever/i }
84
+
85
+ # Only big files
86
+ @fs.index.select! {|inode, node| node.file? && node.size > 1024 * 1024 * 10 }
87
+
88
+ # Sort by size
89
+ @fs.index.replace Hash[@fs.index.sort_by{|inode, node| node.size }.reverse]
90
+ ```
91
+
92
+
65
93
  ## Caveats
66
94
 
67
95
  DLE relies on inode values, do not use with hardlinks! This may lead to unexpected file operations or data loss!
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.7
1
+ 1.0.1
data/lib/dle.rb CHANGED
@@ -18,6 +18,7 @@ require "dle/filesystem/destructive"
18
18
  require "dle/filesystem/softnode"
19
19
  require "dle/filesystem/node"
20
20
  require "dle/filesystem"
21
+ require "dle/application/filter"
21
22
  require "dle/application/dispatch"
22
23
  require "dle/application"
23
24
 
@@ -8,6 +8,7 @@ module Dle
8
8
  class Application
9
9
  attr_reader :opts
10
10
  include Dispatch
11
+ include Filter
11
12
  include Helpers
12
13
 
13
14
  # =========
@@ -30,27 +31,47 @@ module Dle
30
31
  @editor = which_editor
31
32
  @opts = {
32
33
  dispatch: :index,
34
+ console: false,
33
35
  dotfiles: false,
34
36
  check_for_updates: true,
35
37
  review: true,
38
+ select_scripts: false,
36
39
  simulate: false,
40
+ query: false,
37
41
  }
38
42
  yield(self)
39
43
  end
40
44
 
45
+ def collection_size_changed cold, cnew, reason = nil
46
+ if cold != cnew
47
+ log(
48
+ "We have filtered " << c("#{cnew}", :magenta) <<
49
+ c(" nodes") << c(" (#{cnew - cold})", :red) <<
50
+ c(" from originally ") << c("#{cold}", :magenta) <<
51
+ (reason ? c(" (#{reason})", :blue) : "")
52
+ )
53
+ end
54
+ end
55
+
41
56
  def parse_params
42
57
  @optparse = OptionParser.new do |opts|
43
58
  opts.banner = "Usage: dle [options] base_directory"
44
59
 
60
+ opts.separator(c "# Application options", :blue)
45
61
  opts.on("-d", "--dotfiles", "Include dotfiles (unix invisible)") { @opts[:dotfiles] = true }
46
62
  opts.on("-r", "--skip-review", "Skip review changes before applying") { @opts[:review] = false }
47
63
  opts.on("-s", "--simulate", "Don't apply changes, show commands instead") { @opts[:simulate] = true ; @opts[:review] = false }
48
64
  opts.on("-f", "--file DLFILE", "Use input file (be careful)") {|f| @opts[:input_file] = f }
49
65
  opts.on("-o", "--only pattern", c("files", :blue) << c(", ") << c("dirs", :blue) << c(" or regexp (without delimiters)"), " e.g.:" << c(%{ dle ~/Movies -o "(mov|mkv|avi)$"}, :blue)) {|p| @opts[:pattern] = p }
66
+ opts.on("-a", "--apply NAMED,FILTERS", Array, "Filter collection with saved filters") {|s| @opts[:select_scripts] = s }
67
+ opts.on("-q", "--query", "Filter collection with ruby code") { @opts[:query] = true }
68
+ opts.on("-e", "--edit FILTER", "Edit/create filter scripts") {|s| @opts[:dispatch] = :edit_script; @opts[:select_script] = s }
69
+ opts.separator("\n" << c("# General options", :blue))
50
70
  opts.on("-m", "--monochrome", "Don't colorize output") { logger.colorize = false }
51
71
  opts.on("-h", "--help", "Shows this help") { @opts[:dispatch] = :help }
52
72
  opts.on("-v", "--version", "Shows version and other info") { @opts[:dispatch] = :info }
53
73
  opts.on("-z", "Do not check for updates on GitHub (with -v/--version)") { @opts[:check_for_updates] = false }
74
+ opts.on("--console", "Start console to play around with the collection (requires pry)") {|f| @opts[:console] = true }
54
75
  end
55
76
 
56
77
  begin
@@ -14,6 +14,12 @@ module Dle
14
14
  end
15
15
  end
16
16
 
17
+ def dispatch_edit_script
18
+ log("Opening file in editor...")
19
+ f = record_filter(filter_script(@opts[:select_script]))
20
+ log("Saved file " << c(f, :magenta))
21
+ end
22
+
17
23
  def dispatch_help
18
24
  logger.log_without_timestr do
19
25
  @optparse.to_s.split("\n").each(&method(:log))
@@ -96,93 +102,123 @@ module Dle
96
102
  abort("Base directory is empty or not readable", 1) if @fs.index.empty?
97
103
  log("indexed #{c "#{human_number @fs.index.count} nodes", :magenta}") if @fs.index.count > 1000
98
104
 
99
- file = "#{Dir.tmpdir}/#{SecureRandom.urlsafe_base64}"
100
- begin
101
- # read input file or open editor
102
- if @opts[:input_file]
103
- ifile = File.expand_path(@opts[:input_file])
104
- if FileTest.file?(ifile) && FileTest.readable?(ifile)
105
- log "processing file..."
106
- @dlfile = DlFile.parse(ifile)
105
+ if @opts[:console]
106
+ log "You have access to the collection with " << c("@fs", :magenta)
107
+ log "Apply existent select script with " << c("apply_filter(@fs, 'filter_name')", :magenta)
108
+ log "Type " << c("exit", :magenta) << c(" to leave the console.")
109
+ begin
110
+ binding.pry(quiet: true)
111
+ abort c("No changes, nothing to do..."), 0
112
+ rescue NoMethodError => ex
113
+ raise ex unless ex.message["undefined method `pry'"]
114
+ abort c("The pry gem is required to display the console. Please install it: " << c("gem install pry", :blue)), 3
115
+ end
116
+ else
117
+ file = "#{Dir.tmpdir}/#{SecureRandom.urlsafe_base64}"
118
+ begin
119
+ # read input file or open editor
120
+ if @opts[:input_file]
121
+ ifile = File.expand_path(@opts[:input_file])
122
+ if FileTest.file?(ifile) && FileTest.readable?(ifile)
123
+ log "processing file..."
124
+ @dlfile = DlFile.parse(ifile)
125
+ else
126
+ abort "Input file not readable: " << c(ifile, :magenta)
127
+ end
107
128
  else
108
- abort "Input file not readable: " << c(ifile, :magenta)
109
- end
110
- else
111
- FileUtils.mkdir_p(File.dirname(file)) if !FileTest.exist?(File.dirname(file))
112
- if !FileTest.exist?(file) || File.read(file).strip.empty?
113
- notifier do
114
- sleep 3
115
- log "writing result list to file..."
116
- end.perform do
117
- File.open(file, "w") {|f| f.write @fs.to_dlfile }
129
+ old_count = @fs.index.count
130
+ if @opts[:query]
131
+ apply_filter(@fs, record_filter)
132
+ collection_size_changed old_count, @fs.index.count, "custom filter"
133
+ old_count = @fs.index.count
118
134
  end
135
+
136
+ # filter
137
+ (@opts[:select_scripts] || []).each do |filter|
138
+ apply_filter(@fs, filter_script(filter))
139
+ collection_size_changed old_count, @fs.index.count, "filter: #{filter}"
140
+ old_count = @fs.index.count
141
+ end
142
+
143
+ # save file
144
+ FileUtils.mkdir_p(File.dirname(file)) if !FileTest.exist?(File.dirname(file))
145
+ if !FileTest.exist?(file) || File.read(file).strip.empty?
146
+ notifier do
147
+ sleep 3
148
+ log "writing result list to file..."
149
+ end.perform do
150
+ File.open(file, "w") {|f| f.write @fs.to_dlfile }
151
+ end
152
+ end
153
+
154
+ # open editor
155
+ log "open list for editing..."
156
+ open_editor(file)
157
+ log "processing file..."
158
+ @dlfile = DlFile.parse(file)
119
159
  end
120
- log "open list for editing..."
121
- open_editor(file)
122
- log "processing file..."
123
- @dlfile = DlFile.parse(file)
124
- end
125
160
 
126
- # delta changes
127
- @delta = @fs.delta(@dlfile)
161
+ # delta changes
162
+ @delta = @fs.delta(@dlfile)
128
163
 
129
- # no changes
130
- if @delta.all?{|_, v| v.empty? }
131
- abort c("No changes, nothing to do..."), 0
132
- end
164
+ # no changes
165
+ if @delta.all?{|_, v| v.empty? }
166
+ abort c("No changes, nothing to do..."), 0
167
+ end
133
168
 
134
- # review
135
- if @opts[:review]
136
- @delta.each do |action, snodes|
137
- logger.ensure_prefix c("[#{action}]\t", :magenta) do
138
- snodes.each do |snode|
139
- if [:chown, :chmod].include?(action)
140
- log(c("#{snode.node.relative_path} ", :blue) << c(snode.is, :red) << c(" » ") << c(snode.should, :green))
141
- elsif [:cp, :mv].include?(action)
142
- log(c(snode.is, :red) << c(" » ") << c(snode.should, :green))
143
- else
144
- log(c(snode.is, :red) << " (#{snode.snode.mode})")
169
+ # review
170
+ if @opts[:review]
171
+ @delta.each do |action, snodes|
172
+ logger.ensure_prefix c("[#{action}]\t", :magenta) do
173
+ snodes.each do |snode|
174
+ if [:chown, :chmod].include?(action)
175
+ log(c("#{snode.node.relative_path} ", :blue) << c(snode.is, :red) << c(" » ") << c(snode.should, :green))
176
+ elsif [:cp, :mv].include?(action)
177
+ log(c(snode.is, :red) << c(" » ") << c(snode.should, :green))
178
+ else
179
+ log(c(snode.is, :red) << " (#{snode.snode.mode})")
180
+ end
145
181
  end
146
182
  end
147
183
  end
148
- end
149
184
 
150
- answer = ask("Do you want to apply these changes? [yes/no/edit]")
151
- while !["y", "yes", "n", "no", "e", "edit"].include?(answer.downcase)
152
- answer = ask("Please be explicit, yes/no/edit:")
185
+ answer = ask("Do you want to apply these changes? [yes/no/edit]")
186
+ while !["y", "yes", "n", "no", "e", "edit"].include?(answer.downcase)
187
+ answer = ask("Please be explicit, yes/no/edit:")
188
+ end
189
+ raise "retry" if ["e", "edit"].include?(answer.downcase)
190
+ abort("Aborted, nothing changed", 0) if !["y", "yes"].include?(answer.downcase)
153
191
  end
154
- raise "retry" if ["e", "edit"].include?(answer.downcase)
155
- abort("Aborted, nothing changed", 0) if !["y", "yes"].include?(answer.downcase)
192
+ rescue
193
+ $!.message == "retry" ? retry : raise
156
194
  end
157
- rescue
158
- $!.message == "retry" ? retry : raise
159
- end
160
195
 
161
- # apply changes
162
- log "#{@opts[:simulate] ? "Simulating" : "Applying"} changes..."
163
- @fs.opts[:verbose] = false
164
- total_actions = @delta.map{|_, nodes| nodes.count }.inject(&:+)
165
- actions_performed = 0
166
- begin
167
- notifier do
168
- loop do
169
- if BASH_ENABLED
170
- logger.raw("\033]0;#{@opts[:simulate] ? "Simulated" : "Peformed"} #{human_number actions_performed}/#{human_number total_actions} changes\007", :print)
196
+ # apply changes
197
+ log "#{@opts[:simulate] ? "Simulating" : "Applying"} changes..."
198
+ @fs.opts[:verbose] = false
199
+ total_actions = @delta.map{|_, nodes| nodes.count }.inject(&:+)
200
+ actions_performed = 0
201
+ begin
202
+ notifier do
203
+ loop do
204
+ if BASH_ENABLED
205
+ logger.raw("\033]0;#{@opts[:simulate] ? "Simulated" : "Peformed"} #{human_number actions_performed}/#{human_number total_actions} changes\007", :print)
206
+ end
207
+ sleep 1
171
208
  end
172
- sleep 1
173
- end
174
- end.perform do
175
- @delta.each do |action, snodes|
176
- logger.ensure_prefix c("[apply-#{action}]\t", :magenta) do
177
- snodes.each do |snode|
178
- actions_performed += 1
179
- Filesystem::Destructive.new(self, action, @fs, snode).perform
209
+ end.perform do
210
+ @delta.each do |action, snodes|
211
+ logger.ensure_prefix c("[apply-#{action}]\t", :magenta) do
212
+ snodes.each do |snode|
213
+ actions_performed += 1
214
+ Filesystem::Destructive.new(self, action, @fs, snode).perform
215
+ end
180
216
  end
181
217
  end
182
218
  end
219
+ ensure
220
+ log "#{@opts[:simulate] ? "Simulated" : "Peformed"} #{human_number actions_performed} changes..."
183
221
  end
184
- ensure
185
- log "#{@opts[:simulate] ? "Simulated" : "Peformed"} #{human_number actions_performed} changes..."
186
222
  end
187
223
  end
188
224
  end
@@ -0,0 +1,36 @@
1
+ module Dle
2
+ class Application
3
+ module Filter
4
+ def filter_script name
5
+ File.expand_path("~/.dle/#{name}.rb")
6
+ end
7
+
8
+ def apply_filter collection, file
9
+ app = @app
10
+ eval File.read(file), binding, file
11
+ collection
12
+ end
13
+
14
+ def permute_script collection, file = nil
15
+ file ||= "#{Dir.tmpdir}/#{SecureRandom.urlsafe_base64}"
16
+ FileUtils.mkdir(File.dirname(file)) if !File.exist?(File.dirname(file))
17
+ if !File.exist?(file) || File.read(file).strip.empty?
18
+ File.open(file, "w") {|f| f.puts("# Permute your collection, same as with the selector script filters.") }
19
+ end
20
+ system "#{cfg :application, :editor} #{file}"
21
+ eval File.read(file), binding, file
22
+ collection
23
+ end
24
+
25
+ def record_filter file = nil
26
+ file ||= "#{Dir.tmpdir}/#{SecureRandom.urlsafe_base64}.rb"
27
+ FileUtils.mkdir(File.dirname(file)) if !File.exist?(File.dirname(file))
28
+ if !File.exist?(file) || File.read(file).strip.empty?
29
+ FileUtils.cp("#{ROOT}/lib/dle/application/filter.tpl", file)
30
+ end
31
+ open_editor(file)
32
+ file
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ # TIP: Using vim and want to get rid of this example shit?
2
+ # In nav-mode type: 100dd
3
+
4
+ # Hey there,
5
+ # to filter your records you will use Ruby
6
+ # but don't be afraid, it's fairly simple.
7
+ # Just look at the examples and referenced links.
8
+
9
+ # Use ruby methods to narrow down you result set.
10
+ # * http://www.ruby-doc.org/core-2.1.1/File.html
11
+ # * http://www.ruby-doc.org/core-2.1.1/Enumerable.html
12
+
13
+ # ====================================================
14
+ # = Doc (remove, reuse or comment out the examples!) =
15
+ # ====================================================
16
+
17
+ puts "Filtering in #{@fs.base_dir}"
18
+ @fs.index.select! do |inode, node|
19
+ # node has the following methods
20
+ # * dir => source movie directory (e.g. C:/Movies)
21
+ # * relative_path => relative path to @fs.base_dir
22
+ # * mode => file mode
23
+ # * owner => name of the file owner
24
+ # * group => name of the file group
25
+ # * owngrp => owner and group combined by a colon
26
+ # * basename => alias for File#basename
27
+ # * dirname => alias for File#dirname
28
+ # * extname => alias for File#extname
29
+ # * stat => alias for File#stat
30
+ # * size => alias for File#size
31
+ # * directory? => alias for FileTest#directory?
32
+ # * file? => alias for FileTest#file?
33
+ # * symlink? => alias for FileTest#symlink?
34
+
35
+ # The index is NOT NESTED! If you remove a directory node, all sub nodes
36
+ # will still be in the index!
37
+
38
+ # Set break point to interactively call methods from here.
39
+ # See http://pryrepl.org ory type "help" when you are in the REPL.
40
+ # Use exit or exit! to break out of REPL.
41
+ # binding.pry
42
+
43
+ # --------------------------------------------------------------
44
+
45
+ node.directory? && node.basename =~ /^[a-z0-9]+$/
46
+ end
47
+
48
+ # Filter by name, for regex see http://rubular.com
49
+ @fs.index.reject! {|inode, node| node.basename =~ /whatever/i }
50
+
51
+ # Only big files
52
+ @fs.index.select! {|inode, node| node.file? && node.size > 1024 * 1024 * 10 }
53
+
54
+ # Sort by size
55
+ @fs.index.replace Hash[@fs.index.sort_by{|inode, node| node.size }.reverse]
@@ -35,7 +35,7 @@ module Dle
35
35
  [].tap do |r|
36
36
  col_sizes = table.map{|col| col.map(&:to_s).map(&:length).max }
37
37
  headers.map(&:length).each_with_index do |length, header|
38
- col_sizes[header] = [col_sizes[header], length].max
38
+ col_sizes[header] = [col_sizes[header] || 0, length || 0].max
39
39
  end
40
40
 
41
41
  # header
@@ -1,4 +1,4 @@
1
1
  module Dle
2
- VERSION = "0.1.7"
2
+ VERSION = "1.0.1"
3
3
  UPDATE_URL = "https://raw.githubusercontent.com/2called-chaos/dle/master/VERSION"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Pachnit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-11 00:00:00.000000000 Z
11
+ date: 2015-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -77,6 +77,8 @@ files:
77
77
  - lib/dle.rb
78
78
  - lib/dle/application.rb
79
79
  - lib/dle/application/dispatch.rb
80
+ - lib/dle/application/filter.rb
81
+ - lib/dle/application/filter.tpl
80
82
  - lib/dle/dl_file.rb
81
83
  - lib/dle/filesystem.rb
82
84
  - lib/dle/filesystem/destructive.rb
@@ -104,9 +106,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
106
  version: '0'
105
107
  requirements: []
106
108
  rubyforge_project:
107
- rubygems_version: 2.2.2
109
+ rubygems_version: 2.0.14
108
110
  signing_key:
109
111
  specification_version: 4
110
112
  summary: Directory List Edit – Edit file structures in your favorite editor!
111
113
  test_files: []
112
- has_rdoc: