lib/bindata/sanitize.rb in bindata-1.1.0 vs lib/bindata/sanitize.rb in bindata-1.2.0
- old
+ new
@@ -1,44 +1,53 @@
require 'bindata/registry'
module BinData
- class UnknownTypeError < StandardError ; end
-
- # A BinData object accepts arbitrary parameters. This class sanitizes
- # those parameters so they can be used by the BinData object.
+ # When a BinData object is instantiated, it can be supplied parameters to
+ # determine its behaviour. These parameters must be sanitized to ensure
+ # their values are valid. When instantiating many objects, such as an array
+ # of records, there is much duplicated validation.
+ #
+ # The purpose of the sanitizing code is to eliminate the duplicated
+ # validation.
+ #
+ # SanitizedParameters is a hash-like collection of parameters. Its purpose
+ # it to recursively sanitize the parameters of an entire BinData object chain
+ # at a single time.
class SanitizedParameters
- def initialize(params, the_class)
+ def initialize(parameters, the_class)
@all_sanitized = false
@the_class = the_class
@parameters = {}
- params.each { |param, value| @parameters[param.to_sym] = value }
+ parameters.each { |key, value| @parameters[key.to_sym] = value }
ensure_no_nil_values
end
def length
@parameters.size
end
alias_method :size, :length
- def [](param)
- @parameters[param]
+ def [](key)
+ @parameters[key]
end
- def []=(param, value)
- @parameters[param] = value unless @all_sanitized
+ def []=(key, value)
+ @parameters[key] = value unless @all_sanitized
end
- def has_parameter?(param)
- @parameters.has_key?(param)
+ def has_parameter?(key)
+ @parameters.has_key?(key)
end
- def needs_sanitizing?(param)
- has_parameter?(param) and not self[param].is_a?(SanitizedParameter)
+ def needs_sanitizing?(key)
+ parameter = @parameters[key]
+
+ parameter and not parameter.is_a?(SanitizedParameter)
end
def all_sanitized?
@all_sanitized
end
@@ -57,54 +66,56 @@
end
def move_unknown_parameters_to(dest)
unless @all_sanitized
unused_keys = @parameters.keys - @the_class.accepted_parameters.all
- unused_keys.each do |param|
- next if param == :onlyif
- dest[param] = @parameters.delete(param)
+ unused_keys.each do |key|
+ dest[key] = @parameters.delete(key)
end
end
end
- def delete(param)
- @parameters.delete(param)
+ def warn_replacement_parameter(bad_key, suggested_key)
+ if has_parameter?(bad_key)
+ warn ":#{bad_key} is not used with #{@the_class}. " +
+ "You probably want to change this to :#{suggested_key}"
+ end
end
#---------------
private
def ensure_no_nil_values
- @parameters.each do |param, value|
+ @parameters.each do |key, value|
if value.nil?
raise ArgumentError,
- "parameter '#{param}' has nil value in #{@the_class}"
+ "parameter '#{key}' has nil value in #{@the_class}"
end
end
end
def merge_default_parameters!
- @the_class.default_parameters.each do |param, value|
- self[param] ||= value
+ @the_class.default_parameters.each do |key, value|
+ @parameters[key] ||= value
end
end
def ensure_mandatory_parameters_exist
- @the_class.mandatory_parameters.each do |param|
- unless has_parameter?(param)
+ @the_class.mandatory_parameters.each do |key|
+ unless has_parameter?(key)
raise ArgumentError,
- "parameter '#{param}' must be specified in #{@the_class}"
+ "parameter '#{key}' must be specified in #{@the_class}"
end
end
end
def ensure_mutual_exclusion_of_parameters
return if length < 2
- @the_class.mutually_exclusive_parameters.each do |param1, param2|
- if has_parameter?(param1) and has_parameter?(param2)
- raise ArgumentError, "params '#{param1}' and '#{param2}' " +
+ @the_class.mutually_exclusive_parameters.each do |key1, key2|
+ if has_parameter?(key1) and has_parameter?(key2)
+ raise ArgumentError, "params '#{key1}' and '#{key2}' " +
"are mutually exclusive in #{@the_class}"
end
end
end
@@ -140,48 +151,47 @@
sanitized_params
end
def create_sanitized_endian(endian)
- SanitizedEndian.new(endian)
+ # memoize return value to reduce memory usage
+ if endian == :big
+ @@sbe ||= SanitizedBigEndian.new
+ elsif endian == :little
+ @@sle ||= SanitizedLittleEndian.new
+ else
+ raise ArgumentError, "unknown value for endian '#{endian}'"
+ end
end
def create_sanitized_choices(choices)
SanitizedChoices.new(self, choices)
end
- def create_sanitized_fields
- SanitizedFields.new(self)
- end
-
- def clone_sanitized_fields(fields)
+ def create_sanitized_fields(fields = nil)
new_fields = SanitizedFields.new(self)
- new_fields.copy_fields(fields)
+ new_fields.copy_fields(fields) if fields
new_fields
end
def create_sanitized_object_prototype(obj_type, obj_params, endian = nil)
SanitizedPrototype.new(self, obj_type, obj_params, endian)
end
def with_endian(endian, &block)
if endian != nil
saved_endian = @endian
- @endian = endian.is_a?(SanitizedEndian) ? endian.endian : endian
+ @endian = endian.respond_to?(:endian) ? endian.endian : endian
yield
@endian = saved_endian
else
yield
end
end
def lookup_class(type)
- registered_class = RegisteredClasses.lookup(type, @endian)
- if registered_class.nil?
- raise UnknownTypeError, type.to_s
- end
- registered_class
+ RegisteredClasses.lookup(type, @endian)
end
#---------------
private
@@ -226,27 +236,35 @@
class SanitizedFields < SanitizedParameter
def initialize(sanitizer)
@sanitizer = sanitizer
@fields = []
+ @field_names = nil
end
+ attr_reader :fields
def add_field(type, name, params, endian)
+ @field_names = nil
@fields << SanitizedField.new(@sanitizer, name, type, params, endian)
end
def [](idx)
@fields[idx]
end
+ def empty?
+ @fields.empty?
+ end
+
def field_names
- @fields.collect { |field| field.name }
+ # memoize field names to reduce duplicate copies
+ @field_names ||= @fields.collect { |field| field.name }
end
def copy_fields(other)
- other_fields = other.instance_variable_get(:@fields)
- @fields.concat(other_fields)
+ @field_names = nil
+ @fields.concat(other.fields)
end
end
#----------------------------------------------------------------------------
class SanitizedChoices < SanitizedParameter
@@ -263,17 +281,17 @@
@choices[key]
end
end
#----------------------------------------------------------------------------
- class SanitizedEndian < SanitizedParameter
- def initialize(endian)
- unless [:little, :big].include?(endian)
- raise ArgumentError, "unknown value for endian '#{endian}'"
- end
-
- @endian = endian
+ class SanitizedBigEndian < SanitizedParameter
+ def endian
+ :big
end
+ end
- attr_reader :endian
+ class SanitizedLittleEndian < SanitizedParameter
+ def endian
+ :little
+ end
end
end