class BinData::Choice
- selection changes. Default is false.
selection to the current selection whenever the:copy_on_change
- If set to true, copy the value of the previous
specifies the currently active choice.:selection
- An index/key into the :choices array/hash which
not contain symbols as keys.
implementation constraint is that the hash may
be provided as [type_symbol, hash_params]. An
is to have params passed to it, then it should
representing the data object type. If a choice
array/hash.values is a list of symbols
data objects. The format of the:choices
-
Either an array or a hash specifying the possible
an object. These params are:
Parameters may be provided at initialisation to control the behaviour of
== Parameters
a.to_binary_s #=> “000001”
a.selection #=> ‘little’
mychoice.replace ‘little’
a.to_binary_s #=> “001000”
a.value = 256
:selection => lambda { mychoice })
a = BinData::Choice.new(:choices => choices,
choices = {‘big’ => :uint16be, ‘little’ => :uint16le}
mychoice = ‘big’
a.value # => “Type1”
a = BinData::Choice.new(:choices => choices, :selection => 3)
choices = [ nil, nil, nil, type1, nil, type2 ]
a.value # => “Type2”
a = BinData::Choice.new(:choices => choices, :selection => 1)
choices = [ type1, type2 ]
a.value # => “Type1”
a = BinData::Choice.new(:choices => choices, :selection => 5)
choices = {5 => type1, 17 => type2}
type2 = [:string, {:value => “Type2”}]
type1 = [:string, {:value => “Type1”}]
require ‘bindata’
choice.
at any particular time. Method calls will be delegated to the active
A Choice is a collection of data objects of which only one is active
- An index/key into the :choices array/hash which
- If set to true, copy the value of the previous
def _assign(val)
def _assign(val) current_choice.assign(val) end
def _do_num_bytes
def _do_num_bytes current_choice.do_num_bytes end
def _do_read(io)
def _do_read(io) trace_selection current_choice.do_read(io) end
def _do_write(io)
def _do_write(io) current_choice.do_write(io) end
def _done_read
def _done_read current_choice.done_read end
def _snapshot
def _snapshot current_choice.snapshot end
def choices_as_hash(choices)
def choices_as_hash(choices) if choices.respond_to?(:to_ary) key_array_by_index(choices.to_ary) else choices end end
def clear #:nodoc:
def clear #:nodoc: current_choice.clear end
def clear? #:nodoc:
def clear? #:nodoc: current_choice.clear? end
def copy_previous_value_if_required(selection, obj)
def copy_previous_value_if_required(selection, obj) prev = get_previous_choice(selection) if should_copy_value?(prev, obj) obj.assign(prev) end remember_current_selection(selection) end
def current_choice
def current_choice selection = eval_parameter(:selection) if selection.nil? raise IndexError, ":selection returned nil for #{debug_name}" end obj = get_or_instantiate_choice(selection) copy_previous_value_if_required(selection, obj) obj end
def ensure_valid_keys(choices)
def ensure_valid_keys(choices) if choices.has_key?(nil) raise ArgumentError, ":choices hash may not have nil key" end if choices.keys.detect { |key| key.is_a?(Symbol) } raise ArgumentError, ":choices hash may not have symbols for keys" end end
def get_or_instantiate_choice(selection)
def get_or_instantiate_choice(selection) obj = @choices[selection] if obj.nil? obj = instantiate_choice(selection) @choices[selection] = obj end obj end
def get_previous_choice(selection)
def get_previous_choice(selection) if selection != @last_selection and @last_selection != nil @choices[@last_selection] else nil end end
def initialize(params = {}, parent = nil)
def initialize(params = {}, parent = nil) super(params, parent) @choices = {} @last_selection = nil end
def instantiate_choice(selection)
def instantiate_choice(selection) prototype = get_parameter(:choices)[selection] if prototype.nil? raise IndexError, "selection '#{selection}' does not exist in :choices for #{debug_name}" end prototype.instantiate(self) end
def key_array_by_index(array)
def key_array_by_index(array) result = {} array.each_with_index do |el, i| result[i] = el unless el.nil? end result end
def method_missing(symbol, *args, &block) #:nodoc:
def method_missing(symbol, *args, &block) #:nodoc: current_choice.__send__(symbol, *args, &block) end
def remember_current_selection(selection)
def remember_current_selection(selection) if selection != @last_selection @last_selection = selection end end
def respond_to?(symbol, include_private = false) #:nodoc:
def respond_to?(symbol, include_private = false) #:nodoc: super || current_choice.respond_to?(symbol, include_private) end
def sanitize_parameters!(params, sanitizer) #:nodoc:
def sanitize_parameters!(params, sanitizer) #:nodoc: if params.needs_sanitizing?(:choices) choices = choices_as_hash(params[:choices]) ensure_valid_keys(choices) params[:choices] = sanitizer.create_sanitized_choices(choices) end end
def selection
def selection eval_parameter(:selection) end
def selection=(v)
pc.selection = 17
pc #=> "Type1"
pc.selection = 5
pc = ProgrammaticChoice.new(:choices => choices)
choices = {5 => type1, 17 => type2}
type2 = [:string, {:value => "Type2"}]
type1 = [:string, {:value => "Type1"}]
end
attr_accessor :selection
choice :selection => :selection
class ProgrammaticChoice < BinData::Wrapper
then try something like the following.
If you really *must* be able to programmatically adjust the selection
This is deliberate to promote the declarative nature of BinData.
There is no #selection= method to complement the #selection method.
This method does not exist. This stub only exists to document why.
def selection=(v) raise NoMethodError end
def should_copy_value?(prev, cur)
def should_copy_value?(prev, cur) prev != nil and eval_parameter(:copy_on_change) == true end
def trace_selection
def trace_selection BinData::trace_message do |tracer| selection_string = eval_parameter(:selection).inspect if selection_string.length > 30 selection_string = selection_string.slice(0 .. 30) + "..." end tracer.trace("#{debug_name}-selection- => #{selection_string}") end end