metro_2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6da54305e9214a4064d44b76cda323168e1f41ab
4
+ data.tar.gz: beeb5466eb619c9c8a48e089b597b7afb919219b
5
+ SHA512:
6
+ metadata.gz: e7553fab682842f16f9d998989754998713c0fbbfa4d83c75cab97255b16b739954d3bed5cd7f24c9df02cb45cfdd248bfde766989c4d78f1896ed8a82d51ede
7
+ data.tar.gz: 072b5f4387953315956212371cfe6d88a7909a0c100c3b8ed103613616e684672635ec86d064d6ea5b3476408966e034aa8ab5d1c783e6470a63f837bb62f630
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+
3
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in metro_2.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Brian Mascarenhas
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Metro2
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'metro_2'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install metro_2
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/metro_2/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ Bundler.require
6
+ require 'metro_2/version'
7
+
8
+ require 'rspec/core/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task :default => :spec
13
+
14
+ task :build do
15
+ system 'gem build metro_2.gemspec'
16
+ end
17
+
data/lib/metro_2.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'metro_2/version'
2
+
3
+ module Metro2
4
+
5
+ ALPHANUMERIC = /\A([[:alnum:]]|\s)+\z/
6
+ ALPHANUMERIC_PLUS_DASH = /\A([[:alnum:]]|\s|\-)+\z/
7
+ ALPHANUMERIC_PLUS_DOT_DASH_SLASH = /\A([[:alnum:]]|\s|\-|\.|\\|\/)+\z/
8
+ NUMERIC = /\A\d+\.?\d*\z/
9
+
10
+ FIXED_LENGTH = 426
11
+
12
+ def self.alphanumeric_to_metro2(val, required_length, permitted_chars)
13
+ # Left justified and blank-filled
14
+ val = val.to_s
15
+
16
+ return ' ' * required_length if val.empty?
17
+
18
+ unless !!(val =~ permitted_chars)
19
+ raise ArgumentError.new("Content (#{val}) contains invalid characters")
20
+ end
21
+
22
+ if val.size > required_length
23
+ val[0..(required_length-1)]
24
+ else
25
+ val + (' ' * (required_length - val.size))
26
+ end
27
+ end
28
+
29
+ def self.numeric_to_metro2(val, required_length, is_monetary)
30
+ # Right justified and zero-filled
31
+ val = val.to_s
32
+
33
+ return '0' * required_length if val.empty?
34
+
35
+ unless !!(val =~ Metro2::NUMERIC)
36
+ raise ArgumentError.new("field (#{val}) must be numeric")
37
+ end
38
+
39
+ decimal_index = val.index('.')
40
+ val = val[0..(decimal_index - 1)] if decimal_index
41
+
42
+ # any value above 1 billion gets set to 999,999,999
43
+ return '9' * required_length if is_monetary && val.to_f >= 1000000000
44
+ if val.size > required_length
45
+ raise ArgumentError.new("numeric field (#{val}) is too long (max #{required_length})")
46
+ end
47
+
48
+ ('0' * (required_length - val.size)) + val
49
+ end
50
+ end
51
+
52
+ require 'metro_2/fields'
53
+ require 'metro_2/metro2_file'
54
+
55
+ # Require records files
56
+ require 'metro_2/records/record'
57
+
58
+ Dir.new(File.dirname(__FILE__) + '/metro_2/records').each do |file|
59
+ require('metro_2/records/' + File.basename(file)) if File.extname(file) == ".rb"
60
+ end
@@ -0,0 +1,122 @@
1
+ module Metro2
2
+ module Fields
3
+
4
+ def alphanumeric_field(name, required_length, permitted_chars = Metro2::ALPHANUMERIC)
5
+ fields << name
6
+
7
+ # getter
8
+ define_method name do
9
+ instance_variable_get("@#{name}")
10
+ end
11
+
12
+ # setter (includes validations)
13
+ define_method "#{name}=" do | val |
14
+ instance_variable_set("@#{name}", val)
15
+ end
16
+
17
+ # to_metro2
18
+ define_method "#{name}_to_metro2" do
19
+ val = instance_variable_get("@#{name}")
20
+ Metro2.alphanumeric_to_metro2(val, required_length, permitted_chars)
21
+ end
22
+ end
23
+
24
+ def numeric_field(name, required_length, is_monetary = false)
25
+ fields << name
26
+
27
+ # getter
28
+ define_method name do
29
+ instance_variable_get("@#{name}")
30
+ end
31
+
32
+ # setter (includes validations)
33
+ define_method "#{name}=" do | val |
34
+ instance_variable_set("@#{name}", val)
35
+ end
36
+
37
+ # to_metro2
38
+ define_method "#{name}_to_metro2" do
39
+ val = instance_variable_get("@#{name}")
40
+ Metro2.numeric_to_metro2(val, required_length, is_monetary)
41
+ end
42
+ end
43
+
44
+ def alphanumeric_const_field(name, required_length, val, permitted_chars = Metro2::ALPHANUMERIC)
45
+ fields << name
46
+
47
+ # getter
48
+ define_method name do
49
+ val
50
+ end
51
+
52
+ # to_metro2
53
+
54
+ define_method "#{name}_to_metro2" do
55
+ Metro2.alphanumeric_to_metro2(val, required_length, permitted_chars)
56
+ end
57
+ end
58
+
59
+ def numeric_const_field(name, required_length, val, is_monetary = false)
60
+ fields << name
61
+
62
+ # getter
63
+ define_method name do
64
+ val
65
+ end
66
+
67
+ # to_metro2
68
+ define_method "#{name}_to_metro2" do
69
+ Metro2.numeric_to_metro2(val, required_length, is_monetary)
70
+ end
71
+ end
72
+
73
+ def monetary_field(name)
74
+ numeric_field(name, 9, true)
75
+ end
76
+
77
+ def date_field(name)
78
+ fields << name
79
+
80
+ # getter
81
+ define_method name do
82
+ instance_variable_get("@#{name}")
83
+ end
84
+
85
+ # setter (includes validations)
86
+ define_method "#{name}=" do | val |
87
+ instance_variable_set("@#{name}", val)
88
+ end
89
+
90
+ # to_metro2
91
+ define_method "#{name}_to_metro2" do
92
+ # Right justified and zero-filled
93
+ val = instance_variable_get("@#{name}")
94
+ val = val.strftime('%m%d%Y') if val
95
+ Metro2.numeric_to_metro2(val, 8, false)
96
+ end
97
+ end
98
+
99
+ def timestamp_field(name)
100
+ fields << name
101
+
102
+ # getter
103
+ define_method name do
104
+ instance_variable_get("@#{name}")
105
+ end
106
+
107
+ # setter (includes validations)
108
+ define_method "#{name}=" do | val |
109
+ instance_variable_set("@#{name}", val)
110
+ end
111
+
112
+ # to_metro2
113
+ define_method "#{name}_to_metro2" do
114
+ # Right justified and zero-filled
115
+ val = instance_variable_get("@#{name}")
116
+ val = val.strftime('%m%d%Y%H%M%S') if val
117
+ Metro2.numeric_to_metro2(val, 14, false)
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,76 @@
1
+ require 'date'
2
+
3
+ module Metro2
4
+ class Metro2File
5
+ include Fields
6
+
7
+ attr_reader :base_segments
8
+ attr_reader :header
9
+ attr_reader :trailer
10
+
11
+ def initialize
12
+ @base_segments = []
13
+ @header = Records::HeaderSegment.new
14
+ end
15
+
16
+ def to_s
17
+ segments = []
18
+ segments << @header
19
+ @base_segments.each { |base| segments << base }
20
+ @trailer ||= trailer_from_base_segments
21
+ segments << @trailer
22
+
23
+ segments.collect { |r| r.to_metro2 }.join("\n") + "\n"
24
+ end
25
+
26
+ def trailer_from_base_segments
27
+ status_code_count = Hash.new(0)
28
+ num_ssn = 0
29
+ num_dob = 0
30
+ num_telephone = 0
31
+ num_ecoa_code_z = 0
32
+
33
+ @base_segments.each do |base|
34
+ status_code_count[base.account_status.upcase] += 1
35
+ num_ssn += 1 if base.social_security_number
36
+ num_dob += 1 if base.date_of_birth
37
+ num_telephone += 1 if base.telephone_number
38
+ num_ecoa_code_z += 1 if base.ecoa_code == 'Z'
39
+ end
40
+
41
+ trailer = Records::TrailerSegment.new
42
+ trailer.total_base_records = @base_segments.size
43
+ trailer.total_status_code_df = status_code_count['DF']
44
+ trailer.total_status_code_df = status_code_count['DA']
45
+ trailer.total_status_code_df = status_code_count['05']
46
+ trailer.total_status_code_df = status_code_count['11']
47
+ trailer.total_status_code_df = status_code_count['13']
48
+ trailer.total_status_code_df = status_code_count['61']
49
+ trailer.total_status_code_df = status_code_count['62']
50
+ trailer.total_status_code_df = status_code_count['63']
51
+ trailer.total_status_code_df = status_code_count['64']
52
+ trailer.total_status_code_df = status_code_count['65']
53
+ trailer.total_status_code_df = status_code_count['71']
54
+ trailer.total_status_code_df = status_code_count['78']
55
+ trailer.total_status_code_df = status_code_count['80']
56
+ trailer.total_status_code_df = status_code_count['82']
57
+ trailer.total_status_code_df = status_code_count['83']
58
+ trailer.total_status_code_df = status_code_count['84']
59
+ trailer.total_status_code_df = status_code_count['88']
60
+ trailer.total_status_code_df = status_code_count['89']
61
+ trailer.total_status_code_df = status_code_count['93']
62
+ trailer.total_status_code_df = status_code_count['94']
63
+ trailer.total_status_code_df = status_code_count['95']
64
+ trailer.total_status_code_df = status_code_count['96']
65
+ trailer.total_status_code_df = status_code_count['97']
66
+ trailer.ecoa_code_z = num_ecoa_code_z
67
+ trailer.total_social_security_numbers = num_ssn
68
+ trailer.total_social_security_numbers_in_base = num_ssn
69
+ trailer.total_date_of_births = num_dob
70
+ trailer.total_date_of_births_in_base = num_dob
71
+ trailer.total_telephome_numbers = num_telephone
72
+ trailer
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,54 @@
1
+ module Metro2::Records
2
+ class BaseSegment < Record
3
+ @fields = []
4
+
5
+ numeric_const_field :record_descriptor_word, 4, Metro2::FIXED_LENGTH
6
+ alphanumeric_const_field :processing_indicator, 1, 1 # always 1
7
+ timestamp_field :time_stamp
8
+ numeric_field :correction_indicator, 1
9
+ alphanumeric_field :identification_number, 20
10
+ alphanumeric_field :cycle_number, 2
11
+ alphanumeric_field :consumer_account_number, 30
12
+ alphanumeric_field :portfolio_type, 1
13
+ alphanumeric_field :account_type, 2
14
+ date_field :date_opened
15
+ monetary_field :credit_limit
16
+ monetary_field :highest_credit_or_loan_amount
17
+ alphanumeric_field :terms_duration, 3
18
+ alphanumeric_field :terms_frequency, 1
19
+ monetary_field :scheduled_monthly_payment_amount
20
+ monetary_field :actual_payment_amount
21
+ alphanumeric_field :account_status, 2
22
+ alphanumeric_field :payment_rating, 1
23
+ alphanumeric_field :payment_history_profile, 24
24
+ alphanumeric_field :special_comment, 2
25
+ alphanumeric_field :compliance_condition_code, 2
26
+ monetary_field :current_balance
27
+ monetary_field :amount_past_due
28
+ monetary_field :original_charge_off_amount
29
+ date_field :account_information_date
30
+ date_field :first_delinquency_date
31
+ date_field :closed_date
32
+ date_field :last_payment_date
33
+ alphanumeric_field :interest_type_indicator, 1
34
+ alphanumeric_const_field :reserved, 16, nil # reserved - blank fill
35
+ alphanumeric_field :consumer_transaction_type, 1
36
+ alphanumeric_field :surname, 25, Metro2::ALPHANUMERIC_PLUS_DASH
37
+ alphanumeric_field :first_name, 20, Metro2::ALPHANUMERIC_PLUS_DASH
38
+ alphanumeric_field :middle_name, 20, Metro2::ALPHANUMERIC_PLUS_DASH
39
+ alphanumeric_field :generation_code, 1
40
+ numeric_field :social_security_number, 9
41
+ date_field :date_of_birth
42
+ numeric_field :telephone_number, 10
43
+ alphanumeric_field :ecoa_code, 1
44
+ alphanumeric_field :consumer_information_indicator, 2
45
+ alphanumeric_field :country_code, 2
46
+ alphanumeric_field :address_1, 32, Metro2::ALPHANUMERIC_PLUS_DOT_DASH_SLASH
47
+ alphanumeric_field :address_2, 32, Metro2::ALPHANUMERIC_PLUS_DOT_DASH_SLASH
48
+ alphanumeric_field :city, 20
49
+ alphanumeric_field :state, 2
50
+ alphanumeric_field :postal_code, 9
51
+ alphanumeric_field :address_indicator, 1
52
+ alphanumeric_field :residence_code, 1
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ module Metro2::Records
2
+ class HeaderSegment < Record
3
+ @fields = []
4
+
5
+ numeric_const_field :record_descriptor_word, 4, Metro2::FIXED_LENGTH
6
+ alphanumeric_const_field :record_identifier, 6, 'HEADER'
7
+ alphanumeric_field :cycle_number, 2
8
+ alphanumeric_field :innovis_program_identifier, 10
9
+ alphanumeric_field :equifax_program_identifier, 10
10
+ alphanumeric_field :experian_program_identifier, 5
11
+ alphanumeric_field :transunion_program_identifier, 10
12
+ date_field :activity_date
13
+ date_field :created_date
14
+ date_field :program_date
15
+ numeric_field :program_revision_date, 8
16
+ alphanumeric_field :reporter_name, 40
17
+ alphanumeric_field :reporter_address, 96, Metro2::ALPHANUMERIC_PLUS_DOT_DASH_SLASH
18
+ numeric_field :reporter_telephone_number, 10
19
+ alphanumeric_const_field :software_vendor_name, 40, 'Upstart Engineer metro 2 gem'
20
+ alphanumeric_const_field :software_version_number, 5, Metro2::version_string
21
+ alphanumeric_const_field :reserved, 156, nil
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Metro2
2
+ module Records
3
+ class Record
4
+ @fields = []
5
+
6
+ class << self
7
+ def fields
8
+ @fields
9
+ end
10
+ end
11
+
12
+ extend(Fields)
13
+
14
+ def to_metro2
15
+ self.class.fields.collect { |f| send("#{f}_to_metro2") }.join
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+ module Metro2::Records
2
+ class TrailerSegment < Record
3
+ @fields = []
4
+
5
+ numeric_const_field :record_descriptor_word, 4, Metro2::FIXED_LENGTH
6
+ alphanumeric_const_field :record_identifier, 7, 'TRAILER'
7
+ numeric_field :total_base_records, 9
8
+ alphanumeric_const_field :reserved, 9, nil
9
+ numeric_field :total_status_code_df, 9
10
+ numeric_field :total_j1_segments, 9
11
+ numeric_field :total_j2_segments, 9
12
+ numeric_field :block_count, 9
13
+ numeric_field :total_status_code_da, 9
14
+ numeric_field :total_status_code_05, 9
15
+ numeric_field :total_status_code_11, 9
16
+ numeric_field :total_status_code_13, 9
17
+ numeric_field :total_status_code_61, 9
18
+ numeric_field :total_status_code_62, 9
19
+ numeric_field :total_status_code_63, 9
20
+ numeric_field :total_status_code_64, 9
21
+ numeric_field :total_status_code_65, 9
22
+ numeric_field :total_status_code_71, 9
23
+ numeric_field :total_status_code_78, 9
24
+ numeric_field :total_status_code_80, 9
25
+ numeric_field :total_status_code_82, 9
26
+ numeric_field :total_status_code_83, 9
27
+ numeric_field :total_status_code_84, 9
28
+ numeric_field :total_status_code_88, 9
29
+ numeric_field :total_status_code_89, 9
30
+ numeric_field :total_status_code_93, 9
31
+ numeric_field :total_status_code_94, 9
32
+ numeric_field :total_status_code_95, 9
33
+ numeric_field :total_status_code_96, 9
34
+ numeric_field :total_status_code_97, 9
35
+ numeric_field :ecoa_code_z, 9
36
+ numeric_field :total_n1_segments, 9
37
+ numeric_field :total_k1_segments, 9
38
+ numeric_field :total_k2_segments, 9
39
+ numeric_field :total_k3_segments, 9
40
+ numeric_field :total_k4_segments, 9
41
+ numeric_field :total_l1_segments, 9
42
+ numeric_field :total_social_security_numbers, 9
43
+ numeric_field :total_social_security_numbers_in_base, 9
44
+ numeric_field :total_social_security_numbers_in_j1, 9
45
+ numeric_field :total_social_security_numbers_in_j2, 9
46
+ numeric_field :total_date_of_births, 9
47
+ numeric_field :total_date_of_births_in_base, 9
48
+ numeric_field :total_date_of_births_in_j1, 9
49
+ numeric_field :total_date_of_births_in_j2, 9
50
+ numeric_field :total_telephome_numbers, 9
51
+ alphanumeric_const_field :reserved_2, 19, nil
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ module Metro2
2
+ VERSION = "1.0.0"
3
+
4
+ def self.version_string
5
+ str = VERSION.split('.')
6
+ str[0].rjust(2, '0') + str[1].ljust(2, '0') + str[2]
7
+ end
8
+ end
data/metro_2.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'metro_2/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "metro_2"
8
+ spec.version = Metro2::VERSION
9
+ spec.authors = ["Brian Mascarenhas"]
10
+ spec.email = ["brian@upstart.com"]
11
+ spec.summary = "Create Metro 2 format files for credit reporting"
12
+ spec.description = "Create Metro 2 format files for credit reporting"
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 2.13"
24
+ end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metro2::Records::BaseSegment do
4
+ before do
5
+ @base = Metro2::Records::BaseSegment.new
6
+ @base.time_stamp = Time.new(2014, 9, 15, 17, 7, 45)
7
+ @base.identification_number = 'REPORTERXYZ'
8
+ @base.cycle_number = 1
9
+ @base.consumer_account_number = 'ABC123'
10
+ @base.portfolio_type = 'I'
11
+ @base.account_type = '01'
12
+ @base.date_opened = Date.new(2013, 4, 1)
13
+ @base.highest_credit_or_loan_amount = 25000
14
+ @base.terms_duration = 36
15
+ @base.terms_frequency = 'M'
16
+ @base.scheduled_monthly_payment_amount = 817.8
17
+ @base.actual_payment_amount = 817.8
18
+ @base.account_status = '11'
19
+ @base.payment_history_profile = '00000000000000000BBBBBBB'
20
+ @base.current_balance = 11111
21
+ @base.account_information_date = Date.new(2014, 9, 1)
22
+ @base.last_payment_date = Date.new(2014, 8, 1)
23
+ @base.interest_type_indicator = 'F'
24
+ @base.surname = 'Simpson'
25
+ @base.first_name = 'Homer'
26
+ @base.middle_name = 'Jay'
27
+ @base.social_security_number = '333224444'
28
+ @base.date_of_birth = Date.new(1987, 4, 19)
29
+ @base.telephone_number = '5555555555'
30
+ @base.ecoa_code = 1
31
+ @base.country_code = 'US'
32
+ @base.address_1 = '742 Evergreen Terrace'
33
+ @base.city = 'Springfield'
34
+ @base.state = 'IL'
35
+ @base.postal_code = '54321'
36
+ @base.address_indicator = 'N'
37
+ @base.residence_code = 'O'
38
+ end
39
+
40
+ context '#to_metro2' do
41
+ it 'should generate base segment string' do
42
+ exp = [
43
+ '0426',
44
+ '1',
45
+ '09152014170745',
46
+ '0',
47
+ 'REPORTERXYZ'.ljust(20, ' '),
48
+ '1 ',
49
+ 'ABC123'.ljust(30, ' '),
50
+ 'I',
51
+ '01',
52
+ '04012013',
53
+ '0' * 9,
54
+ '000025000',
55
+ '36 ',
56
+ 'M',
57
+ '000000817',
58
+ '000000817',
59
+ '11',
60
+ ' ',
61
+ '00000000000000000BBBBBBB',
62
+ ' ',
63
+ ' ',
64
+ '000011111',
65
+ '000000000',
66
+ '000000000',
67
+ '09012014',
68
+ '00000000',
69
+ '00000000',
70
+ '08012014',
71
+ 'F',
72
+ ' ' * 16,
73
+ ' ',
74
+ 'Simpson'.ljust(25, ' '),
75
+ 'Homer'.ljust(20, ' '),
76
+ 'Jay'.ljust(20, ' '),
77
+ ' ',
78
+ '333224444',
79
+ '04191987',
80
+ '5555555555',
81
+ '1',
82
+ ' ',
83
+ 'US',
84
+ '742 Evergreen Terrace'.ljust(32, ' '),
85
+ ' ' * 32,
86
+ 'Springfield'.ljust(20, ' '),
87
+ 'IL',
88
+ '54321 ',
89
+ 'N',
90
+ 'O',
91
+ ''
92
+ ]
93
+ base_str = @base.to_metro2
94
+ expect(base_str).to eql(exp.join(''))
95
+ expect(base_str.size).to eql(Metro2::FIXED_LENGTH)
96
+ end
97
+ end
98
+
99
+ context 'alphanumerics' do
100
+ describe 'alphanumeric with symbols' do
101
+ it 'should raise an error' do
102
+ @base.surname = 'B@d name'
103
+ expect{@base.surname_to_metro2}.to raise_error(ArgumentError)
104
+ end
105
+ end
106
+
107
+ describe 'alphanumeric with dashes' do
108
+ it 'should not raise an error' do
109
+ @base.surname = 'Name A-ok'
110
+ expect{@base.surname_to_metro2}.not_to raise_error
111
+ end
112
+ end
113
+
114
+ describe 'really long alphanumeric' do
115
+ it 'should be truncated' do
116
+ @base.surname = 'Veryveryveryveryverylongname'
117
+ expect(@base.surname_to_metro2).to eql('Veryveryveryveryverylongn')
118
+ end
119
+ end
120
+
121
+ describe 'alphanumeric with dots and slashes' do
122
+ it 'should be truncated' do
123
+ @base.address_1 = 'Test Address /1.'
124
+ expect(@base.address_1_to_metro2).to eql('Test Address /1.'.ljust(32, ' '))
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'numerics' do
130
+ describe 'numeric with alphas' do
131
+ it 'should raise an error' do
132
+ @base.telephone_number = 'abc5555555'
133
+ expect{@base.telephone_number_to_metro2}.to raise_error(ArgumentError)
134
+ end
135
+ end
136
+
137
+ describe 'numeric with symbols' do
138
+ it 'should raise an error' do
139
+ @base.telephone_number = '+555555555'
140
+ expect{@base.telephone_number_to_metro2}.to raise_error(ArgumentError)
141
+ end
142
+ end
143
+
144
+ describe 'numeric that is too long' do
145
+ it 'should raise an error' do
146
+ @base.telephone_number = '12345678901'
147
+ expect{@base.telephone_number_to_metro2}.to raise_error(ArgumentError)
148
+ end
149
+ end
150
+ end
151
+
152
+ context 'monetary field' do
153
+ describe 'more than one billion' do
154
+ it 'should fill with all 9s' do
155
+ @base.credit_limit = '1000000001'
156
+ expect(@base.credit_limit_to_metro2).to eql('999999999')
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metro2::Records::HeaderSegment do
4
+ before(:each) do
5
+ @header = Metro2::Records::HeaderSegment.new
6
+ @header.cycle_number = 15
7
+ @header.equifax_program_identifier = 'EFAXID'
8
+ @header.transunion_program_identifier = 'TRANSUNION'
9
+ @header.activity_date = Date.new(2014,9,14)
10
+ @header.created_date = Date.new(2014,9,15)
11
+ @header.program_date = Date.new(2014,9,1)
12
+ @header.program_revision_date = '01'
13
+ @header.reporter_name = 'Credit Reporter'
14
+ @header.reporter_address = '123 Report Dr Address CA 91111'
15
+ @header.reporter_telephone_number = '5555555555'
16
+ end
17
+
18
+ describe '#to_metro2' do
19
+ it 'should generate header segment string' do
20
+ exp = [
21
+ '0426',
22
+ 'HEADER',
23
+ '15',
24
+ ' ',
25
+ 'EFAXID ',
26
+ ' ',
27
+ 'TRANSUNION',
28
+ '09142014',
29
+ '09152014',
30
+ '09012014',
31
+ '00000001',
32
+ 'Credit Reporter'.ljust(40, ' '),
33
+ '123 Report Dr Address CA 91111'.ljust(96, ' '),
34
+ '5555555555',
35
+ 'Upstart Engineer metro 2 gem'.ljust(40, ' '),
36
+ '01000',
37
+ ' ' * 156
38
+ ]
39
+ header_str = @header.to_metro2
40
+ expect(header_str).to eql(exp.join(''))
41
+ expect(header_str.size).to eql(Metro2::FIXED_LENGTH)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metro2::Records::TrailerSegment do
4
+ before(:each) do
5
+ @trailer = Metro2::Records::TrailerSegment.new
6
+ @trailer.total_base_records = 1
7
+ @trailer.total_status_code_11 = 1
8
+ @trailer.total_social_security_numbers = 1
9
+ @trailer.total_social_security_numbers_in_base = 1
10
+ @trailer.total_date_of_births = 1
11
+ @trailer.total_date_of_births_in_base = 1
12
+ @trailer.total_telephome_numbers = 1
13
+ end
14
+
15
+ describe '#to_metro2' do
16
+ it 'should generate trailer segment string' do
17
+ exp = [
18
+ '0426',
19
+ 'TRAILER',
20
+ '000000001',
21
+ ' ' * 9,
22
+ '0' * 9,
23
+ '0' * 9,
24
+ '0' * 9,
25
+ '0' * 9,
26
+ '0' * 9,
27
+ '0' * 9,
28
+ '000000001',
29
+ '0' * 9,
30
+ '0' * 9,
31
+ '0' * 9,
32
+ '0' * 9,
33
+ '0' * 9,
34
+ '0' * 9,
35
+ '0' * 9,
36
+ '0' * 9,
37
+ '0' * 9,
38
+ '0' * 9,
39
+ '0' * 9,
40
+ '0' * 9,
41
+ '0' * 9,
42
+ '0' * 9,
43
+ '0' * 9,
44
+ '0' * 9,
45
+ '0' * 9,
46
+ '0' * 9,
47
+ '0' * 9,
48
+ '0' * 9,
49
+ '0' * 9,
50
+ '0' * 9,
51
+ '0' * 9,
52
+ '0' * 9,
53
+ '0' * 9,
54
+ '0' * 9,
55
+ '000000001',
56
+ '000000001',
57
+ '0' * 9,
58
+ '0' * 9,
59
+ '000000001',
60
+ '000000001',
61
+ '0' * 9,
62
+ '0' * 9,
63
+ '000000001',
64
+ ' ' * 19
65
+ ]
66
+ trailer_str = @trailer.to_metro2
67
+ expect(trailer_str).to eql(exp.join(''))
68
+ expect(trailer_str.size).to eql(Metro2::FIXED_LENGTH)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ Bundler.require
3
+
4
+ require 'metro_2'
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metro_2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Mascarenhas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.13'
55
+ description: Create Metro 2 format files for credit reporting
56
+ email:
57
+ - brian@upstart.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/metro_2.rb
69
+ - lib/metro_2/fields.rb
70
+ - lib/metro_2/metro2_file.rb
71
+ - lib/metro_2/records/base_segment.rb
72
+ - lib/metro_2/records/header_segment.rb
73
+ - lib/metro_2/records/record.rb
74
+ - lib/metro_2/records/trailer_segment.rb
75
+ - lib/metro_2/version.rb
76
+ - metro_2.gemspec
77
+ - spec/records/base_segment_spec.rb
78
+ - spec/records/header_segment_spec.rb
79
+ - spec/records/trailer_segment_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: ''
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.4.1
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Create Metro 2 format files for credit reporting
105
+ test_files:
106
+ - spec/records/base_segment_spec.rb
107
+ - spec/records/header_segment_spec.rb
108
+ - spec/records/trailer_segment_spec.rb
109
+ - spec/spec_helper.rb