checksummer 0.2.6 → 0.3.0

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/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