trick_bag 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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