Class: IB::Messages::Incoming::AbstractMessage

Inherits:
AbstractMessage show all
Defined in:
lib/ib/messages/incoming/abstract_message.rb

Direct Known Subclasses

AbstractTick

Instance Attribute Summary

Attributes inherited from AbstractMessage

#created_at, #data

Instance Method Summary collapse

Methods inherited from AbstractMessage

data_map, message_id, #message_id, message_type, #message_type, #to_human, version

Constructor Details

#initialize(source) ⇒ AbstractMessage

Create incoming message from a given source (IB server or data Hash)



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ib/messages/incoming/abstract_message.rb', line 23

def initialize source
  @created_at = Time.now
  if source[:socket] # Source is a server
    @server = source
    @data = Hash.new
    begin
      self.load
    rescue => e
      error "Reading #{self.class}: #{e.class}: #{e.message}", :load, e.backtrace
    ensure
      @server = nil
    end
  else # Source is a @data Hash
    @data = source
  end
end

Instance Method Details

#check_version(actual, expected) ⇒ Object



16
17
18
19
20
# File 'lib/ib/messages/incoming/abstract_message.rb', line 16

def check_version actual, expected
  unless actual == expected || expected.is_a?(Array) && expected.include?(actual)
    error "Unsupported version #{actual} received, expected #{expected}"
  end
end

#loadObject

Every message loads received message version first Override the load method in your subclass to do actual reading into @data.



46
47
48
49
50
51
52
# File 'lib/ib/messages/incoming/abstract_message.rb', line 46

def load
  @data[:version] = socket.read_int

  check_version @data[:version], self.class.version

  load_map *self.class.data_map
end

#load_map(*map) ⇒ Object

Load @data from the socket according to the given data map.

map is a series of Arrays in the format of

[ :name, :type ], [  :group, :name, :type]

type identifiers must have a corresponding read_type method on socket (read_int, etc.). group is used to lump together aggregates, such as Contract or Order fields



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ib/messages/incoming/abstract_message.rb', line 60

def load_map(*map)
  map.each do |instruction|
    # We determine the function of the first element
    head = instruction.first
    case head
      when Integer # >= Version condition: [ min_version, [map]]
        load_map *instruction.drop(1) if version >= head

      when Proc # Callable condition: [ condition, [map]]
        load_map *instruction.drop(1) if head.call

      when true # Pre-condition already succeeded!
        load_map *instruction.drop(1)

      when nil, false # Pre-condition already failed! Do nothing...

      when Symbol # Normal map
        group, name, type, block =
            if  instruction[2].nil? || instruction[2].is_a?(Proc)
              [nil] + instruction # No group, [ :name, :type, (:block) ]
            else
              instruction # [ :group, :name, :type, (:block)]
            end

        data = socket.__send__("read_#{type}", &block)
        if group
          @data[group] ||= {}
          @data[group][name] = data
        else
          @data[name] = data
        end
      else
        error "Unrecognized instruction #{instruction}"
    end
  end
end

#socketObject



40
41
42
# File 'lib/ib/messages/incoming/abstract_message.rb', line 40

def socket
  @server[:socket]
end

#versionObject

Per message, received messages may have the different versions



12
13
14
# File 'lib/ib/messages/incoming/abstract_message.rb', line 12

def version # Per message, received messages may have the different versions
  @data[:version]
end