checksummer 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -6,11 +6,13 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  gem "json"
9
+ gem "thor"
9
10
 
10
11
  group :development do
12
+ gem 'dynport_tools'
11
13
  gem 'autotest'
12
14
  gem 'autotest-growl'
13
- gem 'ruby-debug19'
15
+ gem 'ruby-debug'
14
16
  gem "cucumber", ">= 0"
15
17
  gem "rspec", "~> 2.3.0"
16
18
  gem "bundler", "~> 1.0.0"
@@ -2,12 +2,11 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  ZenTest (4.4.2)
5
- archive-tar-minitar (0.5.2)
6
5
  autotest (4.4.6)
7
6
  ZenTest (>= 4.4.1)
8
7
  autotest-growl (0.2.9)
9
8
  builder (3.0.0)
10
- columnize (0.3.2)
9
+ columnize (0.3.4)
11
10
  cucumber (0.10.2)
12
11
  builder (>= 2.1.2)
13
12
  diff-lcs (>= 1.1.2)
@@ -15,6 +14,12 @@ GEM
15
14
  json (>= 1.4.6)
16
15
  term-ansicolor (>= 1.0.5)
17
16
  diff-lcs (1.1.2)
17
+ dynport_tools (0.2.18)
18
+ diff-lcs
19
+ nokogiri
20
+ redis
21
+ term-ansicolor
22
+ typhoeus
18
23
  gherkin (2.3.5)
19
24
  json (>= 1.4.6)
20
25
  git (1.2.5)
@@ -23,10 +28,14 @@ GEM
23
28
  git (>= 1.2.5)
24
29
  rake
25
30
  json (1.5.1)
26
- linecache19 (0.5.11)
27
- ruby_core_source (>= 0.1.4)
31
+ linecache (0.46)
32
+ rbx-require-relative (> 0.0.4)
33
+ mime-types (1.16)
34
+ nokogiri (1.5.0)
28
35
  rake (0.8.7)
36
+ rbx-require-relative (0.0.5)
29
37
  rcov (0.9.9)
38
+ redis (2.2.2)
30
39
  rspec (2.3.0)
31
40
  rspec-core (~> 2.3.0)
32
41
  rspec-expectations (~> 2.3.0)
@@ -35,18 +44,17 @@ GEM
35
44
  rspec-expectations (2.3.0)
36
45
  diff-lcs (~> 1.1.2)
37
46
  rspec-mocks (2.3.0)
38
- ruby-debug-base19 (0.11.24)
39
- columnize (>= 0.3.1)
40
- linecache19 (>= 0.5.11)
41
- ruby_core_source (>= 0.1.4)
42
- ruby-debug19 (0.11.6)
43
- columnize (>= 0.3.1)
44
- linecache19 (>= 0.5.11)
45
- ruby-debug-base19 (>= 0.11.19)
46
- ruby_core_source (0.1.4)
47
- archive-tar-minitar (>= 0.5.2)
47
+ ruby-debug (0.10.4)
48
+ columnize (>= 0.1)
49
+ ruby-debug-base (~> 0.10.4.0)
50
+ ruby-debug-base (0.10.4)
51
+ linecache (>= 0.3)
48
52
  term-ansicolor (1.0.5)
53
+ thor (0.14.6)
49
54
  timecop (0.3.5)
55
+ typhoeus (0.2.4)
56
+ mime-types
57
+ mime-types
50
58
 
51
59
  PLATFORMS
52
60
  ruby
@@ -56,9 +64,11 @@ DEPENDENCIES
56
64
  autotest-growl
57
65
  bundler (~> 1.0.0)
58
66
  cucumber
67
+ dynport_tools
59
68
  jeweler (~> 1.5.2)
60
69
  json
61
70
  rcov
62
71
  rspec (~> 2.3.0)
63
- ruby-debug19
72
+ ruby-debug
73
+ thor
64
74
  timecop
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.6
1
+ 0.3.0
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ $:<<File.expand_path("../lib", File.dirname(__FILE__))
3
+ require "rubygems"
4
+ require "thor"
5
+ require "checksummer"
6
+
7
+ class ChecksummerThor < Thor
8
+ desc "checksum DESTINATION", "checksum"
9
+ method_options :sleep => 0.1, :replace => false
10
+ method_option :dst, :required => true
11
+ def checksum
12
+ if !File.exists?(options.dst)
13
+ puts "ERROR: #{options.dst} does not exist!"
14
+ end
15
+ Checksummer.new(options.dst, :replace => options[:replace], :sleep => options.sleep).checksum_stream($stdin.readlines)
16
+ end
17
+
18
+ default_task :checksum
19
+ end
20
+
21
+ ChecksummerThor.start
@@ -5,14 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{checksummer}
8
- s.version = "0.2.6"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tobias Schwab"]
12
- s.date = %q{2011-08-10}
12
+ s.date = %q{2011-09-13}
13
13
  s.description = %q{Replace files with links to md5 files}
14
14
  s.email = %q{tobias.schwab@dynport.de}
15
- s.executables = ["checksummer"]
15
+ s.executables = ["checksummer2", "checksummer"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
18
  "README.rdoc"
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "VERSION",
30
30
  "autotest/discover.rb",
31
31
  "bin/checksummer",
32
+ "bin/checksummer2",
32
33
  "checksummer.gemspec",
33
34
  "features/checksum.feature",
34
35
  "features/step_definitions/checksum_steps.rb",
@@ -36,16 +37,18 @@ Gem::Specification.new do |s|
36
37
  "lib/checksummer.rb",
37
38
  "lib/checksummer_file.rb",
38
39
  "spec/checksummer_file_spec.rb",
40
+ "spec/checksummer_integration_spec.rb",
39
41
  "spec/checksummer_spec.rb",
40
42
  "spec/spec_helper.rb"
41
43
  ]
42
44
  s.homepage = %q{http://github.com/dynport/checksummer}
43
45
  s.licenses = ["MIT"]
44
46
  s.require_paths = ["lib"]
45
- s.rubygems_version = %q{1.7.2}
47
+ s.rubygems_version = %q{1.6.2}
46
48
  s.summary = %q{Replace files with links to md5 files}
47
49
  s.test_files = [
48
50
  "spec/checksummer_file_spec.rb",
51
+ "spec/checksummer_integration_spec.rb",
49
52
  "spec/checksummer_spec.rb",
50
53
  "spec/spec_helper.rb"
51
54
  ]
@@ -55,9 +58,11 @@ Gem::Specification.new do |s|
55
58
 
56
59
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
60
  s.add_runtime_dependency(%q<json>, [">= 0"])
61
+ s.add_runtime_dependency(%q<thor>, [">= 0"])
62
+ s.add_development_dependency(%q<dynport_tools>, [">= 0"])
58
63
  s.add_development_dependency(%q<autotest>, [">= 0"])
59
64
  s.add_development_dependency(%q<autotest-growl>, [">= 0"])
60
- s.add_development_dependency(%q<ruby-debug19>, [">= 0"])
65
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
61
66
  s.add_development_dependency(%q<cucumber>, [">= 0"])
62
67
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
63
68
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -66,9 +71,11 @@ Gem::Specification.new do |s|
66
71
  s.add_development_dependency(%q<timecop>, [">= 0"])
67
72
  else
68
73
  s.add_dependency(%q<json>, [">= 0"])
74
+ s.add_dependency(%q<thor>, [">= 0"])
75
+ s.add_dependency(%q<dynport_tools>, [">= 0"])
69
76
  s.add_dependency(%q<autotest>, [">= 0"])
70
77
  s.add_dependency(%q<autotest-growl>, [">= 0"])
71
- s.add_dependency(%q<ruby-debug19>, [">= 0"])
78
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
72
79
  s.add_dependency(%q<cucumber>, [">= 0"])
73
80
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
74
81
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -78,9 +85,11 @@ Gem::Specification.new do |s|
78
85
  end
79
86
  else
80
87
  s.add_dependency(%q<json>, [">= 0"])
88
+ s.add_dependency(%q<thor>, [">= 0"])
89
+ s.add_dependency(%q<dynport_tools>, [">= 0"])
81
90
  s.add_dependency(%q<autotest>, [">= 0"])
82
91
  s.add_dependency(%q<autotest-growl>, [">= 0"])
83
- s.add_dependency(%q<ruby-debug19>, [">= 0"])
92
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
84
93
  s.add_dependency(%q<cucumber>, [">= 0"])
85
94
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
86
95
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -5,6 +5,50 @@ require "json"
5
5
  class Checksummer
6
6
  DEFAULT_SLEEP = 0.1
7
7
 
8
+ attr_accessor :dst, :sleep_in_seconds, :replace
9
+
10
+ def initialize(dst, options = {})
11
+ self.dst = dst
12
+ self.sleep_in_seconds = options[:sleep] || DEFAULT_SLEEP
13
+ self.replace = !!options[:replace]
14
+ end
15
+
16
+ def checksum_stream(stream)
17
+ default = { :started => started, :total => stream.count }
18
+ stream.each_with_index do |line, i|
19
+ log ChecksummerFile.from_line(line).checksum_to!(dst, :replace => replace).merge(default).merge(:index => i + 1, :time => current_time,
20
+ :sleep => sleep_now? ? sleep_in_seconds : 0
21
+ )
22
+ sleep_if_necessary
23
+ end
24
+ end
25
+
26
+ def log(hash)
27
+ puts hash.to_json
28
+ end
29
+
30
+ def started
31
+ @started ||= Time.now.iso8601
32
+ end
33
+
34
+ def current_time
35
+ Time.now.iso8601
36
+ end
37
+
38
+ def log(hash)
39
+ puts hash.to_json
40
+ end
41
+
42
+ def sleep_if_necessary
43
+ sleep sleep_in_seconds if sleep_now?
44
+ end
45
+
46
+ def sleep_now?
47
+ !Range.new(0, 7).include?(Time.now.hour)
48
+ end
49
+
50
+ # LEGACY STUFF
51
+
8
52
  def self.find(directory, options = nil)
9
53
  Kernel.send(:`, %(find #{directory} #{options ? "#{options} " : ""}-type f -printf "%p\t%T+\t%s\t%Y\t%l\n")).split("\n")
10
54
  end
@@ -44,7 +88,7 @@ class Checksummer
44
88
  total = lines.count
45
89
  lines.each_with_index do |line, index|
46
90
  extra = { :index => index + 1, :total => lines.count, :started_at => started_at.iso8601 }
47
- puts ChecksummerFile.from_line(line).checksum_to!(checksum_to).merge(extra).to_json
91
+ puts ChecksummerFile.from_line(line).checksum_to!(checksum_to, :replace => true).merge(extra).to_json
48
92
  $stdout.flush
49
93
  sleep sleep if !Range.new(0,7).include?(Time.now.hour)
50
94
  end
@@ -33,25 +33,49 @@ class ChecksummerFile
33
33
  stream.map { |path| self.new(:path => path.chomp.split("\t").first) }
34
34
  end
35
35
 
36
- def checksum_to!(checksum_dir)
36
+ def copy_to_checksummed_path
37
+ if !File.exists?(checksummed_path)
38
+ FileUtils.mkdir_p(File.dirname(checksummed_path))
39
+ FileUtils.cp(path, "#{checksummed_path}.tmp")
40
+ FileUtils.mv("#{checksummed_path}.tmp", checksummed_path)
41
+ :copied
42
+ else
43
+ :exists
44
+ end
45
+ end
46
+
47
+ def do_symlink
48
+ mtime = File.mtime(self.path)
49
+ FileUtils.ln_sf(checksummed_path, self.path)
50
+ FileUtils.touch(self.path, :mtime => mtime) rescue nil
51
+ end
52
+
53
+ def checksummed_path(base = nil)
54
+ base ||= self.current_base
55
+ raise "no current base set" if base.nil?
56
+ File.expand_path(md5_path(base))
57
+ end
58
+
59
+ attr_accessor :current_base
60
+
61
+ REPLACE_MAPPING = {
62
+ :exists => :symlinked,
63
+ :copied => :replaced
64
+ }
65
+
66
+ def checksum_to!(base, options = {})
67
+ self.current_base = base
37
68
  status = { :original_line => original_line, :original_path => path }
38
69
  if path.to_s.strip.length == 0 || !File.exists?(path)
39
70
  status[:status] = :not_found
40
71
  elsif !File.symlink?(path)
41
- new_path = File.expand_path(md5_path(checksum_dir))
42
- status[:status] = if !File.exists?(new_path)
43
- FileUtils.mkdir_p(File.dirname(new_path))
44
- FileUtils.cp(path, "#{new_path}.tmp")
45
- FileUtils.mv("#{new_path}.tmp", new_path)
46
- :copied
47
- else
48
- :symlinked
72
+ status[:status] = copy_to_checksummed_path
73
+ status[:checksummed_path] = checksummed_path
74
+ status[:checksum] = File.basename(checksummed_path)
75
+ if options[:replace] == true
76
+ do_symlink
77
+ status[:status] = REPLACE_MAPPING[status[:status]] || status[:status]
49
78
  end
50
- status[:checksummed_path] = new_path
51
- status[:checksum] = File.basename(new_path)
52
- mtime = File.mtime(path)
53
- FileUtils.ln_sf(new_path, path)
54
- FileUtils.touch(path, :mtime => mtime) rescue nil
55
79
  else
56
80
  status[:status] = :was_symlink
57
81
  status[:realpath] = Pathname.new(path).realpath
@@ -30,8 +30,33 @@ describe ChecksummerFile do
30
30
  end
31
31
  end
32
32
 
33
+ let(:file) { ChecksummerFile.new(:path => "/some/text.csv") }
34
+
35
+ describe "#do_symlink" do
36
+ before(:each) do
37
+ file.stub!(:checksummed_path).and_return("/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9")
38
+ FileUtils.stub!(:ln_sf)
39
+ FileUtils.stub!(:touch)
40
+ File.stub!(:mtime).and_return "some time"
41
+ end
42
+
43
+ it "creates a symlink for the copied file" do
44
+ FileUtils.should_receive(:ln_sf).with("/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9", "/some/text.csv")
45
+ file.do_symlink
46
+ end
47
+
48
+ it "changes the mtime of the symlink to the mtime of the original file" do
49
+ FileUtils.should_receive(:touch).with("/some/text.csv", :mtime => "some time")
50
+ file.do_symlink
51
+ end
52
+
53
+ it "does not break when touch raises an error" do
54
+ FileUtils.should_receive(:touch).and_raise("not allowed")
55
+ file.do_symlink
56
+ end
57
+ end
58
+
33
59
  describe "#checksum_to!" do
34
- let(:file) { ChecksummerFile.new(:path => "/some/text.csv") }
35
60
  let(:time) { Time.local(2010, 9, 10, 11, 12, 13) }
36
61
  let(:md5) { "fde8dad8ea43640b00cdd1e92e532ca9" }
37
62
 
@@ -81,11 +106,32 @@ describe ChecksummerFile do
81
106
  file.checksum_to!("/tmp/data")[:status].should == :not_found
82
107
  end
83
108
 
84
- it "includes status :no_exusts when path is nil" do
109
+ it "includes status :not_exists when path is nil" do
85
110
  file.path = nil
86
111
  file.checksum_to!("/tmp/data")[:status].should == :not_found
87
112
  end
88
113
 
114
+ describe "with replace being false" do
115
+ before(:each) do
116
+ file.stub(:checksummed_path).and_return "/some/path"
117
+ end
118
+
119
+ it "sets status to :copied" do
120
+ file.stub!(:copy_to_checksummed_path).and_return :exists
121
+ file.checksum_to!("/tmp/data")[:status].should == :exists
122
+ end
123
+
124
+ it "returns copied when copied" do
125
+ file.stub!(:copy_to_checksummed_path).and_return :copied
126
+ file.checksum_to!("/tmp/data")[:status].should == :copied
127
+ end
128
+
129
+ it "does not call do_symlink" do
130
+ file.should_not_receive(:do_symlink)
131
+ file.checksum_to!("/tmp/data")
132
+ end
133
+ end
134
+
89
135
  describe "with the file not being checksummed" do
90
136
  it "copys the file to it's md5" do
91
137
  File.stub!(:exists?).with("/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9").and_return false
@@ -103,10 +149,10 @@ describe ChecksummerFile do
103
149
  FileUtils.should_receive(:mkdir_p).with("/tmp/data/f/d/e/8")
104
150
  file.checksum_to!("/tmp/data")
105
151
  end
106
-
107
- it "creates a symlink for the copied file" do
108
- FileUtils.should_receive(:ln_sf).with("/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9", "/some/text.csv")
109
- file.checksum_to!("/tmp/data")
152
+
153
+ it "calls do_symlink when replace is set to true" do
154
+ file.should_receive(:do_symlink)
155
+ file.checksum_to!("/tmp/data", :replace => true)[:status].should == :replaced
110
156
  end
111
157
 
112
158
  it "returns :copied when copied" do
@@ -116,16 +162,6 @@ describe ChecksummerFile do
116
162
  it "includes the checksummed_path" do
117
163
  file.checksum_to!("/tmp/data")[:checksummed_path].should == "/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9"
118
164
  end
119
-
120
- it "changes the mtime of the symlink to the mtime of the original file" do
121
- FileUtils.should_receive(:touch).with("/some/text.csv", :mtime => time)
122
- file.checksum_to!("/tmp/data")
123
- end
124
-
125
- it "does not break when touch raises an error" do
126
- FileUtils.should_receive(:touch).and_raise("not allowed")
127
- file.checksum_to!("/tmp/data")[:status].should == :copied
128
- end
129
165
  end
130
166
 
131
167
  describe "with the file being checksummed already" do
@@ -143,13 +179,8 @@ describe ChecksummerFile do
143
179
  file.checksum_to!("/tmp/data")
144
180
  end
145
181
 
146
- it "does symlink the file" do
147
- FileUtils.should_receive(:ln_sf).with("/tmp/data/f/d/e/8/fde8dad8ea43640b00cdd1e92e532ca9", "/some/text.csv")
148
- file.checksum_to!("/tmp/data")
149
- end
150
-
151
182
  it "returns :symlinked" do
152
- file.checksum_to!("/tmp/data")[:status].should == :symlinked
183
+ file.checksum_to!("/tmp/data", :replace => true)[:status].should == :symlinked
153
184
  end
154
185
 
155
186
  it "includes the checksummed_path" do
@@ -159,11 +190,6 @@ describe ChecksummerFile do
159
190
  it "includes the checksum" do
160
191
  file.checksum_to!("/tmp/data")[:checksum].should == "fde8dad8ea43640b00cdd1e92e532ca9"
161
192
  end
162
-
163
- it "changes the mtime of the symlink to the mtime of the original file" do
164
- FileUtils.should_receive(:touch).with("/some/text.csv", :mtime => time)
165
- file.checksum_to!("/tmp/data")
166
- end
167
193
  end
168
194
 
169
195
  describe "with the file being a symlink" do
@@ -0,0 +1,131 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Checksummer Integration Spec" do
4
+ let(:cmd) { root.join("bin", "checksummer2") }
5
+
6
+ before(:each) do
7
+ FileUtils.rm_rf(root.join("tmp"))
8
+ %w(src dst).map { |dir| FileUtils.mkdir_p(root.join("tmp", dir) ) }
9
+ end
10
+
11
+ def invoke(*args)
12
+ `ruby #{cmd} #{args.join(" ")} 2>&1`
13
+ end
14
+
15
+ def create_files(hash)
16
+ hash.each do |path, content|
17
+ full_path = root.join("tmp", path)
18
+ FileUtils.mkdir_p(File.dirname(full_path))
19
+ File.open(full_path, "w") do |f|
20
+ f.puts(content)
21
+ end
22
+ end
23
+ end
24
+
25
+ it "does not break when calling with not found files" do
26
+ create_files("src/file1.txt" => "file 1")
27
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --dst #{root.join("tmp/dst")}`
28
+ File.should_not be_symlink("#{root}/tmp/src/file1.txt")
29
+ File.should_not be_exists("#{root}/tmp/src/file2.txt")
30
+
31
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
32
+ File.should_not be_exists("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
33
+
34
+ atts = out.split("\n").map { |l| JSON.parse(l) }
35
+ atts.first.should have_attributes("status" => "copied")
36
+ atts.at(1).should have_attributes("status" => "not_found")
37
+ end
38
+
39
+ it "checksums all files from stdin to dst when dst exists" do
40
+ create_files("src/file1.txt" => "file 1", "src/file2.txt" => "file 2")
41
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --dst #{root.join("tmp/dst")}`
42
+
43
+ File.should_not be_symlink("#{root}/tmp/src/file1.txt")
44
+ File.should_not be_symlink("#{root}/tmp/src/file2.txt")
45
+
46
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
47
+ File.should be_file("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
48
+
49
+ atts = out.split("\n").map { |l| JSON.parse(l) }
50
+ atts.first.should have_attributes(
51
+ "original_path"=>"#{root}/tmp/src/file1.txt", "original_line"=>"#{root}/tmp/src/file1.txt", "total"=>2,
52
+ "checksum"=>"e243bb39c844b3543a7726576c869caf", "checksummed_path"=>"#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf", "index"=>1,
53
+ "status"=>"copied", "sleep" => 0.1
54
+ )
55
+ atts.at(1).should have_attributes(
56
+ "original_path"=>"#{root}/tmp/src/file2.txt", "original_line"=>"#{root}/tmp/src/file2.txt", "total"=>2,
57
+ "checksum"=>"4349cfeff8e2eb74dffc369bb5fd084e", "checksummed_path"=>"#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e", "index"=>2,
58
+ "status"=>"copied"
59
+ )
60
+
61
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --sleep 0.01 --dst #{root.join("tmp/dst")}`
62
+ atts = out.split("\n").map { |l| JSON.parse(l) }
63
+ File.should_not be_symlink("#{root}/tmp/src/file1.txt")
64
+ File.should_not be_symlink("#{root}/tmp/src/file2.txt")
65
+
66
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
67
+ File.should be_file("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
68
+
69
+ atts.first.should have_attributes("status" => "exists", "sleep" => 0.01)
70
+ atts.at(1).should have_attributes("status" => "exists")
71
+
72
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --replace --dst #{root.join("tmp/dst")}`
73
+ atts = out.split("\n").map { |l| JSON.parse(l) }
74
+
75
+ File.should be_symlink("#{root}/tmp/src/file1.txt")
76
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
77
+ Pathname.new("#{root}/tmp/src/file1.txt").realpath.to_s.should == "#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf"
78
+
79
+ File.should be_symlink("#{root}/tmp/src/file2.txt")
80
+ File.should be_file("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
81
+ Pathname.new("#{root}/tmp/src/file2.txt").realpath.to_s.should == "#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e"
82
+
83
+ atts.first.should have_attributes("status" => "symlinked")
84
+ atts.at(1).should have_attributes("status" => "symlinked")
85
+ end
86
+
87
+ it "replaces the original files with symlinks when started with --replace" do
88
+ create_files("src/file1.txt" => "file 1", "src/file2.txt" => "file 2")
89
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --dst #{root.join("tmp/dst")} --replace`
90
+
91
+ File.should be_symlink("#{root}/tmp/src/file1.txt")
92
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
93
+ Pathname.new("#{root}/tmp/src/file1.txt").realpath.to_s.should == "#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf"
94
+
95
+ File.should be_symlink("#{root}/tmp/src/file2.txt")
96
+ File.should be_file("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
97
+ Pathname.new("#{root}/tmp/src/file2.txt").realpath.to_s.should == "#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e"
98
+
99
+ atts = out.split("\n").map { |l| JSON.parse(l) }
100
+ atts.first.should have_attributes(
101
+ "original_path"=>"#{root}/tmp/src/file1.txt", "original_line"=>"#{root}/tmp/src/file1.txt", "total"=>2,
102
+ "checksum"=>"e243bb39c844b3543a7726576c869caf", "checksummed_path"=>"#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf", "index"=>1,
103
+ "status"=>"replaced"
104
+ )
105
+
106
+ atts.at(1).should have_attributes(
107
+ "original_path"=>"#{root}/tmp/src/file2.txt", "original_line"=>"#{root}/tmp/src/file2.txt", "total"=>2,
108
+ "checksum"=>"4349cfeff8e2eb74dffc369bb5fd084e", "checksummed_path"=>"#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e", "index"=>2,
109
+ "status"=>"replaced"
110
+ )
111
+
112
+ out = `echo "#{root.join("tmp/src/file1.txt")}\n#{root.join("tmp/src/file2.txt")}" | ruby #{cmd} --dst #{root.join("tmp/dst")} --replace`
113
+
114
+ File.should be_symlink("#{root}/tmp/src/file1.txt")
115
+ File.should be_file("#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf")
116
+ Pathname.new("#{root}/tmp/src/file1.txt").realpath.to_s.should == "#{root}/tmp/dst/e/2/4/3/e243bb39c844b3543a7726576c869caf"
117
+
118
+ File.should be_symlink("#{root}/tmp/src/file2.txt")
119
+ File.should be_file("#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e")
120
+ Pathname.new("#{root}/tmp/src/file2.txt").realpath.to_s.should == "#{root}/tmp/dst/4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e"
121
+
122
+ atts = out.split("\n").map { |l| JSON.parse(l) }
123
+ atts.first.should have_attributes("status" => "was_symlink")
124
+ atts.at(1).should have_attributes("status" => "was_symlink")
125
+ end
126
+
127
+ it "prints something when dst does not exist" do
128
+ out = `echo "#{root.join("tmp/src/file1.txt")}" | ruby #{cmd} --dst #{root.join("tmp/dst2")}`
129
+ out.should match(/ERROR.*?tmp\/dst2 does not exist/)
130
+ end
131
+ end
@@ -106,8 +106,8 @@ describe "Checksummer" do
106
106
  double2 = double("double 2")
107
107
  ChecksummerFile.stub(:from_line).with("/path/1.txt\n").and_return double1
108
108
  ChecksummerFile.stub(:from_line).with("/path/2.txt").and_return double2
109
- double1.should_receive(:checksum_to!).with("/tmp").and_return({})
110
- double2.should_receive(:checksum_to!).with("/tmp").and_return({})
109
+ double1.should_receive(:checksum_to!).with("/tmp", :replace => true).and_return({})
110
+ double2.should_receive(:checksum_to!).with("/tmp", :replace => true).and_return({})
111
111
  Checksummer.run_for_args(["--stdin", "/tmp"])
112
112
  end
113
113
  end
@@ -153,21 +153,122 @@ describe "Checksummer" do
153
153
  end
154
154
 
155
155
  it "calls checksum_to! with dst" do
156
- checksum_file.should_receive(:checksum_to!).with("/tmp")
156
+ checksum_file.should_receive(:checksum_to!).with("/tmp", :replace => true)
157
157
  Checksummer.run_for_args(["/some/path", "/tmp"])
158
158
  end
159
159
 
160
160
  it "puts result of checksum_to!" do
161
161
  Time.stub(:now).and_return Time.local(2011, 2, 3, 4, 5, 6)
162
162
  checksum_file.stub!(:checksum_to!).and_return(:some => "result")
163
- Checksummer.should_receive(:puts).with(
164
- "{\"some\":\"result\",\"index\":1,\"total\":1,\"started_at\":\"2011-02-03T04:05:06+01:00\"}"
165
- )
163
+ Checksummer.should_receive(:puts).with do |line|
164
+ JSON.parse(line).should == {
165
+ "index" => 1, "some" => "result", "total" => 1, "started_at" => "2011-02-03T04:05:06+01:00"
166
+ }
167
+ end
166
168
  Checksummer.run_for_args(["/some/path", "/tmp"])
167
169
  end
168
170
  end
169
171
  end
170
172
 
173
+ let(:dst) { "/path/to/dst" }
174
+ let(:checksummer) { Checksummer.new(dst) }
175
+
176
+ before(:each) do
177
+ checksummer.stub!(:log)
178
+ end
179
+
180
+ describe "#initialize" do
181
+ it "sets the dst" do
182
+ Checksummer.new(dst).dst.should == dst
183
+ end
184
+
185
+ it "sets sleep when given" do
186
+ Checksummer.new(dst, :sleep => 0.34).sleep_in_seconds.should == 0.34
187
+ end
188
+
189
+ it "sets replace to false when not given" do
190
+ Checksummer.new(dst).replace.should == false
191
+ end
192
+
193
+ it "sets replace to true when given" do
194
+ Checksummer.new(dst, :replace => true).replace.should == true
195
+ end
196
+ end
197
+
198
+ describe "#checksum_stream" do
199
+ let(:file1) { double("file", :checksum_to! => { :status => "ok" }).as_null_object }
200
+ let(:file2) { double("file2", :checksum_to! => { :status => "not_found" }).as_null_object }
201
+
202
+ before(:each) do
203
+ ChecksummerFile.stub(:from_line).with("line1").and_return file1
204
+ ChecksummerFile.stub(:from_line).with("line2").and_return file2
205
+ end
206
+
207
+ it "initializes a new ChecksummerFile" do
208
+ # ChecksummerFile.from_line(line).checksum_to!(checksum_to, :replace => true)
209
+ ChecksummerFile.should_receive(:from_line).with("line1").and_return file1
210
+ ChecksummerFile.should_receive(:from_line).with("line2").and_return file2
211
+ checksummer.checksum_stream(%w(line1 line2))
212
+ end
213
+
214
+ it "calls checksum_to! on all lines with correct " do
215
+ file1.should_receive(:checksum_to!).with(dst, :replace => false).and_return({})
216
+ file2.should_receive(:checksum_to!).with(dst, :replace => false).and_return({})
217
+ checksummer.checksum_stream(%w(line1 line2))
218
+ end
219
+
220
+ it "calls checksum_to! with replace => true when set" do
221
+ file1.should_receive(:checksum_to!).with(dst, :replace => true).and_return({})
222
+ checksummer.replace = true
223
+ checksummer.checksum_stream(%w(line1))
224
+ end
225
+
226
+ it "calls sleep if necessary 2 times" do
227
+ checksummer.should_receive(:sleep_if_necessary).exactly(2).times
228
+ checksummer.checksum_stream(%w(line1 line2))
229
+ end
230
+
231
+ it "calls puts with correct attributes" do
232
+ cs = Checksummer.new(dst)
233
+ cs.stub!(:started).and_return "2011-01-02T03:04:05+01:00"
234
+ cs.stub(:current_time).and_return("2011-01-02T03:04:07+01:00", "2011-01-02T03:04:08+01:00")
235
+ cs.should_receive(:log).with({ :index => 1, :total => 2, :status => "ok", :started => "2011-01-02T03:04:05+01:00", :time => "2011-01-02T03:04:07+01:00", :sleep => 0.1 })
236
+ cs.should_receive(:log).with({ :index => 2, :total => 2, :status => "not_found", :started => "2011-01-02T03:04:05+01:00", :time => "2011-01-02T03:04:08+01:00", :sleep => 0.1 })
237
+ cs.checksum_stream(%w(line1 line2))
238
+ end
239
+ end
240
+
241
+ describe "#sleep_if_necessary" do
242
+ it "calls sleep with set sleep when time inside sleep zone" do
243
+ checksummer.sleep_in_seconds = 10
244
+ checksummer.stub!(:sleep_now?).and_return true
245
+ checksummer.should_receive(:sleep).with(10)
246
+ checksummer.sleep_if_necessary
247
+ end
248
+
249
+ it "does not call sleep in seconds when sleep_now? returns false" do
250
+ checksummer.should_receive(:sleep_now?).and_return false
251
+ checksummer.should_not_receive(:sleep)
252
+ checksummer.sleep_if_necessary
253
+ end
254
+
255
+ it "sleeps for the default number of seconds when not set" do
256
+ checksummer.stub(:sleep_now?).and_return true
257
+ checksummer.should_receive(:sleep).with(0.1)
258
+ checksummer.sleep_if_necessary
259
+ end
260
+ end
261
+
262
+ describe "#sleep_now?" do
263
+ { "08:00" => true, "07:59" => false, "00:00" => false, "23:59" => true }.each do |time, value|
264
+ it "returns #{value} for #{time}" do
265
+ Timecop.freeze(Time.parse("2011-01-02 #{time}")) do
266
+ checksummer.sleep_now?.should == value
267
+ end
268
+ end
269
+ end
270
+ end
271
+
171
272
  describe "#integration" do
172
273
  let(:root) { File.expand_path("tmp", File.dirname(__FILE__)) }
173
274
 
@@ -3,11 +3,19 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
  require 'rspec'
4
4
  require 'checksummer'
5
5
  require "ruby-debug"
6
+ require "dynport_tools"
6
7
 
7
8
  # Requires supporting files with custom matchers and macros, etc,
8
9
  # in ./support/ and its subdirectories.
9
10
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
11
 
12
+
11
13
  RSpec.configure do |config|
12
-
14
+ config.include(DynportTools::HaveAttributesMatcher)
15
+ end
16
+
17
+ def root
18
+ Pathname.new(File.expand_path("../", File.dirname(__FILE__)))
13
19
  end
20
+
21
+ require "timecop"
metadata CHANGED
@@ -1,8 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checksummer
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 19
4
5
  prerelease:
5
- version: 0.2.6
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
6
11
  platform: ruby
7
12
  authors:
8
13
  - Tobias Schwab
@@ -10,121 +15,187 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2011-08-10 00:00:00 Z
18
+ date: 2011-09-13 00:00:00 +02:00
19
+ default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: json
17
- requirement: &id001 !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ version_requirements: &id001 !ruby/object:Gem::Requirement
18
25
  none: false
19
26
  requirements:
20
27
  - - ">="
21
28
  - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
22
32
  version: "0"
23
33
  type: :runtime
34
+ requirement: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: thor
24
37
  prerelease: false
25
- version_requirements: *id001
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ requirement: *id002
26
49
  - !ruby/object:Gem::Dependency
27
- name: autotest
28
- requirement: &id002 !ruby/object:Gem::Requirement
50
+ name: dynport_tools
51
+ prerelease: false
52
+ version_requirements: &id003 !ruby/object:Gem::Requirement
29
53
  none: false
30
54
  requirements:
31
55
  - - ">="
32
56
  - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
33
60
  version: "0"
34
61
  type: :development
62
+ requirement: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: autotest
35
65
  prerelease: false
36
- version_requirements: *id002
66
+ version_requirements: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :development
76
+ requirement: *id004
37
77
  - !ruby/object:Gem::Dependency
38
78
  name: autotest-growl
39
- requirement: &id003 !ruby/object:Gem::Requirement
79
+ prerelease: false
80
+ version_requirements: &id005 !ruby/object:Gem::Requirement
40
81
  none: false
41
82
  requirements:
42
83
  - - ">="
43
84
  - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
44
88
  version: "0"
45
89
  type: :development
46
- prerelease: false
47
- version_requirements: *id003
90
+ requirement: *id005
48
91
  - !ruby/object:Gem::Dependency
49
- name: ruby-debug19
50
- requirement: &id004 !ruby/object:Gem::Requirement
92
+ name: ruby-debug
93
+ prerelease: false
94
+ version_requirements: &id006 !ruby/object:Gem::Requirement
51
95
  none: false
52
96
  requirements:
53
97
  - - ">="
54
98
  - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
55
102
  version: "0"
56
103
  type: :development
57
- prerelease: false
58
- version_requirements: *id004
104
+ requirement: *id006
59
105
  - !ruby/object:Gem::Dependency
60
106
  name: cucumber
61
- requirement: &id005 !ruby/object:Gem::Requirement
107
+ prerelease: false
108
+ version_requirements: &id007 !ruby/object:Gem::Requirement
62
109
  none: false
63
110
  requirements:
64
111
  - - ">="
65
112
  - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
66
116
  version: "0"
67
117
  type: :development
68
- prerelease: false
69
- version_requirements: *id005
118
+ requirement: *id007
70
119
  - !ruby/object:Gem::Dependency
71
120
  name: rspec
72
- requirement: &id006 !ruby/object:Gem::Requirement
121
+ prerelease: false
122
+ version_requirements: &id008 !ruby/object:Gem::Requirement
73
123
  none: false
74
124
  requirements:
75
125
  - - ~>
76
126
  - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 2
130
+ - 3
131
+ - 0
77
132
  version: 2.3.0
78
133
  type: :development
79
- prerelease: false
80
- version_requirements: *id006
134
+ requirement: *id008
81
135
  - !ruby/object:Gem::Dependency
82
136
  name: bundler
83
- requirement: &id007 !ruby/object:Gem::Requirement
137
+ prerelease: false
138
+ version_requirements: &id009 !ruby/object:Gem::Requirement
84
139
  none: false
85
140
  requirements:
86
141
  - - ~>
87
142
  - !ruby/object:Gem::Version
143
+ hash: 23
144
+ segments:
145
+ - 1
146
+ - 0
147
+ - 0
88
148
  version: 1.0.0
89
149
  type: :development
90
- prerelease: false
91
- version_requirements: *id007
150
+ requirement: *id009
92
151
  - !ruby/object:Gem::Dependency
93
152
  name: jeweler
94
- requirement: &id008 !ruby/object:Gem::Requirement
153
+ prerelease: false
154
+ version_requirements: &id010 !ruby/object:Gem::Requirement
95
155
  none: false
96
156
  requirements:
97
157
  - - ~>
98
158
  - !ruby/object:Gem::Version
159
+ hash: 7
160
+ segments:
161
+ - 1
162
+ - 5
163
+ - 2
99
164
  version: 1.5.2
100
165
  type: :development
101
- prerelease: false
102
- version_requirements: *id008
166
+ requirement: *id010
103
167
  - !ruby/object:Gem::Dependency
104
168
  name: rcov
105
- requirement: &id009 !ruby/object:Gem::Requirement
169
+ prerelease: false
170
+ version_requirements: &id011 !ruby/object:Gem::Requirement
106
171
  none: false
107
172
  requirements:
108
173
  - - ">="
109
174
  - !ruby/object:Gem::Version
175
+ hash: 3
176
+ segments:
177
+ - 0
110
178
  version: "0"
111
179
  type: :development
112
- prerelease: false
113
- version_requirements: *id009
180
+ requirement: *id011
114
181
  - !ruby/object:Gem::Dependency
115
182
  name: timecop
116
- requirement: &id010 !ruby/object:Gem::Requirement
183
+ prerelease: false
184
+ version_requirements: &id012 !ruby/object:Gem::Requirement
117
185
  none: false
118
186
  requirements:
119
187
  - - ">="
120
188
  - !ruby/object:Gem::Version
189
+ hash: 3
190
+ segments:
191
+ - 0
121
192
  version: "0"
122
193
  type: :development
123
- prerelease: false
124
- version_requirements: *id010
194
+ requirement: *id012
125
195
  description: Replace files with links to md5 files
126
196
  email: tobias.schwab@dynport.de
127
197
  executables:
198
+ - checksummer2
128
199
  - checksummer
129
200
  extensions: []
130
201
 
@@ -143,6 +214,7 @@ files:
143
214
  - VERSION
144
215
  - autotest/discover.rb
145
216
  - bin/checksummer
217
+ - bin/checksummer2
146
218
  - checksummer.gemspec
147
219
  - features/checksum.feature
148
220
  - features/step_definitions/checksum_steps.rb
@@ -150,8 +222,10 @@ files:
150
222
  - lib/checksummer.rb
151
223
  - lib/checksummer_file.rb
152
224
  - spec/checksummer_file_spec.rb
225
+ - spec/checksummer_integration_spec.rb
153
226
  - spec/checksummer_spec.rb
154
227
  - spec/spec_helper.rb
228
+ has_rdoc: true
155
229
  homepage: http://github.com/dynport/checksummer
156
230
  licenses:
157
231
  - MIT
@@ -165,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
239
  requirements:
166
240
  - - ">="
167
241
  - !ruby/object:Gem::Version
168
- hash: -652076464801799004
242
+ hash: 3
169
243
  segments:
170
244
  - 0
171
245
  version: "0"
@@ -174,15 +248,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
248
  requirements:
175
249
  - - ">="
176
250
  - !ruby/object:Gem::Version
251
+ hash: 3
252
+ segments:
253
+ - 0
177
254
  version: "0"
178
255
  requirements: []
179
256
 
180
257
  rubyforge_project:
181
- rubygems_version: 1.7.2
258
+ rubygems_version: 1.6.2
182
259
  signing_key:
183
260
  specification_version: 3
184
261
  summary: Replace files with links to md5 files
185
262
  test_files:
186
263
  - spec/checksummer_file_spec.rb
264
+ - spec/checksummer_integration_spec.rb
187
265
  - spec/checksummer_spec.rb
188
266
  - spec/spec_helper.rb