Module: JapaneseAddressParser::CacheRegexes

Defined in:
lib/japanese_address_parser/cache_regexes.rb,
sig/cache_regexes.rbs

Overview

データ取得・キャッシュ・正規表現パターン生成(cacheRegexes.ts)。 JS の ^/$(m フラグ無し)は文字列先頭/末尾なので、生成パターンでは \A/\z に翻訳する (working_agreement §3-6)。スレッド安全性は考慮しない(§1-6)。

Defined Under Namespace

Classes: TownAlias

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.cacheObject

JS: const cache = new LRUCache({ max: currentConfig.cacheSize }) 町字 regex パターンのみ LRU でキャッシュする(working_agreement §1-8)。



39
40
41
# File 'lib/japanese_address_parser/cache_regexes.rb', line 39

def cache
  @cache ||= ::LruRedux::Cache.new(JapaneseAddressParser.config.cache_size)
end

.cache_prefectures(data) ⇒ Object

JS: cachePrefectures(data)



77
78
79
# File 'lib/japanese_address_parser/cache_regexes.rb', line 77

def cache_prefectures(data)
  @cached_prefectures = data
end

.fetch_from_cache(key) ⇒ Object

JS: fetchFromCache(key, fetcher)



44
45
46
47
48
49
50
51
# File 'lib/japanese_address_parser/cache_regexes.rb', line 44

def fetch_from_cache(key)
  cached = cache[key]
  return cached unless cached.nil?

  data = yield
  cache[key] = data
  data
end

.get_chiban(pref, city, town, api_version) ⇒ Object

JS: getChiban(pref, city, town, apiVersion)



234
235
236
237
238
239
240
241
242
243
# File 'lib/japanese_address_parser/cache_regexes.rb', line 234

def get_chiban(pref, city, town, api_version)
  row = town.csv_ranges&.dig('地番')
  return [] if row.nil?

  fetch_from_cache("地番-#{pref.code}-#{city.code}-#{town.machi_aza_name}") do
    chibans = parse_subresource(fetch_subresource('地番', pref, city, row, api_version))
    # JS: [prc_num1, prc_num2, prc_num3].filter(Boolean).join('-') の長さ降順(chiban_to_string と同義)。
    stable_sort_by(chibans) { |chiban| -chiban.chiban_to_string.length }
  end
end

.get_city_regex_patterns(pref) ⇒ Object

JS: getCityRegexPatterns(pref)



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/japanese_address_parser/cache_regexes.rb', line 96

def get_city_regex_patterns(pref)
  cached = cached_city_patterns[pref.code]
  return cached unless cached.nil?

  # JS: cities.sort((a, b) => cityName(a).length - cityName(b).length)
  # 少ない文字数の地名に対してミスマッチしないよう文字数の昇順に安定ソートする。
  cities = stable_sort_by(pref.cities) { |city| city.city_name.length }

  patterns =
    cities.map do |city|
      name = city.city_name
      pattern = "\\A#{Dict.to_regex_pattern(name)}"
      if name.match?(/(町|村)\z/) # JS: name.match(/(町|村)$/)
        # JS: `^${toRegexPattern(name).replace(/(.+?)郡/, '($1郡)?')}` — 郡が省略されてるかも
        pattern = "\\A#{Dict.to_regex_pattern(name).sub(/(.+?)郡/, '(\1郡)?')}"
      end
      [city, pattern]
    end

  cached_city_patterns[pref.code] = patterns
end

.get_prefecture_regex_patterns(api) ⇒ Object

JS: getPrefectureRegexPatterns(api)



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/japanese_address_parser/cache_regexes.rb', line 82

def get_prefecture_regex_patterns(api)
  cached = @cached_prefecture_patterns
  return cached if cached

  @cached_prefecture_patterns =
    api.data.map do |pref|
      # JS: pref.pref.replace(/(都|道|府|県)$/, '') — 末尾の都府県が抜けた住所に対応($ → \z)
      pref_name = pref.pref.sub(/(都|道|府|県)\z/, '')
      # JS: `^${_pref}(都|道|府|県)?`(^ → \A)
      [pref, "\\A#{pref_name}(都|道|府|県)?"]
    end
end

.get_prefecturesObject

JS: getPrefectures()



68
69
70
71
72
73
74
# File 'lib/japanese_address_parser/cache_regexes.rb', line 68

def get_prefectures
  cached = @cached_prefectures
  return cached unless cached.nil?

  response = Fetcher.fetch('.json') # ja.json
  cache_prefectures(Data::PrefectureApi.from_json(response.json))
end

.get_rsdt(pref, city, town, api_version) ⇒ Object

JS: getRsdt(pref, city, town, apiVersion)



222
223
224
225
226
227
228
229
230
231
# File 'lib/japanese_address_parser/cache_regexes.rb', line 222

def get_rsdt(pref, city, town, api_version)
  row = town.csv_ranges&.dig('住居表示')
  return [] if row.nil?

  fetch_from_cache("住居表示-#{pref.code}-#{city.code}-#{town.machi_aza_name}") do
    rsdts = parse_subresource(fetch_subresource('住居表示', pref, city, row, api_version))
    # JS: [blk_num, rsdt_num, rsdt_num2].filter(Boolean).join('-') の長さ降順(rsdt_to_string と同義)。
    stable_sort_by(rsdts) { |rsdt| -rsdt.rsdt_to_string.length }
  end
end

.get_same_named_prefecture_city_regex_patterns(pref_api) ⇒ Object

JS: getSameNamedPrefectureCityRegexPatterns(prefApi)



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/japanese_address_parser/cache_regexes.rb', line 196

def get_same_named_prefecture_city_regex_patterns(pref_api)
  cached = @cached_same_named_prefecture_city_regex_patterns
  return cached unless cached.nil?

  pref_list = pref_api.data
  # JS: pref.pref.replace(/[都|道|府|県]$/, '') — 文字クラス(| も含む)は上流のまま($ → \z)
  prefs = pref_list.map { |pref| pref.pref.sub(/[都|道|府|県]\z/, '') }

  # @type var patterns: ::Array[[::String, ::String]]
  patterns = []
  pref_list.each do |pref|
    pref.cities.each do |city|
      city_n = city.city_name

      # 市の名前が別の都道府県名から始まっているケース(例: 福島県石川郡石川町)を考慮する。
      prefs.each do |pref_name|
        # JS: cityN.indexOf(_prefs[j]) === 0
        patterns << ["#{pref.pref}#{city_n}", "\\A#{city_n}"] if city_n.start_with?(pref_name)
      end
    end
  end

  @cached_same_named_prefecture_city_regex_patterns = patterns
end

.get_town_regex_patterns(pref, city, api_version) ⇒ Object

JS: getTownRegexPatterns(pref, city, apiVersion)



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/japanese_address_parser/cache_regexes.rb', line 136

def get_town_regex_patterns(pref, city, api_version)
  fetch_from_cache("#{pref.code}-#{city.code}") do
    api = get_towns(pref, city, api_version)
    pre_towns = api.data
    town_set = ::Set.new(pre_towns.map(&:machi_aza_name))
    # @type var towns: ::Array[Data::SingleMachiAza | TownAlias]
    towns = []

    is_kyoto = city.city == '京都市'

    # 「○○町」が含まれるケースへの対応。通常は「町」の省略を同義語として許容するが、
    # 「○○町」と「○○」が共存するケース・京都・漢数字を含むケースは省略を許容しない。
    pre_towns.each do |town|
      towns << town

      original_town = town.machi_aza_name
      next if original_town.index('').nil? # JS: indexOf('町') === -1

      # JS: originalTown.replace(/(?!^町)町/g, '') — 冒頭の「町」は除外(^ → \A)
      town_abbr = original_town.gsub(/(?!\A町)町/, '')
      next if is_kyoto
      next if town_set.include?(town_abbr)
      next if town_set.include?("大字#{town_abbr}")
      next if kanji_number_followed_by_cho?(original_town)

      # エイリアスとして町なしのパターンを登録
      towns << TownAlias.new(machiaza_id: town.machiaza_id, point: town.point, oaza_cho: town_abbr, original_town: town)
    end

    # JS: towns.sort((a, b) => bLen - aLen)(大字始まりは優先度を下げる)— 文字数の降順に安定ソート
    towns = stable_sort_by(towns) { |town| -town_sort_length(town) }

    # 後段の丁目ループは TownAlias を元 VO に剥がさずそのまま追加する(JS と同挙動)。
    # @type var patterns: ::Array[[Data::SingleMachiAza | TownAlias, ::String]]
    patterns =
      towns.map do |town|
        pattern = Dict.to_regex_pattern(town_pattern_source(town.machi_aza_name))
        # JS: 'originalTown' in town ? town.originalTown : town
        associated = town.is_a?(TownAlias) ? town.original_town : town
        [associated, pattern]
      end

    # X丁目 の丁目なしの数字だけ許容するため、最後に数字だけ追加する。
    towns.each do |town|
      # JS: machiAzaName(town).match(/([^一二三四五六七八九十]+)([一二三四五六七八九十]+)(丁目?)/)
      chome_match = town.machi_aza_name.match(/([^一二三四五六七八九十]+)([一二三四五六七八九十]+)(丁目?)/)
      next unless chome_match

      chome_name_part = chome_match[1]
      chome_num = chome_match[2]
      # JS: toRegexPattern(`^${chomeNamePart}(${chomeNum}|${kan2num(chomeNum)})`)(^ → \A)
      pattern = Dict.to_regex_pattern("\\A#{chome_name_part}(#{chome_num}|#{Kan2num.call(chome_num)})")
      patterns << [town, pattern]
    end

    patterns
  end
end

.get_towns(pref_obj, city_obj, api_version) ⇒ Object

JS: getTowns(prefObj, cityObj, apiVersion)



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/japanese_address_parser/cache_regexes.rb', line 119

def get_towns(pref_obj, city_obj, api_version)
  pref = pref_obj.prefecture_name
  city = city_obj.city_name

  cache_key = "#{pref}-#{city}"
  cached = cached_towns[cache_key]
  return cached unless cached.nil?

  # JS: ['', encodeURI(pref), encodeURI(city) + `.json?v=${apiVersion}`].join('/')
  # 非 ASCII のパーセントエンコードは Fetcher#http_request 側で一括して行う(二重エンコード回避)
  # ため、ここでは encodeURI せず生の県名・市名を渡す。
  input = ['', pref, "#{city}.json?v=#{api_version}"].join('/')
  response = Fetcher.fetch(input)
  cached_towns[cache_key] = Data::MachiAzaApi.from_json(response.json)
end

.reset!Object

モジュールレベルのキャッシュをすべて破棄する。 JS は test ファイルごとに別プロセスでモジュール状態が新鮮になる(プロセス分離)が、 Ruby/RSpec は 1 プロセス共有のため、japanese_addresses_api を切り替える際の明示的な リセット手段を提供する(runtime でデータ源を変えるユーザにも有用。working_agreement §1-6)。



57
58
59
60
61
62
63
64
65
# File 'lib/japanese_address_parser/cache_regexes.rb', line 57

def reset!
  @cache = nil
  @cached_prefectures = nil
  @cached_prefecture_patterns = nil
  @cached_same_named_prefecture_city_regex_patterns = nil
  @cached_city_patterns = nil
  @cached_towns = nil
  nil
end

Instance Method Details

#self?.cacheObject

Returns:

  • (Object)


23
# File 'sig/cache_regexes.rbs', line 23

def self?.cache: () -> untyped

#self?.cache_prefecturesData::PrefectureApi

Parameters:

Returns:



27
# File 'sig/cache_regexes.rbs', line 27

def self?.cache_prefectures: (Data::PrefectureApi data) -> Data::PrefectureApi

#self?.cached_city_patternsHash[Integer, Array[[Data::SingleCity, String]]]

Returns:



39
# File 'sig/cache_regexes.rbs', line 39

def self?.cached_city_patterns: () -> Hash[Integer, Array[[Data::SingleCity, String]]]

#self?.cached_townsHash[String, Data::MachiAzaApi]

Returns:



40
# File 'sig/cache_regexes.rbs', line 40

def self?.cached_towns: () -> Hash[String, Data::MachiAzaApi]

#self?.expand_town_numberString

Parameters:

  • match (String)

Returns:

  • (String)


45
# File 'sig/cache_regexes.rbs', line 45

def self?.expand_town_number: (String match) -> String

#self?.fetch_from_cache { ... } ⇒ Object

Parameters:

  • key (String)

Yields:

Yield Returns:

  • (Object)

Returns:

  • (Object)


24
# File 'sig/cache_regexes.rbs', line 24

def self?.fetch_from_cache: (String key) { () -> untyped } -> untyped

#self?.fetch_subresourceString

Parameters:

Returns:

  • (String)


35
# File 'sig/cache_regexes.rbs', line 35

def self?.fetch_subresource: (String kind, Data::SinglePrefecture pref, Data::SingleCity city, Hash[String, Integer] row, Integer api_version) -> String

#self?.get_chibanArray[Data::SingleChiban]

Parameters:

Returns:



34
# File 'sig/cache_regexes.rbs', line 34

def self?.get_chiban: (Data::SinglePrefecture pref, Data::SingleCity city, Data::SingleMachiAza town, Integer api_version) -> Array[Data::SingleChiban]

#self?.get_city_regex_patternsArray[[Data::SingleCity, String]]

Parameters:

Returns:



29
# File 'sig/cache_regexes.rbs', line 29

def self?.get_city_regex_patterns: (Data::SinglePrefecture pref) -> Array[[Data::SingleCity, String]]

#self?.get_prefecture_regex_patternsArray[[Data::SinglePrefecture, String]]

Parameters:

Returns:



28
# File 'sig/cache_regexes.rbs', line 28

def self?.get_prefecture_regex_patterns: (Data::PrefectureApi api) -> Array[[Data::SinglePrefecture, String]]

#self?.get_prefecturesData::PrefectureApi

Returns:



26
# File 'sig/cache_regexes.rbs', line 26

def self?.get_prefectures: () -> Data::PrefectureApi

#self?.get_rsdtArray[Data::SingleRsdt]

Parameters:

Returns:



33
# File 'sig/cache_regexes.rbs', line 33

def self?.get_rsdt: (Data::SinglePrefecture pref, Data::SingleCity city, Data::SingleMachiAza town, Integer api_version) -> Array[Data::SingleRsdt]

#self?.get_same_named_prefecture_city_regex_patternsArray[[String, String]]

Parameters:

Returns:

  • (Array[[String, String]])


32
# File 'sig/cache_regexes.rbs', line 32

def self?.get_same_named_prefecture_city_regex_patterns: (Data::PrefectureApi pref_api) -> Array[[String, String]]

#self?.get_town_regex_patternsArray[[untyped, String]]

Parameters:

Returns:

  • (Array[[untyped, String]])


31
# File 'sig/cache_regexes.rbs', line 31

def self?.get_town_regex_patterns: (Data::SinglePrefecture pref, Data::SingleCity city, Integer api_version) -> Array[[untyped, String]]

#self?.get_townsData::MachiAzaApi

Parameters:

Returns:



30
# File 'sig/cache_regexes.rbs', line 30

def self?.get_towns: (Data::SinglePrefecture pref_obj, Data::SingleCity city_obj, Integer api_version) -> Data::MachiAzaApi

#self?.kanji_number_followed_by_cho?Boolean

Parameters:

  • target_town_name (String)

Returns:

  • (Boolean)


43
# File 'sig/cache_regexes.rbs', line 43

def self?.kanji_number_followed_by_cho?: (String target_town_name) -> bool

#self?.parse_subresourceArray[untyped]

Parameters:

  • data (String)

Returns:

  • (Array[untyped])


36
# File 'sig/cache_regexes.rbs', line 36

def self?.parse_subresource: (String data) -> Array[untyped]

#self?.point_from[Float, Float]?

Parameters:

  • lng (Object)
  • lat (Object)

Returns:

  • ([Float, Float], nil)


37
# File 'sig/cache_regexes.rbs', line 37

def self?.point_from: (untyped lng, untyped lat) -> [Float, Float]?

#self?.reset!nil

Returns:

  • (nil)


25
# File 'sig/cache_regexes.rbs', line 25

def self?.reset!: () -> nil

#self?.stable_sort_by {|arg0| ... } ⇒ Object

Parameters:

  • list (Object)

Yields:

Yield Parameters:

  • arg0 (Object)

Yield Returns:

  • (Object)

Returns:

  • (Object)


41
# File 'sig/cache_regexes.rbs', line 41

def self?.stable_sort_by: (untyped list) { (untyped) -> untyped } -> untyped

#self?.town_pattern_sourceString

Parameters:

  • name (String)

Returns:

  • (String)


44
# File 'sig/cache_regexes.rbs', line 44

def self?.town_pattern_source: (String name) -> String

#self?.town_sort_lengthInteger

Parameters:

  • town (Object)

Returns:

  • (Integer)


42
# File 'sig/cache_regexes.rbs', line 42

def self?.town_sort_length: (untyped town) -> Integer