segfault-ruby-hl7 0.3.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.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ = License
2
+ Permission is hereby granted, free of charge, to any person obtaining
3
+ a copy of this software and associated documentation files (the
4
+ "Software"), to deal in the Software without restriction, including
5
+ without limitation the rights to use, copy, modify, merge, publish,
6
+ distribute, sublicense, and/or sell copies of the Software, and to
7
+ permit persons to whom the Software is furnished to do so, subject to
8
+ the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be
11
+ included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
17
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,24 @@
1
+ =Ruby HL7 Library README
2
+
3
+ A simple way to parse and create hl7 2.x messages with ruby.
4
+ Examples can be found in HL7::Message
5
+ The version id can be found in the HL7::VERSION constant.
6
+
7
+ * Bug tracking: http://trac.hasno.info/ruby-hl7
8
+ * Subversion: svn://hasno.info/ruby-hl7
9
+ * Git: git://github.com/segfault/ruby-hl7.git
10
+ * Docs: http://ruby-hl7.rubyforge.org
11
+ * Rubyforge: http://rubyforge.org/projects/ruby-hl7
12
+ * Lists
13
+ * Developers: mailto:ruby-hl7-devel@rubyforge.org
14
+ * Users: mailto:ruby-hl7-users@rubyforge.org
15
+
16
+ Copyright (c) 2006-2008 Mark Guzman
17
+
18
+ == Download and Installation
19
+ Install the gem using the following command:
20
+ gem install ruby-hl7
21
+
22
+
23
+ == License
24
+ see the LICENSE file
data/lib/ruby-hl7.rb ADDED
@@ -0,0 +1,644 @@
1
+ # encoding: UTF-8
2
+ #= ruby-hl7.rb
3
+ # Ruby HL7 is designed to provide a simple, easy to use library for
4
+ # parsing and generating HL7 (2.x) messages.
5
+ #
6
+ #
7
+ # Author: Mark Guzman (mailto:segfault@hasno.info)
8
+ #
9
+ # Copyright: (c) 2006-2009 Mark Guzman
10
+ #
11
+ # License: BSD
12
+ #
13
+ # $Id$
14
+ #
15
+ # == License
16
+ # see the LICENSE file
17
+ #
18
+
19
+ require 'rubygems'
20
+ require "stringio"
21
+ require "date"
22
+
23
+ module HL7 # :nodoc:
24
+ VERSION = "0.3"
25
+ def self.ParserConfig
26
+ @parser_cfg ||= { :empty_segment_is_error => true }
27
+ end
28
+ end
29
+
30
+ # Encapsulate HL7 specific exceptions
31
+ class HL7::Exception < StandardError
32
+ end
33
+
34
+ # Parsing failed
35
+ class HL7::ParseError < HL7::Exception
36
+ end
37
+
38
+ # Attempting to use an invalid indice
39
+ class HL7::RangeError < HL7::Exception
40
+ end
41
+
42
+ # Attempting to assign invalid data to a field
43
+ class HL7::InvalidDataError < HL7::Exception
44
+ end
45
+
46
+ # Ruby Object representation of an hl7 2.x message
47
+ # the message object is actually a "smart" collection of hl7 segments
48
+ # == Examples
49
+ #
50
+ # ==== Creating a new HL7 message
51
+ #
52
+ # # create a message
53
+ # msg = HL7::Message.new
54
+ #
55
+ # # create a MSH segment for our new message
56
+ # msh = HL7::Message::Segment::MSH.new
57
+ # msh.recv_app = "ruby hl7"
58
+ # msh.recv_facility = "my office"
59
+ # msh.processing_id = rand(10000).to_s
60
+ #
61
+ # msg << msh # add the MSH segment to the message
62
+ #
63
+ # puts msg.to_s # readable version of the message
64
+ #
65
+ # puts msg.to_hl7 # hl7 version of the message (as a string)
66
+ #
67
+ # puts msg.to_mllp # mllp version of the message (as a string)
68
+ #
69
+ # ==== Parse an existing HL7 message
70
+ #
71
+ # raw_input = open( "my_hl7_msg.txt" ).readlines
72
+ # msg = HL7::Message.new( raw_input )
73
+ #
74
+ # puts "message type: %s" % msg[:MSH].message_type
75
+ #
76
+ #
77
+ class HL7::Message
78
+ include Enumerable # we treat an hl7 2.x message as a collection of segments
79
+ attr :element_delim
80
+ attr :item_delim
81
+ attr :segment_delim
82
+
83
+ # setup a new hl7 message
84
+ # raw_msg:: is an optional object containing an hl7 message
85
+ # it can either be a string or an Enumerable object
86
+ def initialize( raw_msg=nil, &blk )
87
+ @segments = []
88
+ @segments_by_name = {}
89
+ @item_delim = "^"
90
+ @element_delim = '|'
91
+ @segment_delim = "\r"
92
+
93
+ parse( raw_msg ) if raw_msg
94
+
95
+ if block_given?
96
+ blk.call self
97
+ end
98
+ end
99
+
100
+ # access a segment of the message
101
+ # index:: can be a Range, Fixnum or anything that
102
+ # responds to to_sym
103
+ def []( index )
104
+ ret = nil
105
+
106
+ if index.kind_of?(Range) || index.kind_of?(Fixnum)
107
+ ret = @segments[ index ]
108
+ elsif (index.respond_to? :to_sym)
109
+ ret = @segments_by_name[ index.to_sym ]
110
+ ret = ret.first if ret && ret.length == 1
111
+ end
112
+
113
+ ret
114
+ end
115
+
116
+ # modify a segment of the message
117
+ # index:: can be a Range, Fixnum or anything that
118
+ # responds to to_sym
119
+ # value:: an HL7::Message::Segment object
120
+ def []=( index, value )
121
+ unless ( value && value.kind_of?(HL7::Message::Segment) )
122
+ raise HL7::Exception.new( "attempting to assign something other than an HL7 Segment" )
123
+ end
124
+
125
+ if index.kind_of?(Range) || index.kind_of?(Fixnum)
126
+ @segments[ index ] = value
127
+ elsif index.respond_to?(:to_sym)
128
+ (@segments_by_name[ index.to_sym ] ||= []) << value
129
+ else
130
+ raise HL7::Exception.new( "attempting to use an indice that is not a Range, Fixnum or to_sym providing object" )
131
+ end
132
+
133
+ value.segment_parent = self
134
+ end
135
+
136
+ # return the index of the value if it exists, nil otherwise
137
+ # value:: is expected to be a string
138
+ def index( value )
139
+ return nil unless (value && value.respond_to?(:to_sym))
140
+
141
+ segs = @segments_by_name[ value.to_sym ]
142
+ return nil unless segs
143
+
144
+ @segments.index( segs.to_a.first )
145
+ end
146
+
147
+ # add a segment to the message
148
+ # * will force auto set_id sequencing for segments containing set_id's
149
+ def <<( value )
150
+ unless ( value && value.kind_of?(HL7::Message::Segment) )
151
+ raise HL7::Exception.new( "attempting to append something other than an HL7 Segment" )
152
+ end
153
+
154
+ value.segment_parent = self unless value.segment_parent
155
+ (@segments ||= []) << value
156
+ name = value.class.to_s.gsub("HL7::Message::Segment::", "").to_sym
157
+ (@segments_by_name[ name ] ||= []) << value
158
+ sequence_segments unless @parsing # let's auto-set the set-id as we go
159
+ end
160
+
161
+ # parse a String or Enumerable object into an HL7::Message if possible
162
+ # * returns a new HL7::Message if successful
163
+ def self.parse( inobj )
164
+ HL7::Message.new do |msg|
165
+ msg.parse( inobj )
166
+ end
167
+ end
168
+
169
+ # parse the provided String or Enumerable object into this message
170
+ def parse( inobj )
171
+ unless inobj.kind_of?(String) || inobj.respond_to?(:each)
172
+ raise HL7::ParseError.new
173
+ end
174
+
175
+ if inobj.kind_of?(String)
176
+ parse_string( inobj )
177
+ elsif inobj.respond_to?(:each)
178
+ parse_enumerable( inobj )
179
+ end
180
+ end
181
+
182
+ # yield each segment in the message
183
+ def each # :yeilds: segment
184
+ return unless @segments
185
+ @segments.each { |s| yield s }
186
+ end
187
+
188
+ # return the segment count
189
+ def length
190
+ 0 unless @segments
191
+ @segments.length
192
+ end
193
+
194
+ # provide a screen-readable version of the message
195
+ def to_s
196
+ @segments.collect { |s| s if s.to_s.length > 0 }.join( "\n" )
197
+ end
198
+
199
+ # provide a HL7 spec version of the message
200
+ def to_hl7
201
+ @segments.collect { |s| s if s.to_s.length > 0 }.join( @segment_delim )
202
+ end
203
+
204
+ # provide the HL7 spec version of the message wrapped in MLLP
205
+ def to_mllp
206
+ pre_mllp = to_hl7
207
+ "\x0b" + pre_mllp + "\x1c\r"
208
+ end
209
+
210
+ # auto-set the set_id fields of any message segments that
211
+ # provide it and have more than one instance in the message
212
+ def sequence_segments(base=nil)
213
+ last = nil
214
+ segs = @segments
215
+ segs = base.children if base
216
+
217
+ segs.each do |s|
218
+ if s.kind_of?( last.class ) && s.respond_to?( :set_id )
219
+ last.set_id = 1 unless last.set_id && last.set_id.to_i > 0
220
+ s.set_id = last.set_id.to_i + 1
221
+ end
222
+
223
+ if s.respond_to?(:children)
224
+ sequence_segments( s )
225
+ end
226
+
227
+ last = s
228
+ end
229
+ end
230
+
231
+ private
232
+ # Get the element delimiter from an MSH segment
233
+ def parse_element_delim(str)
234
+ (str && str.kind_of?(String)) ? str.slice(3,1) : "|"
235
+ end
236
+
237
+ # Get the item delimiter from an MSH segment
238
+ def parse_item_delim(str)
239
+ (str && str.kind_of?(String)) ? str.slice(4,1) : "^"
240
+ end
241
+
242
+ def parse_enumerable( inary )
243
+ #assumes an enumeration of strings....
244
+ inary.each do |oary|
245
+ parse_string( oary.to_s )
246
+ end
247
+ end
248
+
249
+ def parse_string( instr )
250
+ post_mllp = instr
251
+ if /\x0b((:?.|\r|\n)+)\x1c\r/.match( instr )
252
+ post_mllp = $1 #strip the mllp bytes
253
+ end
254
+
255
+ ary = post_mllp.split( segment_delim, -1 )
256
+ generate_segments( ary )
257
+ end
258
+
259
+ def generate_segments( ary )
260
+ raise HL7::ParseError.new unless ary.length > 0
261
+
262
+ @parsing = true
263
+ last_seg = nil
264
+ ary.each do |elm|
265
+ if elm.slice(0,3) == "MSH"
266
+ @item_delim = parse_item_delim(elm)
267
+ @element_delim = parse_element_delim(elm)
268
+ end
269
+ last_seg = generate_segment( elm, last_seg ) || last_seg
270
+ end
271
+ @parsing = nil
272
+ end
273
+
274
+ def generate_segment( elm, last_seg )
275
+ seg_parts = elm.split( @element_delim, -1 )
276
+ unless seg_parts && (seg_parts.length > 0)
277
+ raise HL7::ParseError.new if HL7.ParserConfig[:empty_segment_is_error] || false
278
+ return nil
279
+ end
280
+
281
+ seg_name = seg_parts[0]
282
+ if RUBY_VERSION < "1.9" && HL7::Message::Segment.constants.index(seg_name) # do we have an implementation?
283
+ kls = eval("HL7::Message::Segment::%s" % seg_name)
284
+ elsif RUBY_VERSION >= "1.9" && HL7::Message::Segment.constants.index(seg_name.to_sym)
285
+ kls = eval("HL7::Message::Segment::%s" % seg_name)
286
+ else
287
+ # we don't have an implementation for this segment
288
+ # so lets just preserve the data
289
+ kls = HL7::Message::Segment::Default
290
+ end
291
+ new_seg = kls.new( elm, [@element_delim, @item_delim] )
292
+ new_seg.segment_parent = self
293
+
294
+ if last_seg && last_seg.respond_to?(:children) && last_seg.accepts?( seg_name )
295
+ last_seg.children << new_seg
296
+ new_seg.is_child_segment = true
297
+ return last_seg
298
+ end
299
+
300
+ @segments << new_seg
301
+
302
+ # we want to allow segment lookup by name
303
+ if seg_name && (seg_name.strip.length > 0)
304
+ seg_sym = seg_name.to_sym
305
+ @segments_by_name[ seg_sym ] ||= []
306
+ @segments_by_name[ seg_sym ] << new_seg
307
+ end
308
+
309
+ new_seg
310
+ end
311
+ end
312
+
313
+ # Ruby Object representation of an hl7 2.x message segment
314
+ # The segments can be setup to provide aliases to specific fields with
315
+ # optional validation code that is run when the field is modified
316
+ # The segment field data is also accessible via the e<number> method.
317
+ #
318
+ # == Defining a New Segment
319
+ # class HL7::Message::Segment::NK1 < HL7::Message::Segment
320
+ # wieght 100 # segments are sorted ascendingly
321
+ # add_field :something_you_want # assumes :idx=>1
322
+ # add_field :something_else, :idx=>6 # :idx=>6 and field count=6
323
+ # add_field :something_more # :idx=>7
324
+ # add_field :block_example do |value|
325
+ # raise HL7::InvalidDataError.new unless value.to_i < 100 && value.to_i > 10
326
+ # return value
327
+ # end
328
+ # # this block will be executed when seg.block_example= is called
329
+ # # and when seg.block_example is called
330
+ #
331
+ class HL7::Message::Segment
332
+ attr :segment_parent, true
333
+ attr :element_delim
334
+ attr :item_delim
335
+ attr :segment_weight
336
+
337
+ # setup a new HL7::Message::Segment
338
+ # raw_segment:: is an optional String or Array which will be used as the
339
+ # segment's field data
340
+ # delims:: an optional array of delimiters, where
341
+ # delims[0] = element delimiter
342
+ # delims[1] = item delimiter
343
+ def initialize(raw_segment="", delims=[], &blk)
344
+ @segments_by_name = {}
345
+ @field_total = 0
346
+ @is_child = false
347
+
348
+ @element_delim = (delims.kind_of?(Array) && delims.length>0) ? delims[0] : "|"
349
+ @item_delim = (delims.kind_of?(Array) && delims.length>1) ? delims[1] : "^"
350
+
351
+ if (raw_segment.kind_of? Array)
352
+ @elements = raw_segment
353
+ else
354
+ @elements = raw_segment.split( @element_delim, -1 )
355
+ if raw_segment == ""
356
+ @elements[0] = self.class.to_s.split( "::" ).last
357
+ @elements << ""
358
+ end
359
+ end
360
+
361
+ if block_given?
362
+ callctx = eval( "self", blk.binding )
363
+ def callctx.__seg__(val=nil)
364
+ @__seg_val__ ||= val
365
+ end
366
+ callctx.__seg__(self)
367
+ # TODO: find out if this pollutes the calling namespace permanently...
368
+
369
+ to_do = <<-END
370
+ def method_missing( sym, *args, &blk )
371
+ __seg__.send( sym, args, blk )
372
+ end
373
+ END
374
+
375
+ eval( to_do, blk.binding )
376
+ yield self
377
+ eval( "undef method_missing", blk.binding )
378
+ end
379
+ end
380
+
381
+ def to_info
382
+ "%s: empty segment >> %s" % [ self.class.to_s, @elements.inspect ]
383
+ end
384
+
385
+ # output the HL7 spec version of the segment
386
+ def to_s
387
+ @elements.join( @element_delim )
388
+ end
389
+
390
+ # at the segment level there is no difference between to_s and to_hl7
391
+ alias :to_hl7 :to_s
392
+
393
+ # handle the e<number> field accessor
394
+ # and any aliases that didn't get added to the system automatically
395
+ def method_missing( sym, *args, &blk )
396
+ base_str = sym.to_s.gsub( "=", "" )
397
+ base_sym = base_str.to_sym
398
+
399
+ if self.class.fields.include?( base_sym )
400
+ # base_sym is ok, let's move on
401
+ elsif /e([0-9]+)/.match( base_str )
402
+ # base_sym should actually be $1, since we're going by
403
+ # element id number
404
+ base_sym = $1.to_i
405
+ else
406
+ super
407
+ end
408
+
409
+ if sym.to_s.include?( "=" )
410
+ write_field( base_sym, args )
411
+ else
412
+
413
+ if args.length > 0
414
+ write_field( base_sym, args.flatten.select { |arg| arg } )
415
+ else
416
+ read_field( base_sym )
417
+ end
418
+
419
+ end
420
+ end
421
+
422
+ # sort-compare two Segments, 0 indicates equality
423
+ def <=>( other )
424
+ return nil unless other.kind_of?(HL7::Message::Segment)
425
+
426
+ # per Comparable docs: http://www.ruby-doc.org/core/classes/Comparable.html
427
+ diff = self.weight - other.weight
428
+ return -1 if diff > 0
429
+ return 1 if diff < 0
430
+ return 0
431
+ end
432
+
433
+ # get the defined sort-weight of this segment class
434
+ # an alias for self.weight
435
+ def weight
436
+ self.class.weight
437
+ end
438
+
439
+
440
+ # return true if the segment has a parent
441
+ def is_child_segment?
442
+ (@is_child_segment ||= false)
443
+ end
444
+
445
+ # indicate whether or not the segment has a parent
446
+ def is_child_segment=(val)
447
+ @is_child_segment = val
448
+ end
449
+
450
+ # get the length of the segment (number of fields it contains)
451
+ def length
452
+ 0 unless @elements
453
+ @elements.length
454
+ end
455
+
456
+
457
+ private
458
+ def self.singleton #:nodoc:
459
+ class << self; self end
460
+ end
461
+
462
+ # DSL element to define a segment's sort weight
463
+ # returns the segment's current weight by default
464
+ # segments are sorted ascending
465
+ def self.weight(new_weight=nil)
466
+ if new_weight
467
+ singleton.module_eval do
468
+ @my_weight = new_weight
469
+ end
470
+ end
471
+
472
+ singleton.module_eval do
473
+ return 999 unless @my_weight
474
+ @my_weight
475
+ end
476
+ end
477
+
478
+
479
+
480
+ # allows a segment to store other segment objects
481
+ # used to handle associated lists like one OBR to many OBX segments
482
+ def self.has_children(child_types)
483
+ @child_types = child_types
484
+ define_method(:child_types) do
485
+ @child_types
486
+ end
487
+
488
+ self.class_eval do
489
+ define_method(:children) do
490
+ unless @my_children
491
+ p = self
492
+ @my_children ||= []
493
+ @my_children.instance_eval do
494
+ @parental = p
495
+ alias :old_append :<<
496
+
497
+ def <<(value)
498
+ unless (value && value.kind_of?(HL7::Message::Segment))
499
+ raise HL7::Exception.new( "attempting to append non-segment to a segment list" )
500
+ end
501
+
502
+ value.segment_parent = @parental
503
+ k = @parental
504
+ while (k && k.segment_parent && !k.segment_parent.kind_of?(HL7::Message))
505
+ k = k.segment_parent
506
+ end
507
+ k.segment_parent << value if k && k.segment_parent
508
+ old_append( value )
509
+ end
510
+ end
511
+ end
512
+
513
+ @my_children
514
+ end
515
+
516
+ define_method('accepts?') do |t|
517
+ t = t.to_sym if t && (t.to_s.length > 0) && t.respond_to?(:to_sym)
518
+ child_types.index t
519
+ end
520
+ end
521
+ end
522
+
523
+ # define a field alias
524
+ # * name is the alias itself (required)
525
+ # * options is a hash of parameters
526
+ # * :id is the field number to reference (optional, auto-increments from 1
527
+ # by default)
528
+ # * :blk is a validation proc (optional, overrides the second argument)
529
+ # * blk is an optional validation proc which MUST take a parameter
530
+ # and always return a value for the field (it will be used on read/write
531
+ # calls)
532
+ def self.add_field( name, options={}, &blk )
533
+ options = { :idx =>-1, :blk =>blk}.merge!( options )
534
+ name ||= :id
535
+ namesym = name.to_sym
536
+ @field_cnt ||= 1
537
+ if options[:idx] == -1
538
+ options[:idx] = @field_cnt # provide default auto-incrementing
539
+ end
540
+ @field_cnt = options[:idx].to_i + 1
541
+
542
+ singleton.module_eval do
543
+ @fields ||= {}
544
+ @fields[ namesym ] = options
545
+ end
546
+
547
+ self.class_eval <<-END
548
+ def #{name}(val=nil)
549
+ unless val
550
+ read_field( :#{namesym} )
551
+ else
552
+ write_field( :#{namesym}, val )
553
+ val # this matches existing n= method functionality
554
+ end
555
+ end
556
+
557
+ def #{name}=(value)
558
+ write_field( :#{namesym}, value )
559
+ end
560
+ END
561
+ end
562
+
563
+ def self.fields #:nodoc:
564
+ singleton.module_eval do
565
+ (@fields ||= [])
566
+ end
567
+ end
568
+
569
+ def field_info( name ) #:nodoc:
570
+ field_blk = nil
571
+ idx = name # assume we've gotten a fixnum
572
+ unless name.kind_of?( Fixnum )
573
+ fld_info = self.class.fields[ name ]
574
+ idx = fld_info[:idx].to_i
575
+ field_blk = fld_info[:blk]
576
+ end
577
+
578
+ [ idx, field_blk ]
579
+ end
580
+
581
+ def read_field( name ) #:nodoc:
582
+ idx, field_blk = field_info( name )
583
+ return nil unless idx
584
+ return nil if (idx >= @elements.length)
585
+
586
+ ret = @elements[ idx ]
587
+ ret = ret.first if (ret.kind_of?(Array) && ret.length == 1)
588
+ ret = field_blk.call( ret ) if field_blk
589
+ ret
590
+ end
591
+
592
+ def write_field( name, value ) #:nodoc:
593
+ idx, field_blk = field_info( name )
594
+ return nil unless idx
595
+
596
+ if (idx >= @elements.length)
597
+ # make some space for the incoming field, missing items are assumed to
598
+ # be empty, so this is valid per the spec -mg
599
+ missing = ("," * (idx-@elements.length)).split(',',-1)
600
+ @elements += missing
601
+ end
602
+
603
+ value = value.first if (value && value.kind_of?(Array) && value.length == 1)
604
+ value = field_blk.call( value ) if field_blk
605
+ @elements[ idx ] = value.to_s
606
+ end
607
+
608
+ @elements = []
609
+
610
+ end
611
+
612
+ # parse an hl7 formatted date
613
+ #def Date.from_hl7( hl7_date )
614
+ #end
615
+
616
+ #def Date.to_hl7_short( ruby_date )
617
+ #end
618
+
619
+ #def Date.to_hl7_med( ruby_date )
620
+ #end
621
+
622
+ #def Date.to_hl7_long( ruby_date )
623
+ #end
624
+
625
+ # Provide a catch-all information preserving segment
626
+ # * nb: aliases are not provided BUT you can use the numeric element accessor
627
+ #
628
+ # seg = HL7::Message::Segment::Default.new
629
+ # seg.e0 = "NK1"
630
+ # seg.e1 = "SOMETHING ELSE"
631
+ # seg.e2 = "KIN HERE"
632
+ #
633
+ class HL7::Message::Segment::Default < HL7::Message::Segment
634
+ def initialize(raw_segment="", delims=[])
635
+ segs = [] if (raw_segment == "")
636
+ segs ||= raw_segment
637
+ super( segs, delims )
638
+ end
639
+ end
640
+
641
+ # load our segments
642
+ Dir["#{File.dirname(__FILE__)}/segments/*.rb"].each { |ext| load ext }
643
+
644
+ # vim:tw=78:sw=2:ts=2:et:fdm=marker: