module Binary::Protocol::ClassMethods
Provides a DSL for defining struct-like fields for building binary messages.
@example
class Command include Binary::Protocol int32 :length end Command.fields # => [:length] command = Command.new command.length = 12 command.serialize_length("") # => "\f\x00\x00\x00"
Public Instance Methods
deserialize(buffer = nil, &block)
click to toggle source
# File lib/binary/protocol.rb, line 214 def deserialize(buffer = nil, &block) if block_given? re_define_method(:deserialize, &block) else message = allocate message.deserialize(buffer) message end end
fields()
click to toggle source
@return [Array] the fields defined for this message
# File lib/binary/protocol.rb, line 77 def fields @fields ||= [] end
finalize()
click to toggle source
Declares the protocol class as complete, and defines its serialization method from the declared fields.
# File lib/binary/protocol.rb, line 204 def finalize class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize(buffer = "") #{serialization.map { |command| "#{command}(buffer)" }.join("\n")} buffer end alias to_s serialize RUBY end
serialization()
click to toggle source
@return [Array] the methods to run in order for serialiation
# File lib/binary/protocol.rb, line 72 def serialization @serialization ||= [] end
string(name, options = {})
click to toggle source
Declare a string field.
@example
class Message include Binary::Protocol string :collection end
@param [String] name the name of this field
# File lib/binary/protocol.rb, line 138 def string(name, options = {}) if options.key?(:always) __define_always__(name, options[:always]) else if options.key?(:default) __define_default__(name, options[:default]) else attr_accessor name end class_eval <<-RUBY, __FILE__, __LINE__ + 1 def deserialize_#{name}(buffer) raise NotImplementedError end RUBY end class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name} end RUBY serialization << :"serialize_#{name}" fields << name end
stringz(name, options = {})
click to toggle source
Declare a null terminated string field.
@example
class Message include Binary::Protocol stringz :collection end
@param [String] name the name of this field
# File lib/binary/protocol.rb, line 174 def stringz(name, options = {}) if options.key?(:always) __define_always__(name, options[:always]) else if options.key?(:default) __define_default__(name, options[:default]) else attr_accessor name end class_eval <<-RUBY, __FILE__, __LINE__ + 1 def deserialize_#{name}(buffer) raise NotImplementedError end RUBY end class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name} buffer << 0 end RUBY serialization << :"serialize_#{name}" fields << name end
Protected Instance Methods
__bytes__(pack_const, size_const, name, options = {}, &block)
click to toggle source
# File lib/binary/protocol.rb, line 226 def __bytes__(pack_const, size_const, name, options = {}, &block) if block_given? class_eval <<-RUBY, __FILE__, __LINE__ + 1 def before_serialize_#{name}(buffer) @#{name}_start = buffer.bytesize end RUBY serialization << :"before_serialize_#{name}" end if options.key?(:always) __define_always__(name, options[:always]) else if options.key?(:default) __define_default__(name, options[:default]) else attr_accessor name end if options[:type] == :array class_eval <<-RUBY, __FILE__, __LINE__ + 1 def deserialize_#{name}(buffer) raise NotImplementedError end RUBY else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def deserialize_#{name}(buffer) self.#{name}, = buffer.read(#{size_const}).unpack(#{pack_const}) end RUBY end end if options[:type] == :array class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << #{name}.pack(#{pack_const}+"*") end RUBY else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def serialize_#{name}(buffer) buffer << [#{name}].pack(#{pack_const}) end RUBY end serialization << :"serialize_#{name}" if block_given? returning = fields << name instance_eval(&block) class_eval <<-RUBY, __FILE__, __LINE__ + 1 def after_serialize_#{name}(buffer) self.#{name} = buffer.bytesize - @#{name}_start - #{options[:inclusive] ? 0 : size_const} buffer[@#{name}_start, #{size_const}] = serialize_#{name} "" end RUBY serialization << :"after_serialize_#{name}" returning else fields << name end end
__define_always__(name, always)
click to toggle source
# File lib/binary/protocol.rb, line 296 def __define_always__(name, always) if always.respond_to?(:call) re_define_method(name, &always) else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= #{always.inspect} end RUBY end class_eval <<-RUBY, __FILE__, __LINE__ + 1 def deserialize_#{name}(buffer) # do nothing end RUBY end
__define_default__(name, default)
click to toggle source
# File lib/binary/protocol.rb, line 313 def __define_default__(name, default) attr_writer name if default.respond_to?(:call) dval = :"__#{name}_default_value__" ivar = :"@#{name}" re_define_method(dval, &default) re_define_method(name) do if instance_variable_defined?(ivar) instance_variable_get(ivar) else instance_variable_set(ivar, __send__(dval)) end end else class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name} @#{name} ||= #{default.inspect} end RUBY end end
Private Instance Methods
inherited(subclass)
click to toggle source
This ensures that subclasses of the primary protocol classes have identical fields.
Calls superclass method
# File lib/binary/protocol.rb, line 340 def inherited(subclass) super subclass.serialization.replace serialization subclass.fields.replace fields end