lib/when_exe/calendarnote.rb in when_exe-0.3.5 vs lib/when_exe/calendarnote.rb in when_exe-0.3.6
- old
+ new
@@ -51,11 +51,11 @@
end
#
# 暦注検索結果を保存するコンテナ
#
- module Notes
+ module NotesContainer
class << self
#
# 永続データに暦注検索結果を登録する
#
@@ -81,30 +81,31 @@
# パターンの値の一致を判定する
#
# @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 [Notes]
+ # @return [NotesContainer]
#
def simplify(compact=true)
if kind_of?(Hash) && !key?(:note)
- # Persistent Notes
+ # Persistent NotesContainer
simplified = {}
each_pair do |key, value|
- value = value.simplify(compact) if value.kind_of?(Notes)
+ value = value.simplify(compact) if value.kind_of?(NotesContainer)
simplified[key] = value if value
end
else
- # Non-Persistent Notes
+ # Non-Persistent NotesContainer
simplified = self
simplified = _compact(simplified) if compact
simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
end
_bless(simplified)
@@ -119,17 +120,17 @@
#
# @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
#
def value(compact=true, symbol=:value)
if kind_of?(Hash) && !key?(:note)
- # Persistent Notes
+ # Persistent NotesContainer
sliced = {}
each_pair do |key, val|
sliced[key] = val[symbol]
end
else
- # Non-Persistent Notes
+ # 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
@@ -154,33 +155,33 @@
#
# @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
#
def subset(condition, compact=true)
if kind_of?(Hash) && !key?(:note)
- # Persistent Notes
+ # Persistent NotesContainer
result = {}
each_pair do |key, value|
- value = value.subset(condition, compact) if value.kind_of?(Notes)
+ value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
result[key] = value if value
end
else
- # Non-Persistent Notes
+ # Non-Persistent NotesContainer
result = _dig(self) {|target|
- (condition.each_pair {|key, pattern| break nil unless Notes.verify(pattern, target[key])}) ? target : nil
+ (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
#
- # 対象オブジェクトを Notes にする
+ # 対象オブジェクトを NotesContainer にする
#
def _bless(target)
- target.extend(Notes) if target && !equal?(target)
+ target.extend(NotesContainer) if target && !equal?(target)
target
end
#
# 多次元配列要素に対して再帰的に要素を抽出して操作を施す
@@ -199,10 +200,15 @@
return result unless result.empty?
nil
end
end
+ #
+ # 暦法によってイベントの動作を変えるか否か
+ #
+ CalendarDepend = false
+
# デフォルトイベント名
#
# @return [String]
#
# @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
@@ -305,17 +311,17 @@
#
# @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
# see {When::TM::TemporalPosition._instance}
#
# @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
- # 本メソッドの :prime オプションで参照される。(BalineseNote#_normalize等参照)
+ # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
#
- # @note 暦注のビットアドレスは、暦注サブクラスのNoteObjects定数の中の定義順序による。
+ # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
# When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
# ビットアドレスの値が 1 の暦注が計算対象となる。
#
- # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::Notesモジュールをextendしている)
+ # @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] に暦注名の入った
@@ -323,27 +329,17 @@
# @note
# 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
#
def notes(date, options={})
dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
- retrieved = Notes.retrieve(persistence, date.to_i)
+ retrieved = NotesContainer.retrieve(persistence, date.to_i)
return retrieved unless retrieved == false
- Notes.register(indices.map {|i|
+ 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|
- unless notes_hash[note]
- void, event, *parameter = note.split(/^([^\d]+)/)
- method = event.downcase
- parameter << conditions unless conditions.empty?
- notes_hash[note] =
- if respond_to?(method)
- send(method, dates, *parameter)
- else
- _elements[i-1][note].send(When::Coordinates::PRECISION_NAME[i].downcase, dates)
- end
- end
+ notes_hash[note] ||= _note_element(note, i, conditions, dates)
end
notes_hash
end
}, persistence, date.to_i)
end
@@ -363,19 +359,19 @@
# @return [Boolean]
# [ true - 暦注が一致 ]
# [ false - 暦注が不一致 ]
#
def note?(date, options={})
- options = _find_note(options) if options.kind_of?(String)
+ 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 Notes.verify(pattern, hash[:value])
+ return true if NotesContainer.verify(pattern, hash[:value])
end
return false
end
# 年の名前を暦注として返す
@@ -403,23 +399,99 @@
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?(:NoteObjects)) ?
- When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/NoteObjects' :
+ @_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
@@ -455,12 +527,12 @@
# @return [Array] dates, indices, notes
#
def _parse_note(date, options)
options =
case options
- when Hash ; options
- when String ; {:notes => 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
@@ -472,15 +544,30 @@
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
@@ -499,95 +586,27 @@
else ; notes
end
end
#
- # 暦注を計算する暦座標の配列
+ # 暦注の計算
#
- # @return [Array<Integer>]
+ # @return [Object] 暦注の値
#
- 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
+ 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
#
- # 年月日暦注計算の共通処理 - コールバック元
- #
- 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
- notes = yield(dates, focused_notes, notes)
- notes.keys.each do |note|
- notes.delete(note) unless focused_notes.include?(note)
- end
-
- # return Array of Hash
- focused_notes.map {|note|
- next {} unless notes[note]
- 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
-
- #
- # 再帰的に配列の中を 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
-
- #
# イベントを取得する Enumerator
#
class Enumerator < When::Parts::Enumerator
#
@@ -646,9 +665,137 @@
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