china_region_fu 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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")