lib/csl/locale/term.rb in csl-1.0.0.pre12 vs lib/csl/locale/term.rb in csl-1.0.0.pre13

- old
+ new

@@ -21,62 +21,111 @@ alias each each_child # @return [Term, nil] the first term that matches the query def lookup(name, options = {}) + options = Term.specialize(options) options[:name] = name = name.to_s - # specialize search conditions - conditions = options.select { |k,_| Term::Attributes.keys.include?(k.to_sym) } + term = registry[name].detect { |t| t.match?(options) } + return term unless term.nil? && options.delete(:'gender-form') - term = registry[name].detect { |t| t.match?(conditions) } - return term unless term.nil? && conditions.delete(:'gender-form') - - registry[name].detect { |t| t.match?(conditions) } + registry[name].detect { |t| t.match?(options) } end alias [] lookup - def ordinalize_modulo(number, divisor, options = {}) - ordinal = ordinalize(number, options) - return unless ordinal && ordinal.match_modulo?(divisor) - ordinal - end - def ordinalize(number, options = {}) return unless has_ordinals? - if number == :default - options[:name] = 'ordinal' + options = Term.specialize(options) + number = number.to_i.abs + + # try to match long-ordinals first + if options.delete(:form).to_s =~ /^long/i + ordinal = lookup_long_ordinal_for number, options + return ordinal unless ordinal.nil? + end + + # select CSL 1.0 or 1.0.1 algorithm + algorithm = ordinalizer + + ordinal = send algorithm, number, options + return ordinal unless ordinal.nil? + + # fallback to non-gendered version + if options.delete(:'gender-form') + ordinal = send algorithm, number, options + end + + ordinal + end + + def ordinalizer + if has_legacy_ordinals? + :lookup_legacy_ordinals_for else - options[:name] = 'ordinal-%02d' % number + :lookup_ordinals_for end + end - if options[:form].to_s =~ /^long/i - options.delete :form - options[:name][0,0] = 'long-' + def lookup_long_ordinal_for(number, options = {}) + ordinals[number].detect { |t| t.long_ordinal? && t.match?(options) } + end + + def lookup_ordinals_for(number, options = {}) + ordinal = lookup_ordinal_for(number, nil, options) + return ordinal unless ordinal.nil? + + unless number < 100 + ordinal = lookup_ordinal_for(number, 100, options) + return ordinal unless ordinal.nil? end - ordinal = ordinals[number].detect { |t| t.match?(options) } - return ordinal unless ordinal.nil? && options.delete(:'gender-form') + unless number < 10 + ordinal = lookup_ordinal_for(number, 10, options) + return ordinal unless ordinal.nil? + end - ordinals[number].detect { |t| t.match?(options) } + default_ordinals.detect { |t| t.match?(options) } end + def lookup_ordinal_for(number, divisor, options = {}) + modulus = divisor ? (number % divisor) : number + ordinals[modulus].detect do |t| + t.short_ordinal? && t.match?(options) && t.match_modulo?(number) + end + end + + def lookup_legacy_ordinals_for(number, options = {}) + case + when (11..13).include?(number.abs % 100) + ordinals[4].detect { |t| t.match?(options) } + when (1..3).include?(number.abs % 10) + ordinals[number.abs % 10].detect { |t| t.match?(options) } + else + ordinals[4].detect { |t| t.match?(options) } + end + end + # @return [Boolean] whether or not regular terms are registered at this node def has_terms? - not registry.empty? + !registry.empty? end # @return [Boolean] whether or not ordinal terms are registered at this node def has_ordinals? - not ordinals.empty? + !ordinals.empty? end def has_legacy_ordinals? has_ordinals? && !ordinals.key?(:default) end + def default_ordinals + ordinals[:default] + end + def drop_ordinals tmp = ordinals.values.flatten(1) ordinals.clear delete_children tmp end @@ -126,29 +175,43 @@ attr_accessor :text def_delegators :attributes, :hash, :eql?, :name, :form, :gender + class << self + def specialize(options) + options = options.select do |k,v| + !v.nil? && Term::Attributes.keys.include?(k.to_sym) + end + + options.delete :'gender-form' unless + options[:'gender-form'].to_s =~ /^masculine|feminine$/ + + options + end + end + # This method returns whether or not the ordinal term matchs the # passed-in modulus. This is determined by the ordinal term's match - # attribute: a value of '2-digits' matches a divisor of 100, '1-digit' - # matches a divisor of 10 and 'whole-number' matches a divisor of 1. + # attribute: a value of 'last-two-digits' matches a modulus of 100, + # 'last-digit' matches a modulus of 10 and 'whole-number' matches + # only if the number is identical to the ordinal value. # # If the term is no ordinal term, this methods always returns false. # # @return [Boolean] whether or not the ordinal term matches the - # passed-in divisor. - def match_modulo?(divisor) + # passed-in number. + def match_modulo?(number) return false unless ordinal? case attributes.match - when '2-digits' - divisor.to_i == 100 - when '1-digit' - divisor.to_i == 10 + when 'last-two-digits' + ordinal == number % 100 + when 'last-digit' + ordinal == number % 10 when 'whole-number' - divisor.to_i == 1 + ordinal == number else true end end alias matches_modulo? match_modulo? @@ -156,16 +219,30 @@ # @return [Boolean] whether or not this term is an ordinal term def ordinal? /^(long-)?ordinal(-\d\d+)?$/ === attributes.name end + # @return [Boolean] whether or not this term is a long-ordinal term + def long_ordinal? + /^long-ordinal(-\d\d+)?$/ === attributes.name + end + + # @return [Boolean] whether or not this term is a regular ordinal term + def short_ordinal? + /^ordinal(-\d\d+)?$/ === attributes.name + end + + def default_ordinal? + attributes.name == 'ordinal' + end + # @return [Fixnum, :default, nil] def ordinal return unless ordinal? return :default if attributes.name == 'ordinal' attributes.name[/\d+/].to_i end - + def gendered? !attributes.gender.blank? end def neutral? \ No newline at end of file