district_cn 1.0.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.
@@ -0,0 +1,24 @@
1
+ module DistrictCn
2
+ module ActAsAreaField
3
+ module ActiveRecord
4
+
5
+ def act_as_area_field(*attributes)
6
+ attributes.each do |attribute|
7
+ class_eval <<-EVAL
8
+ alias_method :_#{attribute}, :#{attribute}
9
+ def #{attribute}
10
+ val = _#{attribute}
11
+ return val if val.blank?
12
+
13
+ unless @_#{attribute} && val.eql?(@_#{attribute}.value)
14
+ @_#{attribute} = DistrictCn.code(val)
15
+ end
16
+ @_#{attribute}
17
+ end
18
+ EVAL
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ module DistrictCn
2
+ module AsOptions
3
+ OPTIONS = {
4
+ :selected_province => nil,
5
+ :selected_city => nil,
6
+ :selected_district => nil,
7
+ :selected_provinces => [],
8
+ :selected_cities => [],
9
+ :selected_districts => []
10
+ }
11
+
12
+ def as_options
13
+ options = OPTIONS.dup
14
+ options[:selected_province] = province && [province_name, province_id]
15
+ options[:selected_city] = city && [city_name,city_id]
16
+ options[:selected_district] = district && [district_name, district_id]
17
+
18
+ options[:selected_provinces] = selected_provinces
19
+ options[:selected_cities] = selected_cities
20
+ options[:selected_districts] = selected_districts
21
+ options
22
+ end
23
+
24
+ private
25
+ def selected_provinces
26
+ self.class.data.map do |province_id,province_hash|
27
+ [province_hash[:text],province_id]
28
+ end
29
+ end
30
+
31
+ def selected_cities
32
+ return [] unless province
33
+ province[:children].map do |city_id,city_hash|
34
+ [city_hash[:text],city_id]
35
+ end
36
+ end
37
+
38
+ def selected_districts
39
+ return [] unless city
40
+ city[:children].map do |district_id,district_hash|
41
+ [district_hash[:text],district_id]
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,107 @@
1
+ module DistrictCn
2
+ class Code
3
+ include AsOptions
4
+
5
+ REGULAR = /(\d{2})(\d{2})(\d{2})/
6
+ attr_reader :value,:id
7
+
8
+ def initialize(id)
9
+ @value = id
10
+ @id = id.to_s
11
+ end
12
+
13
+ def name
14
+ get && get[:text]
15
+ end
16
+
17
+ def area_name(mark="-")
18
+ [province_name,city_name,district_name].compact.join(mark)
19
+ end
20
+
21
+ def children
22
+ return [] unless get && get[:children]
23
+
24
+ get[:children].map{|id,attr| [attr[:text],id] }
25
+ end
26
+
27
+
28
+ def get
29
+ @get ||= province? && province
30
+ @get ||= city? && city
31
+ @get ||= district? && district
32
+ @get || nil
33
+ end
34
+
35
+ def province?
36
+ !!(province_id && city_id.nil?)
37
+ end
38
+
39
+ def city?
40
+ !!(province_id && city_id && district_id.nil?)
41
+ end
42
+
43
+ def district?
44
+ !!(province_id && city_id && district_id)
45
+ end
46
+
47
+ def province
48
+ @province ||=
49
+ province_id && self.class.data && self.class.data[province_id]
50
+ end
51
+
52
+ def city
53
+ city_id && province && province[:children][city_id]
54
+ end
55
+
56
+ def district
57
+ district_id && city && city[:children][district_id]
58
+ end
59
+
60
+ def province_name
61
+ province && province[:text]
62
+ end
63
+
64
+ def city_name
65
+ city && city[:text]
66
+ end
67
+
68
+ def district_name
69
+ district && district[:text]
70
+ end
71
+
72
+ def province_id
73
+ @province_id ||=
74
+ if match && segment_blank?(match[1])
75
+ match[1].ljust(6, '0')
76
+ end
77
+ end
78
+
79
+ def city_id
80
+ @city_id ||=
81
+ if match && segment_blank?(match[2])
82
+ "#{match[1]}#{match[2]}".ljust(6, '0')
83
+ end
84
+ end
85
+
86
+ def district_id
87
+ @district_id ||=
88
+ if match && segment_blank?(match[3])
89
+ id
90
+ end
91
+ end
92
+
93
+ private
94
+ def match
95
+ @match ||= id.match(REGULAR)
96
+ end
97
+
98
+ def segment_blank?(segment)
99
+ segment != "00"
100
+ end
101
+
102
+ def self.data
103
+ @data ||= Db::File.instance.tree
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,81 @@
1
+ require 'json'
2
+ module DistrictCn
3
+ module Db
4
+ class File
5
+ attr_reader :tree,:list
6
+
7
+ def provinces
8
+ @provinces ||= @tree.map{|pvn_id,pvn_hash| [pvn_hash[:text],pvn_id]}
9
+ end
10
+
11
+ def initialize
12
+ @tree = {}
13
+ @list = {}
14
+ parse
15
+ end
16
+
17
+ class << self
18
+ def instance
19
+ @instance ||= new
20
+ end
21
+
22
+ def provinces
23
+ instance.provinces
24
+ end
25
+
26
+ def tree
27
+ instance.tree
28
+ end
29
+
30
+ def list
31
+ instance.list
32
+ end
33
+
34
+ private :new
35
+ end
36
+
37
+ private
38
+ def code_regular
39
+ DistrictCn::Code::REGULAR
40
+ end
41
+
42
+ def path
43
+ ::File.expand_path("../../areas.json", __FILE__)
44
+ end
45
+
46
+ def json_data
47
+ @json_data ||= JSON.parse(::File.read(path))
48
+ end
49
+
50
+ def parse
51
+ json_data["province"].each do |province|
52
+ @tree[province["id"]] = {:text => province["text"],:children => {}}
53
+
54
+ @list[province["id"]] = province["text"]
55
+ end
56
+
57
+ json_data["city"].each do |city|
58
+ city["id"] =~ (code_regular)
59
+ province_id = $1.ljust(6, '0')
60
+ if @tree[province_id]
61
+ @tree[province_id][:children][city["id"]] = {:text => city["text"], :children => {}}
62
+
63
+ @list[city["id"]] = city["text"]
64
+ end
65
+ end
66
+
67
+ json_data["district"].each do |district|
68
+ district["id"] =~ (code_regular)
69
+ province_id = $1.ljust(6, '0')
70
+ city_id = "#{$1}#{$2}".ljust(6, '0')
71
+ if @tree[province_id] && @tree[province_id][:children][city_id]
72
+ @tree[province_id][:children][city_id][:children][district["id"]] = {:text => district["text"]}
73
+
74
+ @list[district["id"]] = district["text"]
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ module DistrictCn
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,40 @@
1
+ require "district_cn/version"
2
+ require "active_support/core_ext/module/delegation"
3
+ require "active_support/core_ext/object/blank"
4
+
5
+ module DistrictCn
6
+ autoload :AsOptions, 'district_cn/as_options'
7
+ autoload :Code, 'district_cn/code'
8
+ autoload :Db, 'district_cn/db'
9
+
10
+ begin
11
+ if defined?(ActiveRecord)
12
+ autoload :ActAsAreaField,'district_cn/act_as_area_field'
13
+ ActiveRecord::Base.extend(ActAsAreaField::ActiveRecord)
14
+ end
15
+ rescue
16
+ end
17
+
18
+ class << self
19
+
20
+ def code(id)
21
+ return id if id.blank?
22
+ Code.new(id)
23
+ end
24
+
25
+ def search(text,limit=10)
26
+ results = []
27
+ list.each do |id,name|
28
+ break if results.size.eql?(limit)
29
+
30
+ if name =~ /#{text}/
31
+ results << Code.new(id)
32
+ end
33
+ end
34
+ results
35
+ end
36
+
37
+ delegate :provinces,:tree,:list, :to => Db::File
38
+ end
39
+
40
+ end
@@ -0,0 +1,67 @@
1
+ #encoding: utf-8
2
+ require 'spec_helper'
3
+ silence_warnings do
4
+ ActiveRecord::Migration.verbose = false
5
+ ActiveRecord::Base.logger = Logger.new(nil)
6
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
7
+ end
8
+
9
+ ActiveRecord::Base.connection.instance_eval do
10
+ create_table :companies do |t|
11
+ t.string :name
12
+ t.timestamps
13
+ end
14
+ end
15
+
16
+ class Company < ActiveRecord::Base
17
+ attr_accessor :region_code
18
+ attr_accessor :loc_code
19
+ attr_accessible :region_code
20
+
21
+ act_as_area_field :region_code
22
+ validates :region_code, presence: true
23
+ end
24
+
25
+ describe DistrictCn::ActAsAreaField do
26
+ subject { Company.new }
27
+ it "should return nil when attribute region_code is nil" do
28
+ expect(subject.region_code).to be_nil
29
+ end
30
+
31
+ it "should return \"\" when attribute region_code eql \"\"" do
32
+ subject.region_code = ""
33
+ expect(subject.region_code).to eq("")
34
+ end
35
+
36
+ it "should return DistrictCn::Code instance" do
37
+ subject.region_code = 331002
38
+ expect(subject.region_code).to be_instance_of(DistrictCn::Code)
39
+ end
40
+
41
+ it "should cached" do
42
+ subject.region_code = 331002
43
+ expect(subject.region_code.object_id).to eq(subject.region_code.object_id)
44
+ end
45
+
46
+ it "331002 and \"\"331002\"\" shoud have diff cache " do
47
+ subject.region_code = 331002
48
+ cache1 = subject.region_code
49
+ subject.region_code = "331002"
50
+ cache2 = subject.region_code
51
+ expect(cache1.object_id).not_to eq(cache2.object_id)
52
+ end
53
+
54
+ it "should return real value without acts_as_area_field" do
55
+ subject.loc_code = 331002
56
+ expect(subject.loc_code).to eq(331002)
57
+ expect(subject.loc_code).not_to be_instance_of(DistrictCn::Code)
58
+ end
59
+
60
+ it "should be unvalid when region_code is blank" do
61
+ subject.region_code = nil
62
+ expect(subject).not_to be_valid
63
+ expect(subject.save).to be_false
64
+ end
65
+
66
+ end
67
+
data/spec/code_spec.rb ADDED
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ describe DistrictCn::Code do
4
+ let!(:code_331002){ DistrictCn::Code.new(331002) }
5
+
6
+ it ".value should return real value" do
7
+ expect(code_331002.value).to eq(331002)
8
+ expect(DistrictCn::Code.new("331002").value).to eq("331002")
9
+ end
10
+
11
+ it ".id should return region_code.to_s" do
12
+ expect(code_331002.id).to eq("331002")
13
+ expect(DistrictCn::Code.new("331002").id).to eq("331002")
14
+ end
15
+
16
+ it "should return province_id" do
17
+ expect(DistrictCn::Code.new("00000a").province_id).to be_nil
18
+ expect(DistrictCn::Code.new("000000").province_id).to be_nil
19
+ expect(DistrictCn::Code.new("330000").province_id).to eq("330000")
20
+ expect(DistrictCn::Code.new("331000").province_id).to eq("330000")
21
+ expect(DistrictCn::Code.new("331002").province_id).to eq("330000")
22
+ end
23
+
24
+ it "should return city_id" do
25
+ expect(DistrictCn::Code.new("00000a").city_id).to be_nil
26
+ expect(DistrictCn::Code.new("000000").city_id).to be_nil
27
+ expect(DistrictCn::Code.new("330000").city_id).to be_nil
28
+ expect(DistrictCn::Code.new("331000").city_id).to eq("331000")
29
+ expect(DistrictCn::Code.new("331002").city_id).to eq("331000")
30
+ end
31
+
32
+ it "should return district_id" do
33
+ expect(DistrictCn::Code.new("00000a").district_id).to be_nil
34
+ expect(DistrictCn::Code.new("000000").district_id).to be_nil
35
+ expect(DistrictCn::Code.new("330000").district_id).to be_nil
36
+ expect(DistrictCn::Code.new("331000").district_id).to be_nil
37
+ expect(DistrictCn::Code.new("331002").district_id).to eq("331002")
38
+ end
39
+
40
+ it "should return area type" do
41
+ expect(DistrictCn::Code.new("330000")).to be_province
42
+ expect(DistrictCn::Code.new("330000")).not_to be_city
43
+ expect(DistrictCn::Code.new("330000")).not_to be_district
44
+
45
+ expect(DistrictCn::Code.new("331000")).to be_city
46
+ expect(DistrictCn::Code.new("331000")).not_to be_province
47
+ expect(DistrictCn::Code.new("331000")).not_to be_district
48
+
49
+ expect(DistrictCn::Code.new("331002")).to be_district
50
+ expect(DistrictCn::Code.new("331002")).not_to be_city
51
+ expect(DistrictCn::Code.new("331002")).not_to be_province
52
+ end
53
+
54
+ it "should return privince,city,district name" do
55
+ expect(DistrictCn::Code.new("331002").province_name).to eq("浙江省")
56
+ expect(DistrictCn::Code.new("331002").city_name).to eq("台州市")
57
+ expect(DistrictCn::Code.new("331002").district_name).to eq("椒江区")
58
+
59
+ expect(DistrictCn::Code.new("330000").city_name).to be_nil
60
+ expect(DistrictCn::Code.new("330000").district_name).to be_nil
61
+ end
62
+
63
+ it "should get detail area" do
64
+ expect(DistrictCn::Code.new("330000").get[:children]).not_to be_empty
65
+ expect(DistrictCn::Code.new("331000").get[:children]).not_to be_empty
66
+ expect(DistrictCn::Code.new("331002").get[:children]).to be_nil
67
+ end
68
+
69
+ it "should get children" do
70
+ expect(DistrictCn::Code.new("330000").children).to be_include(["台州市", "331000"])
71
+ expect(DistrictCn::Code.new("330000").children).not_to be_include(["椒江区", "331002"])
72
+
73
+ expect(DistrictCn::Code.new("331000").children).to be_include(["椒江区", "331002"])
74
+ expect(DistrictCn::Code.new("331002").children).to be_empty
75
+ end
76
+
77
+ it "should get area name" do
78
+ expect(DistrictCn::Code.new("330000").area_name).to eq("浙江省")
79
+ expect(DistrictCn::Code.new("331000").area_name).to eq("浙江省-台州市")
80
+ expect(DistrictCn::Code.new("331002").area_name).to eq("浙江省-台州市-椒江区")
81
+ end
82
+
83
+ context "as options" do
84
+ it "should return selected" do
85
+ options = DistrictCn::Code.new("330000").as_options
86
+ expect(options[:selected_provinces]).not_to be_empty
87
+ expect(options[:selected_cities]).not_to be_empty
88
+ expect(options[:selected_districts]).to be_empty
89
+
90
+ options = DistrictCn::Code.new("331000").as_options
91
+ expect(options[:selected_provinces]).not_to be_empty
92
+ expect(options[:selected_cities]).not_to be_empty
93
+ expect(options[:selected_districts]).not_to be_empty
94
+
95
+ options = DistrictCn::Code.new("331002").as_options
96
+ expect(options[:selected_provinces]).not_to be_empty
97
+ expect(options[:selected_cities]).not_to be_empty
98
+ expect(options[:selected_districts]).not_to be_empty
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
data/spec/db_spec.rb ADDED
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ describe DistrictCn::Db do
4
+ context "file" do
5
+ subject { DistrictCn::Db::File.instance }
6
+
7
+ it "should be cached" do
8
+ expect(subject.object_id).to eq(DistrictCn::Db::File.instance.object_id)
9
+ end
10
+
11
+ it "tree should be present" do
12
+ expect(subject.tree).not_to be_empty
13
+ expect(subject.tree["330000"]).not_to be_nil
14
+ expect(subject.tree['330000'][:text]).to eq("浙江省")
15
+ expect(subject.tree['330000'][:children]).not_to be_empty
16
+ end
17
+
18
+ it "list should be present" do
19
+ expect(subject.list).not_to be_empty
20
+ expect(subject.list["330000"]).to eq("浙江省")
21
+ expect(subject.list["331000"]).to eq("台州市")
22
+ expect(subject.list["331002"]).to eq("椒江区")
23
+ end
24
+
25
+ it "provinces should be present" do
26
+ expect(subject.provinces).not_to be_empty
27
+ expect(subject.provinces).to be_include(["浙江省","330000"])
28
+ expect(subject.provinces).not_to be_include(["台州市","331000"])
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ describe DistrictCn do
4
+ it ".code should return \"\" or nil" do
5
+ expect(DistrictCn.code(nil)).to be_nil
6
+ expect(DistrictCn.code(" ")).to be_blank
7
+ end
8
+
9
+ it ".code should build DistrictCn::Code" do
10
+ expect(DistrictCn.code("331002")).to be_an_instance_of(DistrictCn::Code)
11
+ expect(DistrictCn.code("33100a")).to be_an_instance_of(DistrictCn::Code)
12
+ end
13
+
14
+ it "search" do
15
+ codes = DistrictCn.search("浙江")
16
+ expect(codes.first).to be_an_instance_of(DistrictCn::Code)
17
+ expect(codes.first.name).to eq("浙江省")
18
+
19
+ codes = DistrictCn.search("浙江a")
20
+ expect(codes).to be_blank
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require 'active_record'
5
+ require File.expand_path("../../lib/district_cn", __FILE__)
6
+ require 'rspec/autorun'
7
+ #require 'active_support/core_ext/kernel/reporting'
8
+ require 'logger'
9
+ require 'pp'
10
+