filesortd 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +66 -0
- data/Rakefile +1 -0
- data/bin/filesortd +5 -0
- data/filesortd.gemspec +24 -0
- data/lib/filesortd/afile.rb +75 -0
- data/lib/filesortd/callback.rb +38 -0
- data/lib/filesortd/cli.rb +38 -0
- data/lib/filesortd/cond.rb +19 -0
- data/lib/filesortd/folder.rb +22 -0
- data/lib/filesortd/matcher.rb +40 -0
- data/lib/filesortd/version.rb +3 -0
- data/lib/filesortd.rb +8 -0
- data/spec/afile_spec.rb +64 -0
- data/spec/callback_spec.rb +17 -0
- data/spec/cond_spec.rb +28 -0
- data/spec/matcher_spec.rb +47 -0
- metadata +115 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in filesortd.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rb-kqueue', '~> 0.2' if RbConfig::CONFIG['target_os'] =~ /freebsd/i
|
7
|
+
gem 'rb-fsevent', '~> 0.9.1' if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
8
|
+
gem 'rb-inotify', '~> 0.8.8', :github => 'mbj/rb-inotify' if RbConfig::CONFIG['target_os'] =~ /linux/i
|
9
|
+
gem 'wdm', '~> 0.0.3' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
10
|
+
|
11
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
12
|
+
gem 'osx-plist'
|
13
|
+
end
|
14
|
+
|
15
|
+
group :development, :test do
|
16
|
+
gem 'rspec'
|
17
|
+
gem 'fuubar'
|
18
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 myfreeweb
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Filesortd
|
2
|
+
|
3
|
+
A Ruby DSL for automatically sorting your files based on rules.
|
4
|
+
Like [Hazel](http://www.noodlesoft.com/hazel.php), but cross-platform, no GUI required.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
$ gem install filesortd
|
9
|
+
|
10
|
+
If you're on OS X, also install osx-plist for things like `downloaded_from` (i.e. xattr support) to work:
|
11
|
+
|
12
|
+
$ gem install osx-plist
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
$ filesortd start yourconfig.rb
|
17
|
+
|
18
|
+
yourconfig.rb:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# DSL usage example
|
22
|
+
|
23
|
+
folder "/Users/myfreeweb/Downloads" do
|
24
|
+
|
25
|
+
# Do things to files that match a glob or a regex
|
26
|
+
match "*.mp3" do
|
27
|
+
mv "/Users/myfreeweb/Music"
|
28
|
+
|
29
|
+
# Do things if running on a particular OS
|
30
|
+
os :osx do
|
31
|
+
label :orange
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Mac OS X saves the location you downloaded a file from
|
36
|
+
downloaded_from %r{destroyallsoftware} do
|
37
|
+
mv "/Users/myfreeweb/Movies/DAS"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# You can watch multiple folders at once
|
43
|
+
|
44
|
+
folders "/Users/myfreeweb/Pictures", "/opt/pictures" do
|
45
|
+
|
46
|
+
# Do things to any files
|
47
|
+
any do
|
48
|
+
label :blue
|
49
|
+
end
|
50
|
+
|
51
|
+
match "*.png" do
|
52
|
+
pass "optipng"
|
53
|
+
label :green
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Actions:
|
59
|
+
|
60
|
+
- `contents` (or `read`) -- get the contents
|
61
|
+
- `rm` (or `remove`, `delete`, `unlink`) -- remove
|
62
|
+
- `cp` (or `copy`) -- copy
|
63
|
+
- `mv` (or `move`) -- move/rename
|
64
|
+
- `pipe(cmd)` -- start the command, pass the file to stdin, get the stdout
|
65
|
+
- `pass(cmd)` -- start the command, pass the path to the file as an argument, get the stdout
|
66
|
+
- `label(color)` -- set the OS X Finder label (:none, :orange, :red, :yellow, :blue, :purple, :green, :gray or :grey)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/filesortd
ADDED
data/filesortd.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'filesortd/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "filesortd"
|
8
|
+
gem.version = Filesortd::VERSION
|
9
|
+
gem.authors = ["myfreeweb"]
|
10
|
+
gem.email = ["floatboth@me.com"]
|
11
|
+
gem.description = %q{Automatic rule-based sorting for your files. Like Hazel, but doesn't need a GUI.}
|
12
|
+
gem.summary = %q{Rule-based file sorting}
|
13
|
+
gem.homepage = "https://github.com/myfreeweb/filesortd"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "docile"
|
21
|
+
gem.add_dependency "listen"
|
22
|
+
gem.add_dependency "popen4"
|
23
|
+
gem.add_dependency "thor"
|
24
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "popen4"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Filesortd
|
5
|
+
class Afile
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def contents
|
13
|
+
File.read @path
|
14
|
+
end
|
15
|
+
alias :read :contents
|
16
|
+
|
17
|
+
def rm
|
18
|
+
File.unlink @path
|
19
|
+
@path = nil
|
20
|
+
end
|
21
|
+
alias :remove :rm
|
22
|
+
alias :delete :rm
|
23
|
+
alias :unlink :rm
|
24
|
+
|
25
|
+
def cp(target)
|
26
|
+
FileUtils.cp @path, target
|
27
|
+
@path = target
|
28
|
+
end
|
29
|
+
alias :copy :cp
|
30
|
+
|
31
|
+
def mv(target)
|
32
|
+
FileUtils.mv @path, target
|
33
|
+
@path = target
|
34
|
+
end
|
35
|
+
alias :move :mv
|
36
|
+
|
37
|
+
def pipe(cmd)
|
38
|
+
out = nil
|
39
|
+
POpen4::popen4(cmd) do |stdout, stderr, stdin, pid|
|
40
|
+
stdin.puts File.read(@path)
|
41
|
+
stdin.close
|
42
|
+
out = stdout.read.strip
|
43
|
+
end
|
44
|
+
out
|
45
|
+
end
|
46
|
+
|
47
|
+
def pass(cmd)
|
48
|
+
out = nil
|
49
|
+
POpen4::popen4("#{cmd} #{@path.shellescape}") do |stdout, stderr, stdin, pid|
|
50
|
+
stdin.close
|
51
|
+
out = stdout.read.strip
|
52
|
+
end
|
53
|
+
out
|
54
|
+
end
|
55
|
+
|
56
|
+
def label(lbl)
|
57
|
+
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]
|
69
|
+
else
|
70
|
+
idx = lbl
|
71
|
+
end
|
72
|
+
system %{osascript -e 'tell app "Finder" to set label index of (POSIX file "#{@path}" as alias) to #{idx}' 2&>/dev/null}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "filesortd/matcher"
|
2
|
+
require "filesortd/afile"
|
3
|
+
require "docile"
|
4
|
+
|
5
|
+
module Filesortd
|
6
|
+
class Callback
|
7
|
+
attr_accessor :matchers
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@matchers = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def any(&callback)
|
14
|
+
@matchers[AlwaysTrueMatcher.new] = callback
|
15
|
+
end
|
16
|
+
|
17
|
+
def match(pattern, &callback)
|
18
|
+
@matchers[BasenameMatcher.new(pattern)] = callback
|
19
|
+
end
|
20
|
+
|
21
|
+
def downloaded_from(pattern, &callback)
|
22
|
+
pm = Matcher.new(pattern)
|
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
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(paths)
|
30
|
+
paths.each do |path|
|
31
|
+
@matchers.each do |matcher, callback|
|
32
|
+
Docile.dsl_eval(Afile.new(path), &callback) if matcher.match(path)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "filesortd"
|
3
|
+
|
4
|
+
module Filesortd
|
5
|
+
class Script
|
6
|
+
include Filesortd
|
7
|
+
attr_accessor :listeners
|
8
|
+
def initialize
|
9
|
+
@listeners = []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class CLI < Thor
|
14
|
+
default_task :start
|
15
|
+
|
16
|
+
desc 'start FILENAME', 'Start filesortd'
|
17
|
+
def start(filename)
|
18
|
+
puts 'Started filesortd'
|
19
|
+
begin
|
20
|
+
scpt = Script.new
|
21
|
+
scpt.instance_eval File.read(filename)
|
22
|
+
loop { sleep 0.1 }
|
23
|
+
# sleep to prevent 100% cpu load
|
24
|
+
rescue Interrupt
|
25
|
+
scpt.listeners.each do |l|
|
26
|
+
l.stop
|
27
|
+
end
|
28
|
+
puts 'Stopped filesortd'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'version', 'Show the Guard version'
|
33
|
+
map %w(-v --version) => :version
|
34
|
+
def version
|
35
|
+
puts ::Filesortd::VERSION
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Filesortd
|
2
|
+
def os(s, &block)
|
3
|
+
if s == current_os
|
4
|
+
block.call
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def current_os
|
10
|
+
case RbConfig::CONFIG['target_os']
|
11
|
+
when /freebsd/i then :freebsd
|
12
|
+
when /darwin(1.+)?$/i then :osx
|
13
|
+
when /linux/i then :linux
|
14
|
+
when /mswin|mingw/i then :windows
|
15
|
+
else
|
16
|
+
:unknown
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "listen"
|
2
|
+
require "docile"
|
3
|
+
require "filesortd/callback"
|
4
|
+
|
5
|
+
module Filesortd
|
6
|
+
def folders(*paths, &block)
|
7
|
+
paths.select! do |path|
|
8
|
+
e = File.exists? path
|
9
|
+
puts "Folder does not exist: #{path}" unless e
|
10
|
+
e
|
11
|
+
end
|
12
|
+
callback = Docile.dsl_eval(Callback.new, &block)
|
13
|
+
cb = Proc.new do |modified, added, removed|
|
14
|
+
puts "Processing files: #{added}"
|
15
|
+
callback.call added
|
16
|
+
end
|
17
|
+
l = Listen.to(*paths).latency(0.5).change(&cb)
|
18
|
+
l.start(false)
|
19
|
+
@listeners << l
|
20
|
+
end
|
21
|
+
alias :folder :folders
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Filesortd
|
2
|
+
class Matcher < Struct.new(:pattern)
|
3
|
+
def match(s)
|
4
|
+
if pattern.is_a? Regexp
|
5
|
+
!pattern.match(s).nil? # return a boolean through "not nil"
|
6
|
+
elsif pattern.is_a? String
|
7
|
+
File.fnmatch(pattern, s)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class BasenameMatcher < Matcher
|
13
|
+
alias :super_match :match
|
14
|
+
def match(path)
|
15
|
+
super_match(File.basename(path))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class XattrMatcher
|
20
|
+
def initialize(xattr, &matcher)
|
21
|
+
require "osx/plist"
|
22
|
+
require "Shellwords"
|
23
|
+
@xattr = xattr
|
24
|
+
@matcher = matcher
|
25
|
+
end
|
26
|
+
|
27
|
+
def match(path)
|
28
|
+
return false unless `xattr -l #{path.shellescape}`.include? @xattr
|
29
|
+
str = [`xattr -px #{@xattr.shellescape} #{path.shellescape}`.gsub("\n", "").gsub(" ", "")].pack("H*")
|
30
|
+
pl = OSX::PropertyList.load(str)
|
31
|
+
@matcher.call(pl, path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class AlwaysTrueMatcher
|
36
|
+
def match(s)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/filesortd.rb
ADDED
data/spec/afile_spec.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "filesortd/afile"
|
2
|
+
include Filesortd
|
3
|
+
|
4
|
+
describe Afile do
|
5
|
+
let(:path) { "/tmp/filesortdtest" }
|
6
|
+
|
7
|
+
it "shows contents" do
|
8
|
+
File.write path, "1"
|
9
|
+
Afile.new(path).contents.should == "1"
|
10
|
+
File.unlink path
|
11
|
+
end
|
12
|
+
|
13
|
+
it "copies" do
|
14
|
+
path2 = "/tmp/filesortdtest2"
|
15
|
+
File.write path, "1"
|
16
|
+
af = Afile.new(path)
|
17
|
+
af.cp path2
|
18
|
+
af.path.should == path2
|
19
|
+
File.exists?(path).should be_true
|
20
|
+
File.exists?(path2).should be_true
|
21
|
+
File.unlink path
|
22
|
+
File.unlink path2
|
23
|
+
end
|
24
|
+
|
25
|
+
it "moves" do
|
26
|
+
path2 = "/tmp/filesortdtest2"
|
27
|
+
File.write path, "1"
|
28
|
+
af = Afile.new(path)
|
29
|
+
af.mv path2
|
30
|
+
af.path.should == path2
|
31
|
+
File.exists?(path).should be_false
|
32
|
+
File.exists?(path2).should be_true
|
33
|
+
File.unlink path2
|
34
|
+
end
|
35
|
+
|
36
|
+
it "removes" do
|
37
|
+
File.write path, "1"
|
38
|
+
af = Afile.new(path)
|
39
|
+
af.rm
|
40
|
+
af.path.should be_nil
|
41
|
+
File.exists?(path).should be_false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "pipes" do
|
45
|
+
File.write path, "1+2"
|
46
|
+
Afile.new(path).pipe("bc").should == "3"
|
47
|
+
File.unlink path
|
48
|
+
end
|
49
|
+
|
50
|
+
it "passes" do
|
51
|
+
File.write path, "123"
|
52
|
+
Afile.new(path).pass("cat").should == "123"
|
53
|
+
File.unlink path
|
54
|
+
end
|
55
|
+
|
56
|
+
it "labels" do
|
57
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
58
|
+
File.write path, "123"
|
59
|
+
Afile.new(path).label(:red)
|
60
|
+
`osascript -e 'tell app "Finder" to get label index of (POSIX file "#{path}" as alias)'`.should == "2\n"
|
61
|
+
File.unlink path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "filesortd/callback"
|
2
|
+
include Filesortd
|
3
|
+
|
4
|
+
describe Callback do
|
5
|
+
let(:cb) { Callback.new }
|
6
|
+
|
7
|
+
it "executes callbacks for matched paths" do
|
8
|
+
true_matcher = double("matcher", :match => true)
|
9
|
+
false_matcher = double("matcher", :match => false)
|
10
|
+
true_called = false_called = false
|
11
|
+
cb.matchers[ true_matcher] = Proc.new { true_called = true }
|
12
|
+
cb.matchers[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
|
+
end
|
data/spec/cond_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "filesortd/cond"
|
2
|
+
include Filesortd
|
3
|
+
|
4
|
+
describe "os" do
|
5
|
+
it "executes on freebsd" do
|
6
|
+
RbConfig::CONFIG['target_os'] = 'freebsd'
|
7
|
+
os(:freebsd) { true }.should be_true
|
8
|
+
os(:linux) { true }.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "executes on osx" do
|
12
|
+
RbConfig::CONFIG['target_os'] = 'darwin11.3.0'
|
13
|
+
os(:osx) { true }.should be_true
|
14
|
+
os(:linux) { true }.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "executes on linux" do
|
18
|
+
RbConfig::CONFIG['target_os'] = 'linux'
|
19
|
+
os(:linux) { true }.should be_true
|
20
|
+
os(:osx) { true }.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "executes on windows" do
|
24
|
+
RbConfig::CONFIG['target_os'] = 'mingw'
|
25
|
+
os(:windows) { true }.should be_true
|
26
|
+
os(:osx) { true }.should be_nil
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "filesortd/matcher"
|
2
|
+
include Filesortd
|
3
|
+
|
4
|
+
describe BasenameMatcher do
|
5
|
+
it "matches regexps on filenames" do
|
6
|
+
BasenameMatcher.new(%r{hello-[0-9]+.mp3}).match("/Some/Folder/hello-123.mp3").should be_true
|
7
|
+
BasenameMatcher.new(%r{hello-[0-9]+.mp3}).match("/Some/Folder/321-goodbye.mp3").should be_false
|
8
|
+
end
|
9
|
+
|
10
|
+
it "matches globs on filenames" do
|
11
|
+
BasenameMatcher.new("*.jpg").match("/Some/Folder/pic.jpg").should be_true
|
12
|
+
BasenameMatcher.new("*.jpg").match("/Some/Folder/pic.png").should be_false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe XattrMatcher do
|
17
|
+
let(:path) { "/tmp/xattrtest" }
|
18
|
+
let(:prop) { "com.apple.metadata:kMDItemWhereFroms" }
|
19
|
+
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" }
|
20
|
+
|
21
|
+
before { File.write path, "123" }
|
22
|
+
after { File.unlink path }
|
23
|
+
|
24
|
+
it "returns true for matched xattrs" do
|
25
|
+
system "xattr -wx #{prop.shellescape} '#{downloaded_das}' #{path.shellescape}"
|
26
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
27
|
+
elements.map { |el| !(el =~ %r{destroyallsoftware}).nil? }.count(true) >= 1
|
28
|
+
end
|
29
|
+
m.match(path).should be_true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns false for invalid xattrs" do
|
33
|
+
system "xattr -wx #{prop.shellescape} '#{downloaded_das}' #{path.shellescape}"
|
34
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
35
|
+
elements.map { |el| !(el =~ %r{destroyNOsoftware}).nil? }.count(true) >= 1
|
36
|
+
end
|
37
|
+
m.match(path).should be_false
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns false for no xattrs" do
|
41
|
+
m = XattrMatcher.new(prop) do |elements, path|
|
42
|
+
elements.map { |el| !(el =~ %r{destroyallsoftware}).nil? }.count(true) >= 1
|
43
|
+
end
|
44
|
+
m.match(path).should be_false
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: filesortd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- myfreeweb
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: docile
|
16
|
+
requirement: &70354624421460 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70354624421460
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: listen
|
27
|
+
requirement: &70354624420780 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70354624420780
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: popen4
|
38
|
+
requirement: &70354624420200 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70354624420200
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: thor
|
49
|
+
requirement: &70354624419580 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70354624419580
|
58
|
+
description: Automatic rule-based sorting for your files. Like Hazel, but doesn't
|
59
|
+
need a GUI.
|
60
|
+
email:
|
61
|
+
- floatboth@me.com
|
62
|
+
executables:
|
63
|
+
- filesortd
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- .rspec
|
69
|
+
- Gemfile
|
70
|
+
- LICENSE.txt
|
71
|
+
- README.md
|
72
|
+
- Rakefile
|
73
|
+
- bin/filesortd
|
74
|
+
- filesortd.gemspec
|
75
|
+
- lib/filesortd.rb
|
76
|
+
- lib/filesortd/afile.rb
|
77
|
+
- lib/filesortd/callback.rb
|
78
|
+
- lib/filesortd/cli.rb
|
79
|
+
- lib/filesortd/cond.rb
|
80
|
+
- lib/filesortd/folder.rb
|
81
|
+
- lib/filesortd/matcher.rb
|
82
|
+
- lib/filesortd/version.rb
|
83
|
+
- spec/afile_spec.rb
|
84
|
+
- spec/callback_spec.rb
|
85
|
+
- spec/cond_spec.rb
|
86
|
+
- spec/matcher_spec.rb
|
87
|
+
homepage: https://github.com/myfreeweb/filesortd
|
88
|
+
licenses: []
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.11
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Rule-based file sorting
|
111
|
+
test_files:
|
112
|
+
- spec/afile_spec.rb
|
113
|
+
- spec/callback_spec.rb
|
114
|
+
- spec/cond_spec.rb
|
115
|
+
- spec/matcher_spec.rb
|