fluent-plugin-geoip-kk 1.0.0 → 1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed458000a7ed8af776a38bf822027286ea3770a8f0b5668083419bd79516304b
4
- data.tar.gz: d988309e6a245f51bf670bbc8d6d42dfafb087546a0ccb183e55a3926adb6459
3
+ metadata.gz: 3343f0a6464f46969141b4c6dbc731bf58ff1bddabeecd0f55bfad2a4c21b842
4
+ data.tar.gz: 7c90087650a115cd023d3cde331d7504ee0540432747554e2150abd6d0ea780d
5
5
  SHA512:
6
- metadata.gz: e45cca487405e1c11fb97a3866f9f9c5d20d724ee4285432b78b1ed097de16464532455972ee7222a8b1d3d538d95e849c899bc5b8bfc8501e6d76b24a6e508b
7
- data.tar.gz: dc58642c973dbd96d887fc1160521feaa84372697dfbe812043bc9f11763cfa03551b0095bec43f284fd2fa5029f5590321668fd43bb31229dfcf73d937a4777
6
+ metadata.gz: dfd08a491d65964d1ed9ec4927ed1041f07f5a376b91b36cabf4a18c430854a7e1241fb3cbc73c65d9eb1d912f9f509659939dae5223cc4e0a63ca9cfafa6855
7
+ data.tar.gz: 4f534578358e07d02a193f9019c143304e8c3ef4997a22bf14f52ce8ec384884b804a12d9130ab84c281c4d34ec873dc270f63c216a5a6c6c8246613913f36df
@@ -0,0 +1,34 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ jobs:
10
+ build:
11
+ name: Build + Publish
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+ packages: write
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: '3.2' # 你可以指定 3.1、3.2、3.3 等
24
+
25
+ - name: Publish to RubyGems
26
+ run: |
27
+ mkdir -p $HOME/.gem
28
+ touch $HOME/.gem/credentials
29
+ chmod 0600 $HOME/.gem/credentials
30
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
31
+ gem build *.gemspec
32
+ gem push *.gem
33
+ env:
34
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"
data/README.md CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  [Fluentd](http://fluentd.org) filter plugin to add geoip.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/fluent-plugin-geoip-kk.svg)](https://badge.fury.io/rb/fluent-plugin-geoip-kk)
6
+ [![Ruby](https://github.com/kevin197011/fluent-plugin-geoip-kk/actions/workflows/gem-push.yml/badge.svg)](https://github.com/kevin197011/fluent-plugin-geoip-kk/actions/workflows/gem-push.yml)
5
7
 
6
8
  ## Requirements
7
9
 
8
10
  | fluent-plugin-geoip-kk | fluentd | ruby |
9
11
  |----------------------------|------------|--------|
10
- | >= 1.0.0 | >= v0.14.0 | >= 2.1 |
12
+ | >= 1.0.1 | >= v0.14.0 | >= 2.1 |
11
13
  | < 1.0.0 | >= v0.12.0 | >= 1.9 |
12
14
 
13
15
 
@@ -94,14 +96,32 @@ It is able to customize fields with placeholder.
94
96
  * See [benchmark](test/bench_geoip_filter.rb).
95
97
 
96
98
 
97
- ## TODO
99
+ ## Development
98
100
 
99
- * patches welcome!
101
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
100
102
 
103
+ To install this gem onto your local machine, run `bundle exec rake install`.
104
+
105
+ ## Publishing
106
+
107
+ This gem uses GitHub Actions for automated publishing. To publish a new version:
108
+
109
+ 1. Update the version number in `fluent-plugin-geoip-kk.gemspec`
110
+ 2. Commit the changes:
111
+ ```bash
112
+ git add fluent-plugin-geoip-kk.gemspec
113
+ git commit -m "Bump version to x.x.x"
114
+ ```
115
+ 3. Create and push a new tag:
116
+ ```bash
117
+ git tag vx.x.x
118
+ git push origin main vx.x.x
119
+ ```
120
+ 4. The GitHub Action will automatically build and publish the gem to RubyGems.org
101
121
 
102
122
  ## Contributing
103
123
 
104
- 1. Fork it ( https://github.com/bungoume/fluent-plugin-geoip-filter/fork )
124
+ 1. Fork it ( https://github.com/kevin197011/fluent-plugin-geoip-kk/fork )
105
125
  2. Create your feature branch (`git checkout -b my-new-feature`)
106
126
  3. Commit your changes (`git commit -am 'Add some feature'`)
107
127
  4. Push to the branch (`git push origin my-new-feature`)
@@ -5,12 +5,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'fluent-plugin-geoip-kk'
8
- spec.version = '1.0.0'
8
+ spec.version = '1.0.2'
9
9
  spec.authors = ['kevin197011']
10
10
  spec.email = ['kevin197011@outlook.com']
11
11
  spec.homepage = 'https://github.com/kevin197011/fluent-plugin-geoip-kk'
12
12
  spec.summary = 'Fluentd filter plugin to add geoip'
13
- spec.description = spec.summary
13
+ spec.description = 'A Fluentd filter plugin that adds GeoIP information to records. Supports both legacy GeoIP and GeoIP2 databases, with LRU caching for improved performance.'
14
14
  spec.license = 'Apache-2.0'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -19,10 +19,13 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'fluentd', ['>= 0.14.0', '< 2']
22
- spec.add_runtime_dependency 'geoip', '>= 0.1.22'
23
- spec.add_runtime_dependency 'lru_redux', '>= 1.0.0'
24
- spec.add_runtime_dependency 'maxminddb', '>= 1.5.0'
25
- spec.add_development_dependency 'bundler'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'test-unit' if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
22
+ spec.add_runtime_dependency 'geoip', '~> 0.1', '>= 0.1.22'
23
+ spec.add_runtime_dependency 'lru_redux', '~> 1.0', '>= 1.0.0'
24
+ spec.add_runtime_dependency 'maxminddb', '~> 1.5', '>= 1.5.0'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 2.0'
27
+ spec.add_development_dependency 'rake', '~> 13.0'
28
+ spec.add_development_dependency 'test-unit', '~> 3.0' if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
29
+
30
+ spec.required_ruby_version = '>= 2.1.0'
28
31
  end
@@ -3,45 +3,93 @@
3
3
  require 'maxminddb'
4
4
  require 'lru_redux'
5
5
  require 'fluent/plugin/filter'
6
+ require 'ipaddr'
6
7
 
7
8
  module Fluent
8
9
  module Plugin
9
10
  class GeoipFilter < Filter
10
11
  Fluent::Plugin.register_filter('geoip', self)
11
12
 
12
- def initialize
13
- @geoip_cache = LruRedux::Cache.new(8192)
14
- super
15
- end
13
+ helpers :record_accessor
16
14
 
15
+ # Default cache size (number of IP addresses to cache)
16
+ DEFAULT_CACHE_SIZE = 8192
17
+ DEFAULT_CACHE_TTL = 3600 # 1 hour in seconds
18
+
19
+ desc 'Path to the GeoIP database file'
17
20
  config_param :database_path, :string, default: "#{File.dirname(__FILE__)}/../../../data/GeoLite2-City.mmdb"
21
+
22
+ desc 'Field name that contains the IP address'
18
23
  config_param :key_name, :string, default: 'client_ip'
24
+
25
+ desc 'Output field name to store GeoIP data'
19
26
  config_param :out_key, :string, default: 'geo'
27
+
28
+ desc 'Flatten the GeoIP data structure'
20
29
  config_param :flatten, :bool, default: false
21
30
 
31
+ desc 'Size of the LRU cache'
32
+ config_param :cache_size, :integer, default: DEFAULT_CACHE_SIZE
33
+
34
+ desc 'TTL for cached items in seconds'
35
+ config_param :cache_ttl, :integer, default: DEFAULT_CACHE_TTL
36
+
37
+ desc 'Skip adding GeoIP data for private IP addresses'
38
+ config_param :skip_private_ip, :bool, default: true
39
+
40
+ def initialize
41
+ super
42
+ @geoip = nil
43
+ @ip_accessor = nil
44
+ end
45
+
22
46
  def configure(conf)
23
47
  super
48
+
49
+ unless File.exist?(@database_path)
50
+ raise Fluent::ConfigError, "GeoIP database file '#{@database_path}' does not exist"
51
+ end
52
+
53
+ # Initialize MaxMindDB
24
54
  begin
25
55
  @geoip = MaxMindDB.new(@database_path)
26
56
  rescue StandardError => e
27
- log.warn 'Failed to configure parser. Use default pattern.', error_class: e.class, error: e.message
28
- log.warn_backtrace
57
+ raise Fluent::ConfigError, "Failed to load GeoIP database: #{e.message}"
29
58
  end
59
+
60
+ # Initialize IP field accessor
61
+ @ip_accessor = record_accessor_create(@key_name)
62
+
63
+ # Initialize cache with TTL support
64
+ @geoip_cache = LruRedux::TTL::Cache.new(@cache_size, @cache_ttl)
65
+
66
+ log.info "Initialized GeoIP filter", database: @database_path, cache_size: @cache_size, cache_ttl: @cache_ttl
30
67
  end
31
68
 
32
- def filter(_tag, _time, record)
33
- ip_addr = record[@key_name]
69
+ def filter(tag, time, record)
70
+ ip_addr = @ip_accessor.call(record)
71
+ return record if ip_addr.nil? || ip_addr.empty? || ip_addr == '-'
34
72
 
35
- return record if ip_addr.nil? || ip_addr == '-'
73
+ begin
74
+ ip = IPAddr.new(ip_addr)
75
+ return record if @skip_private_ip && ip.private?
76
+ rescue IPAddr::InvalidAddressError => e
77
+ log.debug "Invalid IP address", ip: ip_addr, error: e.message
78
+ return record
79
+ end
36
80
 
37
81
  geo_ip = @geoip_cache.getset(ip_addr) { get_geoip(ip_addr) }
82
+ return record if geo_ip.empty?
38
83
 
39
- if flatten
40
- record.merge! hash_flatten(geo_ip, [@out_key])
84
+ if @flatten
85
+ record.merge!(hash_flatten(geo_ip, [@out_key]))
41
86
  else
42
87
  record[@out_key] = geo_ip
43
88
  end
44
89
 
90
+ record
91
+ rescue StandardError => e
92
+ log.error "Failed to process GeoIP lookup", error_class: e.class, error: e.message, tag: tag, time: time
45
93
  record
46
94
  end
47
95
 
@@ -49,24 +97,73 @@ module Fluent
49
97
 
50
98
  def get_geoip(ip_addr)
51
99
  geo_ip = @geoip.lookup(ip_addr)
52
- data = {}
53
- return data if geo_ip.nil? || ip_addr == '-'
54
-
55
- data['coordinates'] = [geo_ip.location.longitude, geo_ip.location.latitude] if geo_ip.location
56
- data['country_code'] = geo_ip.country.iso_code if geo_ip.country
57
- data['city'] = geo_ip.city.name if geo_ip.city
58
- data['region_code'] = geo_ip.subdivisions.first.iso_code if geo_ip.subdivisions.any?
59
- data
100
+ return {} if geo_ip.nil?
101
+
102
+ {
103
+ 'coordinates' => get_coordinates(geo_ip),
104
+ 'country' => get_country_info(geo_ip),
105
+ 'city' => get_city_info(geo_ip),
106
+ 'region' => get_region_info(geo_ip),
107
+ 'postal' => get_postal_info(geo_ip),
108
+ 'timezone' => geo_ip.location&.time_zone
109
+ }.compact
110
+ end
111
+
112
+ def get_coordinates(geo_ip)
113
+ return nil unless geo_ip.location
114
+
115
+ {
116
+ 'latitude' => geo_ip.location.latitude,
117
+ 'longitude' => geo_ip.location.longitude,
118
+ 'accuracy_radius' => geo_ip.location.accuracy_radius
119
+ }.compact
120
+ end
121
+
122
+ def get_country_info(geo_ip)
123
+ return nil unless geo_ip.country
124
+
125
+ {
126
+ 'code' => geo_ip.country.iso_code,
127
+ 'name' => geo_ip.country.name
128
+ }.compact
129
+ end
130
+
131
+ def get_city_info(geo_ip)
132
+ return nil unless geo_ip.city
133
+
134
+ {
135
+ 'name' => geo_ip.city.name,
136
+ 'confidence' => geo_ip.city.confidence
137
+ }.compact
138
+ end
139
+
140
+ def get_region_info(geo_ip)
141
+ return nil unless geo_ip.subdivisions&.any?
142
+
143
+ subdivision = geo_ip.subdivisions.first
144
+ {
145
+ 'code' => subdivision.iso_code,
146
+ 'name' => subdivision.name
147
+ }.compact
148
+ end
149
+
150
+ def get_postal_info(geo_ip)
151
+ return nil unless geo_ip.postal
152
+
153
+ {
154
+ 'code' => geo_ip.postal.code,
155
+ 'confidence' => geo_ip.postal.confidence
156
+ }.compact
60
157
  end
61
158
 
62
- def hash_flatten(a, keys = [])
159
+ def hash_flatten(hash, keys = [])
63
160
  ret = {}
64
- a.each do |k, v|
65
- ks = keys + [k]
66
- if v.instance_of?(Hash)
67
- ret.merge!(hash_flatten(v, ks))
161
+ hash.each do |k, v|
162
+ key_chain = keys + [k]
163
+ if v.is_a?(Hash)
164
+ ret.merge!(hash_flatten(v, key_chain))
68
165
  else
69
- ret.merge!({ ks.join('_') => v })
166
+ ret[key_chain.join('_')] = v
70
167
  end
71
168
  end
72
169
  ret
data/push.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ system 'rake'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-geoip-kk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - kevin197011
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-06 00:00:00.000000000 Z
11
+ date: 2025-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -34,6 +34,9 @@ dependencies:
34
34
  name: geoip
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.1'
37
40
  - - ">="
38
41
  - !ruby/object:Gem::Version
39
42
  version: 0.1.22
@@ -41,6 +44,9 @@ dependencies:
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.1'
44
50
  - - ">="
45
51
  - !ruby/object:Gem::Version
46
52
  version: 0.1.22
@@ -48,6 +54,9 @@ dependencies:
48
54
  name: lru_redux
49
55
  requirement: !ruby/object:Gem::Requirement
50
56
  requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.0'
51
60
  - - ">="
52
61
  - !ruby/object:Gem::Version
53
62
  version: 1.0.0
@@ -55,6 +64,9 @@ dependencies:
55
64
  prerelease: false
56
65
  version_requirements: !ruby/object:Gem::Requirement
57
66
  requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
58
70
  - - ">="
59
71
  - !ruby/object:Gem::Version
60
72
  version: 1.0.0
@@ -62,6 +74,9 @@ dependencies:
62
74
  name: maxminddb
63
75
  requirement: !ruby/object:Gem::Requirement
64
76
  requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '1.5'
65
80
  - - ">="
66
81
  - !ruby/object:Gem::Version
67
82
  version: 1.5.0
@@ -69,6 +84,9 @@ dependencies:
69
84
  prerelease: false
70
85
  version_requirements: !ruby/object:Gem::Requirement
71
86
  requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.5'
72
90
  - - ">="
73
91
  - !ruby/object:Gem::Version
74
92
  version: 1.5.0
@@ -76,70 +94,70 @@ dependencies:
76
94
  name: bundler
77
95
  requirement: !ruby/object:Gem::Requirement
78
96
  requirements:
79
- - - ">="
97
+ - - "~>"
80
98
  - !ruby/object:Gem::Version
81
- version: '0'
99
+ version: '2.0'
82
100
  type: :development
83
101
  prerelease: false
84
102
  version_requirements: !ruby/object:Gem::Requirement
85
103
  requirements:
86
- - - ">="
104
+ - - "~>"
87
105
  - !ruby/object:Gem::Version
88
- version: '0'
106
+ version: '2.0'
89
107
  - !ruby/object:Gem::Dependency
90
108
  name: rake
91
109
  requirement: !ruby/object:Gem::Requirement
92
110
  requirements:
93
- - - ">="
111
+ - - "~>"
94
112
  - !ruby/object:Gem::Version
95
- version: '0'
113
+ version: '13.0'
96
114
  type: :development
97
115
  prerelease: false
98
116
  version_requirements: !ruby/object:Gem::Requirement
99
117
  requirements:
100
- - - ">="
118
+ - - "~>"
101
119
  - !ruby/object:Gem::Version
102
- version: '0'
120
+ version: '13.0'
103
121
  - !ruby/object:Gem::Dependency
104
122
  name: test-unit
105
123
  requirement: !ruby/object:Gem::Requirement
106
124
  requirements:
107
- - - ">="
125
+ - - "~>"
108
126
  - !ruby/object:Gem::Version
109
- version: '0'
127
+ version: '3.0'
110
128
  type: :development
111
129
  prerelease: false
112
130
  version_requirements: !ruby/object:Gem::Requirement
113
131
  requirements:
114
- - - ">="
132
+ - - "~>"
115
133
  - !ruby/object:Gem::Version
116
- version: '0'
117
- description: Fluentd filter plugin to add geoip
134
+ version: '3.0'
135
+ description: A Fluentd filter plugin that adds GeoIP information to records. Supports
136
+ both legacy GeoIP and GeoIP2 databases, with LRU caching for improved performance.
118
137
  email:
119
138
  - kevin197011@outlook.com
120
139
  executables: []
121
140
  extensions: []
122
141
  extra_rdoc_files: []
123
142
  files:
124
- - ".gitattributes"
143
+ - ".github/workflows/gem-push.yml"
125
144
  - ".gitignore"
126
145
  - Gemfile
127
146
  - LICENSE
128
147
  - README.md
129
148
  - Rakefile
130
149
  - data/GeoLite2-City.mmdb
131
- - data/GeoLiteCity.dat
132
150
  - fluent-plugin-geoip-kk.gemspec
133
151
  - lib/fluent/plugin/filter_geoip.rb
152
+ - push.rb
134
153
  - test.rb
135
154
  - test/bench_geoip_filter.rb
136
155
  - test/helper.rb
137
- - test/plugin/test_filter_geoip.rb
138
156
  homepage: https://github.com/kevin197011/fluent-plugin-geoip-kk
139
157
  licenses:
140
158
  - Apache-2.0
141
159
  metadata: {}
142
- post_install_message:
160
+ post_install_message:
143
161
  rdoc_options: []
144
162
  require_paths:
145
163
  - lib
@@ -147,18 +165,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
165
  requirements:
148
166
  - - ">="
149
167
  - !ruby/object:Gem::Version
150
- version: '0'
168
+ version: 2.1.0
151
169
  required_rubygems_version: !ruby/object:Gem::Requirement
152
170
  requirements:
153
171
  - - ">="
154
172
  - !ruby/object:Gem::Version
155
173
  version: '0'
156
174
  requirements: []
157
- rubygems_version: 3.5.18
158
- signing_key:
175
+ rubygems_version: 3.4.19
176
+ signing_key:
159
177
  specification_version: 4
160
178
  summary: Fluentd filter plugin to add geoip
161
179
  test_files:
162
180
  - test/bench_geoip_filter.rb
163
181
  - test/helper.rb
164
- - test/plugin/test_filter_geoip.rb
data/.gitattributes DELETED
@@ -1 +0,0 @@
1
- *.dat filter=lfs diff=lfs merge=lfs -text
data/data/GeoLiteCity.dat DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:25bce9e7f5fe9ffd12afa663c2e57b311a4286549a858965c4ff01596f604c8a
3
- size 19148749
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'helper'
4
-
5
- class GeoipFilterTest < Test::Unit::TestCase
6
- def setup
7
- Fluent::Test.setup
8
- end
9
-
10
- CONFIG = %(
11
- @type geoip
12
- key_name client_ip
13
- out_key geo
14
- )
15
-
16
- def create_driver(conf = CONFIG)
17
- Fluent::Test::Driver::Filter.new(Fluent::Plugin::GeoipFilter).configure(conf)
18
- end
19
-
20
- def test_configure
21
- d = create_driver(CONFIG)
22
- assert_equal 'client_ip', d.instance.config['key_name']
23
- assert_equal 'geo', d.instance.config['out_key']
24
- end
25
-
26
- def test_emit
27
- d1 = create_driver(CONFIG)
28
- ip_iddr = '93.184.216.34'
29
-
30
- d1.run(default_tag: 'test') do
31
- d1.feed({ 'client_ip' => ip_iddr })
32
- end
33
- emits = d1.filtered
34
- assert_equal 1, emits.length
35
- geo_object = emits[0][1]['geo']
36
- assert_equal [-70.8228, 42.150800000000004], geo_object['coordinates']
37
- assert_equal 'US', geo_object['country_code']
38
- assert_equal 'Norwell', geo_object['city']
39
- assert_equal 'MA', geo_object['region_code']
40
- end
41
-
42
- def test_emit_flatten
43
- d1 = create_driver(%(
44
- @type geoip
45
- key_name ip_iddr
46
- flatten
47
- ))
48
- ip_iddr = '93.184.216.34'
49
-
50
- d1.run(default_tag: 'test') do
51
- d1.feed({ 'ip_iddr' => ip_iddr })
52
- end
53
-
54
- emits = d1.filtered
55
- assert_equal 1, emits.length
56
- geo_object = emits[0][1]
57
- assert_equal [-70.8228, 42.150800000000004], geo_object['geo_coordinates']
58
- assert_equal 'US', geo_object['geo_country_code']
59
- assert_equal 'Norwell', geo_object['geo_city']
60
- assert_equal 'MA', geo_object['geo_region_code']
61
- end
62
- end