Class: HeadMusic::Content::Composition::HashDeserializer
- Inherits:
-
Object
- Object
- HeadMusic::Content::Composition::HashDeserializer
- Defined in:
- lib/head_music/content/composition/hash_deserializer.rb
Overview
Rebuilds a composition from a schema v3 hash by replaying the public builder API in dependency order: meter and key changes first (position strings roll counts and ticks over via the meter map), then placements, then repeat flags (a pickup-bar flag needs its bar allocated), then comments. Raw values are validated at the boundary by SchemaValues so corrupted input raises ArgumentError with path context instead of silently deserializing wrong.
Instance Attribute Summary collapse
-
#hash ⇒ Object
readonly
private
Returns the value of attribute hash.
Instance Method Summary collapse
-
#add_comments(composition) ⇒ Object
private
-
#apply_bar_changes(composition) ⇒ Object
private
-
#apply_repeat_flags(composition) ⇒ Object
private
-
#bar_hashes ⇒ Object
private
-
#build_base_composition ⇒ Object
private
-
#build_voices(composition) ⇒ Object
private
-
#composition ⇒ Object
-
#initialize(hash) ⇒ HashDeserializer
constructor
A new instance of HashDeserializer.
-
#repeat_state?(bar_hash) ⇒ Boolean
private
-
#validate_schema_version ⇒ Object
private
-
#values ⇒ Object
private
Constructor Details
#initialize(hash) ⇒ HashDeserializer
Returns a new instance of HashDeserializer.
10 11 12 13 14 15 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 10 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.
28 29 30 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 28 def hash @hash end |
Instance Method Details
#add_comments(composition) ⇒ Object (private)
98 99 100 101 102 103 104 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 98 def add_comments(composition) 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 composition.add_comment(comment_hash["text"], position) end end |
#apply_bar_changes(composition) ⇒ Object (private)
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 60 def (composition) .each_with_index do |, index| number = values.(, index) path = "bars[#{index}]" key_signature = values.key_signature(["key_signature"], path) meter = values.meter(["meter"], path) composition.change_key_signature(number, key_signature) if key_signature composition.change_meter(number, meter) if meter end end |
#apply_repeat_flags(composition) ⇒ Object (private)
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 85 def apply_repeat_flags(composition) .each_with_index do |, index| next unless repeat_state?() = composition.(values.(, index)).last .starts_repeat = true if ["starts_repeat"] ends_repeat = ["ends_repeat_after_num_plays"] .ends_repeat_after_num_plays = ends_repeat if ends_repeat plays_on_passes = ["plays_on_passes"] .plays_on_passes = plays_on_passes if plays_on_passes end end |
#bar_hashes ⇒ Object (private)
56 57 58 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 56 def @bar_hashes ||= Array(hash["bars"]) end |
#build_base_composition ⇒ Object (private)
46 47 48 49 50 51 52 53 54 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 46 def build_base_composition HeadMusic::Content::Composition.new( name: hash["name"], key_signature: values.key_signature(hash["key_signature"], "key_signature"), meter: values.meter(hash["meter"], "meter"), composer: hash["composer"], origin: hash["origin"] ) end |
#build_voices(composition) ⇒ Object (private)
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 71 def build_voices(composition) Array(hash["voices"]).each_with_index do |voice_hash, voice_index| voice = composition.add_voice(role: voice_hash["role"]) Array(voice_hash["placements"]).each_with_index do |placement_hash, placement_index| path = "voices[#{voice_index}].placements[#{placement_index}]" position = values.position(placement_hash["position"], path) rhythmic_value = values.rhythmic_value(placement_hash["rhythmic_value"], path) sounds = values.placement_sounds(placement_hash, path) placement = voice.place(position, rhythmic_value, sounds) placement.beam_break_before = placement_hash["beam_break_before"] if placement_hash.key?("beam_break_before") end end end |
#composition ⇒ Object
17 18 19 20 21 22 23 24 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 17 def composition @composition ||= build_base_composition.tap do |composition| (composition) build_voices(composition) apply_repeat_flags(composition) add_comments(composition) end end |
#repeat_state?(bar_hash) ⇒ Boolean (private)
106 107 108 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 106 def repeat_state?() ["starts_repeat"] || ["ends_repeat_after_num_plays"] || ["plays_on_passes"] end |
#validate_schema_version ⇒ Object (private)
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 34 def validate_schema_version version = hash["schema_version"] return if version.is_a?(Integer) && version == SCHEMA_VERSION = "unsupported schema_version: #{version.inspect} (supported: #{SCHEMA_VERSION})" if version == 2 += "; migrate v2 \"pitches\" arrays to v3 \"sounds\" arrays " \ "(pitch strings unchanged; unpitched sounds are {\"unpitched\" => name_key} objects)" end raise ArgumentError, end |
#values ⇒ Object (private)
30 31 32 |
# File 'lib/head_music/content/composition/hash_deserializer.rb', line 30 def values @values ||= SchemaValues.new end |