ip_api_service 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 +7 -0
- data/.rubocop.yml +19 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +45 -0
- data/Makefile +8 -0
- data/README.md +79 -0
- data/Rakefile +12 -0
- data/ip_api_service.gemspec +35 -0
- data/lib/ip_api_service/ip_api_adapter.rb +97 -0
- data/lib/ip_api_service/ip_api_response_processor.rb +60 -0
- data/lib/ip_api_service/serialization.rb +52 -0
- data/lib/ip_api_service/version.rb +5 -0
- data/lib/ip_api_service.rb +67 -0
- data/lib/web_service/http_command.rb +50 -0
- data/lib/web_service/http_response_processor.rb +23 -0
- data/lib/web_service/uri_mapper.rb +13 -0
- data/sig/ip_api_service.rbs +4 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 508fddf5e55811a7f08ae47583e4627631d925c6d6a1e91c3536bd457b3856e4
|
4
|
+
data.tar.gz: 7d302d15e4d3e6e5334d8fafccd9cbb9c4428babe520f9b17c3aa5679e1b277f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60516003088fa1217aa0e8fce5d106744efeecb1a2569ccc1d8076ddc746309a1652ed78d8e578a8755dc74b8a500e74308100196f3c10cec4ea7b629fc70eb7
|
7
|
+
data.tar.gz: 0a2390cfe4396ca468630c8f4fc1bb520612c9fdc8321197881991443c7764ff46929efb06789c5a9c42226a03ce5c5aa8893a605a037c5f411f411db640bd72
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- "**/vendor/**/*"
|
4
|
+
- "**/*.gemspec"
|
5
|
+
- "spec/**"
|
6
|
+
NewCops: enable
|
7
|
+
TargetRubyVersion: 3.0.1
|
8
|
+
SuggestExtensions: false
|
9
|
+
|
10
|
+
Style/Documentation:
|
11
|
+
Enabled: false
|
12
|
+
Metrics/MethodLength:
|
13
|
+
Enabled: false
|
14
|
+
Style/AsciiComments:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
# NOTE: for rspec tests
|
18
|
+
Metrics/BlockLength:
|
19
|
+
IgnoredMethods: ['describe', 'context']
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in ip_api_service.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'rake', '~> 13.0'
|
9
|
+
|
10
|
+
gem 'nokogiri-happymapper'
|
11
|
+
|
12
|
+
gem 'addressable'
|
13
|
+
|
14
|
+
group :develop do
|
15
|
+
gem 'minitest', '~> 5.0'
|
16
|
+
gem 'minitest-power_assert'
|
17
|
+
gem 'webmock'
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ip_api_service (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
addressable (2.8.0)
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
11
|
+
crack (0.4.5)
|
12
|
+
rexml
|
13
|
+
hashdiff (1.0.1)
|
14
|
+
minitest (5.15.0)
|
15
|
+
minitest-power_assert (0.3.1)
|
16
|
+
minitest
|
17
|
+
power_assert (>= 1.1)
|
18
|
+
nokogiri (1.13.3-x86_64-linux)
|
19
|
+
racc (~> 1.4)
|
20
|
+
nokogiri-happymapper (0.9.0)
|
21
|
+
nokogiri (~> 1.5)
|
22
|
+
power_assert (2.0.1)
|
23
|
+
public_suffix (4.0.6)
|
24
|
+
racc (1.6.0)
|
25
|
+
rake (13.0.6)
|
26
|
+
rexml (3.2.5)
|
27
|
+
webmock (3.14.0)
|
28
|
+
addressable (>= 2.8.0)
|
29
|
+
crack (>= 0.3.2)
|
30
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
x86_64-linux
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
addressable
|
37
|
+
ip_api_service!
|
38
|
+
minitest (~> 5.0)
|
39
|
+
minitest-power_assert
|
40
|
+
nokogiri-happymapper
|
41
|
+
rake (~> 13.0)
|
42
|
+
webmock
|
43
|
+
|
44
|
+
BUNDLED WITH
|
45
|
+
2.3.7
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# IpApiService
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Ruby клинет для сервиса [ip-api.com](https://ip-api.com/). Сервис позволяет получать данные геолокации по ip адресу.
|
6
|
+
|
7
|
+
## Установка
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'ip-api-service'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install ip-api-service
|
20
|
+
|
21
|
+
## Использование
|
22
|
+
|
23
|
+
Вы можете получить данные геолокации ip адреса вызвав метод IpApiService.lookup и передав ему следующие параметры:
|
24
|
+
|
25
|
+
* ip - ip адрес
|
26
|
+
* fields - массив с нужными полями
|
27
|
+
* result_format - формат желаемого результата
|
28
|
+
* lang - язык
|
29
|
+
|
30
|
+
Результатом метода, при вызове с параметром result_format: :ipMetaInfo, будет объект.
|
31
|
+
Значения запрашиваемх полей будут доступны по геттеру с именем поля
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
info = IpApiService.lookup '8.8.8.8'
|
35
|
+
puts info.city
|
36
|
+
```
|
37
|
+
Во всех других случаях результатом метода будет строка
|
38
|
+
|
39
|
+
Доступные для запроса поля можно получить вызвав метод available_fields
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
IpApiService.available_fields
|
43
|
+
```
|
44
|
+
|
45
|
+
Поддерживаемые форматы результата - available_formats
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
IpApiService.available_formats
|
49
|
+
```
|
50
|
+
|
51
|
+
Доступные языки - available_languages
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
IpApiService.available_languages
|
55
|
+
```
|
56
|
+
|
57
|
+
Используя метод field_description(field) vожно получить описание поля
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
IpApiServiceIpApiService.field_description :region
|
61
|
+
```
|
62
|
+
|
63
|
+
Параметр ip для метода lookup обязательный, остальные можно установить по умолчанию
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
IpApiService.default_fields = %i(city country countryCode lat lon)
|
67
|
+
IpApiService.result_format = :json
|
68
|
+
IpApiService.default_language = :en
|
69
|
+
```
|
70
|
+
|
71
|
+
## Development
|
72
|
+
|
73
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
74
|
+
|
75
|
+
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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mike090/ip_api_service.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/ip_api_service/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ip_api_service"
|
7
|
+
spec.version = IpApiService::VERSION
|
8
|
+
spec.authors = ["mike09"]
|
9
|
+
spec.email = ["mike09@mail.ru"]
|
10
|
+
|
11
|
+
spec.summary = "API for ip-api.com"
|
12
|
+
spec.homepage = "https://github.com/mike090/ip_api_service"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/mike090/ip_api_service"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/mike090/ip_api_service"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
# Uncomment to register a new dependency of your gem
|
31
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
32
|
+
|
33
|
+
# For more information and examples about making a new gem, check out our
|
34
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
35
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../web_service/uri_mapper'
|
4
|
+
require_relative '../web_service/http_command'
|
5
|
+
require_relative 'ip_api_response_processor'
|
6
|
+
|
7
|
+
module IpApiService
|
8
|
+
# available meta fields
|
9
|
+
META_FIELDS = {
|
10
|
+
continent: { type: :string, description: 'Continent name' },
|
11
|
+
continentCode: { type: :string, description: 'Two-letter continent code' },
|
12
|
+
country: { type: :string, description: 'Country name' },
|
13
|
+
countryCode: { type: :string, description: 'Two-letter country code ISO 3166-1 alpha-2' },
|
14
|
+
region: { type: :string, description: 'Region/state short code (FIPS or ISO)' },
|
15
|
+
regionName: { type: :string, description: 'Region/state' },
|
16
|
+
city: { type: :string, description: 'City' },
|
17
|
+
district: { type: :string, description: 'District (subdivision of city)' },
|
18
|
+
zip: { type: :string, description: 'Zip code' },
|
19
|
+
lat: { type: :float, description: 'Latitude' },
|
20
|
+
lon: { type: :float, description: 'Longitude' },
|
21
|
+
timezone: { type: :string, description: 'Timezone (tz)' },
|
22
|
+
offset: { type: :integer, description: 'Timezone UTC DST offset in seconds' },
|
23
|
+
currency: { type: :string, description: 'National currency' },
|
24
|
+
isp: { type: :string, description: 'ISP name' },
|
25
|
+
org: { type: :string, description: 'Organization name' },
|
26
|
+
as: { type: :string,
|
27
|
+
description: "AS number and organization, separated by space (RIR). Empty for IP blocks \
|
28
|
+
'not being announced in BGP tables." },
|
29
|
+
asname: { type: :string,
|
30
|
+
description: 'AS name (RIR). Empty for IP blocks not being announced in BGP tables' },
|
31
|
+
reverse: { type: :string, description: 'Reverse DNS of the IP (can delay response)' },
|
32
|
+
mobile: { type: :boolean, description: 'Mobile (cellular) connection' },
|
33
|
+
proxy: { type: :boolean, description: 'Proxy, VPN or Tor exit address' },
|
34
|
+
hosting: { type: :boolean, description: 'Hosting, colocated or data center' }
|
35
|
+
}.freeze
|
36
|
+
private_constant :META_FIELDS
|
37
|
+
|
38
|
+
# service fields
|
39
|
+
SERVICE_FIELDS = {
|
40
|
+
status: :string,
|
41
|
+
message: :string,
|
42
|
+
query: :string
|
43
|
+
}.freeze
|
44
|
+
private_constant :SERVICE_FIELDS
|
45
|
+
|
46
|
+
FIELD_TYPES = META_FIELDS.transform_values do |field_scheme|
|
47
|
+
field_scheme[:type]
|
48
|
+
end.merge(SERVICE_FIELDS).freeze
|
49
|
+
private_constant :FIELD_TYPES
|
50
|
+
|
51
|
+
IP_API_COMMAND_TEMPLATE = 'http://ip-api.com/{format}/{ip}{?fields}{&lang}'
|
52
|
+
private_constant :IP_API_COMMAND_TEMPLATE
|
53
|
+
|
54
|
+
USER_AGENT = 'IpApiService/Ruby/1.0'
|
55
|
+
private_constant :USER_AGENT
|
56
|
+
|
57
|
+
ACCEPT_MIME_TYPES = {
|
58
|
+
json: 'application/json',
|
59
|
+
xml: 'application/xml',
|
60
|
+
csv: 'text/csv',
|
61
|
+
newline: 'text/plain',
|
62
|
+
php: 'text/php'
|
63
|
+
}.freeze
|
64
|
+
private_constant :ACCEPT_MIME_TYPES
|
65
|
+
|
66
|
+
class IpApiAdapter
|
67
|
+
def initialize
|
68
|
+
mapper = WebService::UriMapper
|
69
|
+
@command = WebService::HttpCommand.new mapper
|
70
|
+
end
|
71
|
+
|
72
|
+
def ip_meta_info(ip, fields, result_format, lang)
|
73
|
+
target_fields = SERVICE_FIELDS.keys + fields
|
74
|
+
target_format = result_format == :ipMetaInfo ? :xml : result_format
|
75
|
+
headers = prepare_headers target_format
|
76
|
+
response = @command.execute :get, IP_API_COMMAND_TEMPLATE, ip: ip, format: target_format, fields: target_fields,
|
77
|
+
lang: lang, headers: headers
|
78
|
+
return response_processor.process_response(response, target_format, fields) if result_format == :ipMetaInfo
|
79
|
+
|
80
|
+
response.body
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def response_processor
|
86
|
+
@response_processor ||= ResponseProcessor.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def prepare_headers(format)
|
90
|
+
{
|
91
|
+
'User-Agent' => USER_AGENT,
|
92
|
+
'Accept' => ACCEPT_MIME_TYPES[format],
|
93
|
+
'Accept-Encoding' => 'utf-8'
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../web_service/http_response_processor'
|
4
|
+
require_relative 'serialization'
|
5
|
+
|
6
|
+
module IpApiService
|
7
|
+
XML_RESPONSE_ROOT = 'query'
|
8
|
+
private_constant :XML_RESPONSE_ROOT
|
9
|
+
|
10
|
+
IP_API_SUCCESS_STATUS = 'success'
|
11
|
+
private_constant :IP_API_SUCCESS_STATUS
|
12
|
+
|
13
|
+
class ResponseProcessorError < StandardError; end
|
14
|
+
|
15
|
+
class ServiceError < StandardError
|
16
|
+
attr_reader :query, :service_message
|
17
|
+
|
18
|
+
def initialize(query, service_message)
|
19
|
+
@query = query
|
20
|
+
@service_message = service_message
|
21
|
+
super 'Service return fail result'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ResponseProcessor < WebService::HttpResponseProcessor
|
26
|
+
def process_response(response, content_type, fields)
|
27
|
+
super response
|
28
|
+
ip_api_result = service_parser(content_type).parse response.body
|
29
|
+
unless ip_api_result.status == IP_API_SUCCESS_STATUS
|
30
|
+
raise ServiceError.new(ip_api_result.query,
|
31
|
+
ip_api_result.message)
|
32
|
+
end
|
33
|
+
|
34
|
+
parser(content_type, fields).parse response.body
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parser(content_type, fields)
|
40
|
+
case content_type
|
41
|
+
when :xml
|
42
|
+
fields = FIELD_TYPES.slice(*fields) unless fields.is_a? Hash
|
43
|
+
Serialization.xml_parser XML_RESPONSE_ROOT, fields
|
44
|
+
when :json
|
45
|
+
Serialization.json_parser fields
|
46
|
+
else
|
47
|
+
raise ResponseProcessorError 'Parser not implemented'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def service_parser(content_type)
|
52
|
+
case content_type
|
53
|
+
when :xml
|
54
|
+
@xml_service_parser || parser(:xml, SERVICE_FIELDS)
|
55
|
+
when :json
|
56
|
+
@json_service_parser || parser(:json, SERVICE_FIELDS)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'happymapper'
|
4
|
+
|
5
|
+
module IpApiService
|
6
|
+
module Serialization
|
7
|
+
module_function
|
8
|
+
|
9
|
+
class UnknownTypeError < StandardError; end
|
10
|
+
|
11
|
+
# подскажи, как обозвать
|
12
|
+
@type_tra_ta_ta = {
|
13
|
+
string: String,
|
14
|
+
integer: Integer,
|
15
|
+
float: Float,
|
16
|
+
time: Time,
|
17
|
+
date: Date,
|
18
|
+
datetime: DateTime,
|
19
|
+
boolean: HappyMapper::Boolean
|
20
|
+
}
|
21
|
+
|
22
|
+
class JsonParser
|
23
|
+
def initialize(fields)
|
24
|
+
fields = fields.keys if fields.is_a? Hash
|
25
|
+
@fields = fields
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse(json_content)
|
29
|
+
raw = JSON.parse(json_content).transform_keys(&:to_sym).slice(*@fields)
|
30
|
+
Struct.new(*raw.keys).new(*raw.values_at(*raw.keys))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def json_parser(fields)
|
35
|
+
JsonParser.new fields
|
36
|
+
end
|
37
|
+
|
38
|
+
def xml_parser(root, fields)
|
39
|
+
parser = Class.new.include HappyMapper
|
40
|
+
parser.tag root
|
41
|
+
fields.each do |field, field_type|
|
42
|
+
begin
|
43
|
+
field_type = @type_tra_ta_ta.fetch field_type
|
44
|
+
rescue KeyError
|
45
|
+
raise UnknownTypeError "Unknown field type :#{field_type}"
|
46
|
+
end
|
47
|
+
parser.element field, field_type
|
48
|
+
end
|
49
|
+
parser
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './ip_api_service/ip_api_adapter'
|
4
|
+
require 'resolv'
|
5
|
+
|
6
|
+
module IpApiService
|
7
|
+
extend self
|
8
|
+
|
9
|
+
AVAILABLE_LANGUAGES = %i[en de es pt-BR fr ja zh-CN ru].freeze
|
10
|
+
|
11
|
+
AVAILABLE_FORMATS = %i[ipMetaInfo json xml csv line php].freeze
|
12
|
+
|
13
|
+
DEFAULT_FIELDS = %i[country countryCode region regionName city zip lat lon timezone isp org as].freeze
|
14
|
+
private_constant :AVAILABLE_LANGUAGES, :AVAILABLE_FORMATS, :DEFAULT_FIELDS
|
15
|
+
|
16
|
+
@default_language = :en
|
17
|
+
@custom_default_fields = DEFAULT_FIELDS
|
18
|
+
@result_format = :ipMetaInfo
|
19
|
+
|
20
|
+
attr_reader :available_langviges, :available_formats, :default_language, :result_format
|
21
|
+
|
22
|
+
def available_fields
|
23
|
+
META_FIELDS.keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def available_languages
|
27
|
+
AVAILABLE_LANGUAGES
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_language=(value)
|
31
|
+
@default_language = AVAILABLE_LANGUAGES.include?(value) ? value : :en
|
32
|
+
end
|
33
|
+
|
34
|
+
def result_format=(value)
|
35
|
+
@result_format = AVAILABLE_FORMATS.include?(value) ? value : metaInfo
|
36
|
+
end
|
37
|
+
|
38
|
+
def field_description(field)
|
39
|
+
META_FIELDS[field][:description]
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_fields=(value)
|
43
|
+
value &= META_FIELDS.keys
|
44
|
+
@custom_default_fields = value.empty? ? @default_fields : value
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_fields
|
48
|
+
@custom_default_fields
|
49
|
+
end
|
50
|
+
|
51
|
+
def lookup(ip, fields: default_fields, result_format: @result_format, lang: @default_language)
|
52
|
+
raise ArgumentError, 'Unavailable result_format' unless AVAILABLE_FORMATS.include? result_format
|
53
|
+
raise ArgumentError, 'Unavailable language' unless AVAILABLE_LANGUAGES.include? lang
|
54
|
+
|
55
|
+
resolv = ip =~ Resolv::IPv4::Regex ? true : (ip =~ Resolv::IPv6::Regex)
|
56
|
+
raise ArgumentError, 'Invalid ip addess' unless resolv
|
57
|
+
|
58
|
+
fields = default_fields if fields.empty?
|
59
|
+
adapter.ip_meta_info ip, fields, result_format, lang
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def adapter
|
65
|
+
@adapter ||= IpApiAdapter.new
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module WebService
|
6
|
+
class HttpUnknownMethodError < StandardError; end
|
7
|
+
class ConnectionError < StandardError; end
|
8
|
+
|
9
|
+
HTTP_COMMAND_METHODS = {
|
10
|
+
get: Net::HTTP::Get,
|
11
|
+
post: Net::HTTP::Post,
|
12
|
+
head: Net::HTTP::Head,
|
13
|
+
patch: Net::HTTP::Patch,
|
14
|
+
put: Net::HTTP::Put,
|
15
|
+
proppatch: Net::HTTP::Proppatch,
|
16
|
+
lock: Net::HTTP::Lock,
|
17
|
+
unlock: Net::HTTP::Unlock,
|
18
|
+
options: Net::HTTP::Options,
|
19
|
+
propfind: Net::HTTP::Propfind,
|
20
|
+
delete: Net::HTTP::Delete,
|
21
|
+
move: Net::HTTP::Move,
|
22
|
+
copy: Net::HTTP::Copy,
|
23
|
+
mkol: Net::HTTP::Mkcol,
|
24
|
+
trace: Net::HTTP::Trace
|
25
|
+
}.freeze
|
26
|
+
private_constant :HTTP_COMMAND_METHODS
|
27
|
+
|
28
|
+
class HttpCommand
|
29
|
+
def initialize(mapper)
|
30
|
+
@mapper = mapper
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute(command, template, **params) # rubocop:disable Metrics/AbcSize
|
34
|
+
request_class = HTTP_COMMAND_METHODS[command]
|
35
|
+
raise HttpUnknownMethodError, "Unknown Http command: #{command}" unless request_class
|
36
|
+
|
37
|
+
headers = (params.delete :headers) || {}
|
38
|
+
body = params.delete :body
|
39
|
+
body = nil unless request_class::REQUEST_HAS_BODY
|
40
|
+
uri = @mapper.map_template template, **params
|
41
|
+
request = request_class.new uri, {}
|
42
|
+
headers.each { |header, value| request[header] = value }
|
43
|
+
begin
|
44
|
+
Net::HTTP.start(uri.host, uri.port) { |http| http.request request, body }
|
45
|
+
rescue StandardError
|
46
|
+
raise ConnectionError, 'Service unavailable'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module WebService
|
6
|
+
class HttpServiceError < StandardError
|
7
|
+
attr_reader :status_code, :service_message
|
8
|
+
|
9
|
+
def initialize(status_code, service_message)
|
10
|
+
@status_code = status_code
|
11
|
+
@service_message = service_message
|
12
|
+
super 'Service error'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class HttpResponseProcessor
|
17
|
+
def process_response(response)
|
18
|
+
raise HttpServiceError.new(response.code, response.message) unless response.is_a? Net::HTTPSuccess
|
19
|
+
|
20
|
+
response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ip_api_service
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mike09
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- mike09@mail.ru
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".rubocop.yml"
|
21
|
+
- Gemfile
|
22
|
+
- Gemfile.lock
|
23
|
+
- Makefile
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- ip_api_service.gemspec
|
27
|
+
- lib/ip_api_service.rb
|
28
|
+
- lib/ip_api_service/ip_api_adapter.rb
|
29
|
+
- lib/ip_api_service/ip_api_response_processor.rb
|
30
|
+
- lib/ip_api_service/serialization.rb
|
31
|
+
- lib/ip_api_service/version.rb
|
32
|
+
- lib/web_service/http_command.rb
|
33
|
+
- lib/web_service/http_response_processor.rb
|
34
|
+
- lib/web_service/uri_mapper.rb
|
35
|
+
- sig/ip_api_service.rbs
|
36
|
+
homepage: https://github.com/mike090/ip_api_service
|
37
|
+
licenses: []
|
38
|
+
metadata:
|
39
|
+
homepage_uri: https://github.com/mike090/ip_api_service
|
40
|
+
source_code_uri: https://github.com/mike090/ip_api_service
|
41
|
+
changelog_uri: https://github.com/mike090/ip_api_service
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.6.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubygems_version: 3.3.7
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: API for ip-api.com
|
61
|
+
test_files: []
|