mogox/ruby-hl7

View on GitHub
lib/segment_fields.rb

Summary

Maintainability
A
50 mins
Test Coverage
#  SegmentFields
#  class HL7::Message::Segment::NK1 < HL7::Message::Segment
#    wieght 100 # segments are sorted ascendingly
#    add_field :something_you_want       # assumes :idx=>1
#    add_field :something_else, :idx=>6  # :idx=>6 and field count=6
module HL7::Message::SegmentFields
  def self.included(base)
    base.extend(ClassMethods)
  end

  @elements = []

  module ClassMethods
    # define a field alias
    # * name is the alias itself (required)
    # * options is a hash of parameters
    #   * :id is the field number to reference (optional, auto-increments from 1
    #      by default)
    #   * :blk is a validation proc (optional, overrides the second argument)
    # * blk is an optional validation/convertion proc which MUST
    #   take a parameter and always return a value for the field (it will be
    #    used on read/write calls)
    def add_field( name, options={}, &blk )
      options = { :idx =>-1, :blk =>blk}.merge!( options )
      name ||= :id
      namesym = name.to_sym
      @field_cnt ||= 1
      if options[:idx] == -1
        options[:idx] = @field_cnt # provide default auto-incrementing
      end
      @field_cnt = options[:idx].to_i + 1

      singleton.module_eval do
        @fields ||= {}
        @fields[ namesym ] = options
      end

      self.class_eval <<-END
        def #{name}(val=nil)
          unless val
            read_field( :#{namesym} )
          else
            write_field( :#{namesym}, val )
            val # this matches existing n= method functionality
          end
        end

        def #{name}=(value)
          write_field( :#{namesym}, value )
        end
      END
    end

    def fields #:nodoc:
      singleton.module_eval do
        (@fields ||= [])
      end
    end
  end

  def field_info( name ) #:nodoc:
    field_blk = nil
    idx = name # assume we've gotten a fixnum
    unless name.kind_of?( Fixnum )
      fld_info = self.class.fields[ name ]
      idx = fld_info[:idx].to_i
      field_blk = fld_info[:blk]
    end

    [ idx, field_blk ]
  end

  def read_field( name ) #:nodoc:
    idx, field_blk = field_info( name )
    return nil unless idx
    return nil if (idx >= @elements.length)

    ret = @elements[ idx ]
    ret = ret.first if (ret.kind_of?(Array) && ret.length == 1)
    ret = field_blk.call( ret ) if field_blk
    ret
  end

  def write_field( name, value ) #:nodoc:
    idx, field_blk = field_info( name )
    return nil unless idx

    if (idx >= @elements.length)
      # make some space for the incoming field, missing items are assumed to
      # be empty, so this is valid per the spec -mg
      missing = ("," * (idx-@elements.length)).split(',',-1)
      @elements += missing
    end

    value = value.first if (value && value.kind_of?(Array) && value.length == 1)
    value = field_blk.call( value ) if field_blk
    @elements[ idx ] = value.to_s
  end
end