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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/REEK +2 -0
- data/ROODI +5 -0
- data/Rakefile +33 -0
- data/lib/sequential_file.rb +10 -0
- data/lib/sequential_file/base.rb +109 -0
- data/lib/sequential_file/counter_finder.rb +30 -0
- data/lib/sequential_file/namer.rb +107 -0
- data/lib/sequential_file/version.rb +3 -0
- data/sequential_file.gemspec +63 -0
- data/spec/lib/sequential_file/base_spec.rb +60 -0
- data/spec/lib/sequential_file/counter_finder_spec.rb +37 -0
- data/spec/lib/sequential_file/namer_spec.rb +109 -0
- data/spec/scratch/.gitkeep +0 -0
- data/spec/shared_examples_for_files.rb +44 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/test_data/asdf.20140214.qwer.42.json +0 -0
- metadata +218 -0
checksums.yaml
ADDED
@@ -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
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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 | [](http://stillmaintained.com/pboling/sequential_file) |
|
10
|
+
| version | [](http://badge.fury.io/rb/sequential_file) |
|
11
|
+
| dependencies | [](https://gemnasium.com/pboling/sequential_file) |
|
12
|
+
| code quality | [](https://codeclimate.com/github/pboling/sequential_file) |
|
13
|
+
| continuous integration | [](https://travis-ci.org/pboling/sequential_file) |
|
14
|
+
| test coverage | [](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 ~♡ⓛⓞⓥⓔ♡~ | [](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
|
+
[](https://bitdeli.com/free "Bitdeli Badge")
|
data/ROODI
ADDED
data/Rakefile
ADDED
@@ -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,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
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -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]
|
File without changes
|
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
|