Skip to content

Commit

Permalink
Providing better access to calendar data, including eras, periods, qu…
Browse files Browse the repository at this point in the history
…arters, and fields.
  • Loading branch information
Cameron Dutro committed Dec 11, 2012
1 parent 1e5ad00 commit b82605a
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 11 deletions.
105 changes: 94 additions & 11 deletions lib/twitter_cldr/shared/calendar.rb
Expand Up @@ -8,8 +8,27 @@ module Shared
class Calendar

DEFAULT_FORMAT = :'stand-alone'
DEFAULT_PERIOD_FORMAT = :format

NAMES_FORMS = [:wide, :narrow, :abbreviated]
ERAS_NAMES_FORMS = [:abbr, :name]

DATETIME_METHOD_MAP = {
:year_of_week_of_year => :year,
:quarter_stand_alone => :quarter,
:month_stand_alone => :month,
:day_of_month => :day,
:day_of_week_in_month => :day,
:weekday_local => :weekday,
:weekday_local_stand_alone => :weekday,
:second_fraction => :second,
:timezone_generic_non_location => :timezone,
:timezone_metazone => :timezone
}

REDIRECT_CONVERSIONS = {
:dayPeriods => :periods
}

attr_reader :locale, :calendar_type

Expand All @@ -18,29 +37,93 @@ def initialize(locale = TwitterCldr.get_locale, calendar_type = TwitterCldr::DEF
@calendar_type = calendar_type
end

def months(names_form = :wide)
data = get_with_names_form(:months, names_form)
data && data.sort_by{ |m| m.first }.map { |m| m.last }
def months(names_form = :wide, format = DEFAULT_FORMAT)
data = get_with_names_form(:months, names_form, format)
data && data.sort_by { |m| m.first }.map { |m| m.last }
end

def weekdays(names_form = :wide)
get_with_names_form(:days, names_form)
def weekdays(names_form = :wide, format = DEFAULT_FORMAT)
get_with_names_form(:days, names_form, format)
end

def fields
get_data(:fields)
end

def quarters(names_form = :wide, format = DEFAULT_FORMAT)
get_with_names_form(:quarters, names_form, format)
end

def periods(names_form = :wide, format = DEFAULT_PERIOD_FORMAT)
get_with_names_form(:periods, names_form, format)
end

def eras(names_form = :name)
get_data(:eras)[names_form]
end

def date_order(options = {})
get_order_for(TwitterCldr::Tokenizers::DateTokenizer, options)
end

def time_order(options = {})
get_order_for(TwitterCldr::Tokenizers::TimeTokenizer, options)
end

def datetime_order(options = {})
get_order_for(TwitterCldr::Tokenizers::DateTimeTokenizer, options)
end

private

def get_with_names_form(data_type, names_form)
get_data(data_type, DEFAULT_FORMAT, names_form) if NAMES_FORMS.include?(names_form.to_sym)
def calendar_cache
@@calendar_cache ||= {}
end

def get_order_for(const, options)
opts = options.merge(:locale => @locale)
cache_key = TwitterCldr::Utils.compute_cache_key([const.to_s] + opts.keys.sort + opts.values.sort)
calendar_cache.fetch(cache_key) do |key|
tokens = const.new(opts).tokens
calendar_cache[cache_key] = resolve_methods(methods_for_tokens(tokens))
end
end

def resolve_methods(methods)
methods.map { |method| DATETIME_METHOD_MAP.fetch(method, method) }
end

def methods_for_tokens(tokens)
tokens.inject([]) do |ret, token|
if token.type == :pattern
ret << TwitterCldr::Formatters::DateTimeFormatter::METHODS[token.value[0].chr]
end
ret
end
end

def get_with_names_form(data_type, names_form, format)
get_data(data_type, format, names_form) if NAMES_FORMS.include?(names_form.to_sym)
end

def get_data(*path)
data = TwitterCldr::Utils.traverse_hash(calendar_data, path)
redirect = parse_redirect(data)
redirect ? get_data(*redirect) : data
cache_key = TwitterCldr::Utils.compute_cache_key([@locale] + path)
calendar_cache.fetch(cache_key) do |key|
data = TwitterCldr::Utils.traverse_hash(calendar_data, path)
redirect = parse_redirect(data)
calendar_cache[key] = if redirect
get_data(*redirect)
else
data
end
end
end

def parse_redirect(data)
$1.split('.').map(&:to_sym) if data.is_a?(Symbol) && data.to_s =~ redirect_regexp
if data.is_a?(Symbol) && data.to_s =~ redirect_regexp
result = $1.split('.').map(&:to_sym)
result.map { |leg| REDIRECT_CONVERSIONS.fetch(leg, leg) }
end
end

def redirect_regexp
Expand Down
122 changes: 122 additions & 0 deletions spec/shared/calendar_spec.rb
Expand Up @@ -11,6 +11,11 @@

let(:calendar) { Calendar.new(:de) }

before(:each) do
# clear cache for each test
Calendar.class_variable_set(:@@calendar_cache, {})
end

describe '#initialize' do
it 'returns calendar for default locale and type' do
stub(TwitterCldr).get_locale { :fr }
Expand Down Expand Up @@ -130,4 +135,121 @@
end
end

describe '#fields' do
it 'returns the list of fields for the locale (eg. weekday, month, etc)' do
fields = calendar.fields
fields[:hour].should match_normalized("Stunde")
fields[:dayperiod].should match_normalized("Tageshälfte")
fields[:weekday].should match_normalized("Wochentag")

fields = Calendar.new(:ja).fields
fields[:hour].should match_normalized("時")
fields[:dayperiod].should match_normalized("午前/午後")
fields[:weekday].should match_normalized("曜日")
end
end

describe '#quarters' do
it 'returns default quarters' do
calendar.quarters.should == {
1 => "1. Quartal",
2 => "2. Quartal",
3 => "3. Quartal",
4 => "4. Quartal"
}
end

it 'returns quarters with other name forms' do
calendar.quarters(:abbreviated).should == {
1 => "Q1", 2 => "Q2",
3 => "Q3", 4 => "Q4"
}

calendar.quarters(:narrow).should == {
1 => 1, 2 => 2,
3 => 3, 4 => 4
}
end
end

describe '#periods' do
it 'returns default periods' do
periods = calendar.periods
periods[:am].should == "vorm."
periods[:pm].should == "nachm."
end

it 'returns quarters with other name forms' do
periods = calendar.periods(:abbreviated)
periods[:am].should == "vorm."
periods[:pm].should == "nachm."
end
end

describe '#eras' do
it 'returns default eras' do
calendar.eras.should == {
0 => "v. Chr.",
1 => "n. Chr."
}
end

it 'returns eras with other name forms' do
calendar.eras(:abbr).should == {
0 => "v. Chr.",
1 => "n. Chr."
}
end
end

describe '#date_order' do
it 'should return the correct date order for a few different locales' do
Calendar.new(:en).date_order.should == [:month, :day, :year]
Calendar.new(:ja).date_order.should == [:year, :month, :day]
Calendar.new(:ar).date_order.should == [:day, :month, :year]
end
end

describe '#time_order' do
it 'should return the correct time order for a few different locales' do
Calendar.new(:en).time_order.should == [:hour, :minute, :second, :period]
Calendar.new(:ja).time_order.should == [:hour, :minute, :second]
Calendar.new(:ar).time_order.should == [:hour, :minute, :second, :period]
end
end

describe '#datetime_order' do
it 'should return the correct date and time order for a few different locales' do
Calendar.new(:en).datetime_order.should == [:month, :day, :year, :hour, :minute, :second, :period]
Calendar.new(:ja).datetime_order.should == [:year, :month, :day, :hour, :minute, :second]
Calendar.new(:ar).datetime_order.should == [:day, :month, :year, :hour, :minute, :second, :period]
end
end

describe '#methods_for_tokens' do
it 'converts pattern tokens into their corresponding method names' do
tokens = [TwitterCldr::Tokenizers::Token.new(:value => "YYYY", :type => :pattern)]
calendar.send(:methods_for_tokens, tokens).should == [:year_of_week_of_year]
end

it 'ignores plaintext tokens' do
tokens = [TwitterCldr::Tokenizers::Token.new(:value => "blarg", :type => :plaintext)]
calendar.send(:methods_for_tokens, tokens).should == []
end
end

describe '#resolve_methods' do
it 'converts certain method names to their basic equivalents' do
calendar.send(:resolve_methods, [:year_of_week_of_year]).should == [:year]
calendar.send(:resolve_methods, [:weekday_local]).should == [:weekday]
calendar.send(:resolve_methods, [:day_of_month, :second_fraction]).should == [:day, :second]
end

it 'does not convert basic method names' do
calendar.send(:resolve_methods, [:year]).should == [:year]
calendar.send(:resolve_methods, [:day, :month]).should == [:day, :month]
calendar.send(:resolve_methods, [:minute, :hour, :second]).should == [:minute, :hour, :second]
end
end

end

0 comments on commit b82605a

Please sign in to comment.