cleaner 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ coverage
6
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format documentation
data/Gemfile CHANGED
@@ -1,4 +1,8 @@
1
1
  source "http://rubygems.org"
2
2
 
3
+ gem "fakefs", :git => "git://github.com/wijet/fakefs.git"
4
+ gem "guard", :git => "git://github.com/guard/guard.git"
5
+ gem "guard-rspec", :git => "git://github.com/guard/guard-rspec.git"
6
+
3
7
  # Specify your gem's dependencies in cleaner.gemspec
4
8
  gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ guard 'rspec', :version => 2, :cli => '--color --format documentation' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/cleaner/(.+)\.rb$}) { |m| "spec/cleaner/#{m[1]}_spec.rb" }
4
+ watch(%r{^lib/cleaner/actions/(.+)\.rb$}) { |m| "spec/cleaner/actions/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ watch(%r{spec/helpers/.*\.rb}) { "spec" }
7
+ watch('lib/cleaner.rb') { "rspec" }
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Mariusz Pietrzyk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Cleaner
2
+ ---
3
+
4
+ Tool for automatic management of directories on your disk with simple DSL.
5
+
6
+ ## Installation
7
+
8
+ $ gem install cleaner
9
+
10
+ ## Example configuration file
11
+
12
+ ```ruby
13
+ manage '~/Downloads' do
14
+ # Move avi's and audio files to right places
15
+ move :avi, :to => '~/Movies/inbox'
16
+ move %w(mp3 ogg), :to => '~/Music/inbox'
17
+
18
+ # Remove zip files if file without zip extension exists (uncompressed files)
19
+ delete :zip, :if => proc { |file| File.exists?(file.path_without_ext) }
20
+
21
+ # Move all VAT invoices to a special place
22
+ move :pdf, :if => proc { |file| file.name =~ /VAT/ }, :to => '~/Documents/invoices'
23
+
24
+ # You've probably installed it already
25
+ delete :dmg, :after => 10.hours
26
+
27
+ # Delete everything older than 1 month.
28
+ # Was here for so long? Doesn't deserve to exist!
29
+ delete :after => 1.month
30
+ end
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ Generate sample configuration file (~/.cleaner.rb)
36
+
37
+ $ cleaner init
38
+
39
+ Run cleaner in the background. By default it will run every 1 hour.
40
+
41
+ $ cleaner start
42
+
43
+ You can specify cleaning interval with "rails like" syntax: 20.minutes, 4.hours, 1.day
44
+
45
+ $ cleaner start 4.hours
46
+
47
+ Stop cleaner daemon
48
+
49
+ $ cleaner stop
50
+
51
+ ## Contributions
52
+
53
+ To fetch & test the library for development, do:
54
+
55
+ $ git clone https://github.com/wijet/cleaner
56
+ $ cd cleaner
57
+ $ bundle
58
+ $ bundle exec rspec
59
+
60
+ If you wont to contribute, please:
61
+
62
+ * Fork the project.
63
+ * Make your feature addition or bug fix.
64
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
65
+ * Send me a pull request on Github.
data/bin/cleaner ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "cleaner"
3
+ require "cleaner/cli"
4
+ Cleaner::CLI.start
data/cleaner.gemspec CHANGED
@@ -7,9 +7,12 @@ Gem::Specification.new do |s|
7
7
  s.version = Cleaner::VERSION
8
8
  s.authors = ["Mariusz Pietrzyk"]
9
9
  s.email = ["wijet@wijet.pl"]
10
- s.homepage = ""
11
- s.summary = %q{Write a gem summary}
12
- s.description = %q{Write a gem description}
10
+ s.homepage = "http://github.com/wijet/cleaner"
11
+ s.summary = %q{Tool for cleaning up your directories with friendly DSL}
12
+ s.description = <<desc
13
+ Cleaner is a small tool which helps you keep your directories clean.
14
+ With simple DSL you define set of rules, which are then periodically executed against specified directory.
15
+ desc
13
16
 
14
17
  s.rubyforge_project = "cleaner"
15
18
 
@@ -17,4 +20,16 @@ Gem::Specification.new do |s|
17
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
22
  s.require_paths = ["lib"]
23
+
24
+ s.add_dependency "activesupport", "2.3.14"
25
+ s.add_dependency "filelist"
26
+ s.add_dependency "thor"
27
+ s.add_dependency "daemons"
28
+ s.add_development_dependency "rspec"
29
+ s.add_development_dependency "simplecov"
30
+ s.add_development_dependency "timecop"
31
+ if RUBY_PLATFORM =~ /darwin/
32
+ s.add_development_dependency "rb-fsevent"
33
+ s.add_development_dependency "ruby_gntp"
34
+ end
20
35
  end
@@ -0,0 +1,18 @@
1
+ manage '~/Downloads' do
2
+ # Move avi's and audio files to right places
3
+ # move :avi, :to => '~/Movies/inbox'
4
+ # move %w(mp3 ogg), :to => '~/Music/inbox'
5
+
6
+ # Remove zip files if file without zip extension exists (uncompressed files)
7
+ # delete :zip, :if => proc { |file| File.exists?(file.path_without_ext) }
8
+
9
+ # Move all VAT invoices to a special place
10
+ # move :pdf, :if => proc { |file| file.name =~ /VAT/ }, :to => '~/Documents/invoices'
11
+
12
+ # You've probably installed it already
13
+ # delete :dmg, :after => 10.hours
14
+
15
+ # Delete everything older than 1 month.
16
+ # Was here for so long? Doesn't deserve to exist!
17
+ # delete :after => 1.month
18
+ end
data/lib/cleaner.rb CHANGED
@@ -1,5 +1,12 @@
1
- require "cleaner/version"
1
+ require "active_support/all"
2
+ require "filelist"
2
3
 
3
- module Cleaner
4
- # Your code goes here...
5
- end
4
+ require "cleaner/action"
5
+ require "cleaner/actions/delete"
6
+ require "cleaner/actions/move"
7
+ require "cleaner/actions/copy"
8
+ require "cleaner/file_extension"
9
+ require "cleaner/directory"
10
+ require "cleaner/runner"
11
+ require "cleaner/file_filter"
12
+ require "cleaner/version"
@@ -0,0 +1,17 @@
1
+ module Cleaner
2
+ class Action
3
+ attr_reader :files, :options
4
+
5
+ def initialize(files, options = {})
6
+ @files = files
7
+ @options = options
8
+ end
9
+
10
+ def execute
11
+ raise NotImplementedException.new("#execute should be implemented in your Action class")
12
+ end
13
+
14
+ class UnknownActionException < Exception; end
15
+ class NotImplementedException < Exception; end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ require 'fileutils'
2
+
3
+ module Cleaner
4
+ module Actions
5
+ class Copy < Action
6
+ def execute
7
+ dest = File.expand_path(options[:to])
8
+ FileUtils.mkdir_p(dest)
9
+ FileUtils.cp_r(files, dest)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Cleaner
2
+ module Actions
3
+ class Delete < Action
4
+ def execute
5
+ FileUtils.rm(files)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Cleaner
2
+ module Actions
3
+ class Move < Action
4
+ def execute
5
+ dest = File.expand_path(options[:to])
6
+ FileUtils.mkdir_p(dest)
7
+ files.each { |src| FileUtils.mv(src, dest) }
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,75 @@
1
+ require "thor"
2
+ require "daemons"
3
+
4
+ module Cleaner
5
+ class CLI < Thor
6
+ include Thor::Actions
7
+ attr_reader :interval
8
+ source_root("#{File.dirname(__FILE__)}/../../examples")
9
+
10
+ desc 'init', 'Create sample config file in ~/.cleaner.rb'
11
+ def init
12
+ copy_file "cleaner.rb", "~/.cleaner.rb"
13
+ end
14
+
15
+ desc 'start [INTERVAL]', %q{
16
+ Start cleaner in background. It cleans directories every 1 hour
17
+ }
18
+ def start(interval = "1.hour")
19
+ @interval = eval(interval)
20
+ daemon.start
21
+ end
22
+
23
+ desc 'stop', 'Stop cleaner in background'
24
+ def stop
25
+ daemon.stop
26
+ end
27
+
28
+ desc 'cleanup', 'Run cleaner ad hoc'
29
+ def cleanup
30
+ run_cleaner
31
+ end
32
+
33
+ no_tasks do
34
+ def daemon
35
+ group = Daemons::ApplicationGroup.new('cleaner', daemon_options)
36
+ group.new_application(daemon_options)
37
+ end
38
+
39
+ def daemon_options
40
+ {
41
+ :mode => :proc,
42
+ :proc => runner_proc,
43
+ :dir_mode => :normal,
44
+ :dir => "/tmp"
45
+ }
46
+ end
47
+
48
+ def runner_proc
49
+ Proc.new do
50
+ loop do
51
+ run_cleaner
52
+ sleep interval
53
+ end
54
+ end
55
+ end
56
+
57
+ def run_cleaner
58
+ cleaner = Cleaner::Runner.new(load_rules)
59
+ cleaner.start
60
+ end
61
+
62
+ def load_rules
63
+ File.read(config_file_path)
64
+ rescue Errno::ENOENT
65
+ say "Config file #{config_file_path} doesn't exist", :red
66
+ say "Generate sample config using `cleaner init`", :green
67
+ exit 1
68
+ end
69
+
70
+ def config_file_path
71
+ File.expand_path("~/.cleaner.rb")
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,35 @@
1
+ module Cleaner
2
+ class Directory
3
+ attr_reader :path, :block
4
+
5
+ def initialize(path, block)
6
+ @path = path
7
+ @block = block
8
+ end
9
+
10
+ def clean
11
+ instance_eval(&block)
12
+ end
13
+
14
+ protected
15
+
16
+ def method_missing(method, *args, &block)
17
+ action_class = construct_action_class(method)
18
+ # FIXME: refactor this argument parsing
19
+ options = args.last.is_a?(Hash) ? args.last : {}
20
+ options = options.merge(
21
+ :pattern => args.first.is_a?(Hash) ? nil : args.first,
22
+ :path => path
23
+ )
24
+ filter = FileFilter.new(options)
25
+ action = action_class.new(filter.filterize, options)
26
+ action.execute
27
+ end
28
+
29
+ def construct_action_class(method)
30
+ Cleaner::Actions::const_get(method.to_s.capitalize)
31
+ rescue NameError => e
32
+ raise Action::UnknownActionException.new("Action '#{method}' is unknown")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module Cleaner
2
+ # Provides helper methods for File object which is passed to conditions.
3
+ # With this module we can write file.name insted of File.basename(file)
4
+ #
5
+ module FileExtension
6
+ def name
7
+ File.basename(path)
8
+ end
9
+
10
+ def path_without_ext
11
+ pathname = Pathname.new(path)
12
+ pathname.sub_ext("").to_s
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ module Cleaner
2
+ class FileFilter
3
+ CONDITIONS = [:after, :if]
4
+
5
+ attr_reader :options
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ @pattern = options[:pattern]
10
+ end
11
+
12
+ def filterize
13
+ paths = filter_by_name
14
+ conditions_provided? ? filter_by_conditions(paths) : paths
15
+ end
16
+
17
+ def search_pattern
18
+ case @pattern
19
+ when NilClass; "#{path}/*"
20
+ when String; "#{path}/#{@pattern}"
21
+ when Symbol; "#{path}/*.#{@pattern}"
22
+ when Array; @pattern.map { |ext| "#{path}/*.#{ext}" }
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def filter_by_conditions(paths)
29
+ paths.select do |path|
30
+ after_condition?(path) && if_condition?(path)
31
+ end
32
+ end
33
+
34
+ def conditions_provided?
35
+ options.keys.any? { |c| CONDITIONS.include?(c) }
36
+ end
37
+
38
+ def after_condition?(path)
39
+ return true unless options.has_key?(:after)
40
+ File.ctime(path) < options[:after].ago
41
+ end
42
+
43
+ def if_condition?(path)
44
+ return true unless options.has_key?(:if)
45
+ file = File.new(path)
46
+ file.extend(FileExtension)
47
+ options[:if].call(file)
48
+ end
49
+
50
+ def path
51
+ File.expand_path(options[:path])
52
+ end
53
+
54
+ def filter_by_name
55
+ FileList[search_pattern]
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ module Cleaner
2
+ class Runner
3
+ attr_reader :rules, :directories
4
+
5
+ def initialize(rules)
6
+ @rules = rules
7
+ @directories = []
8
+ end
9
+
10
+ def start
11
+ instance_eval(rules)
12
+ directories.each(&:clean)
13
+ end
14
+
15
+ protected
16
+
17
+ def manage(path, &block)
18
+ @directories << Directory.new(path, block)
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Cleaner
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Cleaner::Action do
4
+ let(:files) { %w(file1 file2) }
5
+ let(:options) { {:to => '~/foo'} }
6
+ let(:action) { Cleaner::Action.new(files, options) }
7
+
8
+ describe "being initialized" do
9
+ it "should have files" do
10
+ action.files.should be(files)
11
+ end
12
+
13
+ it "should have options" do
14
+ action.options.should be(options)
15
+ end
16
+ end
17
+
18
+ describe "#execute" do
19
+ it "should raise exception" do
20
+ lambda {
21
+ action.execute
22
+ }.should raise_error(Cleaner::Action::NotImplementedException,
23
+ "#execute should be implemented in your Action class")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Cleaner::Actions::Copy do
4
+ before do
5
+ example_dir '/foo' do
6
+ %w(a.zip c.zip).each { |name| touch name }
7
+ touch 'b.txt', :content => 'btext'
8
+ end
9
+
10
+ example_dir '/foo/bar' do
11
+ touch 'barfile', :content => 'bartext'
12
+ end
13
+
14
+ Dir.chdir('/foo')
15
+ end
16
+
17
+ let(:copy) { Cleaner::Actions::Copy.new(%w(b.txt bar), :to => "/somewhere/dest") }
18
+
19
+ context "destination doesn't exist" do
20
+ it "should create it" do
21
+ File.exists?("/somewhere/dest").should be_false
22
+ copy.execute
23
+ File.exists?("/somewhere/dest").should be_true
24
+ end
25
+ end
26
+
27
+ it "should copy files to destination" do
28
+ copy.execute
29
+ File.read("/somewhere/dest/b.txt").should == "btext"
30
+ end
31
+
32
+ it "should copy directories to destination" do
33
+ copy.execute
34
+ File.read("/somewhere/dest/bar/barfile").should == "bartext"
35
+ end
36
+
37
+ it "should leave source files" do
38
+ copy.execute
39
+ File.read("/foo/b.txt").should == "btext"
40
+ File.read("/foo/bar/barfile").should == "bartext"
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Cleaner::Actions::Delete do
4
+ before do
5
+ example_dir '/foo' do
6
+ %w(a.zip b.txt c.zip).each { |name| touch name }
7
+ example_dir '/foo/bar'
8
+ end
9
+ Dir.chdir('/foo')
10
+ end
11
+
12
+ let(:delete) { Cleaner::Actions::Delete.new(%w(a.zip c.zip bar)) }
13
+
14
+ it "should delete given files and directories" do
15
+ delete.execute
16
+ File.exists?('a.zip').should be_false
17
+ File.exists?('c.zip').should be_false
18
+ File.exists?('bar').should be_false
19
+ end
20
+
21
+ it "should leave other files" do
22
+ delete.execute
23
+ File.exists?('b.txt').should be_true
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Cleaner::Actions::Move do
4
+ before do
5
+ example_dir '/foo' do
6
+ %w(a.zip b.txt).each { |name| touch name }
7
+ end
8
+ Dir.chdir('/foo')
9
+ end
10
+
11
+ let(:move) { Cleaner::Actions::Move.new(%w(a.zip), :to => "~/zip-files") }
12
+
13
+ context "destination dir doesn't exist" do
14
+ it "should create it" do
15
+ move.execute
16
+ File.exists?(File.expand_path("~/zip-files")).should be_true
17
+ end
18
+ end
19
+
20
+ it "should move files" do
21
+ move.execute
22
+ File.exists?("a.zip").should be_false
23
+ File.exists?(File.expand_path("~/zip-files/a.zip")).should be_true
24
+ end
25
+
26
+ it "should leave other files" do
27
+ move.execute
28
+ File.exists?("b.txt").should be_true
29
+ end
30
+ end
@@ -0,0 +1,115 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+ require "cleaner/cli"
3
+
4
+ describe Cleaner::CLI do
5
+ let(:rules) { "manage('~/Downloads') {}" }
6
+ let(:cli) { Cleaner::CLI.new }
7
+ before do
8
+ @home_path = File.expand_path("~")
9
+ @config_path = File.join(@home_path, ".cleaner.rb")
10
+ FileUtils.mkdir_p(@home_path)
11
+ File.open(@config_path, "w") do |file|
12
+ file << "manage('~/Downloads') {}"
13
+ end
14
+ @app = mock(:start => nil)
15
+ end
16
+
17
+ describe "#init" do
18
+ it "should copy sample config to ~/.cleaner.rb" do
19
+ cli.should_receive("copy_file").with("cleaner.rb", "~/.cleaner.rb")
20
+ cli.init
21
+ end
22
+ end
23
+
24
+ describe "#start" do
25
+ before do
26
+ cli.stub(:daemon).and_return(@app)
27
+ end
28
+
29
+ it "should invoke start on daemon" do
30
+ @app.should_receive(:start)
31
+ cli.should_receive(:daemon).and_return(@app)
32
+ cli.start
33
+ end
34
+
35
+ it "should assign and eval time interval" do
36
+ cli.start
37
+ cli.interval.should == 1.hour
38
+ end
39
+
40
+ it "should accept custom interval" do
41
+ cli.start("20.minutes")
42
+ cli.interval.should == 20.minutes
43
+ end
44
+ end
45
+
46
+ describe "#stop" do
47
+ it "should invoke stop on daemon" do
48
+ @app.should_receive(:stop)
49
+ cli.should_receive(:daemon).and_return(@app)
50
+ cli.stop
51
+ end
52
+ end
53
+
54
+ describe "#cleanup" do
55
+ it "should invoke run_cleaner" do
56
+ cli.should_receive(:run_cleaner)
57
+ cli.cleanup
58
+ end
59
+ end
60
+
61
+ describe "#run_cleaner" do
62
+ before do
63
+ @runner = mock(:start => nil)
64
+ Cleaner::Runner.stub(:new).and_return(@runner)
65
+ end
66
+
67
+ it "should initialize runner with rules" do
68
+ Cleaner::Runner.should_receive(:new).with(rules)
69
+ cli.run_cleaner
70
+ end
71
+
72
+ it "should start the runner" do
73
+ @runner.should_receive(:start)
74
+ cli.run_cleaner
75
+ end
76
+ end
77
+
78
+ describe "#daemon" do
79
+ it "should construct daemon application" do
80
+ proc = Proc.new {}
81
+ cli.stub(:runner_proc).and_return(proc)
82
+ group = mock
83
+ options = {:mode => :proc, :proc => proc, :dir_mode => :normal, :dir => "/tmp"}
84
+ group.should_receive(:new_application).with(options)
85
+ Daemons::ApplicationGroup.should_receive(:new).with('cleaner', options).and_return(group)
86
+ cli.daemon
87
+ end
88
+ end
89
+
90
+ describe "#load_rules" do
91
+ it "should load config file" do
92
+ cli.load_rules.should == rules
93
+ end
94
+
95
+ context "on missing file" do
96
+ before do
97
+ FileUtils.rm_rf(@config_path)
98
+ $stdout.stub(:puts)
99
+ end
100
+
101
+ it "should display error message" do
102
+ $stdout.should_receive(:puts).with("\e[31mConfig file #{@config_path} doesn't exist\e[0m")
103
+ lambda {
104
+ cli.load_rules
105
+ }.should raise_error(SystemExit)
106
+ end
107
+
108
+ it "should exit with 1" do
109
+ lambda {
110
+ cli.load_rules
111
+ }.should raise_error { |error| error.status.should be(1) }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Cleaner::Directory do
4
+ let(:block) do
5
+ Proc.new do
6
+ delete :dmg, :after => 10.days
7
+ delete :download
8
+ delete :after => 100.days
9
+ end
10
+ end
11
+
12
+ let(:directory) { Cleaner::Directory.new('~/downloads', block)}
13
+
14
+ describe "being initialize" do
15
+ it "should have path" do
16
+ directory.path.should == '~/downloads'
17
+ end
18
+
19
+ it "should have block" do
20
+ directory.block.should == block
21
+ end
22
+ end
23
+
24
+ describe "#clean" do
25
+ it "should eval block in its own context" do
26
+ block = Proc.new {}
27
+ directory = Cleaner::Directory.new('~/downloads', block)
28
+ directory.should_receive(:instance_eval).with(&block)
29
+ directory.clean
30
+ end
31
+ end
32
+
33
+ describe "#method_missing" do
34
+ before do
35
+ @filter = mock(:filterize => %w(file1))
36
+ Cleaner::FileFilter.stub(:new).and_return(@filter)
37
+ @action = mock(:execute => true)
38
+ Cleaner::Actions::Delete.stub(:new).and_return(@action)
39
+ end
40
+
41
+ context "on unknown action" do
42
+ it "should raise Action::UnknownActionException" do
43
+ lambda {
44
+ block = Proc.new { fooooo :txt }
45
+ directory = Cleaner::Directory.new('~/downloads', block)
46
+ directory.clean
47
+ }.should raise_error(Cleaner::Action::UnknownActionException, "Action 'fooooo' is unknown")
48
+ end
49
+ end
50
+
51
+ it "should initialize FileFilter object" do
52
+ Cleaner::FileFilter.should_receive(:new).with(
53
+ :pattern => :dmg,
54
+ :path => '~/downloads',
55
+ :after => 10.days
56
+ )
57
+ directory.clean
58
+ end
59
+
60
+ it "should initialize action class with files and options" do
61
+ Cleaner::Actions::Delete.should_receive(:new).with(%w(file1),
62
+ :pattern => :dmg,
63
+ :path => '~/downloads',
64
+ :after => 10.days
65
+ )
66
+ directory.clean
67
+ end
68
+
69
+ context "when no pattern is given" do
70
+ it "should initialize action with nil as pattern" do
71
+ Cleaner::Actions::Delete.should_receive(:new).with(%w(file1),
72
+ :pattern => nil,
73
+ :path => '~/downloads',
74
+ :after => 100.days
75
+ )
76
+ directory.clean
77
+ end
78
+ end
79
+
80
+ it "should execute action" do
81
+ @action.should_receive(:execute)
82
+ directory.clean
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Cleaner::FileExtension do
4
+ before do
5
+ example_dir '/foo' do
6
+ touch 'bar.txt'
7
+ end
8
+ end
9
+
10
+ let(:file) { File.new("/foo/bar.txt").extend(Cleaner::FileExtension) }
11
+
12
+ describe "#name" do
13
+ it "should return file name" do
14
+ file.name.should == "bar.txt"
15
+ end
16
+ end
17
+
18
+ describe "#path_without_ext" do
19
+ it "should return file path name without file extension" do
20
+ file.path_without_ext.should == "/foo/bar"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,76 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Cleaner::FileFilter do
4
+ before do
5
+ example_dir '/foo' do
6
+ touch 'b-0.zip', :mtime => 10.days.ago
7
+ touch 'b-1.zip', :ctime => 2.days.ago
8
+ touch 'cc.txt', :ctime => 3.days.ago
9
+ end
10
+ end
11
+
12
+ def filter(options = {})
13
+ options = {:path => '/foo', :pattern => :zip}.merge(options)
14
+ Cleaner::FileFilter.new(options)
15
+ end
16
+
17
+ describe "being initialize" do
18
+ it "should have options" do
19
+ filter.options.should == {:path => '/foo', :pattern => :zip}
20
+ end
21
+ end
22
+
23
+ describe "#filterize" do
24
+ it "should return files filtered by name" do
25
+ filter.filterize.should == %w(/foo/b-0.zip /foo/b-1.zip)
26
+ end
27
+
28
+ it "should return files filtered by :after condition (using change time)" do
29
+ filter(:after => 1.day).filterize.should == %w(/foo/b-1.zip)
30
+ end
31
+
32
+ it "should return files filtered by :if option" do
33
+ filter(
34
+ :if => proc { |file| file.name =~ /\w\w\.txt$/ },
35
+ :pattern => nil
36
+ ).filterize.should == %w(/foo/cc.txt)
37
+ end
38
+
39
+ it "should return files filtered by two conditions :if and :after" do
40
+ filter(
41
+ :after => 1.day,
42
+ :if => proc { |file| file.name =~ /c/ },
43
+ :pattern => nil
44
+ ).filterize.should == %w(/foo/cc.txt)
45
+ end
46
+ end
47
+
48
+ describe "#search_pattern" do
49
+ context "when nil given" do
50
+ it "should use '*' as filename pattern" do
51
+ filter = Cleaner::FileFilter.new(:path => '/some', :pattern => nil)
52
+ filter.search_pattern.should == '/some/*'
53
+ end
54
+ end
55
+
56
+ context "when symbol given" do
57
+ it "should be used as file extension" do
58
+ filter.search_pattern.should == '/foo/*.zip'
59
+ end
60
+ end
61
+
62
+ context "when string given" do
63
+ it "should be used as filename pattern" do
64
+ filter = Cleaner::FileFilter.new(:path => '/some', :pattern => 'foo.*')
65
+ filter.search_pattern.should == '/some/foo.*'
66
+ end
67
+ end
68
+
69
+ context "when Array given" do
70
+ it "should use every element as file extension pattern" do
71
+ filter = Cleaner::FileFilter.new(:path => '/some', :pattern => %w(pdf doc))
72
+ filter.search_pattern.should == %w(/some/*.pdf /some/*.doc)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,107 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe "Cleaner: integration spec" do
4
+ let(:config) do %q{
5
+ manage '~/Downloads' do
6
+ delete :if => proc { |file| file.name =~ /secret-\d\.txt/ }
7
+ move :avi, :to => '~/Movies'
8
+ delete :zip, :if => proc { |file| File.exists?(file.path_without_ext) }
9
+ delete :dmg, :after => 5.days
10
+ delete %w(rar gz)
11
+ delete :after => 1.month
12
+ end
13
+
14
+ manage '/foo/bar' do
15
+ copy %w(pdf), :to => '~/Documents'
16
+ delete "abc.*"
17
+ end
18
+ }
19
+ end
20
+
21
+ let(:runner) { Cleaner::Runner.new(config) }
22
+
23
+ before do
24
+ example_dir '~/Downloads' do
25
+ touch 'Firefox99.dmg', :ctime => 7.days.ago
26
+ touch 'growl.dmg', :ctime => 2.days.ago
27
+ touch 'something-old', :ctime => 2.months.ago
28
+ touch 'lol-cat.avi'
29
+ touch 'something.rar'
30
+ touch 'another-thing.gz'
31
+ touch 'thing.dmg.zip'
32
+ touch 'thing.dmg'
33
+ touch 'thing2.zip'
34
+ 2.times do |i|
35
+ touch "important-#{i}.doc"
36
+ touch "secret-#{i}.txt"
37
+ end
38
+ end
39
+
40
+ example_dir '/foo/bar' do
41
+ %w(abc.txt abc.mp3 ddd.doc doc.pdf).each { |name| touch name }
42
+ end
43
+
44
+ runner.start
45
+ end
46
+
47
+ context "~/Downloads directory" do
48
+ before { Dir.chdir("~/Downloads") }
49
+
50
+ it "should delete :dmg files older than 5 days" do
51
+ File.exists?("Firefox99.dmg").should be_false
52
+ end
53
+
54
+ it "should leave :dmg files younger than 5 days" do
55
+ File.exists?("growl.dmg").should be_true
56
+ end
57
+
58
+ it "should leave :doc files" do
59
+ File.exists?("important-0.doc").should be_true
60
+ File.exists?("important-1.doc").should be_true
61
+ end
62
+
63
+ it "should delete everything that is older than 1 month" do
64
+ File.exists?("something-old").should be_false
65
+ end
66
+
67
+ it "should move :avi files to ~/Movies" do
68
+ file = File.expand_path("~/Movies/lol-cat.avi")
69
+ File.exists?(file).should be_true
70
+ File.exists?("lol-cat.avi").should be_false
71
+ end
72
+
73
+ it "shoud remove rar and gz files given as Array of extensions" do
74
+ File.exists?("something.rar").should be_false
75
+ File.exists?("another-thing.gz").should be_false
76
+ end
77
+
78
+ it "should delete files by :if condition" do
79
+ File.exists?("secret-1.txt").should be_false
80
+ File.exists?("secret-2.txt").should be_false
81
+ end
82
+
83
+ it "should delete zip archives if uncompressed file exist" do
84
+ # File thing.dmg exists
85
+ File.exists?("thing.dmg.zip").should be_false
86
+ # File thing2 doesn't exist
87
+ File.exists?("thing2.zip").should be_true
88
+ end
89
+ end
90
+
91
+ context "/foo/bar directory" do
92
+ before { Dir.chdir("/foo/bar") }
93
+
94
+ it "should delete all files maching abc.*" do
95
+ File.exists?("abc.txt").should be_false
96
+ File.exists?("abc.mp3").should be_false
97
+ end
98
+
99
+ it "should leave not matching abc.*" do
100
+ File.exists?("ddd.doc").should be_true
101
+ end
102
+
103
+ it "should copy file to destination" do
104
+ File.exists?(File.expand_path("~/Documents/doc.pdf")).should be_true
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Cleaner::Runner do
4
+ let(:rules) do
5
+ "manage '~/Downloads' do
6
+ end"
7
+ end
8
+ let(:runner) { Cleaner::Runner.new(rules) }
9
+
10
+ describe "being initialize" do
11
+ it "should have rules" do
12
+ runner.rules.should == rules
13
+ end
14
+ end
15
+
16
+ describe "#start" do
17
+ it "should instance eval rules" do
18
+ runner.should_receive(:instance_eval).with(rules)
19
+ runner.start
20
+ end
21
+
22
+ it "should run #clean on all directories" do
23
+ directory = mock
24
+ directory.should_receive(:clean)
25
+ runner.stub(:directories).and_return([directory])
26
+ runner.start
27
+ end
28
+ end
29
+
30
+ describe "#manage" do
31
+ it "should initialize new directory" do
32
+ runner.start
33
+ directory = runner.directories.first
34
+ directory.path.should == "~/Downloads"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require 'timecop'
2
+
3
+ module ExampleDirHelper
4
+ def example_dir(name, &block)
5
+ path = File.expand_path(name)
6
+ FileUtils.mkdir_p(path)
7
+ Dir.chdir(path) { block.call } if block
8
+ end
9
+
10
+ def touch(name, options = {})
11
+ options[:mtime] ||= Time.now
12
+ options[:atime] ||= Time.now
13
+ options[:ctime] ||= Time.now
14
+ Timecop.freeze(options[:ctime]) do
15
+ FileUtils.touch(name)
16
+ end
17
+ File.utime(options[:atime], options[:mtime], name)
18
+ File.open(name, "w") { |f| f << options[:content] } if options[:content]
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'rspec'
7
+ require 'fakefs/spec_helpers'
8
+ require 'helpers/example_dir_helper'
9
+ require 'cleaner'
10
+
11
+ RSpec.configure do |config|
12
+ config.include FakeFS::SpecHelpers
13
+ config.include ExampleDirHelper
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cleaner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,151 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-27 00:00:00.000000000Z
13
- dependencies: []
14
- description: Write a gem description
12
+ date: 2012-01-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70196097750240 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.14
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70196097750240
25
+ - !ruby/object:Gem::Dependency
26
+ name: filelist
27
+ requirement: &70196097749820 !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: *70196097749820
36
+ - !ruby/object:Gem::Dependency
37
+ name: thor
38
+ requirement: &70196097749360 !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: *70196097749360
47
+ - !ruby/object:Gem::Dependency
48
+ name: daemons
49
+ requirement: &70196097748940 !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: *70196097748940
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &70196097748520 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70196097748520
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: &70196097748100 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70196097748100
80
+ - !ruby/object:Gem::Dependency
81
+ name: timecop
82
+ requirement: &70196097747680 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70196097747680
91
+ - !ruby/object:Gem::Dependency
92
+ name: rb-fsevent
93
+ requirement: &70196097747220 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70196097747220
102
+ - !ruby/object:Gem::Dependency
103
+ name: ruby_gntp
104
+ requirement: &70196097746800 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70196097746800
113
+ description: ! " Cleaner is a small tool which helps you keep your directories clean.\n
114
+ \ With simple DSL you define set of rules, which are then periodically executed
115
+ against specified directory.\n"
15
116
  email:
16
117
  - wijet@wijet.pl
17
- executables: []
118
+ executables:
119
+ - cleaner
18
120
  extensions: []
19
121
  extra_rdoc_files: []
20
122
  files:
21
123
  - .gitignore
124
+ - .rspec
22
125
  - Gemfile
126
+ - Guardfile
127
+ - LICENSE
128
+ - README.md
23
129
  - Rakefile
130
+ - bin/cleaner
24
131
  - cleaner.gemspec
132
+ - examples/cleaner.rb
25
133
  - lib/cleaner.rb
134
+ - lib/cleaner/action.rb
135
+ - lib/cleaner/actions/copy.rb
136
+ - lib/cleaner/actions/delete.rb
137
+ - lib/cleaner/actions/move.rb
138
+ - lib/cleaner/cli.rb
139
+ - lib/cleaner/directory.rb
140
+ - lib/cleaner/file_extension.rb
141
+ - lib/cleaner/file_filter.rb
142
+ - lib/cleaner/runner.rb
26
143
  - lib/cleaner/version.rb
27
- homepage: ''
144
+ - spec/cleaner/action_spec.rb
145
+ - spec/cleaner/actions/copy_spec.rb
146
+ - spec/cleaner/actions/delete_spec.rb
147
+ - spec/cleaner/actions/move_spec.rb
148
+ - spec/cleaner/cli_spec.rb
149
+ - spec/cleaner/directory_spec.rb
150
+ - spec/cleaner/file_extension_spec.rb
151
+ - spec/cleaner/file_filter_spec.rb
152
+ - spec/cleaner/integration_spec.rb
153
+ - spec/cleaner/runner_spec.rb
154
+ - spec/helpers/example_dir_helper.rb
155
+ - spec/spec_helper.rb
156
+ homepage: http://github.com/wijet/cleaner
28
157
  licenses: []
29
158
  post_install_message:
30
159
  rdoc_options: []
@@ -47,5 +176,17 @@ rubyforge_project: cleaner
47
176
  rubygems_version: 1.8.10
48
177
  signing_key:
49
178
  specification_version: 3
50
- summary: Write a gem summary
51
- test_files: []
179
+ summary: Tool for cleaning up your directories with friendly DSL
180
+ test_files:
181
+ - spec/cleaner/action_spec.rb
182
+ - spec/cleaner/actions/copy_spec.rb
183
+ - spec/cleaner/actions/delete_spec.rb
184
+ - spec/cleaner/actions/move_spec.rb
185
+ - spec/cleaner/cli_spec.rb
186
+ - spec/cleaner/directory_spec.rb
187
+ - spec/cleaner/file_extension_spec.rb
188
+ - spec/cleaner/file_filter_spec.rb
189
+ - spec/cleaner/integration_spec.rb
190
+ - spec/cleaner/runner_spec.rb
191
+ - spec/helpers/example_dir_helper.rb
192
+ - spec/spec_helper.rb