district_cn 1.0.0

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