trick_bag 0.33.0 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
data/RELEASE_NOTES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## v0.34.0
2
+
3
+ * Added FileLineReader, StartMax. Added strategies to dos2unix.
4
+
5
+
1
6
  ## v0.33.0
2
7
 
3
8
  * Added dos2unix and dos2unix! to Formatters.
@@ -0,0 +1,91 @@
1
+ require 'trick_bag/numeric/start_and_max'
2
+
3
+ module TrickBag
4
+ module Enumerables
5
+
6
+ # Reads a file containing strings, one on each line, and feeds them
7
+ # in its each() method. Strips all lines leading and trailing whitespace,
8
+ # and ignores all empty lines and lines beginning with the comment character '#'.
9
+ #
10
+ # If calling each without a block to get an enumerator, call enumerator.close when done
11
+ # if not all values have been exhausted, so that the input file will be closed.
12
+ class FileLineReader
13
+
14
+ include Enumerable
15
+
16
+ attr_reader :filespec, :start_and_max
17
+
18
+ # @param filespec - the file from which to read domain names; blank/empty lines and lines
19
+ # with the first nonblank character == '#' will be ignored.
20
+ # @param start_pos - the record number, zero offset, at which to begin each() processing
21
+ # @param max_count - the maximum number of records to be served by each()
22
+ def initialize(filespec, start_pos = :first, max_count = :infinite)
23
+ @filespec = filespec
24
+ @start_and_max = TrickBag::Numeric::StartAndMax.new(start_pos, max_count)
25
+ end
26
+
27
+
28
+ def line_valid?(line)
29
+ ! (line.empty? || /^#/ === line)
30
+ end
31
+
32
+
33
+ def close_file(file)
34
+ if file && (! file.closed?)
35
+ file.close
36
+ end
37
+ end
38
+
39
+
40
+ # Any enum returned should have a close method to close the file from which lines are read.
41
+ # Get the enumerator from the superclass, and add a close method to it.
42
+ def to_enum(file)
43
+ enumerator = super()
44
+ enumerator.instance_variable_set(:@file, file)
45
+
46
+ def enumerator.close
47
+ if @file && (! @file.closed?)
48
+ @file.close
49
+ @file = nil
50
+ end
51
+ end
52
+
53
+ enumerator
54
+ end
55
+
56
+
57
+ # Returns an Enumerator if called without a block; in that case, remember
58
+ # to close the file explicitly by calling the enumerator's close method
59
+ # if you finish using it before all values have been exhausted.
60
+ def each
61
+
62
+ file = File.open(filespec, 'r')
63
+ return to_enum(file) unless block_given?
64
+
65
+ valid_record_num = 0
66
+ yield_count = 0
67
+
68
+ begin
69
+
70
+ file.each do |line|
71
+ line.strip!
72
+ if line_valid?(line)
73
+ if start_and_max.start_position_reached?(valid_record_num)
74
+ yield(line)
75
+ yield_count += 1
76
+ if start_and_max.max_count_reached?(yield_count)
77
+ return
78
+ end
79
+ end
80
+
81
+ valid_record_num += 1
82
+ end
83
+ end
84
+
85
+ ensure
86
+ close_file(file)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -65,18 +65,45 @@ module Formatters
65
65
  end
66
66
 
67
67
 
68
- # Like the Unix dos2unix command, but on strings rather than files, strips CR characters.
68
+ # Like the Unix dos2unix command, but on strings rather than files,
69
+ # strips CR ("\r", 13, 0xD) characters.
70
+ #
71
+ # WARNING:
72
+ #
73
+ # Currently, two strategies are supported, but they do not account for
74
+ # character sets that might include characters that have "\r"'s numeric value,
75
+ # 13, or 0xd, as part of their legitimate values, so we may need to
76
+ # add strategies to accommodate this.
77
+ #
78
+ # An example of a more complex implementation is at:
79
+ # http://dos2unix.sourcearchive.com/documentation/5.0-1/dos2unix_8c-source.html.
80
+ #
69
81
  # Note: The 'os' gem can be used to determine os.
70
- def dos2unix(string)
71
- string ? string.gsub("\r", '') : string
82
+ #
83
+ # @param string the string to convert
84
+ # @param strategy the strategy to use for the conversion (note: the default
85
+ # may change over time, so if you're sure you want to use the current
86
+ # default even if the default changes, don't rely on the default; specify it)
87
+ def dos2unix(string, strategy = :remove_all_cr)
88
+ strategies = {
89
+ remove_all_cr: -> { string.gsub("\r", '') },
90
+ remove_cr_in_crlf: -> { string.gsub("\r\n", "\n") }
91
+ }
92
+
93
+ unless strategies.keys.include?(strategy)
94
+ raise "Unsupported strategy: #{strategy}. Must be one of [#{strategies.keys.sort.join(', ')}]."
95
+ end
96
+
97
+ strategies[strategy].()
72
98
  end
73
99
 
74
100
 
75
101
  # Like the Unix dos2unix command, but on strings rather than files, strips CR characters.
76
102
  # Modifies the original string.
103
+ # See warning in dos2unix header.
77
104
  # Note: The 'os' gem can be used to determine os.
78
- def dos2unix!(string)
79
- string ? string.gsub!("\r", '') : string
105
+ def dos2unix!(string, strategy = :remove_all_cr)
106
+ string.replace(dos2unix(string, strategy))
80
107
  end
81
108
 
82
109
  end
@@ -0,0 +1,35 @@
1
+ module TrickBag
2
+ module Numeric
3
+
4
+ # Like a Range, but includes useful functions with understandable names
5
+ # that account for the absence of limits.
6
+ class StartAndMax
7
+
8
+ attr_reader :start_pos, :max_count
9
+
10
+ # @param start_pos - the record number, zero offset, at which to begin each() processing; or :first
11
+ # @param max_count - the maximum number of records to be served by each(); or :infinite
12
+ def initialize(start_pos = :first, max_count = :infinite)
13
+ @start_pos = ([nil, :first].include?(start_pos) || start_pos <= 0) ? :first : start_pos
14
+ @max_count = ([nil, :infinite].include?(max_count) || max_count <= 0) ? :infinite : max_count
15
+ end
16
+
17
+
18
+ def start_position_reached?(num)
19
+ @start_pos == :first || num >= @start_pos
20
+ end
21
+
22
+
23
+ def max_count_reached?(count)
24
+ @max_count != :infinite && (count >= @max_count)
25
+ end
26
+
27
+
28
+ def to_s
29
+ "#{self.class}: start position=#{start_pos}, max count=#{max_count}"
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+
@@ -1,3 +1,3 @@
1
1
  module TrickBag
2
- VERSION = "0.33.0"
2
+ VERSION = "0.34.0"
3
3
  end
@@ -0,0 +1,120 @@
1
+ require 'os'
2
+ require 'tempfile'
3
+
4
+ require_relative '../../spec_helper'
5
+ require 'trick_bag/enumerables/file_line_reader'
6
+
7
+ module TrickBag
8
+ module Enumerables
9
+
10
+ describe FileLineReader do
11
+
12
+ TEST_DOMAINS = %w(abc.com cbs.com nbc.com)
13
+
14
+ DOMAIN_FILE_CONTENT = \
15
+ "# Comment text followed by a blank line
16
+
17
+ abc.com
18
+ cbs.com
19
+
20
+ # Another comment, but this time with whitespace before it
21
+ nbc.com
22
+ "
23
+
24
+ before(:each) do
25
+ @tempfile = Tempfile.new('file_domain_reader_spec')
26
+ @tempfile.write DOMAIN_FILE_CONTENT
27
+ @tempfile.close
28
+ end
29
+
30
+ after(:each) do
31
+ @tempfile.unlink
32
+ end
33
+
34
+ let(:can_run_lsof?) { OS.posix? && system('which lsof > /dev/null') }
35
+
36
+ subject { FileLineReader.new(@tempfile.path) }
37
+
38
+
39
+ it "should create a temporary file" do
40
+ expect(File.exist?(@tempfile.path)).to be_true
41
+ end
42
+
43
+ it "should produce an array" do
44
+ expect(subject.to_a).to be_a(Array)
45
+ end
46
+
47
+ it "should produce a nonempty array" do
48
+ expect(subject.to_a.empty?).to be_false
49
+ end
50
+
51
+ it "should produce a nonempty array containing 'abc.com'" do
52
+ expect(subject.to_a.include?('abc.com')).to be_true
53
+ end
54
+
55
+ it "should produce an array without blank lines" do
56
+ expect(subject.to_a.include?('')).to be_false
57
+ end
58
+
59
+ it "should produce an array without beginning comment characters" do
60
+ expect(subject.to_a.detect { |s| /^#/ === s }).to be_nil
61
+ end
62
+
63
+ it "should produce an array containing TEST_DOMAINS" do
64
+ expect(subject.to_a).to eq(TEST_DOMAINS)
65
+ end
66
+
67
+ it "should have a working 'each' function" do
68
+ array = []
69
+ subject.each { |name| array << name}
70
+ expect(array).to eq(TEST_DOMAINS)
71
+ end
72
+
73
+ it "should respect a start_pos" do
74
+ p = FileLineReader.new(@tempfile.path, 1)
75
+ expect(p.to_a).to eq(TEST_DOMAINS[1..-1])
76
+ end
77
+
78
+ it "should respect a max_count" do
79
+ p = FileLineReader.new(@tempfile.path, 0, 2)
80
+ expect(p.to_a).to eq(TEST_DOMAINS[0..1])
81
+ end
82
+
83
+ it 'should be usable as an Enumerable' do
84
+ e = subject.each
85
+ expect(e).to be_an(Enumerable)
86
+ expect(->{ 3.times { e.next} }).not_to raise_error
87
+ expect(->{ e.next }).to raise_error(StopIteration)
88
+ e.close
89
+ end
90
+
91
+ # If this test fails, it's probably the test before it that
92
+ # failed to close its file.
93
+ it 'closes the file even when its end is not reached' do
94
+
95
+ unless can_run_lsof?
96
+ pending "This test can only be run on a Posix-based OS having the 'lsof' command."
97
+ end
98
+
99
+ get_lsof_lines = -> do
100
+ output = `lsof -p #{Process.pid} | grep file_domain_reader_spec`
101
+ output.split("\n")
102
+ end
103
+
104
+ reader = FileLineReader.new(@tempfile.path, 0, 1)
105
+ reader.each do |line|
106
+ expect(get_lsof_lines.().size).to eq(1)
107
+ end
108
+ expect(get_lsof_lines.().size).to eq(0)
109
+ end
110
+
111
+
112
+ specify "calling the enumerator's close method multiple times will not raise an error" do
113
+ enumerator = subject.each
114
+ expect(-> { 3.times { enumerator.close } }).not_to raise_error
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+
@@ -86,20 +86,41 @@ describe Formatters do
86
86
  specify "CR characters are stripped" do
87
87
  expect(Formatters.dos2unix("foo\r\nbar\r\n")).to eq("foo\nbar\n")
88
88
  end
89
+
90
+ specify "a bad strategy will result in a raised error" do
91
+ expect(->() { Formatters.dos2unix('', :not_a_strategy) }).to raise_error
92
+ end
89
93
  end
90
94
 
91
95
  context ".dos2unix!" do
92
96
 
93
- specify "strings not containing line endings remain unchanged" do
94
- expect(Formatters.dos2unix('')).to eq('')
95
- expect(Formatters.dos2unix(' ')).to eq(' ')
96
- expect(Formatters.dos2unix('abc')).to eq('abc')
97
+ context ':remove_all_cr' do
98
+ specify "strings not containing line endings remain unchanged" do
99
+ expect(Formatters.dos2unix('', :remove_all_cr)).to eq('')
100
+ expect(Formatters.dos2unix(' ', :remove_all_cr)).to eq(' ')
101
+ expect(Formatters.dos2unix('abc', :remove_all_cr)).to eq('abc')
102
+ end
103
+
104
+ specify "CR characters are stripped" do
105
+ s = "foo\r\nbar\r\n"
106
+ Formatters.dos2unix!(s, :remove_all_cr)
107
+ expect(s).to eq("foo\nbar\n")
108
+ end
97
109
  end
98
110
 
99
- specify "CR characters are stripped" do
100
- s = "foo\r\nbar\r\n"
101
- Formatters.dos2unix!(s)
102
- expect(s).to eq("foo\nbar\n")
111
+ context 'remove_cr_in_crlf' do
112
+
113
+ specify "strings not containing line endings remain unchanged" do
114
+ expect(Formatters.dos2unix('', :remove_cr_in_crlf)).to eq('')
115
+ expect(Formatters.dos2unix(' ', :remove_cr_in_crlf)).to eq(' ')
116
+ expect(Formatters.dos2unix('abc', :remove_cr_in_crlf)).to eq('abc')
117
+ end
118
+
119
+ specify "cr is replaced only in crlf" do
120
+ s = "\rfoo\r\nbar\r\n\r"
121
+ expect(Formatters.dos2unix(s, :remove_cr_in_crlf)).to eq("\rfoo\nbar\n\r")
122
+
123
+ end
103
124
  end
104
125
  end
105
126
 
@@ -0,0 +1,50 @@
1
+ require 'trick_bag/numeric/start_and_max'
2
+
3
+ module TrickBag
4
+ module Numeric
5
+
6
+
7
+ describe StartAndMax do
8
+
9
+ let(:n) { 111 } # arbitrary number
10
+
11
+ context '#start_position_reached?' do
12
+ it 'considers first element to be included when no start position and max are specified' do
13
+ expect(StartAndMax.new.start_position_reached?(0)).to be_true
14
+ end
15
+
16
+ it 'considers first element to be included when start position == n' do
17
+ expect(StartAndMax.new(n).start_position_reached?(n)).to be_true
18
+ end
19
+
20
+ it 'considers first element to be included when start position < n' do
21
+ expect(StartAndMax.new(n).start_position_reached?(n + 1)).to be_true
22
+ end
23
+
24
+ it 'considers first element NOT to be included when start position is specified but n < start' do
25
+ expect(StartAndMax.new(n).start_position_reached?(n - 1)).to be_false
26
+ end
27
+ end
28
+
29
+ context '#max_count_reached?' do
30
+ it 'returns false for a very large number when max is not specified' do
31
+ expect(StartAndMax.new.max_count_reached?(10 ** 100)).to be_false
32
+ end
33
+
34
+ it 'returns true when when n == max' do
35
+ expect(StartAndMax.new(0, n).max_count_reached?(n)).to be_true
36
+ end
37
+
38
+ it 'returns false when when n < max' do
39
+ expect(StartAndMax.new(0, n).max_count_reached?(n - 1)).to be_false
40
+ end
41
+
42
+ it 'returns true when when n > max' do
43
+ expect(StartAndMax.new(0, n).max_count_reached?(n + 1)).to be_true
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ end
50
+ end
data/trick_bag.gemspec CHANGED
@@ -18,9 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'vrsn-ie-dnsruby'
22
-
23
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "os"
24
23
  spec.add_development_dependency "rake"
25
24
  spec.add_development_dependency "rspec"
26
25
  spec.add_development_dependency "guard"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trick_bag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.33.0
4
+ version: 0.34.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,40 +9,40 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-17 00:00:00.000000000 Z
12
+ date: 2014-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: vrsn-ie-dnsruby
15
+ name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
21
+ version: '1.3'
22
+ type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ! '>='
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: '1.3'
30
30
  - !ruby/object:Gem::Dependency
31
- name: bundler
31
+ name: os
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ~>
35
+ - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
- version: '1.3'
37
+ version: '0'
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ~>
43
+ - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
- version: '1.3'
45
+ version: '0'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: rake
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -126,12 +126,14 @@ files:
126
126
  - lib/trick_bag/enumerables/buffered_enumerable.rb
127
127
  - lib/trick_bag/enumerables/compound_enumerable.rb
128
128
  - lib/trick_bag/enumerables/endless_last_enumerable.rb
129
+ - lib/trick_bag/enumerables/file_line_reader.rb
129
130
  - lib/trick_bag/enumerables/filtered_enumerable.rb
130
131
  - lib/trick_bag/formatters/formatters.rb
131
132
  - lib/trick_bag/io/temp_files.rb
132
133
  - lib/trick_bag/io/text_mode_status_updater.rb
133
134
  - lib/trick_bag/meta/classes.rb
134
135
  - lib/trick_bag/numeric/multi_counter.rb
136
+ - lib/trick_bag/numeric/start_and_max.rb
135
137
  - lib/trick_bag/numeric/totals.rb
136
138
  - lib/trick_bag/operators/operators.rb
137
139
  - lib/trick_bag/timing/timing.rb
@@ -143,12 +145,14 @@ files:
143
145
  - spec/trick_bag/enumerables/buffered_enumerable_spec.rb
144
146
  - spec/trick_bag/enumerables/compound_enumerable_spec.rb
145
147
  - spec/trick_bag/enumerables/endless_last_enumerable_spec.rb
148
+ - spec/trick_bag/enumerables/file_line_reader_spec.rb
146
149
  - spec/trick_bag/enumerables/filtered_enumerable_spec.rb
147
150
  - spec/trick_bag/formatters/formatters_spec.rb
148
151
  - spec/trick_bag/io/temp_files_spec.rb
149
152
  - spec/trick_bag/io/text_mode_status_updater_spec.rb
150
153
  - spec/trick_bag/meta/classes_spec.rb
151
154
  - spec/trick_bag/numeric/multi_counter_spec.rb
155
+ - spec/trick_bag/numeric/start_and_max_spec.rb
152
156
  - spec/trick_bag/numeric/totals_spec.rb
153
157
  - spec/trick_bag/operators/operators_spec.rb
154
158
  - spec/trick_bag/timing/timing_spec.rb
@@ -186,12 +190,14 @@ test_files:
186
190
  - spec/trick_bag/enumerables/buffered_enumerable_spec.rb
187
191
  - spec/trick_bag/enumerables/compound_enumerable_spec.rb
188
192
  - spec/trick_bag/enumerables/endless_last_enumerable_spec.rb
193
+ - spec/trick_bag/enumerables/file_line_reader_spec.rb
189
194
  - spec/trick_bag/enumerables/filtered_enumerable_spec.rb
190
195
  - spec/trick_bag/formatters/formatters_spec.rb
191
196
  - spec/trick_bag/io/temp_files_spec.rb
192
197
  - spec/trick_bag/io/text_mode_status_updater_spec.rb
193
198
  - spec/trick_bag/meta/classes_spec.rb
194
199
  - spec/trick_bag/numeric/multi_counter_spec.rb
200
+ - spec/trick_bag/numeric/start_and_max_spec.rb
195
201
  - spec/trick_bag/numeric/totals_spec.rb
196
202
  - spec/trick_bag/operators/operators_spec.rb
197
203
  - spec/trick_bag/timing/timing_spec.rb