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