history_file 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.9.3@history_file
data/Gemfile ADDED
@@ -0,0 +1,25 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler"
12
+ gem "jeweler", "~> 1.8.4"
13
+ gem "simplecov", ">= 0"
14
+ end
15
+ group :development, :test do
16
+ gem 'webmock'
17
+ gem 'rspec'
18
+ gem 'terminal-notifier'
19
+ gem 'guard'
20
+ gem 'guard-rspec'
21
+ gem 'guard-bundler'
22
+ gem 'guard-yard'
23
+ gem 'guard-spork'
24
+ gem 'rb-fsevent'
25
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,98 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.9)
5
+ i18n (~> 0.6)
6
+ multi_json (~> 1.0)
7
+ addressable (2.3.2)
8
+ childprocess (0.3.6)
9
+ ffi (~> 1.0, >= 1.0.6)
10
+ coderay (1.0.8)
11
+ crack (0.3.1)
12
+ diff-lcs (1.1.3)
13
+ ffi (1.1.5)
14
+ git (1.2.5)
15
+ guard (1.5.4)
16
+ listen (>= 0.4.2)
17
+ lumberjack (>= 1.0.2)
18
+ pry (>= 0.9.10)
19
+ thor (>= 0.14.6)
20
+ guard-bundler (1.0.0)
21
+ bundler (~> 1.0)
22
+ guard (~> 1.1)
23
+ guard-rspec (2.1.1)
24
+ guard (>= 1.1)
25
+ rspec (~> 2.11)
26
+ guard-spork (1.2.3)
27
+ childprocess (>= 0.2.3)
28
+ guard (>= 1.1)
29
+ spork (>= 0.8.4)
30
+ sys-proctable
31
+ guard-yard (2.0.1)
32
+ guard (>= 1.1.0)
33
+ yard (>= 0.7.0)
34
+ i18n (0.6.1)
35
+ jeweler (1.8.4)
36
+ bundler (~> 1.0)
37
+ git (>= 1.2.5)
38
+ rake
39
+ rdoc
40
+ json (1.7.5)
41
+ listen (0.5.3)
42
+ lumberjack (1.0.2)
43
+ method_source (0.8.1)
44
+ multi_json (1.3.7)
45
+ pry (0.9.10)
46
+ coderay (~> 1.0.5)
47
+ method_source (~> 0.8)
48
+ slop (~> 3.3.1)
49
+ rake (10.0.1)
50
+ rb-fsevent (0.9.2)
51
+ rdoc (3.12)
52
+ json (~> 1.4)
53
+ rspec (2.12.0)
54
+ rspec-core (~> 2.12.0)
55
+ rspec-expectations (~> 2.12.0)
56
+ rspec-mocks (~> 2.12.0)
57
+ rspec-core (2.12.0)
58
+ rspec-expectations (2.12.0)
59
+ diff-lcs (~> 1.1.3)
60
+ rspec-mocks (2.12.0)
61
+ shoulda (3.3.2)
62
+ shoulda-context (~> 1.0.1)
63
+ shoulda-matchers (~> 1.4.1)
64
+ shoulda-context (1.0.1)
65
+ shoulda-matchers (1.4.1)
66
+ activesupport (>= 3.0.0)
67
+ simplecov (0.7.1)
68
+ multi_json (~> 1.0)
69
+ simplecov-html (~> 0.7.1)
70
+ simplecov-html (0.7.1)
71
+ slop (3.3.3)
72
+ spork (0.9.2)
73
+ sys-proctable (0.9.2)
74
+ terminal-notifier (1.4.2)
75
+ thor (0.16.0)
76
+ webmock (1.9.0)
77
+ addressable (>= 2.2.7)
78
+ crack (>= 0.1.7)
79
+ yard (0.8.3)
80
+
81
+ PLATFORMS
82
+ ruby
83
+
84
+ DEPENDENCIES
85
+ bundler
86
+ guard
87
+ guard-bundler
88
+ guard-rspec
89
+ guard-spork
90
+ guard-yard
91
+ jeweler (~> 1.8.4)
92
+ rb-fsevent
93
+ rdoc (~> 3.12)
94
+ rspec
95
+ shoulda
96
+ simplecov
97
+ terminal-notifier
98
+ webmock
data/Guardfile ADDED
@@ -0,0 +1,45 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ # Uncomment next line if Gemfile contain `gemspec' command
7
+ # watch(/^.+\.gemspec/)
8
+ end
9
+
10
+ guard 'rspec', :version => 2 do
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch('spec/spec_helper.rb') { "spec" }
13
+ watch(%r{^lib/history_file/(.+)\.rb$}) { "spec" }
14
+
15
+ # Rails example
16
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
17
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
18
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
19
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
20
+ watch('config/routes.rb') { "spec/routing" }
21
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
22
+
23
+ # Turnip features and steps
24
+ watch(%r{^spec/acceptance/(.+)\.feature$})
25
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
26
+ end
27
+
28
+
29
+ guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
30
+ watch('config/application.rb')
31
+ watch('config/environment.rb')
32
+ watch('config/environments/test.rb')
33
+ watch(%r{^config/initializers/.+\.rb$})
34
+ watch('Gemfile')
35
+ watch('Gemfile.lock')
36
+ watch('spec/spec_helper.rb') { :rspec }
37
+ watch('test/test_helper.rb') { :test_unit }
38
+ watch(%r{features/support/}) { :cucumber }
39
+ end
40
+
41
+ guard 'yard', port: 8808, stdout: '/dev/null' do
42
+ watch(%r{app/.+\.rb})
43
+ watch(%r{lib/.+\.rb})
44
+ watch(%r{ext/.+\.c})
45
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Jannis Hermanns
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.rdoc ADDED
@@ -0,0 +1,32 @@
1
+ HistoryFile
2
+ ===========
3
+
4
+ Behaves like a {File} class and does some convenience stuff
5
+ around a {HistoryFile::FileDelegator} instance. It all
6
+ revolves about defining a time offset. If however, you want
7
+ to access different versions of a file, use it like this:
8
+
9
+ ```ruby
10
+ > f = HistoryFile[1.day.ago].new("/tmp/foo.txt", "w")
11
+ => #<File:/tmp/2012.11.02-foo.txt>
12
+ ```
13
+
14
+ The returned {HistoryFile::FileDelegator} object supports all
15
+ methods that File has, but adds a date prefix to those methods
16
+ that revolve around a single file (reading, writing, etc.)
17
+
18
+ If a file for a given date is not available, {HistoryFile} falls
19
+ back to the freshest file that is older than the given date.
20
+
21
+ ```ruby
22
+ > f = HistoryFile[3.days.ago].new("test.txt", "w")
23
+ => #<File:./2012.11.12-test.txt>
24
+ > f.write("I am old")
25
+ => 8
26
+ > f.close
27
+ => nil
28
+ > HistoryFile[Date.today].read("test.txt")
29
+ => "I am old"
30
+ > HistoryFile[10.days.ago].read("test.txt")
31
+ Errno::ENOENT: No such file or directory - ./2012.11.05-test.txt
32
+ ```
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "history_file"
18
+ gem.homepage = "http://github.com/moviepilot/history_file"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A File like class with history support}
21
+ gem.description = %Q{A File like class that supports versioning by date and has a fallback to older files}
22
+ gem.email = "jannis@moviepilot.com"
23
+ gem.authors = ["Jannis Hermanns"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,93 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "history_file"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jannis Hermanns"]
12
+ s.date = "2012-11-16"
13
+ s.description = "A File like class that supports versioning by date and has a fallback to older files"
14
+ s.email = "jannis@moviepilot.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rvmrc",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "Guardfile",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "history_file.gemspec",
30
+ "lib/history_file.rb",
31
+ "lib/history_file/file_delegator.rb",
32
+ "lib/history_file/history_file.rb",
33
+ "spec/history_file/history_file_spec.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = "http://github.com/moviepilot/history_file"
37
+ s.licenses = ["MIT"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = "1.8.24"
40
+ s.summary = "A File like class with history support"
41
+
42
+ if s.respond_to? :specification_version then
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
47
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
48
+ s.add_development_dependency(%q<bundler>, [">= 0"])
49
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
50
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
51
+ s.add_development_dependency(%q<webmock>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ s.add_development_dependency(%q<terminal-notifier>, [">= 0"])
54
+ s.add_development_dependency(%q<guard>, [">= 0"])
55
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
56
+ s.add_development_dependency(%q<guard-bundler>, [">= 0"])
57
+ s.add_development_dependency(%q<guard-yard>, [">= 0"])
58
+ s.add_development_dependency(%q<guard-spork>, [">= 0"])
59
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
60
+ else
61
+ s.add_dependency(%q<shoulda>, [">= 0"])
62
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
63
+ s.add_dependency(%q<bundler>, [">= 0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
65
+ s.add_dependency(%q<simplecov>, [">= 0"])
66
+ s.add_dependency(%q<webmock>, [">= 0"])
67
+ s.add_dependency(%q<rspec>, [">= 0"])
68
+ s.add_dependency(%q<terminal-notifier>, [">= 0"])
69
+ s.add_dependency(%q<guard>, [">= 0"])
70
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
71
+ s.add_dependency(%q<guard-bundler>, [">= 0"])
72
+ s.add_dependency(%q<guard-yard>, [">= 0"])
73
+ s.add_dependency(%q<guard-spork>, [">= 0"])
74
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
75
+ end
76
+ else
77
+ s.add_dependency(%q<shoulda>, [">= 0"])
78
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
79
+ s.add_dependency(%q<bundler>, [">= 0"])
80
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
81
+ s.add_dependency(%q<simplecov>, [">= 0"])
82
+ s.add_dependency(%q<webmock>, [">= 0"])
83
+ s.add_dependency(%q<rspec>, [">= 0"])
84
+ s.add_dependency(%q<terminal-notifier>, [">= 0"])
85
+ s.add_dependency(%q<guard>, [">= 0"])
86
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
87
+ s.add_dependency(%q<guard-bundler>, [">= 0"])
88
+ s.add_dependency(%q<guard-yard>, [">= 0"])
89
+ s.add_dependency(%q<guard-spork>, [">= 0"])
90
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
91
+ end
92
+ end
93
+
@@ -0,0 +1,146 @@
1
+ module HistoryFile
2
+ # This class delegates all method calls to the {File} class.
3
+ # The generic methods that don't revolve around one specific
4
+ # file, {File.join} for example, are just passed on. These
5
+ # methods are defined in
6
+ # {HistoryFile::FileDelegator::EXCLUDED_METHODS}.
7
+ #
8
+ # The methods that revolve around doing something with one
9
+ # specific file, however, will be called with an altered
10
+ # Filename. Consider this example:
11
+ #
12
+ # > fd = HistoryFile::FileDelegator.new("some_prefix")
13
+ # > fd.open("/path/to/my_file.txt", "w") do |io|
14
+ # > io.puts "hello there"
15
+ # > end
16
+ # > puts fd.read("/path/to/my_file.txt")
17
+ # => hello there
18
+ #
19
+ # This will pass on your block to {File.open}, but with a
20
+ # prefixed the filename. So what's really called is:
21
+ #
22
+ # File.open("/path/to/some_prefix-my_file.txt")
23
+ #
24
+ # For methods that get a bunch of filenames, but only filenames,
25
+ # as arguments, all of the filenames are patched to include the
26
+ # date prefix. These methods are defined in
27
+ # {HistoryFile::FileDelegator::BULK_METHODS}
28
+ #
29
+ # You shouldn't need to instanciate this class directly,
30
+ # {HistoryFile} wraps this for you.
31
+ class FileDelegator
32
+ EXCLUDED_METHODS = [
33
+ :absolute_path,
34
+ :basename,
35
+ :catname,
36
+ :chmod,
37
+ :chown,
38
+ :compare,
39
+ :copy,
40
+ :directory?,
41
+ :dirname,
42
+ :expand_path,
43
+ :extname,
44
+ :fnmatch,
45
+ :fnmatch?,
46
+ :identical?,
47
+ :install,
48
+ :join,
49
+ :lchown,
50
+ :link,
51
+ :makedirs,
52
+ :move,
53
+ :path,
54
+ :realdirpath,
55
+ :realpath,
56
+ :rename,
57
+ :split,
58
+ :umask,
59
+ :utime
60
+ ]
61
+
62
+ BULK_METHODS = [
63
+ :delete,
64
+ :unlink,
65
+ :safe_unlink
66
+ ]
67
+
68
+ # @param prefix [String] The prefix for all methods that revolve around
69
+ # filenames
70
+ # @param fallback_glob [String] If you want to fall back to an alphabetically
71
+ # smaller file on Errno::ENOENT, you can supply a fallback glob here. It
72
+ # will be used with {Dir.glob} to find all candidates (so this should match
73
+ # all prefixes)
74
+ def initialize(prefix, fallback_glob = nil)
75
+ @prefix = prefix
76
+ @fallback_glob = fallback_glob
77
+ end
78
+
79
+ # Either
80
+ # - passes on the call directly to File (why am I not
81
+ # removing this feature?) or
82
+ # - adds the date prefix to the first argument or
83
+ # - adds the date prefix to all arguments
84
+ def delegate(*args, &block)
85
+ method = args.slice!(0,1).first
86
+ if EXCLUDED_METHODS.include?(method)
87
+ File.send(method, *args, &block)
88
+ elsif BULK_METHODS.include?(method)
89
+ delegate_with_patched_filenames(method, *args, &block)
90
+ else
91
+ delegate_with_patched_filename(method, *args, &block)
92
+ end
93
+ end
94
+
95
+ def prefixed_filename(f)
96
+ dir = File.dirname(f.to_s)
97
+ file = File.basename(f.to_s)
98
+ File.join(dir, "#{@prefix}-#{file}")
99
+ end
100
+
101
+ private
102
+
103
+ def file_fallback(original_filename, target_filename)
104
+ return false unless @fallback_glob
105
+ dir = File.dirname(original_filename.to_s)
106
+ file = File.basename(original_filename.to_s)
107
+ glob = File.join(dir, @fallback_glob+file)
108
+ candidates = Dir[glob].sort.select do |c|
109
+ c < target_filename
110
+ end.last
111
+ end
112
+
113
+ # Treats the first argument of the method as a file and passes
114
+ # the rest on as is. If we get a file not found exception, we'll
115
+ # fall back to the next older version we find.
116
+ def delegate_with_patched_filename(method, *args, &block)
117
+ filename = args.slice!(0,1).first
118
+ pf = prefixed_filename(filename)
119
+ begin
120
+ File.send(method, pf, *args, &block)
121
+ rescue Errno::ENOENT => e
122
+ raise e unless fallback = file_fallback(filename, pf)
123
+ File.send(method, fallback, *args, &block)
124
+ end
125
+ end
126
+
127
+ # Treats all arguments of the methods as files and prepends the
128
+ # history prefix
129
+ def delegate_with_patched_filenames(method, *args, &block)
130
+ pfs = args.map{ |f| prefixed_filename(f) }
131
+ File.send(method, *pfs, &block)
132
+ end
133
+
134
+ # TL;DR: This is essentially a method missing, but only for the class
135
+ # methods of {File} and without the side effects.
136
+ #
137
+ # Since method missing causes weird things (especially in tests and
138
+ # puts), we just define all methods. Keeps me from overwriting/aliasing
139
+ # all the methods as I would have to do when inheriting from File.
140
+ ((File.methods-Object.methods)+[:new]).sort.each do |m|
141
+ self.send(:define_method, m) do |*args, &block|
142
+ delegate(*([m]+args), &block)
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,47 @@
1
+ # Behaves like a {File} class and does some convenience stuff
2
+ # around a {HistoryFile::FileDelegator} instance. It all
3
+ # revolves about defining a time offset. If however, you want
4
+ # to access different versions of a file, use it like this:
5
+ #
6
+ # > f = HistoryFile[1.day.ago].new("/tmp/foo.txt", "w")
7
+ # => #<File:/tmp/2012.11.02-foo.txt>
8
+ #
9
+ # The returned {HistoryFile::FileDelegator} object supports all
10
+ # methods that File has, but adds a date prefix to those methods
11
+ # that revolve around a single file (reading, writing, etc.)
12
+ #
13
+ # If a file for a given date is not available, {HistoryFile} falls
14
+ # back to the freshest file that is older than the given date.
15
+ #
16
+ # > f = HistoryFile[3.days.ago].new("test.txt", "w")
17
+ # => #<File:./2012.11.12-test.txt>
18
+ # > f.write("I am old")
19
+ # => 8
20
+ # > f.close
21
+ # => nil
22
+ # > HistoryFile[Date.today].read("test.txt")
23
+ # => "I am old"
24
+ # > HistoryFile[10.days.ago].read("test.txt")
25
+ # Errno::ENOENT: No such file or directory - ./2012.11.05-test.txt
26
+ #
27
+ #
28
+ module HistoryFile
29
+
30
+ def self.[](offset)
31
+ validate_offset(offset)
32
+ fallback_glob = "[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]-"
33
+ FileDelegator.new(prefix(offset), fallback_glob)
34
+ end
35
+
36
+ private
37
+
38
+ def self.prefix(offset)
39
+ offset.strftime("%Y.%m.%d")
40
+ end
41
+
42
+ def self.validate_offset(offset)
43
+ return if offset.respond_to?(:strftime)
44
+ raise ArgumentError, "Offset #{offset} must respond to strftime"
45
+ end
46
+
47
+ end
@@ -0,0 +1,2 @@
1
+ require 'history_file/file_delegator'
2
+ require 'history_file/history_file'
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe HistoryFile::FileDelegator do
5
+
6
+ let(:fd){ HistoryFile::FileDelegator.new("some_prefix") }
7
+
8
+ after(:all) do
9
+ File.unlink("some_prefix-rspec_tmp_test.txt") rescue nil
10
+ end
11
+
12
+ it "delegates generic methods to File directly" do
13
+ File.should_receive(:split).with("/some/path")
14
+ fd.split("/some/path")
15
+ end
16
+
17
+ it "delegates bulk methods with all parameters prefixed" do
18
+ offset = Date.parse("1983-04-03")
19
+ File.should_receive(:unlink).with("./1983.04.03-file1", "./1983.04.03-file2")
20
+ HistoryFile[offset].unlink("file1", "file2")
21
+ end
22
+
23
+ it "instanciating new File instances works as expected" do
24
+ f = fd.new("rspec_tmp_test.txt", "w")
25
+ f.write("works")
26
+ f.close
27
+ File.read("some_prefix-rspec_tmp_test.txt").should == "works"
28
+ end
29
+
30
+ it "writing into a file using a block works as expected" do
31
+ fd.open("rspec_tmp_test.txt", "w") do |file|
32
+ file.write("works")
33
+ end
34
+ File.read("some_prefix-rspec_tmp_test.txt").should == "works"
35
+ end
36
+ end
37
+
38
+ describe HistoryFile do
39
+
40
+ context "writing and reading files" do
41
+ it "uses a correct prefix" do
42
+ offset = Date.parse("1979-12-22")
43
+ HistoryFile[offset].open("rspec_tmp_test.txt", "w") do |file|
44
+ file.write("old dude")
45
+ end
46
+ File.read("1979.12.22-rspec_tmp_test.txt").should == "old dude"
47
+ File.unlink("1979.12.22-rspec_tmp_test.txt")
48
+ end
49
+
50
+ it "doesn't allow offsets that don't respond to strftime" do
51
+ expect{
52
+ HistoryFile[:today].open("rspec_tmp_test.txt", "w")
53
+ }.to raise_error(ArgumentError, "Offset today must respond to strftime")
54
+ end
55
+ end
56
+
57
+ context "falling back to older files" do
58
+ before(:all) do
59
+ [1,2,3,6,7].each do |i|
60
+ date = DateTime.now - i
61
+ HistoryFile[date].open("ht.txt", "w") do |file|
62
+ file.write "Day #{i}"
63
+ end
64
+ end
65
+ end
66
+
67
+ after(:all) do
68
+ [1,2,3,6,7].each do |i|
69
+ date = DateTime.now - i
70
+ HistoryFile[date].unlink("ht.txt")
71
+ end
72
+ end
73
+
74
+ it "to yesterday's file" do
75
+ date = DateTime.now - 4
76
+ HistoryFile[date].read("ht.txt").should == "Day 6"
77
+ end
78
+
79
+ it "to an error if nothing older exists" do
80
+ expect{
81
+ date = DateTime.now - 8
82
+ HistoryFile[date].read("ht.txt")
83
+ }.to raise_error(Errno::ENOENT)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ require 'bundler'
6
+ Bundler.require
7
+
8
+ Spork.prefork do
9
+ # Loading more in this block will cause your tests to run faster. However,
10
+ # if you change any configuration or code from libraries loaded here, you'll
11
+ # need to restart spork for it take effect.
12
+
13
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
14
+ ENV["RAILS_ENV"] ||= 'test'
15
+ # require File.expand_path("../../config/environment", __FILE__)
16
+ #require 'capybara/poltergeist'
17
+
18
+ # Requires supporting ruby files with custom matchers and macros, etc,
19
+ # in spec/support/ and its subdirectories.
20
+ Dir[File.expand_path("../../spec/support/**/*.rb", __FILE__)].each {|f| require f}
21
+
22
+ RSpec.configure do |config|
23
+ # == Mock Framework
24
+ #
25
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
26
+ #
27
+ # config.mock_with :mocha
28
+ # config.mock_with :flexmock
29
+ # config.mock_with :rr
30
+ config.mock_with :rspec
31
+
32
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
33
+
34
+ end
35
+
36
+ module TestResponseExtensions
37
+ def json_body
38
+ JSON.parse(self.body)
39
+ end
40
+
41
+ def unauthorized?
42
+ code.to_i == 401
43
+ end
44
+ end
45
+
46
+ def expect_id_and_return(id, ret_val)
47
+ lambda { |id| id.to_s.should == id.to_s; ret_val }
48
+ end
49
+
50
+ end
51
+
52
+ require File.expand_path("../../lib/history_file/", __FILE__)
53
+
54
+ # Spork.each_run do
55
+ # FactoryGirl.reload
56
+ # end
metadata ADDED
@@ -0,0 +1,290 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: history_file
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jannis Hermanns
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shoulda
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rdoc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.12'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.12'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: jeweler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.8.4
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.8.4
78
+ - !ruby/object:Gem::Dependency
79
+ name: simplecov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: webmock
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: terminal-notifier
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: guard
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: guard-rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: guard-bundler
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: guard-yard
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: guard-spork
208
+ requirement: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ! '>='
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ - !ruby/object:Gem::Dependency
223
+ name: rb-fsevent
224
+ requirement: !ruby/object:Gem::Requirement
225
+ none: false
226
+ requirements:
227
+ - - ! '>='
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ none: false
234
+ requirements:
235
+ - - ! '>='
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ description: A File like class that supports versioning by date and has a fallback
239
+ to older files
240
+ email: jannis@moviepilot.com
241
+ executables: []
242
+ extensions: []
243
+ extra_rdoc_files:
244
+ - LICENSE.txt
245
+ - README.rdoc
246
+ files:
247
+ - .document
248
+ - .rvmrc
249
+ - Gemfile
250
+ - Gemfile.lock
251
+ - Guardfile
252
+ - LICENSE.txt
253
+ - README.rdoc
254
+ - Rakefile
255
+ - VERSION
256
+ - history_file.gemspec
257
+ - lib/history_file.rb
258
+ - lib/history_file/file_delegator.rb
259
+ - lib/history_file/history_file.rb
260
+ - spec/history_file/history_file_spec.rb
261
+ - spec/spec_helper.rb
262
+ homepage: http://github.com/moviepilot/history_file
263
+ licenses:
264
+ - MIT
265
+ post_install_message:
266
+ rdoc_options: []
267
+ require_paths:
268
+ - lib
269
+ required_ruby_version: !ruby/object:Gem::Requirement
270
+ none: false
271
+ requirements:
272
+ - - ! '>='
273
+ - !ruby/object:Gem::Version
274
+ version: '0'
275
+ segments:
276
+ - 0
277
+ hash: 3192868008292833198
278
+ required_rubygems_version: !ruby/object:Gem::Requirement
279
+ none: false
280
+ requirements:
281
+ - - ! '>='
282
+ - !ruby/object:Gem::Version
283
+ version: '0'
284
+ requirements: []
285
+ rubyforge_project:
286
+ rubygems_version: 1.8.24
287
+ signing_key:
288
+ specification_version: 3
289
+ summary: A File like class with history support
290
+ test_files: []