filesortd 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.8.7
5
+ - rbx-18mode
6
+ - rbx-19mode
data/Gemfile CHANGED
@@ -13,6 +13,8 @@ if RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
13
13
  end
14
14
 
15
15
  group :development, :test do
16
+ gem 'rake'
16
17
  gem 'rspec'
18
+ gem 'cucumber'
17
19
  gem 'fuubar'
18
20
  end
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
- match "*.mp3" do
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
- match "*.png" do
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
@@ -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}
@@ -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
@@ -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 :matchers
8
+ attr_accessor :matcher_sets
8
9
 
9
10
  def initialize
10
- @matchers = {}
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
- @matchers[AlwaysTrueMatcher.new] = callback
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 match(pattern, &callback)
18
- @matchers[BasenameMatcher.new(pattern)] = callback
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
- 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
41
+ match :downloaded_from => pattern, &callback
27
42
  end
28
43
 
29
44
  def call(paths)
30
45
  paths.each do |path|
31
- @matchers.each do |matcher, callback|
32
- Docile.dsl_eval(Afile.new(path), &callback) if matcher.match(path)
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
@@ -2,6 +2,7 @@ require "thor"
2
2
  require "filesortd"
3
3
 
4
4
  module Filesortd
5
+ # Evaluation context
5
6
  class Script
6
7
  include Filesortd
7
8
  attr_accessor :listeners
@@ -10,6 +11,7 @@ module Filesortd
10
11
  end
11
12
  end
12
13
 
14
+ # CLI
13
15
  class CLI < Thor
14
16
  default_task :start
15
17
 
@@ -3,20 +3,26 @@ require "docile"
3
3
  require "filesortd/callback"
4
4
 
5
5
  module Filesortd
6
- def folders(*paths, &block)
7
- paths.select! do |path|
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
- callback = Docile.dsl_eval(Callback.new, &block)
13
- cb = Proc.new do |modified, added, removed|
14
- puts "Processing files: #{added}"
15
- callback.call added
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
@@ -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
@@ -0,0 +1,9 @@
1
+ if RUBY_VERSION =~ /1.8/
2
+ class File
3
+ def self.write(path, content)
4
+ f = File.new(path, "w")
5
+ f.write(content)
6
+ f.close
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Filesortd
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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, "1+2"
46
- Afile.new(path).pipe("bc").should == "3"
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
@@ -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.matchers[ true_matcher] = Proc.new { true_called = true }
12
- cb.matchers[false_matcher] = Proc.new { false_called = true }
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
@@ -13,35 +13,46 @@ describe BasenameMatcher do
13
13
  end
14
14
  end
15
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
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
- 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
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
- 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
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
- m.match(path).should be_false
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.1.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-12 00:00:00.000000000 Z
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: &70354624421460 !ruby/object:Gem::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: *70354624421460
25
+ version_requirements: *70311998121040
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: listen
27
- requirement: &70354624420780 !ruby/object:Gem::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: *70354624420780
36
+ version_requirements: *70311998120300
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: popen4
38
- requirement: &70354624420200 !ruby/object:Gem::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: *70354624420200
47
+ version_requirements: *70311998136240
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: thor
49
- requirement: &70354624419580 !ruby/object:Gem::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: *70354624419580
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