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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -5
  3. data/.rspec +2 -1
  4. data/.rubocop.yml +101 -0
  5. data/.travis.yml +4 -2
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +10 -4
  8. data/LICENSE.txt +21 -0
  9. data/README.en.md +212 -0
  10. data/README.md +103 -68
  11. data/Rakefile +1 -1
  12. data/app/controllers/china_region_fu/fetch_options_controller.rb +64 -28
  13. data/app/models/city.rb +1 -11
  14. data/app/models/district.rb +1 -11
  15. data/app/models/province.rb +4 -4
  16. data/bin/console +14 -0
  17. data/bin/rspec +17 -0
  18. data/bin/setup +8 -0
  19. data/china_region_fu.gemspec +20 -19
  20. data/db/migrate/20111111111111_create_china_region_tables.rb +19 -17
  21. data/lib/china_region_fu.rb +4 -3
  22. data/lib/china_region_fu/engine.rb +11 -9
  23. data/lib/china_region_fu/errors.rb +5 -0
  24. data/lib/china_region_fu/helpers/formtastic.rb +4 -6
  25. data/lib/china_region_fu/helpers/helpers.rb +44 -53
  26. data/lib/china_region_fu/helpers/simple_form.rb +15 -14
  27. data/lib/china_region_fu/helpers/utils.rb +82 -0
  28. data/lib/china_region_fu/version.rb +1 -1
  29. data/lib/generators/china_region_fu/models/USAGE +1 -1
  30. data/lib/generators/china_region_fu/models/models_generator.rb +3 -3
  31. data/lib/tasks/{region.rake → china_region_fu_tasks.rake} +8 -14
  32. data/spec/china_region_fu_spec.rb +3 -7
  33. data/spec/helpers/helpers_spec.rb +113 -0
  34. data/spec/helpers/utils_spec.rb +87 -0
  35. data/spec/spec_helper.rb +6 -1
  36. data/spec/support/models.rb +39 -0
  37. metadata +50 -61
  38. data/LICENSE +0 -22
  39. data/lib/china_region_fu/exceptions.rb +0 -7
  40. 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
- [![Gem Version](https://badge.fury.io/rb/china_region_fu.png)](http://badge.fury.io/rb/china_region_fu)
4
- [![Build Status](https://travis-ci.org/Xuhao/china_region_fu.png?branch=master)](https://travis-ci.org/Xuhao/china_region_fu)
5
+ [![Build Status](https://travis-ci.org/Xuhao/china_region_fu.svg?branch=master)](https://travis-ci.org/Xuhao/china_region_fu)
6
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/85643743367e47d6853b94431f4f503f)](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
+ [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/85643743367e47d6853b94431f4f503f)](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
+ [![Gem Version](https://badge.fury.io/rb/china_region_fu.svg)](https://badge.fury.io/rb/china_region_fu)
9
+
10
+ ChinaRegionFu 是一个提供中国行政区域信息的rails engine(只适用rails)。使用ChinaRegionFu后,你将拥有全面且准确的中国区域数据,而且你还可以使用其提供的表单helper,轻松将具有联动效果的区域选择器放入你的表单中。
11
+
12
+ ![Screenshot](https://cloud.githubusercontent.com/assets/324973/19191241/2c5726a8-8cd4-11e6-960d-6edd1fa69427.gif "Screenshot")
13
+
14
+ ## 运行环境
5
15
 
6
- ChinaRegionFu provide the region data of china.You will got complete China region data after use this gem.
16
+ * CRuby 2.2 及以上
17
+ * Rails 4.0 及以上
18
+ * jQuery 1.8 及以上, 若不需要联动效果可忽略
7
19
 
8
- ## Installation
20
+ ## 安装
9
21
 
10
- Put 'gem china_region_fu' to your Gemfile:
22
+ 'gem china_region_fu' 放入项目的 Gemfile中:
11
23
 
12
24
  gem 'china_region_fu'
13
25
 
14
- Run bundler command to install the gem:
26
+ 运行 bundler 命令来安装:
15
27
 
16
28
  bundle install
17
29
 
18
- After you install the gem, you need run below tasks one by one:
30
+ bundle安装完成后,你需要依次运行以下命令:
19
31
 
20
- 1. Copy migration file to your app.
32
+ 1. 复制数据迁移文件到项目中:
21
33
 
22
- <pre>rake china_region_fu_engine:install:migrations</pre>
34
+ <pre>rake china_region_fu:install:migrations</pre>
23
35
 
24
- 2. Run db:migrate to create region tables.
36
+ 2. 运行 db:migrate 来创建数据库表:
25
37
 
26
38
  <pre>rake db:migrate</pre>
27
39
 
28
- 3. Download the latest regions.yml form [github](https://raw.github.com/Xuhao/china_region_data/master/regions.yml).
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 region:download</pre>
42
+ <pre>rake china_region_fu:download</pre>
31
43
 
32
- 4. Import regions to database.
44
+ 4. 将区域信息导入到数据库:
33
45
 
34
- <pre>rake region:import</pre>
46
+ <pre>rake china_region_fu:import</pre>
35
47
 
36
- You also can use below task to do the same things as four tasks above:
48
+ 以上每条命令运行完成后,你可以根据需要对生成的文件做一些修改,然后再运行下一条。如果你不需要做自定义修改,也可以通过下面这条命令实现和上面4个命令同样的动作:
37
49
 
38
- rake region:install
50
+ rake china_region_fu:setup
39
51
 
40
- Region data is from [ChinaRegionData](https://github.com/Xuhao/china_region_data), check it out to see what kind of data you have now.
52
+ 区域数据来自[china_region_data](https://github.com/Xuhao/china_region_data), 你可以通过查看这个git库来了解都有哪些区域数据。
41
53
 
42
- If you want to customize the region modules you can run the generator:
54
+ ChinaRegionFu通过`ActiveRecord`将各种区域映射成类:
55
+ * `Province`: 省、自治区、直辖市、特别行政区
56
+ * `City`: 地区、盟、自治州、地级市
57
+ * `District`: 县、自治县、旗、自治旗、县级市、市辖区、林区、特区
58
+
59
+ 如果你想自定义这些类,可以通过以下命令将其复制到你的项目中:
43
60
 
44
61
  rails g china_region_fu:models
45
62
 
46
- This will create:
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
- So you can do what you want to do in this files.
69
+ 你可以在以上这些类文件中做任何事。
53
70
 
54
- ## Usage
71
+ ## 使用说明
55
72
 
56
- #### Model
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
- #### View
100
+ ### View
84
101
 
85
- ##### Form helpers
102
+ #### Form helpers
86
103
 
87
104
  ```erb
88
- <%= form_for(@post) do |f| %>
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 [:province, :city, :district], province_prompt: 'Do', city_prompt: 'it', district_prompt: 'like this' %>
95
- <%= f.region_select [:province, :city], include_blank: true %>
96
- <%= f.region_select [:city, :district] %>
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 :post, :province %>
101
- <%= region_select :post, [:province, :city, :district] %>
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 :province, class: 'my', include_blank: true %>
106
- <%= region_select_tag [:province, :city, :district], province_prompt: 'Do', city_prompt: 'it', district_prompt: 'like this', class: 'my' %>
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
- ##### SimpleForm
128
+ #### SimpleForm
113
129
 
114
130
  ```erb
115
- <%= simple_form_for(@post) do |f| %>
116
- <%= f.input :province, as: :region, collection: Province.select('id, name'), sub_region: :city %>
117
- <%= f.input :city, as: :region, sub_region: :district %>
118
- <%= f.input :district, as: :region %>
119
- <%= js_for_region_ajax %>
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
- ##### Formtastic
139
+ #### Formtastic
124
140
 
125
141
  ```erb
126
- <%= semantic_form_for(@post) do |f| %>
127
- <%= f.input :province, as: :region, collection: Province.select('id, name'), sub_region: :city %>
128
- <%= f.input :city, as: :region, sub_region: :district %>
129
- <%= f.input :district, as: :region %>
130
- <%= js_for_region_ajax %>
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
- ##### Fetch sub regions by Ajax
150
+ #### 通过AJAX实现联动效果
135
151
 
136
- Once select one province, we want fetch cities of the selected province and fill the city select box automatically. If you use `:region_select_tag` and FormBuilder/FormHelper method aka `:region_select`, you need do nothing for this. If you use simple_form or normal form helper like `:select_tag` or `:select`, to implement this, what you need to do is add below helper in your form:
152
+ 当选中某个省份时,我们希望城市下拉列表自动显示这个省下属的城市;当选择某个城市时,我们希望区域列表显示该城市下属区域。你需要在你的页面中添加以下helper来实现联动效果:
137
153
 
138
154
  ```erb
139
- <%= js_for_region_ajax %>
155
+ <%= china_region_fu_js %>
156
+ # or
157
+ <%= content_for :china_region_fu_js %>
140
158
  ```
141
159
 
142
- it will render:
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', '.region_select', function(event) {
149
- var self, $targetDom;
150
- self = $(event.currentTarget);
151
- $targetDom = $('#' + self.data('region-target'));
152
- if ($targetDom.size() > 0) {
153
- $.getJSON('/china_region_fu/fetch_options', {klass: self.data('region-target-kalss'), parent_klass: self.data('region-klass'), parent_id: self.val()}, function(data) {
154
- var options = [];
155
- $('option[value!=""]', $targetDom).remove();
156
- $.each(data, function(index, value) {
157
- options.push("<option value='" + value.id + "'>" + value.name + "</option>");
158
- });
159
- $targetDom.append(options.join(''));
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
- 1. Fork it
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
- ChinaRegionFu is released under the MIT license.
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
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task :default => :spec
@@ -1,37 +1,73 @@
1
- module ChinaRegionFu
2
- class FetchOptionsController < ::ActionController::Metal
3
-
4
- def index
5
- if params_valid? and klass and parent_klass and (parent_object = parent_klass.find(params[:parent_id]))
6
- regions = parent_object.__send__(klass.model_name.plural).select("#{klass.table_name}.id, #{klass.table_name}.name").order("#{klass.table_name}.name ASC")
7
- regions.reorder("#{klass.table_name}.level ASC") if has_level_column?
8
- self.response_body = regions.to_json
9
- else
10
- self.response_body = [].to_json
11
- end
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
- private
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
- def has_level_column?
17
- klass.column_names.to_a.include?('level')
18
- end
26
+ private
19
27
 
20
- def klass
21
- params[:klass].sub(/_id\Z/, '').classify.safe_constantize
22
- end
28
+ def has_level_column?
29
+ sub_klass.column_names.include?('level')
30
+ end
23
31
 
24
- def parent_klass
25
- parent_klass_name.classify.safe_constantize
26
- end
32
+ # Available fields in DB
33
+ def region_fields
34
+ sub_klass.column_names & params_columns
35
+ end
27
36
 
28
- def parent_klass_name
29
- params[:parent_klass].sub(/_id\Z/, '')
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
- def params_valid?
33
- params[:klass].present? and parent_klass_name=~ /\Aprovince\Z|\Acity\Z/i and params[:parent_id].present?
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
- end
37
- end
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
@@ -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
@@ -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.gsub(/区|县|市|自治县/,'')
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
@@ -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
@@ -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
@@ -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")