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

binary(name) click to toggle source

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
cstring(name) click to toggle source

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
document(name, options = {}) click to toggle source

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
fields() click to toggle source

@return [Array] the fields defined for this message

# File lib/bones/rpc/protocol/binary_helper.rb, line 98
def fields
  @fields ||= []
end
finalize() click to toggle source

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
flags(name, flag_map = {}) click to toggle source

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
int32(name) click to toggle source

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
int64(name, options = {}) click to toggle source

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
int8(name) click to toggle source

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
uint32(name) click to toggle source

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
uint8(name) click to toggle source

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

inherited(subclass) click to toggle source

This ensures that subclasses of the primary wire message classes have identical fields.

Calls superclass method
# File lib/bones/rpc/protocol/binary_helper.rb, line 423
def inherited(subclass)
  super
  subclass.fields.replace(fields)
end