filesortd 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +2 -0
- data/README.md +31 -3
- data/Rakefile +12 -0
- data/features/listener.feature +27 -0
- data/features/step_definitions/listener.rb +51 -0
- data/filesortd.gemspec +1 -1
- data/lib/filesortd/afile.rb +39 -11
- data/lib/filesortd/callback.rb +55 -12
- data/lib/filesortd/cli.rb +2 -0
- data/lib/filesortd/folder.rb +15 -9
- data/lib/filesortd/matcher.rb +15 -1
- data/lib/filesortd/oneeight.rb +9 -0
- data/lib/filesortd/version.rb +1 -1
- data/spec/afile_spec.rb +11 -2
- data/spec/callback_spec.rb +13 -2
- data/spec/matcher_spec.rb +35 -24
- metadata +17 -10
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Filesortd
|
1
|
+
# Filesortd [![Build Status](https://travis-ci.org/myfreeweb/filesortd.png?branch=master)](https://travis-ci.org/myfreeweb/filesortd)
|
2
2
|
|
3
3
|
A Ruby DSL for automatically sorting your files based on rules.
|
4
4
|
Like [Hazel](http://www.noodlesoft.com/hazel.php), but cross-platform, no GUI required.
|
@@ -23,7 +23,7 @@ yourconfig.rb:
|
|
23
23
|
folder "/Users/myfreeweb/Downloads" do
|
24
24
|
|
25
25
|
# Do things to files that match a glob or a regex
|
26
|
-
|
26
|
+
pattern "*.mp3" do
|
27
27
|
mv "/Users/myfreeweb/Music"
|
28
28
|
|
29
29
|
# Do things if running on a particular OS
|
@@ -35,6 +35,24 @@ folder "/Users/myfreeweb/Downloads" do
|
|
35
35
|
# Mac OS X saves the location you downloaded a file from
|
36
36
|
downloaded_from %r{destroyallsoftware} do
|
37
37
|
mv "/Users/myfreeweb/Movies/DAS"
|
38
|
+
open_in :default
|
39
|
+
# or
|
40
|
+
open_in "MPlayerX"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Match on the kind, also OS X only
|
44
|
+
kind "Ruby Source" do
|
45
|
+
label :red
|
46
|
+
end
|
47
|
+
|
48
|
+
# Match on Finder label, OS X
|
49
|
+
label :green do
|
50
|
+
mv "/Users/myfreeweb/Documents"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Match all mp4 files downloaded from DAS
|
54
|
+
match pattern: '*.mp4', downloaded_from: %r{destroyallsoftware} do
|
55
|
+
label :gray
|
38
56
|
end
|
39
57
|
end
|
40
58
|
|
@@ -45,10 +63,12 @@ folders "/Users/myfreeweb/Pictures", "/opt/pictures" do
|
|
45
63
|
|
46
64
|
# Do things to any files
|
47
65
|
any do
|
66
|
+
applescript 'tell app "Finder" to reveal theFile'
|
48
67
|
label :blue
|
49
68
|
end
|
50
69
|
|
51
|
-
|
70
|
+
# Match by extension -- same as pattern "*.png"
|
71
|
+
ext :png do
|
52
72
|
pass "optipng"
|
53
73
|
label :green
|
54
74
|
end
|
@@ -59,8 +79,16 @@ Actions:
|
|
59
79
|
|
60
80
|
- `contents` (or `read`) -- get the contents
|
61
81
|
- `rm` (or `remove`, `delete`, `unlink`) -- remove
|
82
|
+
- `trash` -- put to trash (OS X/Linux, Linux requires [trash-cli](https://github.com/andreafrancia/trash-cli))
|
62
83
|
- `cp` (or `copy`) -- copy
|
63
84
|
- `mv` (or `move`) -- move/rename
|
64
85
|
- `pipe(cmd)` -- start the command, pass the file to stdin, get the stdout
|
65
86
|
- `pass(cmd)` -- start the command, pass the path to the file as an argument, get the stdout
|
66
87
|
- `label(color)` -- set the OS X Finder label (:none, :orange, :red, :yellow, :blue, :purple, :green, :gray or :grey)
|
88
|
+
- `open_in(app)` -- open the file using the OS X `open` command, use :default for the default app for the file type
|
89
|
+
- `applescript(script)` -- run provided AppleScript. Use `theFile` inside it to refer to the file matched
|
90
|
+
|
91
|
+
## Contributors
|
92
|
+
|
93
|
+
- [myfreeweb](https://github.com/myfreeweb)
|
94
|
+
- [goshakkk](https://github.com/goshakkk)
|
data/Rakefile
CHANGED
@@ -1 +1,13 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "cucumber"
|
4
|
+
require "cucumber/rake/task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |rspec|
|
7
|
+
end
|
8
|
+
|
9
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
10
|
+
t.cucumber_opts = "features --format pretty"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => [:spec, :features]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Listener
|
2
|
+
In order to sort files
|
3
|
+
As a filesortd user
|
4
|
+
I want filesortd to listen to file system events and do actions I want
|
5
|
+
|
6
|
+
Scenario: One folder
|
7
|
+
Given the folder "/tmp/fsdlistener" exists
|
8
|
+
And filesortd listens to "/tmp/fsdlistener" and removes files
|
9
|
+
When I create "/tmp/fsdlistener/hello"
|
10
|
+
And I wait 2 seconds
|
11
|
+
Then "/tmp/fsdlistener/hello" should not exist
|
12
|
+
|
13
|
+
Scenario: Many folders
|
14
|
+
Given the folder "/tmp/fsdlistener1" exists
|
15
|
+
And the folder "/tmp/fsdlistener2" exists
|
16
|
+
And the folder "/tmp/fsdlistener3" exists
|
17
|
+
And filesortd listens to 3 folders "/tmp/fsdlistener1", "/tmp/fsdlistener2", "/tmp/fsdlistener3" and removes files
|
18
|
+
When I create "/tmp/fsdlistener1/hello1"
|
19
|
+
And I create "/tmp/fsdlistener2/hello2"
|
20
|
+
And I create "/tmp/fsdlistener3/hello3"
|
21
|
+
And I wait 2 seconds
|
22
|
+
Then "/tmp/fsdlistener1/hello1" should not exist
|
23
|
+
And "/tmp/fsdlistener2/hello2" should not exist
|
24
|
+
And "/tmp/fsdlistener3/hello3" should not exist
|
25
|
+
Then remove folder "/tmp/fsdlistener1"
|
26
|
+
And remove folder "/tmp/fsdlistener2"
|
27
|
+
And remove folder "/tmp/fsdlistener3"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rspec/expectations"
|
2
|
+
require "filesortd"
|
3
|
+
require "filesortd/oneeight"
|
4
|
+
include Filesortd
|
5
|
+
|
6
|
+
Before do
|
7
|
+
@listeners = []
|
8
|
+
end
|
9
|
+
|
10
|
+
After do
|
11
|
+
@listeners.each do |l|
|
12
|
+
l.stop
|
13
|
+
end
|
14
|
+
@listeners = []
|
15
|
+
end
|
16
|
+
|
17
|
+
Given /^the folder "(.*?)" exists$/ do |path|
|
18
|
+
FileUtils.mkdir path
|
19
|
+
end
|
20
|
+
|
21
|
+
Given /^filesortd listens to "(.*?)" and removes files$/ do |path|
|
22
|
+
folder path do
|
23
|
+
any do
|
24
|
+
rm
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Given /^filesortd listens to 3 folders "(.*?)", "(.*?)", "(.*?)" and removes files$/ do |p1, p2, p3|
|
30
|
+
folders p1, p2, p3 do
|
31
|
+
any do
|
32
|
+
rm
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^I create "(.*?)"$/ do |path|
|
38
|
+
File.write path, "123"
|
39
|
+
end
|
40
|
+
|
41
|
+
When /^I wait (\d+) seconds$/ do |s|
|
42
|
+
sleep s.to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^"(.*?)" should not exist$/ do |path|
|
46
|
+
File.exists?(path).should be_false
|
47
|
+
end
|
48
|
+
|
49
|
+
Then /^remove folder "(.*?)"$/ do |path|
|
50
|
+
FileUtils.rmdir path
|
51
|
+
end
|
data/filesortd.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'filesortd/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "filesortd"
|
8
8
|
gem.version = Filesortd::VERSION
|
9
|
-
gem.authors = ["myfreeweb"]
|
9
|
+
gem.authors = ["myfreeweb", "goshakkk"]
|
10
10
|
gem.email = ["floatboth@me.com"]
|
11
11
|
gem.description = %q{Automatic rule-based sorting for your files. Like Hazel, but doesn't need a GUI.}
|
12
12
|
gem.summary = %q{Rule-based file sorting}
|
data/lib/filesortd/afile.rb
CHANGED
@@ -1,8 +1,22 @@
|
|
1
1
|
require "popen4"
|
2
2
|
require "fileutils"
|
3
|
+
require "shellwords"
|
3
4
|
|
4
5
|
module Filesortd
|
6
|
+
# File actions DSL
|
5
7
|
class Afile
|
8
|
+
FINDER_LABELS = {
|
9
|
+
:none => 0,
|
10
|
+
:orange => 1,
|
11
|
+
:red => 2,
|
12
|
+
:yellow => 3,
|
13
|
+
:blue => 4,
|
14
|
+
:purple => 5,
|
15
|
+
:green => 6,
|
16
|
+
:gray => 7,
|
17
|
+
:grey => 7
|
18
|
+
}
|
19
|
+
|
6
20
|
attr_reader :path
|
7
21
|
|
8
22
|
def initialize(path)
|
@@ -22,6 +36,15 @@ module Filesortd
|
|
22
36
|
alias :delete :rm
|
23
37
|
alias :unlink :rm
|
24
38
|
|
39
|
+
def trash
|
40
|
+
case RbConfig::CONFIG['target_os']
|
41
|
+
when /darwin(1.+)?$/i then applescript 'tell app "Finder" to delete theFile'
|
42
|
+
when /freebsd/i then pass "trash-put"
|
43
|
+
when /linux/i then pass "trash-put"
|
44
|
+
when /mswin|mingw/i then puts "Trash not supported on Windows."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
25
48
|
def cp(target)
|
26
49
|
FileUtils.cp @path, target
|
27
50
|
@path = target
|
@@ -53,23 +76,28 @@ module Filesortd
|
|
53
76
|
out
|
54
77
|
end
|
55
78
|
|
79
|
+
def open_in(app)
|
80
|
+
if app == :default
|
81
|
+
pass "open"
|
82
|
+
else
|
83
|
+
pass "open -a #{app.shellescape}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
56
87
|
def label(lbl)
|
57
88
|
if lbl.is_a? Symbol
|
58
|
-
idx =
|
59
|
-
:none => 0,
|
60
|
-
:orange => 1,
|
61
|
-
:red => 2,
|
62
|
-
:yellow => 3,
|
63
|
-
:blue => 4,
|
64
|
-
:purple => 5,
|
65
|
-
:green => 6,
|
66
|
-
:gray => 7,
|
67
|
-
:grey => 7
|
68
|
-
}[lbl]
|
89
|
+
idx = FINDER_LABELS[lbl]
|
69
90
|
else
|
70
91
|
idx = lbl
|
71
92
|
end
|
72
93
|
system %{osascript -e 'tell app "Finder" to set label index of (POSIX file "#{@path}" as alias) to #{idx}' 2&>/dev/null}
|
73
94
|
end
|
95
|
+
|
96
|
+
def applescript(script)
|
97
|
+
full_script = %{set theFile to POSIX file "#{@path}"\n} + script
|
98
|
+
cmd = %{osascript -e '#{full_script}'}
|
99
|
+
|
100
|
+
pass cmd
|
101
|
+
end
|
74
102
|
end
|
75
103
|
end
|
data/lib/filesortd/callback.rb
CHANGED
@@ -3,36 +3,79 @@ require "filesortd/afile"
|
|
3
3
|
require "docile"
|
4
4
|
|
5
5
|
module Filesortd
|
6
|
+
# Folder DSL (add matchers)
|
6
7
|
class Callback
|
7
|
-
attr_accessor :
|
8
|
+
attr_accessor :matcher_sets
|
8
9
|
|
9
10
|
def initialize
|
10
|
-
@
|
11
|
+
@matcher_sets = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def match(options={}, &callback)
|
15
|
+
matchers = options.map { |matchr, params| matcher(matchr.to_sym, params) }
|
16
|
+
@matcher_sets[matchers] = callback
|
11
17
|
end
|
12
18
|
|
13
19
|
def any(&callback)
|
14
|
-
|
20
|
+
match :any => nil, &callback
|
21
|
+
end
|
22
|
+
|
23
|
+
def pattern(pattern, &callback)
|
24
|
+
match :pattern => pattern, &callback
|
15
25
|
end
|
16
26
|
|
17
|
-
def
|
18
|
-
|
27
|
+
def extension(ext, &callback)
|
28
|
+
match :extension => ext, &callback
|
29
|
+
end
|
30
|
+
alias :ext :extension
|
31
|
+
|
32
|
+
def kind(pattern, &callback)
|
33
|
+
match :kind => pattern, &callback
|
34
|
+
end
|
35
|
+
|
36
|
+
def label(lbl, &callback)
|
37
|
+
match :label => lbl, &callback
|
19
38
|
end
|
20
39
|
|
21
40
|
def downloaded_from(pattern, &callback)
|
22
|
-
|
23
|
-
m = XattrMatcher.new("com.apple.metadata:kMDItemWhereFroms") do |elements, path|
|
24
|
-
elements.map { |el| pm.match(el) }.count(true) >= 1
|
25
|
-
end
|
26
|
-
@matchers[m] = callback
|
41
|
+
match :downloaded_from => pattern, &callback
|
27
42
|
end
|
28
43
|
|
29
44
|
def call(paths)
|
30
45
|
paths.each do |path|
|
31
|
-
@
|
32
|
-
|
46
|
+
@matcher_sets.each do |matchers, callback|
|
47
|
+
# If matcher is an array already, it won't be changed
|
48
|
+
# And if it's a single matcher, it'll be wrapped into an array
|
49
|
+
matchers = [matchers].flatten
|
50
|
+
Docile.dsl_eval(Afile.new(path), &callback) if matchers.all? { |m| m.match(path) }
|
33
51
|
end
|
34
52
|
end
|
35
53
|
end
|
36
54
|
|
55
|
+
private
|
56
|
+
def matcher(type, pattern=nil)
|
57
|
+
case type
|
58
|
+
when :any
|
59
|
+
AlwaysTrueMatcher.new
|
60
|
+
when :pattern
|
61
|
+
BasenameMatcher.new pattern
|
62
|
+
when :extension, :ext
|
63
|
+
BasenameMatcher.new "*.#{pattern}"
|
64
|
+
when :kind
|
65
|
+
SpotlightMatcher.new "kMDItemKind", pattern
|
66
|
+
when :label
|
67
|
+
if pattern.is_a? Symbol
|
68
|
+
idx = Afile::FINDER_LABELS[pattern]
|
69
|
+
else
|
70
|
+
idx = pattern
|
71
|
+
end
|
72
|
+
SpotlightMatcher.new "kMDItemFSLabel", idx
|
73
|
+
when :downloaded_from
|
74
|
+
pm = Matcher.new(pattern)
|
75
|
+
m = XattrMatcher.new("com.apple.metadata:kMDItemWhereFroms") do |elements, path|
|
76
|
+
elements.map { |el| pm.match(el) }.count(true) >= 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
37
80
|
end
|
38
81
|
end
|
data/lib/filesortd/cli.rb
CHANGED
data/lib/filesortd/folder.rb
CHANGED
@@ -3,20 +3,26 @@ require "docile"
|
|
3
3
|
require "filesortd/callback"
|
4
4
|
|
5
5
|
module Filesortd
|
6
|
-
def
|
7
|
-
paths.select
|
6
|
+
def select_existing(paths)
|
7
|
+
paths.select do |path|
|
8
8
|
e = File.exists? path
|
9
9
|
puts "Folder does not exist: #{path}" unless e
|
10
10
|
e
|
11
11
|
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def folders(*paths, &block)
|
15
|
+
paths = select_existing paths
|
16
|
+
unless paths == []
|
17
|
+
callback = Docile.dsl_eval(Callback.new, &block)
|
18
|
+
cb = Proc.new do |modified, added, removed|
|
19
|
+
puts "Processing files: #{added}"
|
20
|
+
callback.call added
|
21
|
+
end
|
22
|
+
l = Listen.to(*paths).latency(0.5).change(&cb)
|
23
|
+
l.start(false)
|
24
|
+
@listeners << l
|
16
25
|
end
|
17
|
-
l = Listen.to(*paths).latency(0.5).change(&cb)
|
18
|
-
l.start(false)
|
19
|
-
@listeners << l
|
20
26
|
end
|
21
27
|
alias :folder :folders
|
22
28
|
end
|
data/lib/filesortd/matcher.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
require "shellwords"
|
2
|
+
|
1
3
|
module Filesortd
|
4
|
+
# Basic matcher
|
2
5
|
class Matcher < Struct.new(:pattern)
|
3
6
|
def match(s)
|
4
7
|
if pattern.is_a? Regexp
|
@@ -19,7 +22,6 @@ module Filesortd
|
|
19
22
|
class XattrMatcher
|
20
23
|
def initialize(xattr, &matcher)
|
21
24
|
require "osx/plist"
|
22
|
-
require "Shellwords"
|
23
25
|
@xattr = xattr
|
24
26
|
@matcher = matcher
|
25
27
|
end
|
@@ -32,6 +34,18 @@ module Filesortd
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
37
|
+
class SpotlightMatcher
|
38
|
+
def initialize(key, value)
|
39
|
+
@key = key
|
40
|
+
@matcher = Matcher.new(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def match(path)
|
44
|
+
val = `mdls -raw -name #{@key} #{path.shellescape}`
|
45
|
+
@matcher.match(val)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
35
49
|
class AlwaysTrueMatcher
|
36
50
|
def match(s)
|
37
51
|
true
|
data/lib/filesortd/version.rb
CHANGED
data/spec/afile_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "filesortd/oneeight"
|
1
2
|
require "filesortd/afile"
|
2
3
|
include Filesortd
|
3
4
|
|
@@ -42,8 +43,8 @@ describe Afile do
|
|
42
43
|
end
|
43
44
|
|
44
45
|
it "pipes" do
|
45
|
-
File.write path, "
|
46
|
-
Afile.new(path).pipe("
|
46
|
+
File.write path, "123"
|
47
|
+
Afile.new(path).pipe("cat").should == "123"
|
47
48
|
File.unlink path
|
48
49
|
end
|
49
50
|
|
@@ -61,4 +62,12 @@ describe Afile do
|
|
61
62
|
File.unlink path
|
62
63
|
end
|
63
64
|
end
|
65
|
+
|
66
|
+
it "runs applescript" do
|
67
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
68
|
+
File.write path, "123"
|
69
|
+
Afile.new(path).applescript('get POSIX path of theFile').should == path
|
70
|
+
File.unlink path
|
71
|
+
end
|
72
|
+
end
|
64
73
|
end
|
data/spec/callback_spec.rb
CHANGED
@@ -8,8 +8,19 @@ describe Callback do
|
|
8
8
|
true_matcher = double("matcher", :match => true)
|
9
9
|
false_matcher = double("matcher", :match => false)
|
10
10
|
true_called = false_called = false
|
11
|
-
cb.
|
12
|
-
cb.
|
11
|
+
cb.matcher_sets[ true_matcher] = Proc.new { true_called = true }
|
12
|
+
cb.matcher_sets[false_matcher] = Proc.new { false_called = true }
|
13
|
+
cb.call([""])
|
14
|
+
true_called.should be_true
|
15
|
+
false_called.should be_false
|
16
|
+
end
|
17
|
+
|
18
|
+
it "supports matcher sets" do
|
19
|
+
true_matcher = double("matcher", :match => true)
|
20
|
+
false_matcher = double("matcher", :match => false)
|
21
|
+
true_called = false_called = false
|
22
|
+
cb.matcher_sets[[ true_matcher, true_matcher]] = Proc.new { true_called = true }
|
23
|
+
cb.matcher_sets[[false_matcher, true_matcher]] = Proc.new { false_called = true }
|
13
24
|
cb.call([""])
|
14
25
|
true_called.should be_true
|
15
26
|
false_called.should be_false
|
data/spec/matcher_spec.rb
CHANGED
@@ -13,35 +13,46 @@ describe BasenameMatcher do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
17
|
+
describe XattrMatcher do
|
18
|
+
let(:path) { "/tmp/xattrtest" }
|
19
|
+
let(:prop) { "com.apple.metadata:kMDItemWhereFroms" }
|
20
|
+
let(:downloaded_das) { "62 70 6C 69 73 74 30 30 A2 01 02 5F 10 B5 68 74\n74 70 73 3A 2F 2F 73 33 2E 61 6D 61 7A 6F 6E 61\n77 73 2E 63 6F 6D 2F 64 65 73 74 72 6F 79 61 6C\n6C 73 6F 66 74 77 61 72 65 2F 64 61 73 2D 30 30\n31 34 2D 65 78 74 72 61 63 74 69 6E 67 2D 6F 62\n6A 65 63 74 73 2D 69 6E 2D 64 6A 61 6E 67 6F 2E\n6D 6F 76 3F 41 57 53 41 63 63 65 73 73 4B 65 79\n49 64 3D 41 4B 49 41 49 4B 52 56 43 45 43 58 42\n43 34 5A 47 48 49 51 26 45 78 70 69 72 65 73 3D\n31 33 35 35 33 32 35 35 31 33 26 53 69 67 6E 61\n74 75 72 65 3D 64 6C 4D 48 52 6D 73 78 64 56 70\n45 6F 4B 58 44 62 50 38 59 6F 65 36 51 37 44 77\n25 33 44 5F 10 36 68 74 74 70 73 3A 2F 2F 77 77\n77 2E 64 65 73 74 72 6F 79 61 6C 6C 73 6F 66 74\n77 61 72 65 2E 63 6F 6D 2F 73 63 72 65 65 6E 63\n61 73 74 73 2F 63 61 74 61 6C 6F 67 08 0B C3 00\n00 00 00 00 00 01 01 00 00 00 00 00 00 00 03 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC" }
|
21
|
+
|
22
|
+
before { File.write path, "123" }
|
23
|
+
after { File.unlink path }
|
24
|
+
|
25
|
+
it "returns true for matched xattrs" do
|
26
|
+
system "xattr -wx #{prop.shellescape} '#{downloaded_das}' #{path.shellescape}"
|
27
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
28
|
+
elements.map { |el| !(el =~ %r{destroyallsoftware}).nil? }.count(true) >= 1
|
29
|
+
end
|
30
|
+
m.match(path).should be_true
|
28
31
|
end
|
29
|
-
m.match(path).should be_true
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
it "returns false for invalid xattrs" do
|
34
|
+
system "xattr -wx #{prop.shellescape} '#{downloaded_das}' #{path.shellescape}"
|
35
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
36
|
+
elements.map { |el| !(el =~ %r{destroyNOsoftware}).nil? }.count(true) >= 1
|
37
|
+
end
|
38
|
+
m.match(path).should be_false
|
36
39
|
end
|
37
|
-
m.match(path).should be_false
|
38
|
-
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
it "returns false for no xattrs" do
|
42
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
43
|
+
elements.map { |el| !(el =~ %r{destroyallsoftware}).nil? }.count(true) >= 1
|
44
|
+
end
|
45
|
+
m.match(path).should be_false
|
43
46
|
end
|
44
|
-
|
47
|
+
|
45
48
|
end
|
46
49
|
|
50
|
+
describe SpotlightMatcher do
|
51
|
+
it "matches spotlight attributes" do
|
52
|
+
path = "/tmp/spotlighttest.rb"
|
53
|
+
File.write path, "#!/usr/bin/env ruby"
|
54
|
+
SpotlightMatcher.new("kMDItemKind", "Ruby Source").match(path).should be_true
|
55
|
+
SpotlightMatcher.new("kMDItemKind", "Perl Source").match(path).should be_false
|
56
|
+
end
|
57
|
+
end
|
47
58
|
end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filesortd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- myfreeweb
|
9
|
+
- goshakkk
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
13
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: docile
|
16
|
-
requirement: &
|
17
|
+
requirement: &70311998121040 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,10 +22,10 @@ dependencies:
|
|
21
22
|
version: '0'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *70311998121040
|
25
26
|
- !ruby/object:Gem::Dependency
|
26
27
|
name: listen
|
27
|
-
requirement: &
|
28
|
+
requirement: &70311998120300 !ruby/object:Gem::Requirement
|
28
29
|
none: false
|
29
30
|
requirements:
|
30
31
|
- - ! '>='
|
@@ -32,10 +33,10 @@ dependencies:
|
|
32
33
|
version: '0'
|
33
34
|
type: :runtime
|
34
35
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
+
version_requirements: *70311998120300
|
36
37
|
- !ruby/object:Gem::Dependency
|
37
38
|
name: popen4
|
38
|
-
requirement: &
|
39
|
+
requirement: &70311998136240 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
41
|
requirements:
|
41
42
|
- - ! '>='
|
@@ -43,10 +44,10 @@ dependencies:
|
|
43
44
|
version: '0'
|
44
45
|
type: :runtime
|
45
46
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
+
version_requirements: *70311998136240
|
47
48
|
- !ruby/object:Gem::Dependency
|
48
49
|
name: thor
|
49
|
-
requirement: &
|
50
|
+
requirement: &70311998135740 !ruby/object:Gem::Requirement
|
50
51
|
none: false
|
51
52
|
requirements:
|
52
53
|
- - ! '>='
|
@@ -54,7 +55,7 @@ dependencies:
|
|
54
55
|
version: '0'
|
55
56
|
type: :runtime
|
56
57
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
+
version_requirements: *70311998135740
|
58
59
|
description: Automatic rule-based sorting for your files. Like Hazel, but doesn't
|
59
60
|
need a GUI.
|
60
61
|
email:
|
@@ -66,11 +67,14 @@ extra_rdoc_files: []
|
|
66
67
|
files:
|
67
68
|
- .gitignore
|
68
69
|
- .rspec
|
70
|
+
- .travis.yml
|
69
71
|
- Gemfile
|
70
72
|
- LICENSE.txt
|
71
73
|
- README.md
|
72
74
|
- Rakefile
|
73
75
|
- bin/filesortd
|
76
|
+
- features/listener.feature
|
77
|
+
- features/step_definitions/listener.rb
|
74
78
|
- filesortd.gemspec
|
75
79
|
- lib/filesortd.rb
|
76
80
|
- lib/filesortd/afile.rb
|
@@ -79,6 +83,7 @@ files:
|
|
79
83
|
- lib/filesortd/cond.rb
|
80
84
|
- lib/filesortd/folder.rb
|
81
85
|
- lib/filesortd/matcher.rb
|
86
|
+
- lib/filesortd/oneeight.rb
|
82
87
|
- lib/filesortd/version.rb
|
83
88
|
- spec/afile_spec.rb
|
84
89
|
- spec/callback_spec.rb
|
@@ -109,6 +114,8 @@ signing_key:
|
|
109
114
|
specification_version: 3
|
110
115
|
summary: Rule-based file sorting
|
111
116
|
test_files:
|
117
|
+
- features/listener.feature
|
118
|
+
- features/step_definitions/listener.rb
|
112
119
|
- spec/afile_spec.rb
|
113
120
|
- spec/callback_spec.rb
|
114
121
|
- spec/cond_spec.rb
|