Class: HeadMusic::Content::Flow::HashDeserializer
- Inherits:
-
Deserializer
- Object
- Deserializer
- HeadMusic::Content::Flow::HashDeserializer
- Defined in:
- lib/head_music/content/flow/hash_deserializer.rb
Overview
Rebuilds a flow from a schema v4 hash, in dependency order: the timeline first, because a position string rolls its counts and ticks over through the meter map; then parts, because a voice's staff assignment names a staff of its part's system; then placements; then repeat flags, which need their bar allocated.
Constant Summary collapse
- SCHEMA_VERSION =
HeadMusic::Content::Flow::SCHEMA_VERSION
Instance Method Summary collapse
-
#apply_citations(flow) ⇒ Object
private
Read here rather than on the shared base, so the v3 reader gains nothing.
- #apply_instrument_changes(part, part_hash, part_path) ⇒ Object private
-
#apply_staff_assignments(voice, part, voice_hash) ⇒ Object
private
Replayed as bare map entries: a crossing is one event, and the serialized form is the map.
- #apply_staff_system_changes(part, part_hash, part_path) ⇒ Object private
- #apply_timeline_changes(flow) ⇒ Object private
- #build(flow) ⇒ Object private
-
#build_parts(flow) ⇒ Object
private
Staff system changes replay before the voices because a voice's staff assignment is resolved against the system in force at its bar.
- #build_voices(part, part_hash, part_path) ⇒ Object private
- #each_timeline_change(key, &block) ⇒ Object private
-
#publication_from_h(source_hash) ⇒ Object
private
A source written from the cantus firmus catalog carries its key, and reads back as the catalog entry itself rather than as a copy of its fields.
- #timeline_hash ⇒ Object private
- #timeline_path(key) ⇒ Object private
-
#unsupported_version_message(version) ⇒ Object
private
No recipe migrates v3 in place, so a v3 document is told which release still reads it rather than merely rejected.
Constructor Details
This class inherits a constructor from HeadMusic::Content::Flow::Deserializer
Instance Method Details
#apply_citations(flow) ⇒ Object (private)
Read here rather than on the shared base, so the v3 reader gains nothing. A document written before these keys existed has neither, and reads.
38 39 40 41 42 43 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 38 def apply_citations(flow) work = hash["work"] flow.work = HeadMusic::Content::Work.from_h(work) if work source = hash["source"] flow.source = publication_from_h(source) if source end |
#apply_instrument_changes(part, part_hash, part_path) ⇒ Object (private)
84 85 86 87 88 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 84 def apply_instrument_changes(part, part_hash, part_path) each_change(part_hash["instrument_changes"], "#{part_path}.instrument_changes") do |, change, path| part.change_instrument(, values.instrument(change["instrument"], path)) end end |
#apply_staff_assignments(voice, part, voice_hash) ⇒ Object (private)
Replayed as bare map entries: a crossing is one event, and the serialized form is the map.
109 110 111 112 113 114 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 109 def apply_staff_assignments(voice, part, voice_hash) each_change(voice_hash["staff_assignments"], "staff_assignments") do |, assignment, _path| staff = part.staff_system_at().staves[assignment["staff"].to_i] voice.assign_staff(, staff) if staff end end |
#apply_staff_system_changes(part, part_hash, part_path) ⇒ Object (private)
90 91 92 93 94 95 96 97 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 90 def apply_staff_system_changes(part, part_hash, part_path) each_change(part_hash["staff_system_changes"], "#{part_path}.staff_system_changes") do |, change, path| staff_system = values.staff_system(change["staff_system"], path) raise ArgumentError, "#{path}: a staff system change names a staff system, got nil" if staff_system.nil? part.change_staff_system(, staff_system) end end |
#apply_timeline_changes(flow) ⇒ Object (private)
52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 52 def apply_timeline_changes(flow) each_timeline_change("meter_changes") do |, change, path| flow.change_meter(, values.meter(change["meter"], path)) end each_timeline_change("key_signature_changes") do |, change, path| tonal_context = values.tonal_context(change["tonal_context"], path) flow.change_key_signature(, values.fifths(change["signature"], path), tonal_context: tonal_context) end each_timeline_change("tempo_changes") do |, change, path| flow.change_tempo(, values.tempo(change["tempo"], path)) end end |
#build(flow) ⇒ Object (private)
28 29 30 31 32 33 34 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 28 def build(flow) apply_citations(flow) apply_timeline_changes(flow) build_parts(flow) apply_repeat_flags(flow) add_comments(flow) end |
#build_parts(flow) ⇒ Object (private)
Staff system changes replay before the voices because a voice's staff assignment is resolved against the system in force at its bar.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 71 def build_parts(flow) Array(hash["parts"]).each_with_index do |part_hash, part_index| path = "parts[#{part_index}]" part = flow.add_part( instrument: values.instrument(part_hash["instrument"], path), staff_system: values.staff_system(part_hash["staff_system"], path) ) apply_instrument_changes(part, part_hash, path) apply_staff_system_changes(part, part_hash, path) build_voices(part, part_hash, path) end end |
#build_voices(part, part_hash, part_path) ⇒ Object (private)
99 100 101 102 103 104 105 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 99 def build_voices(part, part_hash, part_path) Array(part_hash["voices"]).each_with_index do |voice_hash, voice_index| voice = part.add_voice(role: voice_hash["role"]) build_placements(voice, voice_hash, "#{part_path}.voices[#{voice_index}]") apply_staff_assignments(voice, part, voice_hash) end end |
#each_timeline_change(key, &block) ⇒ Object (private)
65 66 67 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 65 def each_timeline_change(key, &block) each_change(timeline_hash[key], timeline_path(key), &block) end |
#publication_from_h(source_hash) ⇒ Object (private)
A source written from the cantus firmus catalog carries its key, and reads back as the catalog entry itself rather than as a copy of its fields.
47 48 49 50 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 47 def publication_from_h(source_hash) key = source_hash["key"] (key && HeadMusic::Content::CantusFirmus::Source.get(key)) || HeadMusic::Content::Publication.from_h(source_hash) end |
#timeline_hash ⇒ Object (private)
20 21 22 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 20 def timeline_hash @timeline_hash ||= hash["timeline"] || {} end |
#timeline_path(key) ⇒ Object (private)
24 25 26 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 24 def timeline_path(key) "timeline.#{key}" end |
#unsupported_version_message(version) ⇒ Object (private)
No recipe migrates v3 in place, so a v3 document is told which release still reads it rather than merely rejected.
14 15 16 17 18 |
# File 'lib/head_music/content/flow/hash_deserializer.rb', line 14 def (version) = super += "; read it with Flow.from_v3_h in head_music 21.x and save it again" if version == 3 end |