norma43_parser 1.0.1 → 2.0.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.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +13 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
  4. data/.rubocop.yml +234 -0
  5. data/Gemfile +7 -1
  6. data/Gemfile.lock +73 -0
  7. data/README.md +1 -1
  8. data/Rakefile +2 -0
  9. data/lib/norma43.rb +9 -3
  10. data/lib/norma43/line_handlers.rb +2 -3
  11. data/lib/norma43/line_parsers/account_end.rb +18 -0
  12. data/lib/norma43/line_parsers/account_start.rb +18 -0
  13. data/lib/norma43/line_parsers/additional_currency.rb +12 -0
  14. data/lib/norma43/line_parsers/additional_item.rb +11 -0
  15. data/lib/norma43/line_parsers/document_end.rb +9 -0
  16. data/lib/norma43/line_parsers/document_start.rb +13 -0
  17. data/lib/norma43/line_parsers/file_format_validator.rb +15 -12
  18. data/lib/norma43/line_parsers/line_parser.rb +29 -28
  19. data/lib/norma43/line_parsers/transaction.rb +18 -0
  20. data/lib/norma43/line_processors.rb +2 -2
  21. data/lib/norma43/models.rb +5 -4
  22. data/lib/norma43/parser.rb +38 -42
  23. data/lib/norma43/utils/contexts.rb +40 -37
  24. data/lib/norma43/utils/string_helpers.rb +10 -6
  25. data/lib/norma43/utils/typecaster.rb +16 -12
  26. data/lib/norma43/version.rb +3 -1
  27. data/norma43_parser.gemspec +11 -9
  28. data/spec/example1_parse_spec.rb +12 -11
  29. data/spec/norma43/line_parsers/account_end_spec.rb +1 -1
  30. data/spec/norma43/line_parsers/account_start_spec.rb +1 -2
  31. data/spec/norma43/line_parsers/additional_currency_spec.rb +1 -1
  32. data/spec/norma43/line_parsers/additional_items_spec.rb +1 -1
  33. data/spec/norma43/line_parsers/document_end_spec.rb +1 -2
  34. data/spec/norma43/line_parsers/document_start_spec.rb +1 -1
  35. data/spec/norma43/line_parsers/transaction_spec.rb +1 -3
  36. data/spec/norma43/line_processors/account_end_spec.rb +6 -8
  37. data/spec/norma43/line_processors/account_start_spec.rb +8 -9
  38. data/spec/norma43/line_processors/additional_currency_spec.rb +6 -7
  39. data/spec/norma43/line_processors/additional_items_spec.rb +6 -7
  40. data/spec/norma43/line_processors/document_end_spec.rb +5 -6
  41. data/spec/norma43/line_processors/document_start_spec.rb +3 -4
  42. data/spec/norma43/line_processors/transaction_spec.rb +6 -7
  43. data/spec/norma43/parser_spec.rb +5 -4
  44. data/spec/norma43_spec.rb +2 -0
  45. data/spec/spec_helper.rb +1 -4
  46. data/spec/support/shared_examples_for_values_line_parsers.rb +2 -0
  47. metadata +41 -32
  48. data/lib/norma43/line_parsers/line_parsers.rb +0 -69
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module LineParsers
5
+ class AdditionalCurrency < LineParser
6
+ field :data_code, 2..3, :integer
7
+ field :currency_code, 4..6, :integer
8
+ field :amount, 7..20, :integer
9
+ field :free, 21..79
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module LineParsers
5
+ class AdditionalItem < LineParser
6
+ field :data_code, 2..3, :integer
7
+ field :item_1, 4..41
8
+ field :item_2, 42..79
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module LineParsers
5
+ class DocumentEnd < LineParser
6
+ field :record_number, 20..25, :integer
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module LineParsers
5
+ class DocumentStart < LineParser
6
+ field :id, 2..13
7
+ field :created_at, 14..33, :time
8
+ field :delivery_number, 34..35, :integer
9
+ field :file_type, 36..38
10
+ field :name, 39..48
11
+ end
12
+ end
13
+ end
@@ -1,19 +1,22 @@
1
- require "norma43/line_parsers/line_parser"
1
+ # frozen_string_literal: true
2
+
2
3
  module Norma43
3
- class FileFormatValidator < LineParser
4
- field :record_type, 0..1, :raw
5
- field :file_type, 36..38
4
+ module LineParsers
5
+ class FileFormatValidator < LineParser
6
+ field :record_type, 0..1, :raw
7
+ field :file_type, 36..38
6
8
 
7
- def has_document?; file_type=="00" end
9
+ def has_document?; file_type == "00" end
8
10
 
9
- def valid?
10
- errors.empty?
11
- end
11
+ def valid?
12
+ errors.empty?
13
+ end
12
14
 
13
- def errors
14
- errors = []
15
- %w(11 00).include? record_type or errors << "Must start with 00 (was ”#{record_type}”)"
16
- errors
15
+ def errors
16
+ errors = []
17
+ %w(11 00).include?(record_type) || errors << ("Must start with 00 (was ”#{record_type}”)")
18
+ errors
19
+ end
17
20
  end
18
21
  end
19
22
  end
@@ -1,42 +1,43 @@
1
- require "norma43/utils/typecaster"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Norma43
4
- class LineParser
5
- attr_reader :line
6
- def initialize line
7
- @line = line
8
- end
4
+ module LineParsers
5
+ class LineParser
6
+ attr_reader :line
7
+ def initialize(line)
8
+ @line = line
9
+ end
9
10
 
10
- def attributes
11
- self.class.field_names.each_with_object({}) do |field, attrs|
12
- attrs[field] = self.public_send(field)
11
+ def attributes
12
+ self.class.field_names.each_with_object({}) do |field, attrs|
13
+ attrs[field] = self.public_send(field)
14
+ end
13
15
  end
14
- end
15
16
 
16
- def self.field name, range, type = :string
17
- self.field_names.push name
17
+ def self.field(name, range, type = :string)
18
+ self.field_names.push name
18
19
 
19
- define_method name do
20
- if range.is_a?(Array) # let multivalued attribute
21
- range.map { |r| value_at_position(r, type) }.compact
22
- else
23
- value_at_position range, type
20
+ define_method name do
21
+ if range.is_a?(Array) # let multivalued attribute
22
+ range.map { |r| value_at_position(r, type) }.compact
23
+ else
24
+ value_at_position range, type
25
+ end
24
26
  end
25
27
  end
26
- end
27
-
28
- private
29
28
 
30
- def self.field_names
31
- @field_names ||= []
32
- end
29
+ private
30
+ def self.field_names
31
+ @field_names ||= []
32
+ end
33
33
 
34
- def value_at_position range, type
35
- typecast line[range].to_s.strip, type
36
- end
34
+ def value_at_position(range, type)
35
+ typecast line[range].to_s.strip, type
36
+ end
37
37
 
38
- def typecast value, type
39
- Typecaster.cast value, type
38
+ def typecast(value, type)
39
+ Norma43::Utils::Typecaster.cast value, type
40
+ end
40
41
  end
41
42
  end
42
43
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module LineParsers
5
+ class Transaction < LineParser
6
+ field :origin_branch_code, 6..9, :integer
7
+ field :transaction_date, 10..15, :date
8
+ field :value_date, 16..21, :date
9
+ field :shared_item, 22..23, :integer
10
+ field :own_item, 24..26, :integer
11
+ field :amount_code, 27, :integer
12
+ field :amount, 28..41, :integer
13
+ field :document_number, 42..51, :integer
14
+ field :reference_1, 52..63, :integer
15
+ field :reference_2, 64..79
16
+ end
17
+ end
18
+ end
@@ -1,4 +1,5 @@
1
- require "norma43/models"
1
+ # frozen_string_literal: true
2
+
2
3
  module Norma43
3
4
  module LineProcessors
4
5
  DocumentStart = ->(line, contexts) {
@@ -55,6 +56,5 @@ module Norma43
55
56
  contexts.current.additional_currency = additional_currency
56
57
  contexts
57
58
  }
58
-
59
59
  end
60
60
  end
@@ -1,9 +1,10 @@
1
- require "norma43/utils/string_helpers"
2
- require 'virtus'
1
+ # frozen_string_literal: true
2
+
3
+ require "virtus"
3
4
 
4
5
  module Norma43
5
6
  module Models
6
- #forward declarations
7
+ # forward declarations
7
8
  class Account; end
8
9
  class Transaction; end
9
10
  class AdditionalItem; end
@@ -62,7 +63,7 @@ module Norma43
62
63
  attribute :reference_2
63
64
  attribute :additional_items, Array[AdditionalItem]
64
65
  attribute :additional_currency, AdditionalCurrency
65
- def debit?; self.amount_code==DEBIT_CODE end
66
+ def debit?; self.amount_code == DEBIT_CODE end
66
67
  end
67
68
 
68
69
  class AdditionalItem
@@ -1,25 +1,23 @@
1
- require "norma43/line_parsers/file_format_validator"
2
- require "norma43/line_handlers"
3
- require "norma43/utils/contexts"
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Norma43
6
- class InvalidFileFormatError < ArgumentError; end;
4
+ class InvalidFileFormatError < ArgumentError; end
7
5
 
8
6
  class Parser
9
7
  attr_reader :file
10
8
 
11
9
  # Parser.new accepts a File instance or a String
12
10
  # A InvalidFileFormatError will be raised if file isn't in the Norma43 format
13
- def initialize file
11
+ def initialize(file)
14
12
  @file = file
15
13
  validator = validate_file_format
16
14
  @contexts = if validator.has_document?
17
- Contexts.new
15
+ Norma43::Utils::Contexts.new
18
16
  else
19
17
  # in theory Norma43 says that files should start with DocumentStart but
20
18
  # practically doesn't happen, so that we create one artificially
21
19
  # to avoid corner cases in the processors
22
- Contexts.new().tap { |ctx| ctx.add Models::Document.new }
20
+ Norma43::Utils::Contexts.new().tap { |ctx| ctx.add Models::Document.new }
23
21
  end
24
22
  end
25
23
 
@@ -28,52 +26,50 @@ module Norma43
28
26
  end
29
27
 
30
28
  protected
31
-
32
- def lines
33
- @lines ||= file.each_line
34
- end
29
+ def lines
30
+ @lines ||= file.each_line
31
+ end
35
32
 
36
33
  private
34
+ def validate_file_format
35
+ validator = Norma43::LineParsers::FileFormatValidator.new first_line
36
+ raise InvalidFileFormatError.new(validator.errors.join(", ")) unless validator.valid?
37
+ validator
38
+ end
37
39
 
38
- def validate_file_format
39
- validator = FileFormatValidator.new first_line
40
- raise InvalidFileFormatError.new(validator.errors.join(", ")) unless validator.valid?
41
- validator
42
- end
43
-
44
- def parse_lines contexts
45
- parse_lines parse_line(self.lines.next, contexts)
40
+ def parse_lines(contexts)
41
+ parse_lines parse_line(self.lines.next, contexts)
46
42
 
47
- rescue StopIteration# because lines is an enumerator raises StopIteration on end
48
- self.lines.rewind # Ensure we do not bomb out when calling result multiple times
49
- contexts
50
- end
43
+ rescue StopIteration # because lines is an enumerator raises StopIteration on end
44
+ self.lines.rewind # Ensure we do not bomb out when calling result multiple times
45
+ contexts
46
+ end
51
47
 
52
- # Look up a matching handler for the line and process it
53
- # The process method on a handler always returns a Contexts object
54
- def parse_line line, contexts
55
- line = line.encode Encoding::UTF_8 if encode_lines?
48
+ # Look up a matching handler for the line and process it
49
+ # The process method on a handler always returns a Contexts object
50
+ def parse_line(line, contexts)
51
+ line = line.encode Encoding::UTF_8 if encode_lines?
56
52
 
57
- handler = handler_for_line line
53
+ handler = handler_for_line line
58
54
 
59
- handler.process line, contexts
60
- end
55
+ handler.process line, contexts
56
+ end
61
57
 
62
- def handler_for_line line
63
- LineHandlers.mapping.fetch line[0..1]
64
- end
58
+ def handler_for_line(line)
59
+ LineHandlers.mapping.fetch line[0..1]
60
+ end
65
61
 
66
- def encode_lines?
67
- first_line.encoding != Encoding::UTF_8
68
- end
62
+ def encode_lines?
63
+ first_line.encoding != Encoding::UTF_8
64
+ end
69
65
 
70
- def first_line
71
- @first_line ||= begin
72
- line = self.lines.peek
73
- self.lines.rewind # peek seems to move the pointer when file is an actual File object
66
+ def first_line
67
+ @first_line ||= begin
68
+ line = self.lines.peek
69
+ self.lines.rewind # peek seems to move the pointer when file is an actual File object
74
70
 
75
- line
71
+ line
72
+ end
76
73
  end
77
- end
78
74
  end
79
75
  end
@@ -1,54 +1,57 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Norma43
2
- class Contexts
3
- def initialize containers = nil
4
- Array(containers).compact.each do |container|
5
- add container
4
+ module Utils
5
+ class Contexts
6
+ def initialize(containers = nil)
7
+ Array(containers).compact.each do |container|
8
+ add container
9
+ end
6
10
  end
7
- end
8
-
9
- def result
10
- contexts.first
11
- end
12
11
 
13
- def current
14
- contexts.last
15
- end
16
-
17
- def add container
18
- contexts.push container
19
- end
12
+ def result
13
+ contexts.first
14
+ end
20
15
 
21
- def move_up
22
- contexts.pop
23
- end
16
+ def current
17
+ contexts.last
18
+ end
24
19
 
25
- def move_to container_class
26
- until current.is_a?(container_class) or current.nil?
27
- move_up
28
- end if contexts.any?
29
- end
20
+ def add(container)
21
+ contexts.push container
22
+ end
30
23
 
31
- def move_to_or_add_to_parent container_class, parent_container_class
32
- return self if current.is_a?(container_class)
24
+ def move_up
25
+ contexts.pop
26
+ end
33
27
 
34
- until current.kind_of?(parent_container_class)
35
- move_up
28
+ def move_to(container_class)
29
+ until current.is_a?(container_class) || current.nil?
30
+ move_up
31
+ end if contexts.any?
36
32
  end
37
33
 
38
- entity = container_class.new
34
+ def move_to_or_add_to_parent(container_class, parent_container_class)
35
+ return self if current.is_a?(container_class)
39
36
 
40
- setter_name = StringHelpers.underscore container_class.name.split("::").last
41
- current.public_send "#{setter_name}=", entity
37
+ until current.kind_of?(parent_container_class)
38
+ move_up
39
+ end
42
40
 
43
- add entity
41
+ entity = container_class.new
44
42
 
45
- self
46
- end
43
+ setter_name = StringHelpers.underscore container_class.name.split("::").last
44
+ current.public_send "#{setter_name}=", entity
47
45
 
48
- private
46
+ add entity
47
+
48
+ self
49
+ end
49
50
 
50
- def contexts
51
- @contexts ||= []
51
+ private
52
+ def contexts
53
+ @contexts ||= []
54
+ end
52
55
  end
53
56
  end
54
57
  end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Norma43
2
- module StringHelpers
3
- def self.underscore word
4
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
5
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
6
- word.tr!("-", "_")
7
- word.downcase
4
+ module Utils
5
+ module StringHelpers
6
+ def self.underscore(word)
7
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
8
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
9
+ word.tr!("-", "_")
10
+ word.downcase
11
+ end
8
12
  end
9
13
  end
10
14
  end