It take me 3 hours to figure out how to implement in ruby the example described in
Language Workbenches. It is one of my effort to learn the ruby language. The implementation uses what Jamis Buck's in
the article Writing Domain Specific Languages called Sandboxing Approach.
#file ./lib/data.rb
class ServiceCall
attr_accessor :customer_name,:customer_ID,
:call_type_code,:date_of_call_string
end
class Usage
attr_accessor :customer_name,:customer_ID,
:cycle,:read_date
end
#file ./lib/data_reader.rb
class Reader
attr_accessor :result
def initialize
@result = []
@strategies = {}
end
def process file_name
File.open(file_name,'r') do |file|
while line = file.gets
process_line line
end
end
end
def process_line line
return if is_blank(line) || is_comment(line)
type_code = get_type_code(line)
strategy = @strategies[type_code]
msg_err = <<-END
unable to find strategy for
type_code #{type_code}
END
raise msg_err if nil==strategy
@result << strategy.process(line)
end
def is_blank line
line == ''
end
def is_comment line
line[0..0] == '#'
end
def get_type_code line
line[0..3]
end
def mapping type_code, type, &block
@strategies[type_code] =
ReaderStrategy.new type_code, type, &block
end
end
#file ./lib/data_reader_strategy.rb
class ReaderStrategy
attr_accessor :extractors
def initialize type_code, type, &block
@type_code, @type = type_code, type
@extractors = []
instance_eval &block if block_given?
end
def process line
result = @type.new
@extractors.each do |e|
e.extract_field result, line
end
result
end
def extract from_to, field_name
@extractors << FieldExtractor.new(from_to,
field_name)
end
end
class FieldExtractor
attr_accessor :from_to, :field_name
def initialize from_to, field_name
@from_to, @field_name = from_to, field_name
end
def extract_field target, line
target.instance_variable_set(inst_var_sym,
line[@from_to])
end
def inst_var_sym
result = '@' + @field_name
result.to_sym
end
end
#file ./test/sample.txt
#123456789012345678901234567890123456789012345678901234567890
SVCLFOWLER 10101MS0120050313.........................
SVCLHOHPE 10201DX0320050315........................
SVCLTWO x10301MRP220050329..............................
USGE10301TWO x50214..7050329...............................
#file ./test/test_data_reader.rb
require 'test/unit'
require File.join(File.dirname(__FILE__),
'..', 'lib', 'data')
require File.join(File.dirname(__FILE__),
'..', 'lib', 'data_reader')
require File.join(File.dirname(__FILE__),
'..', 'lib', 'data_reader_strategy')
class TestReader < Test::Unit::TestCase
def setup
@reader = Reader.new()
end
def test_read_file
@reader.mapping('SVCL', ServiceCall) do
extract 4..18, 'customer_name'
extract 19..23, 'customer_ID'
extract 24..27, 'call_type_code'
extract 28..35, 'date_of_call_string'
end
@reader.mapping('USGE', Usage) do
extract 9..22, 'customer_name'
extract 4..8, 'customer_ID'
extract 30..30, 'cycle'
extract 31..36, 'read_date'
end
file_name = File.join(File.dirname(__FILE__),
'sample.txt')
@reader.process(file_name)
assert_equal 4, @reader.result.size
end
def test_is_blank
assert @reader.is_blank('')
end
def test_is_comment
assert @reader.is_comment('#1234')
end
def test_get_type_code
@reader.mapping('SVCL', ServiceCall)
assert_equal 'SVCL',
@reader.get_type_code('SVCLFOWLER')
end
end
#file ./test/test_data_reader_strategy.rb
require 'test/unit'
require File.join(File.dirname(__FILE__),
'..', 'lib', 'data')
require File.join(File.dirname(__FILE__),
'..', 'lib', 'data_reader_strategy')
class TestReaderStrategy < Test::Unit::TestCase
def test_service_call
strategy = ReaderStrategy.new 'SVCL',
ServiceCall do
extract 4..18, 'customer_name'
end
assert_equal 1, strategy.extractors.size
result = strategy.process('SVCLFOWLER')
assert_equal ServiceCall, result.class
assert_equal 'FOWLER', result.customer_name
end
def test_usage_call
strategy = ReaderStrategy.new 'USGE',
Usage do
extract 9..22, 'customer_name'
end
assert_equal 1, strategy.extractors.size
result = strategy.process('USGE10301TWO')
assert_equal Usage, result.class
assert_equal 'TWO', result.customer_name
end
end
No comments:
Post a Comment