mm_reader 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.
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: []