lib/rasn1/model.rb in rasn1-0.10.0 vs lib/rasn1/model.rb in rasn1-0.11.0

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require_relative 'types/constrained' + 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: @@ -16,10 +18,12 @@ # content: [integer(:id), # integer(:room, implicit: 0, optional: true), # integer(:house, explicit: 1, default: 0)]) # end # + # In a model, each element must have a unique name. + # # === Parse a DER-encoded string # record = Record.parse(der_string) # record[:id] # => RASN1::Types::Integer # record[:id].value # => Integer # record[:id].to_i # => Integer @@ -51,20 +55,21 @@ # or like this: # record2 = Record2.new(rented: true, a_record: { id: 65537, room: 43 }) # # == Delegation # {Model} may delegate some methods to its root element. Thus, if root element - # is, for example, a {Types::Choice}, model may delegate +#chosen+ and +#chosen_value+. + # is, for example, a {TypeInts::Choice}, model may delegate +#chosen+ and +#chosen_value+. # # All methods defined by root may be delegated by model, unless model also defines # this method. # @author Sylvain Daubert - class Model # rubocop:disable Metrics/ClassLength + class Model # @private Elem = Struct.new(:name, :proc_or_class, :content) - class << self + # @private + module Accel # @return [Hash] attr_reader :options # Use another model in this model # @param [String,Symbol] name @@ -98,124 +103,43 @@ super root = @root klass.class_eval { @root = root } end - # @method sequence(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Sequence#initialize - # @method set(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Set#initialize - # @method choice(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Choice#initialize - %w[sequence set choice].each do |type| - class_eval "def #{type}(name, options={})\n" \ - " options.merge!(name: name)\n" \ - " proc = proc do |opts|\n" \ - " Types::#{type.capitalize}.new(options.merge(opts))\n" \ - " end\n" \ - " @root = Elem.new(name, proc, options[:content])\n" \ - 'end' + def define_type_accel_base(accel_name, klass) + singleton_class.class_eval( + "def #{accel_name}(name, options={})\n" \ + " options[:name] = name\n" \ + " proc = proc do |opts|\n" \ + " #{klass}.new(options.merge(opts))\n" \ + " end\n" \ + " @root = Elem.new(name, proc, options[:content])\n" \ + '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 - # @param [Hash] options - # @return [Elem] - # @see Types::SequenceOf#initialize - # @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 - # @return [Elem] - # @see Types::SetOf#initialize - %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 do |opts|\n" \ - " #{klass_name}.new(type, options.merge(opts))\n" \ - " end\n" \ - " @root = Elem.new(name, proc, nil)\n" \ - 'end' + def define_type_accel_of(accel_name, klass) + singleton_class.class_eval( + "def #{accel_name}_of(name, type, options={})\n" \ + " options[:name] = name\n" \ + " proc = proc do |opts|\n" \ + " #{klass}.new(type, options.merge(opts))\n" \ + " end\n" \ + " @root = Elem.new(name, proc, nil)\n" \ + 'end' + ) end - # @method boolean(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Boolean#initialize - # @method integer(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Integer#initialize - # @method bit_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::BitString#initialize - # @method octet_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::OctetString#initialize - # @method null(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Null#initialize - # @method enumerated(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Enumerated#initialize - # @method utf8_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::Utf8String#initialize - # @method numeric_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::NumericString#initialize - # @method printable_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::PrintableString#initialize - # @method visible_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @see Types::VisibleString#initialize - # @method ia5_string(name, options) - # @param [Symbol,String] name name of object in model - # @param [Hash] options - # @return [Elem] - # @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 do |opts|\n" \ - " #{prim}.new(options.merge(opts))\n" \ - " end\n" \ - " @root = Elem.new(name, proc, nil)\n" \ - 'end' + # Define an accelarator to access a type in a model definition + # @param [String] accel_name + # @param [Class] klass + def define_type_accel(accel_name, klass) + if klass < Types::SequenceOf + define_type_accel_of(accel_name, klass) + else + define_type_accel_base(accel_name, klass) + end end # @param [Symbol,String] name name of object in model # @param [Hash] options # @return [Elem] @@ -256,10 +180,109 @@ model.parse!(str, ber: ber) model end end + extend Accel + + # @method sequence(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Sequence#initialize + # @method set(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Set#initialize + # @method choice(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Choice#initialize + %w[sequence set choice].each do |type| + self.define_type_accel_base(type, Types.const_get(type.capitalize)) + 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 + # @param [Hash] options + # @return [Elem] + # @see Types::SequenceOf#initialize + # @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 + # @return [Elem] + # @see Types::SetOf#initialize + %w[sequence set].each do |type| + define_type_accel_of(type, Types.const_get("#{type.capitalize}Of")) + end + + # @method boolean(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Boolean#initialize + # @method integer(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Integer#initialize + # @method bit_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::BitString#initialize + # @method octet_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::OctetString#initialize + # @method null(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Null#initialize + # @method enumerated(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Enumerated#initialize + # @method utf8_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::Utf8String#initialize + # @method numeric_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::NumericString#initialize + # @method printable_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::PrintableString#initialize + # @method visible_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @see Types::VisibleString#initialize + # @method ia5_string(name, options) + # @param [Symbol,String] name name of object in model + # @param [Hash] options + # @return [Elem] + # @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+/, '_') + self.define_type_accel_base(method_name, prim) + end + # Create a new instance of a {Model} # @param [Hash] args def initialize(args={}) root = generate_root set_elements(root) @@ -423,10 +446,10 @@ def get_type(proc_or_class, options={}) case proc_or_class when Proc proc_or_class.call(options) when Class - proc_or_class.new + proc_or_class.new(options) end end def generate_root class_element = self.class.class_eval { @root }