hippo 0.2.1 → 0.2.2
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.
- data/CHANGELOG +16 -0
- data/README.md +208 -176
- data/lib/hippo/field.rb +59 -23
- data/lib/hippo/parser/transaction_set.rb +5 -3
- data/lib/hippo/parser.rb +15 -5
- data/lib/hippo/segments/base.rb +20 -2
- data/lib/hippo/transaction_sets/base.rb +20 -1
- data/lib/hippo/transaction_sets/repeating_component.rb +14 -2
- data/lib/hippo/version.rb +1 -1
- data/samples/200823.EDI +22 -0
- data/test/test_segments_base.rb +6 -0
- metadata +7 -6
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
0.2.2 - 2012/01/25
|
2
|
+
* Fix issue with segment parsing. Make sure to set each segments parent
|
3
|
+
properly once we identify that it belongs in the particular transaction
|
4
|
+
set. (This allows traversing via segment.parent.parent.)
|
5
|
+
* Add ancestors method to TransactionSet::Base,TransactionSet::RepeatingComponent,
|
6
|
+
and Segments::Base which returns an array containing each ancestor (parent)
|
7
|
+
from the current segment to the outermost containing element.
|
8
|
+
* Fix issue causing non-required fields to be blank filled.
|
9
|
+
* Refactor Field#string_value and Field#formatted_value to handle more situations.
|
10
|
+
* Add segments method to TransactionSet::Base,TransactionSet::RepeatingComponent,
|
11
|
+
and Segments::Base which returns a flattened array of segments within each
|
12
|
+
container. (Segment::Base simply returns [self].)
|
13
|
+
* Refactor TransactionSet::Base#segment_count to use new segments array.
|
14
|
+
* Add shortcut methods to Hippo::Parser for parse_file and parse_string. Now they
|
15
|
+
can be called without creating an instance of Parser. (i.e Hippo::Parser.parse_string(s))
|
16
|
+
* Fix issue causing access to empty composite fields to throw an error ([] for NilClass).
|
1
17
|
0.2.1 - 2012/01/21
|
2
18
|
* Fix issue preventing fixed width segments from printing
|
3
19
|
properly. (Non set fields on ISA were empty not padded.)
|
data/README.md
CHANGED
@@ -40,45 +40,45 @@ guide please review [test/test_hipaa_837.rb](/promedical/hippo/blob/master/test/
|
|
40
40
|
Below is a small sample of how to create a transaction set.
|
41
41
|
|
42
42
|
```ruby
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
43
|
+
ts = Hippo::TransactionSets::HIPAA_837::Base.new
|
44
|
+
|
45
|
+
ts.ST do |st|
|
46
|
+
st.TransactionSetControlNumber = '0021'
|
47
|
+
st.ImplementationConventionReference = '005010X222A1'
|
48
|
+
end
|
49
|
+
|
50
|
+
ts.BHT do |bht|
|
51
|
+
bht.TransactionSetPurposeCode = '00'
|
52
|
+
bht.ReferenceIdentification = '244579'
|
53
|
+
bht.Date = '20061015'
|
54
|
+
bht.Time = '1023'
|
55
|
+
bht.TransactionTypeCode = 'CH'
|
56
|
+
end
|
57
|
+
|
58
|
+
ts.L1000A do |l1000a|
|
59
|
+
l1000a.NM1 do |nm1|
|
60
|
+
nm1.EntityTypeQualifier = '2'
|
61
|
+
nm1.NameLastOrOrganizationName = 'PREMIER BILLING SERVICE'
|
62
|
+
nm1.IdentificationCode = 'TGJ23'
|
63
|
+
end
|
64
|
+
|
65
|
+
l1000a.PER do |per|
|
66
|
+
per.Name = 'JERRY'
|
67
|
+
per.CommunicationNumberQualifier_01 = 'TE'
|
68
|
+
per.CommunicationNumber_01 = '3055552222'
|
69
|
+
per.CommunicationNumberQualifier_02 = 'EX'
|
70
|
+
per.CommunicationNumber_02 = '231'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
puts ts.to_s
|
75
|
+
|
76
|
+
# Below is the output of ts.to_s (split onto separate lines for readability)
|
77
|
+
#
|
78
|
+
# ST*837*0021*005010X222A1~
|
79
|
+
# BHT*0019*00*244579*20061015*1023*CH~
|
80
|
+
# NM1*41*2*PREMIER BILLING SERVICE*****46*TGJ23~
|
81
|
+
# PER*IC*JERRY*TE*3055552222*EX*231~
|
82
82
|
```
|
83
83
|
|
84
84
|
Transaction Set/Loop and Segment DSL
|
@@ -86,76 +86,76 @@ Transaction Set/Loop and Segment DSL
|
|
86
86
|
Transaction Sets/Loops and Segments are defined with a very straight forward DSL.
|
87
87
|
|
88
88
|
```ruby
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
field :name => 'Field7'
|
123
|
-
end
|
89
|
+
module Hippo::Segments
|
90
|
+
class TestSimpleSegment < Hippo::Segments::Base
|
91
|
+
segment_identifier 'TSS'
|
92
|
+
|
93
|
+
field :name => 'Field1'
|
94
|
+
field :name => 'Field2'
|
95
|
+
field :name => 'Field3'
|
96
|
+
field :name => 'Field4'
|
97
|
+
field :name => 'CommonName'
|
98
|
+
field :name => 'CommonName'
|
99
|
+
field :name => 'DateField', :datatype => :date
|
100
|
+
field :name => 'TimeField', :datatype => :time
|
101
|
+
field :name => 'IntegerField', :datatype => :integer
|
102
|
+
field :name => 'DecimalField', :datatype => :decimal
|
103
|
+
end
|
104
|
+
|
105
|
+
class TestCompoundSegment < Hippo::Segments::Base
|
106
|
+
segment_identifier 'TCS'
|
107
|
+
|
108
|
+
composite_field 'CompositeField' do
|
109
|
+
field :name => 'Field1'
|
110
|
+
field :name => 'Field2'
|
111
|
+
field :name => 'Field3'
|
112
|
+
field :name => 'CompositeCommonName'
|
113
|
+
end
|
114
|
+
|
115
|
+
composite_field 'CompositeField' do
|
116
|
+
field :name => 'Field4'
|
117
|
+
field :name => 'Field5'
|
118
|
+
field :name => 'Field6'
|
119
|
+
field :name => 'CompositeCommonName'
|
124
120
|
end
|
125
121
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
122
|
+
field :name => 'Field7'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Hippo::TransactionSets
|
127
|
+
module Test
|
128
|
+
class Base < Hippo::TransactionSets::Base
|
129
|
+
|
130
|
+
segment Hippo::Segments::TestSimpleSegment,
|
131
|
+
:name => 'Test Simple Segment #1',
|
132
|
+
:minimum => 1,
|
133
|
+
:maximum => 5,
|
134
|
+
:position => 50,
|
135
|
+
:defaults => {
|
136
|
+
'TSS01' => 'Blah'
|
137
|
+
}
|
138
|
+
|
139
|
+
segment Hippo::Segments::TestCompoundSegment,
|
140
|
+
:name => 'Test Compound Segment #2',
|
141
|
+
:minimum => 1,
|
142
|
+
:maximum => 1,
|
143
|
+
:position => 100,
|
144
|
+
:defaults => {
|
145
|
+
'Field7' => 'Preset Field 7'
|
146
|
+
}
|
147
|
+
|
148
|
+
segment Hippo::Segments::TestSimpleSegment,
|
149
|
+
:name => 'Test Simple Segment #3',
|
150
|
+
:minimum => 1,
|
151
|
+
:maximum => 1,
|
152
|
+
:position => 50,
|
153
|
+
:defaults => {
|
154
|
+
'TSS01' => 'Last Segment'
|
155
|
+
}
|
158
156
|
end
|
157
|
+
end
|
158
|
+
end
|
159
159
|
```
|
160
160
|
|
161
161
|
Quick Guide to Populating a Transaction Set
|
@@ -166,14 +166,14 @@ the fields.
|
|
166
166
|
To create a transaction set simple choose the set you want and call new on it's Base class.
|
167
167
|
|
168
168
|
```ruby
|
169
|
-
|
169
|
+
ts = Hippo::TransactionSets::Test::Base.new
|
170
170
|
```
|
171
171
|
|
172
172
|
The segments can be accessed directly from the created transaction set using the segment
|
173
173
|
identifier.
|
174
174
|
|
175
175
|
```ruby
|
176
|
-
|
176
|
+
ts.TCS
|
177
177
|
```
|
178
178
|
|
179
179
|
Since the TSS segment can be repeated we must call #build to generate a new
|
@@ -181,13 +181,13 @@ instance for each repeat. (You will be returned the first instance each time if
|
|
181
181
|
do not call #build.)
|
182
182
|
|
183
183
|
```ruby
|
184
|
-
|
184
|
+
tss = ts.TSS.build
|
185
185
|
|
186
|
-
|
186
|
+
# or
|
187
187
|
|
188
|
-
|
189
|
-
|
190
|
-
|
188
|
+
ts.TSS.build do |tss|
|
189
|
+
# do something here...
|
190
|
+
end
|
191
191
|
```
|
192
192
|
|
193
193
|
The code above produces the following string output (notice how the values from
|
@@ -195,7 +195,7 @@ The code above produces the following string output (notice how the values from
|
|
195
195
|
that the segments were declared):
|
196
196
|
|
197
197
|
```ruby
|
198
|
-
|
198
|
+
# ts.to_s => 'TSS*Blah~TCS***Preset Field 7~'
|
199
199
|
```
|
200
200
|
|
201
201
|
You can set the field values on a given segment a few different ways.
|
@@ -204,18 +204,18 @@ First you must access the segment that the field belongs to. You can
|
|
204
204
|
either access the fields directly on the segment or use the block syntax.
|
205
205
|
|
206
206
|
```ruby
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
207
|
+
# this is one way to populate the fields
|
208
|
+
ts.TCS.Field1 = 'Foo'
|
209
|
+
ts.TSS.Field2 = 'Bar'
|
210
|
+
|
211
|
+
# this is another way
|
212
|
+
ts.TCS do |tcs|
|
213
|
+
tcs.Field1 = 'Foo'
|
214
|
+
end
|
215
|
+
|
216
|
+
ts.TSS do |tss|
|
217
|
+
tss.Field2 = 'Bar'
|
218
|
+
end
|
219
219
|
```
|
220
220
|
|
221
221
|
Once you have access to the segment you can set the field values by either
|
@@ -224,12 +224,12 @@ field name is used more than once in a segment or if you are accessing a
|
|
224
224
|
composite field you can optionally pass the index of the field to access.
|
225
225
|
|
226
226
|
```ruby
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
227
|
+
ts.TCS do |tcs|
|
228
|
+
tcs.Field1 = 'Foo' # use the field name
|
229
|
+
tcs.TCS01_01 = 'Bar' # use shorthand notation:
|
230
|
+
# TCS01 refers to the first field within the current segment
|
231
|
+
# _01 refers to the first field within the composite field
|
232
|
+
end
|
233
233
|
```
|
234
234
|
|
235
235
|
If you read the transaction set declaration from above you will notice that the TSS segment
|
@@ -239,11 +239,11 @@ but if you need to access the second instance of TSS in the transaction set you
|
|
239
239
|
TSS_02 instead.
|
240
240
|
|
241
241
|
```ruby
|
242
|
-
|
243
|
-
|
244
|
-
|
242
|
+
ts.TCS.Field1 = 'Foo'
|
243
|
+
ts.TSS.Field2 = 'Bar'
|
244
|
+
ts.TSS_02.Field2 = 'Baz'
|
245
245
|
|
246
|
-
|
246
|
+
# ts.to_s => 'TSS*Blah*Bar~TCS*Foo**Preset Field 7~TSS*Last Segment*Baz~'
|
247
247
|
```
|
248
248
|
|
249
249
|
Obviously, this could get somewhat tedious when operating on a TransactionSet with many segments
|
@@ -252,25 +252,25 @@ on the name provided in the TransactionSet definition. You can either pass the
|
|
252
252
|
a Regexp to search with.
|
253
253
|
|
254
254
|
```ruby
|
255
|
-
|
256
|
-
|
257
|
-
|
255
|
+
ts.find_by_name('Test Simple Segment #1') do |tss|
|
256
|
+
tss.Field2 = 'Baz'
|
257
|
+
end
|
258
258
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
259
|
+
# which is essentially equivilent (because the search occurs in order of declaration)
|
260
|
+
ts.find_by_name(/Segment/) do |tss|
|
261
|
+
tss.Field2 = 'Baz'
|
262
|
+
end
|
263
263
|
|
264
|
-
|
264
|
+
# ts.to_s => 'TSS*Blah*Baz~'
|
265
265
|
```
|
266
266
|
|
267
267
|
The same technique can be used to reference fields within a segment that have the same name.
|
268
268
|
|
269
269
|
```ruby
|
270
|
-
|
271
|
-
|
270
|
+
ts.TSS.CommonName = 'Value1'
|
271
|
+
ts.TSS.CommonName_02 = 'Value2'
|
272
272
|
|
273
|
-
|
273
|
+
# ts.to_s => 'TSS*Blah*Bar***Value1*Value2~TCS*Foo**Preset Field 7~TSS*Last Segment*Baz~'
|
274
274
|
```
|
275
275
|
|
276
276
|
Type Conversion
|
@@ -286,40 +286,72 @@ with a valid value for that particular data type.
|
|
286
286
|
Just a few examples using the type conversion:
|
287
287
|
|
288
288
|
```ruby
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
289
|
+
seg = Hippo::Segments::TSS.new # Please review definition from above.
|
290
|
+
|
291
|
+
# Date fields:
|
292
|
+
seg.DateField = Date.new(2012, 01, 20)
|
293
|
+
seg.DateField = "20120120"
|
294
|
+
seg.DateField = Time.new(2012, 01, 20, 10, 15, 20)
|
295
|
+
|
296
|
+
# all of these formats result in the same internal representation
|
297
|
+
puts seg.DateField.inspect # => #<Date: 2012-01-20 ((2455947j,0s,0n),+0s,2299161j)>
|
298
|
+
|
299
|
+
# To set the field back to a blank/empty value simply assign it to nil
|
300
|
+
seg.DateField = nil
|
301
|
+
|
302
|
+
# Time fields:
|
303
|
+
seg.TimeField = "0120" # => 1:20 am (HHMM)
|
304
|
+
seg.TimeField = "012023" # => 1:20:23 am (HHMMSS)
|
305
|
+
seg.TimeField = "01202322" # => 1:20:23.22 am (HHMMSSDD)
|
306
|
+
seg.TimeField = Time.now
|
307
|
+
|
308
|
+
# Integer fields:
|
309
|
+
seg.IntegerField = "10" # => 10
|
310
|
+
seg.IntegerField = 10 # => 10
|
311
|
+
seg.IntegerField = "10blah" # => 10
|
312
|
+
|
313
|
+
# Decimal fields:
|
314
|
+
seg.DecimalField = "123.45" # => #<BigDecimal:7fe83c315750,'0.12345E3',18(18)>
|
315
|
+
seg.DecimalField = 123.45 # => #<BigDecimal:7fe83c315750,'0.12345E3',18(18)>
|
316
|
+
seg.DecimalField = 123 # => #<BigDecimal:7fe83b9dd4f8,'0.123E3',9(18)>
|
317
|
+
seg.DecimalField = 123.0 # => #<BigDecimal:7fe83b9dd4f8,'0.123E3',9(18)>
|
318
318
|
```
|
319
319
|
|
320
320
|
__Please Note__: Due to issues with floating point representation of currency values we have
|
321
321
|
chosen to use BigDecimal internally to store all fields with a decimal datatype.
|
322
322
|
|
323
|
+
Hierarchy Traversal
|
324
|
+
-------------------
|
325
|
+
|
326
|
+
There are times with a given transaction set that you may start with a given segment but need
|
327
|
+
to traverse up to a higher level loop/transaction set container. The best example of this is
|
328
|
+
when dealing with 997 or 999 acknowledgments. If there are errors in your original transmission
|
329
|
+
they are reported on the 997 and 999 as the segment number in error. We need to then take that
|
330
|
+
errored segment and figure out more context.
|
331
|
+
|
332
|
+
The first thing we have to do is find the segment in error. The 999 contains this in the IK3
|
333
|
+
segment of the 2100 - AK2/IK3 loop. Then we need to access those segments in the original
|
334
|
+
transmitted file. Finally, we need to access an ancestor that gives enough context to resolve
|
335
|
+
the error.
|
336
|
+
|
337
|
+
Here is a quick example:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
ts_999 = Hippo::Parser.parse_file('location/to/999/file.999')
|
341
|
+
ts_837 = Hippo::Parser.parse_file('location/to/837/file.837')
|
342
|
+
|
343
|
+
# first lets get the index of all of the errored segments
|
344
|
+
error_indexes = ts_999.L2000AK2.map{|l| l.L2100AK2.map{|m| m.IK3.IK303}}.flatten
|
345
|
+
# or
|
346
|
+
error_indexes = ts_999.segments.select{|s| s.class.to_s =~ /IK3/}.collect{|s| s.IK303}
|
347
|
+
|
348
|
+
# now lets find those segments in the file being confirmed
|
349
|
+
errored_segments = ts_837.segments.values_at(error_indexes)
|
350
|
+
|
351
|
+
# and finally lets find the claim that they belong to
|
352
|
+
errored_claims = errored_segments.collect{|s| s.ancestors.select{|a| a.class.to_s =~ /L2000B/}}.flatten
|
353
|
+
```
|
354
|
+
|
323
355
|
For more example please review the test suite.
|
324
356
|
|
325
357
|
License
|
data/lib/hippo/field.rb
CHANGED
@@ -21,7 +21,7 @@ module Hippo
|
|
21
21
|
when :decimal then BigDecimal.new(value.to_s)
|
22
22
|
when :date then parse_date(value)
|
23
23
|
when :time then parse_time(value)
|
24
|
-
else value
|
24
|
+
else parse_string(value)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -31,31 +31,51 @@ module Hippo
|
|
31
31
|
case datatype
|
32
32
|
when :binary then value
|
33
33
|
when :integer then value.to_s.rjust(minimum, '0')
|
34
|
-
when :decimal then
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
34
|
+
when :decimal then generate_decimal(value)
|
35
|
+
when :date then generate_date(value)
|
36
|
+
when :time then generate_time(value)
|
37
|
+
else generate_string(value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_string(value)
|
42
|
+
if required
|
43
|
+
value.to_s.ljust(minimum)
|
44
|
+
else
|
45
|
+
value.to_s
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_string(value)
|
50
|
+
if value.to_s.empty? && !required
|
51
|
+
nil
|
52
|
+
else
|
53
|
+
value.to_s.strip
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_decimal(value)
|
58
|
+
value ||= BigDecimal.new('0')
|
59
|
+
|
60
|
+
value.to_s('F').sub(/\.0\z/,'').rjust(minimum, '0')
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_time(value)
|
64
|
+
value ||= Time.now
|
65
|
+
|
66
|
+
if maximum == 4 || value.sec == 0
|
67
|
+
value.strftime('%H%M')
|
68
|
+
else
|
69
|
+
value.strftime('%H%M%S')
|
55
70
|
end
|
56
71
|
end
|
57
72
|
|
58
73
|
def parse_time(value)
|
74
|
+
if value == ''
|
75
|
+
invalid! if required
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
|
59
79
|
case value.class.to_s
|
60
80
|
when 'Time' then value
|
61
81
|
when 'String'
|
@@ -73,7 +93,22 @@ module Hippo
|
|
73
93
|
invalid!
|
74
94
|
end
|
75
95
|
|
96
|
+
def generate_date(value)
|
97
|
+
value ||= Date.today
|
98
|
+
|
99
|
+
if maximum == 6
|
100
|
+
value.strftime('%y%m%d')
|
101
|
+
else
|
102
|
+
value.strftime('%Y%m%d')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
76
106
|
def parse_date(value)
|
107
|
+
if value == ''
|
108
|
+
invalid! if required
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
|
77
112
|
case value.class.to_s
|
78
113
|
when "Date" then value
|
79
114
|
when "Time" then value.to_date
|
@@ -81,7 +116,8 @@ module Hippo
|
|
81
116
|
format = case value
|
82
117
|
when /\A\d{6}\z/ then '%y%m%d'
|
83
118
|
when /\A\d{8}\z/ then '%Y%m%d'
|
84
|
-
else
|
119
|
+
else
|
120
|
+
invalid!
|
85
121
|
end
|
86
122
|
|
87
123
|
Date.parse(value, format)
|
@@ -17,7 +17,7 @@ module Hippo
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def parsed_segments
|
21
21
|
@segments ||= @unparsed_data.split(@segment_separator).collect do |segment_string|
|
22
22
|
segment = Hippo::Segments.const_get(segment_string.split(@field_separator).first).new(:parent => self)
|
23
23
|
|
@@ -25,14 +25,16 @@ module Hippo
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def read(input)
|
28
|
+
def read(input = nil)
|
29
|
+
input ||= ''
|
30
|
+
|
29
31
|
@unparsed_data = input.gsub(/[\a\e\f\n\r\t\v]/,'')
|
30
32
|
parse_separators(@unparsed_data)
|
31
33
|
end
|
32
34
|
|
33
35
|
def parse(input)
|
34
36
|
read(input)
|
35
|
-
populate(
|
37
|
+
populate(parsed_segments)
|
36
38
|
self
|
37
39
|
end
|
38
40
|
end
|
data/lib/hippo/parser.rb
CHANGED
@@ -6,6 +6,16 @@ module Hippo
|
|
6
6
|
include Hippo::Separator
|
7
7
|
include TransactionSet
|
8
8
|
|
9
|
+
def self.parse_file(input)
|
10
|
+
parser = new
|
11
|
+
parser.parse_string(input)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.parse_string(input)
|
15
|
+
parser = new
|
16
|
+
parser.parse_string(input)
|
17
|
+
end
|
18
|
+
|
9
19
|
def initialize
|
10
20
|
super
|
11
21
|
end
|
@@ -13,10 +23,10 @@ module Hippo
|
|
13
23
|
def initialize_transaction_set(index)
|
14
24
|
{
|
15
25
|
:segments => [],
|
16
|
-
:ISA => find_first_segment(
|
17
|
-
:GS => find_first_segment(
|
18
|
-
:GE => find_first_segment(
|
19
|
-
:IEA => find_first_segment(
|
26
|
+
:ISA => find_first_segment(parsed_segments[0,index + 1], 'ISA', true),
|
27
|
+
:GS => find_first_segment(parsed_segments[0,index + 1], 'GS', true),
|
28
|
+
:GE => find_first_segment(parsed_segments[index + 1, parsed_segments.length - index + 1], 'GE'),
|
29
|
+
:IEA => find_first_segment(parsed_segments[index + 1, parsed_segments.length - index + 1], 'IEA')
|
20
30
|
}
|
21
31
|
end
|
22
32
|
|
@@ -24,7 +34,7 @@ module Hippo
|
|
24
34
|
raw_transaction_sets = []
|
25
35
|
inside_transaction = false
|
26
36
|
|
27
|
-
|
37
|
+
parsed_segments.each_with_index do |segment, index|
|
28
38
|
if segment.identifier == 'ST'
|
29
39
|
raw_transaction_sets << initialize_transaction_set(index)
|
30
40
|
|
data/lib/hippo/segments/base.rb
CHANGED
@@ -49,6 +49,7 @@ module Hippo::Segments
|
|
49
49
|
|
50
50
|
def segment_identifier(id)
|
51
51
|
@identifier = id
|
52
|
+
@fields = []
|
52
53
|
end
|
53
54
|
|
54
55
|
def segment_fixed_width
|
@@ -58,11 +59,27 @@ module Hippo::Segments
|
|
58
59
|
|
59
60
|
attr_accessor :values, :parent
|
60
61
|
|
61
|
-
|
62
|
+
def segments
|
63
|
+
[self]
|
64
|
+
end
|
65
|
+
|
62
66
|
def segment_count
|
63
|
-
|
67
|
+
segments.count
|
64
68
|
end
|
65
69
|
|
70
|
+
def ancestors
|
71
|
+
if parent
|
72
|
+
parent.ancestors.flatten
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_ary
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
alias :to_a :to_ary
|
82
|
+
|
66
83
|
def initialize(options = {})
|
67
84
|
@parent = options.delete(:parent)
|
68
85
|
|
@@ -151,6 +168,7 @@ module Hippo::Segments
|
|
151
168
|
end
|
152
169
|
else
|
153
170
|
if field.composite
|
171
|
+
self.values[field.composite_sequence] ||= {}
|
154
172
|
self.values[field.composite_sequence][field.sequence]
|
155
173
|
else
|
156
174
|
self.values[field.sequence]
|
@@ -47,6 +47,8 @@ module Hippo::TransactionSets
|
|
47
47
|
break unless segment
|
48
48
|
break unless component.valid?(segment)
|
49
49
|
|
50
|
+
segment.parent = self
|
51
|
+
|
50
52
|
if component.repeating?
|
51
53
|
values[component.sequence] ||= component.initialize_component(self)
|
52
54
|
values[component.sequence] << segment
|
@@ -99,9 +101,26 @@ module Hippo::TransactionSets
|
|
99
101
|
@sequences[segment_identifier] += 1
|
100
102
|
end
|
101
103
|
|
104
|
+
def segments
|
105
|
+
values.values.collect(&:segments).flatten
|
106
|
+
end
|
107
|
+
|
102
108
|
def segment_count
|
103
|
-
|
109
|
+
segments.count
|
110
|
+
end
|
111
|
+
|
112
|
+
def ancestors
|
113
|
+
if parent
|
114
|
+
[parent, parent.ancestors].flatten
|
115
|
+
else
|
116
|
+
[]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_ary
|
121
|
+
nil
|
104
122
|
end
|
123
|
+
alias :to_a :to_ary
|
105
124
|
|
106
125
|
def to_s
|
107
126
|
output = ''
|
@@ -20,10 +20,22 @@ module Hippo::TransactionSets
|
|
20
20
|
self.map(&:to_s).join
|
21
21
|
end
|
22
22
|
|
23
|
+
def segments
|
24
|
+
return [] unless self.length != 0
|
25
|
+
|
26
|
+
self.map(&:segments).flatten
|
27
|
+
end
|
28
|
+
|
23
29
|
def segment_count
|
24
|
-
|
30
|
+
segments.length
|
31
|
+
end
|
25
32
|
|
26
|
-
|
33
|
+
def ancestors
|
34
|
+
if parent
|
35
|
+
parent.ancestors.flatten
|
36
|
+
else
|
37
|
+
[]
|
38
|
+
end
|
27
39
|
end
|
28
40
|
|
29
41
|
def method_missing(method_name, *args, &block)
|
data/lib/hippo/version.rb
CHANGED
data/samples/200823.EDI
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
ISA*00* *00* *ZZ*592015694 *ZZ*B9820 *120119*1701*^*00501*000000001*0*P*:~
|
2
|
+
GS*FA*09102*B9820*20120119*170146*1*X*005010X231A1~
|
3
|
+
ST*999*0001*005010X231A1~
|
4
|
+
AK1*HC*155*005010X222A1~
|
5
|
+
AK2*837*0002*005010X222A1~
|
6
|
+
IK5*A~
|
7
|
+
AK9*A*1*1*1~
|
8
|
+
SE*6*0001~
|
9
|
+
GE*1*1~
|
10
|
+
IEA*1*000000001~
|
11
|
+
ISA*00* *00* *ZZ*592015694 *ZZ*B9820 *120119*1703*^*00501*000000001*0*P*:~
|
12
|
+
GS*FA*09102*B9820*20120119*170300*1*X*005010X231A1~
|
13
|
+
ST*999*0001*005010X231A1~
|
14
|
+
AK1*HC*156*005010X222A1~
|
15
|
+
AK2*837*0002*005010X222A1~
|
16
|
+
IK3*N4*5074*2010*8~
|
17
|
+
IK4*3*26*2~
|
18
|
+
IK5*R*5~
|
19
|
+
AK9*R*1*1*0~
|
20
|
+
SE*8*0001~
|
21
|
+
GE*1*1~
|
22
|
+
IEA*1*000000001~
|
data/test/test_segments_base.rb
CHANGED
@@ -175,4 +175,10 @@ class TestSegmentsBase < MiniTest::Unit::TestCase
|
|
175
175
|
|
176
176
|
assert_equal "ISA*00* *00* *ZZ*593208085 *ZZ*OVERRIDE *#{Date.today.strftime('%y%m%d')}*#{Time.now.strftime('%H%M')}*^*00501*000012345*1*T*:~", isa.to_s
|
177
177
|
end
|
178
|
+
|
179
|
+
def test_access_empty_composite_returns_nil
|
180
|
+
seg = Hippo::Segments::TCS.new
|
181
|
+
|
182
|
+
assert_equal nil, seg.TCS01_01
|
183
|
+
end
|
178
184
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hippo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-01-
|
13
|
+
date: 2012-01-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: minitest
|
17
|
-
requirement: &
|
17
|
+
requirement: &2182290180 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2182290180
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rake
|
28
|
-
requirement: &
|
28
|
+
requirement: &2182289680 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: 0.9.2
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2182289680
|
37
37
|
description: HIPAA Transaction Set Generator/Parser
|
38
38
|
email:
|
39
39
|
- robertj@promedicalinc.com
|
@@ -262,6 +262,7 @@ files:
|
|
262
262
|
- samples/005010X222A1_commercial_health_insurance.edi
|
263
263
|
- samples/005010X231A1_01.edi
|
264
264
|
- samples/005010X231A1_02.edi
|
265
|
+
- samples/200823.EDI
|
265
266
|
- test/test_helper.rb
|
266
267
|
- test/test_hipaa_835.rb
|
267
268
|
- test/test_hipaa_837.rb
|