mm_reader 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e249a24e890b035b344001022ff512a55dd9ea6
4
+ data.tar.gz: b655f367614aa89e029eb84223bfbaf660fdead3
5
+ SHA512:
6
+ metadata.gz: 2d68c85d654f6a49c0eb19239fa7d00e372954ce7af24e332bdd292c3815e5245ea9b3ebb806bc1c8836af733d49412c4ac46b10bf2aed6b59aa86293113f706
7
+ data.tar.gz: d120c047d841952894560e0244101ed93ae7cae130b263c4d62897f2b10ea1227d2cea9849bca54031ff1e87780c3dada1077c3ac0e77156180b446861d61b34
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mm_reader.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # MmReader
2
+
3
+ Адаптер для работы с базой MaxMind
4
+
5
+ ## Установка
6
+
7
+ Добавить в Gemfile:
8
+
9
+ ```ruby
10
+ gem 'mm_reader'
11
+ ```
12
+
13
+ Запустить:
14
+
15
+ $ bundle
16
+
17
+ Установать путь к будующему файлу базы sql.
18
+
19
+ ```
20
+ # config/initializer/mm_reader.db
21
+
22
+ require 'mm_reader'
23
+ MmReader::Connection.current_db = 'path to sqlite.db'
24
+ ```
25
+
26
+ Выполнить задачу на генерацию базы:
27
+
28
+ $ rake mm:create MM_PATH='path/to/csv/dir'
29
+
30
+ ## Использование
31
+
32
+ Запросы к базе.
33
+
34
+ ```
35
+ MmReader.find(123000, 'Москва', 'Московская область') # => ['10.0.0.0', '10.0.0.1', ...]
36
+ ```
37
+
38
+
39
+ ## Удаление базы
40
+
41
+ Выполнить задачу на удаление базы:
42
+
43
+ $ rake mm:clear
44
+
45
+
46
+ ## Development
47
+
48
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
49
+
50
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
51
+
52
+ ## Contributing
53
+
54
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/mm_reader.
55
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mm_reader"
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
data/bin/setup ADDED
@@ -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
data/lib/mm_reader.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "mm_reader/version"
2
+ require 'sqlite3'
3
+ require 'csv'
4
+ require 'ipaddr'
5
+ require 'pry'
6
+ require 'fiber'
7
+
8
+ module MmReader
9
+ # Your code goes here...
10
+ def self.find(postal_code = nil, city = nil, state = nil)
11
+ MmReader::Query.new(postal_code, city, state).result
12
+ end
13
+ end
14
+
15
+ require "mm_reader/connection"
16
+ require "mm_reader/converter"
17
+ require "mm_reader/query"
@@ -0,0 +1,12 @@
1
+ module MmReader
2
+ class Connection
3
+ class << self
4
+ attr_accessor :current_db
5
+
6
+ def get sqlite_file = MmReader::Connection.current_db
7
+ raise "Необходимо указать путь к базе sqlite" if sqlite_file.nil?
8
+ SQLite3::Database.new(sqlite_file)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,146 @@
1
+ module MmReader
2
+ class Converter
3
+ def self.clear sqlite_file = MmReader::Connection.current_db
4
+ FileUtils.rm sqlite_file if File.exists?(sqlite_file)
5
+ end
6
+
7
+ def initialize csv_dir
8
+ @csv_dir = csv_dir
9
+ end
10
+
11
+ def to_sqlite sqlite_file = MmReader::Connection.current_db
12
+ @db = MmReader::Connection.get sqlite_file
13
+ @db.execute('PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA locking_mode = EXCLUSIVE; PRAGMA count_changes = OFF; PRAGMA cache_size=500000; PRAGMA temp_store = MEMORY; PRAGMA auto_vacuum = NONE;')
14
+ @db.execute('BEGIN;')
15
+ puts "import_blocks_ip4"
16
+ import_blocks_ip4
17
+ puts "import_blocks_ip6"
18
+ import_blocks_ip6
19
+ puts "import_locations_ru"
20
+ import_locations_ru
21
+ puts "index_data"
22
+ index_data
23
+ puts "drop_temp"
24
+ drop_temp
25
+ puts "Commit"
26
+ @db.execute('COMMIT;')
27
+ end
28
+
29
+ def import_blocks_ip4
30
+ import_csv "#{@csv_dir}/GeoLite2-City-Blocks-IPv4.csv", "tmp_ip4"
31
+
32
+ end
33
+
34
+ def import_blocks_ip6
35
+ import_csv "#{@csv_dir}/GeoLite2-City-Blocks-IPv6.csv", "tmp_ip6"
36
+ end
37
+
38
+ def import_locations_ru
39
+ import_csv "#{@csv_dir}/GeoLite2-City-Locations-ru.csv", "tmp_locations"
40
+ end
41
+
42
+ def drop_temp
43
+ @db.execute("DROP TABLE tmp_ip4")
44
+ @db.execute("DROP TABLE tmp_ip6")
45
+ @db.execute("DROP TABLE tmp_locations")
46
+ end
47
+
48
+ def index_data
49
+ @db.execute("
50
+ CREATE TABLE ip4_index (
51
+ network varchar(255),
52
+ postal_code varchar(10),
53
+ city varchar(255),
54
+ state varchar(255)
55
+ );
56
+ ")
57
+ @db.execute("
58
+ CREATE TABLE ip6_index (
59
+ network varchar(255),
60
+ postal_code varchar(10),
61
+ city varchar(255),
62
+ state varchar(255)
63
+ );
64
+ ")
65
+
66
+ @db.execute("
67
+ INSERT INTO ip4_index (network, postal_code, city, state)
68
+ SELECT tmp_ip4.network, tmp_ip4.postal_code, tmp_locations.city_name, tmp_locations.subdivision_1_name FROM tmp_ip4 LEFT JOIN tmp_locations ON tmp_ip4.geoname_id=tmp_locations.geoname_id;
69
+ ")
70
+
71
+ @db.execute("
72
+ INSERT INTO ip6_index (network, postal_code, city, state)
73
+ SELECT tmp_ip6.network, tmp_ip6.postal_code, tmp_locations.city_name, tmp_locations.subdivision_1_name FROM tmp_ip6 LEFT JOIN tmp_locations ON tmp_ip6.geoname_id=tmp_locations.geoname_id;
74
+ ")
75
+ @db.execute "CREATE INDEX postal_code4_idx ON ip4_index (postal_code);"
76
+ @db.execute "CREATE INDEX postal_code6_idx ON ip6_index (postal_code);"
77
+
78
+ end
79
+
80
+ def import_csv from_file, to_table
81
+ provider = prepare_data from_file
82
+ columns = provider.transfer
83
+ schema = provider.transfer
84
+ @db.execute "CREATE TABLE #{to_table} (#{schema});"
85
+
86
+ loop do
87
+ stop = insert_bulk to_table, columns, provider
88
+ break if stop
89
+ end
90
+ end
91
+
92
+ def insert_bulk to_table, columns, provider, limit = 10000
93
+ begin
94
+ stop = false
95
+ data = []
96
+ limit.times{ data << provider.transfer }
97
+ stop
98
+ rescue FiberError
99
+ stop = true
100
+ ensure
101
+ @db.execute "INSERT INTO #{to_table} (#{columns}) VALUES #{data_mask_map(data)};", data
102
+ stop
103
+ end
104
+ end
105
+
106
+ def prepare_data from_file
107
+ row_fiber = nil
108
+ iterator = Fiber.new do
109
+ header = nil
110
+ CSV.foreach(from_file, col_sep: ',') do |row|
111
+ row_fiber.transfer row
112
+ end
113
+ end
114
+
115
+ row_fiber = Fiber.new do
116
+ headers = iterator.transfer
117
+ Fiber.yield headers.join(', ') # columns
118
+ first_row = iterator.transfer
119
+ schema = headers.map.with_index{ |col, index| "#{col} #{col_type(first_row[index])}" }.join(', ')
120
+ Fiber.yield schema # schema
121
+
122
+ Fiber.yield first_row # first_row
123
+
124
+ loop do
125
+ Fiber.yield iterator.transfer # other
126
+ end
127
+ end
128
+ end
129
+
130
+ TYPES = {
131
+ String => 'varchar(255)',
132
+ Fixnum => 'integer'
133
+ }
134
+
135
+ def col_type data
136
+ TYPES[data.class] || TYPES[String]
137
+ end
138
+
139
+ def data_mask_map data
140
+ row = Array.new(data.first.size, '?').join(', ')
141
+ rows = ("(#{row}), " * data.size)
142
+ rows.chomp!(', ')
143
+ rows
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,32 @@
1
+ module MmReader
2
+ class Query
3
+ attr_accessor :postal, :city, :state
4
+
5
+ def initialize postal, city, state
6
+ @postal = postal
7
+ @city = city
8
+ @state = state
9
+ end
10
+
11
+ def query_result
12
+ db = MmReader::Connection.get
13
+ result = db.execute('SELECT DISTINCT network FROM ip4_index WHERE postal_code = ?', [@postal])
14
+ return result if result.size > 0
15
+
16
+ result = db.execute('SELECT DISTINCT network FROM ip4_index WHERE city = ? and state = ?', [@city, @state])
17
+ return result if result.size > 0
18
+
19
+ result = db.execute('SELECT DISTINCT network FROM ip4_index WHERE city = ?', [@city])
20
+ return result if result.size > 0
21
+
22
+ result = db.execute('SELECT DISTINCT network FROM ip4_index WHERE state = ?', [@state])
23
+ return result if result.size > 0
24
+
25
+ return []
26
+ end
27
+ def result
28
+ query_result.flatten.map{ |mask| IPAddr.new(mask).to_range.map(&:to_s) }.flatten
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module MmReader
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ namespace :mm do
2
+ task :create do
3
+ raise "Вы должны указать путь к CSV базе MaxMind в переменной окружения MM_PATH" unless ENV[:MM_PATH]
4
+ raise "Не верно указан путь к XML базе ФИАС в переменной окружения MM_PATH" unless File.exist? ENV[:MM_PATH]
5
+ db = Converter.new(ENV[:MM_PATH])
6
+ db.to_sqlite
7
+ end
8
+
9
+ task :clear do
10
+ Converter.clear
11
+ end
12
+ end
data/mm_reader.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mm_reader/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mm_reader"
8
+ spec.version = MmReader::VERSION
9
+ spec.authors = ["Andrey Bolshov", "uno4ki"]
10
+ spec.email = ["asnow.dev@gmail.com", "i.have@no.mail"]
11
+
12
+ spec.summary = %q{Конвертор базы MaxMind.}
13
+ spec.description = %q{Конвертор базы MaxMind.}
14
+ spec.homepage = "http://github.com/the-furnish/mm_reader"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sqlite3", "~> 1.3"
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'ruby-prof'
27
+ spec.add_development_dependency 'pry-byebug'
28
+ spec.add_development_dependency 'pry-stack_explorer'
29
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mm_reader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrey Bolshov
8
+ - uno4ki
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sqlite3
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.12'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.12'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: pry
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: ruby-prof
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry-byebug
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: pry-stack_explorer
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: Конвертор базы MaxMind.
127
+ email:
128
+ - asnow.dev@gmail.com
129
+ - i.have@no.mail
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - lib/mm_reader.rb
142
+ - lib/mm_reader/connection.rb
143
+ - lib/mm_reader/converter.rb
144
+ - lib/mm_reader/query.rb
145
+ - lib/mm_reader/version.rb
146
+ - lib/tasks/convert.rake
147
+ - mm_reader.gemspec
148
+ homepage: http://github.com/the-furnish/mm_reader
149
+ licenses: []
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.6.6
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Конвертор базы MaxMind.
171
+ test_files: []