fix_protocol_tools 1.0.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/bin/fixgrep ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include? path
5
+
6
+ require 'optparse'
7
+ require 'fix_protocol_tools'
8
+
9
+ raise "To be implemented"
10
+ #FixProtocolTools::Runner.new.grep!
data/bin/fixless ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include? path
5
+
6
+ require 'optparse'
7
+ require 'fix_protocol_tools'
8
+
9
+ FixProtocolTools::Runner.new.less!
@@ -0,0 +1,107 @@
1
+ require 'term/ansicolor'
2
+ require 'logger'
3
+ require 'fix_protocol_tools/specification/fix_specification'
4
+
5
+ module FixProtocolTools
6
+ class MessagesProcessor
7
+ include Term::ANSIColor
8
+ MESSAGE_DELIMITER = "\x01"
9
+
10
+ def initialize(options)
11
+ @spec = nil
12
+ @is_even_line = true
13
+ @output = init_output_chanel options
14
+ @grep = options[:grep]
15
+ end
16
+
17
+ def process()
18
+ buffer = nil
19
+
20
+ ARGF.each do |line|
21
+ line = line.gsub(/\r/, '').chomp
22
+ if buffer
23
+ buffer += line
24
+ if buffer =~ /8=FIX.*[A\x01]10=\d+/
25
+ process_line(buffer)
26
+ buffer = nil
27
+ end
28
+ else
29
+ if line =~ /8=FIX/ && !(line =~ /[A\x01]10=\d+/)
30
+ buffer = line
31
+ else
32
+ process_line(line)
33
+ end
34
+ end
35
+ end
36
+
37
+ process_line(buffer) if buffer
38
+
39
+ @output.close
40
+ end
41
+
42
+ private
43
+
44
+ def print_line(line)
45
+ @output.puts(@is_even_line ? green(line) : line)
46
+ @is_even_line = !@is_even_line
47
+ end
48
+
49
+ def process_line(line)
50
+ fields = fix_message_fields(line.gsub('^A', MESSAGE_DELIMITER))
51
+ return unless fields
52
+
53
+ @spec ||= resolve_specification(fields)
54
+ @output.puts(yellow(' =-=-=-=-=-=-==-=-=-=-=-=-==-=-=-=-=-=-= '))
55
+
56
+ fields.each do |field_id, value_id|
57
+ field_name = @spec.field_name(field_id) || field_id
58
+ field_name_padding = @spec.max_field_length - field_name.length - 2
59
+
60
+ print_line(format_line(field_id, field_name, field_name_padding, value_id))
61
+ end
62
+ end
63
+
64
+ def format_line(field_id, field_name, padding, value_id)
65
+ if @spec.message_type?(field_id)
66
+ formatted_row(field_id, value_id, field_name, red(@spec.message_type(value_id)), padding)
67
+ else
68
+ formatted_row(field_id, value_id, field_name, @spec.enum_value(field_id, value_id), padding)
69
+ end
70
+ end
71
+
72
+ def formatted_row(field_id, value_id, field_name, value_name, field_name_padding)
73
+ field_name + ' = '.rjust(field_name_padding) + value_name +
74
+ ' '.rjust(35 - value_name.length) +
75
+ field_id.rjust(5 - field_id.length) + ' = ' + value_id
76
+ end
77
+
78
+ def resolve_specification(fields)
79
+ fix_version = fields[0].last
80
+ Specification::Specification.new(fix_version)
81
+ end
82
+
83
+ def fix_message_fields(line)
84
+ start_index = line.index("8=FIX")
85
+ end_index = line.rindex(MESSAGE_DELIMITER)
86
+
87
+ if start_index || end_index
88
+ line[start_index, end_index].split(MESSAGE_DELIMITER).map do |pair|
89
+ pair.strip.split '='
90
+ end
91
+ else
92
+ nil
93
+ end
94
+ end
95
+
96
+ def init_output_chanel(options)
97
+ if options[:less]
98
+ cmd = 'less'
99
+ cmd += ' -r' if options[:color]
100
+ IO.popen(cmd, 'w')
101
+ else
102
+ STDOUT
103
+ end
104
+ end
105
+ end
106
+ end
107
+
@@ -0,0 +1,56 @@
1
+ require 'term/ansicolor'
2
+ require 'fix_protocol_tools/messages_processor'
3
+
4
+ Term::ANSIColor::coloring = STDOUT.isatty
5
+
6
+ module FixProtocolTools
7
+ class Runner
8
+
9
+ def less!
10
+ options = {:less => false, :color => false}
11
+ opt_parse = OptionParser.new do |opts|
12
+ opts.banner = "Usage: fixless [options] [fixlogfile]"
13
+
14
+ color(options, opts)
15
+ help(opts)
16
+
17
+ opts.on('-l', '--[no-]less', 'Use less command for output') do |color|
18
+ options[:less] = color
19
+ end
20
+ end
21
+
22
+ opt_parse.parse!
23
+
24
+ MessagesProcessor.new(options).process
25
+ end
26
+
27
+ def grep!
28
+ options = {:color => false}
29
+ opt_parse = OptionParser.new do |opts|
30
+ opts.banner = "Usage: fixgrep [options] [fixlogfile]"
31
+
32
+ color(opts, options)
33
+ help(opts)
34
+ end
35
+
36
+ opt_parse.parse!
37
+ MessagesProcessor.new(options).process
38
+ end
39
+
40
+ private
41
+
42
+ def help(opts)
43
+ opts.on('--help', '-h', 'Display help message') do
44
+ puts opts
45
+ exit
46
+ end
47
+ end
48
+
49
+ def color(options, opts)
50
+ opts.on('-c', '--[no-]color', 'Generate color output') do |color|
51
+ Term::ANSIColor::coloring = color
52
+ options[:color] = color
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,39 @@
1
+ require 'fix_protocol_tools/specification/specification_reader'
2
+
3
+ module FixProtocolTools::Specification
4
+ class Specification
5
+ attr_reader :max_field_length
6
+
7
+ def initialize(fix_version)
8
+ reader = Reader.read_specification(fix_version)
9
+
10
+ @enums = reader.enums
11
+ @fields = reader.fields
12
+ @message_types = reader.message_types
13
+ @max_field_length = reader.max_field_length
14
+ end
15
+
16
+ def field_name(field_number)
17
+ @fields[field_number]
18
+ end
19
+
20
+ def message_type?(tag)
21
+ tag == '35'
22
+ end
23
+
24
+ def message_type(tag35)
25
+ @message_types[tag35]
26
+ end
27
+
28
+
29
+ def enum_value(field, enum_id)
30
+ enum = @enums[field]
31
+
32
+ if enum and enum.has_key?(enum_id)
33
+ enum[enum_id]
34
+ else
35
+ enum_id
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ require 'rexml/document'
2
+ require 'rexml/streamlistener'
3
+
4
+ module FixProtocolTools::Specification
5
+ class Reader
6
+ include REXML::StreamListener
7
+ SPECIFICATION_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 'xml')
8
+
9
+ attr_reader :fields, :message_types, :enums, :max_field_length
10
+
11
+ def self.read_specification(fix_version)
12
+ reader = new
13
+
14
+ File.open(File.join(SPECIFICATION_PATH, fix_version.delete('.') + '.xml'), 'r') do |file|
15
+ REXML::Document.parse_stream(file, reader)
16
+ end
17
+
18
+ reader
19
+ end
20
+
21
+ def initialize
22
+ @message_types = {}
23
+ @fields = {}
24
+ @enums = Hash.new { |h, k| h[k] = {} }
25
+
26
+ @max_field_length = 0
27
+ end
28
+
29
+ def tag_start(tag_name, attrs)
30
+ if message?(attrs, tag_name)
31
+ @message_types[attrs['msgtype']] = attrs['name']
32
+ end
33
+
34
+ if field_definition?(attrs, tag_name)
35
+ @current_field = attrs['number']
36
+ @fields[attrs['number']] = attrs['name']
37
+ end
38
+
39
+ if enum_value?(attrs, tag_name)
40
+ @enums[@current_field][attrs['enum']] = attrs['description']
41
+ end
42
+
43
+ if attrs.has_key? 'name'
44
+ @max_field_length = [@max_field_length, attrs['name'].length].max
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def field_definition?(attrs, tag_name)
51
+ tag_name == 'field' && attrs.has_key?('number') && attrs.has_key?('name')
52
+ end
53
+
54
+ def enum_value?(attrs, tag_name)
55
+ tag_name == 'value' && attrs.has_key?('enum')
56
+ end
57
+
58
+ def message?(attrs, tag_name)
59
+ tag_name == 'message' && attrs.has_key?('name') != nil && attrs['msgtype'] != nil && attrs['msgcat'] != nil
60
+ end
61
+ end
62
+ end