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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,27 +1,28 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'china_region_fu/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "china_region_fu"
6
+ spec.name = 'china_region_fu'
8
7
  spec.version = ChinaRegionFu::VERSION
9
- spec.authors = ["Xuhao"]
10
- spec.email = ["xuhao@rubyfans.com"]
11
- spec.description = %q{China region Ruby on rails interface}
12
- spec.summary = %q{China region Ruby on rails interface}
13
- spec.homepage = "https://github.com/Xuhao/china_region_fu"
14
- spec.license = "MIT"
15
-
16
- spec.files = `git ls-files`.split($/)
8
+ spec.authors = ['Xuhao']
9
+ spec.email = ['xuhao@rubyfans.com']
10
+ spec.summary = 'china_region_fu provides simple helpers to get an HTML select list of countries, cities and districts.'
11
+ spec.description = 'china_region_fu provides simple helpers to get an HTML select list of countries, cities and districts.'
12
+ spec.homepage = 'https://github.com/Xuhao/china_region_fu'
13
+ spec.license = 'MIT'
14
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
15
+ f.match(%r{^(test|spec|features)/})
16
+ end
17
+ spec.bindir = 'bin'
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.test_files = Dir['spec/**/*']
20
+ spec.require_paths = ['lib']
21
+ spec.required_ruby_version = '>= 2.2.2'
22
+ spec.required_rubygems_version = '>= 1.8.11'
20
23
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec"
24
- spec.add_dependency 'activesupport'
25
- spec.add_dependency 'actionpack'
26
- spec.add_dependency 'httparty'
27
- end
24
+ spec.add_development_dependency 'bundler', '~> 1.13'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_dependency 'activesupport', '>= 4.0'
27
+ spec.add_dependency 'httparty', '~> 0.14.0'
28
+ end
@@ -7,12 +7,12 @@ class CreateChinaRegionTables < ActiveRecord::Migration
7
7
  t.string :pinyin_abbr
8
8
  t.timestamps
9
9
  end
10
-
11
- add_index :provinces, :name
12
- add_index :provinces, :pinyin
13
- add_index :provinces, :pinyin_abbr
10
+
11
+ add_index(:provinces, :name) unless index_exists?(:provinces, :name)
12
+ add_index(:provinces, :pinyin) unless index_exists?(:provinces, :pinyin)
13
+ add_index(:provinces, :pinyin_abbr) unless index_exists?(:provinces, :pinyin_abbr)
14
14
  end
15
-
15
+
16
16
  unless table_exists? 'cities'
17
17
  create_table :cities do |t|
18
18
  t.string :name
@@ -23,14 +23,15 @@ class CreateChinaRegionTables < ActiveRecord::Migration
23
23
  t.string :pinyin_abbr
24
24
  t.timestamps
25
25
  end
26
- add_index :cities, :name
27
- add_index :cities, :province_id
28
- add_index :cities, :level
29
- add_index :cities, :zip_code
30
- add_index :cities, :pinyin
31
- add_index :cities, :pinyin_abbr
26
+
27
+ add_index(:cities, :name) unless index_exists?(:cities, :name)
28
+ add_index(:cities, :province_id) unless index_exists?(:cities, :province_id)
29
+ add_index(:cities, :level) unless index_exists?(:cities, :level)
30
+ add_index(:cities, :zip_code) unless index_exists?(:cities, :zip_code)
31
+ add_index(:cities, :pinyin) unless index_exists?(:cities, :pinyin)
32
+ add_index(:cities, :pinyin_abbr) unless index_exists?(:cities, :pinyin_abbr)
32
33
  end
33
-
34
+
34
35
  unless table_exists? 'districts'
35
36
  create_table :districts do |t|
36
37
  t.string :name
@@ -39,10 +40,11 @@ class CreateChinaRegionTables < ActiveRecord::Migration
39
40
  t.string :pinyin_abbr
40
41
  t.timestamps
41
42
  end
42
- add_index :districts, :name
43
- add_index :districts, :city_id
44
- add_index :districts, :pinyin
45
- add_index :districts, :pinyin_abbr
43
+
44
+ add_index(:districts, :name) unless index_exists?(:districts, :name)
45
+ add_index(:districts, :city_id) unless index_exists?(:districts, :city_id)
46
+ add_index(:districts, :pinyin) unless index_exists?(:districts, :pinyin)
47
+ add_index(:districts, :pinyin_abbr) unless index_exists?(:districts, :pinyin_abbr)
46
48
  end
47
49
  end
48
- end
50
+ end
@@ -1,4 +1,5 @@
1
- require "china_region_fu/version"
2
- require 'china_region_fu/engine' if defined? Rails
1
+ require 'china_region_fu/version'
2
+ require 'china_region_fu/engine' if Object.const_defined?('Rails')
3
3
 
4
- module ChinaRegionFu;end
4
+ module ChinaRegionFu
5
+ end
@@ -1,18 +1,20 @@
1
- require "rails"
1
+ require 'rails'
2
2
  require 'china_region_fu/helpers/helpers'
3
3
 
4
4
  module ChinaRegionFu
5
- class Engine < Rails::Engine
6
- initializer "form helper extensions" do
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace ChinaRegionFu
7
+
8
+ initializer 'china_region_fu: form helper extensions' do
7
9
  ActiveSupport.on_load :action_view do
8
- ActionView::Base.send :include, ChinaRegionFu::Helpers
9
- ActionView::Helpers::FormBuilder.send :include, ChinaRegionFu::FormBuilder
10
+ ActionView::Base.send :include, ChinaRegionFu::Helpers::FormHelper
11
+ ActionView::Helpers::FormBuilder.send :include, ChinaRegionFu::Helpers::FormBuilder
10
12
  end
11
13
  end
12
14
 
13
- initializer "simple extension" do
14
- require "china_region_fu/helpers/simple_form" if Object.const_defined?("SimpleForm")
15
- require "china_region_fu/helpers/formtastic" if Object.const_defined?("Formtastic")
15
+ initializer 'china_region_fu: third-party extensions' do
16
+ require 'china_region_fu/helpers/simple_form' if Object.const_defined?('SimpleForm')
17
+ require 'china_region_fu/helpers/formtastic' if Object.const_defined?('Formtastic')
16
18
  end
17
19
  end
18
- end
20
+ end
@@ -0,0 +1,5 @@
1
+ # :nocov:
2
+ module ChinaRegionFu
3
+ InvalidFieldError = Class.new(StandardError)
4
+ end
5
+ # :nocov:
@@ -1,7 +1,7 @@
1
- require 'china_region_fu/helpers/utilities'
1
+ require 'china_region_fu/helpers/utils'
2
2
 
3
3
  class RegionInput < Formtastic::Inputs::SelectInput
4
- include ChinaRegionFu::Utilities
4
+ include ChinaRegionFu::Utils
5
5
 
6
6
  def collection_from_options
7
7
  return [] unless options.key?(:collection)
@@ -9,8 +9,6 @@ class RegionInput < Formtastic::Inputs::SelectInput
9
9
  end
10
10
 
11
11
  def input_html_options
12
- the_options = append_region_class(super.dup)
13
- the_options = set_html_options(object_name, input_name, the_options, input_options.delete(:sub_region).to_s.sub(/_id\Z/, '').foreign_key) if input_options.key?(:sub_region)
14
- the_options
12
+ append_html_options(input_name, input_options.delete(:sub_region), super)
15
13
  end
16
- end
14
+ end
@@ -1,70 +1,61 @@
1
- require 'china_region_fu/exceptions'
2
- require 'china_region_fu/helpers/utilities'
1
+ require 'china_region_fu/errors'
2
+ require 'china_region_fu/helpers/utils'
3
+ require 'active_support/core_ext/object/blank'
3
4
 
4
5
  module ChinaRegionFu
5
6
  module Helpers
6
- include Utilities
7
+ module FormHelper
8
+ include Utils
7
9
 
8
- def region_select_tag(names, options = {})
9
- append_region_class(options)
10
-
11
- if Array === names
12
- output = ActiveSupport::SafeBuffer.new
13
- names.each_with_index do |name, index|
14
- if klass = to_class(name)
15
- choices = index == 0 ? options_from_collection_for_select(klass.select('id, name'), "id", "name") : ''
16
- next_name = names.at(index + 1)
17
- set_html_options(nil, name, options, next_name)
10
+ def region_select_tag(regions, options = {})
11
+ render_region_select_tags(nil, regions, ActiveSupport::SafeBuffer.new, options)
12
+ end
18
13
 
19
- output << content_tag(:div, select_tag(name, choices, options.merge(prompt: options.delete("#{name}_prompt".to_sym))), class: "input region #{name.to_s}")
20
- else
21
- raise InvalidAttributeError
22
- end
23
- end
24
- output << js_for_region_ajax if names.size > 1
25
- output
26
- else
27
- if klass = to_class(names)
28
- select_tag(names, options_from_collection_for_select(klass.select('id, name'), "id", "name"), options)
29
- else
30
- raise InvalidAttributeError
31
- end
14
+ def region_select(object, methods, options = {}, html_options = {})
15
+ render_region_select_tags(object, methods, ActiveSupport::SafeBuffer.new, options, html_options)
32
16
  end
33
- end
34
17
 
35
- def region_select(object, methods, options = {}, html_options = {})
36
- options.symbolize_keys!
37
- html_options.symbolize_keys!
38
- append_region_class(html_options)
18
+ private
39
19
 
40
- if Array === methods
41
- output = ActiveSupport::SafeBuffer.new
42
- methods.each_with_index do |method, index|
43
- if klass = to_class(method)
44
- choices = index == 0 ? klass.select('id, name').collect {|p| [ p.name, p.id ] } : []
45
- next_method = methods.at(index + 1)
46
- set_html_options(object, method, html_options, next_method)
20
+ def render_region_select_tags(object, regions, buffer = ActiveSupport::SafeBuffer.new, options = {}, html_options = {})
21
+ regions = (regions.is_a?(Symbol) || regions.is_a?(String)) ? [regions] : regions.to_a
22
+ regions.each_with_index do |region, index|
23
+ if klass = region.to_s.sub(/_id\Z/, '').classify.safe_constantize
24
+ buffer << content_tag(:div, make_select(object, klass, region, regions.at(index + 1), index, options, html_options), class: "input region #{region}")
25
+ else
26
+ raise ChinaRegionFu::InvalidFieldError, "Invalid region field: `#{region}`, valid fields are: province, province_id, city, city_id, district and district_id."
27
+ end
28
+ end
29
+ content_for(:china_region_fu_js) { china_region_fu_js }
30
+ buffer
31
+ end
47
32
 
48
- output << content_tag(:div, select(object, method.to_s, choices, options.merge(prompt: options.delete("#{method}_prompt".to_sym)), html_options = html_options), class: "input region #{method.to_s}")
33
+ def make_select(object, klass, region, sub_region, order_index, options = {}, html_options = {})
34
+ choices = order_index == 0 ? klass.pluck(:name, :id) : get_choices(object, klass, region, options[:object])
35
+ if object
36
+ select(object, region, choices, append_prompt(region, options), append_html_options(region, sub_region, html_options))
49
37
  else
50
- raise InvalidAttributeError
38
+ select_tag(region, options_for_select(choices), append_html_options(region, sub_region, append_prompt(region, options)))
51
39
  end
52
40
  end
53
- output << js_for_region_ajax if methods.size > 1
54
- output
55
- else
56
- if klass = to_class(methods)
57
- content_tag(:div, select(object, methods, klass.select('id, name').collect {|p| [ p.name, p.id ] }, options = options, html_options = html_options), class: "input region #{methods.to_s}")
58
- else
59
- raise InvalidAttributeError
41
+
42
+ def get_choices(object, klass, region, ar_object)
43
+ return [] if object.blank?
44
+ return [] if !ar_object.is_a?(ActiveRecord::Base)
45
+ if %w(city city_id).include?(region.to_s) && ar_object.province_id.present?
46
+ klass.where(province_id: ar_object.province_id).pluck(:name, :id)
47
+ elsif %w(district district_id).include?(region.to_s) && ar_object.city_id.present?
48
+ klass.where(city_id: ar_object.city_id).pluck(:name, :id)
49
+ else
50
+ []
51
+ end
60
52
  end
61
- end
62
53
  end
63
- end
64
54
 
65
- module FormBuilder
66
- def region_select(methods, options = {}, html_options = {})
67
- @template.region_select(@object_name, methods, options = options, html_options = html_options)
55
+ module FormBuilder
56
+ def region_select(methods, options = {}, html_options = {})
57
+ @template.region_select(@object_name, methods, objectify_options(options), @default_options.merge(html_options))
58
+ end
68
59
  end
69
60
  end
70
- end
61
+ end
@@ -1,23 +1,24 @@
1
- require 'china_region_fu/helpers/utilities'
1
+ require 'china_region_fu/helpers/utils'
2
2
 
3
3
  module ChinaRegionFu
4
4
  module SimpleForm
5
- class RegionInput < ::SimpleForm::Inputs::CollectionInput
6
- include ChinaRegionFu::Utilities
5
+ class RegionInput < ::SimpleForm::Inputs::CollectionSelectInput
6
+ include ChinaRegionFu::Utils
7
+ def input_html_options
8
+ append_html_options(attribute_name, sub_region, super)
9
+ end
7
10
 
8
- def input
9
- label_method, value_method = detect_collection_methods
10
- append_region_class(input_html_options)
11
- set_html_options(object_name, attribute_name, input_html_options, input_options.delete(:sub_region)) if input_options.key?(:sub_region)
12
- region_collection = collection
13
- region_collection = [] if region_collection == ::SimpleForm::Inputs::CollectionInput.boolean_collection
14
- @builder.collection_select(
15
- attribute_name, region_collection, value_method, label_method,
16
- input_options, input_html_options
17
- )
11
+ def collection
12
+ @collection ||= options.delete(:collection) || []
18
13
  end
14
+
15
+ private
16
+
17
+ def sub_region
18
+ @sub_region ||= input_options.delete(:sub_region)
19
+ end
19
20
  end
20
21
  end
21
22
  end
22
23
 
23
- ::SimpleForm::FormBuilder.map_type :region, to: ChinaRegionFu::SimpleForm::RegionInput
24
+ ::SimpleForm::FormBuilder.map_type :region, to: ChinaRegionFu::SimpleForm::RegionInput
@@ -0,0 +1,82 @@
1
+ require 'active_support/core_ext/object/deep_dup'
2
+ require 'active_support/core_ext/string/output_safety'
3
+
4
+ module ChinaRegionFu
5
+ module Utils
6
+ def china_region_fu_js
7
+ js = <<-JAVASCRIPT
8
+ <script type="text/javascript">
9
+ //<![CDATA[
10
+ window.chinaRegionFu = window.chinaRegionFu || {};
11
+ $(function(){
12
+ $('body').off('change', '.china-region-select').on('change', '.china-region-select', function(event) {
13
+ var $self = $(event.currentTarget),
14
+ $subRegionDom = $('[data-region-name="' + $self.data('sub-region') + '"]'),
15
+ subName = $self.data('sub-region'),
16
+ parentName = $self.data('region-name'),
17
+ parentId = $self.val();
18
+ if ($subRegionDom.size() > 0 && subName && parentName && parentId) {
19
+ $.getJSON('/china_region_fu/fetch_options', {
20
+ columns: window.chinaRegionFu.fetchColumns || 'id,name',
21
+ sub_name: subName,
22
+ parent_name: parentName,
23
+ parent_id: parentId
24
+ }, function(json) {
25
+ if (window.chinaRegionFu.ajaxDone) {
26
+ window.chinaRegionFu.ajaxDone(json);
27
+ } else {
28
+ var options = [];
29
+ $self.parent().nextAll().find('.china-region-select > option[value!=""]').remove()
30
+ $.each(json.data, function(_, value) {
31
+ options.push("<option value='" + value.id + "'>" + value.name + "</option>");
32
+ });
33
+ $subRegionDom.append(options.join(''));
34
+ }
35
+ }).fail(function(xhr, textStatus, error) {
36
+ window.chinaRegionFu.ajaxFail && window.chinaRegionFu.ajaxFail(xhr, textStatus, error);
37
+ }).always(function(event, xhr, settings) {
38
+ window.chinaRegionFu.ajaxAlways && window.chinaRegionFu.ajaxAlways(event, xhr, settings);
39
+ });
40
+ }
41
+ });
42
+ });
43
+ //]]>
44
+ </script>
45
+ JAVASCRIPT
46
+ js.html_safe
47
+ end
48
+
49
+ def append_html_class_option(html_options)
50
+ _html_options = html_options.deep_dup
51
+ _html_options[:class] ||= ''
52
+ _html_options[:class] << ' china-region-select' unless _html_options[:class].include?('china-region-select')
53
+ _html_options
54
+ end
55
+
56
+ def append_html_data_option(current_region, sub_region, html_options)
57
+ _html_options = html_options.deep_dup
58
+ _html_options[:data] ||= {}
59
+ _html_options[:data][:region_name] = current_region
60
+ if sub_region
61
+ _html_options[:data][:sub_region] = sub_region
62
+ else
63
+ _html_options[:data].delete(:sub_region)
64
+ end
65
+ _html_options
66
+ end
67
+
68
+ def append_html_options(current_region, sub_region, html_options)
69
+ _html_options = html_options.deep_dup
70
+ _html_options = append_html_class_option(_html_options)
71
+ _html_options = append_html_data_option(current_region, sub_region, _html_options)
72
+ _html_options
73
+ end
74
+
75
+ def append_prompt(current_region, options)
76
+ current_name = current_region.to_s.sub(/_id\Z/, '')
77
+ options_without_prompt = options.except(:province_prompt, :city_prompt, :district_prompt)
78
+ options_without_prompt[:prompt] = options["#{current_name}_prompt".to_sym]
79
+ options_without_prompt
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module ChinaRegionFu
2
- VERSION = "0.0.6"
2
+ VERSION = '0.1.0'
3
3
  end
@@ -7,4 +7,4 @@ Example:
7
7
  This will create:
8
8
  app/models/province.rb
9
9
  app/models/city.rb
10
- app/models/district.rb
10
+ app/models/district.rb
@@ -3,9 +3,9 @@ module ChinaRegionFu
3
3
  source_root File.expand_path('../../../../../app', __FILE__)
4
4
 
5
5
  def copy_model_files
6
- copy_file "models/province.rb", "app/models/province.rb"
7
- copy_file "models/city.rb", "app/models/city.rb"
8
- copy_file "models/district.rb", "app/models/district.rb"
6
+ copy_file 'models/province.rb', 'app/models/province.rb'
7
+ copy_file 'models/city.rb', 'app/models/city.rb'
8
+ copy_file 'models/district.rb', 'app/models/district.rb'
9
9
  end
10
10
  end
11
11
  end