Class: HeadMusic::Content::Flow::Deserializer

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/content/flow/deserializer.rb

Overview

What every schema version's reader does the same way: check the version, build the base flow, and replay placements, repeat flags, and comments through the public builder API. A subclass names its SCHEMA_VERSION, says where the opening timeline values live, and walks its own containers.

Raw values are validated at the boundary by SchemaValues, so corrupted input raises ArgumentError with path context instead of silently deserializing wrong.

Direct Known Subclasses

HashDeserializer

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash) ⇒ Deserializer

Returns a new instance of Deserializer.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
# File 'lib/head_music/content/flow/deserializer.rb', line 11

def initialize(hash)
  raise ArgumentError, "expected a Hash, got #{hash.class}" unless hash.is_a?(Hash)

  @hash = hash.deep_transform_keys(&:to_s)
  validate_schema_version
end

Instance Attribute Details

#hash ⇒ Object (readonly, private)

Returns the value of attribute hash.



24
25
26
# File 'lib/head_music/content/flow/deserializer.rb', line 24

def hash
  @hash
end

Instance Method Details

#add_comments(flow) ⇒ Object (private)



90
91
92
93
94
95
96
# File 'lib/head_music/content/flow/deserializer.rb', line 90

def add_comments(flow)
  Array(hash["comments"]).each_with_index do |comment_hash, index|
    raw_position = comment_hash["position"]
    position = values.position(raw_position, "comments[#{index}]") if raw_position
    flow.add_comment(comment_hash["text"], position)
  end
end

#apply_repeat_flags(flow) ⇒ Object (private)



79
80
81
82
83
84
85
86
87
88
# File 'lib/head_music/content/flow/deserializer.rb', line 79

def apply_repeat_flags(flow)
  each_change(bar_hashes, "bars") do |bar_number, bar_hash, _path|
    bar = flow.bars(bar_number).last
    bar.starts_repeat = true if bar_hash["starts_repeat"]
    ends_repeat = bar_hash["ends_repeat_after_num_plays"]
    bar.ends_repeat_after_num_plays = ends_repeat if ends_repeat
    plays_on_passes = bar_hash["plays_on_passes"]
    bar.plays_on_passes = plays_on_passes if plays_on_passes
  end
end

#bar_hashes ⇒ Object (private)



52
53
54
# File 'lib/head_music/content/flow/deserializer.rb', line 52

def bar_hashes
  @bar_hashes ||= Array(hash["bars"])
end

#build_base_flow ⇒ Object (private)



41
42
43
44
45
46
47
48
49
50
# File 'lib/head_music/content/flow/deserializer.rb', line 41

def build_base_flow
  HeadMusic::Content::Flow.new(
    name: hash["name"],
    key_signature: values.key_signature(timeline_hash["key_signature"], timeline_path("key_signature")),
    meter: values.meter(timeline_hash["meter"], timeline_path("meter")),
    tempo: values.tempo(timeline_hash["tempo"], timeline_path("tempo")),
    composer: hash["composer"],
    origin: hash["origin"]
  )
end

#build_placements(voice, voice_hash, voice_path) ⇒ Object (private)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/head_music/content/flow/deserializer.rb', line 64

def build_placements(voice, voice_hash, voice_path)
  Array(voice_hash["placements"]).each_with_index do |placement_hash, placement_index|
    path = "#{voice_path}.placements[#{placement_index}]"
    placement = voice.place(
      values.position(placement_hash["position"], path),
      values.rhythmic_value(placement_hash["rhythmic_value"], path),
      values.placement_sounds(placement_hash, path)
    )
    placement.beam_break_before = placement_hash["beam_break_before"] if placement_hash.key?("beam_break_before")
    values.placement_syllables(placement_hash, path).each do |syllable|
      placement.sing(syllable.text, verse: syllable.verse, hyphen_after: syllable.hyphen_after)
    end
  end
end

#each_change(list, base) ⇒ Object (private)

A list of bar-keyed changes, each yielded with its validated bar number and its path for error messages.



58
59
60
61
62
# File 'lib/head_music/content/flow/deserializer.rb', line 58

def each_change(list, base)
  Array(list).each_with_index do |change, index|
    yield values.bar_number(change, index, base), change, "#{base}[#{index}]"
  end
end

#flow ⇒ Object



18
19
20
# File 'lib/head_music/content/flow/deserializer.rb', line 18

def flow
  @flow ||= build_base_flow.tap { |flow| build(flow) }
end

#unsupported_version_message(version) ⇒ Object (private)



37
38
39
# File 'lib/head_music/content/flow/deserializer.rb', line 37

def unsupported_version_message(version)
  "unsupported schema_version: #{version.inspect} (supported: #{self.class::SCHEMA_VERSION})"
end

#validate_schema_version ⇒ Object (private)

Raises:

  • (ArgumentError)


30
31
32
33
34
35
# File 'lib/head_music/content/flow/deserializer.rb', line 30

def validate_schema_version
  version = hash["schema_version"]
  return if version.is_a?(Integer) && version == self.class::SCHEMA_VERSION

  raise ArgumentError, unsupported_version_message(version)
end

#values ⇒ Object (private)



26
27
28
# File 'lib/head_music/content/flow/deserializer.rb', line 26

def values
  @values ||= SchemaValues.new
end