rightmove_blm 0.1.1 → 0.2.0

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
  SHA256:
3
- metadata.gz: '08bd05a65c0404a94ac0923c3ac48b338d7714b6f0b53dd6b879607e501adced'
4
- data.tar.gz: 294e256eb0bc617199816ef32c4ee5f92c785da180ef88e4fcd816e2eb59e86d
3
+ metadata.gz: a4ec5f3eee9950c331f0a1dd23129b47d6917c332fae762df37346f67c3ae15b
4
+ data.tar.gz: e803eb23307b33115103e2cd99c94398d272576db5206670eff24c64f13f72af
5
5
  SHA512:
6
- metadata.gz: 351167830c63ca9037d91300d1081d6afa8d665c5fc3bcdabbbc5f7e4304784ad5738c7d9111878b33a8c67cb8a2a56c1d715bf8db9654ef7d8413c6ee805753
7
- data.tar.gz: 9ecc44105fbebec4e3f07e6596f9ad0336368f1e7cac604c4d728a32232905d16f0740153b298976c2e2101ccb6cdd3455d1aa8017d586a25093823a16224b1a
6
+ metadata.gz: 784c8aad75336281c6e7bc59a0a16866929f62268542d0ebf93c5bccb5a21b1e651a91b1edeca885d571ab18bdd35289afd27937316ff8a5c473d44ad612c760
7
+ data.tar.gz: 9f8dc40c0e294360d4ca3b7b713da7922b4c9a90795517f98fcd71d13b9ba22e21435aa5f984618d3d30aabb91a6ffa0fad7640d248339a2b59c1c20453e4f96
data/.gitignore CHANGED
@@ -15,6 +15,8 @@ doc
15
15
  pkg
16
16
 
17
17
  .rspec_status
18
+
19
+ *.gem
18
20
  # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
21
  #
20
22
  # * Create a file at ~/.gitignore
data/.rubocop.yml CHANGED
@@ -1,6 +1,9 @@
1
+ require:
2
+ - rubocop-rspec
3
+
1
4
  AllCops:
2
5
  NewCops: enable
3
6
 
4
7
  Metrics/BlockLength:
5
8
  Exclude:
6
- - 'spec/blm_spec.rb'
9
+ - 'spec/**/*_spec.rb'
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5.9
1
+ 2.7.7
data/Gemfile.lock CHANGED
@@ -3,16 +3,17 @@ GEM
3
3
  specs:
4
4
  ast (2.4.2)
5
5
  concurrent-ruby (1.1.8)
6
- devpack (0.3.2)
6
+ devpack (0.3.3)
7
7
  diff-lcs (1.4.4)
8
8
  i18n (1.8.10)
9
9
  concurrent-ruby (~> 1.0)
10
+ json (2.6.3)
10
11
  paint (2.2.1)
11
- parallel (1.20.1)
12
- parser (3.0.1.0)
12
+ parallel (1.22.1)
13
+ parser (3.1.3.0)
13
14
  ast (~> 2.4.1)
14
- rainbow (3.0.0)
15
- regexp_parser (2.1.1)
15
+ rainbow (3.1.1)
16
+ regexp_parser (2.6.1)
16
17
  rexml (3.2.5)
17
18
  rspec (3.10.0)
18
19
  rspec-core (~> 3.10.0)
@@ -32,17 +33,18 @@ GEM
32
33
  diff-lcs (>= 1.2.0, < 2.0)
33
34
  rspec-support (~> 3.10.0)
34
35
  rspec-support (3.10.2)
35
- rubocop (1.13.0)
36
+ rubocop (1.41.1)
37
+ json (~> 2.3)
36
38
  parallel (~> 1.10)
37
- parser (>= 3.0.0.0)
39
+ parser (>= 3.1.2.1)
38
40
  rainbow (>= 2.2.2, < 4.0)
39
41
  regexp_parser (>= 1.8, < 3.0)
40
- rexml
41
- rubocop-ast (>= 1.2.0, < 2.0)
42
+ rexml (>= 3.2.5, < 4.0)
43
+ rubocop-ast (>= 1.23.0, < 2.0)
42
44
  ruby-progressbar (~> 1.7)
43
45
  unicode-display_width (>= 1.4.0, < 3.0)
44
- rubocop-ast (1.4.1)
45
- parser (>= 2.7.1.5)
46
+ rubocop-ast (1.24.0)
47
+ parser (>= 3.1.1.0)
46
48
  rubocop-rspec (2.2.0)
47
49
  rubocop (~> 1.0)
48
50
  rubocop-ast (>= 1.1.0)
@@ -50,7 +52,7 @@ GEM
50
52
  strong_versions (0.4.5)
51
53
  i18n (>= 0.5)
52
54
  paint (~> 2.0)
53
- unicode-display_width (2.0.0)
55
+ unicode-display_width (2.3.0)
54
56
 
55
57
  PLATFORMS
56
58
  ruby
@@ -65,4 +67,4 @@ DEPENDENCIES
65
67
  strong_versions (~> 0.4.5)
66
68
 
67
69
  BUNDLED WITH
68
- 2.2.16
70
+ 2.3.7
data/README.md CHANGED
@@ -10,6 +10,8 @@ This library is not affiliated with or endorsed by _Rightmove Plc_ in any way.
10
10
 
11
11
  ### Loading a BLM file
12
12
 
13
+ #### RightmoveBLM::Document
14
+
13
15
  Load a BLM file by passing the `source` parameter to `RightmoveBLM::Document.new`:
14
16
 
15
17
  ```ruby
@@ -20,17 +22,32 @@ The returned `RightmoveBLM::Document` instance implements:
20
22
 
21
23
  * `#header` - the header containing information about the document's structure.
22
24
  * `#definition` - the field list contained in the document.
23
- * `#data` - an array of `RightmoveBLM::Row` objects (use dot notation to access fields, e.g. `row.foo` to access the "foo" field).
25
+ * `#rows` - an array of `RightmoveBLM::Row` objects.
26
+ * `#valid?` - `true` if no rows have errors, `false` if any rows have errors.
27
+ * `#errors` - all error messages for all invalid rows.
28
+ * `#version` - the version of the document format as a string (e.g. `'3'`).
29
+ * `#international?` - `true` if document meets the [Rightmove International](https://www.rightmove.co.uk/ps/pdf/guides/RightmoveDatafeedFormatV3iOVS_1.6.pdf) specification, `false` otherwise.
30
+
31
+ #### RightmoveBLM::Row
32
+
33
+ `RightmoveBLM::Row` implements:
24
34
 
25
- `RightmoveBLM::Row` also implements `#to_h` which provides a _Hash_ of the row data.
35
+ * `#valid?` - `true` if no errors were encountered, false otherwise.
36
+ * `#errors` - an array of error strings (empty if no errors present).
37
+ * `#to_h` (or `#attributes`) - a hash of row attributes (`nil` if errors encountered).
38
+ * `#method_missing` - allows accessing row attributes by dot notation (e.g. `row.address_1`).
26
39
 
27
40
  #### Example
28
41
 
29
42
  ```ruby
30
- blm.data.each do |row|
43
+ blm.data.select(&:valid?).each do |row|
31
44
  puts row.address_1
32
45
  puts row.to_h
33
46
  end
47
+
48
+ blm.data.reject(&:valid?).each do |row|
49
+ puts "Errors: #{row.errors.join(', ')}"
50
+ end
34
51
  ```
35
52
 
36
53
  ### Writing BLM data
@@ -5,7 +5,7 @@ module RightmoveBLM
5
5
  class Document
6
6
  def self.from_array_of_hashes(array)
7
7
  date = Time.now.utc.strftime('%d-%b-%Y %H:%M').upcase
8
- header = { version: '3', eof: '^', eor: '~', "property count": array.size.to_s, "generated date": date }
8
+ header = { version: '3', eof: '^', eor: '~', 'property count': array.size.to_s, 'generated date': date }
9
9
  new(header: header, definition: array.first.keys.map(&:to_sym), data: array)
10
10
  end
11
11
 
@@ -13,7 +13,15 @@ module RightmoveBLM
13
13
  @source = source
14
14
  @header = header
15
15
  @definition = definition
16
- @data = data&.map { |row| Row.new(row) }
16
+ initialize_with_data(data) unless data.nil?
17
+ end
18
+
19
+ def inspect
20
+ %(<##{self.class.name} version=#{version} rows=#{rows.size} valid=#{valid?} errors=#{errors.size}>)
21
+ end
22
+
23
+ def to_s
24
+ inspect
17
25
  end
18
26
 
19
27
  def to_blm
@@ -43,27 +51,49 @@ module RightmoveBLM
43
51
  end.compact
44
52
  end
45
53
 
46
- def data
47
- @data ||= contents.split(header[:eor]).map do |line|
48
- row(line)
49
- end
54
+ def rows
55
+ data
56
+ end
57
+
58
+ def errors
59
+ @errors ||= data.reject(&:valid?).flat_map(&:errors)
60
+ end
61
+
62
+ def valid?
63
+ errors.empty?
64
+ end
65
+
66
+ def version
67
+ header[:version]
68
+ end
69
+
70
+ def international?
71
+ %w[H1 3I].include?(version)
50
72
  end
51
73
 
52
74
  private
53
75
 
76
+ def initialize_with_data(data)
77
+ @data = data.each_with_index.map { |hash, index| Row.from_attributes(hash, index: index) }
78
+ end
79
+
80
+ def data
81
+ @data ||= contents.split(header[:eor]).each_with_index.map do |line, index|
82
+ Row.new(index: index, data: line, separator: header[:eof], definition: definition)
83
+ end
84
+ end
85
+
54
86
  def contents(section = :data)
55
87
  marker = "##{section.to_s.upcase}#"
56
- start = @source.index(marker) + marker.size
57
- finish = @source.index('#', start) - 1
88
+ start = verify(:start, @source.index(marker)) + marker.size
89
+ finish = verify(:end, @source.index('#', start)) - 1
58
90
  @source[start..finish].strip
59
91
  end
60
92
 
61
- def row(line)
62
- entry = {}
63
- line.split(header[:eof]).each_with_index do |field, index|
64
- entry[definition[index].to_sym] = field.strip
65
- end
66
- Row.new(entry)
93
+ def verify(type, val)
94
+ return val unless val.nil?
95
+
96
+ raise ParserError, "Unable to parse document: could not detect #{type} marker."
67
97
  end
68
98
 
69
99
  def generated_date
@@ -3,22 +3,72 @@
3
3
  module RightmoveBLM
4
4
  # A row in a BLM document.
5
5
  class Row
6
- attr_accessor :attributes
6
+ attr_reader :index
7
7
 
8
- def initialize(hash)
9
- @attributes = hash
8
+ def self.from_attributes(attributes, index:)
9
+ new(index: index, data: nil, separator: nil, definition: attributes.keys, attributes: attributes)
10
+ end
11
+
12
+ def initialize(index:, data:, separator:, definition:, attributes: nil)
13
+ @index = index
14
+ @data = data
15
+ @separator = separator
16
+ @definition = definition
17
+ @attributes = attributes
18
+ @fields = attributes.keys unless attributes.nil?
10
19
  end
11
20
 
12
21
  def to_h
13
- @attributes
22
+ attributes
23
+ end
24
+
25
+ def valid?
26
+ return false unless field_size_valid?
27
+
28
+ true
29
+ end
30
+
31
+ def errors
32
+ return [] if valid?
33
+
34
+ errors = []
35
+ errors << field_size_mismatch_message unless field_size_valid?
36
+ errors
37
+ end
38
+
39
+ def attributes
40
+ return nil unless valid?
41
+
42
+ @attributes ||= fields.each_with_index.to_h do |field, field_index|
43
+ [definition[field_index].to_sym, field.strip]
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :data, :separator, :definition
50
+
51
+ def field_size_mismatch_message
52
+ "Field size mismatch in row #{index}. Expected: #{definition.size} fields, found: #{fields.size}"
53
+ end
54
+
55
+ def fields
56
+ @fields ||= data.split(separator)
57
+ end
58
+
59
+ def field_size_valid?
60
+ definition.size >= fields.size
14
61
  end
15
62
 
16
63
  def method_missing(method, *_arguments)
17
- return @attributes[method] unless @attributes[method].nil?
64
+ return super if attributes.nil?
65
+ return attributes[method] unless attributes[method].nil?
66
+
67
+ super
18
68
  end
19
69
 
20
70
  def respond_to_missing?(method, _ = false)
21
- !@attributes[method].nil?
71
+ !attributes[method].nil?
22
72
  end
23
73
  end
24
74
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RightmoveBLM
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/rightmove_blm.rb CHANGED
@@ -8,4 +8,6 @@ require 'time'
8
8
  # Rightmove BLM (Bulk Load Mass) data format parsing tools.
9
9
  # https://www.rightmove.co.uk/ps/pdf/guides/RightmoveDatafeedFormatV3iOVS_1.6.pdf
10
10
  module RightmoveBLM
11
+ class Error < StandardError; end
12
+ class ParserError < Error; end
11
13
  end
@@ -5,24 +5,20 @@ require_relative 'lib/rightmove_blm/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'rightmove_blm'
7
7
  spec.version = RightmoveBLM::VERSION
8
- spec.authors = ['Robert Farrell']
8
+ spec.authors = ['Bob Farrell']
9
9
  spec.email = 'git@bob.frl'
10
10
 
11
11
  spec.summary
12
12
  spec.description = 'Parse and generate Rightmove BLM files'
13
- spec.required_ruby_version = '~> 2.5'
13
+ spec.required_ruby_version = '>= 2.7'
14
14
 
15
15
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
16
16
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  end
18
18
 
19
- spec.homepage = 'https://github.com/bobf/rightmove_blm'
19
+ spec.homepage = 'http://github.com/robertmay/blm'
20
20
  spec.licenses = ['MIT']
21
21
  spec.require_paths = ['lib']
22
- spec.rubygems_version = '1.3.7'
23
22
  spec.summary = 'A parser for the Rightmove .blm format'
24
- spec.test_files = [
25
- 'spec/blm_spec.rb',
26
- 'spec/spec_helper.rb'
27
- ]
23
+ spec.metadata['rubygems_mfa_required'] = 'true'
28
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rightmove_blm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Robert Farrell
7
+ - Bob Farrell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-27 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Parse and generate Rightmove BLM files
14
14
  email: git@bob.frl
@@ -27,37 +27,33 @@ files:
27
27
  - Makefile
28
28
  - README.md
29
29
  - Rakefile
30
- - VERSION
31
30
  - lib/rightmove_blm.rb
32
31
  - lib/rightmove_blm/document.rb
33
32
  - lib/rightmove_blm/row.rb
34
33
  - lib/rightmove_blm/version.rb
35
34
  - rightmove_blm.gemspec
36
- - spec/blm_spec.rb
37
- - spec/spec_helper.rb
38
- homepage: https://github.com/bobf/rightmove_blm
35
+ homepage: http://github.com/robertmay/blm
39
36
  licenses:
40
37
  - MIT
41
- metadata: {}
38
+ metadata:
39
+ rubygems_mfa_required: 'true'
42
40
  post_install_message:
43
41
  rdoc_options: []
44
42
  require_paths:
45
43
  - lib
46
44
  required_ruby_version: !ruby/object:Gem::Requirement
47
45
  requirements:
48
- - - "~>"
46
+ - - ">="
49
47
  - !ruby/object:Gem::Version
50
- version: '2.5'
48
+ version: '2.7'
51
49
  required_rubygems_version: !ruby/object:Gem::Requirement
52
50
  requirements:
53
51
  - - ">="
54
52
  - !ruby/object:Gem::Version
55
53
  version: '0'
56
54
  requirements: []
57
- rubygems_version: 3.2.3
55
+ rubygems_version: 3.1.6
58
56
  signing_key:
59
57
  specification_version: 4
60
58
  summary: A parser for the Rightmove .blm format
61
- test_files:
62
- - spec/blm_spec.rb
63
- - spec/spec_helper.rb
59
+ test_files: []
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.1
data/spec/blm_spec.rb DELETED
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe RightmoveBLM do
4
- context 'reading a .blm file' do
5
- let(:data) { fixture('example_data.blm') }
6
- let(:blm) { RightmoveBLM::Document.new(source: data.read) }
7
-
8
- it 'should parse settings from the header' do
9
- expect(blm.header).to be_a(Hash)
10
- expect(blm.header[:version]).to eq('3')
11
- expect(blm.header[:eof]).to_not be_nil
12
- expect(blm.header[:eor]).to_not be_nil
13
- end
14
-
15
- it 'should parse the column definition' do
16
- expect(blm.definition).to be_a(Array)
17
- expect(blm.definition.size).to be >= 1
18
- end
19
-
20
- it 'should parse the data into an array of hashes' do
21
- expect(blm.data).to be_a(Array)
22
- expect(blm.data.size).to be >= 1
23
- expect(blm.data).to respond_to(:each, :each_with_index)
24
- blm.data.each { |row| expect(row).to be_a(RightmoveBLM::Row) }
25
- end
26
-
27
- it 'should allow access to data values via methods' do
28
- blm.data.each { |row| expect(row.address_1).to_not be_nil }
29
- end
30
-
31
- it 'should allow access to the @attributes hash directly' do
32
- blm.data.each { |row| expect(row.attributes).to be_a(Hash) }
33
- end
34
- end
35
-
36
- context 'creating a .blm file' do
37
- subject(:document) { RightmoveBLM::Document.from_array_of_hashes(data) }
38
-
39
- let(:data) do
40
- [{ field1: 'row 1 field 1 data', field2: 'row 1 field 2 data', field3: 'row 1 field 3 data' },
41
- { field1: 'row 2 field 1 data', field2: 'row 2 field 2 data', field3: 'row 2 field 3 data' }]
42
- end
43
-
44
- its(:header) { is_expected.to include({ 'property count': '2' }) }
45
- its(:header) { is_expected.to include({ version: '3' }) }
46
- its(:definition) { is_expected.to eql %i[field1 field2 field3] }
47
-
48
- describe '#to_blm' do
49
- let(:loaded_document) { RightmoveBLM::Document.new(source: subject.to_blm) }
50
-
51
- it 'has a property count' do
52
- expect(loaded_document.header[:'property count']).to eql '2'
53
- end
54
-
55
- it 'has a version' do
56
- expect(loaded_document.header[:version]).to eql '3'
57
- end
58
-
59
- it 'has a "generated date" timestamp' do
60
- expect(loaded_document.header[:'generated date']).to start_with Time.now.utc.strftime('%d-%b-%Y').upcase
61
- end
62
-
63
- it 'has property data' do
64
- expect(loaded_document.data.map(&:to_h)).to eql data
65
- end
66
-
67
- it 'has a field definition' do
68
- expect(loaded_document.definition).to eql %w[field1 field2 field3]
69
- end
70
- end
71
- end
72
- end
data/spec/spec_helper.rb DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'devpack'
5
- require 'rightmove_blm'
6
-
7
- require 'rspec/file_fixtures'
8
- require 'rspec/its'
9
-
10
- RSpec.configure do |config|
11
- config.example_status_persistence_file_path = '.rspec_status'
12
- config.disable_monkey_patching!
13
- config.expect_with :rspec do |c|
14
- c.syntax = :expect
15
- end
16
- end