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
@@ -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