fix-protocol 0.0.53 → 0.0.64

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.
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