when_exe 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +171 -0
  3. data/lib/when_exe.rb +78 -47
  4. data/lib/when_exe/basictypes.rb +752 -747
  5. data/lib/when_exe/calendarnote.rb +805 -801
  6. data/lib/when_exe/calendartypes.rb +1583 -1531
  7. data/lib/when_exe/coordinates.rb +16 -15
  8. data/lib/when_exe/core/duration.rb +114 -110
  9. data/lib/when_exe/core/extension.rb +504 -504
  10. data/lib/when_exe/ephemeris.rb +1917 -1913
  11. data/lib/when_exe/ephemeris/moon.rb +333 -333
  12. data/lib/when_exe/ephemeris/notes.rb +389 -387
  13. data/lib/when_exe/ephemeris/planets.rb +585 -585
  14. data/lib/when_exe/ephemeris/sun.rb +214 -214
  15. data/lib/when_exe/googlecalendar.rb +144 -140
  16. data/lib/when_exe/icalendar.rb +1636 -1636
  17. data/lib/when_exe/inspect.rb +46 -22
  18. data/lib/when_exe/locales/akt.rb +176 -176
  19. data/lib/when_exe/locales/encoding_conversion.rb +134 -126
  20. data/lib/when_exe/locales/iast.rb +90 -90
  21. data/lib/when_exe/locales/locale.rb +750 -746
  22. data/lib/when_exe/locales/transliteration_table.rb +62 -62
  23. data/lib/when_exe/mini_application.rb +307 -305
  24. data/lib/when_exe/parts/enumerator.rb +2 -2
  25. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  26. data/lib/when_exe/parts/method_cash.rb +224 -224
  27. data/lib/when_exe/parts/resource.rb +1069 -1071
  28. data/lib/when_exe/parts/timezone.rb +240 -230
  29. data/lib/when_exe/region/armenian.rb +56 -56
  30. data/lib/when_exe/region/babylonian.rb +405 -0
  31. data/lib/when_exe/region/bahai.rb +146 -146
  32. data/lib/when_exe/region/balinese.rb +622 -622
  33. data/lib/when_exe/region/chinese.rb +95 -25
  34. data/lib/when_exe/region/chinese/calendars.rb +1016 -1016
  35. data/lib/when_exe/region/chinese/epochs.rb +1 -1
  36. data/lib/when_exe/region/chinese/twins.rb +803 -795
  37. data/lib/when_exe/region/christian.rb +824 -824
  38. data/lib/when_exe/region/coptic.rb +106 -87
  39. data/lib/when_exe/region/discordian.rb +225 -225
  40. data/lib/when_exe/region/far_east.rb +188 -188
  41. data/lib/when_exe/region/french.rb +56 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +58 -58
  44. data/lib/when_exe/region/indian.rb +1254 -1251
  45. data/lib/when_exe/region/iranian.rb +8 -8
  46. data/lib/when_exe/region/islamic.rb +3 -3
  47. data/lib/when_exe/region/japanese.rb +93 -99
  48. data/lib/when_exe/region/japanese/calendars.rb +396 -397
  49. data/lib/when_exe/region/japanese/epochs.rb +26 -26
  50. data/lib/when_exe/region/japanese/nihon_shoki.rb +71 -71
  51. data/lib/when_exe/region/japanese/notes.rb +1383 -1386
  52. data/lib/when_exe/region/japanese/residues.rb +1306 -1306
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -0
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +126 -126
  57. data/lib/when_exe/region/korean.rb +378 -378
  58. data/lib/when_exe/region/m17n.rb +114 -113
  59. data/lib/when_exe/region/martian.rb +258 -255
  60. data/lib/when_exe/region/mayan.rb +32 -32
  61. data/lib/when_exe/region/residue.rb +89 -89
  62. data/lib/when_exe/region/roman.rb +36 -24
  63. data/lib/when_exe/region/ryukyu.rb +97 -97
  64. data/lib/when_exe/region/shire.rb +240 -240
  65. data/lib/when_exe/region/soviet.rb +209 -0
  66. data/lib/when_exe/region/symmetry.rb +50 -50
  67. data/lib/when_exe/region/thai.rb +336 -335
  68. data/lib/when_exe/region/tibetan.rb +316 -315
  69. data/lib/when_exe/region/vietnamese.rb +440 -439
  70. data/lib/when_exe/region/weekdate.rb +80 -80
  71. data/lib/when_exe/region/world.rb +175 -175
  72. data/lib/when_exe/region/yerm.rb +14 -14
  73. data/lib/when_exe/region/zoroastrian.rb +203 -203
  74. data/lib/when_exe/timestandard.rb +707 -681
  75. data/lib/when_exe/tmduration.rb +338 -330
  76. data/lib/when_exe/tmobjects.rb +1346 -1325
  77. data/lib/when_exe/tmposition.rb +2115 -2072
  78. data/lib/when_exe/tmreference.rb +1693 -1669
  79. data/lib/when_exe/version.rb +1 -1
  80. data/link_to_online_documents +1 -1
  81. data/test/examples/JapanHolidaysRFC6350.ics +1 -1
  82. data/test/test.rb +67 -61
  83. data/test/test/basictypes.rb +409 -409
  84. data/test/test/calendarnote.rb +86 -69
  85. data/test/test/calendartypes.rb +97 -97
  86. data/test/test/coordinates.rb +396 -396
  87. data/test/test/ephemeris.rb +83 -74
  88. data/test/test/ephemeris/moon.rb +14 -14
  89. data/test/test/ephemeris/planets.rb +14 -14
  90. data/test/test/ephemeris/sun.rb +14 -14
  91. data/test/test/googlecalendar.rb +194 -176
  92. data/test/test/icalendar.rb +867 -858
  93. data/test/test/inspect.rb +117 -117
  94. data/test/test/parts.rb +487 -487
  95. data/test/test/region/balinese.rb +34 -0
  96. data/test/test/region/chinese.rb +218 -206
  97. data/test/test/region/christian.rb +245 -245
  98. data/test/test/region/coptic.rb +27 -27
  99. data/test/test/region/french.rb +33 -33
  100. data/test/test/region/geologicalage.rb +17 -17
  101. data/test/test/region/indian.rb +57 -57
  102. data/test/test/region/iran.rb +54 -54
  103. data/test/test/region/islamic.rb +18 -18
  104. data/test/test/region/japanese.rb +237 -219
  105. data/test/test/region/jewish.rb +61 -61
  106. data/test/test/region/m17n.rb +184 -184
  107. data/test/test/region/mayan.rb +195 -195
  108. data/test/test/region/residue.rb +147 -139
  109. data/test/test/region/thai.rb +116 -116
  110. data/test/test/region/tibetan.rb +30 -30
  111. data/test/test/region/vietnamese.rb +102 -102
  112. data/test/test/region/yerm.rb +146 -146
  113. data/test/test/timestandard.rb +81 -81
  114. data/test/test/tmobjects.rb +328 -328
  115. data/test/test/tmposition.rb +397 -284
  116. data/test/test/tmreference.rb +157 -157
  117. metadata +13 -10
@@ -1,801 +1,805 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2014 Takashi SUGA
4
-
5
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
- =end
7
-
8
- module When
9
-
10
- #
11
- # 暦注 - Calendar Note
12
- #
13
- class CalendarNote < TM::ReferenceSystem
14
-
15
- #
16
- # 暦注要素への名前アクセス機能提供
17
- #
18
- # @private
19
- module LabelAccess
20
- attr_reader :_pool
21
-
22
- #
23
- # 暦注要素への名前(label)によるアクセス
24
- #
25
- # @param [Numeric] key 配列インデックスと見なしてアクセス
26
- # @param [String] key 名前(label)と見なしてアクセス
27
- #
28
- # @return [Object] 暦注要素
29
- #
30
- def [](key)
31
- return super if key.kind_of?(Numeric)
32
- @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
33
- @_pool[key]
34
- end
35
- end
36
-
37
- #
38
- # 暦注要素のひな形クラス
39
- #
40
- # @private
41
- class NoteElement < When::BasicTypes::Object
42
- #
43
- # _m17n_form のための要素生成
44
- #
45
- # @param [Hash] options 下記のとおり
46
- # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
47
- #
48
- def _to_hash_value(options={})
49
- options[:method] == :to_m17n ? label : super
50
- end
51
- end
52
-
53
- #
54
- # 暦注検索結果を保存するコンテナ
55
- #
56
- module NotesContainer
57
-
58
- class << self
59
- #
60
- # 永続データに暦注検索結果を登録する
61
- #
62
- # @private
63
- def register(notes, persistence, sdn)
64
- if persistence
65
- persistence.store(sdn, notes)
66
- persistence.extend(self)
67
- end
68
- notes.extend(self)
69
- end
70
-
71
- #
72
- # 永続データから暦注検索結果を取り出す
73
- #
74
- # @private
75
- def retrieve(persistence, sdn)
76
- return false unless persistence
77
- persistence.fetch(sdn, false)
78
- end
79
-
80
- #
81
- # パターンの値の一致を判定する
82
- #
83
- # @private
84
- def verify(pattern, value)
85
- value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
86
- pattern = When::EncodingConversion.to_internal_encoding(pattern)
87
- pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
88
- end
89
- end
90
-
91
- # 暦注検索結果コンテナの次元を下げる
92
- #
93
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
94
- #
95
- # @return [NotesContainer]
96
- #
97
- def simplify(compact=true)
98
- if kind_of?(Hash) && !key?(:note)
99
- # Persistent NotesContainer
100
- simplified = {}
101
- each_pair do |key, value|
102
- value = value.simplify(compact) if value.kind_of?(NotesContainer)
103
- simplified[key] = value if value
104
- end
105
- else
106
- # Non-Persistent NotesContainer
107
- simplified = self
108
- simplified = _compact(simplified) if compact
109
- simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
110
- end
111
- _bless(simplified)
112
- end
113
-
114
- # 暦注検索結果コンテナのハッシュ要素
115
- #
116
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
117
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
118
- #
119
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
120
- #
121
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
122
- #
123
- def value(compact=true, symbol=:value)
124
- if kind_of?(Hash) && !key?(:note)
125
- # Persistent NotesContainer
126
- sliced = {}
127
- each_pair do |key, val|
128
- sliced[key] = val[symbol]
129
- end
130
- else
131
- # Non-Persistent NotesContainer
132
- return _bless(slice(symbol)) if symbol.kind_of?(Integer)
133
- sliced = _dig(self) {|target| target.fetch(symbol, nil)}
134
- end
135
- sliced = _bless(sliced)
136
- compact ? sliced.simplify : sliced
137
- end
138
-
139
- # 暦注検索結果コンテナのハッシュ要素
140
- #
141
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
142
- #
143
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
144
- #
145
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
146
- #
147
- def [](symbol)
148
- value(false, symbol)
149
- end
150
-
151
- # 暦注検索結果コンテナの条件に合う要素を抽出する
152
- #
153
- # @param [Hash{Symbol=>String or Regexp}] condition 条件
154
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
155
- #
156
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
157
- #
158
- def subset(condition, compact=true)
159
- if kind_of?(Hash) && !key?(:note)
160
- # Persistent NotesContainer
161
- result = {}
162
- each_pair do |key, value|
163
- value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
164
- result[key] = value if value
165
- end
166
- else
167
- # Non-Persistent NotesContainer
168
- result = _dig(self) {|target|
169
- (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
170
- }
171
- result = _compact(result) if compact
172
- end
173
- _bless(result)
174
- end
175
-
176
- private
177
-
178
- #
179
- # 対象オブジェクトを NotesContainer にする
180
- #
181
- def _bless(target)
182
- target.extend(NotesContainer) if target && !equal?(target)
183
- target
184
- end
185
-
186
- #
187
- # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
188
- #
189
- def _dig(list, &block)
190
- return yield(list) unless list.kind_of?(Array)
191
- list.map {|element| _dig(element, &block)}
192
- end
193
-
194
- #
195
- # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
196
- #
197
- def _compact(list)
198
- return list unless list.kind_of?(Array)
199
- result = list.map {|element| _compact(element)}.compact
200
- return result unless result.empty?
201
- nil
202
- end
203
- end
204
-
205
- #
206
- # 暦法によってイベントの動作を変えるか否か
207
- #
208
- CalendarDepend = false
209
-
210
- # デフォルトイベント名
211
- #
212
- # @return [String]
213
- #
214
- # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
215
- # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
216
- #
217
- attr_accessor :event
218
- protected :event=
219
-
220
- # デフォルトイベントの指定
221
- #
222
- # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
223
- #
224
- # @return [When::CalendarNote]
225
- #
226
- def copy(event)
227
- c = self.clone
228
- c.event = event
229
- c
230
- end
231
-
232
- # 典型的なイベントの発生間隔
233
- #
234
- # @param [String] event
235
- #
236
- # @return [When::TM::PeriodDuration]
237
- #
238
- def duration(event=@event)
239
- void, event, parameter = event.split(/^([^\d]+)/)
240
- send((event+'_delta').downcase.to_sym, parameter)
241
- end
242
-
243
- # 指定の日時が指定イベントに該当するか?
244
- #
245
- # @param [When::TM::TemporalPosition] date チェックされる日時
246
- # @param [String] event チェックするイベント名
247
- #
248
- # @return [Boolean]
249
- # [ true - 該当する ]
250
- # [ false - 該当しない ]
251
- #
252
- def include?(date, event=@event)
253
- enum_for(date, :forward, event.downcase).next.include?(date)
254
- end
255
-
256
- # Enumeratorの生成
257
- #
258
- # @overload enum_for(range, options={})
259
- # @param [Range, When::Parts::GeometricComplex] range
260
- # [ 始点 - range.first ]
261
- # [ 終点 - range.last ]
262
- # @param [Hash] options 以下の通り
263
- # @option options [String] :event イベント名(デフォルトは@event)
264
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
265
- #
266
- # @overload enum_for(first, direction=:forward, options={})
267
- # @param [When::TM::TemporalPosition] first 始点
268
- # @param [Symbol] direction (options[:direction]で渡してもよい)
269
- # [:forward] 昇順(デフォルト)
270
- # [:reverse] 降順
271
- # @param [Hash] options 以下の通り
272
- # @option options [String] :event イベント名(デフォルトは@event)
273
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
274
- #
275
- # @return [Enumerator]
276
- #
277
- def enum_for(*args)
278
- params = args.dup
279
- options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
280
- options[:event] ||= @event
281
- self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
282
- [self, params[0], options] :
283
- [self, params[0], params[1] || :forward, options]))
284
- end
285
- alias :to_enum :enum_for
286
-
287
- # 暦注の計算
288
- #
289
- # @param [When::TM::TemporalPosition] date 暦注を計算する日時
290
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
291
- # @param [String] options { :notes => String } という Hash の指定と等価
292
- # @param [Integer] options { :indices => Integer} という Hash の指定と等価
293
- # @param [Hash] options 下記のとおり
294
- # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
295
- # [ When::DAY ( 0) - 日 ]
296
- # [ When::MONTH(-1) - 月 ]
297
- # [ When::YEAR (-2) - 年 ]
298
- #
299
- # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
300
- # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
301
- # @option options [String] :notes 計算する暦注名(日の暦注)
302
- # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
303
- # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
304
- # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
305
- # @option options [:all] :notes すべての暦注を計算
306
- # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
307
- # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
308
- # @option options [Hash] :conditions 暦注計算の条件
309
- # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
310
- # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
311
- #
312
- # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
313
- # see {When::TM::TemporalPosition._instance}
314
- #
315
- # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
316
- # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
317
- #
318
- # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
319
- # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
320
- # ビットアドレスの値が 1 の暦注が計算対象となる。
321
- #
322
- # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
323
- # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
324
- # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
325
- #
326
- # @note
327
- # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
328
- # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
329
- # @note
330
- # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
331
- #
332
- def notes(date, options={})
333
- dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
334
- retrieved = NotesContainer.retrieve(persistence, date.to_i)
335
- return retrieved unless retrieved == false
336
- NotesContainer.register(indices.map {|i|
337
- next [] unless i <= date.precision
338
- _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
339
- focused_notes.each do |note|
340
- notes_hash[note] ||= _note_element(note, i, conditions, dates)
341
- end
342
- notes_hash
343
- end
344
- }, persistence, date.to_i)
345
- end
346
-
347
- #
348
- # 暦注の一致 or 不一致
349
- #
350
- # @param [When::TM::TemporalPosition] date 暦注を確認する日時
351
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
352
- # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
353
- # (指定の notes が存在する場合は前者、しない場合は後者)
354
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
355
- # @param [Hash] options 下記のとおり
356
- # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
357
- # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
358
- #
359
- # @return [Boolean]
360
- # [ true - 暦注が一致 ]
361
- # [ false - 暦注が不一致 ]
362
- #
363
- def note?(date, options={})
364
- options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
365
- pattern = options.delete(:value) if options.kind_of?(Hash)
366
- result = notes(date, options)
367
- result.flatten!
368
- result.delete_if {|hash| hash.empty?}
369
- return false unless result.size > 0
370
- return true unless pattern
371
- result.each do |hash|
372
- return true if NotesContainer.verify(pattern, hash[:value])
373
- end
374
- return false
375
- end
376
-
377
- # 年の名前を暦注として返す
378
- # @method year
379
- # @return [When::BasicTypes::M17n]
380
-
381
- # 月の名前を暦注として返す
382
- # @method month
383
- # @return [When::BasicTypes::M17n]
384
-
385
- # 日の名前を暦注として返す
386
- # @method day
387
- # @return [When::BasicTypes::M17n]
388
-
389
- #
390
- # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
391
- #
392
- # @private
393
- ['year', 'month', 'day'].each do |c|
394
- module_eval %Q{
395
- def #{c}(date)
396
- date.name('#{c}')
397
- end
398
- }
399
- end
400
-
401
- private
402
-
403
- #
404
- # 年月日暦注計算の共通処理 - コールバック元
405
- #
406
- def _note_values(dates, focused_notes, all_notes, note_objects)
407
- return [] unless dates && all_notes
408
-
409
- # prepare focused notes
410
- case focused_notes
411
- when Integer
412
- bits = ~focused_notes << 1
413
- focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
414
- when []
415
- focused_notes = all_notes
416
- when nil
417
- focused_notes = []
418
- end
419
- focused_notes = focused_notes.dup
420
- not_focused_notes = all_notes - focused_notes
421
- notes = {}
422
- not_focused_notes.each do |note|
423
- notes[note] = true
424
- end
425
- focused_notes.each do |note|
426
- notes[note] = nil
427
- end
428
-
429
- # update notes
430
- focused_notes_actual = focused_notes.dup
431
- notes = yield(dates, focused_notes_actual, notes)
432
- notes.keys.each do |note|
433
- notes.delete(note) unless focused_notes_actual.include?(note)
434
- end
435
-
436
- # return Array of Hash
437
- focused_notes.map {|note|
438
- case notes[note]
439
- when nil, false ; {}
440
- when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
441
- else
442
- if note_objects[note].respond_to?(:to_note_hash)
443
- note_objects[note].to_note_hash(notes[note], dates)
444
- else
445
- {:note=>note_objects[note].label, :value=>notes[note]}
446
- end
447
- end
448
- }
449
- end
450
-
451
- #
452
- # オブジェクトの正規化
453
- #
454
- def _normalize(args=[], options={})
455
- @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
456
- When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
457
- _to_iri(args, options[:prefix] || '_co:')
458
- if @_elements.kind_of?(Array)
459
- @_elements.each do |e|
460
- e.extend LabelAccess
461
- end
462
- end
463
- end
464
-
465
- #
466
- # 再帰的に配列の中を Resource化する
467
- #
468
- def _to_iri(args, prefix)
469
- args.map {|arg|
470
- case arg
471
- when String
472
- arg, method = $1, $2 if (arg =~ /^(.+)#([_A-Z0-9]+)$/i)
473
- obj = When.Resource(arg, prefix)
474
- obj = obj.copy(method) if method
475
- obj
476
- when Array
477
- _to_iri(arg, prefix)
478
- else
479
- arg
480
- end
481
- }
482
- end
483
-
484
- #
485
- # 暦日を当該暦注計算用クラスに変換
486
- #
487
- # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
488
- #
489
- def _to_date_for_note(date)
490
- date
491
- end
492
-
493
- # 暦注要素
494
- #
495
- # @return [Array<Array<When::Parts::Resource>>]
496
- #
497
- def _elements
498
- @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
499
- @_elements
500
- end
501
-
502
- #
503
- # [[暦注名]](全暦注)
504
- #
505
- # @return [Array<Array<String, When::BasicTypes::M17n>>]
506
- #
507
- def _all_keys
508
- @_all_keys ||= _elements.map { |c|
509
- c.map {|n|
510
- n.label.to_s
511
- }
512
- }
513
- end
514
-
515
- #
516
- # [[暦注名]](主要暦注)
517
- #
518
- # @return [Array<Array<When::Parts::Resource>>]
519
- #
520
- def _prime_keys
521
- @prime ||= _all_keys
522
- end
523
-
524
- #
525
- # notes メソッドの引数を parse する
526
- #
527
- # @return [Array] dates, indices, notes
528
- #
529
- def _parse_note(date, options)
530
- options =
531
- case options
532
- when Hash ; When::EncodingConversion.to_internal_encoding(options)
533
- when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
534
- when Integer ; {:indices => options}
535
- else ; {}
536
- end
537
- conditions = options.delete(:conditions) || {}
538
- _opt = options.dup
539
- persistence = _opt.delete(:persistence)
540
- notes = _notes(_opt.delete(:notes) || :prime)
541
- indices = _indices(_opt.delete(:indices), notes)
542
- _opt.keys.each do |key|
543
- conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
544
- end
545
- [_to_date_for_note(date), indices, notes, persistence, conditions, options]
546
- end
547
-
548
- #
549
- # 暦注を計算する暦座標の配列
550
- #
551
- # @return [Array<Integer>]
552
- #
553
- def _indices(indices, notes)
554
- case indices
555
- when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
556
- when Range ; indices.to_a
557
- when Array ; indices
558
- else ; [indices.to_i]
559
- end
560
- end
561
-
562
- #
563
- # notes メソッドの 文字列引数の意味を解釈する
564
- #
565
- # @return [Hash] options for note String
566
- #
567
- def _find_note(note)
568
- note = When::EncodingConversion.to_internal_encoding(note)
569
- _elements.each_index do |index|
570
- return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
571
- end
572
- {:value => note}
573
- end
574
-
575
- #
576
- # 計算する暦注
577
- #
578
- # @return [Array<Array<String>, Integer>]
579
- #
580
- def _notes(notes)
581
- case notes
582
- when :all ; _all_keys
583
- when :prime ; _prime_keys
584
- when Integer ; [notes]
585
- when String ; [[notes]]
586
- else ; notes
587
- end
588
- end
589
-
590
- #
591
- # 暦注の計算
592
- #
593
- # @return [Object] 暦注の値
594
- #
595
- def _note_element(note, index, conditions, dates)
596
- void, event, *parameter = note.split(/^([^\d]+)/)
597
- method = event.downcase
598
- parameter << conditions unless conditions.empty?
599
- return send(method, dates, *parameter) if respond_to?(method)
600
- root = _elements[index-1][note]
601
- parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
602
- parent = When.Resource(parent) if leaf
603
- return parent.send(method, dates, *parameter) if parent.respond_to?(method)
604
- root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
605
- end
606
-
607
- #
608
- # イベントを取得する Enumerator
609
- #
610
- class Enumerator < When::Parts::Enumerator
611
-
612
- #
613
- # 次のイベントを得る
614
- #
615
- # @return [When::TM::TemporalPosition]
616
- #
617
- def _succ
618
- @current = (@current==:first) ? @first : event_eval(@current + @delta)
619
- end
620
-
621
- # オブジェクトの生成
622
- #
623
- # @overload initialize(parent, range, options)
624
- # @param [When::CalendarNote] parent 暦注アルゴリズム
625
- # @param [Range, When::Parts::GeometricComplex] range
626
- # [ 始点 - range.first ]
627
- # [ 終点 - range.last ]
628
- # @param [Hash] options 以下の通り
629
- # @option options [String] :event イベント名
630
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
631
- #
632
- # @overload initialize(parent, first, direction, options)
633
- # @param [When::CalendarNote] parent 暦注アルゴリズム
634
- # @param [When::TM::TemporalPosition] first 始点
635
- # @param [Symbol] direction (options[:direction]で渡してもよい)
636
- # [:forward] 昇順(デフォルト)
637
- # [:reverse] 降順
638
- # @param [Hash] options 以下の通り
639
- # @option options [String] :event イベント名
640
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
641
- #
642
- def initialize(*args)
643
- if args[1].kind_of?(Range)
644
- parent, range, options = args
645
- first = range.first
646
- last = range.last
647
- direction = first < last ? :forward : :reverse
648
- else
649
- parent, first, direction, options = args
650
- end
651
- direction = options[:direction] if options[:direction]
652
- @parent = parent
653
- void, @event, @parameter = options.delete(:event).split(/^([^\d]+)/)
654
- @delta = @parent.send((@event+'_delta').to_sym, @parameter)
655
- instance_eval %Q{
656
- def event_eval(date)
657
- @parent.#{@event}(date, @parameter)
658
- end
659
- }
660
- date = event_eval(first)
661
- if direction == :reverse
662
- @delta = -@delta
663
- date = event_eval(first + @delta) if first.to_i < date.to_i
664
- else
665
- date = event_eval(first + @delta) if first.to_i > date.to_i
666
- end
667
- range ?
668
- super(@parent, range.exclude_end? ? date...last : date..last, options) :
669
- super(@parent, date, direction, options)
670
- end
671
- end
672
-
673
- #
674
- # 暦週
675
- #
676
- class Week < CalendarNote
677
-
678
- #
679
- # 暦週要素
680
- #
681
- class DayOfWeek < When::BasicTypes::M17n
682
-
683
- # 当該暦週要素の標準的な出現間隔
684
- #
685
- # @return [Integer]
686
- #
687
- attr_reader :delta
688
-
689
- # 所属する暦週オブジェクト
690
- #
691
- # @return [When::CalendarNote]
692
- #
693
- def week_note
694
- @week_note ||= When.CalendarNote(iri.split('/Notes').first)
695
- end
696
-
697
- # 当日または直前に当該暦週要素が現れる日付
698
- #
699
- # @return [When::TM::TemporalPosition]
700
- #
701
- def just_or_last(date)
702
- date = week_note._to_date_for_note(date)
703
- ([parent.child.length, @delta[When::DAY]].max*2).times do
704
- if equal?(week_note.week(date))
705
- date.events ||= []
706
- date.events << self
707
- return date
708
- end
709
- date -= When::P1D
710
- end
711
- raise ArgumentError, "#{self} not found"
712
- end
713
-
714
- private
715
-
716
- #
717
- # オブジェクトの正規化
718
- #
719
- def initialize(*args)
720
- super
721
- @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
722
- end
723
- end
724
-
725
- #
726
- # 曜日の名前の一覧
727
- #
728
- # @param [When::TM::TemporalPosition] date
729
- #
730
- # @return [Array<When::CalendarNote::Week::DayOfWeek>]
731
- #
732
- def week_labels(date)
733
- @days_of_week.child[0...week(date)[:position].last]
734
- end
735
-
736
- # 七曜
737
- #
738
- # @param [When::TM::TemporalPosition] date
739
- #
740
- # @return [When::Coordinates::Residue] 七曜
741
- #
742
- def standard_week(date)
743
- When.Residue('Week')[date.to_i % 7]
744
- end
745
-
746
- #
747
- # オブジェクトの正規化
748
- #
749
- def _normalize(args=[], options={})
750
- super
751
- @days_of_week = When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
752
- @days_of_week.child.length.times do |index|
753
- name = @days_of_week.child[index].label.to_s.downcase.gsub(/[- ]/, '_')
754
- self.class.module_eval %Q{
755
- def #{name}(date, parameter=nil)
756
- @days_of_week.child[#{index}].just_or_last(date)
757
- end
758
- } unless respond_to?(name)
759
- self.class.module_eval %Q{
760
- def #{name}_delta(parameter=nil)
761
- @days_of_week.child[#{index}].delta
762
- end
763
- } unless respond_to?(name + '_delta')
764
- end
765
- end
766
-
767
- #
768
- # イベントを取得する Enumerator
769
- #
770
- class Enumerator < When::CalendarNote::Enumerator
771
-
772
- #
773
- # 次のイベントを得る
774
- #
775
- # @return [When::TM::TemporalPosition]
776
- #
777
- def succ
778
- value = @current
779
- plus = @delta.sign > 0
780
- if @current==:first
781
- @first = event_eval(@first) unless plus
782
- @current = @first
783
- else
784
- if plus
785
- @current = event_eval(@current + @delta)
786
- else
787
- @last = event_eval(@current - When::P1D)
788
- @current = event_eval(@current + @delta)
789
- unless [@current.to_i, value.to_i].include?(@last.to_i)
790
- @current = @last
791
- return value
792
- end
793
- end
794
- @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
795
- end
796
- return value
797
- end
798
- end
799
- end
800
- end
801
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2014 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ module When
9
+
10
+ #
11
+ # 暦注 - Calendar Note
12
+ #
13
+ class CalendarNote < TM::ReferenceSystem
14
+
15
+ #
16
+ # 暦注要素への名前アクセス機能提供
17
+ #
18
+ # @private
19
+ module LabelAccess
20
+ attr_reader :_pool
21
+
22
+ #
23
+ # 暦注要素への名前(label)によるアクセス
24
+ #
25
+ # @param [Numeric] key 配列インデックスと見なしてアクセス
26
+ # @param [String] key 名前(label)と見なしてアクセス
27
+ #
28
+ # @return [Object] 暦注要素
29
+ #
30
+ def [](key)
31
+ return super if key.kind_of?(Numeric)
32
+ @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
33
+ @_pool[key]
34
+ end
35
+ end
36
+
37
+ #
38
+ # 暦注要素のひな形クラス
39
+ #
40
+ # @private
41
+ class NoteElement < When::BasicTypes::Object
42
+ #
43
+ # _m17n_form のための要素生成
44
+ #
45
+ # @param [Hash] options 下記のとおり
46
+ # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
47
+ #
48
+ def _to_hash_value(options={})
49
+ options[:method] == :to_m17n ? label : super
50
+ end
51
+ end
52
+
53
+ #
54
+ # 暦注検索結果を保存するコンテナ
55
+ #
56
+ module NotesContainer
57
+
58
+ class << self
59
+ #
60
+ # 永続データに暦注検索結果を登録する
61
+ #
62
+ # @private
63
+ def register(notes, persistence, sdn)
64
+ if persistence
65
+ persistence.store(sdn, notes)
66
+ persistence.extend(self)
67
+ end
68
+ notes.extend(self)
69
+ end
70
+
71
+ #
72
+ # 永続データから暦注検索結果を取り出す
73
+ #
74
+ # @private
75
+ def retrieve(persistence, sdn)
76
+ return false unless persistence
77
+ persistence.fetch(sdn, false)
78
+ end
79
+
80
+ #
81
+ # パターンの値の一致を判定する
82
+ #
83
+ # @private
84
+ def verify(pattern, value)
85
+ value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
86
+ pattern = When::EncodingConversion.to_internal_encoding(pattern)
87
+ pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
88
+ end
89
+ end
90
+
91
+ # 暦注検索結果コンテナの次元を下げる
92
+ #
93
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
94
+ #
95
+ # @return [NotesContainer]
96
+ #
97
+ def simplify(compact=true)
98
+ if kind_of?(Hash) && !key?(:note)
99
+ # Persistent NotesContainer
100
+ simplified = {}
101
+ each_pair do |key, value|
102
+ value = value.simplify(compact) if value.kind_of?(NotesContainer)
103
+ simplified[key] = value if value
104
+ end
105
+ else
106
+ # Non-Persistent NotesContainer
107
+ simplified = self
108
+ simplified = _compact(simplified) if compact
109
+ simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
110
+ end
111
+ _bless(simplified)
112
+ end
113
+
114
+ # 暦注検索結果コンテナのハッシュ要素
115
+ #
116
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
117
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
118
+ #
119
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
120
+ #
121
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
122
+ #
123
+ def value(compact=true, symbol=:value)
124
+ if kind_of?(Hash) && !key?(:note)
125
+ # Persistent NotesContainer
126
+ sliced = {}
127
+ each_pair do |key, val|
128
+ sliced[key] = val[symbol]
129
+ end
130
+ else
131
+ # Non-Persistent NotesContainer
132
+ return _bless(slice(symbol)) if symbol.kind_of?(Integer)
133
+ sliced = _dig(self) {|target| target.fetch(symbol, nil)}
134
+ end
135
+ sliced = _bless(sliced)
136
+ compact ? sliced.simplify : sliced
137
+ end
138
+
139
+ # 暦注検索結果コンテナのハッシュ要素
140
+ #
141
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
142
+ #
143
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
144
+ #
145
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
146
+ #
147
+ def [](symbol)
148
+ value(false, symbol)
149
+ end
150
+
151
+ # 暦注検索結果コンテナの条件に合う要素を抽出する
152
+ #
153
+ # @param [Hash{Symbol=>String or Regexp}] condition 条件
154
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
155
+ #
156
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
157
+ #
158
+ def subset(condition, compact=true)
159
+ if kind_of?(Hash) && !key?(:note)
160
+ # Persistent NotesContainer
161
+ result = {}
162
+ each_pair do |key, value|
163
+ value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
164
+ result[key] = value if value
165
+ end
166
+ else
167
+ # Non-Persistent NotesContainer
168
+ result = _dig(self) {|target|
169
+ (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
170
+ }
171
+ result = _compact(result) if compact
172
+ end
173
+ _bless(result)
174
+ end
175
+
176
+ private
177
+
178
+ #
179
+ # 対象オブジェクトを NotesContainer にする
180
+ #
181
+ def _bless(target)
182
+ target.extend(NotesContainer) if target && !equal?(target)
183
+ target
184
+ end
185
+
186
+ #
187
+ # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
188
+ #
189
+ def _dig(list, &block)
190
+ return yield(list) unless list.kind_of?(Array)
191
+ list.map {|element| _dig(element, &block)}
192
+ end
193
+
194
+ #
195
+ # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
196
+ #
197
+ def _compact(list)
198
+ return list unless list.kind_of?(Array)
199
+ result = list.map {|element| _compact(element)}.compact
200
+ return result unless result.empty?
201
+ nil
202
+ end
203
+ end
204
+
205
+ #
206
+ # 暦法によってイベントの動作を変えるか否か
207
+ #
208
+ CalendarDepend = false
209
+
210
+ # デフォルトイベント名
211
+ #
212
+ # @return [String]
213
+ #
214
+ # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
215
+ # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
216
+ #
217
+ attr_accessor :event
218
+ private :event=
219
+
220
+ # デフォルトイベントの指定
221
+ #
222
+ # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
223
+ #
224
+ # @return [When::CalendarNote]
225
+ #
226
+ def copy(event)
227
+ c = self.clone
228
+ c.send(:event=, event)
229
+ c
230
+ end
231
+
232
+ # 典型的なイベントの発生間隔
233
+ #
234
+ # @param [String] event
235
+ #
236
+ # @return [When::TM::PeriodDuration]
237
+ #
238
+ def duration(event=@event)
239
+ void, event, parameter = event.split(/\A([^\d]+)/)
240
+ send((event+'_delta').downcase.to_sym, parameter)
241
+ end
242
+
243
+ # 指定の日時が指定イベントに該当するか?
244
+ #
245
+ # @param [When::TM::TemporalPosition] date チェックされる日時
246
+ # @param [String] event チェックするイベント名
247
+ #
248
+ # @return [Boolean]
249
+ # [ true - 該当する ]
250
+ # [ false - 該当しない ]
251
+ #
252
+ def include?(date, event=@event)
253
+ enum_for(date, :forward, event.downcase).next.include?(date)
254
+ end
255
+
256
+ # Enumeratorの生成
257
+ #
258
+ # @overload enum_for(range, options={})
259
+ # @param [Range, When::Parts::GeometricComplex] range
260
+ # [ 始点 - range.first ]
261
+ # [ 終点 - range.last ]
262
+ # @param [Hash] options 以下の通り
263
+ # @option options [String] :event イベント名(デフォルトは@event)
264
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
265
+ #
266
+ # @overload enum_for(first, direction=:forward, options={})
267
+ # @param [When::TM::TemporalPosition] first 始点
268
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
269
+ # [:forward] 昇順(デフォルト)
270
+ # [:reverse] 降順
271
+ # @param [Hash] options 以下の通り
272
+ # @option options [String] :event イベント名(デフォルトは@event)
273
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
274
+ #
275
+ # @return [Enumerator]
276
+ #
277
+ def enum_for(*args)
278
+ params = args.dup
279
+ options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
280
+ options[:event] ||= @event
281
+ self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
282
+ [self, params[0], options] :
283
+ [self, params[0], params[1] || :forward, options]))
284
+ end
285
+ alias :to_enum :enum_for
286
+
287
+ # 暦注の計算
288
+ #
289
+ # @param [When::TM::TemporalPosition] date 暦注を計算する日時
290
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
291
+ # @param [String] options { :notes => String } という Hash の指定と等価
292
+ # @param [Integer] options { :indices => Integer} という Hash の指定と等価
293
+ # @param [Hash] options 下記のとおり
294
+ # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
295
+ # [ When::DAY ( 0) - 日 ]
296
+ # [ When::MONTH(-1) - 月 ]
297
+ # [ When::YEAR (-2) - 年 ]
298
+ #
299
+ # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
300
+ # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
301
+ # @option options [String] :notes 計算する暦注名(日の暦注)
302
+ # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
303
+ # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
304
+ # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
305
+ # @option options [:all] :notes すべての暦注を計算
306
+ # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
307
+ # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
308
+ # @option options [Hash] :conditions 暦注計算の条件
309
+ # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
310
+ # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
311
+ #
312
+ # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
313
+ # see {When::TM::TemporalPosition._instance}
314
+ #
315
+ # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
316
+ # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
317
+ #
318
+ # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
319
+ # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
320
+ # ビットアドレスの値が 1 の暦注が計算対象となる。
321
+ #
322
+ # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
323
+ # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
324
+ # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
325
+ #
326
+ # @note
327
+ # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
328
+ # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
329
+ # @note
330
+ # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
331
+ #
332
+ def notes(date, options={})
333
+ dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
334
+ retrieved = NotesContainer.retrieve(persistence, date.to_i)
335
+ return retrieved unless retrieved == false
336
+ NotesContainer.register(indices.map {|i|
337
+ next [] unless i <= date.precision
338
+ _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
339
+ focused_notes.each do |note|
340
+ notes_hash[note] ||= _note_element(note, i, conditions, dates)
341
+ end
342
+ notes_hash
343
+ end
344
+ }, persistence, date.to_i)
345
+ end
346
+
347
+ #
348
+ # 暦注の一致 or 不一致
349
+ #
350
+ # @param [When::TM::TemporalPosition] date 暦注を確認する日時
351
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
352
+ # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
353
+ # (指定の notes が存在する場合は前者、しない場合は後者)
354
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
355
+ # @param [Hash] options 下記のとおり
356
+ # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
357
+ # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
358
+ #
359
+ # @return [Boolean]
360
+ # [ true - 暦注が一致 ]
361
+ # [ false - 暦注が不一致 ]
362
+ #
363
+ def note?(date, options={})
364
+ options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
365
+ pattern = options.delete(:value) if options.kind_of?(Hash)
366
+ result = notes(date, options)
367
+ result.flatten!
368
+ result.delete_if {|hash| hash.empty?}
369
+ return false unless result.size > 0
370
+ return true unless pattern
371
+ result.each do |hash|
372
+ return true if NotesContainer.verify(pattern, hash[:value])
373
+ end
374
+ return false
375
+ end
376
+
377
+ # 年の名前を暦注として返す
378
+ # @method year
379
+ # @return [When::BasicTypes::M17n]
380
+
381
+ # 月の名前を暦注として返す
382
+ # @method month
383
+ # @return [When::BasicTypes::M17n]
384
+
385
+ # 日の名前を暦注として返す
386
+ # @method day
387
+ # @return [When::BasicTypes::M17n]
388
+
389
+ #
390
+ # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
391
+ #
392
+ # @private
393
+ ['year', 'month', 'day'].each do |c|
394
+ module_eval %Q{
395
+ def #{c}(date)
396
+ date.name('#{c}')
397
+ end
398
+ }
399
+ end
400
+
401
+ private
402
+
403
+ #
404
+ # 年月日暦注計算の共通処理 - コールバック元
405
+ #
406
+ def _note_values(dates, focused_notes, all_notes, note_objects)
407
+ return [] unless dates && all_notes
408
+
409
+ # prepare focused notes
410
+ case focused_notes
411
+ when Integer
412
+ bits = ~focused_notes << 1
413
+ focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
414
+ when []
415
+ focused_notes = all_notes
416
+ when nil
417
+ focused_notes = []
418
+ end
419
+ focused_notes = focused_notes.dup
420
+ not_focused_notes = all_notes - focused_notes
421
+ notes = {}
422
+ not_focused_notes.each do |note|
423
+ notes[note] = true
424
+ end
425
+ focused_notes.each do |note|
426
+ notes[note] = nil
427
+ end
428
+
429
+ # update notes
430
+ focused_notes_actual = focused_notes.dup
431
+ notes = yield(dates, focused_notes_actual, notes)
432
+ notes.keys.each do |note|
433
+ notes.delete(note) unless focused_notes_actual.include?(note)
434
+ end
435
+
436
+ # return Array of Hash
437
+ focused_notes.map {|note|
438
+ case notes[note]
439
+ when nil, false ; {}
440
+ when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
441
+ else
442
+ if note_objects[note].respond_to?(:to_note_hash)
443
+ note_objects[note].to_note_hash(notes[note], dates)
444
+ else
445
+ {:note=>note_objects[note].label, :value=>notes[note]}
446
+ end
447
+ end
448
+ }
449
+ end
450
+
451
+ #
452
+ # オブジェクトの正規化
453
+ #
454
+ def _normalize(args=[], options={})
455
+ @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
456
+ When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
457
+ _to_iri(args, options[:prefix] || '_co:')
458
+ if @_elements.kind_of?(Array)
459
+ @_elements.each do |e|
460
+ e.extend LabelAccess
461
+ end
462
+ end
463
+ end
464
+
465
+ #
466
+ # 再帰的に配列の中を Resource化する
467
+ #
468
+ def _to_iri(args, prefix)
469
+ args.map {|arg|
470
+ case arg
471
+ when String
472
+ arg, method = $1, $2 if (arg =~ /\A(.+)#([_A-Z0-9]+)\z/i)
473
+ obj = When.Resource(arg, prefix)
474
+ obj = obj.copy(method) if method
475
+ obj
476
+ when Array
477
+ _to_iri(arg, prefix)
478
+ else
479
+ arg
480
+ end
481
+ }
482
+ end
483
+
484
+ #
485
+ # 暦日を当該暦注計算用クラスに変換
486
+ #
487
+ # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
488
+ #
489
+ def _to_date_for_note(date)
490
+ date
491
+ end
492
+
493
+ # 暦注要素
494
+ #
495
+ # @return [Array<Array<When::Parts::Resource>>]
496
+ #
497
+ def _elements
498
+ @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
499
+ @_elements
500
+ end
501
+
502
+ #
503
+ # [[暦注名]](全暦注)
504
+ #
505
+ # @return [Array<Array<String, When::BasicTypes::M17n>>]
506
+ #
507
+ def _all_keys
508
+ @_all_keys ||= _elements.map { |c|
509
+ c.map {|n|
510
+ n.label.to_s
511
+ }
512
+ }
513
+ end
514
+
515
+ #
516
+ # [[暦注名]](主要暦注)
517
+ #
518
+ # @return [Array<Array<When::Parts::Resource>>]
519
+ #
520
+ def _prime_keys
521
+ @prime ||= _all_keys
522
+ end
523
+
524
+ #
525
+ # notes メソッドの引数を parse する
526
+ #
527
+ # @return [Array] dates, indices, notes
528
+ #
529
+ def _parse_note(date, options)
530
+ options =
531
+ case options
532
+ when Hash ; When::EncodingConversion.to_internal_encoding(options)
533
+ when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
534
+ when Integer ; {:indices => options}
535
+ else ; {}
536
+ end
537
+ conditions = options.delete(:conditions) || {}
538
+ _opt = options.dup
539
+ persistence = _opt.delete(:persistence)
540
+ notes = _notes(_opt.delete(:notes) || :prime)
541
+ indices = _indices(_opt.delete(:indices), notes)
542
+ _opt.keys.each do |key|
543
+ conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
544
+ end
545
+ [_to_date_for_note(date), indices, notes, persistence, conditions, options]
546
+ end
547
+
548
+ #
549
+ # 暦注を計算する暦座標の配列
550
+ #
551
+ # @return [Array<Integer>]
552
+ #
553
+ def _indices(indices, notes)
554
+ case indices
555
+ when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
556
+ when Range ; indices.to_a
557
+ when Array ; indices
558
+ else ; [indices.to_i]
559
+ end
560
+ end
561
+
562
+ #
563
+ # notes メソッドの 文字列引数の意味を解釈する
564
+ #
565
+ # @return [Hash] options for note String
566
+ #
567
+ def _find_note(note)
568
+ note = When::EncodingConversion.to_internal_encoding(note)
569
+ _elements.each_index do |index|
570
+ return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
571
+ end
572
+ {:value => note}
573
+ end
574
+
575
+ #
576
+ # 計算する暦注
577
+ #
578
+ # @return [Array<Array<String>, Integer>]
579
+ #
580
+ def _notes(notes)
581
+ case notes
582
+ when :all ; _all_keys
583
+ when :prime ; _prime_keys
584
+ when Integer ; [notes]
585
+ when String ; [[notes]]
586
+ else ; notes
587
+ end
588
+ end
589
+
590
+ #
591
+ # 暦注の計算
592
+ #
593
+ # @return [Object] 暦注の値
594
+ #
595
+ def _note_element(note, index, conditions, dates)
596
+ void, event, *parameter = note.split(/\A([^\d]+)/)
597
+ method = event.downcase
598
+ parameter << conditions unless conditions.empty?
599
+ return send(method, dates, *parameter) if respond_to?(method)
600
+ root = _elements[index-1][note]
601
+ parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
602
+ parent = When.Resource(parent) if leaf
603
+ return parent.send(method, dates, *parameter) if parent.respond_to?(method)
604
+ root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
605
+ end
606
+
607
+ #
608
+ # イベントを取得する Enumerator
609
+ #
610
+ class Enumerator < When::Parts::Enumerator
611
+
612
+ #
613
+ # 次のイベントを得る
614
+ #
615
+ # @return [When::TM::TemporalPosition]
616
+ #
617
+ def _succ
618
+ @current = (@current==:first) ? @first : event_eval(@current + @delta)
619
+ end
620
+
621
+ # オブジェクトの生成
622
+ #
623
+ # @overload initialize(parent, range, options)
624
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
625
+ # @param [Range, When::Parts::GeometricComplex] range
626
+ # [ 始点 - range.first ]
627
+ # [ 終点 - range.last ]
628
+ # @param [Hash] options 以下の通り
629
+ # @option options [String] :event イベント名
630
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
631
+ #
632
+ # @overload initialize(parent, first, direction, options)
633
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
634
+ # @param [When::TM::TemporalPosition] first 始点
635
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
636
+ # [:forward] 昇順(デフォルト)
637
+ # [:reverse] 降順
638
+ # @param [Hash] options 以下の通り
639
+ # @option options [String] :event イベント名
640
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
641
+ #
642
+ def initialize(*args)
643
+ if args[1].kind_of?(Range)
644
+ parent, range, options = args
645
+ first = range.first
646
+ last = range.last
647
+ direction = first < last ? :forward : :reverse
648
+ else
649
+ parent, first, direction, options = args
650
+ end
651
+ direction = options[:direction] if options[:direction]
652
+ @parent = parent
653
+ event = options.delete(:event)
654
+ case event
655
+ when String ; void, @event, @parameter = event.split(/\A([^\d]+)/)
656
+ else ; @event, @parameter = [parent.event, event]
657
+ end
658
+ @delta = @parent.send((@event+'_delta').to_sym, @parameter)
659
+ instance_eval %Q{
660
+ def event_eval(date)
661
+ @parent.#{@event}(date, @parameter)
662
+ end
663
+ }
664
+ date = event_eval(first)
665
+ if direction == :reverse
666
+ @delta = -@delta
667
+ date = event_eval(first + @delta) if first.to_i < date.to_i
668
+ else
669
+ date = event_eval(first + @delta) if first.to_i > date.to_i
670
+ end
671
+ range ?
672
+ super(@parent, range.exclude_end? ? date...last : date..last, options) :
673
+ super(@parent, date, direction, options)
674
+ end
675
+ end
676
+
677
+ #
678
+ # 暦週
679
+ #
680
+ class Week < CalendarNote
681
+
682
+ #
683
+ # 暦週要素
684
+ #
685
+ class DayOfWeek < When::BasicTypes::M17n
686
+
687
+ # 当該暦週要素の標準的な出現間隔
688
+ #
689
+ # @return [Integer]
690
+ #
691
+ attr_reader :delta
692
+
693
+ # 所属する暦週オブジェクト
694
+ #
695
+ # @return [When::CalendarNote]
696
+ #
697
+ def week_note
698
+ @week_note ||= When.CalendarNote(iri.split('/Notes').first)
699
+ end
700
+
701
+ # 当日または直前に当該暦週要素が現れる日付
702
+ #
703
+ # @return [When::TM::TemporalPosition]
704
+ #
705
+ def just_or_last(date)
706
+ date = week_note._to_date_for_note(date)
707
+ ([parent.child.length, @delta[When::DAY]].max*2).times do
708
+ if equal?(week_note.week(date)[:value])
709
+ date.events ||= []
710
+ date.events << self
711
+ return date
712
+ end
713
+ date -= When::P1D
714
+ end
715
+ raise ArgumentError, "#{self} not found"
716
+ end
717
+
718
+ private
719
+
720
+ #
721
+ # オブジェクトの正規化
722
+ #
723
+ def initialize(*args)
724
+ super
725
+ @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
726
+ end
727
+ end
728
+
729
+ #
730
+ # 曜日の名前の一覧
731
+ #
732
+ # @param [When::TM::TemporalPosition] date
733
+ #
734
+ # @return [Array<When::CalendarNote::Week::DayOfWeek>]
735
+ #
736
+ def week_labels(date)
737
+ @days_of_week.child[0...week(date)[:position].last]
738
+ end
739
+
740
+ # 七曜
741
+ #
742
+ # @param [When::TM::TemporalPosition] date
743
+ #
744
+ # @return [When::Coordinates::Residue] 七曜
745
+ #
746
+ def common_week(date)
747
+ When.Residue('Week')[date.to_i % 7]
748
+ end
749
+
750
+ #
751
+ # オブジェクトの正規化
752
+ #
753
+ def _normalize(args=[], options={})
754
+ super
755
+ @days_of_week ||= When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
756
+ @days_of_week.child.length.times do |index|
757
+ name = @days_of_week.child[index].translate('en').downcase.gsub(/[- ]/, '_')
758
+ self.class.module_eval %Q{
759
+ def #{name}(date, parameter=nil)
760
+ @days_of_week.child[#{index}].just_or_last(date)
761
+ end
762
+ } unless respond_to?(name)
763
+ self.class.module_eval %Q{
764
+ def #{name}_delta(parameter=nil)
765
+ @days_of_week.child[#{index}].delta
766
+ end
767
+ } unless respond_to?(name + '_delta')
768
+ end
769
+ end
770
+
771
+ #
772
+ # イベントを取得する Enumerator
773
+ #
774
+ class Enumerator < When::CalendarNote::Enumerator
775
+
776
+ #
777
+ # 次のイベントを得る
778
+ #
779
+ # @return [When::TM::TemporalPosition]
780
+ #
781
+ def succ
782
+ value = @current
783
+ plus = @delta.sign > 0
784
+ if @current==:first
785
+ @first = event_eval(@first) unless plus
786
+ @current = @first
787
+ else
788
+ if plus
789
+ @current = event_eval(@current + @delta)
790
+ else
791
+ @last = event_eval(@current - When::P1D)
792
+ @current = event_eval(@current + @delta)
793
+ unless [@current.to_i, value.to_i].include?(@last.to_i)
794
+ @current = @last
795
+ return value
796
+ end
797
+ end
798
+ @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
799
+ end
800
+ return value
801
+ end
802
+ end
803
+ end
804
+ end
805
+ end