module Bones::RPC::Protocol::BinaryHelper::ClassMethods
Provides a DSL for defining struct-like fields for building messages for the Mongo Wire.
@example
class Command extend Message::ClassMethods int32 :length end Command.fields # => [:length] command = Command.new command.length = 12 command.serialize_length("") # => "\f\x00\x00\x00"
Public Instance Methods
Declare a binary field.
@example
class Query < Message binary :collection end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 110 def binary(name) attr_accessor name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name} end RUBY fields << name end
Declare a null terminated string field.
@example
class Query < Message cstring :collection end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 130 def cstring(name) attr_accessor name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name} buffer << 0 end RUBY fields << name end
Declare a BSON Document field.
@example
class Update < Message document :selector end
@example optional document field
class Query < Message document :selector document :fields, optional: true end
@example array of documents
class Reply < Message document :documents, type: :array end
@param [String] name the name of this field @param [Hash] options the options for this field @option options [:array] :type specify an array of documents @option options [Boolean] :optional specify this field as optional
# File lib/bones/rpc/protocol/binary_helper.rb, line 165 def document(name, options = {}) attr_accessor name if options[:optional] class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name}.to_bson if #{name} end RUBY elsif options[:type] == :array class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) #{name}.each do |document| buffer << document.to_bson end end RUBY else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name}.to_bson end RUBY end fields << name end
@return [Array] the fields defined for this message
# File lib/bones/rpc/protocol/binary_helper.rb, line 98 def fields @fields ||= [] end
Declares the message class as complete, and defines its serialization method from the declared fields.
# File lib/bones/rpc/protocol/binary_helper.rb, line 409 def finalize class_eval <<-EOS, __FILE__, __LINE__ + 1 def serialize(buffer = "", adapter = nil) #{fields.map { |f| "serialize_#{f}(buffer)" }.join("\n")} buffer end alias :to_s :serialize EOS end
Declare a flag field (32 bit signed integer)
@example
class Update < Message flags :flags, upsert: 2 ** 0, multi: 2 ** 1 end
@param [String] name the name of this field @param [Hash{Symbol => Number}] flags the flags for this flag field
# File lib/bones/rpc/protocol/binary_helper.rb, line 203 def flags(name, flag_map = {}) class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= [] end def #{name}=(flags) if flags.is_a? Numeric @#{name} = #{name}_from_int(flags) else @#{name} = flags end end def #{name}_as_int bits = 0 flags = self.#{name} #{flag_map.map { |flag, value| "bits |= #{value} if flags.include? #{flag.inspect}" }.join "\n"} bits end def #{name}_from_int(bits) flags = [] #{flag_map.map { |flag, value| "flags << #{flag.inspect} if #{value} & bits == #{value}" }.join "\n"} flags end def serialize_#{name}(buffer) buffer << [#{name}_as_int].pack('l<') end def deserialize_#{name}(buffer) bits, = buffer.read(4).unpack('l<') self.#{name} = bits end RUBY fields << name end
Declare a 32 bit signed integer field.
@example
class Query < Message int32 :length end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 308 def int32(name) attr_writer name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= 0 end def serialize_#{name}(buffer) buffer << [#{name}].pack('l<') end def deserialize_#{name}(buffer) self.#{name}, = buffer.read(4).unpack('l<') end RUBY fields << name end
Declare a 64 bit signed integer field.
@example
class Query < Message int64 :cursor_id end
@example with array type
class KillCursors < Message int64 :cursor_ids, type: :array end
@param [String] name the name of this field @param [Hash] options the options for this field @option options [:array] :type specify an array of 64 bit ints
# File lib/bones/rpc/protocol/binary_helper.rb, line 371 def int64(name, options = {}) attr_writer name if options[:type] == :array class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= [] end def serialize_#{name}(buffer) buffer << #{name}.pack('q<*') end def deserialize_#{name}(buffer) raise NotImplementedError end RUBY else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= 0 end def serialize_#{name}(buffer) buffer << [#{name}].pack('q<') end def deserialize_#{name}(buffer) self.#{name}, = buffer.read(8).unpack('q<') end RUBY end fields << name end
Declare a 8 bit signed integer field.
@example
class Query < Message int8 :length end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 280 def int8(name) attr_writer name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= 0 end def serialize_#{name}(buffer) buffer << [#{name}].pack('c') end def deserialize_#{name}(buffer) self.#{name}, = buffer.read(1).unpack('c') end RUBY fields << name end
Declare a 32 bit unsigned integer field.
@example
class Query < Message uint32 :length end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 336 def uint32(name) attr_writer name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= 0 end def serialize_#{name}(buffer) buffer << [#{name}].pack('N') end def deserialize_#{name}(buffer) self.#{name}, = buffer.read(4).unpack('N') end RUBY fields << name end
Declare a 8 bit unsigned integer field.
@example
class Query < Message uint8 :length end
@param [String] name the name of this field
# File lib/bones/rpc/protocol/binary_helper.rb, line 252 def uint8(name) attr_writer name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= 0 end def serialize_#{name}(buffer) buffer << [#{name}].pack('C') end def deserialize_#{name}(buffer) self.#{name}, = buffer.read(1).unpack('C') end RUBY fields << name end
Private Instance Methods
This ensures that subclasses of the primary wire message classes have identical fields.
# File lib/bones/rpc/protocol/binary_helper.rb, line 423 def inherited(subclass) super subclass.fields.replace(fields) end