sequential_file 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e61c7c5b4f4c0cbfb6097b0110a843ddd6a4e4d1
4
+ data.tar.gz: 80ea9b4d801cdbf48db74c9e4224c4e2998b136a
5
+ SHA512:
6
+ metadata.gz: a8937ecceba9d2ac7b78c8e128b128eef2598e38ddd81a7a30ce5680a9cf9cf0aa05fb14268a0308db2dac8a82050a22af69f9970a2d2c2a5259f90223d4963d
7
+ data.tar.gz: be2b9618b48c2482f57eb0dafa2b3c19bb2a1743c074a3d226605bb49072028a20c337ca1e64e62a105ef2f3a57bf5c6fc7071466f4626a668a50e84fcfa92c7
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - ruby-head
@@ -0,0 +1,2 @@
1
+ Version 0.0.1 - FEB.26.2014
2
+ * Initial Release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sequential_file.gemspec
4
+ gemspec
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Peter Boling
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,143 @@
1
+ # sequential_file
2
+
3
+ Sequential File makes determination of which file to process (read or write) easy!
4
+
5
+ | Project | Sequential File |
6
+ |------------------------ | ----------------- |
7
+ | gem name | sequential_file |
8
+ | license | MIT |
9
+ | moldiness | [![Maintainer Status](http://stillmaintained.com/pboling/sequential_file.png)](http://stillmaintained.com/pboling/sequential_file) |
10
+ | version | [![Gem Version](https://badge.fury.io/rb/sequential_file.png)](http://badge.fury.io/rb/sequential_file) |
11
+ | dependencies | [![Dependency Status](https://gemnasium.com/pboling/sequential_file.png)](https://gemnasium.com/pboling/sequential_file) |
12
+ | code quality | [![Code Climate](https://codeclimate.com/github/pboling/sequential_file.png)](https://codeclimate.com/github/pboling/sequential_file) |
13
+ | continuous integration | [![Build Status](https://secure.travis-ci.org/pboling/sequential_file.png?branch=master)](https://travis-ci.org/pboling/sequential_file) |
14
+ | test coverage | [![Coverage Status](https://coveralls.io/repos/pboling/sequential_file/badge.png)](https://coveralls.io/r/pboling/sequential_file) |
15
+ | homepage | [https://github.com/pboling/sequential_file][homepage] |
16
+ | documentation | [http://rdoc.info/github/pboling/sequential_file/frames][documentation] |
17
+ | author | [Peter Boling](https://coderbits.com/pboling) |
18
+ | Spread ~♡ⓛⓞⓥⓔ♡~ | [![Endorse Me](https://api.coderwall.com/pboling/endorsecount.png)](http://coderwall.com/pboling) |
19
+
20
+ ## Summary
21
+
22
+ It allows you to create files that are named sequentially *and* intelligently.
23
+
24
+ Files will have four primary parts in their filenames:
25
+
26
+ 1. First part, which will be useful for list files in the shell.
27
+ 2. Second part is a date stamp, which will be useful in not having unwanted junk in our globs.
28
+ 3. Third part makes it easy to find the counter
29
+ 4. Fourth part is the counter
30
+
31
+ ## Compatibility
32
+
33
+ Tested with Ruby 2.0.0+. Due to the use of Ruby's 'prepend' initialization feature, will not work with anything older.
34
+
35
+ There are no dependencies, gem or otherwise, this is a pure Ruby library, and will work with any sort of File-type class.
36
+
37
+ ## Installation
38
+
39
+ Add this line to your application's Gemfile:
40
+
41
+ gem 'sequential_file'
42
+
43
+ And then execute:
44
+
45
+ $ bundle
46
+
47
+ Or install it yourself as:
48
+
49
+ $ gem install sequential_file
50
+
51
+ ## Usage
52
+
53
+ If you have your own File-like class then you may just need to use the SequentialFile::Namer by including it.
54
+
55
+ ```
56
+ class MyFileLikeThing
57
+ include SequentialFile::Namer
58
+ end
59
+ ```
60
+
61
+ This gem ships with a reference implementation of a File-wrapper class that uses the Namer to determine which file to process.
62
+
63
+ Have a look at `lib/sequential_file/base.rb` or require it and play with it:
64
+
65
+ ```
66
+ require 'sequential_file/base'
67
+
68
+ file = SequentialFile::Base.new({
69
+ directory_path: '/tmp',
70
+ name: 'access_log.FileBase.csv',
71
+ process_date: Date.today
72
+ })
73
+
74
+ file.write('this is new text') # => write ensures that the file is open for writing, but you have to remember to close!
75
+ file.close
76
+ file.write('this is after close text')
77
+ file.read =~ /this is after close text/ # => integer representation of the position in the file where the text match starts
78
+ file.close
79
+ ```
80
+
81
+ Also, see the specs.
82
+
83
+ ## Contributors
84
+
85
+ See the [Network View](https://github.com/pboling/sequential_file/network) and the [CHANGELOG](https://github.com/pboling/sequential_file/blob/master/CHANGELOG.md)
86
+
87
+ ## How you can help!
88
+
89
+ Take a look at the `reek` and `roodi` lists. These are the files names `REEK` and `ROODI` in the root of the gem.
90
+
91
+ Currently they are clean, and we like to keep them that way! Once you complete a change, run the tests:
92
+
93
+ ```
94
+ bundle exec rake spec
95
+ ```
96
+
97
+ If the tests pass refresh the `reek` and `roodi` lists and make sure things have not gotten worse:
98
+
99
+ ```
100
+ bundle exec rake reek > REEK
101
+ bundle exec rake reek > ROODI
102
+ ```
103
+
104
+ Follow the instructions for "Contributing" below.
105
+
106
+ ## Contributing
107
+
108
+ 1. Fork it
109
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
110
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
111
+ 4. Push to the branch (`git push origin my-new-feature`)
112
+ 5. Create new Pull Request
113
+
114
+ ## Versioning
115
+
116
+ This library aims to adhere to [Semantic Versioning 2.0.0][semver].
117
+ Violations of this scheme should be reported as bugs. Specifically,
118
+ if a minor or patch version is released that breaks backward
119
+ compatibility, a new version should be immediately released that
120
+ restores compatibility. Breaking changes to the public API will
121
+ only be introduced with new major versions.
122
+
123
+ As a result of this policy, you can (and should) specify a
124
+ dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision.
125
+
126
+ For example:
127
+
128
+ spec.add_dependency 'sequential_file', '~> 1.0.8'
129
+
130
+ ## Legal
131
+
132
+ * MIT License - See LICENSE file in this project
133
+ * Copyright (c) 2014 [Peter H. Boling][peterboling] of [Rails Bling][railsbling]
134
+
135
+ [semver]: http://semver.org/
136
+ [pvc]: http://docs.rubygems.org/read/chapter/16#page74
137
+ [railsbling]: http://www.railsbling.com
138
+ [peterboling]: http://www.peterboling.com
139
+ [documentation]: http://rdoc.info/github/pboling/sequential_file/frames
140
+ [homepage]: https://github.com/pboling/sequential_file
141
+
142
+
143
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/pboling/sequential_file/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
data/REEK ADDED
@@ -0,0 +1,2 @@
1
+
2
+ 0 total warnings
data/ROODI ADDED
@@ -0,0 +1,5 @@
1
+
2
+ Running Roodi checks
3
+
4
+ Checked 9 files
5
+ Found 0 errors.
@@ -0,0 +1,33 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ require 'reek/rake/task'
10
+ Reek::Rake::Task.new do |t|
11
+ t.fail_on_error = true
12
+ t.verbose = false
13
+ t.source_files = 'lib/**/*.rb'
14
+ end
15
+
16
+ require 'roodi'
17
+ require 'roodi_task'
18
+ RoodiTask.new do |t|
19
+ t.verbose = false
20
+ end
21
+
22
+ task :default => :spec
23
+
24
+ require File.expand_path('../lib/sequential_file/version', __FILE__)
25
+ require 'rdoc'
26
+ require 'rdoc/task'
27
+ RDoc::Task.new do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = "SequentialFile #{SequentialFile::VERSION}"
30
+ rdoc.options << '--line-numbers'
31
+ rdoc.rdoc_files.include('README*')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
@@ -0,0 +1,10 @@
1
+ require "sequential_file/version"
2
+ require "sequential_file/namer"
3
+
4
+ # SequentialFile::Base is not loaded by default, because it is a reference implementation, and third party libs may not need or want it.
5
+ # It can be used by adding the following to any third party lib:
6
+ #require "sequential_file/base"
7
+
8
+ module SequentialFile
9
+ # Just a namespace here...
10
+ end
@@ -0,0 +1,109 @@
1
+ require 'date'
2
+
3
+ module SequentialFile
4
+ # This is a reference implementation of a File-wrapper class that uses the Namer to determine which file to process
5
+ class Base
6
+
7
+ include SequentialFile::Namer
8
+
9
+ attr_accessor :complete_path # the complete path to the file
10
+ attr_accessor :read_position # the byte position in the file where read stopped and monitor will begin.
11
+ attr_accessor :file, :permission_style
12
+ attr_accessor :buffer_size, :buffer_limit, :buffer_position, :verbose
13
+
14
+ def initialize(options = {}, &block)
15
+ yield if block_given?
16
+ @complete_path ||= File.join(self.directory_path, self.name)
17
+ @buffer_limit = options[:buffer_limit] || 200 # bytes
18
+ @buffer_size = 0
19
+ @permission_style = options[:permission_style] || File::RDWR|File::CREAT
20
+ @verbose = !!options[:verbose]
21
+ end
22
+
23
+ def info_string
24
+ "PERM: #{@permission_style}\n\tPATH: #{@complete_path}"
25
+ end
26
+
27
+ def file_handle
28
+ # autoclose: false => Keep the file descriptor open until we want to close it.
29
+ File.new(@complete_path, @permission_style, {autoclose: false})
30
+ self.file ||= File.new(@complete_path, @permission_style, {autoclose: false})
31
+ end
32
+
33
+ def num_lines
34
+ file_handle.seek(0, IO::SEEK_SET)
35
+ file_handle.readlines.size
36
+ end
37
+
38
+ # Need to remember to close file with self.close after done using the file descriptor
39
+ def write(msg)
40
+ prep_write
41
+ puts "#-------> @[ #{@file_handle.pos} ] B[ #{@buffer_size} ] in #{@complete_path} <---------#\n#{msg}" if verbose
42
+ add_to_buffer(msg)
43
+ flush_if_buffer_full
44
+ end
45
+
46
+ def flush_if_buffer_full
47
+ if buffer_size >= buffer_limit
48
+ file_handle.flush
49
+ @buffer_size = 0
50
+ end
51
+ end
52
+
53
+ # Read file from beginning to end, recording position when done
54
+ # Need to remember to close file with self.close after done using the file descriptor
55
+ def read &block
56
+ prep_read
57
+ data = block_given? ?
58
+ file_handle.each {|line| yield line } :
59
+ file_handle.read
60
+ @read_position = file_handle.pos
61
+ data
62
+ end
63
+
64
+ def close
65
+ file_handle.close
66
+ # Set the file to nil, so it will be reopened when needed.
67
+ self.file = nil
68
+ end
69
+
70
+ def clear
71
+ file_handle.truncate 0
72
+ # Set the file to nil, so it will be reopened when needed.
73
+ close
74
+ end
75
+
76
+ def delete
77
+ if file
78
+ close # just in case
79
+ File.delete(complete_path)
80
+ end
81
+ end
82
+
83
+ protected
84
+ def prep_write
85
+ file_handle.flock(File::LOCK_EX) # an exclusive lock on the file.
86
+ file_handle.seek(0, IO::SEEK_END)
87
+ end
88
+ def prep_read
89
+ file_handle.flock(File::LOCK_SH) # a shared lock on the file.
90
+ file_handle.seek(0, IO::SEEK_SET)
91
+ end
92
+ def add_to_buffer(msg)
93
+ @buffer_size += msg.length
94
+ file_handle.write(msg)
95
+ end
96
+ end
97
+ end
98
+
99
+ # Read file from #position where read stopped and continue to monitor for additional lines added
100
+ # Need to remember to close file with self.close after done using the file descriptor
101
+ # WARNING: this is an infinite loop, so know how you will stop it before you start it.
102
+ #def monitor &block
103
+ # f = File.open(self.complete_path,"r")
104
+ # f.seek(self.read_position, IO::SEEK_SET)
105
+ # while true do
106
+ # select([f]) # Kernel#select
107
+ # yield f.gets # a line
108
+ # end
109
+ #end
@@ -0,0 +1,30 @@
1
+ module SequentialFile
2
+
3
+ # This class takes a filename and an extension, and finds the counter (Integer) part of the file name
4
+ class CounterFinder
5
+ NUMBER_REGEX = /\d*/
6
+
7
+ # determines the counter value of the filename passed in.
8
+ # params:
9
+ # filename - string, complete filename
10
+ # extension - string, the part of the filename that is the extension (e.g. .log or .csv)
11
+ def initialize(filename, extension)
12
+ @filename = File.basename(filename, extension)
13
+ @length = @filename.length
14
+ @index_of_extension_separator = @filename.rindex('.')
15
+ end
16
+
17
+ def has_extension_separator?
18
+ !!@index_of_extension_separator
19
+ end
20
+
21
+ # returns:
22
+ # an integer representation of the counter
23
+ def counter
24
+ has_extension_separator? ?
25
+ @filename[@index_of_extension_separator + 1,@length][NUMBER_REGEX].to_i :
26
+ 0
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,107 @@
1
+ require 'sequential_file/counter_finder'
2
+
3
+ module SequentialFile
4
+ module Namer
5
+
6
+ module Initializer
7
+ def initialize(options = {})
8
+ super do
9
+ name = options[:name]
10
+ name ?
11
+ derive_name_parts_from_name(name) :
12
+ set_name_parts(options[:filename_first_part], options[:filename_third_part], options[:file_extension])
13
+ @directory_path = options[:directory_path]
14
+ @process_date = options[:process_date] || Date.today
15
+ if options[:append]
16
+ @last_filename_counter = self.last_used_counter
17
+ else
18
+ @last_filename_counter = self.get_next_available_counter
19
+ end
20
+ @name = self.determine_name
21
+ end
22
+ end
23
+ end
24
+
25
+ # :directory_path - directory path where file will be located
26
+ # :name - complete intelligent *and* sequential filename
27
+ # :process_date - date stamp of invocation, used as part of filename
28
+ # :filename_first_part - first part of filename, separated from third part by the chronometer
29
+ # :filename_third_part - third part of filename, separated from first part by the chronometer
30
+ # :last_filename_counter - the last-used file counter; prevent files from stepping on each other.
31
+ # :file_extension - the file extension
32
+ def self.included(klass)
33
+ klass.send :prepend, Initializer
34
+ klass.send :attr_accessor, :directory_path, :name, :process_date, :filename_first_part, :filename_third_part, :last_filename_counter, :file_extension
35
+ end
36
+
37
+ protected
38
+ def set_name_parts(first_part, third_part, name_extension)
39
+ @filename_first_part = first_part
40
+ @filename_third_part = third_part
41
+ @file_extension = name_extension
42
+ end
43
+
44
+ def derive_name_parts_from_name(name_option)
45
+ parts = name_option.split('.')
46
+ raise ArgumentError, ":name must have three parts separated by dots ('.') in order to be a valid option for #{self.class.name}" unless parts.length == 3
47
+ set_name_parts(parts[0], parts[1], '.' << parts[2])
48
+ end
49
+
50
+ def globular_file_parts
51
+ chrono = get_chronometer
52
+ with_dot = chrono.empty? ? '' : ".#{chrono}"
53
+ "#{@filename_first_part}#{with_dot}.#{@filename_third_part}"
54
+ end
55
+
56
+ def determine_name
57
+ "#{globular_file_parts}.#{@last_filename_counter}#{@file_extension}"
58
+ end
59
+
60
+ def get_chronometer
61
+ @process_date.respond_to?(:strftime) ?
62
+ @process_date.strftime("%Y%m%d") :
63
+ @process_date.to_s
64
+ end
65
+
66
+ # determines the last used file counter value
67
+ # returns:
68
+ # an integer representation of the counter
69
+ def last_used_counter
70
+ directory_glob.inject(0) do |high_value, file|
71
+ counter = get_counter_for_file(file)
72
+ ((high_value <=> counter) == 1) ? high_value : counter
73
+ end
74
+ end
75
+
76
+ # determines the counter value for the next bump file to create
77
+ # returns:
78
+ # an integer representation of the next counter to use
79
+ def get_next_available_counter
80
+ if @last_filename_counter
81
+ @last_filename_counter + 1
82
+ else
83
+ last_used_counter + 1
84
+ end
85
+ end
86
+
87
+ # determines the counter value of the filename passed in.
88
+ # params:
89
+ # none
90
+ # returns:
91
+ # an array of file names in the directory matching the glob
92
+ def directory_glob
93
+ glob = File.join(directory_path, "#{globular_file_parts}.*")
94
+ Dir.glob(glob)
95
+ end
96
+
97
+ # determines the counter value of the filename passed in.
98
+ # params:
99
+ # filename - string, complete filename
100
+ # returns:
101
+ # an integer representation of the counter
102
+ def get_counter_for_file(filename)
103
+ CounterFinder.new(filename, @file_extension).counter
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module SequentialFile
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sequential_file/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sequential_file"
8
+ spec.version = SequentialFile::VERSION
9
+ spec.authors = ["Peter Boling"]
10
+ spec.email = ["peter.boling@gmail.com"]
11
+ spec.description = %q{Create Files Named Sequentially Intelligently}
12
+ spec.summary = %q{Create Files Named Sequentially Intelligently}
13
+ spec.homepage = "http://railsbling.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ ############################
22
+ # RUNTIME DEPENDENCIES #
23
+ ############################
24
+ #
25
+ # NONE!
26
+
27
+ ############################
28
+ # DEVELOPMENT DEPENDENCIES #
29
+ ############################
30
+
31
+ # Gem dependency manager
32
+ spec.add_development_dependency(%q<bundler>, ["~> 1.5.3"])
33
+
34
+ # The ruby workhorse command line tool.
35
+ spec.add_development_dependency(%q<rake>, ["~> 10.1.1"])
36
+
37
+ # For a test suite.
38
+ # https://github.com/rspec/rspec
39
+ spec.add_development_dependency(%q<rspec>, ["~> 2.14"])
40
+
41
+ # Give the test suite super powers (and less verbosity).
42
+ # https://github.com/rspec/rspec
43
+ spec.add_development_dependency(%q<shoulda>, ["~> 3.5.0"])
44
+ spec.add_development_dependency(%q<shoulda-matchers>, ["~> 1.5.6"])
45
+
46
+ # For detecting code smells.
47
+ # https://github.com/troessner/reek
48
+ spec.add_development_dependency(%q<reek>, ["~> 1.3.6"])
49
+
50
+ # For detecting more code smells.
51
+ # https://github.com/roodi/roodi
52
+ spec.add_development_dependency(%q<roodi>, ["~> 3.3.1"])
53
+
54
+ # For code coverage
55
+ spec.add_development_dependency "coveralls"
56
+
57
+ # Useful to work with relative paths to file, and to derive absolute paths
58
+ spec.add_dependency(%q<rbx-require-relative>, ["~> 0.0.9"])
59
+
60
+ # For autotest-like sweetness
61
+ spec.add_dependency(%q<guard-rspec>, ["~> 2.4.0"])
62
+
63
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples_for_files'
3
+ require 'sequential_file/base'
4
+
5
+ describe SequentialFile::Base do
6
+ FILE_BASE_TEST_PATH = File.join(GEM_ROOT, 'spec/tmp')
7
+ describe "#initialize" do
8
+ context "process date as empty string" do
9
+ let(:file){ SequentialFile::Base.new({
10
+ directory_path: FILE_BASE_TEST_PATH,
11
+ name: 'access_log.FileBase.tsv',
12
+ process_date: ''
13
+ }) }
14
+ subject { file }
15
+ after(:each) do
16
+ subject.delete
17
+ end
18
+ it_behaves_like "a SequentialFile::Base"
19
+
20
+ it('- class') { subject.class.should == SequentialFile::Base }
21
+ it('- complete_filename') { subject.name.should == 'access_log.FileBase.1.tsv' }
22
+ it('- complete_path') { subject.complete_path.should == File.join(subject.directory_path, 'access_log.FileBase.1.tsv') }
23
+ end
24
+
25
+ context "process date as nil" do
26
+ let(:file){ SequentialFile::Base.new({
27
+ directory_path: FILE_BASE_TEST_PATH,
28
+ name: 'access_log.FileBase.tsv',
29
+ process_date: nil
30
+ }) }
31
+ subject { file }
32
+ after(:each) do
33
+ subject.delete
34
+ end
35
+ it_behaves_like "a SequentialFile::Base"
36
+
37
+ it('- class') { subject.class.should == SequentialFile::Base }
38
+ it('- complete_filename') { subject.name.should == "access_log.#{Date.today.strftime("%Y%m%d")}.FileBase.1.tsv" }
39
+ it('- complete_path') { subject.complete_path.should == File.join(subject.directory_path, "access_log.#{Date.today.strftime("%Y%m%d")}.FileBase.1.tsv") }
40
+ end
41
+
42
+ context "process date as word" do
43
+ let(:file){ SequentialFile::Base.new({
44
+ directory_path: FILE_BASE_TEST_PATH,
45
+ name: 'access_log.FileBase.tsv',
46
+ process_date: 'today'
47
+ }) }
48
+ subject { file }
49
+ after(:each) do
50
+ subject.delete
51
+ end
52
+ it_behaves_like "a SequentialFile::Base"
53
+
54
+ it('- class') { subject.class.should == SequentialFile::Base }
55
+ it('- complete_filename') { subject.name.should == 'access_log.today.FileBase.1.tsv' }
56
+ it('- complete_path') { subject.complete_path.should == File.join(subject.directory_path, 'access_log.today.FileBase.1.tsv') }
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe SequentialFile::CounterFinder::NUMBER_REGEX do
4
+ it('- should be a Regexp') { SequentialFile::CounterFinder::NUMBER_REGEX.should be_a(Regexp)}
5
+ it('- should match numbers') { ('asdf5' =~ SequentialFile::CounterFinder::NUMBER_REGEX.should) == 4}
6
+ end
7
+
8
+ describe SequentialFile::CounterFinder do
9
+
10
+ describe "#initialize" do
11
+ let(:counter_finder){ SequentialFile::CounterFinder.new('access_log.FileBase.1.tsv', '.tsv') }
12
+ subject { counter_finder }
13
+ it('- class') { subject.class.should == SequentialFile::CounterFinder }
14
+ end
15
+
16
+ context "with counter" do
17
+ let(:counter_finder){ SequentialFile::CounterFinder.new('access_log.FileBase.1.tsv', '.tsv') }
18
+ subject { counter_finder }
19
+ it('- has_extension_separator?') { subject.has_extension_separator?.should == true }
20
+ it('- counter') { subject.counter.should == 1 }
21
+ end
22
+
23
+ context "without counter" do
24
+ let(:counter_finder){ SequentialFile::CounterFinder.new('access_log.FileBase.tsv', '.tsv') }
25
+ subject { counter_finder }
26
+ it('- has_extension_separator?') { subject.has_extension_separator?.should == true }
27
+ it('- counter') { subject.counter.should == 0 }
28
+ end
29
+
30
+ context "with large counter" do
31
+ let(:counter_finder){ SequentialFile::CounterFinder.new('access_log.FileBase.10234856.tsv', '.tsv') }
32
+ subject { counter_finder }
33
+ it('- has_extension_separator?') { subject.has_extension_separator?.should == true }
34
+ it('- counter') { subject.counter.should == 10234856 }
35
+ end
36
+
37
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples_for_files'
3
+ require 'sequential_file/base'
4
+
5
+ class BumpFileB < SequentialFile::Base
6
+ include SequentialFile
7
+ end
8
+
9
+ class BumpFile < BumpFileB
10
+ def initialize(options = {})
11
+ options[:file_extension] = '.json'
12
+ super options
13
+ end
14
+ end
15
+
16
+ describe BumpFile do
17
+ NAMER_TEST_PATH = File.join(GEM_ROOT, 'spec/tmp')
18
+ describe "as a file" do
19
+ let(:file){ BumpFile.new({ directory_path: NAMER_TEST_PATH, process_date: Date.new(2014,2,14) }) }
20
+ subject { file }
21
+ after(:each) do
22
+ subject.delete
23
+ end
24
+ it_behaves_like "a SequentialFile::Base"
25
+ end
26
+ describe "#initialize" do
27
+ context "basic options" do
28
+ before(:each) do
29
+ @bump_file = BumpFile.new({ directory_path: NAMER_TEST_PATH, process_date: Date.new(2014,2,14) })
30
+ end
31
+ it('- class') { @bump_file.class.should == BumpFile }
32
+ it('- name') { @bump_file.name.should == '.20140214..1.json' }
33
+ it('- complete_path') { @bump_file.complete_path.should == File.join(@bump_file.directory_path, '.20140214..1.json') }
34
+ end
35
+ context "realistic options" do
36
+ before(:each) do
37
+ @bump_file = BumpFile.new({
38
+ directory_path: NAMER_TEST_PATH,
39
+ filename_first_part: 'asdf',
40
+ filename_third_part: 'qwer',
41
+ process_date: Date.new(2014,2,14)
42
+ })
43
+ end
44
+ it('- filename_counter') { @bump_file.last_filename_counter.should == 1 }
45
+ it('- name') { @bump_file.name.should == 'asdf.20140214.qwer.1.json' }
46
+ it('- complete_path') { @bump_file.complete_path.should == File.join(@bump_file.directory_path, 'asdf.20140214.qwer.1.json') }
47
+ end
48
+ context "name option" do
49
+ before(:each) do
50
+ @bump_file = BumpFile.new({
51
+ directory_path: NAMER_TEST_PATH,
52
+ name: 'abra.cadabra.log',
53
+ process_date: Date.new(2014,3,14)
54
+ })
55
+ end
56
+ it('- filename_counter') { @bump_file.last_filename_counter.should == 1 }
57
+ it('- name') { @bump_file.name.should == 'abra.20140314.cadabra.1.log' }
58
+ it('- complete_path') { @bump_file.complete_path.should == File.join(@bump_file.directory_path, 'abra.20140314.cadabra.1.log') }
59
+ end
60
+ context "append" do
61
+ before(:each) do
62
+ @bump_file = BumpFile.new({
63
+ directory_path: NAMER_TEST_PATH,
64
+ name: 'abra.cadabra.log',
65
+ process_date: Date.new(2014,3,14),
66
+ append: true
67
+ })
68
+ end
69
+ it('- filename_counter') { @bump_file.last_filename_counter.should == 0 }
70
+ it('- name') { @bump_file.name.should == 'abra.20140314.cadabra.0.log' }
71
+ it('- complete_path') { @bump_file.complete_path.should == File.join(@bump_file.directory_path, 'abra.20140314.cadabra.0.log') }
72
+ end
73
+ context "pre-existing file on counter #42" do
74
+ before(:each) do
75
+ @bump_file = BumpFile.new({
76
+ directory_path: File.join(GEM_ROOT,'spec/test_data'),
77
+ filename_first_part: 'asdf',
78
+ filename_third_part: 'qwer',
79
+ process_date: Date.new(2014,2,14)
80
+ })
81
+ end
82
+ it('- last_filename_counter') { @bump_file.last_filename_counter.should == 43 }
83
+ it('- name') { @bump_file.name.should == 'asdf.20140214.qwer.43.json' }
84
+ it('- complete_path') { @bump_file.complete_path.should == File.join(GEM_ROOT,'spec/test_data', 'asdf.20140214.qwer.43.json') }
85
+ end
86
+ end
87
+ describe "#write" do
88
+ context "data to a file" do
89
+ before(:each) do
90
+ @bump_file = BumpFile.new({
91
+ directory_path: File.join(GEM_ROOT,'spec/scratch'),
92
+ filename_first_part: 'asdf',
93
+ filename_third_part: 'qwer',
94
+ process_date: Date.new(2014,2,14)
95
+ })
96
+ end
97
+ after(:each) do
98
+ @bump_file.delete
99
+ end
100
+ it('- no exception') { lambda { @bump_file.write('This is bogus') }.should_not raise_exception }
101
+ it('- can read what was written') {
102
+ @bump_file.write('This is bogus')
103
+ # Because of the random seeding of test order this spec may run before the one above
104
+ # So "This is bogus" may be printed once or twice.
105
+ @bump_file.read.should =~ /This is bogus/
106
+ }
107
+ end
108
+ end
109
+ end
File without changes
@@ -0,0 +1,44 @@
1
+ shared_examples "a SequentialFile::Base" do
2
+
3
+ describe "ancestry" do
4
+ it('- class') { subject.is_a?( SequentialFile::Base ).should == true }
5
+ end
6
+
7
+ describe "#write" do
8
+ context "data to a file" do
9
+ it('- should succeed') {
10
+ lambda { subject.write('this is text') }.should_not raise_exception
11
+ }
12
+ it('- should be readable') {
13
+ subject.write('this is text')
14
+ subject.read.should =~ /this is text/
15
+ }
16
+ end
17
+ context "after close" do
18
+ it('- should write') {
19
+ subject.write('this is text')
20
+ subject.close
21
+ lambda { subject.write('this is text') }.should_not raise_exception
22
+ }
23
+ it('- should be readable') {
24
+ subject.write('this is new text')
25
+ subject.close
26
+ subject.write('this is after close text')
27
+ subject.read.should =~ /this is after close text/
28
+ }
29
+ end
30
+ end
31
+
32
+ describe "#read" do
33
+ context "data from a file" do
34
+ it('- without block') { lambda { subject.read {} }.should_not raise_exception }
35
+ it('- with block') { lambda { subject.read { |line| puts line } }.should_not raise_exception }
36
+ it('- advances position') {
37
+ subject.write('this is text')
38
+ subject.read
39
+ subject.read_position.should be == 12
40
+ }
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,7 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --loadby
5
+ mtime
6
+ --reverse
7
+ --backtrace
@@ -0,0 +1,24 @@
1
+ require 'require_relative' # loads rbx-require-relative gem
2
+
3
+ # For code coverage, must be required before all application / gem / library code.
4
+ unless ENV['NOCOVER']
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+ end
8
+
9
+ require 'sequential_file'
10
+
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ config.order = 'random'
22
+ end
23
+
24
+ GEM_ROOT = RequireRelative.abs_file.split("spec/spec_helper.rb")[0]
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequential_file
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Peter Boling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.3
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 10.1.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 10.1.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: shoulda
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 3.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: shoulda-matchers
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.5.6
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.5.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: reek
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.6
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.6
97
+ - !ruby/object:Gem::Dependency
98
+ name: roodi
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 3.3.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 3.3.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
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
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rbx-require-relative
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: 0.0.9
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 0.0.9
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: 2.4.0
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: 2.4.0
153
+ description: Create Files Named Sequentially Intelligently
154
+ email:
155
+ - peter.boling@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - .coveralls.yml
161
+ - .gitignore
162
+ - .rspec
163
+ - .travis.yml
164
+ - CHANGELOG.md
165
+ - Gemfile
166
+ - Guardfile
167
+ - LICENSE.txt
168
+ - README.md
169
+ - REEK
170
+ - ROODI
171
+ - Rakefile
172
+ - lib/sequential_file.rb
173
+ - lib/sequential_file/base.rb
174
+ - lib/sequential_file/counter_finder.rb
175
+ - lib/sequential_file/namer.rb
176
+ - lib/sequential_file/version.rb
177
+ - sequential_file.gemspec
178
+ - spec/lib/sequential_file/base_spec.rb
179
+ - spec/lib/sequential_file/counter_finder_spec.rb
180
+ - spec/lib/sequential_file/namer_spec.rb
181
+ - spec/scratch/.gitkeep
182
+ - spec/shared_examples_for_files.rb
183
+ - spec/spec.opts
184
+ - spec/spec_helper.rb
185
+ - spec/test_data/asdf.20140214.qwer.42.json
186
+ homepage: http://railsbling.com
187
+ licenses:
188
+ - MIT
189
+ metadata: {}
190
+ post_install_message:
191
+ rdoc_options: []
192
+ require_paths:
193
+ - lib
194
+ required_ruby_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - '>='
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ required_rubygems_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - '>='
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ requirements: []
205
+ rubyforge_project:
206
+ rubygems_version: 2.2.2
207
+ signing_key:
208
+ specification_version: 4
209
+ summary: Create Files Named Sequentially Intelligently
210
+ test_files:
211
+ - spec/lib/sequential_file/base_spec.rb
212
+ - spec/lib/sequential_file/counter_finder_spec.rb
213
+ - spec/lib/sequential_file/namer_spec.rb
214
+ - spec/scratch/.gitkeep
215
+ - spec/shared_examples_for_files.rb
216
+ - spec/spec.opts
217
+ - spec/spec_helper.rb
218
+ - spec/test_data/asdf.20140214.qwer.42.json