paxmex 1.2.0 → 1.2.1

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: 873f859a60a234be55dfd8e57e8d8ce52806465e
4
- data.tar.gz: 14ad88336ee18973abbba8abc6b07a89b06ab349
3
+ metadata.gz: 328b6d41aeeeccde646c599fbc62a8e08b5250aa
4
+ data.tar.gz: f21beec92244d5731a3643b7dd07b0cc4e7b0c94
5
5
  SHA512:
6
- metadata.gz: cc895ad6ce66301ce496350b4f495cc9b20bb2c5c8d02f9dc2c990f4246f3a7412bda3e9ff4ed7bf8ff01300c6717e484c12caf1b1380d358e61ceafec6384c0
7
- data.tar.gz: 670b609085c611796b57de80e953426487c52e74746348f1c573e801ba88467aadafc9ae7239e7a9962492b17d603a58db2ae19964a384f1957f6f1e136af81e
6
+ metadata.gz: 6204b8480df620b96e07967dfe18b8c64c2d6f9b3822391ab64651f7f873af2e5f8dbc4c760b7b6d80c12b4f97e778a4eec6e7adfe3152930909d0c9cdaa2362
7
+ data.tar.gz: b5a7ba48f0ca141a4ee034a5ec1f97f3d62116f9f021f9468af15e7519d953f438ae6e608414d7ef025e5763aaa9d54e48b67a489a8335e2251b8aaa73f91bc1
data/README.md CHANGED
@@ -16,9 +16,10 @@ This gem parses your Amex data files into human readable data.
16
16
 
17
17
  * parse_eptrn(file_path)
18
18
  * parse_epraw(file_path)
19
+ * parse_cbnot(file_path)
19
20
  * parse_epa(file_path)
20
21
 
21
- The first two methods return a readable hash in the following format:
22
+ The first three methods return a readable hash in the following format:
22
23
 
23
24
  ```ruby
24
25
  {
@@ -90,12 +91,13 @@ If you'd like the raw values to be returned instead, you can set the ```raw_valu
90
91
  ```ruby
91
92
  Paxmex.parse_eptrn(path_to_file, raw_values: true)
92
93
  Paxmex.parse_epraw(path_to_file, raw_values: true)
94
+ Paxmex.parse_cbnot(path_to_file, raw_values: true)
93
95
  Paxmex.parse_epa(path_to_file, raw_values: true)
94
96
  ```
95
97
 
96
98
  ## User-defined schema
97
99
 
98
- If you need to parse a different format (i.e. not EPRAW, EPTRN or EPA), write your own schema definition and use it like this:
100
+ If you need to parse a different format (i.e. not EPRAW, EPTRN, CBNOT, or EPA), write your own schema definition and use it like this:
99
101
  ```ruby
100
102
  parser = Parser.new(path_to_raw_file, path_to_schema_file)
101
103
  result = parser.parse
@@ -109,6 +111,7 @@ require 'paxmex'
109
111
  # Use default schema definitions
110
112
  Paxmex.parse_eptrn('/path/to/amex/eptrn/raw/file')
111
113
  Paxmex.parse_epraw('/path/to/amex/epraw/raw/file')
114
+ Paxmex.parse_cbnot('/path/to/amex/cbnot/raw/file')
112
115
  Paxmex.parse_epa('/path/to/amex/epa/raw/file')
113
116
 
114
117
  # Use your own schema definition
@@ -116,7 +119,7 @@ parser = Parser.new('/path/to/raw/file', '/path/to/your/schema.yml')
116
119
  result = parser.parse
117
120
  ```
118
121
 
119
- The raw input files for either methods are data report files provided by American Express. These files are in either EPRAW, EPTRN or EPA format so use the relevant method to parse them. We have provided dummy EPRAW, EPTRN and EPA files in `spec/support`. Output and key-value pairs vary depending on whether you choose to parse an EPTRN, EPRAW or EPA file.
122
+ The raw input files for either methods are data report files provided by American Express. These files are in either EPRAW, EPTRN, CBNOT, or EPA format so use the relevant method to parse them. We have provided dummy EPRAW, EPTRN, CBNOT, and EPA files in `spec/support`. Output and key-value pairs vary depending on whether you choose to parse an EPTRN, EPRAW, CBNOT, or EPA file.
120
123
  If you need to parse a file in another format, you can write your own YAML schema for this purpose. We would be happy if you help us improving this project by sharing your schemas.
121
124
 
122
125
  ## Contributing
@@ -1,59 +1,69 @@
1
1
  require 'bigdecimal'
2
2
  require 'paxmex/schema'
3
3
 
4
- class Paxmex::Schema::Field
5
- DATE_PATTERN = /^date\((.+)\)$/
6
- TIME_PATTERN = /^time\((.+)\)$/
4
+ module Paxmex
5
+ class Schema
6
+ class Field
7
+ DATE_PATTERN = /^date\((.+)\)$/
8
+ TIME_PATTERN = /^time\((.+)\)$/
7
9
 
8
- attr_reader :name, :start, :final, :type
10
+ attr_reader :name, :start, :final, :type
9
11
 
10
- def initialize(opts = {})
11
- @name = opts[:name]
12
- @start = opts[:start]
13
- @final = opts[:final]
14
- @type = opts[:type] || 'string'
15
- end
12
+ def initialize(opts = {})
13
+ @name = opts[:name]
14
+ @start = opts[:start]
15
+ @final = opts[:final]
16
+ @type = opts[:type] || 'string'
17
+ end
16
18
 
17
- def parse(raw_value)
18
- case type
19
- when 'string' then raw_value.rstrip
20
- when 'julian' then parse_julian_date(raw_value) rescue nil
21
- when 'date' then Date.strptime(raw_value, '%m%d%Y') rescue nil
22
- when 'numeric' then raw_value.strip.to_i
23
- when 'decimal' then parse_decimal(raw_value)
24
- when DATE_PATTERN then parse_date_pattern(raw_value) rescue nil
25
- when TIME_PATTERN then parse_time_pattern(raw_value) rescue nil
26
- else fail "Could not parse field type #{type}. Supported types: string, julian, date, numeric, decimal, date(format), time(format)"
27
- end
28
- end
19
+ def parse(raw_value)
20
+ case type
21
+ when 'string' then raw_value.rstrip
22
+ when 'julian' then parse_julian_date(raw_value) rescue nil
23
+ when 'date' then Date.strptime(raw_value, '%m%d%Y') rescue nil
24
+ when 'numeric' then parse_numeric(raw_value)
25
+ when 'decimal' then parse_decimal(raw_value)
26
+ when DATE_PATTERN then parse_date_pattern(raw_value) rescue nil
27
+ when TIME_PATTERN then parse_time_pattern(raw_value) rescue nil
28
+ else fail "Could not parse field type #{type}. Supported types: string, julian, date, numeric, decimal, date(format), time(format)"
29
+ end
30
+ end
29
31
 
30
- private
32
+ private
31
33
 
32
- def parse_date_pattern(value)
33
- Date.strptime(value, DATE_PATTERN.match(type).captures.first)
34
- end
34
+ def parse_date_pattern(value)
35
+ Date.strptime(value, DATE_PATTERN.match(type).captures.first)
36
+ end
35
37
 
36
- def parse_time_pattern(value)
37
- Time.strptime(value, TIME_PATTERN.match(type).captures.first)
38
- end
38
+ def parse_time_pattern(value)
39
+ Time.strptime(value, TIME_PATTERN.match(type).captures.first)
40
+ end
39
41
 
40
- def parse_decimal(value)
41
- # fields _may_ end with a letter
42
- unless value.match(/^[0-9]*[0-9A-R{}]$/)
43
- fail "Unexpected value '#{value}' for field '#{name}'"
44
- end
42
+ def parse_numeric(value)
43
+ value.strip!
44
+ return value.to_f if value.include?('.')
45
+ value.to_i
46
+ end
45
47
 
46
- is_credit = !!(value =~ /[JKLMNOPQR}]/)
47
- value = value.gsub(/[ABCDEFGHIJKLMNOPQR{}]/,
48
- 'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8, 'I'=>9,
49
- 'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'O'=>6, 'P'=>7, 'Q'=>8, 'R'=>9,
50
- '{'=>0, '}'=>0)
48
+ def parse_decimal(value)
49
+ # fields _may_ end with a letter
50
+ unless value.match(/^[0-9]*[0-9A-R{}]$/)
51
+ fail "Unexpected value '#{value}' for field '#{name}'"
52
+ end
51
53
 
52
- parsed_value = value.to_i * (is_credit ? -1 : 1) / 100.0
53
- BigDecimal.new(parsed_value.to_s, 7)
54
- end
54
+ is_credit = !!(value =~ /[JKLMNOPQR}]/)
55
+ value = value.gsub(/[ABCDEFGHIJKLMNOPQR{}]/,
56
+ 'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8, 'I'=>9,
57
+ 'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'O'=>6, 'P'=>7, 'Q'=>8, 'R'=>9,
58
+ '{'=>0, '}'=>0)
55
59
 
56
- def parse_julian_date(date_string)
57
- Date.ordinal(date_string[0..3].to_i, date_string[4..6].to_i)
60
+ parsed_value = value.to_i * (is_credit ? -1 : 1) / 100.0
61
+ BigDecimal.new(parsed_value.to_s, 7)
62
+ end
63
+
64
+ def parse_julian_date(date_string)
65
+ Date.ordinal(date_string[0..3].to_i, date_string[4..6].to_i)
66
+ end
67
+ end
58
68
  end
59
69
  end
@@ -1,64 +1,68 @@
1
1
  require 'paxmex/schema/field'
2
2
 
3
- class Paxmex::Schema::Section
4
- BLOCK_LENGTH = 450
3
+ module Paxmex
4
+ class Schema
5
+ class Section
6
+ BLOCK_LENGTH = 450
5
7
 
6
- attr_reader :key, :data, :parent_key
8
+ attr_reader :key, :data, :parent_key
7
9
 
8
- def initialize(key, data)
9
- @parent_key = data.delete('PARENT') { nil }
10
- @key = key
11
- @data = data
12
- end
10
+ def initialize(key, data)
11
+ @parent_key = data.delete('PARENT') { nil }
12
+ @key = key
13
+ @data = data
14
+ end
13
15
 
14
- def recurring?
15
- !!data['RECURRING']
16
- end
16
+ def recurring?
17
+ !!data['RECURRING']
18
+ end
17
19
 
18
- def abstract?
19
- !!data['ABSTRACT']
20
- end
20
+ def abstract?
21
+ !!data['ABSTRACT']
22
+ end
21
23
 
22
- def trailer?
23
- !!data['TRAILER']
24
- end
24
+ def trailer?
25
+ !!data['TRAILER']
26
+ end
25
27
 
26
- def child?
27
- !!@parent_key
28
- end
28
+ def child?
29
+ !!@parent_key
30
+ end
29
31
 
30
- def fields
31
- @fields ||= data['FIELDS'].map do |field|
32
- Paxmex::Schema::Field.new(
33
- name: field['NAME'],
34
- start: field['RANGE'].first,
35
- final: field['RANGE'].last,
36
- type: field['TYPE']
37
- )
38
- end
39
- end
32
+ def fields
33
+ @fields ||= data['FIELDS'].map do |field|
34
+ Paxmex::Schema::Field.new(
35
+ name: field['NAME'],
36
+ start: field['RANGE'].first,
37
+ final: field['RANGE'].last,
38
+ type: field['TYPE']
39
+ )
40
+ end
41
+ end
40
42
 
41
- def types
42
- data['TYPES']
43
- end
43
+ def types
44
+ data['TYPES']
45
+ end
44
46
 
45
- def type_field
46
- data['TYPE_FIELD']
47
- end
47
+ def type_field
48
+ data['TYPE_FIELD']
49
+ end
48
50
 
49
- def type_mapping
50
- data['TYPE_MAPPING']
51
- end
51
+ def type_mapping
52
+ data['TYPE_MAPPING']
53
+ end
52
54
 
53
- def section_for_type(type)
54
- sections_for_types.detect { |t| t.key == type_mapping[type] }
55
- end
55
+ def section_for_type(type)
56
+ sections_for_types.detect { |t| t.key == type_mapping[type] }
57
+ end
56
58
 
57
- def length
58
- BLOCK_LENGTH
59
- end
59
+ def length
60
+ BLOCK_LENGTH
61
+ end
60
62
 
61
- def sections_for_types
62
- @sections_for_types ||= types.map { |k, v| self.class.new(k, v) }
63
+ def sections_for_types
64
+ @sections_for_types ||= types.map { |k, v| self.class.new(k, v) }
65
+ end
66
+ end
63
67
  end
64
68
  end
data/lib/paxmex/schema.rb CHANGED
@@ -1,46 +1,48 @@
1
- class Paxmex::Schema
2
- require 'paxmex/schema/section'
1
+ require 'paxmex/schema/section'
3
2
 
4
- def initialize(schema_hash)
5
- @schema_hash = schema_hash
6
- @parents = []
7
- end
3
+ module Paxmex
4
+ class Schema
5
+ def initialize(schema_hash)
6
+ @schema_hash = schema_hash
7
+ @parents = []
8
+ end
8
9
 
9
- def sections
10
- @sections ||= @schema_hash.map { |k, v| build_section(k,v) }
11
- end
10
+ def sections
11
+ @sections ||= @schema_hash.map { |k, v| build_section(k,v) }
12
+ end
12
13
 
13
- def to_h
14
- @schema_hash
15
- end
14
+ def to_h
15
+ @schema_hash
16
+ end
16
17
 
17
- def parent_section?(key)
18
- sections.any? do |s|
19
- if s.abstract?
20
- s.sections_for_types.any? { |ss| ss.parent_key == key }
21
- else
22
- s.parent_key == key
18
+ def parent_section?(key)
19
+ sections.any? do |s|
20
+ if s.abstract?
21
+ s.sections_for_types.any? { |ss| ss.parent_key == key }
22
+ else
23
+ s.parent_key == key
24
+ end
23
25
  end
24
26
  end
25
- end
26
27
 
27
- private
28
+ private
28
29
 
29
- def build_section(key, data)
30
- section = Section.new(key, data)
31
- mark_abstract if section.abstract?
32
- add_parent(section) if section.child?
30
+ def build_section(key, data)
31
+ section = Section.new(key, data)
32
+ mark_abstract if section.abstract?
33
+ add_parent(section) if section.child?
33
34
 
34
- section
35
- end
35
+ section
36
+ end
36
37
 
37
- def mark_abstract
38
- fail 'Cannot have more than one abstract section' if @have_abstract
39
- @have_abstract = true
40
- end
38
+ def mark_abstract
39
+ fail 'Cannot have more than one abstract section' if @have_abstract
40
+ @have_abstract = true
41
+ end
41
42
 
42
- def add_parent(section)
43
- fail 'Recursive parent definition' if @parents.include?(section.key)
44
- @parents << parent
43
+ def add_parent(section)
44
+ fail 'Recursive parent definition' if @parents.include?(section.key)
45
+ @parents << parent
46
+ end
45
47
  end
46
48
  end
@@ -1,3 +1,3 @@
1
1
  module Paxmex
2
- VERSION = '1.2.0'
2
+ VERSION = '1.2.1'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paxmex
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daryl Yeo
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-03-22 00:00:00.000000000 Z
12
+ date: 2017-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec