cleaner 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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