Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ JIS X 0401 で定義されている都道府県コードをベースに、

また、Rails のプラグインとして使用することもできます。


## 使い方

### ライブラリの読み込み
Expand Down Expand Up @@ -69,6 +68,8 @@ JpPrefecture::Prefecture.find(code: 13)

### 都道府県を検索

`find` は複数の都道府県に一致する場合、都道府県コード順で最初の 1 件を返します。

前方一致で都道府県を検索します:

```ruby
Expand All @@ -91,12 +92,24 @@ JpPrefecture::Prefecture.find(name_h: "とうきょうと")
JpPrefecture::Prefecture.find(name_k: "トウキョウト")
```

マッピングのすべての項目を検索します (推奨しません):
マッピングのすべての項目を検索します (非推奨):

```ruby
JpPrefecture::Prefecture.find(all_fields: "東京")
```

一致したすべての都道府県が必要な場合は `where` を使用します:

```ruby
JpPrefecture::Prefecture.where(name: "山")
# => [山形県, 山梨県, 山口県]

JpPrefecture::Prefecture.where(name_e: "o")
# => [大阪府, 岡山県, 大分県, 沖縄県]
```

`where` で検索できる項目は `name` / `name_e` / `name_r` / `name_h` / `name_k` です。対応していない項目を指定した場合は `ArgumentError` が発生します。

### 都道府県の一覧を取得

```ruby
Expand Down Expand Up @@ -192,7 +205,6 @@ end

データのフォーマットについては [zip.yml](https://github.com/chocoby/jp_prefecture/blob/main/data/zip.yml) を参考にしてください。


## インストール

以下の行を `Gemfile` に記述してから:
Expand All @@ -209,15 +221,14 @@ gem 'jp_prefecture'
$ gem install jp_prefecture
```


## ドキュメント

[https://rubydoc.info/gems/jp_prefecture](https://rubydoc.info/gems/jp_prefecture)

## サポートしているバージョン

* Ruby: 2.4 - 4.0
* Rails: 5.0 - 8.1
- Ruby: 2.4 - 4.0
- Rails: 5.0 - 8.1

これより古い Ruby/Rails バージョンを使用する場合は、[`v0.11.0`](https://github.com/chocoby/jp_prefecture/tree/0.x) を利用してください。

Expand Down
20 changes: 16 additions & 4 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ JpPrefecture::Prefecture.find(code: 13)

### Search by Prefecture Name

`find` returns the first match in prefecture code order when multiple prefectures match.

Search for a prefecture by forward match.

```ruby
Expand Down Expand Up @@ -94,6 +96,18 @@ Search all items in the mapping (not recommended).
JpPrefecture::Prefecture.find(all_fields: "東京")
```

Use `where` when you need all matching prefectures.

```ruby
JpPrefecture::Prefecture.where(name: "山")
# => [山形県, 山梨県, 山口県]

JpPrefecture::Prefecture.where(name_e: "o")
# => [大阪府, 岡山県, 大分県, 沖縄県]
```

`where` supports `name` / `name_e` / `name_r` / `name_h` / `name_k`. Specifying any other item raises `ArgumentError`.

### All Prefectures

```ruby
Expand Down Expand Up @@ -191,7 +205,6 @@ end

Check out [zip.yml](https://github.com/chocoby/jp_prefecture/blob/main/data/zip.yml) for data format.


## Installation

Add this line in Gemfile.
Expand All @@ -212,15 +225,14 @@ Or install gem with `gem install`
$ gem install jp_prefecture
```


## Documentation

[https://rubydoc.info/gems/jp_prefecture](https://rubydoc.info/gems/jp_prefecture)

## Supported versions

* Ruby: 2.4 - 4.0
* Rails: 5.0 - 8.1
- Ruby: 2.4 - 4.0
- Rails: 5.0 - 8.1

If you are using an older Ruby/Rails version, please use [`v0.11.0`](https://github.com/chocoby/jp_prefecture/tree/0.x).

Expand Down
33 changes: 33 additions & 0 deletions lib/jp_prefecture/prefecture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def self.all
#
# 文字列は前方一致で検索する
#
# 複数の都道府県に一致する場合は、都道府県コード順で最初の 1 件を返す。
# 一致したすべての都道府県が必要な場合は {.where} を使用する
#
# @example
# # 都道府県コードを検索
# JpPrefecture::Prefecture.find(1)
Expand Down Expand Up @@ -108,5 +111,35 @@ def self.find(args)
JpPrefecture::Prefecture::Finder.new.find(field: search_field, value: search_value)
end
end

# 都道府県を検索し、一致したすべての都道府県を返す
#
# 文字列は前方一致で検索する
#
# @example
# # 複数の都道府県に一致する検索
# JpPrefecture::Prefecture.where(name: '山')
# # => [山形県, 山梨県, 山口県]
#
# # 英語表記で検索
# JpPrefecture::Prefecture.where(name_e: 'o')
# # => [大阪府, 岡山県, 大分県, 沖縄県]
#
# @param args [Hash<Symbol, String>] :name 漢字表記/:name_e 英語表記/:name_r ローマ字表記/:name_h ひらがな表記/:name_k カタカナ表記
# @return [Array<JpPrefecture::Prefecture>] 一致した都道府県インスタンスの配列 (コード順)。一致しない場合は空配列
# @raise [ArgumentError] 対応していない項目、または項目が 1 つでない場合
def self.where(args)
unless args.is_a?(Hash) && args.size == 1
raise ArgumentError, "expected a Hash with exactly one key, got: #{args.inspect}"
end

field, value = args.first

unless Finder::NAME_FIELDS.include?(field)
raise ArgumentError, "unsupported field: #{field.inspect} (supported: #{Finder::NAME_FIELDS.join(', ')})"
end

JpPrefecture::Prefecture::Finder.new.where(field: field, value: value)
end
end
end
20 changes: 19 additions & 1 deletion lib/jp_prefecture/prefecture/finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module JpPrefecture
class Prefecture
# 都道府県の検索を行うクラス
class Finder
# 名前を前方一致で検索できる項目
NAME_FIELDS = %i[name name_e name_r name_h name_k].freeze

def initialize
@mapping = Mapping.data
end
Expand All @@ -22,6 +25,21 @@ def find(field:, value:)
JpPrefecture::Prefecture.build_by_code(code)
end

# 指定した項目を前方一致で検索し、一致したすべての都道府県を返す
#
# @param field [Symbol] 検索する項目。NAME_FIELDS 以外を指定した場合は空配列
# @param value [String] 検索する内容
# @return [Array<JpPrefecture::Prefecture>] 一致した都道府県インスタンスの配列 (コード順)
def where(field:, value:)
return [] unless NAME_FIELDS.include?(field)

value = value.to_s.downcase
return [] if value.empty?

@mapping.select { |_code, names| names[field].start_with?(value) }
.map { |code, _names| JpPrefecture::Prefecture.build_by_code(code) }
end

private

# @param field [Symbol] 検索する項目
Expand All @@ -34,7 +52,7 @@ def find_code(field, value)
case field
when :all_fields
find_code_by_name_from_all_fields(value.to_s)
when :name, :name_h, :name_k, :name_e, :name_r
when *NAME_FIELDS
find_code_by_name(field, value.to_s)
when :code
value.to_i
Expand Down
50 changes: 50 additions & 0 deletions spec/prefecture/finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,54 @@
it_behaves_like '都道府県が見つからない', nil, '999'
end
end

describe '#where' do
shared_examples '都道府県が見つかる' do |field, value, expected_names|
let(:result) { JpPrefecture::Prefecture::Finder.new.where(field: field, value: value) }
it { expect(result.map(&:name)).to eq(expected_names) }
it { expect(result).to all(be_an_instance_of(JpPrefecture::Prefecture)) }
end

shared_examples '都道府県が見つからない' do |field, value|
let(:result) { JpPrefecture::Prefecture::Finder.new.where(field: field, value: value) }
it { expect(result).to eq([]) }
end

describe '複数の都道府県に一致する' do
it_behaves_like '都道府県が見つかる', :name, '山', %w[山形県 山梨県 山口県]
it_behaves_like '都道府県が見つかる', :name_e, 'o', %w[大阪府 岡山県 大分県 沖縄県]
end

describe '一つの都道府県に一致する' do
it_behaves_like '都道府県が見つかる', :name, '北海道', %w[北海道]
it_behaves_like '都道府県が見つかる', :name_h, 'ほっかい', %w[北海道]
it_behaves_like '都道府県が見つかる', :name_k, 'ホッカイ', %w[北海道]
it_behaves_like '都道府県が見つかる', :name_r, 'hokkaidō', %w[北海道]
end

describe '大文字と小文字を区別しない' do
it_behaves_like '都道府県が見つかる', :name_e, 'O', %w[大阪府 岡山県 大分県 沖縄県]
it_behaves_like '都道府県が見つかる', :name_e, 'HOKKAIDO', %w[北海道]
end

describe 'String 以外の値を指定する' do
it_behaves_like '都道府県が見つかる', :name_e, :hokkaido, %w[北海道]
end

describe 'どの都道府県にも一致しない' do
it_behaves_like '都道府県が見つからない', :name, '饂飩'
end

describe '空の値を指定する' do
it_behaves_like '都道府県が見つからない', :name, ''
it_behaves_like '都道府県が見つからない', :name, nil
end

describe '名前以外の field を指定する' do
it_behaves_like '都道府県が見つからない', :code, 1
it_behaves_like '都道府県が見つからない', :zip, 10_000
it_behaves_like '都道府県が見つからない', :all_fields, '東'
it_behaves_like '都道府県が見つからない', nil, '山'
end
end
end
37 changes: 37 additions & 0 deletions spec/prefecture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,41 @@
it { expect(result).to be_nil }
end
end

describe '.where' do
context '名前系の項目を指定' do
before do
finder = spy('finder')
allow(JpPrefecture::Prefecture::Finder).to receive(:new).and_return(finder)
JpPrefecture::Prefecture.where(name: '山')
end

it { expect(JpPrefecture::Prefecture::Finder.new).to have_received(:where).with(field: :name, value: '山') }
end

context '複数の都道府県に一致' do
let(:result) { JpPrefecture::Prefecture.where(name: '山') }
it { expect(result.map(&:name)).to eq(%w[山形県 山梨県 山口県]) }
end

context '対応していない項目を指定' do
it { expect { JpPrefecture::Prefecture.where(code: 1) }.to raise_error(ArgumentError) }
it { expect { JpPrefecture::Prefecture.where(zip: 1_000_000) }.to raise_error(ArgumentError) }
it { expect { JpPrefecture::Prefecture.where(all_fields: '東') }.to raise_error(ArgumentError) }
it { expect { JpPrefecture::Prefecture.where(name_j: '山') }.to raise_error(ArgumentError) }
end

context '項目を複数指定' do
it { expect { JpPrefecture::Prefecture.where(name: '山', name_e: 'y') }.to raise_error(ArgumentError) }
end

context '空の Hash を指定' do
it { expect { JpPrefecture::Prefecture.where({}) }.to raise_error(ArgumentError) }
end

context 'Hash 以外を指定' do
it { expect { JpPrefecture::Prefecture.where('山') }.to raise_error(ArgumentError) }
it { expect { JpPrefecture::Prefecture.where(nil) }.to raise_error(ArgumentError) }
end
end
end