china_region_fu 0.0.6 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.gitignore +12 -5
- data/.rspec +2 -1
- data/.rubocop.yml +101 -0
- data/.travis.yml +4 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -4
- data/LICENSE.txt +21 -0
- data/README.en.md +212 -0
- data/README.md +103 -68
- data/Rakefile +1 -1
- data/app/controllers/china_region_fu/fetch_options_controller.rb +64 -28
- data/app/models/city.rb +1 -11
- data/app/models/district.rb +1 -11
- data/app/models/province.rb +4 -4
- data/bin/console +14 -0
- data/bin/rspec +17 -0
- data/bin/setup +8 -0
- data/china_region_fu.gemspec +20 -19
- data/db/migrate/20111111111111_create_china_region_tables.rb +19 -17
- data/lib/china_region_fu.rb +4 -3
- data/lib/china_region_fu/engine.rb +11 -9
- data/lib/china_region_fu/errors.rb +5 -0
- data/lib/china_region_fu/helpers/formtastic.rb +4 -6
- data/lib/china_region_fu/helpers/helpers.rb +44 -53
- data/lib/china_region_fu/helpers/simple_form.rb +15 -14
- data/lib/china_region_fu/helpers/utils.rb +82 -0
- data/lib/china_region_fu/version.rb +1 -1
- data/lib/generators/china_region_fu/models/USAGE +1 -1
- data/lib/generators/china_region_fu/models/models_generator.rb +3 -3
- data/lib/tasks/{region.rake → china_region_fu_tasks.rake} +8 -14
- data/spec/china_region_fu_spec.rb +3 -7
- data/spec/helpers/helpers_spec.rb +113 -0
- data/spec/helpers/utils_spec.rb +87 -0
- data/spec/spec_helper.rb +6 -1
- data/spec/support/models.rb +39 -0
- metadata +50 -61
- data/LICENSE +0 -22
- data/lib/china_region_fu/exceptions.rb +0 -7
- data/lib/china_region_fu/helpers/utilities.rb +0 -54
data/README.md
CHANGED
@@ -1,59 +1,76 @@
|
|
1
|
+
# [English Readme](https://github.com/Xuhao/china_region_fu/blob/master/README.en.md)
|
2
|
+
|
1
3
|
# ChinaRegionFu
|
2
4
|
|
3
|
-
[](https://travis-ci.org/Xuhao/china_region_fu)
|
6
|
+
[](https://www.codacy.com/app/xuhao/china_region_fu?utm_source=github.com&utm_medium=referral&utm_content=Xuhao/china_region_fu&utm_campaign=Badge_Grade)
|
7
|
+
[](https://www.codacy.com/app/xuhao/china_region_fu?utm_source=github.com&utm_medium=referral&utm_content=Xuhao/china_region_fu&utm_campaign=Badge_Coverage)
|
8
|
+
[](https://badge.fury.io/rb/china_region_fu)
|
9
|
+
|
10
|
+
ChinaRegionFu 是一个提供中国行政区域信息的rails engine(只适用rails)。使用ChinaRegionFu后,你将拥有全面且准确的中国区域数据,而且你还可以使用其提供的表单helper,轻松将具有联动效果的区域选择器放入你的表单中。
|
11
|
+
|
12
|
+

|
13
|
+
|
14
|
+
## 运行环境
|
5
15
|
|
6
|
-
|
16
|
+
* CRuby 2.2 及以上
|
17
|
+
* Rails 4.0 及以上
|
18
|
+
* jQuery 1.8 及以上, 若不需要联动效果可忽略
|
7
19
|
|
8
|
-
##
|
20
|
+
## 安装
|
9
21
|
|
10
|
-
|
22
|
+
将 'gem china_region_fu' 放入项目的 Gemfile中:
|
11
23
|
|
12
24
|
gem 'china_region_fu'
|
13
25
|
|
14
|
-
|
26
|
+
运行 bundler 命令来安装:
|
15
27
|
|
16
28
|
bundle install
|
17
29
|
|
18
|
-
|
30
|
+
bundle安装完成后,你需要依次运行以下命令:
|
19
31
|
|
20
|
-
1.
|
32
|
+
1. 复制数据迁移文件到项目中:
|
21
33
|
|
22
|
-
<pre>rake
|
34
|
+
<pre>rake china_region_fu:install:migrations</pre>
|
23
35
|
|
24
|
-
2.
|
36
|
+
2. 运行 db:migrate 来创建数据库表:
|
25
37
|
|
26
38
|
<pre>rake db:migrate</pre>
|
27
39
|
|
28
|
-
3.
|
40
|
+
3. 从[china_region_data](https://raw.github.com/Xuhao/china_region_data)下载最新的<b>[regions.yml](https://raw.github.com/Xuhao/china_region_data/master/regions.yml)</b>:
|
29
41
|
|
30
|
-
<pre>rake
|
42
|
+
<pre>rake china_region_fu:download</pre>
|
31
43
|
|
32
|
-
4.
|
44
|
+
4. 将区域信息导入到数据库:
|
33
45
|
|
34
|
-
<pre>rake
|
46
|
+
<pre>rake china_region_fu:import</pre>
|
35
47
|
|
36
|
-
|
48
|
+
以上每条命令运行完成后,你可以根据需要对生成的文件做一些修改,然后再运行下一条。如果你不需要做自定义修改,也可以通过下面这条命令实现和上面4个命令同样的动作:
|
37
49
|
|
38
|
-
rake
|
50
|
+
rake china_region_fu:setup
|
39
51
|
|
40
|
-
|
52
|
+
区域数据来自[china_region_data](https://github.com/Xuhao/china_region_data), 你可以通过查看这个git库来了解都有哪些区域数据。
|
41
53
|
|
42
|
-
|
54
|
+
ChinaRegionFu通过`ActiveRecord`将各种区域映射成类:
|
55
|
+
* `Province`: 省、自治区、直辖市、特别行政区
|
56
|
+
* `City`: 地区、盟、自治州、地级市
|
57
|
+
* `District`: 县、自治县、旗、自治旗、县级市、市辖区、林区、特区
|
58
|
+
|
59
|
+
如果你想自定义这些类,可以通过以下命令将其复制到你的项目中:
|
43
60
|
|
44
61
|
rails g china_region_fu:models
|
45
62
|
|
46
|
-
|
63
|
+
将会创建:
|
47
64
|
|
48
65
|
create app/models/province.rb
|
49
66
|
create app/models/city.rb
|
50
67
|
create app/models/district.rb
|
51
68
|
|
52
|
-
|
69
|
+
你可以在以上这些类文件中做任何事。
|
53
70
|
|
54
|
-
##
|
71
|
+
## 使用说明
|
55
72
|
|
56
|
-
|
73
|
+
### Model
|
57
74
|
|
58
75
|
```ruby
|
59
76
|
a = Province.last
|
@@ -80,83 +97,98 @@ d.city.name # => "阿勒泰地区"
|
|
80
97
|
d.province.name # => "新疆维吾尔自治区"
|
81
98
|
```
|
82
99
|
|
83
|
-
|
100
|
+
### View
|
84
101
|
|
85
|
-
|
102
|
+
#### Form helpers
|
86
103
|
|
87
104
|
```erb
|
88
|
-
<%= form_for(@
|
105
|
+
<%= form_for(@address) do |f| %>
|
89
106
|
<div class="field">
|
90
107
|
<%= f.label :province, '选择地区:' %><br />
|
91
108
|
|
92
109
|
# FormBuilder
|
93
110
|
<%= f.region_select :city %>
|
94
|
-
<%= f.region_select [:
|
95
|
-
<%= f.region_select [:
|
96
|
-
<%= f.region_select [:
|
97
|
-
<%= f.region_select [:province, :district] %>
|
111
|
+
<%= f.region_select [:province_id, :city_id, :district_id], province_prompt: 'Do', city_prompt: 'it', district_prompt: 'like this' %>
|
112
|
+
<%= f.region_select [:province_id, :city_id], include_blank: true %>
|
113
|
+
<%= f.region_select [:city_id, :district_id] %>
|
98
114
|
|
99
115
|
# FormHelper
|
100
|
-
<%= region_select :
|
101
|
-
<%= region_select :
|
116
|
+
<%= region_select :address, :province_id %>
|
117
|
+
<%= region_select :address, [:province_id, :city_id, :district_id] %>
|
102
118
|
...
|
103
119
|
|
104
120
|
# FormTagHelper
|
105
|
-
<%= region_select_tag :
|
106
|
-
<%= region_select_tag [:
|
121
|
+
<%= region_select_tag :province_id, class: 'province-select', include_blank: true %>
|
122
|
+
<%= region_select_tag [:province_id, :city_id, :district_id], province_prompt: 'Do', city_prompt: 'it', district_prompt: 'like this', class: 'region-select' %>
|
107
123
|
...
|
108
124
|
</div>
|
109
125
|
<% end %>
|
110
126
|
```
|
111
127
|
|
112
|
-
|
128
|
+
#### SimpleForm
|
113
129
|
|
114
130
|
```erb
|
115
|
-
<%= simple_form_for(@
|
116
|
-
<%= f.input :
|
117
|
-
<%= f.input :
|
118
|
-
<%= f.input :
|
119
|
-
<%=
|
131
|
+
<%= simple_form_for(@address) do |f| %>
|
132
|
+
<%= f.input :province_id, as: :region, collection: Province.select('id, name'), sub_region: :city_id %>
|
133
|
+
<%= f.input :city_id, as: :region, collection: City.where(province_id: f.object.province_id).select('id, name'), sub_region: :district_id %>
|
134
|
+
<%= f.input :district_id, as: :region, collection: District.where(city_id: f.object.city_id).select('id, name') %>
|
135
|
+
<%= china_region_fu_js %>
|
120
136
|
<% end %>
|
121
137
|
```
|
122
138
|
|
123
|
-
|
139
|
+
#### Formtastic
|
124
140
|
|
125
141
|
```erb
|
126
|
-
<%= semantic_form_for(@
|
127
|
-
<%= f.input :
|
128
|
-
<%= f.input :
|
129
|
-
<%= f.input :
|
130
|
-
<%=
|
142
|
+
<%= semantic_form_for(@address) do |f| %>
|
143
|
+
<%= f.input :province_id, as: :region, collection: Province.select('id, name'), sub_region: :city_id) %>
|
144
|
+
<%= f.input :city_id, as: :region, collection: City.where(province_id: f.object.province_id).select('id, name'), sub_region: :district_id %>
|
145
|
+
<%= f.input :district_id, as: :region, collection: District.where(city_id: f.object.city_id).select('id, name') %>
|
146
|
+
<%= china_region_fu_js %>
|
131
147
|
<% end %>
|
132
148
|
```
|
133
149
|
|
134
|
-
|
150
|
+
#### 通过AJAX实现联动效果
|
135
151
|
|
136
|
-
|
152
|
+
当选中某个省份时,我们希望城市下拉列表自动显示这个省下属的城市;当选择某个城市时,我们希望区域列表显示该城市下属区域。你需要在你的页面中添加以下helper来实现联动效果:
|
137
153
|
|
138
154
|
```erb
|
139
|
-
<%=
|
155
|
+
<%= china_region_fu_js %>
|
156
|
+
# or
|
157
|
+
<%= content_for :china_region_fu_js %>
|
140
158
|
```
|
141
159
|
|
142
|
-
|
160
|
+
将会渲染:
|
143
161
|
|
144
162
|
```html
|
145
163
|
<script type="text/javascript">
|
146
164
|
//<![CDATA[
|
165
|
+
window.chinaRegionFu = window.chinaRegionFu || {};
|
147
166
|
$(function(){
|
148
|
-
$('body').on('change', '.
|
149
|
-
var self, $targetDom;
|
150
|
-
self = $(event.currentTarget);
|
151
|
-
$
|
152
|
-
if ($
|
153
|
-
$.getJSON('/china_region_fu/fetch_options', {
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
})
|
159
|
-
|
167
|
+
$('body').off('change', '.china-region-select').on('change', '.china-region-select', function(event) {
|
168
|
+
var $self, $targetDom;
|
169
|
+
$self = $(event.currentTarget);
|
170
|
+
$subRegionDom = $('[data-region-name="' + $self.data('sub-region') + '"]');
|
171
|
+
if ($subRegionDom.size() > 0) {
|
172
|
+
$.getJSON('/china_region_fu/fetch_options', {
|
173
|
+
columns: window.chinaRegionFu.fetchColumns || 'id,name',
|
174
|
+
sub_name: $self.data('sub-region'),
|
175
|
+
parent_name: $self.data('region-name'),
|
176
|
+
parent_id: $self.val()
|
177
|
+
}, function(json) {
|
178
|
+
if (window.chinaRegionFu.ajaxDone) {
|
179
|
+
window.chinaRegionFu.ajaxDone(json);
|
180
|
+
} else {
|
181
|
+
var options = [];
|
182
|
+
$self.parent().nextAll().find('.china-region-select > option[value!=""]').remove()
|
183
|
+
$.each(json.data, function(_, value) {
|
184
|
+
options.push("<option value='" + value.id + "'>" + value.name + "</option>");
|
185
|
+
});
|
186
|
+
$subRegionDom.append(options.join(''));
|
187
|
+
}
|
188
|
+
}).fail(function(xhr, textStatus, error) {
|
189
|
+
window.chinaRegionFu.ajaxFail && window.chinaRegionFu.ajaxFail(xhr, textStatus, error);
|
190
|
+
}).always(function(event, xhr, settings) {
|
191
|
+
window.chinaRegionFu.ajaxAlways && window.chinaRegionFu.ajaxAlways(event, xhr, settings);
|
160
192
|
});
|
161
193
|
}
|
162
194
|
});
|
@@ -164,19 +196,22 @@ it will render:
|
|
164
196
|
//]]>
|
165
197
|
</script>
|
166
198
|
```
|
199
|
+
自定义:
|
200
|
+
|
201
|
+
* `window.chinaRegionFu.fetchColumns`: 设定返回栏位数据,默认: `id,name`
|
202
|
+
* `window.chinaRegionFu.ajaxDone(json)`: 自定义ajax `success`事件,默认:重新填充下级选项
|
203
|
+
* `window.chinaRegionFu.ajaxFail(xhr, textStatus, error)`: 自定义ajax `fail`事件
|
204
|
+
* `window.chinaRegionFu.ajaxAlways(event, xhr, settings)`: 自定义ajax `always`事件
|
205
|
+
|
206
|
+
## 在线的例子
|
167
207
|
|
168
|
-
## Online example
|
169
208
|
[医院之家](http://www.yihub.com/ "医院").
|
170
209
|
|
171
210
|
## Contributing
|
172
211
|
|
173
|
-
|
174
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
175
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
176
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
177
|
-
5. Create new Pull Request
|
212
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/xuhao/china_region_fu. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
178
213
|
|
179
214
|
## License
|
180
215
|
|
181
|
-
|
216
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
182
217
|
|
data/Rakefile
CHANGED
@@ -1,37 +1,73 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
1
|
+
# GET: /china_region_fu/fetch_options
|
2
|
+
# Pass parent's id(by `province_id`, `city_id`) to fetch children(cities, districts),
|
3
|
+
# If no parent id passed, return all provinces.
|
4
|
+
#
|
5
|
+
# ==== Paramters
|
6
|
+
#
|
7
|
+
# * <tt>:columns</tt> - Set the columns to return for each one.
|
8
|
+
# * <tt>:parent_name</tt> - Name for parent region
|
9
|
+
# * <tt>:parent_id</tt> - ID for parent region
|
10
|
+
# * <tt>:sub_name</tt> - Name for sub region
|
11
|
+
require 'active_support/core_ext/object/blank'
|
13
12
|
|
14
|
-
|
13
|
+
class ChinaRegionFu::FetchOptionsController < ::ActionController::Metal
|
14
|
+
def index
|
15
|
+
validate_params!
|
16
|
+
regions = sub_klass.order(name: :asc)
|
17
|
+
regions = regions.where(where_value) if where_value
|
18
|
+
regions = regions.reorder(level: :asc) if has_level_column?
|
19
|
+
regions = regions.select(region_fields)
|
20
|
+
self.response_body = { data: regions.as_json(only: region_fields, methods: region_methods) }.to_json
|
21
|
+
rescue => e
|
22
|
+
self.response_body = { error: e.message }.to_json
|
23
|
+
self.status = 500
|
24
|
+
end
|
15
25
|
|
16
|
-
|
17
|
-
klass.column_names.to_a.include?('level')
|
18
|
-
end
|
26
|
+
private
|
19
27
|
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
def has_level_column?
|
29
|
+
sub_klass.column_names.include?('level')
|
30
|
+
end
|
23
31
|
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
# Available fields in DB
|
33
|
+
def region_fields
|
34
|
+
sub_klass.column_names & params_columns
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
37
|
+
# Methods can be responded to
|
38
|
+
def region_methods
|
39
|
+
params_columns.select { |column| sub_klass.instance_methods(false).include?(column.to_sym) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def params_columns
|
43
|
+
@params_columns ||= params[:columns].to_s.split(',').presence || %w(id name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def sub_klass
|
47
|
+
@sub_klass ||= begin
|
48
|
+
case params[:sub_name]
|
49
|
+
when 'city', 'city_id' then City
|
50
|
+
when 'district', 'district_id' then District
|
51
|
+
end
|
30
52
|
end
|
53
|
+
end
|
31
54
|
|
32
|
-
|
33
|
-
|
55
|
+
def where_value
|
56
|
+
@where_value ||= begin
|
57
|
+
case params[:parent_name]
|
58
|
+
when 'province', 'province_id' then { province_id: params[:parent_id] }
|
59
|
+
when 'city', 'city_id' then { city_id: params[:parent_id] }
|
60
|
+
else nil
|
61
|
+
end
|
34
62
|
end
|
63
|
+
end
|
35
64
|
|
36
|
-
|
37
|
-
|
65
|
+
def validate_params!
|
66
|
+
(params[:parent_name].present? &&
|
67
|
+
params[:parent_id].present? &&
|
68
|
+
params[:sub_name].present? &&
|
69
|
+
%w(city city_id district district_id).include?(params[:sub_name]) &&
|
70
|
+
%w(province province_id city city_id).include?(params[:parent_name])) ||
|
71
|
+
raise('Invalid paramters!')
|
72
|
+
end
|
73
|
+
end
|
data/app/models/city.rb
CHANGED
@@ -1,18 +1,8 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
class City < ActiveRecord::Base
|
3
|
-
if Rails.version < '4.0'
|
4
|
-
attr_accessible :name, :province_id, :level, :zip_code, :pinyin, :pinyin_abbr
|
5
|
-
end
|
6
|
-
|
7
2
|
belongs_to :province
|
8
3
|
has_many :districts, dependent: :destroy
|
9
4
|
|
10
5
|
def short_name
|
11
|
-
@short_name ||= name.gsub(
|
6
|
+
@short_name ||= name.gsub(/市|(回族|哈萨克|柯尔克孜|彝族|蒙古(族)?|白族|朝鲜族|傣族|景颇族|傈僳族|土家族|壮族|苗族|藏族|哈尼族|羌族|侗族|布依)?自治州|地区|特别行政区$/,'')
|
12
7
|
end
|
13
|
-
|
14
|
-
def brothers
|
15
|
-
@brothers ||= City.where("province_id = #{province_id}")
|
16
|
-
end
|
17
|
-
|
18
8
|
end
|
data/app/models/district.rb
CHANGED
@@ -1,18 +1,8 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
class District < ActiveRecord::Base
|
3
|
-
if Rails.version < '4.0'
|
4
|
-
attr_accessible :name, :city_id, :pinyin, :pinyin_abbr
|
5
|
-
end
|
6
|
-
|
7
2
|
belongs_to :city
|
8
3
|
has_one :province, through: :city
|
9
4
|
|
10
5
|
def short_name
|
11
|
-
@short_name ||= name.
|
6
|
+
@short_name ||= name.sub(/区|县|市$/,'')
|
12
7
|
end
|
13
|
-
|
14
|
-
def brothers
|
15
|
-
@brothers ||= District.where("city_id = #{city_id}")
|
16
|
-
end
|
17
|
-
|
18
8
|
end
|
data/app/models/province.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
class Province < ActiveRecord::Base
|
2
|
-
if Rails.version < '4.0'
|
3
|
-
attr_accessible :name, :pinyin, :pinyin_abbr
|
4
|
-
end
|
5
|
-
|
6
2
|
has_many :cities, dependent: :destroy
|
7
3
|
has_many :districts, through: :cities
|
4
|
+
|
5
|
+
def short_name
|
6
|
+
@short_name ||= name.sub(/省|市|(回族|壮族|维吾尔)?自治区|特别行政区&/, '')
|
7
|
+
end
|
8
8
|
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'china_region_fu'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require 'pry'
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/rspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rspec' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rspec-core", "rspec")
|