Skip to content

Commit beea229

Browse files
authored
Merge pull request #44 from lemontree55/model-name
2 parents 1a99589 + c41fea5 commit beea229

File tree

4 files changed

+92
-36
lines changed

4 files changed

+92
-36
lines changed

lib/rasn1/model.rb

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -381,15 +381,24 @@ def initialize(args={})
381381
lazy_initialize(args) unless args.empty?
382382
end
383383

384-
# Access an element of the model by its name
385-
# @param [Symbol] name
386-
# @return [Model, Types::Base, Wrapper]
387-
def [](name)
388-
elt = @elements[name]
389-
return elt unless elt.is_a?(Proc)
390-
391-
# Lazy element -> generate it
392-
@elements[name] = elt.call
384+
# @overload [](name)
385+
# Access an element of the model by its name
386+
# @param [Symbol] name
387+
# @return [Model, Types::Base, Wrapper]
388+
# @overload [](idx)
389+
# Access an element of root element by its index. Root element must be a {Sequence} or {SequenceOf}.
390+
# @param [Integer] idx
391+
# @return [Model, Types::Base, Wrapper]
392+
def [](name_or_idx)
393+
case name_or_idx
394+
when Symbol
395+
elt = @elements[name_or_idx]
396+
return elt unless elt.is_a?(Proc)
397+
398+
@elements[name_or_idx] = elt.call
399+
when Integer
400+
root[name_or_idx]
401+
end
393402
end
394403

395404
# Set value of element +name+. Element should be a {Types::Base}.
@@ -582,29 +591,37 @@ def by_name(name)
582591
private
583592

584593
def generate_root(args)
585-
opts = args.slice(:explicit, :implicit, :optional, :class, :default, :constructed, :tag_value)
594+
opts = args.slice(:name, :explicit, :implicit, :optional, :class, :default, :constructed, :tag_value)
586595
root = self.class.class_eval { @root }
587596
root_options = self.class.options || {}
588597
root_options.merge!(opts)
598+
@root_name = args[:name] || root.name
589599
@root = generate_element(root, root_options)
590-
@root_name = root.name
591-
@elements[root.name] = @root
600+
@elements[@root_name] = @root
592601
end
593602

594603
def generate_element(elt, opts={})
595604
case elt
596605
when BaseElem
597606
generate_base_element(elt, opts)
598607
when ModelElem
608+
opts[:name] ||= elt.name
599609
elt.klass.new(opts)
600610
when WrapElem
601-
wrapped = elt.element.is_a?(ModelElem) ? elt.element.klass : generate_element(elt.element)
602-
wrapper = Wrapper.new(wrapped, elt.options.merge(opts))
603-
@elements[elt.element.name] = proc { wrapper.element }
604-
wrapper
611+
generate_wrapper_element(elt, opts)
605612
end
606613
end
607614

615+
def generate_wrapper_element(elt, opts)
616+
wrapped = elt.element.is_a?(ModelElem) ? elt.element.klass : generate_element(elt.element)
617+
options = elt.options.merge(opts)
618+
options[:name] = elt.element.name if elt.element.is_a?(ModelElem)
619+
wrapper = Wrapper.new(wrapped, options)
620+
# Use a proc as wrapper may be lazy
621+
@elements[elt.element.name] = proc { wrapper.element }
622+
wrapper
623+
end
624+
608625
def generate_base_element(elt, opts)
609626
element = elt.proc.call(opts)
610627
return element if elt.content.nil?
@@ -616,19 +633,20 @@ def generate_base_element(elt, opts)
616633
element
617634
end
618635

636+
# @author sdaubert
637+
# @author lemontree55
638+
# @author adfoster-r7
619639
def private_to_h(element=nil) # rubocop:disable Metrics/CyclomaticComplexity
620640
my_element = element || root
621-
my_element = my_element.root if my_element.is_a?(Model)
622641
value = case my_element
642+
when Model
643+
model_to_h(my_element)
623644
when Types::SequenceOf
624645
sequence_of_to_h(my_element)
625646
when Types::Sequence
626647
sequence_to_h(my_element)
627-
# @author adfoster-r7
628648
when Types::Choice
629-
raise ChoiceError.new(my_element) if my_element.chosen.nil?
630-
631-
private_to_h(my_element.value[my_element.chosen])
649+
choice_to_h(my_element)
632650
when Wrapper
633651
wrapper_to_h(my_element)
634652
else
@@ -641,6 +659,15 @@ def private_to_h(element=nil) # rubocop:disable Metrics/CyclomaticComplexity
641659
end
642660
end
643661

662+
def model_to_h(elt)
663+
hsh = elt.to_h
664+
if root.is_a?(Types::Choice)
665+
hsh[hsh.keys.first]
666+
else
667+
{ @elements.key(elt) => hsh[hsh.keys.first] }
668+
end
669+
end
670+
644671
def sequence_of_to_h(elt)
645672
if elt.of_type < Model
646673
elt.value&.map { |el| el.to_h.values.first }
@@ -655,8 +682,7 @@ def sequence_to_h(seq)
655682

656683
case el
657684
when Model
658-
hsh = el.to_h
659-
[@elements.key(el), hsh[hsh.keys.first]]
685+
model_to_h(el).to_a[0]
660686
when Wrapper
661687
[unwrap_keyname(@elements.key(el)), wrapper_to_h(el)]
662688
else
@@ -666,6 +692,13 @@ def sequence_to_h(seq)
666692
ary.compact.to_h
667693
end
668694

695+
def choice_to_h(elt)
696+
raise ChoiceError.new(elt) if elt.chosen.nil?
697+
698+
chosen = elt.value[elt.chosen]
699+
{ chosen.name => private_to_h(chosen) }
700+
end
701+
669702
def unwrap_keyname(key)
670703
key.to_s.delete_suffix('_wrapper').to_sym
671704
end

spec/model_spec.rb

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class NamedModelTest < ModelTest
7979
ERRORED_VALUE = "\x01\x01\x00".b.freeze
8080
CHOICE_INTEGER = "\x02\x01\x10".b.freeze
8181
CHOICE_SEQUENCE = SIMPLE_VALUE
82+
NESTED_CHOICE = "\x30\x0b\x04\x03abc\x63\x03\x40\x01\x01".b.freeze
8283
IMPLICIT_WRAPPED_SUBMODEL = "\x30\x0a\xa5\x08\x02\x01\x0a\x81\x03\x02\x01\x33".b.freeze
8384
EXPLICIT_WRAPPED_SUBMODEL = "\x30\x0c\x86\x0a\xa4\x08\x02\x01\x0a\x81\x03\x02\x01\x33".b.freeze
8485
OPTIONAL_VOID_WRAPPED_SUBMODEL = "\x30\x03\x02\x01\x01".b.freeze
@@ -161,15 +162,15 @@ class NamedModelTest < ModelTest
161162

162163
it 'initializes a model from a parameter hash' do
163164
model = SuperOfModel.new(of: [{ id: 1234 }, { id: 4567, room: 43, house: 21 }])
164-
expect(model[:of][:seqof].length).to eq(2)
165-
expect(model[:of][:seqof][0]).to be_a(ModelTest)
166-
expect(model[:of][:seqof][0][:id].to_i).to eq(1234)
167-
expect(model[:of][:seqof][0][:room].value).to be_nil
168-
expect(model[:of][:seqof][0][:house].to_i).to eq(0)
169-
expect(model[:of][:seqof][1]).to be_a(ModelTest)
170-
expect(model[:of][:seqof][1][:id].to_i).to eq(4567)
171-
expect(model[:of][:seqof][1][:room].value).to eq(43)
172-
expect(model[:of][:seqof][1][:house].to_i).to eq(21)
165+
expect(model[:of].length).to eq(2)
166+
expect(model[:of][0]).to be_a(ModelTest)
167+
expect(model[:of][0][:id].to_i).to eq(1234)
168+
expect(model[:of][0][:room].value).to be_nil
169+
expect(model[:of][0][:house].to_i).to eq(0)
170+
expect(model[:of][1]).to be_a(ModelTest)
171+
expect(model[:of][1][:id].to_i).to eq(4567)
172+
expect(model[:of][1][:room].value).to eq(43)
173+
expect(model[:of][1][:house].to_i).to eq(21)
173174
end
174175

175176
it 'initializes a model with an implicit wrapper from a parameter hash' do
@@ -267,10 +268,10 @@ class NamedModelTest < ModelTest
267268

268269
it 'generates a Hash image of a model with a choice model' do
269270
model = ModelChoice.parse(CHOICE_INTEGER)
270-
expect(model.to_h).to eq({ choice: 16 })
271+
expect(model.to_h).to eq({ choice: { id: 16 }})
271272

272273
model = ModelChoice.parse(CHOICE_SEQUENCE)
273-
expect(model.to_h).to eq({ choice: { id: 65537, room: 43, house: 4660 } })
274+
expect(model.to_h).to eq({ choice: { a_record: { id: 65537, room: 43, house: 4660 } } })
274275

275276
model = ModelChoice.new
276277
expect { model.to_h }
@@ -290,6 +291,11 @@ class NamedModelTest < ModelTest
290291
model[:a_record][:house] = 5
291292
expect(model.to_h).to eq({ seq: { a_record: { id: 4, house: 5 } } })
292293
end
294+
295+
it 'generates a Hash image of a model with a nested Choice' do
296+
model = NestedModelChoice.parse(NESTED_CHOICE)
297+
expect(model.to_h).to eq({seq: { os: 'abc', first_choice: { more: { nested_choice:{ id: 1 }}}}})
298+
end
293299
end
294300

295301
describe '#to_der' do
@@ -412,7 +418,7 @@ class NamedModelTest < ModelTest
412418
expect(cert[:signatureValue].value).to eq(der[0x1b6, 128])
413419

414420
issuer = cert[:tbsCertificate][:issuer].to_h
415-
expect(issuer[:rdnSequence][0][0][:value]).to eq("\x13\x03org".b)
421+
expect(issuer[:issuer][0][0][:value]).to eq("\x13\x03org".b)
416422
end
417423

418424
it 'may generate a X.509 certificate' do

spec/spec_helper.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ class ModelChoice < RASN1::Model
6868
model(:a_record, ModelTest)]
6969
end
7070

71+
class ImplicitModelChoice < RASN1::Model
72+
choice :choice,
73+
content: [integer(:id, class: :application, implicit: 0),
74+
wrapper(model(:a_record, ModelTest), class: :application, implicit: 1)]
75+
end
76+
77+
class NestedModelChoice < RASN1::Model
78+
sequence :seq,
79+
content: [octet_string(:os),
80+
choice(:first_choice,
81+
content: [integer(:int, class: :application, implicit: 2),
82+
sequence(:more, class: :application, implicit: 3,
83+
content: [model(:nested_choice, ImplicitModelChoice)])
84+
])
85+
]
86+
end
87+
7188
class ExplicitTaggedSeq < RASN1::Model
7289
sequence :seq, explicit: 0, class: :application,
7390
content: [integer(:id), integer(:extern_id)]

spec/tracer_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ module TestTrace
183183
end
184184
expect(io.string).to eq(<<~ENDOFDATA
185185
seq [ 16 ] SEQUENCE (0x30), len: 13 (0x0d)
186-
record [ CONTEXT 5 ] IMPLICIT SEQUENCE (0xa5), len: 11 (0x0b)
186+
a_record [ CONTEXT 5 ] IMPLICIT SEQUENCE (0xa5), len: 11 (0x0b)
187187
id [ 2 ] INTEGER (0x02), len: 1 (0x01) 16 (0x10)
188188
room [ CONTEXT 0 ] IMPLICIT INTEGER OPTIONAL (0x80), len: 1 (0x01) 7 (0x07)
189189
house [ CONTEXT 1 ] EXPLICIT INTEGER (0x81), len: 3 (0x03)

0 commit comments

Comments
 (0)