fix-protocol 0.0.53 → 0.0.64

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 316f1e25ae64a1c178904c60db2939019c96b711
4
- data.tar.gz: 93d480f3a398270e4b42eed42271871300f4162f
3
+ metadata.gz: 444655c70c2c0184fd14ccd5a9964024e5cdc40d
4
+ data.tar.gz: 84fa0babcfc637bddb6bde19dda4440c28e550ce
5
5
  SHA512:
6
- metadata.gz: 82a0aa1e26e42fb2bdf5f47804875407f149ee9ea64e31e89819d05f6ae2955973289c6f1436d10db8fac5d655f2547d4fbd557f5b9ad935b3c1412b2849e2e3
7
- data.tar.gz: b881f7453af5710c9bd10b3b81d998f51ef263151c4859aa26adfd577bee22f99c44fce2f5ca8826a0da3b1e5edd5f50d54a9cf4d24a6cd73f1f59a076b4e41d
6
+ metadata.gz: 6cb0b2a65b0955992c87d46aa4954dd8decceb97a620da7b7d98c71e10aa89c7bb9bc00f2e2bac6041c412ac586893d4863d0b46123cb14eab90b553b52cb580
7
+ data.tar.gz: 4403f13296f1dcba4e6b5bfd6709bab252b465fe58523c7113475eb96f3e8f462c00b595ade9655467bf6c59b743e061a4615c439423dd42a461cc4621fd4d7f
@@ -27,6 +27,12 @@ module Fix
27
27
  @value && "#{tag}=#{@value}\x01"
28
28
  end
29
29
 
30
+ #
31
+ # Checks whether the start of the given string can be parsed as this particular field
32
+ #
33
+ # @param str [String] The string for which we want to parse the beginning
34
+ # @return [Boolean] Whether the beginning of the string can be parsed for this field
35
+ #
30
36
  def can_parse?(str)
31
37
  str.match(/^#{tag}\=[^\x01]+\x01/)
32
38
  end
@@ -103,9 +109,9 @@ module Fix
103
109
  # @return [String] The FIX field value
104
110
  #
105
111
  def from_type(obj)
106
- if obj && type && !mapping
112
+ if !obj.nil? && type && !mapping
107
113
  send("dump_#{type}", obj)
108
- elsif obj && mapping && mapping.has_key?(obj)
114
+ elsif !obj.nil? && mapping && mapping.has_key?(obj)
109
115
  mapping[obj]
110
116
  else
111
117
  obj
@@ -1,11 +1,6 @@
1
- require 'polyglot'
2
- require 'treetop'
3
- require 'forwardable'
4
-
5
1
  require 'fix/protocol/message_part'
6
2
  require 'fix/protocol/unordered_part'
7
3
  require 'fix/protocol/repeating_message_part'
8
- require 'fix/protocol/message_header'
9
4
 
10
5
  module Fix
11
6
  module Protocol
@@ -15,14 +10,18 @@ module Fix
15
10
  #
16
11
  class Message < MessagePart
17
12
 
18
- extend Forwardable
19
- def_delegators :header,
20
- :sender_comp_id, :sender_comp_id=,
21
- :target_comp_id, :target_comp_id=,
22
- :msg_seq_num, :msg_seq_num=,
23
- :sending_time, :sending_time=
13
+ part :header do
14
+ field :version, tag: 8, required: true, default: 'FIX.4.4'
15
+ field :body_length, tag: 9
24
16
 
25
- part :header, klass: MessageHeader
17
+ unordered :header_fields do
18
+ field :msg_type, tag: 35, required: true
19
+ field :sender_comp_id, tag: 49, required: true
20
+ field :target_comp_id, tag: 56, required: true
21
+ field :msg_seq_num, tag: 34, required: true, type: :integer
22
+ field :sending_time, tag: 52, required: true, type: :timestamp, default: proc { Time.now }
23
+ end
24
+ end
26
25
 
27
26
  def initialize
28
27
  super
@@ -53,6 +52,19 @@ module Fix
53
52
  (errors.nil? || errors.empty?) && parse_failure.nil?
54
53
  end
55
54
 
55
+ #
56
+ # Returns the errors relevant to the message header
57
+ #
58
+ # @return [Array<String>] The errors on the message header
59
+ #
60
+ def errors
61
+ if (version == 'FIX.4.4')
62
+ super
63
+ else
64
+ [super, "Unsupported version: <#{version}>"].flatten
65
+ end
66
+ end
67
+
56
68
  end
57
69
  end
58
70
  end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'fix/protocol/field'
2
3
 
3
4
  module Fix
@@ -8,7 +9,9 @@ module Fix
8
9
  #
9
10
  class MessagePart
10
11
 
11
- attr_accessor :parse_failure, :name
12
+ extend Forwardable
13
+
14
+ attr_accessor :parse_failure, :name, :delegations
12
15
 
13
16
  def initialize(opts = {})
14
17
  self.name = opts[:name]
@@ -56,14 +59,12 @@ module Fix
56
59
  # part instance by the constructor. Usually one node is initialized for each structure element
57
60
  #
58
61
  def initialize_node(node)
59
- if node[:node_type] == :part
62
+ if [:unordered, :part].include?(node[:node_type])
60
63
  nodes << node[:klass].new(node)
61
64
  elsif node[:node_type] == :field
62
65
  nodes << FP::Field.new(node)
63
66
  elsif node[:node_type] == :collection
64
67
  nodes << FP::RepeatingMessagePart.new(node)
65
- elsif node[:node_type] == :unordered
66
- nodes << node[:klass].new(node)
67
68
  end
68
69
  end
69
70
 
@@ -95,6 +96,16 @@ module Fix
95
96
  instce
96
97
  end
97
98
 
99
+ #
100
+ # Defines the attributes that should be accessible from the parent node
101
+ #
102
+ # @param args [Array<Symbol>] The methods that the parent should respond to and delegate to the child
103
+ #
104
+ def self.parent_delegate(*args)
105
+ @delegations ||= []
106
+ args.each { |a| @delegations << a }
107
+ end
108
+
98
109
  #
99
110
  # Defines a collection as a part of this message part, collections typically have a counter and a repeating element
100
111
  #
@@ -107,6 +118,8 @@ module Fix
107
118
  define_method(name) do
108
119
  node_for_name(name)
109
120
  end
121
+
122
+ parent_delegate(name)
110
123
  end
111
124
 
112
125
  #
@@ -115,8 +128,30 @@ module Fix
115
128
  # @param name [String] The part name, this will be the name of a dynamically created accessor on the message part
116
129
  # @param opts [Hash] Options hash
117
130
  #
118
- def self.part(name, opts = {})
119
- structure << { node_type: :part, name: name }.merge(opts)
131
+ def self.part(name, opts = {}, &block)
132
+ options = { node_type: :part, name: name, delegate: true }.merge(opts)
133
+ klass = options[:klass]
134
+
135
+ # If a block is given it overrides the +:klass+ option
136
+ if block_given?
137
+ names = (to_s + name.to_s.split(/\_/).map(&:capitalize).join).split('::')
138
+ klass = names.pop
139
+ parent_klass = (options[:node_type] == :part) ? MessagePart : UnorderedPart
140
+ klass = names.inject(Object) { |mem, obj| mem = mem.const_get(obj) }.const_set(klass, Class.new(parent_klass))
141
+ klass.instance_eval(&block)
142
+ options.merge!({ klass: klass })
143
+ elsif options[:klass]
144
+ parent_delegate(name)
145
+ end
146
+
147
+ # Do we need to delegate some methods from the parent node ?
148
+ delegations = klass.instance_variable_get(:@delegations)
149
+ if delegations && !delegations.empty? && options[:delegate]
150
+ def_delegators(name, *delegations)
151
+ parent_delegate(*delegations)
152
+ end
153
+
154
+ structure << options.merge(opts)
120
155
 
121
156
  define_method(name) do
122
157
  node_for_name(name)
@@ -129,11 +164,10 @@ module Fix
129
164
  # @param name [String] The part name, this will be the name of a dynamically created accessor on the message part
130
165
  # @param opts [Hash] Options hash
131
166
  #
132
- def self.unordered(name, opts = {})
133
- part(name, opts.merge({ node_type: :unordered }))
167
+ def self.unordered(name, opts = {}, &block)
168
+ part(name, opts.merge({ node_type: :unordered }), &block)
134
169
  end
135
170
 
136
-
137
171
  #
138
172
  # Defines a field as part of the structure for this class
139
173
  #
@@ -162,6 +196,9 @@ module Fix
162
196
  node_for_name(name).raw_value = val
163
197
  end
164
198
  end
199
+
200
+ # Delegate getter and setter from parent node
201
+ parent_delegate(name, "#{name}=")
165
202
  end
166
203
 
167
204
  #
@@ -9,9 +9,17 @@ module Fix
9
9
 
10
10
  field :symbol, tag: 55, required: true
11
11
 
12
- end
12
+ #
13
+ # Checks whether the start of the given string can be parsed as this particular field
14
+ #
15
+ # @param str [String] The string for which we want to parse the beginning
16
+ # @return [Boolean] Whether the beginning of the string can be parsed for this field
17
+ #
18
+ def can_parse?(str)
19
+ str =~ /55\=[^\x01]+\x01/
20
+ end
13
21
 
22
+ end
14
23
  end
15
24
  end
16
25
  end
17
-
@@ -1,5 +1,3 @@
1
- require 'fix/protocol/messages/logon_body'
2
-
3
1
  module Fix
4
2
  module Protocol
5
3
  module Messages
@@ -9,11 +7,12 @@ module Fix
9
7
  #
10
8
  class Logon < Message
11
9
 
12
- extend Forwardable
13
- def_delegators :body, :encrypt_method, :encrypt_method=, :heart_bt_int, :heart_bt_int=,
14
- :username, :username=, :reset_seq_num, :reset_seq_num=
15
-
16
- unordered :body, klass: LogonBody
10
+ unordered :body do
11
+ field :encrypt_method, tag: 98, required: true, type: :integer, default: 0
12
+ field :heart_bt_int, tag: 108, required: true, type: :integer, default: 30
13
+ field :username, tag: 553, required: true
14
+ field :reset_seq_num_flag, tag: 141, type: :yn_bool, default: false
15
+ end
17
16
 
18
17
  #
19
18
  # Returns the logon-specific errors
@@ -10,8 +10,10 @@ module Fix
10
10
  #
11
11
  class MarketDataIncrementalRefresh < Message
12
12
 
13
- field :md_req_id, tag: 262, required: true
14
- collection :md_entries, counter_tag: 268, klass: FP::Messages::MdEntry
13
+ unordered :body do
14
+ field :md_req_id, tag: 262, required: true
15
+ collection :md_entries, counter_tag: 268, klass: FP::Messages::MdEntry
16
+ end
15
17
 
16
18
  end
17
19
  end
@@ -14,9 +14,9 @@ module Fix
14
14
  # The subscription type, see: http://www.onixs.biz/fix-dictionary/4.4/tagNum_263.html
15
15
  #
16
16
  SUBSCRIPTION_TYPES = {
17
- snapshot: 1,
18
- updates: 2,
19
- unsubscribe: 3
17
+ snapshot: 0,
18
+ updates: 1,
19
+ unsubscribe: 2
20
20
  }
21
21
 
22
22
  #
@@ -35,13 +35,27 @@ module Fix
35
35
  incremental: 1
36
36
  }
37
37
 
38
- field :md_req_id, tag: 262, required: true
39
- field :subscription_request_type, tag: 263, required: true, type: :integer, mapping: SUBSCRIPTION_TYPES
40
- field :market_depth, tag: 264, required: true, type: :integer, mapping: MKT_DPTH_TYPES
41
- field :md_update_type, tag: 265, required: true, type: :integer, mapping: UPDATE_TYPES
38
+ unordered :body do
39
+ field :md_req_id, tag: 262, required: true
40
+ field :subscription_request_type, tag: 263, required: true, type: :integer, mapping: SUBSCRIPTION_TYPES
41
+ field :market_depth, tag: 264, required: true, type: :integer, mapping: MKT_DPTH_TYPES
42
+ field :md_update_type, tag: 265, required: true, type: :integer, mapping: UPDATE_TYPES
42
43
 
43
- collection :md_entry_types, counter_tag: 267, klass: FP::Messages::MdEntryType
44
- collection :instruments, counter_tag: 146, klass: FP::Messages::Instrument
44
+ collection :md_entry_types, counter_tag: 267, klass: FP::Messages::MdEntryType
45
+ collection :instruments, counter_tag: 146, klass: FP::Messages::Instrument
46
+ end
47
+
48
+ #
49
+ # Check that the collections aren't empty
50
+ #
51
+ def errors
52
+ errs = []
53
+
54
+ errs << "MdEntryTypes can not be empty" if md_entry_types.empty?
55
+ errs << "Instruments can not be empty" if instruments.empty?
56
+
57
+ [super, errs].flatten
58
+ end
45
59
 
46
60
  end
47
61
  end
@@ -10,11 +10,11 @@ module Fix
10
10
  #
11
11
  class MarketDataSnapshot < Message
12
12
 
13
- field :md_req_id, tag: 262, required: true
14
-
15
- part :instrument, klass: FP::Messages::Instrument
16
-
17
- collection :md_entries, counter_tag: 268, klass: FP::Messages::MdEntry
13
+ unordered :body do
14
+ field :md_req_id, tag: 262, required: true
15
+ part :instrument, klass: FP::Messages::Instrument
16
+ collection :md_entries, counter_tag: 268, klass: FP::Messages::MdEntry
17
+ end
18
18
 
19
19
  end
20
20
  end
@@ -7,8 +7,10 @@ module Fix
7
7
  #
8
8
  class Reject < Message
9
9
 
10
- field :ref_seq_num, tag: 45, required: true, type: :integer
11
- field :text, tag: 58, required: true
10
+ unordered :body do
11
+ field :ref_seq_num, tag: 45, required: true, type: :integer
12
+ field :text, tag: 58, required: true
13
+ end
12
14
 
13
15
  end
14
16
  end
@@ -7,8 +7,10 @@ module Fix
7
7
  #
8
8
  class ResendRequest < Message
9
9
 
10
- field :begin_seq_no, tag: 7, required: true, type: :integer
11
- field :end_seq_no, tag: 16, required: true, type: :integer, default: 0
10
+ unordered :body do
11
+ field :begin_seq_no, tag: 7, required: true, type: :integer
12
+ field :end_seq_no, tag: 16, required: true, type: :integer, default: 0
13
+ end
12
14
 
13
15
  #
14
16
  # Returns the logon-specific errors
@@ -11,7 +11,7 @@ module Fix
11
11
  class RepeatingMessagePart < MessagePart
12
12
 
13
13
  extend Forwardable
14
- def_delegators :nodes, :[], :first, :last, :length, :size, :each
14
+ def_delegators :nodes, :[], :first, :last, :length, :size, :each, :empty?
15
15
 
16
16
  include Enumerable
17
17
 
@@ -45,6 +45,16 @@ module Fix
45
45
  "#{counter_tag}=#{length}\x01#{super}"
46
46
  end
47
47
 
48
+ #
49
+ # Checks whether the start of the given string can be parsed as this particular field
50
+ #
51
+ # @param str [String] The string for which we want to parse the beginning
52
+ # @return [Boolean] Whether the beginning of the string can be parsed for this field
53
+ #
54
+ def can_parse?(str)
55
+ str =~ /^#{counter_tag}\=[^\x01]+\x01/
56
+ end
57
+
48
58
  #
49
59
  # Parses an arbitrary number of nodes according to the found counter tag
50
60
  #
@@ -54,8 +64,6 @@ module Fix
54
64
  @nodes = []
55
65
  len.times { @nodes << element_klass.new }
56
66
  super(str.gsub(/^#{counter_tag}\=[^\x01]+\x01/, ''))
57
- else
58
- self.parse_failure = "Expected <#{str}> to begin with <#{counter_tag}=...|>"
59
67
  end
60
68
  end
61
69
 
@@ -49,6 +49,26 @@ module Fix
49
49
  i.to_s
50
50
  end
51
51
 
52
+ #
53
+ # Dumps a boolean to a Y/N FIX string
54
+ #
55
+ # @param b [Boolean] A boolean
56
+ # @return [String] 'Y' if the parameter is true, 'N' otherwise
57
+ #
58
+ def dump_yn_bool(b)
59
+ b ? 'Y' : 'N'
60
+ end
61
+
62
+ #
63
+ # Parses a string into a boolean value
64
+ #
65
+ # @param str [String] The string to parse
66
+ # @return [Boolean] +true+ if the string is 'Y', +false+ otherwise
67
+ #
68
+ def parse_yn_bool(str)
69
+ !!(str == 'Y')
70
+ end
71
+
52
72
  end
53
73
  end
54
74
  end
@@ -12,10 +12,12 @@ module Fix
12
12
  # @return [String] The string part that wasn't consumed during the parsing
13
13
  #
14
14
  def parse(str)
15
+
15
16
  left_to_parse = str
16
17
  left_before_pass = nil
17
18
 
18
19
  while (left_to_parse != left_before_pass) && !parse_failure
20
+
19
21
  left_before_pass = left_to_parse
20
22
 
21
23
  nodes.each do |node|
@@ -4,7 +4,7 @@ module Fix
4
4
  #
5
5
  # The fix-protocol gem version string
6
6
  #
7
- VERSION = '0.0.53'
7
+ VERSION = '0.0.64'
8
8
 
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fix-protocol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.53
4
+ version: 0.0.64
5
5
  platform: ruby
6
6
  authors:
7
7
  - David François
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-31 00:00:00.000000000 Z
11
+ date: 2014-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -132,15 +132,12 @@ files:
132
132
  - lib/fix/protocol/field.rb
133
133
  - lib/fix/protocol/grammar.treetop
134
134
  - lib/fix/protocol/grammar_extensions/message.rb
135
- - lib/fix/protocol/header_fields.rb
136
135
  - lib/fix/protocol/message.rb
137
136
  - lib/fix/protocol/message_class_mapping.rb
138
- - lib/fix/protocol/message_header.rb
139
137
  - lib/fix/protocol/message_part.rb
140
138
  - lib/fix/protocol/messages/heartbeat.rb
141
139
  - lib/fix/protocol/messages/instrument.rb
142
140
  - lib/fix/protocol/messages/logon.rb
143
- - lib/fix/protocol/messages/logon_body.rb
144
141
  - lib/fix/protocol/messages/logout.rb
145
142
  - lib/fix/protocol/messages/market_data_incremental_refresh.rb
146
143
  - lib/fix/protocol/messages/market_data_request.rb
@@ -1,18 +0,0 @@
1
- module Fix
2
- module Protocol
3
-
4
- #
5
- # The fields in the header for which the order is not enforced
6
- #
7
- class HeaderFields < UnorderedPart
8
-
9
- field :msg_type, tag: 35, required: true
10
- field :sender_comp_id, tag: 49, required: true
11
- field :target_comp_id, tag: 56, required: true
12
- field :msg_seq_num, tag: 34, required: true, type: :integer
13
- field :sending_time, tag: 52, required: true, type: :timestamp, default: proc { Time.now }
14
-
15
- end
16
-
17
- end
18
- end
@@ -1,36 +0,0 @@
1
- require 'fix/protocol/message_part'
2
- require 'fix/protocol/header_fields'
3
-
4
- module Fix
5
- module Protocol
6
-
7
- #
8
- # The standard FIX message header
9
- #
10
- class MessageHeader < MessagePart
11
-
12
- extend Forwardable
13
- def_delegators :header_fields, :msg_type, :msg_type=, :sender_comp_id, :sender_comp_id=, :target_comp_id,
14
- :target_comp_id=, :msg_seq_num, :msg_seq_num=, :sending_time, :sending_time=
15
-
16
- field :version, tag: 8, required: true, default: 'FIX.4.4'
17
- field :body_length, tag: 9
18
-
19
- unordered :header_fields, klass: HeaderFields
20
-
21
- #
22
- # Returns the errors relevant to the message header
23
- #
24
- # @return [Array<String>] The errors on the message header
25
- #
26
- def errors
27
- if version == 'FIX.4.4'
28
- super
29
- else
30
- [super, "Unsupported version: <#{version}>"].flatten
31
- end
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,18 +0,0 @@
1
- module Fix
2
- module Protocol
3
- module Messages
4
-
5
- #
6
- # Body of a logon message
7
- #
8
- class LogonBody < UnorderedPart
9
-
10
- field :encrypt_method, tag: 98, required: true, type: :integer, default: 0
11
- field :heart_bt_int, tag: 108, required: true, type: :integer, default: 30
12
- field :username, tag: 553, required: true
13
- field :reset_seq_num_flag, tag: 141
14
-
15
- end
16
- end
17
- end
18
- end