rfd 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/rfd.rb +296 -31
- data/lib/rfd/commands.rb +65 -14
- data/lib/rfd/item.rb +44 -20
- data/lib/rfd/windows.rb +16 -1
- data/rfd.gemspec +4 -3
- data/spec/controller_spec.rb +142 -41
- data/spec/spec_helper.rb +0 -3
- data/spec/testdir/gz1.tar.gz +0 -0
- data/spec/testdir/zip1.zip +0 -0
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1dd36a7cd7b7b8a56e25d704089e6c390935e07
|
4
|
+
data.tar.gz: eb3055ac47eb7a6105fb9a1a0b093323bab1d23a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14187a51b3c5d3ea90b7f4fcbd363170c9cd391e0d4ea7f54f75045de1a34e6dd762c56f61964af537b4a4e83d28325198e794c99c4dd942f8a94645d41a0a92
|
7
|
+
data.tar.gz: d1beb6cd74c3c03c06c02140d2d2680727ab27ac8cb2dfcb93da8659e1f0c23f3c879437701e6f81b8eb90884a298ac23786a166eb4f949c59faa403524fd5e7
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rfd (Ruby on Files
|
1
|
+
# rfd (Ruby on Files & Directories)
|
2
2
|
|
3
3
|
rfd is a terminal based File explorer, inpsired by the legendary freesoft MS-DOS filer, "FD".
|
4
4
|
|
@@ -14,7 +14,7 @@ rfd is a terminal based File explorer, inpsired by the legendary freesoft MS-DOS
|
|
14
14
|
|
15
15
|
## Tested environment
|
16
16
|
|
17
|
-
|
17
|
+
Mac OSX Snow Leopard
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
data/lib/rfd.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'ffi-ncurses'
|
2
|
+
require 'ffi-ncurses/mouse'
|
2
3
|
Curses = FFI::NCurses
|
3
4
|
require 'fileutils'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'rubygems/package'
|
7
|
+
require 'zip'
|
8
|
+
require 'zip/filesystem'
|
4
9
|
require_relative 'rfd/commands'
|
5
10
|
require_relative 'rfd/item'
|
6
11
|
require_relative 'rfd/windows'
|
@@ -20,6 +25,9 @@ module Rfd
|
|
20
25
|
[Curses::COLOR_WHITE, Curses::COLOR_CYAN, Curses::COLOR_MAGENTA, Curses::COLOR_GREEN, Curses::COLOR_RED].each do |c|
|
21
26
|
Curses.init_pair c, c, Curses::COLOR_BLACK
|
22
27
|
end
|
28
|
+
|
29
|
+
Curses.mousemask Curses::ALL_MOUSE_EVENTS | Curses::REPORT_MOUSE_POSITION, nil
|
30
|
+
Curses.extend FFI::NCurses::Mouse
|
23
31
|
end
|
24
32
|
|
25
33
|
# Start the app here!
|
@@ -37,7 +45,7 @@ module Rfd
|
|
37
45
|
class Controller
|
38
46
|
include Rfd::Commands
|
39
47
|
|
40
|
-
attr_reader :header_l, :header_r, :main, :command_line, :items, :displayed_items, :current_row, :current_page, :current_dir
|
48
|
+
attr_reader :header_l, :header_r, :main, :command_line, :items, :displayed_items, :current_row, :current_page, :current_dir, :current_zip
|
41
49
|
|
42
50
|
# :nodoc:
|
43
51
|
def initialize
|
@@ -45,10 +53,12 @@ module Rfd
|
|
45
53
|
@header_l = HeaderLeftWindow.new
|
46
54
|
@header_r = HeaderRightWindow.new
|
47
55
|
@command_line = CommandLineWindow.new
|
56
|
+
@direction, @dir_history = nil, []
|
48
57
|
end
|
49
58
|
|
50
59
|
# The main loop.
|
51
60
|
def run
|
61
|
+
mouse_event = Curses::MEVENT.new
|
52
62
|
loop do
|
53
63
|
begin
|
54
64
|
case (c = Curses.getch)
|
@@ -77,6 +87,14 @@ module Rfd
|
|
77
87
|
else
|
78
88
|
debug "key: #{c}" if ENV['DEBUG']
|
79
89
|
end
|
90
|
+
when Curses::KEY_MOUSE
|
91
|
+
if Curses.getmouse(mouse_event) == Curses::OK
|
92
|
+
if Curses.BUTTON_CLICK(mouse_event[:bstate], 1) > 0
|
93
|
+
click y: mouse_event[:y], x: mouse_event[:x]
|
94
|
+
elsif Curses.BUTTON_DOUBLE_CLICK(mouse_event[:bstate], 1) > 0
|
95
|
+
double_click y: mouse_event[:y], x: mouse_event[:x]
|
96
|
+
end
|
97
|
+
end
|
80
98
|
else
|
81
99
|
debug "key: #{c}" if ENV['DEBUG']
|
82
100
|
end
|
@@ -143,18 +161,29 @@ module Rfd
|
|
143
161
|
|
144
162
|
# Change the current directory.
|
145
163
|
def cd(dir, pushd: true)
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
164
|
+
if dir.is_a?(Item) && dir.zip?
|
165
|
+
cd_into_zip dir
|
166
|
+
else
|
167
|
+
target = expand_path dir
|
168
|
+
if File.readable? target
|
169
|
+
Dir.chdir target
|
170
|
+
@dir_history << current_dir if current_dir && pushd
|
171
|
+
@current_dir, @current_page, @current_row, @current_zip = target, 0, nil, nil
|
172
|
+
main.activate_pane 0
|
173
|
+
end
|
152
174
|
end
|
153
175
|
end
|
154
176
|
|
177
|
+
def cd_into_zip(zipfile)
|
178
|
+
@current_zip = zipfile
|
179
|
+
@dir_history << current_dir if current_dir
|
180
|
+
@current_dir, @current_page, @current_row = zipfile.path, 0, nil
|
181
|
+
main.activate_pane 0
|
182
|
+
end
|
183
|
+
|
155
184
|
# cd to the previous directory.
|
156
185
|
def popd
|
157
|
-
if
|
186
|
+
if @dir_history.any?
|
158
187
|
cd @dir_history.pop, pushd: false
|
159
188
|
ls
|
160
189
|
end
|
@@ -163,12 +192,12 @@ module Rfd
|
|
163
192
|
# Fetch files from current directory.
|
164
193
|
# Then update each windows reflecting the newest information.
|
165
194
|
def ls
|
166
|
-
|
195
|
+
fetch_items_from_filesystem_or_zip
|
167
196
|
sort_items_according_to_current_direction
|
168
197
|
|
169
198
|
@current_page ||= 0
|
170
199
|
draw_items
|
171
|
-
move_cursor current_row
|
200
|
+
move_cursor (current_row ? [current_row, items.size - 1].min : nil)
|
172
201
|
|
173
202
|
draw_marked_items
|
174
203
|
draw_total_items
|
@@ -200,18 +229,46 @@ module Rfd
|
|
200
229
|
# Change the file permission of the selected files and directories.
|
201
230
|
#
|
202
231
|
# ==== Parameters
|
203
|
-
# * +mode+ - Unix chmod string (e.g. +w, g-r)
|
204
|
-
#
|
205
|
-
#TODO: accept number forms such as 755, 0644
|
232
|
+
# * +mode+ - Unix chmod string (e.g. +w, g-r, 755, 0644)
|
206
233
|
def chmod(mode = nil)
|
207
234
|
return unless mode
|
235
|
+
begin
|
236
|
+
Integer mode
|
237
|
+
mode = Integer mode.size == 3 ? "0#{mode}" : mode
|
238
|
+
rescue ArgumentError
|
239
|
+
end
|
208
240
|
FileUtils.chmod mode, selected_items.map(&:path)
|
209
241
|
ls
|
210
242
|
end
|
211
243
|
|
212
|
-
#
|
213
|
-
|
214
|
-
|
244
|
+
# Change the file owner of the selected files and directories.
|
245
|
+
#
|
246
|
+
# ==== Parameters
|
247
|
+
# * +user_and_group+ - user name and group name separated by : (e.g. alice, nobody:nobody, :admin)
|
248
|
+
def chown(user_and_group)
|
249
|
+
return unless user_and_group
|
250
|
+
user, group = user_and_group.split(':').map {|s| s == '' ? nil : s}
|
251
|
+
FileUtils.chown user, group, selected_items.map(&:path)
|
252
|
+
ls
|
253
|
+
end
|
254
|
+
|
255
|
+
# Fetch files from current directory or current .zip file.
|
256
|
+
def fetch_items_from_filesystem_or_zip
|
257
|
+
unless in_zip?
|
258
|
+
@items = Dir.foreach(current_dir).map {|fn|
|
259
|
+
stat = File.lstat File.join(current_dir, fn)
|
260
|
+
Item.new dir: current_dir, name: fn, stat: stat, window_width: maxx
|
261
|
+
}.to_a
|
262
|
+
else
|
263
|
+
@items = [Item.new(dir: current_dir, name: '.', stat: File.stat(current_dir), window_width: maxx),
|
264
|
+
Item.new(dir: current_dir, name: '..', stat: File.stat(File.dirname(current_dir)), window_width: maxx)]
|
265
|
+
zf = Zip::File.new current_dir
|
266
|
+
zf.each {|entry|
|
267
|
+
next if entry.name_is_directory?
|
268
|
+
stat = zf.file.stat entry.name
|
269
|
+
@items << Item.new(dir: current_dir, name: entry.name, stat: stat, window_width: maxx)
|
270
|
+
}
|
271
|
+
end
|
215
272
|
end
|
216
273
|
|
217
274
|
# Focus at the first file or directory of which name starts with the given String.
|
@@ -223,7 +280,7 @@ module Rfd
|
|
223
280
|
# Focus at the last file or directory of which name starts with the given String.
|
224
281
|
def find_reverse(str)
|
225
282
|
index = items.reverse.index {|i| i.name.start_with? str}
|
226
|
-
move_cursor items.
|
283
|
+
move_cursor items.size - index - 1 if index
|
227
284
|
end
|
228
285
|
|
229
286
|
# Width of the currently active pane.
|
@@ -288,7 +345,7 @@ module Rfd
|
|
288
345
|
# .*\.pdf$ : Search PDF files
|
289
346
|
def grep(pattern = '.*')
|
290
347
|
regexp = Regexp.new(pattern)
|
291
|
-
|
348
|
+
fetch_items_from_filesystem_or_zip
|
292
349
|
@items = items.shift(2) + items.select {|i| i.name =~ regexp}
|
293
350
|
sort_items_according_to_current_direction
|
294
351
|
switch_page 0
|
@@ -299,15 +356,29 @@ module Rfd
|
|
299
356
|
|
300
357
|
# Copy selected files and directories to the destination.
|
301
358
|
def cp(dest)
|
302
|
-
|
303
|
-
|
359
|
+
unless in_zip?
|
360
|
+
src = (m = marked_items).any? ? m.map(&:path) : current_item.path
|
361
|
+
FileUtils.cp_r src, expand_path(dest)
|
362
|
+
else
|
363
|
+
raise 'cping multiple items in .zip is not supported.' if selected_items.size > 1
|
364
|
+
Zip::File.open(current_zip.path) do |zip|
|
365
|
+
entry = zip.find_entry(selected_items.first.name).dup
|
366
|
+
entry.name, entry.name_length = dest, dest.size
|
367
|
+
zip.instance_variable_get(:@entry_set) << entry
|
368
|
+
end
|
369
|
+
end
|
304
370
|
ls
|
305
371
|
end
|
306
372
|
|
307
373
|
# Move selected files and directories to the destination.
|
308
374
|
def mv(dest)
|
309
|
-
|
310
|
-
|
375
|
+
unless in_zip?
|
376
|
+
src = (m = marked_items).any? ? m.map(&:path) : current_item.path
|
377
|
+
FileUtils.mv src, expand_path(dest)
|
378
|
+
else
|
379
|
+
raise 'mving multiple items in .zip is not supported.' if selected_items.size > 1
|
380
|
+
rename "#{selected_items.first.name}/#{dest}"
|
381
|
+
end
|
311
382
|
ls
|
312
383
|
end
|
313
384
|
|
@@ -318,22 +389,165 @@ module Rfd
|
|
318
389
|
def rename(pattern)
|
319
390
|
from, to = pattern.split '/'
|
320
391
|
from = Regexp.new from
|
321
|
-
|
322
|
-
|
323
|
-
|
392
|
+
unless in_zip?
|
393
|
+
selected_items.each do |item|
|
394
|
+
name = item.name.gsub from, to
|
395
|
+
FileUtils.mv item.path, File.join(current_dir, name)
|
396
|
+
end
|
397
|
+
else
|
398
|
+
Zip::File.open(current_zip.path) do |zip|
|
399
|
+
selected_items.each do |item|
|
400
|
+
name = item.name.gsub from, to
|
401
|
+
zip.rename item.name, name
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
ls
|
406
|
+
end
|
407
|
+
|
408
|
+
# Soft delete selected files and directories.
|
409
|
+
#
|
410
|
+
# If the OS is not OSX, performs the same as `delete` command.
|
411
|
+
def trash
|
412
|
+
unless in_zip?
|
413
|
+
if osx?
|
414
|
+
FileUtils.mv selected_items.map(&:path), File.expand_path('~/.Trash/')
|
415
|
+
else
|
416
|
+
#TODO support other OS
|
417
|
+
FileUtils.rm_rf selected_items.map(&:path)
|
418
|
+
end
|
419
|
+
else
|
420
|
+
return unless ask %Q[Trashing zip entries is not supported. Actually the files will be deleted. Are you sure want to proceed? (y/n)]
|
421
|
+
delete
|
324
422
|
end
|
423
|
+
@current_row -= selected_items.count {|i| i.index <= current_row}
|
424
|
+
ls
|
425
|
+
end
|
426
|
+
|
427
|
+
# Delete selected files and directories.
|
428
|
+
def delete
|
429
|
+
unless in_zip?
|
430
|
+
FileUtils.rm_rf selected_items.map(&:path)
|
431
|
+
else
|
432
|
+
Zip::File.open(current_zip.path) do |zip|
|
433
|
+
zip.select {|e| selected_items.map(&:name).include? e.to_s}.each do |entry|
|
434
|
+
if entry.name_is_directory?
|
435
|
+
zip.dir.delete entry.to_s
|
436
|
+
else
|
437
|
+
zip.file.delete entry.to_s
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
@current_row -= selected_items.count {|i| i.index <= current_row}
|
325
443
|
ls
|
326
444
|
end
|
327
445
|
|
328
446
|
# Create a new directory.
|
329
447
|
def mkdir(dir)
|
330
|
-
|
448
|
+
unless in_zip?
|
449
|
+
FileUtils.mkdir_p File.join(current_dir, dir)
|
450
|
+
else
|
451
|
+
Zip::File.open(current_zip.path) do |zip|
|
452
|
+
zip.dir.mkdir dir
|
453
|
+
end
|
454
|
+
end
|
331
455
|
ls
|
332
456
|
end
|
333
457
|
|
334
458
|
# Create a new empty file.
|
335
459
|
def touch(filename)
|
336
|
-
|
460
|
+
unless in_zip?
|
461
|
+
FileUtils.touch File.join(current_dir, filename)
|
462
|
+
else
|
463
|
+
Zip::File.open(current_zip.path) do |zip|
|
464
|
+
# zip.file.open(filename, 'w') {|_f| } #HAXX this code creates an unneeded temporary file
|
465
|
+
zip.instance_variable_get(:@entry_set) << Zip::Entry.new(current_zip.path, filename)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
ls
|
469
|
+
end
|
470
|
+
|
471
|
+
# Create a symlink to the current file or directory.
|
472
|
+
def symlink(name)
|
473
|
+
FileUtils.ln_s current_item.path, name
|
474
|
+
ls
|
475
|
+
end
|
476
|
+
|
477
|
+
# Copy selected files and directories' path into clipboard on OSX.
|
478
|
+
def clipboard
|
479
|
+
IO.popen('pbcopy', 'w') {|f| f << selected_items.map(&:path).join(' ')} if osx?
|
480
|
+
end
|
481
|
+
|
482
|
+
# Archive selected files and directories into a .zip file.
|
483
|
+
def zip(zipfile_name)
|
484
|
+
return unless zipfile_name
|
485
|
+
zipfile_name << '.zip' unless zipfile_name.end_with? '.zip'
|
486
|
+
|
487
|
+
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
|
488
|
+
selected_items.each do |item|
|
489
|
+
next if item.symlink?
|
490
|
+
if item.directory?
|
491
|
+
Dir[File.join(item.path, '**/**')].each do |file|
|
492
|
+
zipfile.add file.sub("#{current_dir}/", ''), file
|
493
|
+
end
|
494
|
+
else
|
495
|
+
zipfile.add item.name, item.path
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
ls
|
500
|
+
end
|
501
|
+
|
502
|
+
# Unarchive .zip and .tar.gz files within selected files and directories into current_directory.
|
503
|
+
def unarchive
|
504
|
+
unless in_zip?
|
505
|
+
zips, gzs = selected_items.partition(&:zip?).tap {|z, others| break [z, *others.partition(&:gz?)]}
|
506
|
+
zips.each do |item|
|
507
|
+
FileUtils.mkdir_p File.join(current_dir, item.basename)
|
508
|
+
Zip::File.open(item.path) do |zip|
|
509
|
+
zip.each do |entry|
|
510
|
+
FileUtils.mkdir_p File.join(File.join(item.basename, File.dirname(entry.to_s)))
|
511
|
+
zip.extract(entry, File.join(item.basename, entry.to_s)) { true }
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
gzs.each do |item|
|
516
|
+
Zlib::GzipReader.open(item.path) do |gz|
|
517
|
+
Gem::Package::TarReader.new(gz) do |tar|
|
518
|
+
dest_dir = File.join current_dir, (gz.orig_name || item.basename).sub(/\.tar$/, '')
|
519
|
+
tar.each do |entry|
|
520
|
+
dest = nil
|
521
|
+
if entry.full_name == '././@LongLink'
|
522
|
+
dest = File.join dest_dir, entry.read.strip
|
523
|
+
next
|
524
|
+
end
|
525
|
+
dest ||= File.join dest_dir, entry.full_name
|
526
|
+
if entry.directory?
|
527
|
+
FileUtils.mkdir_p dest, :mode => entry.header.mode
|
528
|
+
elsif entry.file?
|
529
|
+
FileUtils.mkdir_p dest_dir
|
530
|
+
File.open(dest, 'wb') {|f| f.print entry.read}
|
531
|
+
FileUtils.chmod entry.header.mode, dest
|
532
|
+
elsif entry.header.typeflag == '2' # symlink
|
533
|
+
File.symlink entry.header.linkname, dest
|
534
|
+
end
|
535
|
+
unless Dir.exist? dest_dir
|
536
|
+
FileUtils.mkdir_p dest_dir
|
537
|
+
File.open(File.join(dest_dir, gz.orig_name || item.basename), 'wb') {|f| f.print gz.read}
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
else
|
544
|
+
Zip::File.open(current_zip.path) do |zip|
|
545
|
+
zip.select {|e| selected_items.map(&:name).include? e.to_s}.each do |entry|
|
546
|
+
FileUtils.mkdir_p File.join(current_zip.dir, current_zip.basename, File.dirname(entry.to_s))
|
547
|
+
zip.extract(entry, File.join(current_zip.dir, current_zip.basename, entry.to_s)) { true }
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
337
551
|
ls
|
338
552
|
end
|
339
553
|
|
@@ -349,7 +563,7 @@ module Rfd
|
|
349
563
|
|
350
564
|
# Number of pages in the current directory.
|
351
565
|
def total_pages
|
352
|
-
items.
|
566
|
+
items.size / max_items + 1
|
353
567
|
end
|
354
568
|
|
355
569
|
# Move to the given page number.
|
@@ -426,15 +640,54 @@ module Rfd
|
|
426
640
|
def edit
|
427
641
|
execute_external_command do
|
428
642
|
editor = ENV['EDITOR'] || 'vim'
|
429
|
-
|
643
|
+
unless in_zip?
|
644
|
+
system %Q[#{editor} "#{current_item.path}"]
|
645
|
+
else
|
646
|
+
begin
|
647
|
+
tmpdir, tmpfile_name = nil
|
648
|
+
Zip::File.open(current_zip.path) do |zip|
|
649
|
+
tmpdir = Dir.mktmpdir
|
650
|
+
FileUtils.mkdir_p File.join(tmpdir, File.dirname(current_item.name))
|
651
|
+
tmpfile_name = File.join(tmpdir, current_item.name)
|
652
|
+
File.open(tmpfile_name, 'w') {|f| f.puts zip.file.read(current_item.name)}
|
653
|
+
system %Q[#{editor} "#{tmpfile_name}"]
|
654
|
+
zip.add(current_item.name, tmpfile_name) { true }
|
655
|
+
end
|
656
|
+
ls
|
657
|
+
ensure
|
658
|
+
FileUtils.remove_entry_secure tmpdir if tmpdir
|
659
|
+
end
|
660
|
+
end
|
430
661
|
end
|
431
662
|
end
|
432
663
|
|
433
664
|
# Open current file or directory with the viewer.
|
434
665
|
def view
|
666
|
+
pager = ENV['PAGER'] || 'less'
|
435
667
|
execute_external_command do
|
436
|
-
|
437
|
-
|
668
|
+
unless in_zip?
|
669
|
+
system %Q[#{pager} "#{current_item.path}"]
|
670
|
+
else
|
671
|
+
begin
|
672
|
+
tmpdir, tmpfile_name = nil
|
673
|
+
Zip::File.open(current_zip.path) do |zip|
|
674
|
+
tmpdir = Dir.mktmpdir
|
675
|
+
FileUtils.mkdir_p File.join(tmpdir, File.dirname(current_item.name))
|
676
|
+
tmpfile_name = File.join(tmpdir, current_item.name)
|
677
|
+
File.open(tmpfile_name, 'w') {|f| f.puts zip.file.read(current_item.name)}
|
678
|
+
end
|
679
|
+
system %Q[#{pager} "#{tmpfile_name}"]
|
680
|
+
ensure
|
681
|
+
FileUtils.remove_entry_secure tmpdir if tmpdir
|
682
|
+
end
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
def move_cursor_by_click(y: nil, x: nil)
|
688
|
+
if (idx = main.pane_index_at(y: y, x: x))
|
689
|
+
row = current_page * max_items + main.maxy * idx + y - main.begy
|
690
|
+
move_cursor row if (row >= 0) && (row < items.size)
|
438
691
|
end
|
439
692
|
end
|
440
693
|
|
@@ -449,6 +702,18 @@ module Rfd
|
|
449
702
|
Curses.refresh
|
450
703
|
end
|
451
704
|
|
705
|
+
def expand_path(path)
|
706
|
+
File.expand_path path.is_a?(Rfd::Item) ? path.path : path.start_with?('/') || path.start_with?('~') ? path : current_dir ? File.join(current_dir, path) : path
|
707
|
+
end
|
708
|
+
|
709
|
+
def osx?
|
710
|
+
@_osx ||= RbConfig::CONFIG['host_os'] =~ /darwin/
|
711
|
+
end
|
712
|
+
|
713
|
+
def in_zip?
|
714
|
+
@current_zip
|
715
|
+
end
|
716
|
+
|
452
717
|
def debug(str)
|
453
718
|
header_r.debug str
|
454
719
|
end
|
data/lib/rfd/commands.rb
CHANGED
@@ -10,13 +10,11 @@ module Rfd
|
|
10
10
|
process_command_line preset_command: 'cp'
|
11
11
|
end
|
12
12
|
|
13
|
-
# Soft "d"elete (actually mv to the trash folder) selected files and directories.
|
13
|
+
# Soft "d"elete (actually mv to the trash folder on OSX) selected files and directories.
|
14
14
|
def d
|
15
15
|
if selected_items.any?
|
16
|
-
if ask %Q[Are
|
17
|
-
|
18
|
-
@current_row -= selected_items.count {|i| i.index <= current_row}
|
19
|
-
ls
|
16
|
+
if ask %Q[Are you sure want to trash #{selected_items.one? ? selected_items.first.name : "these #{selected_items.size} files"}? (y/n)]
|
17
|
+
trash
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -26,7 +24,7 @@ module Rfd
|
|
26
24
|
edit
|
27
25
|
end
|
28
26
|
|
29
|
-
# "f"ind the first
|
27
|
+
# "f"ind the first file or directory of which name starts with the given String.
|
30
28
|
def f
|
31
29
|
process_command_line preset_command: 'find'
|
32
30
|
end
|
@@ -66,12 +64,16 @@ module Rfd
|
|
66
64
|
|
67
65
|
# "o"pen selected files and directories with the OS "open" command.
|
68
66
|
def o
|
69
|
-
|
67
|
+
if selected_items.any?
|
68
|
+
system "open #{selected_items.map {|i| %Q["#{i.path}"]}.join(' ')}"
|
69
|
+
elsif %w(. ..).include? current_item.name
|
70
|
+
system %Q[open "#{current_item.path}"]
|
71
|
+
end
|
70
72
|
end
|
71
73
|
|
72
74
|
# "q"uit the app.
|
73
75
|
def q
|
74
|
-
raise StopIteration if ask 'Are
|
76
|
+
raise StopIteration if ask 'Are you sure want to exit? (y/n)'
|
75
77
|
end
|
76
78
|
|
77
79
|
# "r"ename selected files and directories.
|
@@ -89,23 +91,41 @@ module Rfd
|
|
89
91
|
process_command_line preset_command: 'touch'
|
90
92
|
end
|
91
93
|
|
94
|
+
# "u"narchive .zip and .tar.gz files within selected files and directories into current_directory.
|
95
|
+
def u
|
96
|
+
unarchive
|
97
|
+
end
|
98
|
+
|
92
99
|
# "o"pen selected files and directories with the viewer.
|
93
100
|
def v
|
94
101
|
view
|
95
102
|
end
|
96
103
|
|
104
|
+
# Change o"w"ner of selected files and directories.
|
105
|
+
def w
|
106
|
+
process_command_line preset_command: 'chown'
|
107
|
+
end
|
108
|
+
|
109
|
+
# Archive selected files and directories into a "z"ip file.
|
110
|
+
def z
|
111
|
+
process_command_line preset_command: 'zip'
|
112
|
+
end
|
113
|
+
|
114
|
+
# "C"opy paths of selected files and directory to the "C"lipboard.
|
115
|
+
def C
|
116
|
+
clipboard
|
117
|
+
end
|
118
|
+
|
97
119
|
# Hard "d"elete selected files and directories.
|
98
120
|
def D
|
99
121
|
if selected_items.any?
|
100
|
-
if ask %Q[Are
|
101
|
-
|
102
|
-
@current_row -= selected_items.count {|i| i.index <= current_row}
|
103
|
-
ls
|
122
|
+
if ask %Q[Are you sure want to delete #{selected_items.one? ? selected_items.first.name : "these #{selected_items.size} files"}? (y/n)]
|
123
|
+
delete
|
104
124
|
end
|
105
125
|
end
|
106
126
|
end
|
107
127
|
|
108
|
-
# "f"ind the last
|
128
|
+
# "f"ind the last file or directory of which name starts with the given String.
|
109
129
|
def F
|
110
130
|
process_command_line preset_command: 'find_reverse'
|
111
131
|
end
|
@@ -130,6 +150,19 @@ module Rfd
|
|
130
150
|
move_cursor current_page * max_items + displayed_items.size / 2
|
131
151
|
end
|
132
152
|
|
153
|
+
# "O"pen terminal here.
|
154
|
+
def O
|
155
|
+
dir = current_item.directory? ? current_item.path : current_dir
|
156
|
+
system %Q[osascript -e 'tell app "Terminal"
|
157
|
+
do script "cd #{dir}"
|
158
|
+
end tell'] if osx?
|
159
|
+
end
|
160
|
+
|
161
|
+
# "S"ymlink the current file or directory
|
162
|
+
def S
|
163
|
+
process_command_line preset_command: 'symlink'
|
164
|
+
end
|
165
|
+
|
133
166
|
# Mark or unmark "a"ll files and directories.
|
134
167
|
def ctrl_a
|
135
168
|
mark = marked_items.size != (items.size - 2) # exclude . and ..
|
@@ -207,7 +240,13 @@ module Rfd
|
|
207
240
|
|
208
241
|
# cd into a directory, or view a file.
|
209
242
|
def enter
|
210
|
-
if current_item.
|
243
|
+
if current_item.name == '.' # do nothing
|
244
|
+
elsif current_item.name == '..'
|
245
|
+
cd '..'
|
246
|
+
ls
|
247
|
+
elsif in_zip?
|
248
|
+
v
|
249
|
+
elsif current_item.directory? || current_item.zip?
|
211
250
|
cd current_item
|
212
251
|
ls
|
213
252
|
else
|
@@ -230,5 +269,17 @@ module Rfd
|
|
230
269
|
ls
|
231
270
|
end
|
232
271
|
end
|
272
|
+
|
273
|
+
# Move cursor position by mouse click.
|
274
|
+
def click(y: nil, x: nil)
|
275
|
+
move_cursor_by_click y: y, x: x
|
276
|
+
end
|
277
|
+
|
278
|
+
# Move cursor position and enter
|
279
|
+
def double_click(y: nil, x: nil)
|
280
|
+
if move_cursor_by_click y: y, x: x
|
281
|
+
enter
|
282
|
+
end
|
283
|
+
end
|
233
284
|
end
|
234
285
|
end
|
data/lib/rfd/item.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Rfd
|
2
2
|
class Item
|
3
3
|
include Comparable
|
4
|
-
attr_reader :name
|
4
|
+
attr_reader :name, :dir, :stat
|
5
5
|
attr_accessor :index
|
6
6
|
|
7
|
-
def initialize(dir: nil, name: nil, window_width: nil)
|
8
|
-
@dir, @name, @window_width, @marked = dir, name, window_width, false
|
7
|
+
def initialize(dir: nil, name: nil, stat: nil, window_width: nil)
|
8
|
+
@dir, @name, @stat, @window_width, @marked = dir, name, stat, window_width, false
|
9
9
|
end
|
10
10
|
|
11
11
|
def path
|
@@ -35,20 +35,12 @@ module Rfd
|
|
35
35
|
if symlink?
|
36
36
|
mb_left n, @window_width - 16
|
37
37
|
else
|
38
|
-
"#{mb_left(basename, @window_width - 16 - extname.
|
38
|
+
"#{mb_left(basename, @window_width - 16 - extname.size)}…#{extname}"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def lstat
|
45
|
-
@lstat ||= File.lstat path
|
46
|
-
end
|
47
|
-
|
48
|
-
def stat
|
49
|
-
@stat ||= File.stat path
|
50
|
-
end
|
51
|
-
|
52
44
|
def color
|
53
45
|
if symlink?
|
54
46
|
Curses::COLOR_MAGENTA
|
@@ -64,7 +56,7 @@ module Rfd
|
|
64
56
|
end
|
65
57
|
|
66
58
|
def size
|
67
|
-
directory? ? 0 :
|
59
|
+
directory? ? 0 : stat.size
|
68
60
|
end
|
69
61
|
|
70
62
|
def size_or_dir
|
@@ -72,20 +64,20 @@ module Rfd
|
|
72
64
|
end
|
73
65
|
|
74
66
|
def atime
|
75
|
-
|
67
|
+
stat.atime.strftime('%Y-%m-%d %H:%M:%S')
|
76
68
|
end
|
77
69
|
|
78
70
|
def ctime
|
79
|
-
|
71
|
+
stat.ctime.strftime('%Y-%m-%d %H:%M:%S')
|
80
72
|
end
|
81
73
|
|
82
74
|
def mtime
|
83
|
-
|
75
|
+
stat.mtime.strftime('%Y-%m-%d %H:%M:%S')
|
84
76
|
end
|
85
77
|
|
86
78
|
def mode
|
87
79
|
@mode ||= begin
|
88
|
-
m =
|
80
|
+
m = stat.mode
|
89
81
|
ret = directory? ? 'd' : symlink? ? 'l' : '-'
|
90
82
|
[(m & 0700) / 64, (m & 070) / 8, m & 07].inject(ret) do |str, s|
|
91
83
|
str << "#{s & 4 == 4 ? 'r' : '-'}#{s & 2 == 2 ? 'w' : '-'}#{s & 1 == 1 ? 'x' : '-'}"
|
@@ -104,11 +96,15 @@ module Rfd
|
|
104
96
|
end
|
105
97
|
|
106
98
|
def directory?
|
107
|
-
|
99
|
+
@directory ||= if symlink?
|
100
|
+
File.stat(path).directory?
|
101
|
+
else
|
102
|
+
stat.directory?
|
103
|
+
end
|
108
104
|
end
|
109
105
|
|
110
106
|
def symlink?
|
111
|
-
|
107
|
+
stat.symlink?
|
112
108
|
end
|
113
109
|
|
114
110
|
def hidden?
|
@@ -116,7 +112,35 @@ module Rfd
|
|
116
112
|
end
|
117
113
|
|
118
114
|
def executable?
|
119
|
-
|
115
|
+
stat.executable?
|
116
|
+
end
|
117
|
+
|
118
|
+
def zip?
|
119
|
+
@zip_ ||= begin
|
120
|
+
if directory?
|
121
|
+
false
|
122
|
+
elsif symlink?
|
123
|
+
File.binread(target, 4).unpack('V').first == 0x04034b50
|
124
|
+
else
|
125
|
+
File.binread(path, 4).unpack('V').first == 0x04034b50
|
126
|
+
end
|
127
|
+
rescue
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def gz?
|
133
|
+
@gz_ ||= begin
|
134
|
+
if directory?
|
135
|
+
false
|
136
|
+
elsif symlink?
|
137
|
+
File.binread(target, 2).unpack('n').first == 0x1f8b
|
138
|
+
else
|
139
|
+
File.binread(path, 2).unpack('n').first == 0x1f8b
|
140
|
+
end
|
141
|
+
rescue
|
142
|
+
false
|
143
|
+
end
|
120
144
|
end
|
121
145
|
|
122
146
|
def target
|
data/lib/rfd/windows.rb
CHANGED
@@ -126,6 +126,13 @@ module Rfd
|
|
126
126
|
@current_index = index if index < @panes.size
|
127
127
|
end
|
128
128
|
|
129
|
+
def get_index_by_point(y: nil, x: nil)
|
130
|
+
@panes.each.with_index do |p, i|
|
131
|
+
return i if include_point? pane: p, y: y, x: x
|
132
|
+
end if y && x
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
129
136
|
def size
|
130
137
|
@panes.size
|
131
138
|
end
|
@@ -133,6 +140,10 @@ module Rfd
|
|
133
140
|
def close_all
|
134
141
|
@panes.each {|p| Curses.delwin p}
|
135
142
|
end
|
143
|
+
|
144
|
+
def include_point?(pane: pane, y: nil, x: nil)
|
145
|
+
(y >= Curses.getbegy(pane)) && (Curses.getbegy(pane) + Curses.getmaxy(pane) > y) && (x >= Curses.getbegx(pane)) && (Curses.getbegx(pane) + Curses.getmaxx(pane) > x)
|
146
|
+
end
|
136
147
|
end
|
137
148
|
|
138
149
|
def initialize(dir = '.')
|
@@ -155,6 +166,10 @@ module Rfd
|
|
155
166
|
@panes.activate num
|
156
167
|
end
|
157
168
|
|
169
|
+
def pane_index_at(y: nil, x: nil)
|
170
|
+
@panes.get_index_by_point y: y, x: x
|
171
|
+
end
|
172
|
+
|
158
173
|
def window
|
159
174
|
@panes.active
|
160
175
|
end
|
@@ -207,7 +222,7 @@ module Rfd
|
|
207
222
|
|
208
223
|
def get_command(prompt: nil)
|
209
224
|
Curses.echo
|
210
|
-
startx = prompt ? prompt.
|
225
|
+
startx = prompt ? prompt.size : 1
|
211
226
|
s = ' ' * 100
|
212
227
|
Curses.mvwgetstr window, 0, startx, s
|
213
228
|
"#{prompt[1..-1] if prompt}#{s.strip}"
|
data/rfd.gemspec
CHANGED
@@ -4,11 +4,11 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "rfd"
|
7
|
-
spec.version = '0.0
|
7
|
+
spec.version = '0.1.0'
|
8
8
|
spec.authors = ["Akira Matsuda"]
|
9
9
|
spec.email = ["ronnie@dio.jp"]
|
10
|
-
spec.description = 'Ruby on Files
|
11
|
-
spec.summary = 'Ruby on Files
|
10
|
+
spec.description = 'Ruby on Files & Directories'
|
11
|
+
spec.summary = 'Ruby on Files & Directories'
|
12
12
|
spec.homepage = 'https://github.com/amatsuda/rfd'
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_dependency 'ffi-ncurses'
|
21
|
+
spec.add_dependency 'rubyzip', '>= 1.0.0'
|
21
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
23
|
spec.add_development_dependency "rake"
|
23
24
|
spec.add_development_dependency 'rspec'
|
data/spec/controller_spec.rb
CHANGED
@@ -6,12 +6,16 @@ describe Rfd::Controller do
|
|
6
6
|
|
7
7
|
around do |example|
|
8
8
|
@stdout = capture(:stdout) do
|
9
|
-
|
9
|
+
FileUtils.cp_r File.join(__dir__, 'testdir'), tmpdir
|
10
|
+
@rfd = Rfd.start tmpdir
|
10
11
|
def (@rfd.main).maxy
|
11
12
|
3
|
12
13
|
end
|
13
14
|
|
14
15
|
example.run
|
16
|
+
|
17
|
+
FileUtils.rm_r tmpdir
|
18
|
+
Dir.chdir __dir__
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
@@ -19,7 +23,7 @@ describe Rfd::Controller do
|
|
19
23
|
Curses.endwin
|
20
24
|
end
|
21
25
|
|
22
|
-
let(:
|
26
|
+
let(:tmpdir) { File.join __dir__, 'tmpdir' }
|
23
27
|
let!(:controller) { @rfd }
|
24
28
|
subject { controller }
|
25
29
|
let(:items) { controller.items }
|
@@ -102,13 +106,13 @@ describe Rfd::Controller do
|
|
102
106
|
before do
|
103
107
|
controller.cd 'dir1'
|
104
108
|
end
|
105
|
-
its(:current_dir) { should == File.join(
|
109
|
+
its(:current_dir) { should == File.join(tmpdir, 'dir1') }
|
106
110
|
|
107
111
|
describe '#popd' do
|
108
112
|
before do
|
109
113
|
controller.popd
|
110
114
|
end
|
111
|
-
its(:current_dir) { should ==
|
115
|
+
its(:current_dir) { should == tmpdir }
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
@@ -122,30 +126,76 @@ describe Rfd::Controller do
|
|
122
126
|
|
123
127
|
describe '#sort' do
|
124
128
|
let(:item) do
|
125
|
-
Dir.mkdir File.join
|
126
|
-
|
129
|
+
Dir.mkdir File.join(tmpdir, '.a')
|
130
|
+
stat = File.lstat File.join(tmpdir, '.a')
|
131
|
+
Rfd::Item.new dir: tmpdir, name: '.a', stat: stat, window_width: 100
|
127
132
|
end
|
128
133
|
before do
|
129
134
|
controller.items << item
|
130
135
|
controller.sort
|
131
136
|
end
|
132
|
-
after do
|
133
|
-
Dir.rmdir File.join testdir, '.a'
|
134
|
-
end
|
135
137
|
subject { item }
|
136
138
|
its(:index) { should == 2 } # . .. then next
|
137
139
|
end
|
138
140
|
|
139
141
|
describe '#chmod' do
|
140
142
|
let(:item) { controller.items.detect {|i| !i.directory?} }
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
|
144
|
+
context 'With an octet string' do
|
145
|
+
before do
|
146
|
+
item.toggle_mark
|
147
|
+
controller.chmod '666'
|
148
|
+
end
|
149
|
+
subject { controller.items.detect {|i| !i.directory?} }
|
150
|
+
its(:mode) { should == '-rw-rw-rw-' }
|
146
151
|
end
|
152
|
+
|
153
|
+
context 'With a decimal string' do
|
154
|
+
before do
|
155
|
+
item.toggle_mark
|
156
|
+
controller.chmod '0666'
|
157
|
+
end
|
158
|
+
subject { controller.items.detect {|i| !i.directory?} }
|
159
|
+
its(:mode) { should == '-rw-rw-rw-' }
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'With a non-numeric string' do
|
163
|
+
before do
|
164
|
+
item.toggle_mark
|
165
|
+
controller.chmod 'a+w'
|
166
|
+
end
|
167
|
+
subject { controller.items.detect {|i| !i.directory?} }
|
168
|
+
its(:mode) { should == '-rw-rw-rw-' }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#chown' do
|
173
|
+
let(:item) { controller.items.detect {|i| !i.directory?} }
|
147
174
|
subject { item }
|
148
|
-
|
175
|
+
|
176
|
+
context 'With user name only' do
|
177
|
+
before do
|
178
|
+
expect(FileUtils).to receive(:chown).with('alice', nil, Array(item.path))
|
179
|
+
item.toggle_mark
|
180
|
+
end
|
181
|
+
specify { controller.chown 'alice' }
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'With group name only' do
|
185
|
+
before do
|
186
|
+
expect(FileUtils).to receive(:chown).with(nil, 'admin', Array(item.path))
|
187
|
+
item.toggle_mark
|
188
|
+
end
|
189
|
+
specify { controller.chown ':admin' }
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'With both user name and group name' do
|
193
|
+
before do
|
194
|
+
expect(FileUtils).to receive(:chown).with('nobody', 'nobody', Array(item.path))
|
195
|
+
item.toggle_mark
|
196
|
+
end
|
197
|
+
specify { controller.chown 'nobody:nobody' }
|
198
|
+
end
|
149
199
|
end
|
150
200
|
|
151
201
|
describe '#find' do
|
@@ -178,11 +228,8 @@ describe Rfd::Controller do
|
|
178
228
|
controller.find 'file1'
|
179
229
|
controller.cp 'file4'
|
180
230
|
end
|
181
|
-
after do
|
182
|
-
File.delete File.join(testdir, 'file4')
|
183
|
-
end
|
184
231
|
it 'should be the same file as the copy source file' do
|
185
|
-
File.read(File.join(
|
232
|
+
File.read(File.join(tmpdir, 'file1')).should == File.read(File.join(tmpdir, 'file4'))
|
186
233
|
end
|
187
234
|
end
|
188
235
|
|
@@ -191,12 +238,8 @@ describe Rfd::Controller do
|
|
191
238
|
controller.find 'file3'
|
192
239
|
controller.mv 'dir2'
|
193
240
|
end
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
it 'should move current file to the specified directory' do
|
198
|
-
File.exist?(File.join(testdir, 'dir2/file3')).should == true
|
199
|
-
end
|
241
|
+
subject { File }
|
242
|
+
it { should be_exist File.join(tmpdir, 'dir2/file3') }
|
200
243
|
end
|
201
244
|
|
202
245
|
describe '#rename' do
|
@@ -207,13 +250,33 @@ describe Rfd::Controller do
|
|
207
250
|
controller.toggle_mark
|
208
251
|
controller.rename 'fi/faaai'
|
209
252
|
end
|
210
|
-
|
211
|
-
|
212
|
-
|
253
|
+
subject { File }
|
254
|
+
it { should be_exist File.join(tmpdir, '.faaaile2') }
|
255
|
+
it { should be_exist File.join(tmpdir, 'faaaile3') }
|
256
|
+
end
|
257
|
+
|
258
|
+
describe '#trash' do
|
259
|
+
before do
|
260
|
+
controller.find 'file3'
|
261
|
+
controller.toggle_mark
|
262
|
+
controller.trash
|
263
|
+
end
|
264
|
+
it 'should be properly deleted from the current directory' do
|
265
|
+
controller.items.should be_none {|i| i.name == 'file3'}
|
213
266
|
end
|
214
|
-
|
215
|
-
|
216
|
-
|
267
|
+
end
|
268
|
+
|
269
|
+
describe '#delete' do
|
270
|
+
before do
|
271
|
+
controller.find 'file3'
|
272
|
+
controller.toggle_mark
|
273
|
+
controller.find 'dir2'
|
274
|
+
controller.toggle_mark
|
275
|
+
controller.delete
|
276
|
+
end
|
277
|
+
it 'should be properly deleted from the current directory' do
|
278
|
+
controller.items.should be_none {|i| i.name == 'file3'}
|
279
|
+
controller.items.should be_none {|i| i.name == 'dir2'}
|
217
280
|
end
|
218
281
|
end
|
219
282
|
|
@@ -221,24 +284,62 @@ describe Rfd::Controller do
|
|
221
284
|
before do
|
222
285
|
controller.mkdir 'aho'
|
223
286
|
end
|
224
|
-
|
225
|
-
|
226
|
-
end
|
227
|
-
it 'should create a new directory' do
|
228
|
-
Dir.exist?(File.join(testdir, 'aho')).should == true
|
229
|
-
end
|
287
|
+
subject { Dir }
|
288
|
+
it { should be_exist File.join(tmpdir, 'aho') }
|
230
289
|
end
|
231
290
|
|
232
291
|
describe '#touch' do
|
233
292
|
before do
|
234
293
|
controller.touch 'fuga'
|
235
294
|
end
|
236
|
-
|
237
|
-
|
295
|
+
subject { File }
|
296
|
+
it { should be_exist File.join(tmpdir, 'fuga') }
|
297
|
+
end
|
298
|
+
|
299
|
+
describe '#symlink' do
|
300
|
+
before do
|
301
|
+
controller.find 'dir1'
|
302
|
+
controller.symlink 'aaa'
|
238
303
|
end
|
239
|
-
|
240
|
-
|
304
|
+
subject { File }
|
305
|
+
it { should be_symlink File.join(tmpdir, 'aaa') }
|
306
|
+
end
|
307
|
+
|
308
|
+
describe '#pbcopy' do
|
309
|
+
before do
|
310
|
+
controller.find '.file1'
|
311
|
+
controller.toggle_mark
|
312
|
+
controller.find 'dir3'
|
313
|
+
controller.toggle_mark
|
314
|
+
controller.clipboard
|
315
|
+
end
|
316
|
+
it 'copies the selected paths into clipboard' do
|
317
|
+
`pbpaste`.should == "#{File.join(tmpdir, 'dir3')} #{File.join(tmpdir, '.file1')}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe '#zip' do
|
322
|
+
before do
|
323
|
+
controller.find 'dir1'
|
324
|
+
controller.zip 'archive1'
|
325
|
+
end
|
326
|
+
subject { File }
|
327
|
+
it { should be_exist File.join(tmpdir, 'archive1.zip') }
|
328
|
+
end
|
329
|
+
|
330
|
+
describe '#unarchive' do
|
331
|
+
before do
|
332
|
+
controller.find 'zip1'
|
333
|
+
controller.toggle_mark
|
334
|
+
controller.find 'gz1'
|
335
|
+
controller.toggle_mark
|
336
|
+
controller.unarchive
|
241
337
|
end
|
338
|
+
subject { File }
|
339
|
+
it { should be_exist File.join(tmpdir, 'zip1/zip_content1') }
|
340
|
+
it { should be_exist File.join(tmpdir, 'zip1/zip_content_dir1/zip_content1_1') }
|
341
|
+
it { should be_exist File.join(tmpdir, 'gz1/gz_content1') }
|
342
|
+
it { should be_exist File.join(tmpdir, 'gz1/gz_content_dir1/gz_content1_1') }
|
242
343
|
end
|
243
344
|
|
244
345
|
describe '#first_page? and #last_page?' do
|
data/spec/spec_helper.rb
CHANGED
Binary file
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rfd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Akira Matsuda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi-ncurses
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubyzip
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +80,7 @@ dependencies:
|
|
66
80
|
- - '>='
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
69
|
-
description: Ruby on Files
|
83
|
+
description: Ruby on Files & Directories
|
70
84
|
email:
|
71
85
|
- ronnie@dio.jp
|
72
86
|
executables:
|
@@ -95,8 +109,10 @@ files:
|
|
95
109
|
- spec/testdir/file1
|
96
110
|
- spec/testdir/file2
|
97
111
|
- spec/testdir/file3
|
112
|
+
- spec/testdir/gz1.tar.gz
|
98
113
|
- spec/testdir/link1
|
99
114
|
- spec/testdir/link2
|
115
|
+
- spec/testdir/zip1.zip
|
100
116
|
homepage: https://github.com/amatsuda/rfd
|
101
117
|
licenses:
|
102
118
|
- MIT
|
@@ -120,7 +136,7 @@ rubyforge_project:
|
|
120
136
|
rubygems_version: 2.0.3
|
121
137
|
signing_key:
|
122
138
|
specification_version: 4
|
123
|
-
summary: Ruby on Files
|
139
|
+
summary: Ruby on Files & Directories
|
124
140
|
test_files:
|
125
141
|
- spec/controller_spec.rb
|
126
142
|
- spec/spec_helper.rb
|
@@ -132,6 +148,8 @@ test_files:
|
|
132
148
|
- spec/testdir/file1
|
133
149
|
- spec/testdir/file2
|
134
150
|
- spec/testdir/file3
|
151
|
+
- spec/testdir/gz1.tar.gz
|
135
152
|
- spec/testdir/link1
|
136
153
|
- spec/testdir/link2
|
154
|
+
- spec/testdir/zip1.zip
|
137
155
|
has_rdoc:
|