lib/rasn1/model.rb in rasn1-0.6.8 vs lib/rasn1/model.rb in rasn1-0.7.0

- old
+ new

@@ -1,7 +1,8 @@ -module RASN1 +# frozen_string_literal: true +module RASN1 # @abstract # {Model} class is a base class to define ASN.1 models. # == Create a simple ASN.1 model # Given this ASN.1 example: # Record ::= SEQUENCE { @@ -56,11 +57,10 @@ # # All methods defined by root may be delegated by model, unless model also defines # this method. # @author Sylvain Daubert class Model - class << self # @return [Hash] attr_reader :options # Use another model in this model @@ -90,11 +90,11 @@ # On inheritance, create +@root+ class variable # @param [Class] klass # @return [void] def inherited(klass) super - root = defined?(@root )? @root : nil + root = @root klass.class_eval { @root = root } end # @method sequence(name, options) # @param [Symbol,String] name name of object in model @@ -106,20 +106,20 @@ # @see Types::Set#initialize # @method choice(name, options) # @param [Symbol,String] name name of object in model # @param [Hash] options # @see Types::Choice#initialize - %w(sequence set choice).each do |type| + %w[sequence set choice].each do |type| class_eval "def #{type}(name, options={})\n" \ " options.merge!(name: name)\n" \ - " proc = Proc.new do |opts|\n" \ + " proc = proc do |opts|\n" \ " Types::#{type.capitalize}.new(options.merge(opts))\n" \ " end\n" \ " @root = [name, proc]\n" \ " @root << options[:content] unless options[:content].nil?\n" \ " @root\n" \ - "end" + 'end' end # @method sequence_of(name, type, options) # @param [Symbol,String] name name of object in model # @param [Model, Types::Base] type type for SEQUENCE OF @@ -128,19 +128,19 @@ # @method set_of(name, type, options) # @param [Symbol,String] name name of object in model # @param [Model, Types::Base] type type for SET OF # @param [Hash] options # @see Types::SetOf#initialize - %w(sequence set).each do |type| + %w[sequence set].each do |type| klass_name = "Types::#{type.capitalize}Of" class_eval "def #{type}_of(name, type, options={})\n" \ " options.merge!(name: name)\n" \ - " proc = Proc.new do |opts|\n" \ + " proc = proc do |opts|\n" \ " #{klass_name}.new(type, options.merge(opts))\n" \ " end\n" \ " @root = [name, proc]\n" \ - "end" + 'end' end # @method boolean(name, options) # @param [Symbol,String] name name of object in model # @param [Hash] options @@ -185,44 +185,46 @@ # @param [Symbol,String] name name of object in model # @param [Hash] options # @see Types::IA5String#initialize Types.primitives.each do |prim| next if prim == Types::ObjectId + method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_') class_eval "def #{method_name}(name, options={})\n" \ " options.merge!(name: name)\n" \ - " proc = Proc.new do |opts|\n" \ - " #{prim.to_s}.new(options.merge(opts))\n" \ + " proc = proc do |opts|\n" \ + " #{prim}.new(options.merge(opts))\n" \ " end\n" \ " @root = [name, proc]\n" \ - "end" + 'end' end # @param [Symbol,String] name name of object in model # @param [Hash] options # @note This method is named +objectid+ and not +object_id+ to not override # +Object#object_id+. # @see Types::ObjectId#initialize def objectid(name, options={}) options.merge!(name: name) - proc = Proc.new { |opts| Types::ObjectId.new(options.merge(opts)) } + proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) } @root = [name, proc] end # @param [Symbol,String] name name of object in model # @param [Hash] options # @see Types::Any#initialize def any(name, options={}) options.merge!(name: name) - proc = Proc.new { |opts| Types::Any.new(options.merge(opts)) } + proc = proc { |opts| Types::Any.new(options.merge(opts)) } @root = [name, proc] end # Give type name (aka class name) # @return [String] def type return @type if defined? @type + @type = self.to_s.gsub(/.*::/, '') end # Parse a DER/BER encoded string # @param [String] str @@ -254,11 +256,12 @@ # Set value of element +name+. Element should be a {Base}. # @param [String,Symbol] name # @param [Object] value # @return [Object] value def []=(name, value) - raise Error, "cannot set value for a Model" if @elements[name].is_a? Model + raise Error, 'cannot set value for a Model' if @elements[name].is_a? Model + @elements[name].value = value end # Get name frm root type # @return [String,Symbol] @@ -302,20 +305,51 @@ # @raise [ASN1Error] error on parsing def parse!(str, ber: false) @elements[@root].parse!(str.dup.force_encoding('BINARY'), ber: ber) end + # @overload value + # Get value of root element + # @return [Object,nil] + # @overload value(name) + # Direct access to the value of +name+ (nested) element of model. + # @param [String,Symbol] name + # @return [Object,nil] + # @return [Object,nil] + def value(name=nil, *args) + if name.nil? + @elements[@root].value + else + elt = by_name(name) + + unless args.empty? + elt = elt.root if elt.is_a?(Model) + args.each do |arg| + elt = elt.root if elt.is_a?(Model) + elt = elt[arg] + end + end + + elt.value + end + end + # Delegate some methods to root element # @param [Symbol] meth def method_missing(meth, *args) if @elements[@root].respond_to? meth @elements[@root].send meth, *args else super end end + # @return [Boolean] + def respond_to_missing?(meth, *) + @elements[@root].respond_to?(meth) || super + end + # @return [String] def inspect(level=0) ' ' * level + "(#{type}) #{root.inspect(-level)}" end @@ -324,18 +358,33 @@ # @return [Boolean] def ==(other) (other.class == self.class) && (other.to_der == self.to_der) end - private + protected - def is_composed?(el) - [Types::Sequence, Types::Set].include? el.class + # Give a (nested) element from its name + # @param [String, Symbol] name + # @return [Model, Types::Base] + def by_name(name) + elt = self[name] + return elt unless elt.nil? + + keys.each do |subelt_name| + if self[subelt_name].is_a?(Model) + elt = self[subelt_name][name] + return elt unless elt.nil? + end + end + + nil end - def is_of?(el) - [Types::SequenceOf, Types::SetOf].include? el.class + private + + def composed?(elt) + [Types::Sequence, Types::Set].include? elt.class end def get_type(proc_or_class, options={}) case proc_or_class when Proc @@ -351,20 +400,20 @@ @elements = {} @elements[@root] = get_type(root[1], self.class.options || {}) root end - def set_elements(name, el, content=nil) - if content.is_a? Array - @elements[name].value = content.map do |name2, proc_or_class, content2| - subel = get_type(proc_or_class) - @elements[name2] = subel - if is_composed?(subel) and content2.is_a? Array - set_elements(name2, proc_or_class, content2) - end - subel + def set_elements(name, _elt, content=nil) + return unless content.is_a? Array + + @elements[name].value = content.map do |name2, proc_or_class, content2| + subel = get_type(proc_or_class) + @elements[name2] = subel + if composed?(subel) && content2.is_a?(Array) + set_elements(name2, proc_or_class, content2) end + subel end end def initialize_elements(obj, args) args.each do |name, value| @@ -408,13 +457,14 @@ else my_element.value.map { |el| private_to_h(el) } end when Types::Sequence seq = my_element.value.map do |el| - next if el.optional? and el.value.nil? - name = el.is_a?(Model) ? @elements.key(el) : el.name - [name, private_to_h(el)] - end + next if el.optional? && el.value.nil? + + name = el.is_a?(Model) ? @elements.key(el) : el.name + [name, private_to_h(el)] + end seq.compact! Hash[seq] else my_element.value end