module Moped::Protocol::Message::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

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/moped/protocol/message.rb, line 81
        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/moped/protocol/message.rb, line 116
        def document(name, options = {})
          attr_accessor name

          if options[:optional]
            class_eval <<-RUBY, __FILE__, __LINE__ + 1
              def serialize_#{name}(buffer)
                BSON::Document.serialize(#{name}, buffer) if #{name}
              end
            RUBY
          elsif options[:type] == :array
            class_eval <<-RUBY, __FILE__, __LINE__ + 1
              def serialize_#{name}(buffer)
                #{name}.each do |document|
                  BSON::Document.serialize(document, buffer)
                end
              end
            RUBY
          else
            class_eval <<-RUBY, __FILE__, __LINE__ + 1
              def serialize_#{name}(buffer)
                BSON::Document.serialize(#{name}, buffer)
              end
            RUBY
          end

          fields << name
        end
fields() click to toggle source

@return [Array] the fields defined for this message

# File lib/moped/protocol/message.rb, line 69
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/moped/protocol/message.rb, line 276
        def finalize
          class_eval <<-EOS, __FILE__, __LINE__ + 1
            def serialize(buffer = "")
              start = buffer.bytesize

              #{fields.map { |f| "serialize_#{f}(buffer)" }.join("\n")}

              self.length = buffer.bytesize - start
              buffer[start, 4] = serialize_length ""
              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/moped/protocol/message.rb, line 154
        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/moped/protocol/message.rb, line 203
        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/moped/protocol/message.rb, line 238
        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

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/moped/protocol/message.rb, line 295
def inherited(subclass)
  super

  subclass.fields.replace fields
end