Class: HeadMusic::Rudiment::RhythmicUnit::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/rudiment/rhythmic_unit/parser.rb

Constant Summary collapse

RHYTHMIC_UNITS_DATA =
HeadMusic::Rudiment::RhythmicUnit::RHYTHMIC_UNITS_DATA
TEMPO_SHORTHAND_PATTERN =
RHYTHMIC_UNITS_DATA.map { |unit| unit["tempo_shorthand"] }.compact.uniq.sort_by { |s| -s.length }.join("|")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(identifier) ⇒ Parser

Returns a new instance of Parser.



13
14
15
16
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 13

def initialize(identifier)
  @identifier = identifier.to_s.strip
  parse
end

Instance Attribute Details

#identifierObject (readonly)

Returns the value of attribute identifier.



2
3
4
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 2

def identifier
  @identifier
end

#rhythmic_unitObject (readonly)

Returns the value of attribute rhythmic_unit.



2
3
4
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 2

def rhythmic_unit
  @rhythmic_unit
end

Class Method Details

.parse(identifier) ⇒ Object



8
9
10
11
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 8

def self.parse(identifier)
  return nil if identifier.nil? || identifier.to_s.strip.empty?
  new(identifier).parsed_name
end

Instance Method Details

#american_nameObject



35
36
37
38
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 35

def american_name
  # Always return the American name if a unit was found
  @unit_data&.fetch("american_name", nil)
end

#from_american_nameObject



44
45
46
47
48
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 44

def from_american_name
  RHYTHMIC_UNITS_DATA.find do |unit|
    normalize_name(unit["american_name"]) == normalized_identifier
  end
end

#from_british_nameObject



50
51
52
53
54
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 50

def from_british_name
  RHYTHMIC_UNITS_DATA.find do |unit|
    normalize_name(unit["british_name"]) == normalized_identifier
  end
end

#from_durationObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 64

def from_duration
  RHYTHMIC_UNITS_DATA.find do |unit|
    # Match decimal duration (e.g., "0.25")
    return unit if unit["duration"].to_s == identifier.strip

    # Match fraction notation (e.g., "1/4" = 0.25)
    if identifier.match?(%r{^\d+/\d+$})
      numerator, denominator = identifier.split("/").map(&:to_f)
      calculated_duration = numerator / denominator
      return unit if (calculated_duration - unit["duration"]).abs < 0.0001
    end

    false
  end
end

#from_tempo_shorthandObject



56
57
58
59
60
61
62
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 56

def from_tempo_shorthand
  # Handle shorthand with dots (e.g., "q." should match "q")
  clean_identifier = identifier.downcase.strip.gsub(/\.*$/, "")
  RHYTHMIC_UNITS_DATA.find do |unit|
    unit["tempo_shorthand"] && unit["tempo_shorthand"].downcase == clean_identifier
  end
end

#normalize_name(name) ⇒ Object (private)



82
83
84
85
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 82

def normalize_name(name)
  return nil if name.nil?
  name.to_s.downcase.strip.gsub(/[^a-z0-9]/, "_").gsub(/_+/, "_").gsub(/^_|_$/, "")
end

#normalized_identifierObject



40
41
42
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 40

def normalized_identifier
  @normalized_identifier ||= identifier.downcase.strip.gsub(/[^a-z0-9]/, "_").gsub(/_+/, "_").gsub(/^_|_$/, "")
end

#parseObject



18
19
20
21
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 18

def parse
  @unit_data = from_american_name || from_british_name || from_tempo_shorthand || from_duration
  @rhythmic_unit = @unit_data ? HeadMusic::Rudiment::RhythmicUnit.get_by_name(@unit_data["american_name"]) : nil
end

#parsed_nameObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/head_music/rudiment/rhythmic_unit/parser.rb', line 23

def parsed_name
  # Return the name format that was used in input
  return nil unless @unit_data

  # Check which type matched
  if from_british_name == @unit_data && @unit_data["british_name"]
    @unit_data["british_name"]
  else
    @unit_data["american_name"]
  end
end