lib/when_exe/calendarnote.rb in when_exe-0.3.6 vs lib/when_exe/calendarnote.rb in when_exe-0.3.7
- old
+ new
@@ -1,801 +1,805 @@
-# -*- coding: utf-8 -*-
-=begin
- Copyright (C) 2011-2014 Takashi SUGA
-
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
-=end
-
-module When
-
- #
- # 暦注 - Calendar Note
- #
- class CalendarNote < TM::ReferenceSystem
-
- #
- # 暦注要素への名前アクセス機能提供
- #
- # @private
- module LabelAccess
- attr_reader :_pool
-
- #
- # 暦注要素への名前(label)によるアクセス
- #
- # @param [Numeric] key 配列インデックスと見なしてアクセス
- # @param [String] key 名前(label)と見なしてアクセス
- #
- # @return [Object] 暦注要素
- #
- def [](key)
- return super if key.kind_of?(Numeric)
- @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
- @_pool[key]
- end
- end
-
- #
- # 暦注要素のひな形クラス
- #
- # @private
- class NoteElement < When::BasicTypes::Object
- #
- # _m17n_form のための要素生成
- #
- # @param [Hash] options 下記のとおり
- # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
- #
- def _to_hash_value(options={})
- options[:method] == :to_m17n ? label : super
- end
- end
-
- #
- # 暦注検索結果を保存するコンテナ
- #
- module NotesContainer
-
- class << self
- #
- # 永続データに暦注検索結果を登録する
- #
- # @private
- def register(notes, persistence, sdn)
- if persistence
- persistence.store(sdn, notes)
- persistence.extend(self)
- end
- notes.extend(self)
- end
-
- #
- # 永続データから暦注検索結果を取り出す
- #
- # @private
- def retrieve(persistence, sdn)
- return false unless persistence
- persistence.fetch(sdn, false)
- end
-
- #
- # パターンの値の一致を判定する
- #
- # @private
- def verify(pattern, value)
- value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
- pattern = When::EncodingConversion.to_internal_encoding(pattern)
- pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
- end
- end
-
- # 暦注検索結果コンテナの次元を下げる
- #
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
- #
- # @return [NotesContainer]
- #
- def simplify(compact=true)
- if kind_of?(Hash) && !key?(:note)
- # Persistent NotesContainer
- simplified = {}
- each_pair do |key, value|
- value = value.simplify(compact) if value.kind_of?(NotesContainer)
- simplified[key] = value if value
- end
- else
- # Non-Persistent NotesContainer
- simplified = self
- simplified = _compact(simplified) if compact
- simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
- end
- _bless(simplified)
- end
-
- # 暦注検索結果コンテナのハッシュ要素
- #
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
- #
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
- #
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
- #
- def value(compact=true, symbol=:value)
- if kind_of?(Hash) && !key?(:note)
- # Persistent NotesContainer
- sliced = {}
- each_pair do |key, val|
- sliced[key] = val[symbol]
- end
- else
- # Non-Persistent NotesContainer
- return _bless(slice(symbol)) if symbol.kind_of?(Integer)
- sliced = _dig(self) {|target| target.fetch(symbol, nil)}
- end
- sliced = _bless(sliced)
- compact ? sliced.simplify : sliced
- end
-
- # 暦注検索結果コンテナのハッシュ要素
- #
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
- #
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
- #
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
- #
- def [](symbol)
- value(false, symbol)
- end
-
- # 暦注検索結果コンテナの条件に合う要素を抽出する
- #
- # @param [Hash{Symbol=>String or Regexp}] condition 条件
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
- #
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
- #
- def subset(condition, compact=true)
- if kind_of?(Hash) && !key?(:note)
- # Persistent NotesContainer
- result = {}
- each_pair do |key, value|
- value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
- result[key] = value if value
- end
- else
- # Non-Persistent NotesContainer
- result = _dig(self) {|target|
- (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
- }
- result = _compact(result) if compact
- end
- _bless(result)
- end
-
- private
-
- #
- # 対象オブジェクトを NotesContainer にする
- #
- def _bless(target)
- target.extend(NotesContainer) if target && !equal?(target)
- target
- end
-
- #
- # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
- #
- def _dig(list, &block)
- return yield(list) unless list.kind_of?(Array)
- list.map {|element| _dig(element, &block)}
- end
-
- #
- # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
- #
- def _compact(list)
- return list unless list.kind_of?(Array)
- result = list.map {|element| _compact(element)}.compact
- return result unless result.empty?
- nil
- end
- end
-
- #
- # 暦法によってイベントの動作を変えるか否か
- #
- CalendarDepend = false
-
- # デフォルトイベント名
- #
- # @return [String]
- #
- # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
- # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
- #
- attr_accessor :event
- protected :event=
-
- # デフォルトイベントの指定
- #
- # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
- #
- # @return [When::CalendarNote]
- #
- def copy(event)
- c = self.clone
- c.event = event
- c
- end
-
- # 典型的なイベントの発生間隔
- #
- # @param [String] event
- #
- # @return [When::TM::PeriodDuration]
- #
- def duration(event=@event)
- void, event, parameter = event.split(/^([^\d]+)/)
- send((event+'_delta').downcase.to_sym, parameter)
- end
-
- # 指定の日時が指定イベントに該当するか?
- #
- # @param [When::TM::TemporalPosition] date チェックされる日時
- # @param [String] event チェックするイベント名
- #
- # @return [Boolean]
- # [ true - 該当する ]
- # [ false - 該当しない ]
- #
- def include?(date, event=@event)
- enum_for(date, :forward, event.downcase).next.include?(date)
- end
-
- # Enumeratorの生成
- #
- # @overload enum_for(range, options={})
- # @param [Range, When::Parts::GeometricComplex] range
- # [ 始点 - range.first ]
- # [ 終点 - range.last ]
- # @param [Hash] options 以下の通り
- # @option options [String] :event イベント名(デフォルトは@event)
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
- #
- # @overload enum_for(first, direction=:forward, options={})
- # @param [When::TM::TemporalPosition] first 始点
- # @param [Symbol] direction (options[:direction]で渡してもよい)
- # [:forward] 昇順(デフォルト)
- # [:reverse] 降順
- # @param [Hash] options 以下の通り
- # @option options [String] :event イベント名(デフォルトは@event)
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
- #
- # @return [Enumerator]
- #
- def enum_for(*args)
- params = args.dup
- options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
- options[:event] ||= @event
- self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
- [self, params[0], options] :
- [self, params[0], params[1] || :forward, options]))
- end
- alias :to_enum :enum_for
-
- # 暦注の計算
- #
- # @param [When::TM::TemporalPosition] date 暦注を計算する日時
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
- # @param [String] options { :notes => String } という Hash の指定と等価
- # @param [Integer] options { :indices => Integer} という Hash の指定と等価
- # @param [Hash] options 下記のとおり
- # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
- # [ When::DAY ( 0) - 日 ]
- # [ When::MONTH(-1) - 月 ]
- # [ When::YEAR (-2) - 年 ]
- #
- # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
- # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
- # @option options [String] :notes 計算する暦注名(日の暦注)
- # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
- # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
- # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
- # @option options [:all] :notes すべての暦注を計算
- # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
- # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
- # @option options [Hash] :conditions 暦注計算の条件
- # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
- # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
- #
- # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
- # see {When::TM::TemporalPosition._instance}
- #
- # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
- # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
- #
- # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
- # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
- # ビットアドレスの値が 1 の暦注が計算対象となる。
- #
- # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
- # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
- # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
- #
- # @note
- # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
- # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
- # @note
- # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
- #
- def notes(date, options={})
- dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
- retrieved = NotesContainer.retrieve(persistence, date.to_i)
- return retrieved unless retrieved == false
- NotesContainer.register(indices.map {|i|
- next [] unless i <= date.precision
- _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
- focused_notes.each do |note|
- notes_hash[note] ||= _note_element(note, i, conditions, dates)
- end
- notes_hash
- end
- }, persistence, date.to_i)
- end
-
- #
- # 暦注の一致 or 不一致
- #
- # @param [When::TM::TemporalPosition] date 暦注を確認する日時
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
- # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
- # (指定の notes が存在する場合は前者、しない場合は後者)
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
- # @param [Hash] options 下記のとおり
- # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
- # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
- #
- # @return [Boolean]
- # [ true - 暦注が一致 ]
- # [ false - 暦注が不一致 ]
- #
- def note?(date, options={})
- options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
- pattern = options.delete(:value) if options.kind_of?(Hash)
- result = notes(date, options)
- result.flatten!
- result.delete_if {|hash| hash.empty?}
- return false unless result.size > 0
- return true unless pattern
- result.each do |hash|
- return true if NotesContainer.verify(pattern, hash[:value])
- end
- return false
- end
-
- # 年の名前を暦注として返す
- # @method year
- # @return [When::BasicTypes::M17n]
-
- # 月の名前を暦注として返す
- # @method month
- # @return [When::BasicTypes::M17n]
-
- # 日の名前を暦注として返す
- # @method day
- # @return [When::BasicTypes::M17n]
-
- #
- # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
- #
- # @private
- ['year', 'month', 'day'].each do |c|
- module_eval %Q{
- def #{c}(date)
- date.name('#{c}')
- end
- }
- end
-
- private
-
- #
- # 年月日暦注計算の共通処理 - コールバック元
- #
- def _note_values(dates, focused_notes, all_notes, note_objects)
- return [] unless dates && all_notes
-
- # prepare focused notes
- case focused_notes
- when Integer
- bits = ~focused_notes << 1
- focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
- when []
- focused_notes = all_notes
- when nil
- focused_notes = []
- end
- focused_notes = focused_notes.dup
- not_focused_notes = all_notes - focused_notes
- notes = {}
- not_focused_notes.each do |note|
- notes[note] = true
- end
- focused_notes.each do |note|
- notes[note] = nil
- end
-
- # update notes
- focused_notes_actual = focused_notes.dup
- notes = yield(dates, focused_notes_actual, notes)
- notes.keys.each do |note|
- notes.delete(note) unless focused_notes_actual.include?(note)
- end
-
- # return Array of Hash
- focused_notes.map {|note|
- case notes[note]
- when nil, false ; {}
- when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
- else
- if note_objects[note].respond_to?(:to_note_hash)
- note_objects[note].to_note_hash(notes[note], dates)
- else
- {:note=>note_objects[note].label, :value=>notes[note]}
- end
- end
- }
- end
-
- #
- # オブジェクトの正規化
- #
- def _normalize(args=[], options={})
- @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
- When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
- _to_iri(args, options[:prefix] || '_co:')
- if @_elements.kind_of?(Array)
- @_elements.each do |e|
- e.extend LabelAccess
- end
- end
- end
-
- #
- # 再帰的に配列の中を Resource化する
- #
- def _to_iri(args, prefix)
- args.map {|arg|
- case arg
- when String
- arg, method = $1, $2 if (arg =~ /^(.+)#([_A-Z0-9]+)$/i)
- obj = When.Resource(arg, prefix)
- obj = obj.copy(method) if method
- obj
- when Array
- _to_iri(arg, prefix)
- else
- arg
- end
- }
- end
-
- #
- # 暦日を当該暦注計算用クラスに変換
- #
- # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
- #
- def _to_date_for_note(date)
- date
- end
-
- # 暦注要素
- #
- # @return [Array<Array<When::Parts::Resource>>]
- #
- def _elements
- @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
- @_elements
- end
-
- #
- # [[暦注名]](全暦注)
- #
- # @return [Array<Array<String, When::BasicTypes::M17n>>]
- #
- def _all_keys
- @_all_keys ||= _elements.map { |c|
- c.map {|n|
- n.label.to_s
- }
- }
- end
-
- #
- # [[暦注名]](主要暦注)
- #
- # @return [Array<Array<When::Parts::Resource>>]
- #
- def _prime_keys
- @prime ||= _all_keys
- end
-
- #
- # notes メソッドの引数を parse する
- #
- # @return [Array] dates, indices, notes
- #
- def _parse_note(date, options)
- options =
- case options
- when Hash ; When::EncodingConversion.to_internal_encoding(options)
- when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
- when Integer ; {:indices => options}
- else ; {}
- end
- conditions = options.delete(:conditions) || {}
- _opt = options.dup
- persistence = _opt.delete(:persistence)
- notes = _notes(_opt.delete(:notes) || :prime)
- indices = _indices(_opt.delete(:indices), notes)
- _opt.keys.each do |key|
- conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
- end
- [_to_date_for_note(date), indices, notes, persistence, conditions, options]
- end
-
- #
- # 暦注を計算する暦座標の配列
- #
- # @return [Array<Integer>]
- #
- def _indices(indices, notes)
- case indices
- when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
- when Range ; indices.to_a
- when Array ; indices
- else ; [indices.to_i]
- end
- end
-
- #
- # notes メソッドの 文字列引数の意味を解釈する
- #
- # @return [Hash] options for note String
- #
- def _find_note(note)
- note = When::EncodingConversion.to_internal_encoding(note)
- _elements.each_index do |index|
- return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
- end
- {:value => note}
- end
-
- #
- # 計算する暦注
- #
- # @return [Array<Array<String>, Integer>]
- #
- def _notes(notes)
- case notes
- when :all ; _all_keys
- when :prime ; _prime_keys
- when Integer ; [notes]
- when String ; [[notes]]
- else ; notes
- end
- end
-
- #
- # 暦注の計算
- #
- # @return [Object] 暦注の値
- #
- def _note_element(note, index, conditions, dates)
- void, event, *parameter = note.split(/^([^\d]+)/)
- method = event.downcase
- parameter << conditions unless conditions.empty?
- return send(method, dates, *parameter) if respond_to?(method)
- root = _elements[index-1][note]
- parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
- parent = When.Resource(parent) if leaf
- return parent.send(method, dates, *parameter) if parent.respond_to?(method)
- root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
- end
-
- #
- # イベントを取得する Enumerator
- #
- class Enumerator < When::Parts::Enumerator
-
- #
- # 次のイベントを得る
- #
- # @return [When::TM::TemporalPosition]
- #
- def _succ
- @current = (@current==:first) ? @first : event_eval(@current + @delta)
- end
-
- # オブジェクトの生成
- #
- # @overload initialize(parent, range, options)
- # @param [When::CalendarNote] parent 暦注アルゴリズム
- # @param [Range, When::Parts::GeometricComplex] range
- # [ 始点 - range.first ]
- # [ 終点 - range.last ]
- # @param [Hash] options 以下の通り
- # @option options [String] :event イベント名
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
- #
- # @overload initialize(parent, first, direction, options)
- # @param [When::CalendarNote] parent 暦注アルゴリズム
- # @param [When::TM::TemporalPosition] first 始点
- # @param [Symbol] direction (options[:direction]で渡してもよい)
- # [:forward] 昇順(デフォルト)
- # [:reverse] 降順
- # @param [Hash] options 以下の通り
- # @option options [String] :event イベント名
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
- #
- def initialize(*args)
- if args[1].kind_of?(Range)
- parent, range, options = args
- first = range.first
- last = range.last
- direction = first < last ? :forward : :reverse
- else
- parent, first, direction, options = args
- end
- direction = options[:direction] if options[:direction]
- @parent = parent
- void, @event, @parameter = options.delete(:event).split(/^([^\d]+)/)
- @delta = @parent.send((@event+'_delta').to_sym, @parameter)
- instance_eval %Q{
- def event_eval(date)
- @parent.#{@event}(date, @parameter)
- end
- }
- date = event_eval(first)
- if direction == :reverse
- @delta = -@delta
- date = event_eval(first + @delta) if first.to_i < date.to_i
- else
- date = event_eval(first + @delta) if first.to_i > date.to_i
- end
- range ?
- super(@parent, range.exclude_end? ? date...last : date..last, options) :
- super(@parent, date, direction, options)
- end
- end
-
- #
- # 暦週
- #
- class Week < CalendarNote
-
- #
- # 暦週要素
- #
- class DayOfWeek < When::BasicTypes::M17n
-
- # 当該暦週要素の標準的な出現間隔
- #
- # @return [Integer]
- #
- attr_reader :delta
-
- # 所属する暦週オブジェクト
- #
- # @return [When::CalendarNote]
- #
- def week_note
- @week_note ||= When.CalendarNote(iri.split('/Notes').first)
- end
-
- # 当日または直前に当該暦週要素が現れる日付
- #
- # @return [When::TM::TemporalPosition]
- #
- def just_or_last(date)
- date = week_note._to_date_for_note(date)
- ([parent.child.length, @delta[When::DAY]].max*2).times do
- if equal?(week_note.week(date))
- date.events ||= []
- date.events << self
- return date
- end
- date -= When::P1D
- end
- raise ArgumentError, "#{self} not found"
- end
-
- private
-
- #
- # オブジェクトの正規化
- #
- def initialize(*args)
- super
- @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
- end
- end
-
- #
- # 曜日の名前の一覧
- #
- # @param [When::TM::TemporalPosition] date
- #
- # @return [Array<When::CalendarNote::Week::DayOfWeek>]
- #
- def week_labels(date)
- @days_of_week.child[0...week(date)[:position].last]
- end
-
- # 七曜
- #
- # @param [When::TM::TemporalPosition] date
- #
- # @return [When::Coordinates::Residue] 七曜
- #
- def standard_week(date)
- When.Residue('Week')[date.to_i % 7]
- end
-
- #
- # オブジェクトの正規化
- #
- def _normalize(args=[], options={})
- super
- @days_of_week = When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
- @days_of_week.child.length.times do |index|
- name = @days_of_week.child[index].label.to_s.downcase.gsub(/[- ]/, '_')
- self.class.module_eval %Q{
- def #{name}(date, parameter=nil)
- @days_of_week.child[#{index}].just_or_last(date)
- end
- } unless respond_to?(name)
- self.class.module_eval %Q{
- def #{name}_delta(parameter=nil)
- @days_of_week.child[#{index}].delta
- end
- } unless respond_to?(name + '_delta')
- end
- end
-
- #
- # イベントを取得する Enumerator
- #
- class Enumerator < When::CalendarNote::Enumerator
-
- #
- # 次のイベントを得る
- #
- # @return [When::TM::TemporalPosition]
- #
- def succ
- value = @current
- plus = @delta.sign > 0
- if @current==:first
- @first = event_eval(@first) unless plus
- @current = @first
- else
- if plus
- @current = event_eval(@current + @delta)
- else
- @last = event_eval(@current - When::P1D)
- @current = event_eval(@current + @delta)
- unless [@current.to_i, value.to_i].include?(@last.to_i)
- @current = @last
- return value
- end
- end
- @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
- end
- return value
- end
- end
- end
- end
-end
+# -*- coding: utf-8 -*-
+=begin
+ Copyright (C) 2011-2014 Takashi SUGA
+
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
+=end
+
+module When
+
+ #
+ # 暦注 - Calendar Note
+ #
+ class CalendarNote < TM::ReferenceSystem
+
+ #
+ # 暦注要素への名前アクセス機能提供
+ #
+ # @private
+ module LabelAccess
+ attr_reader :_pool
+
+ #
+ # 暦注要素への名前(label)によるアクセス
+ #
+ # @param [Numeric] key 配列インデックスと見なしてアクセス
+ # @param [String] key 名前(label)と見なしてアクセス
+ #
+ # @return [Object] 暦注要素
+ #
+ def [](key)
+ return super if key.kind_of?(Numeric)
+ @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
+ @_pool[key]
+ end
+ end
+
+ #
+ # 暦注要素のひな形クラス
+ #
+ # @private
+ class NoteElement < When::BasicTypes::Object
+ #
+ # _m17n_form のための要素生成
+ #
+ # @param [Hash] options 下記のとおり
+ # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
+ #
+ def _to_hash_value(options={})
+ options[:method] == :to_m17n ? label : super
+ end
+ end
+
+ #
+ # 暦注検索結果を保存するコンテナ
+ #
+ module NotesContainer
+
+ class << self
+ #
+ # 永続データに暦注検索結果を登録する
+ #
+ # @private
+ def register(notes, persistence, sdn)
+ if persistence
+ persistence.store(sdn, notes)
+ persistence.extend(self)
+ end
+ notes.extend(self)
+ end
+
+ #
+ # 永続データから暦注検索結果を取り出す
+ #
+ # @private
+ def retrieve(persistence, sdn)
+ return false unless persistence
+ persistence.fetch(sdn, false)
+ end
+
+ #
+ # パターンの値の一致を判定する
+ #
+ # @private
+ def verify(pattern, value)
+ value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
+ pattern = When::EncodingConversion.to_internal_encoding(pattern)
+ pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
+ end
+ end
+
+ # 暦注検索結果コンテナの次元を下げる
+ #
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
+ #
+ # @return [NotesContainer]
+ #
+ def simplify(compact=true)
+ if kind_of?(Hash) && !key?(:note)
+ # Persistent NotesContainer
+ simplified = {}
+ each_pair do |key, value|
+ value = value.simplify(compact) if value.kind_of?(NotesContainer)
+ simplified[key] = value if value
+ end
+ else
+ # Non-Persistent NotesContainer
+ simplified = self
+ simplified = _compact(simplified) if compact
+ simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
+ end
+ _bless(simplified)
+ end
+
+ # 暦注検索結果コンテナのハッシュ要素
+ #
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
+ #
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
+ #
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
+ #
+ def value(compact=true, symbol=:value)
+ if kind_of?(Hash) && !key?(:note)
+ # Persistent NotesContainer
+ sliced = {}
+ each_pair do |key, val|
+ sliced[key] = val[symbol]
+ end
+ else
+ # Non-Persistent NotesContainer
+ return _bless(slice(symbol)) if symbol.kind_of?(Integer)
+ sliced = _dig(self) {|target| target.fetch(symbol, nil)}
+ end
+ sliced = _bless(sliced)
+ compact ? sliced.simplify : sliced
+ end
+
+ # 暦注検索結果コンテナのハッシュ要素
+ #
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
+ #
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
+ #
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
+ #
+ def [](symbol)
+ value(false, symbol)
+ end
+
+ # 暦注検索結果コンテナの条件に合う要素を抽出する
+ #
+ # @param [Hash{Symbol=>String or Regexp}] condition 条件
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
+ #
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
+ #
+ def subset(condition, compact=true)
+ if kind_of?(Hash) && !key?(:note)
+ # Persistent NotesContainer
+ result = {}
+ each_pair do |key, value|
+ value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
+ result[key] = value if value
+ end
+ else
+ # Non-Persistent NotesContainer
+ result = _dig(self) {|target|
+ (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
+ }
+ result = _compact(result) if compact
+ end
+ _bless(result)
+ end
+
+ private
+
+ #
+ # 対象オブジェクトを NotesContainer にする
+ #
+ def _bless(target)
+ target.extend(NotesContainer) if target && !equal?(target)
+ target
+ end
+
+ #
+ # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
+ #
+ def _dig(list, &block)
+ return yield(list) unless list.kind_of?(Array)
+ list.map {|element| _dig(element, &block)}
+ end
+
+ #
+ # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
+ #
+ def _compact(list)
+ return list unless list.kind_of?(Array)
+ result = list.map {|element| _compact(element)}.compact
+ return result unless result.empty?
+ nil
+ end
+ end
+
+ #
+ # 暦法によってイベントの動作を変えるか否か
+ #
+ CalendarDepend = false
+
+ # デフォルトイベント名
+ #
+ # @return [String]
+ #
+ # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
+ # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
+ #
+ attr_accessor :event
+ private :event=
+
+ # デフォルトイベントの指定
+ #
+ # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
+ #
+ # @return [When::CalendarNote]
+ #
+ def copy(event)
+ c = self.clone
+ c.send(:event=, event)
+ c
+ end
+
+ # 典型的なイベントの発生間隔
+ #
+ # @param [String] event
+ #
+ # @return [When::TM::PeriodDuration]
+ #
+ def duration(event=@event)
+ void, event, parameter = event.split(/\A([^\d]+)/)
+ send((event+'_delta').downcase.to_sym, parameter)
+ end
+
+ # 指定の日時が指定イベントに該当するか?
+ #
+ # @param [When::TM::TemporalPosition] date チェックされる日時
+ # @param [String] event チェックするイベント名
+ #
+ # @return [Boolean]
+ # [ true - 該当する ]
+ # [ false - 該当しない ]
+ #
+ def include?(date, event=@event)
+ enum_for(date, :forward, event.downcase).next.include?(date)
+ end
+
+ # Enumeratorの生成
+ #
+ # @overload enum_for(range, options={})
+ # @param [Range, When::Parts::GeometricComplex] range
+ # [ 始点 - range.first ]
+ # [ 終点 - range.last ]
+ # @param [Hash] options 以下の通り
+ # @option options [String] :event イベント名(デフォルトは@event)
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
+ #
+ # @overload enum_for(first, direction=:forward, options={})
+ # @param [When::TM::TemporalPosition] first 始点
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
+ # [:forward] 昇順(デフォルト)
+ # [:reverse] 降順
+ # @param [Hash] options 以下の通り
+ # @option options [String] :event イベント名(デフォルトは@event)
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
+ #
+ # @return [Enumerator]
+ #
+ def enum_for(*args)
+ params = args.dup
+ options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
+ options[:event] ||= @event
+ self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
+ [self, params[0], options] :
+ [self, params[0], params[1] || :forward, options]))
+ end
+ alias :to_enum :enum_for
+
+ # 暦注の計算
+ #
+ # @param [When::TM::TemporalPosition] date 暦注を計算する日時
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
+ # @param [String] options { :notes => String } という Hash の指定と等価
+ # @param [Integer] options { :indices => Integer} という Hash の指定と等価
+ # @param [Hash] options 下記のとおり
+ # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
+ # [ When::DAY ( 0) - 日 ]
+ # [ When::MONTH(-1) - 月 ]
+ # [ When::YEAR (-2) - 年 ]
+ #
+ # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
+ # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
+ # @option options [String] :notes 計算する暦注名(日の暦注)
+ # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
+ # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
+ # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
+ # @option options [:all] :notes すべての暦注を計算
+ # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
+ # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
+ # @option options [Hash] :conditions 暦注計算の条件
+ # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
+ # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
+ #
+ # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
+ # see {When::TM::TemporalPosition._instance}
+ #
+ # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
+ # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
+ #
+ # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
+ # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
+ # ビットアドレスの値が 1 の暦注が計算対象となる。
+ #
+ # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
+ # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
+ # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
+ #
+ # @note
+ # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
+ # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
+ # @note
+ # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
+ #
+ def notes(date, options={})
+ dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
+ retrieved = NotesContainer.retrieve(persistence, date.to_i)
+ return retrieved unless retrieved == false
+ NotesContainer.register(indices.map {|i|
+ next [] unless i <= date.precision
+ _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
+ focused_notes.each do |note|
+ notes_hash[note] ||= _note_element(note, i, conditions, dates)
+ end
+ notes_hash
+ end
+ }, persistence, date.to_i)
+ end
+
+ #
+ # 暦注の一致 or 不一致
+ #
+ # @param [When::TM::TemporalPosition] date 暦注を確認する日時
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
+ # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
+ # (指定の notes が存在する場合は前者、しない場合は後者)
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
+ # @param [Hash] options 下記のとおり
+ # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
+ # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
+ #
+ # @return [Boolean]
+ # [ true - 暦注が一致 ]
+ # [ false - 暦注が不一致 ]
+ #
+ def note?(date, options={})
+ options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
+ pattern = options.delete(:value) if options.kind_of?(Hash)
+ result = notes(date, options)
+ result.flatten!
+ result.delete_if {|hash| hash.empty?}
+ return false unless result.size > 0
+ return true unless pattern
+ result.each do |hash|
+ return true if NotesContainer.verify(pattern, hash[:value])
+ end
+ return false
+ end
+
+ # 年の名前を暦注として返す
+ # @method year
+ # @return [When::BasicTypes::M17n]
+
+ # 月の名前を暦注として返す
+ # @method month
+ # @return [When::BasicTypes::M17n]
+
+ # 日の名前を暦注として返す
+ # @method day
+ # @return [When::BasicTypes::M17n]
+
+ #
+ # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
+ #
+ # @private
+ ['year', 'month', 'day'].each do |c|
+ module_eval %Q{
+ def #{c}(date)
+ date.name('#{c}')
+ end
+ }
+ end
+
+ private
+
+ #
+ # 年月日暦注計算の共通処理 - コールバック元
+ #
+ def _note_values(dates, focused_notes, all_notes, note_objects)
+ return [] unless dates && all_notes
+
+ # prepare focused notes
+ case focused_notes
+ when Integer
+ bits = ~focused_notes << 1
+ focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
+ when []
+ focused_notes = all_notes
+ when nil
+ focused_notes = []
+ end
+ focused_notes = focused_notes.dup
+ not_focused_notes = all_notes - focused_notes
+ notes = {}
+ not_focused_notes.each do |note|
+ notes[note] = true
+ end
+ focused_notes.each do |note|
+ notes[note] = nil
+ end
+
+ # update notes
+ focused_notes_actual = focused_notes.dup
+ notes = yield(dates, focused_notes_actual, notes)
+ notes.keys.each do |note|
+ notes.delete(note) unless focused_notes_actual.include?(note)
+ end
+
+ # return Array of Hash
+ focused_notes.map {|note|
+ case notes[note]
+ when nil, false ; {}
+ when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
+ else
+ if note_objects[note].respond_to?(:to_note_hash)
+ note_objects[note].to_note_hash(notes[note], dates)
+ else
+ {:note=>note_objects[note].label, :value=>notes[note]}
+ end
+ end
+ }
+ end
+
+ #
+ # オブジェクトの正規化
+ #
+ def _normalize(args=[], options={})
+ @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
+ When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
+ _to_iri(args, options[:prefix] || '_co:')
+ if @_elements.kind_of?(Array)
+ @_elements.each do |e|
+ e.extend LabelAccess
+ end
+ end
+ end
+
+ #
+ # 再帰的に配列の中を Resource化する
+ #
+ def _to_iri(args, prefix)
+ args.map {|arg|
+ case arg
+ when String
+ arg, method = $1, $2 if (arg =~ /\A(.+)#([_A-Z0-9]+)\z/i)
+ obj = When.Resource(arg, prefix)
+ obj = obj.copy(method) if method
+ obj
+ when Array
+ _to_iri(arg, prefix)
+ else
+ arg
+ end
+ }
+ end
+
+ #
+ # 暦日を当該暦注計算用クラスに変換
+ #
+ # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
+ #
+ def _to_date_for_note(date)
+ date
+ end
+
+ # 暦注要素
+ #
+ # @return [Array<Array<When::Parts::Resource>>]
+ #
+ def _elements
+ @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
+ @_elements
+ end
+
+ #
+ # [[暦注名]](全暦注)
+ #
+ # @return [Array<Array<String, When::BasicTypes::M17n>>]
+ #
+ def _all_keys
+ @_all_keys ||= _elements.map { |c|
+ c.map {|n|
+ n.label.to_s
+ }
+ }
+ end
+
+ #
+ # [[暦注名]](主要暦注)
+ #
+ # @return [Array<Array<When::Parts::Resource>>]
+ #
+ def _prime_keys
+ @prime ||= _all_keys
+ end
+
+ #
+ # notes メソッドの引数を parse する
+ #
+ # @return [Array] dates, indices, notes
+ #
+ def _parse_note(date, options)
+ options =
+ case options
+ when Hash ; When::EncodingConversion.to_internal_encoding(options)
+ when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
+ when Integer ; {:indices => options}
+ else ; {}
+ end
+ conditions = options.delete(:conditions) || {}
+ _opt = options.dup
+ persistence = _opt.delete(:persistence)
+ notes = _notes(_opt.delete(:notes) || :prime)
+ indices = _indices(_opt.delete(:indices), notes)
+ _opt.keys.each do |key|
+ conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
+ end
+ [_to_date_for_note(date), indices, notes, persistence, conditions, options]
+ end
+
+ #
+ # 暦注を計算する暦座標の配列
+ #
+ # @return [Array<Integer>]
+ #
+ def _indices(indices, notes)
+ case indices
+ when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
+ when Range ; indices.to_a
+ when Array ; indices
+ else ; [indices.to_i]
+ end
+ end
+
+ #
+ # notes メソッドの 文字列引数の意味を解釈する
+ #
+ # @return [Hash] options for note String
+ #
+ def _find_note(note)
+ note = When::EncodingConversion.to_internal_encoding(note)
+ _elements.each_index do |index|
+ return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
+ end
+ {:value => note}
+ end
+
+ #
+ # 計算する暦注
+ #
+ # @return [Array<Array<String>, Integer>]
+ #
+ def _notes(notes)
+ case notes
+ when :all ; _all_keys
+ when :prime ; _prime_keys
+ when Integer ; [notes]
+ when String ; [[notes]]
+ else ; notes
+ end
+ end
+
+ #
+ # 暦注の計算
+ #
+ # @return [Object] 暦注の値
+ #
+ def _note_element(note, index, conditions, dates)
+ void, event, *parameter = note.split(/\A([^\d]+)/)
+ method = event.downcase
+ parameter << conditions unless conditions.empty?
+ return send(method, dates, *parameter) if respond_to?(method)
+ root = _elements[index-1][note]
+ parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
+ parent = When.Resource(parent) if leaf
+ return parent.send(method, dates, *parameter) if parent.respond_to?(method)
+ root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
+ end
+
+ #
+ # イベントを取得する Enumerator
+ #
+ class Enumerator < When::Parts::Enumerator
+
+ #
+ # 次のイベントを得る
+ #
+ # @return [When::TM::TemporalPosition]
+ #
+ def _succ
+ @current = (@current==:first) ? @first : event_eval(@current + @delta)
+ end
+
+ # オブジェクトの生成
+ #
+ # @overload initialize(parent, range, options)
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
+ # @param [Range, When::Parts::GeometricComplex] range
+ # [ 始点 - range.first ]
+ # [ 終点 - range.last ]
+ # @param [Hash] options 以下の通り
+ # @option options [String] :event イベント名
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
+ #
+ # @overload initialize(parent, first, direction, options)
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
+ # @param [When::TM::TemporalPosition] first 始点
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
+ # [:forward] 昇順(デフォルト)
+ # [:reverse] 降順
+ # @param [Hash] options 以下の通り
+ # @option options [String] :event イベント名
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
+ #
+ def initialize(*args)
+ if args[1].kind_of?(Range)
+ parent, range, options = args
+ first = range.first
+ last = range.last
+ direction = first < last ? :forward : :reverse
+ else
+ parent, first, direction, options = args
+ end
+ direction = options[:direction] if options[:direction]
+ @parent = parent
+ event = options.delete(:event)
+ case event
+ when String ; void, @event, @parameter = event.split(/\A([^\d]+)/)
+ else ; @event, @parameter = [parent.event, event]
+ end
+ @delta = @parent.send((@event+'_delta').to_sym, @parameter)
+ instance_eval %Q{
+ def event_eval(date)
+ @parent.#{@event}(date, @parameter)
+ end
+ }
+ date = event_eval(first)
+ if direction == :reverse
+ @delta = -@delta
+ date = event_eval(first + @delta) if first.to_i < date.to_i
+ else
+ date = event_eval(first + @delta) if first.to_i > date.to_i
+ end
+ range ?
+ super(@parent, range.exclude_end? ? date...last : date..last, options) :
+ super(@parent, date, direction, options)
+ end
+ end
+
+ #
+ # 暦週
+ #
+ class Week < CalendarNote
+
+ #
+ # 暦週要素
+ #
+ class DayOfWeek < When::BasicTypes::M17n
+
+ # 当該暦週要素の標準的な出現間隔
+ #
+ # @return [Integer]
+ #
+ attr_reader :delta
+
+ # 所属する暦週オブジェクト
+ #
+ # @return [When::CalendarNote]
+ #
+ def week_note
+ @week_note ||= When.CalendarNote(iri.split('/Notes').first)
+ end
+
+ # 当日または直前に当該暦週要素が現れる日付
+ #
+ # @return [When::TM::TemporalPosition]
+ #
+ def just_or_last(date)
+ date = week_note._to_date_for_note(date)
+ ([parent.child.length, @delta[When::DAY]].max*2).times do
+ if equal?(week_note.week(date)[:value])
+ date.events ||= []
+ date.events << self
+ return date
+ end
+ date -= When::P1D
+ end
+ raise ArgumentError, "#{self} not found"
+ end
+
+ private
+
+ #
+ # オブジェクトの正規化
+ #
+ def initialize(*args)
+ super
+ @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
+ end
+ end
+
+ #
+ # 曜日の名前の一覧
+ #
+ # @param [When::TM::TemporalPosition] date
+ #
+ # @return [Array<When::CalendarNote::Week::DayOfWeek>]
+ #
+ def week_labels(date)
+ @days_of_week.child[0...week(date)[:position].last]
+ end
+
+ # 七曜
+ #
+ # @param [When::TM::TemporalPosition] date
+ #
+ # @return [When::Coordinates::Residue] 七曜
+ #
+ def common_week(date)
+ When.Residue('Week')[date.to_i % 7]
+ end
+
+ #
+ # オブジェクトの正規化
+ #
+ def _normalize(args=[], options={})
+ super
+ @days_of_week ||= When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
+ @days_of_week.child.length.times do |index|
+ name = @days_of_week.child[index].translate('en').downcase.gsub(/[- ]/, '_')
+ self.class.module_eval %Q{
+ def #{name}(date, parameter=nil)
+ @days_of_week.child[#{index}].just_or_last(date)
+ end
+ } unless respond_to?(name)
+ self.class.module_eval %Q{
+ def #{name}_delta(parameter=nil)
+ @days_of_week.child[#{index}].delta
+ end
+ } unless respond_to?(name + '_delta')
+ end
+ end
+
+ #
+ # イベントを取得する Enumerator
+ #
+ class Enumerator < When::CalendarNote::Enumerator
+
+ #
+ # 次のイベントを得る
+ #
+ # @return [When::TM::TemporalPosition]
+ #
+ def succ
+ value = @current
+ plus = @delta.sign > 0
+ if @current==:first
+ @first = event_eval(@first) unless plus
+ @current = @first
+ else
+ if plus
+ @current = event_eval(@current + @delta)
+ else
+ @last = event_eval(@current - When::P1D)
+ @current = event_eval(@current + @delta)
+ unless [@current.to_i, value.to_i].include?(@last.to_i)
+ @current = @last
+ return value
+ end
+ end
+ @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
+ end
+ return value
+ end
+ end
+ end
+ end
+end