fbo 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 038791a71715415069e8fef85ee55570b4c604a7
4
- data.tar.gz: ec28f0ae4378dd0d7066bada202ce433d8f5ef32
3
+ metadata.gz: cb42052147996e0d44fdbaee04e245b538affecb
4
+ data.tar.gz: 4fb9642b21ee11a0840110cd21ed1488ba779662
5
5
  SHA512:
6
- metadata.gz: 29f5094d45d2a9014447e79ffd852da117b4040005713420ec186e2adf1491223e3b910f7f0e5c577e8d4b1c431d960b59783646ff9678dd92746c3b394e9268
7
- data.tar.gz: 4499d2b12c10327761a83c834db340aa515b44bfdb822f1e0b423596fa1a166cd6a8f401600f5fc2d5934cdec9ecf9edafd0b9cbb3ec4345ddc78f30afd2cd12
6
+ metadata.gz: 5cdded369892b66f85d27ee09eacff5dcbca57f96508eed2d119b01d93e7c66b1f4ed86c8185dadbf4797ab211d1b59c8dad59040343b88db4ec7fd515a7b71f
7
+ data.tar.gz: 0c496f7dc170fcb6bd98eef097fcc74a422049c6ee10ab3e3b0950881bff49f31dd900b77a4f437d025795bcd2eca6b0067d427e218e95ab227d9fa77af9a050
@@ -1,11 +1,11 @@
1
1
  require 'forwardable'
2
2
 
3
3
  module FBO
4
- class ChunkedFile
4
+ class ChunkedFile < File
5
5
  extend Forwardable
6
6
 
7
7
  attr_reader :file, :chunk_size
8
- def_delegators :@file, :readline, :read, :eof?
8
+ def_delegators :@file, :eof?, :eof
9
9
 
10
10
 
11
11
  DEFAULT_CHUNK_SIZE = 250 * 1024 # 250KB
@@ -19,7 +19,7 @@ module FBO
19
19
  def contents
20
20
  if @contents.nil?
21
21
  @contents = []
22
- while !eof?
22
+ while !eof
23
23
  @contents << next_chunk
24
24
  end
25
25
  @contents.compact!
@@ -31,7 +31,7 @@ module FBO
31
31
  private
32
32
 
33
33
  def next_chunk
34
- return nil if eof?
34
+ return nil if eof
35
35
 
36
36
  chunk, line = "", ""
37
37
 
@@ -40,7 +40,7 @@ module FBO
40
40
  line = gets
41
41
  break unless line
42
42
  chunk += line
43
- end while (chunk.bytesize < @chunk_size)
43
+ end while (chunk.bytesize < @chunk_size && !eof)
44
44
 
45
45
  # Add lines up to the end of a notice.
46
46
  if line && line !~ FBO::NOTICE_CLOSE_REGEXP
@@ -49,23 +49,10 @@ module FBO
49
49
  break unless line
50
50
  chunk += line
51
51
  break if line =~ FBO::NOTICE_CLOSE_REGEXP
52
- end while (true)
52
+ end while (!eof)
53
53
  end
54
54
 
55
55
  return chunk.strip
56
56
  end
57
-
58
- def gets
59
- line = file.gets
60
- return unless line
61
- cleanup_data(line)
62
- end
63
-
64
- def cleanup_data(data)
65
- data.encode('UTF-16le', :invalid => :replace, :replace => '')
66
- .encode('UTF-8')
67
- .gsub(/\r\n/, "\n")
68
- .gsub(/^M/, "")
69
- end
70
57
  end
71
58
  end
@@ -5,7 +5,7 @@ module FBO
5
5
  extend Forwardable
6
6
 
7
7
  attr_reader :file
8
- def_delegators :@file, :readline, :read, :eof?, :gets
8
+ def_delegators :@file, :eof?, :eof
9
9
 
10
10
  class << self
11
11
  def filename_for_date(date)
@@ -18,6 +18,11 @@ module FBO
18
18
  @file = ::File.new(filename)
19
19
  end
20
20
 
21
+ def gets
22
+ str = @file.gets
23
+ str.nil? ? nil : cleanup_data(str)
24
+ end
25
+
21
26
  def contents
22
27
  if @contents.nil?
23
28
  @contents = cleanup_data(@file.read)
@@ -25,7 +30,7 @@ module FBO
25
30
  @contents
26
31
  end
27
32
 
28
- private
33
+ protected
29
34
 
30
35
  def cleanup_data(data)
31
36
  data.encode('UTF-16le', :invalid => :replace, :replace => '')
@@ -1,76 +1,47 @@
1
1
  module FBO
2
2
  class Interpreter
3
- class << self
4
- def notice_enumeration(name, type)
5
- define_method(name) do |&block|
6
- selected = notice_nodes(type)
7
- return unless selected
8
- selected.each do |node|
9
- block.call node.to_hash.merge({ type: node.type })
10
- end
11
- end
12
- end
13
- end
14
-
15
- notice_enumeration :each_notice, :all
16
- notice_enumeration :each_presolicitation, :presol
17
- notice_enumeration :each_combined_solicitation, :combine
18
- notice_enumeration :each_amendment, :amdcss
19
- notice_enumeration :each_modification, :mod
20
- notice_enumeration :each_award, :award
21
- notice_enumeration :each_justification_and_approval, :ja
22
- notice_enumeration :each_intent_to_bundle, :itb
23
- notice_enumeration :each_fair_opportunity, :fairopp
24
- notice_enumeration :each_sources_sought, :srcsgt
25
- notice_enumeration :each_foreign_standard, :fstd
26
- notice_enumeration :each_special_notice, :snote
27
- notice_enumeration :each_sale_of_surplus, :ssale
28
- notice_enumeration :each_document_upload, :epsupload
29
- notice_enumeration :each_document_delete, :delete
30
- notice_enumeration :each_document_archival, :archive
31
- notice_enumeration :each_document_unarchival, :unarchive
32
-
33
3
  def initialize(file)
34
4
  @file = file
5
+ @parser = Parser.new
35
6
  end
36
7
 
37
- private
8
+ # Walk through the file returning each notice text block and structure.
9
+ # If a block is given, yield passing the text block and structure as args.
10
+ #
11
+ def next_notice(&block)
12
+ return unless notice_text = next_notice_string
13
+ return if notice_text.empty?
38
14
 
39
- def notice_nodes(type = :all)
40
- if @notice_nodes.nil?
41
- @notice_nodes = {}
42
- end
43
-
44
- if @file.is_a?(FBO::SegmentedFile) && type != :all
45
- @notice_nodes[type] = parse_segment(type)
46
- else
47
- @notice_nodes[:all] = parse_file
48
- if type == :all
49
- @notice_nodes[:all]
50
- else
51
- @notice_nodes[type] = @notice_nodes[:all].select { |n| n.type == type }
52
- end
53
- end
15
+ notice_node = parse_notice(notice_text)
16
+ yield notice_text, notice_node if block_given?
17
+ return notice_text, notice_node
54
18
  end
55
19
 
56
- def parse_file
57
- tree = FBO::Parser.new(@file).parse
58
- tree.elements
59
- end
60
20
 
61
- def parse_segment(type = :all)
62
- return unless @file.is_a? FBO::SegmentedFile
21
+ private
63
22
 
64
- content = ""
65
- if type == :all
66
- content = @file.contents
67
- else
68
- content = @file.contents_for_type(type)
23
+ # Get the next whole notice from the FBO dump file.
24
+ # Return an empty string if no more exist
25
+ #
26
+ def next_notice_string
27
+ return if @file.eof?
28
+
29
+ line, entry_string = '', ''
30
+ while !@file.eof
31
+ line = @file.gets
32
+ break unless line
33
+ next unless line =~ /\S/
34
+ entry_string += line
35
+ break if line =~ FBO::NOTICE_CLOSE_REGEXP
69
36
  end
70
- return unless content
37
+ return entry_string.strip
38
+ end
71
39
 
72
- tree = FBO::Parser.new.parse(content)
73
- tree.elements
40
+ # Parse the text to extract a structure of data for the notice.
41
+ #
42
+ def parse_notice(text)
43
+ tree = @parser.parse(text)
44
+ tree.elements.first
74
45
  end
75
46
  end
76
47
  end
@@ -1,11 +1,11 @@
1
1
  require 'forwardable'
2
2
 
3
3
  module FBO
4
- class SegmentedFile
4
+ class SegmentedFile < File
5
5
  extend Forwardable
6
6
 
7
7
  attr_reader :file
8
- def_delegators :@file, :readline, :read, :eof?
8
+ def_delegators :@file, :eof?, :eof
9
9
 
10
10
 
11
11
  def initialize(file)
@@ -1,3 +1,3 @@
1
1
  module FBO
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FBO::ChunkedFile do
4
- let(:filename) { File.join(File.dirname(__FILE__), '..', 'fixtures', 'FBOFeed20131003') }
4
+ let(:filename) { File.join(File.dirname(__FILE__), '..', 'fixtures', 'FBOFeed20131013') }
5
5
  let(:file) { FBO::File.new(filename) }
6
- let(:chunk_size) { 50 * 1024 } # 50KB
6
+ let(:chunk_size) { 25 * 1024 } # 25KB
7
7
  subject { FBO::ChunkedFile.new(file, chunk_size) }
8
8
 
9
9
  describe '#contents' do
@@ -3,102 +3,22 @@ require 'spec_helper'
3
3
  describe FBO::Interpreter do
4
4
  let(:filename) { File.join(File.dirname(__FILE__), '..', 'fixtures', 'FBOFeed20130331') }
5
5
  let(:file) { FBO::File.new(filename) }
6
- let(:actor) { stub('Act on ALL the Notices', process: nil) }
6
+ let(:actor) { MiniTest::Mock.new }
7
+
7
8
 
8
9
  context 'with input FBO::File' do
9
10
  subject { FBO::Interpreter.new(file) }
10
11
 
11
- describe '#each_notice' do
12
- it 'acts on each notice' do
13
- actor.expects(:process).with(instance_of(Hash)).times(8)
14
- subject.each_notice { |n| actor.process(n) }
15
- end
16
- end
17
-
18
- describe '#each_presolicitation' do
19
- it 'acts on each PRESOL notice' do
20
- actor.expects(:process).with(has_entry(solicitation_number: 'SPE4A713R0795'))
21
- subject.each_presolicitation { |n| actor.process(n) }
22
- end
23
- end
24
-
25
- describe '#each_sources_sought' do
26
- it 'acts on each SRCSGT notice' do
27
- actor.expects(:process).with(has_entry(solicitation_number: 'W5J9JE-13-S-0002'))
28
- subject.each_sources_sought { |n| actor.process(n) }
29
- end
30
- end
31
-
32
- describe '#each_special_notice' do
33
- it 'does nothing (no SNOTE notices)' do
34
- actor.expects(:process).never
35
- subject.each_special_notice { |n| actor.process(n) }
36
- end
37
- end
38
- end
39
-
40
- context 'with input FBO::ChunkedFile' do
41
- let(:chunked) { FBO::ChunkedFile.new(file) }
42
- subject { FBO::Interpreter.new(chunked) }
43
-
44
- describe '#each_notice' do
45
- it 'acts on each notice' do
46
- actor.expects(:process).with(instance_of(Hash)).times(8)
47
- subject.each_notice { |n| actor.process(n) }
48
- end
49
- end
50
-
51
- describe '#each_presolicitation' do
52
- it 'acts on each PRESOL notice' do
53
- actor.expects(:process).with(has_entry(solicitation_number: 'SPE4A713R0795'))
54
- subject.each_presolicitation { |n| actor.process(n) }
55
- end
56
- end
57
-
58
- describe '#each_sources_sought' do
59
- it 'acts on each SRCSGT notice' do
60
- actor.expects(:process).with(has_entry(solicitation_number: 'W5J9JE-13-S-0002'))
61
- subject.each_sources_sought { |n| actor.process(n) }
62
- end
63
- end
64
-
65
- describe '#each_special_notice' do
66
- it 'does nothing (no SNOTE notices)' do
67
- actor.expects(:process).never
68
- subject.each_special_notice { |n| actor.process(n) }
69
- end
70
- end
71
- end
72
-
73
- context 'with input FBO::SegmentedFile' do
74
- let(:segmented) { FBO::SegmentedFile.new(file) }
75
- subject { FBO::Interpreter.new(segmented) }
76
-
77
- describe '#each_notice' do
78
- it 'acts on each notice' do
79
- actor.expects(:process).with(instance_of(Hash)).times(8)
80
- subject.each_notice { |n| actor.process(n) }
81
- end
82
- end
83
-
84
- describe '#each_presolicitation' do
85
- it 'acts on each PRESOL notice' do
86
- actor.expects(:process).with(has_entry(solicitation_number: 'SPE4A713R0795'))
87
- subject.each_presolicitation { |n| actor.process(n) }
88
- end
89
- end
90
-
91
- describe '#each_sources_sought' do
92
- it 'acts on each SRCSGT notice' do
93
- actor.expects(:process).with(has_entry(solicitation_number: 'W5J9JE-13-S-0002'))
94
- subject.each_sources_sought { |n| actor.process(n) }
95
- end
96
- end
12
+ describe '#next_notice' do
13
+ it 'acts on the first notice' do
14
+ actor.expect(:process, nil, [String, Treetop::Runtime::SyntaxNode ])
15
+ string, structure = subject.next_notice { |str, struct| actor.process(str, struct) }
16
+ actor.verify
97
17
 
98
- describe '#each_special_notice' do
99
- it 'does nothing (no SNOTE notices)' do
100
- actor.expects(:process).never
101
- subject.each_special_notice { |n| actor.process(n) }
18
+ string.must_match /^\<PRESOL\>/
19
+ string.must_match /\<SOLNBR\>SPE4A713R0795/
20
+ structure.type.must_equal :presol
21
+ structure.to_hash[:solicitation_number].must_equal 'SPE4A713R0795'
102
22
  end
103
23
  end
104
24
  end
@@ -38,7 +38,7 @@ describe FBO::RemoteFile do
38
38
  end
39
39
 
40
40
  it 'should to open the file and read its contents' do
41
- subject.readline.wont_be_nil
41
+ subject.gets.wont_be_nil
42
42
  end
43
43
  end
44
44
 
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Kottom
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-02 00:00:00.000000000 Z
11
+ date: 2014-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: treetop
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: 4.7.5
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 4.7.5
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: turn
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  description: |2
@@ -107,7 +107,7 @@ executables: []
107
107
  extensions: []
108
108
  extra_rdoc_files: []
109
109
  files:
110
- - .gitignore
110
+ - ".gitignore"
111
111
  - Gemfile
112
112
  - LICENSE.txt
113
113
  - README.md
@@ -143,17 +143,17 @@ require_paths:
143
143
  - lib
144
144
  required_ruby_version: !ruby/object:Gem::Requirement
145
145
  requirements:
146
- - - '>='
146
+ - - ">="
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  required_rubygems_version: !ruby/object:Gem::Requirement
150
150
  requirements:
151
- - - '>='
151
+ - - ">="
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
154
  requirements: []
155
155
  rubyforge_project:
156
- rubygems_version: 2.0.3
156
+ rubygems_version: 2.2.2
157
157
  signing_key:
158
158
  specification_version: 4
159
159
  summary: Parse and process FBO bulk listings