lib/head_music/named.rb in head_music-0.24.0 vs lib/head_music/named.rb in head_music-0.24.1

- old
+ new

@@ -1,45 +1,112 @@ # frozen_string_literal: true # NameRudiment is a module to be included in classes whose instances may be identified by name. module HeadMusic::Named - attr_reader :name delegate :to_s, to: :name - def initialize(name) - @name = name.to_s - end + # Locale encapsulates a language and optional region or country. + class Locale + DEFAULT_CODE = :en_US - def hash_key - HeadMusic::Utilities::HashKey.for(name) + attr_reader :language, :region + + def self.default_locale + get(DEFAULT_CODE) + end + + def self.get(code) + @locales ||= {} + parts = code.to_s.split(/[_\-]/) + language = parts[0].downcase + region = parts[1]&.upcase + key = [language, region].compact.join('_').to_sym + @locales[key] ||= new(language: language, region: region) + end + + def initialize(language:, region: nil) + @language = language + @region = region + end + + def code + @code ||= [@language, @region].compact.join('_') + end + + private_class_method :new end - def self.included(base) - base.extend(ClassMethods) + # A LocalizedName is the name of a rudiment in a locale. + class LocalizedName + attr_reader :locale, :name, :abbreviation + + delegate :code, to: :locale, prefix: true + delegate :language, :region, to: :locale + + def initialize(name:, locale_code: Locale::DEFAULT_CODE, abbreviation: nil) + @name = name + @locale = Locale.get(locale_code) + @abbreviation = abbreviation + end end # Adds .get_by_name to the including class. module ClassMethods def get_by_name(name) name = name.to_s - @instances_by_name ||= {} + @instances ||= {} key = HeadMusic::Utilities::HashKey.for(name) - @instances_by_name[key] ||= new(name) + @instances[key] ||= new(name) end + end - def aliases - {} - end + def self.included(base) + base.extend(ClassMethods) end - # An Alias encapsulates an alternative name for a rudiment. - class Alias - attr_reader :key, :name, :abbreviation, :locale + attr_reader :name_key, :alias_name_keys - def initialize(key:, name:, abbreviation: nil, locale: nil) - @key = key - @name = name - @abbreviation = abbreviation - @locale = locale - end + def name=(name) + ensure_localized_name(name: name) + end + + def name(locale_code: Locale::DEFAULT_CODE) + localized_name(locale_code: locale_code)&.name + end + + def localized_name(locale_code: Locale::DEFAULT_CODE) + locale = Locale.get(locale_code || Locale::DEFAULT_CODE) + localized_name_in_matching_locale(locale) || + localized_name_in_locale_matching_language(locale) || + localized_name_in_default_locale || + localized_names.first + end + + def ensure_localized_name(name:, locale_code: Locale::DEFAULT_CODE, abbreviation: nil) + @localized_names ||= [] + @localized_names << LocalizedName.new(name: name, locale_code: locale_code, abbreviation: abbreviation) + @localized_names.uniq! + end + + # Returns an array of LocalizedName instances that are synonymous with the name. + def localized_names + @localized_names ||= [] + end + + private + + def localized_name_in_matching_locale(locale) + localized_names.detect { |candidate| candidate.locale_code == locale.code } + end + + def localized_name_in_locale_matching_language(locale) + localized_names.detect { |candidate| candidate.language == locale.language } + end + + def localized_name_in_default_locale + localized_names.detect { |name| name.locale_code == Locale::DEFAULT_CODE } + end + + def hash_key + HeadMusic::Utilities::HashKey.for(name) end end