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 +10 -0
- data/bin/fixless +9 -0
- data/lib/fix_protocol_tools/messages_processor.rb +107 -0
- data/lib/fix_protocol_tools/runner.rb +56 -0
- data/lib/fix_protocol_tools/specification/fix_specification.rb +39 -0
- data/lib/fix_protocol_tools/specification/specification_reader.rb +62 -0
- data/lib/fix_protocol_tools/specification/xml/FIX40.xml +856 -0
- data/lib/fix_protocol_tools/specification/xml/FIX41.xml +1267 -0
- data/lib/fix_protocol_tools/specification/xml/FIX42.xml +2669 -0
- data/lib/fix_protocol_tools/specification/xml/FIX43.xml +3991 -0
- data/lib/fix_protocol_tools/specification/xml/FIX44.xml +6744 -0
- data/lib/fix_protocol_tools/version.rb +3 -0
- data/lib/fix_protocol_tools.rb +2 -0
- metadata +109 -0
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,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
|