loqate 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47002dc81cfda694969dd28475fbe196b99f00e72df5bd6ef5cfe05172d853f1
4
- data.tar.gz: 59bfa426b7132b601e84ea52dacfcfc9650b428beb1e7142738faf0743166e69
3
+ metadata.gz: e1f1396b79c0115311d766dd1b8fb7f4b0325596d27293d1b1d4af7c0d3d4a29
4
+ data.tar.gz: e2a275316138017ec971b0a4412e8cc92a872983a523067c33c1ea2c88785de3
5
5
  SHA512:
6
- metadata.gz: 9fe20f3fbe659b7116e648ff538652be8417693d825fa4c8f73b50898ddd95fbad9c9d071e9eb506b0957ddfb01dcb60d13175c9fc54d2648a7a08d2ae79d92a
7
- data.tar.gz: 51c9b923de5ec21e2c3ad61de941a114d2d52a690d7f8614ae257ae952fef0175486a85c66d627212e70af5b4c4881d8808e199871bd1b44b59ddc88ab4f3685
6
+ metadata.gz: abf8817900f777554ded8b558dd43e4f94ebead001fb53c9ac012e8d462f7b4c3926e86a00ea1ac3d5b9230376c19af03931d08c4a0c684bf137369dd8ce332b
7
+ data.tar.gz: 470d80e084b048173e3ae34d230227308c2855fbb2fa26be93897464ba9c9fd9a0490f0ee9062302107315f4ecb168db50c39e6799af009e492af223b2dad2b2
data/.gitignore CHANGED
@@ -10,3 +10,5 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ .api_key
14
+ /Gemfile.lock
data/.rubocop.yml CHANGED
@@ -5,11 +5,16 @@ AllCops:
5
5
 
6
6
  Metrics/BlockLength:
7
7
  Exclude:
8
+ - spec/**/*_spec.rb
8
9
  - loqate.gemspec
9
10
 
10
11
  Metrics/LineLength:
11
12
  Max: 120
12
13
 
14
+ Naming/MethodName:
15
+ Exclude:
16
+ - lib/loqate/result.rb
17
+
13
18
  # ----------------------- Style -----------------------
14
19
 
15
20
  Style/FrozenStringLiteralComment:
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.1
data/.travis.yml CHANGED
@@ -13,6 +13,7 @@ rvm:
13
13
  before_install: gem install bundler -v 1.16.5
14
14
 
15
15
  before_script:
16
+ - echo 'fake-key' > .api_key
16
17
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
17
18
  - chmod +x ./cc-test-reporter
18
19
  - ./cc-test-reporter before-build
data/.yardstick.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  threshold: 100
2
2
  rules:
3
3
  ApiTag::Presence:
4
- enabled: true
4
+ enabled: false
5
5
  exclude: []
6
6
  ApiTag::Inclusion:
7
- enabled: true
7
+ enabled: false
8
8
  exclude: []
9
9
  ApiTag::ProtectedMethod:
10
10
  enabled: true
@@ -14,18 +14,93 @@ rules:
14
14
  exclude: []
15
15
  ExampleTag:
16
16
  enabled: true
17
- exclude: []
17
+ exclude:
18
+ - Loqate::Address#==
19
+ - Loqate::Address#attributes
20
+ - Loqate::Address#id
21
+ - Loqate::Address#initialize
22
+ - Loqate::Address#type
23
+ - Loqate::Address#text
24
+ - Loqate::Address#highlight
25
+ - Loqate::Address#description
26
+ - Loqate::AddressGateway#initialize
27
+ - Loqate::APIResult#errors?
28
+ - Loqate::APIResult#items
29
+ - Loqate::APIResult#initialize
30
+ - Loqate::Configuration#api_key
31
+ - Loqate::Configuration#host
32
+ - Loqate::Configuration#language
33
+ - Loqate::Configuration#initialize
34
+ - Loqate::DetailedAddress#initialize
35
+ - Loqate::DetailedAddress#==
36
+ - Loqate::Error#id
37
+ - Loqate::Error#description
38
+ - Loqate::Error#cause
39
+ - Loqate::Error#resolution
40
+ - Loqate::Error#initialize
41
+ - Loqate::Gateway#config
42
+ - Loqate::Gateway#initialize
43
+ - Loqate::Gateway#address
44
+ - Loqate::Result::Success#success?
45
+ - Loqate::Result::Success#failure?
46
+ - Loqate::Result::Failure#success?
47
+ - Loqate::Result::Failure#failure?
48
+ - Loqate::Result#code
49
+ - Loqate::Result#value
18
50
  ReturnTag:
19
51
  enabled: true
20
- exclude: []
52
+ exclude:
53
+ - Loqate::Address#==
54
+ - Loqate::Address#attributes
55
+ - Loqate::Address#id
56
+ - Loqate::Address#type
57
+ - Loqate::Address#text
58
+ - Loqate::Address#highlight
59
+ - Loqate::Address#description
60
+ - Loqate::AddressGateway#client
61
+ - Loqate::AddressGateway#mapper
62
+ - Loqate::AddressGateway#error_mapper
63
+ - Loqate::AddressGateway#build_errors_from
64
+ - Loqate::AddressGateway#build_address_from
65
+ - Loqate::AddressGateway#build_detailed_addresses_from
66
+ - Loqate::Client#configuration
67
+ - Loqate::Client#authenticate_params
68
+ - Loqate::Client#format_params
69
+ - Loqate::DetailedAddress#==
70
+ - Loqate::Util.camelize
71
+ - Loqate::Util#camelize
72
+ - Loqate::Util.underscore
73
+ - Loqate::Util#underscore
74
+ - Loqate::Error#attributes
75
+ - Loqate::Gateway#client
21
76
  Summary::Presence:
22
77
  enabled: true
23
- exclude: []
78
+ exclude:
79
+ - Loqate::Address#==
80
+ - Loqate::Address#attributes
81
+ - Loqate::Address#id
82
+ - Loqate::Address#type
83
+ - Loqate::Address#text
84
+ - Loqate::Address#highlight
85
+ - Loqate::Address#description
86
+ - Loqate::AddressGateway#client
87
+ - Loqate::AddressGateway#mapper
88
+ - Loqate::AddressGateway#error_mapper
89
+ - Loqate::AddressGateway#build_errors_from
90
+ - Loqate::AddressGateway#build_address_from
91
+ - Loqate::AddressGateway#build_detailed_addresses_from
92
+ - Loqate::Client#configuration
93
+ - Loqate::Client#authenticate_params
94
+ - Loqate::Client#format_params
95
+ - Loqate::DetailedAddress#initialize
96
+ - Loqate::DetailedAddress#==
97
+ - Loqate::Error#attributes
98
+ - Loqate::Gateway#client
24
99
  Summary::Length:
25
100
  enabled: false
26
101
  Summary::Delimiter:
27
102
  enabled: false
28
103
  exclude: []
29
104
  Summary::SingleLine:
30
- enabled: true
105
+ enabled: false
31
106
  exclude: []
data/CHANGELOG.md CHANGED
@@ -3,3 +3,21 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.2.0] - 2018-10-31
8
+ ### Added
9
+ - VCR and WebMock to record HTTP interactions
10
+ - A file to hold a development API key (`.api_key`)
11
+ - A ruby version dotfile (`.ruby-version`)
12
+ - Core of the gem
13
+ - Address API
14
+
15
+ ### Changed
16
+ - Document the available code maintenance Rake tasks.
17
+
18
+ ## [0.1.0] - 2018-09-29
19
+ ### Added
20
+ - Initial core functionality
21
+ - Codebase maintenance tools
22
+
23
+ [0.2.0]: https://github.com/wilsonsilva/memoria/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -7,9 +7,7 @@
7
7
  [![Security](https://hakiri.io/github/wilsonsilva/loqate/master.svg)](https://hakiri.io/github/wilsonsilva/loqate/master)
8
8
  [![Inline docs](http://inch-ci.org/github/wilsonsilva/loqate.svg?branch=master)](http://inch-ci.org/github/wilsonsilva/loqate)
9
9
 
10
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/loqate`. To experiment with that code, run `bin/console` for an interactive prompt.
11
-
12
- TODO: Delete this and the text above, and describe your gem
10
+ Client to address verification, postcode lookup, & data quality services from Loqate.
13
11
 
14
12
  ## Installation
15
13
 
@@ -29,12 +27,67 @@ Or install it yourself as:
29
27
 
30
28
  ## Usage
31
29
 
32
- TODO: Write usage instructions here
30
+ ### Getting started
31
+
32
+ Loqate provides multiple APIs. And each API provides several services. This gem exposes these APIs through
33
+ an API gateway.
34
+
35
+ To get started, initialize an API gateway with [your API key](https://account.loqate.com/account#/):
36
+
37
+ ```ruby
38
+ gateway = Loqate::Gateway.new(api_key: '<YOUR_API_KEY>')
39
+ ```
40
+
41
+ Every call to a gateway method will return a `Loqate::Result` object, which will respond to `success?` and `failure?`.
42
+
43
+ ### Address API
44
+
45
+ The Address API consists of two main API requests:
46
+ [Find](https://www.loqate.com/resources/support/apis/Capture/Interactive/Find/1/) request is used to narrow down a
47
+ possible list of addresses;
48
+ and a [Retrieve](https://www.loqate.com/resources/support/apis/Capture/Interactive/Retrieve/1/) request is used to
49
+ retrieve a fully formatted address.
50
+
51
+ A typical address search is made up of a series of `find` requests, followed by a `retrieve` based on the user
52
+ selection.
53
+
54
+ #### Finding addresses
55
+
56
+ ```ruby
57
+ result = gateway.address.find(text: 'EC1Y 8AF', country: 'GB', limit: 5)
58
+
59
+ result.success? # => true
60
+ result.failure? # => false
61
+
62
+ addresses = result.value
63
+ addresses.first.id # => 'GB|RM|B|8144611'
64
+ ```
65
+
66
+ #### Retrieving the details of an address
67
+
68
+ ```ruby
69
+ result = gateway.address.retrieve(id: 'GB|RM|B|8144611')
70
+
71
+ result.success? # => true
72
+ result.failure? # => false
73
+
74
+ addresses = result.value
75
+ address = addresses.first
76
+
77
+ address.city # 'London'
78
+ address.line1 # '148 Warner Road'
79
+ address.postal_code # 'E17 7EA'
80
+ ```
33
81
 
34
82
  ## Development
35
83
 
36
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive
37
- prompt that will allow you to experiment. The health and maintainability of the codebase is ensured through a set of
84
+ After checking out the repo, run `bin/setup` to install dependencies, configure git hooks and create support files.
85
+
86
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
87
+
88
+ Then add your Loqate API key to `.api_key`. It will be used by RSpec and VCR, but not stored in the codebase.
89
+
90
+ The health and maintainability of the codebase is ensured through a set of
38
91
  Rake tasks to test, lint and audit the gem for security vulnerabilities and documentation:
39
92
 
40
93
  ```
@@ -51,4 +104,4 @@ rake yardstick_measure # Measure docs in lib/**/*.rb with yardstick
51
104
 
52
105
  ## Contributing
53
106
 
54
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/loqate.
107
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wilsonsilva/loqate.
data/ROADMAP.md CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  ## Core
4
4
 
5
- - [ ] HTTP Client
6
- - [ ] Core Client
5
+ - [x] HTTP Client
6
+ - [x] Core Client
7
7
  - [ ] Logging
8
- - [ ] Domain error handling
9
- - [ ] Configuration
10
- - [ ] Versioning
8
+ - [x] Domain error handling
9
+ - [x] Configuration
10
+ - [ ] Validate request parameters
11
+ - [ ] API/Service versioning
11
12
  - [ ] Integration with RSpec
12
13
 
13
14
  ## APIs
14
15
 
15
- - [ ] Address Verification
16
+ - [x] Address Verification
16
17
  - [ ] Geocode
17
18
  - [ ] Data Cleanse
18
19
  - [ ] Email Verification
@@ -26,7 +27,7 @@
26
27
  - [ ] File Renderer
27
28
  - [ ] HTML Table
28
29
  - [ ] Image Renderer
29
- - [ ] JSON
30
+ - [x] JSON
30
31
  - [ ] JSON (with extra)
31
32
  - [ ] JSONP (JavaScript)
32
33
  - [ ] PDF
data/bin/setup CHANGED
@@ -12,3 +12,5 @@ if ! which overcommit >/dev/null; then
12
12
  fi
13
13
 
14
14
  overcommit --install
15
+
16
+ touch ../.api_key
data/lib/loqate.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'loqate/version'
4
+ require 'loqate/gateway'
4
5
 
5
6
  # Encapsulates all the code of the gem.
6
7
  module Loqate
@@ -0,0 +1,71 @@
1
+ module Loqate
2
+ # A result from the address find service.
3
+ class Address
4
+ # An address ID or a container ID for further results
5
+ #
6
+ # @return [String]
7
+ #
8
+ attr_reader :id
9
+
10
+ # If the Type is 'Address' then the ID can be passed to the Retrieve service.
11
+ # Any other ID should be passed as the Container to a further Find request to get more results.
12
+ #
13
+ # @return [String]
14
+ #
15
+ attr_reader :type
16
+
17
+ # The name of the result
18
+ #
19
+ # @return [String]
20
+ #
21
+ attr_reader :text
22
+
23
+ # A list of number ranges identifying the matched characters in the Text and Description
24
+ #
25
+ # @return [String]
26
+ #
27
+ attr_reader :highlight
28
+
29
+ # Descriptive information about the result
30
+ #
31
+ # @return [String]
32
+ #
33
+ attr_reader :description
34
+
35
+ # Creates an address
36
+ #
37
+ # @param [String] id An address ID or a container ID for further results
38
+ # @param [String] type If the Type is 'Address' then the ID can be passed to the Retrieve service.
39
+ # Any other ID should be passed as the Container to a further Find request to get more results.
40
+ # @param [String] text The name of the result
41
+ # @param [String] highlight A list of number ranges identifying the matched characters in the Text and Description
42
+ # @param [String] description Descriptive information about the result
43
+ #
44
+ def initialize(id:, type:, text:, highlight:, description:)
45
+ @id = id
46
+ @type = type
47
+ @text = text
48
+ @highlight = highlight
49
+ @description = description
50
+ end
51
+
52
+ # @!visibility private
53
+ # @api private
54
+ def ==(other)
55
+ attributes == other.send(:attributes)
56
+ end
57
+
58
+ private
59
+
60
+ # @api private
61
+ def attributes
62
+ @attributes ||= {
63
+ id: id,
64
+ type: type,
65
+ text: text,
66
+ highlight: highlight,
67
+ description: description
68
+ }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,101 @@
1
+ require 'loqate/address'
2
+ require 'loqate/detailed_address'
3
+ require 'loqate/error'
4
+ require 'loqate/client'
5
+ require 'loqate/result'
6
+ require 'loqate/mappers/error_mapper'
7
+ require 'loqate/mappers/generic_mapper'
8
+
9
+ module Loqate
10
+ # Address Verification consists of two main API requests: a Find request is used to narrow down a possible
11
+ # list of addresses; and a Retrieve request is used to retrieve a fully formatted address.
12
+ #
13
+ # A typical address search is made up of a series of Find requests, followed by a Retrieve based on the
14
+ # user selection. Choose a service below to find out how to use each request.
15
+ #
16
+ class AddressGateway
17
+ FIND_ENDPOINT = '/Capture/Interactive/Find/v1.00/json3.ws'.freeze
18
+ RETRIEVE_ENDPOINT = '/Capture/Interactive/Retrieve/v1.00/json3.ws'.freeze
19
+
20
+ include Result::Mixin
21
+
22
+ # Creates an address gateway
23
+ #
24
+ # @param [Client] client The client responsible for the HTTP interactions
25
+ #
26
+ def initialize(client)
27
+ @client = client
28
+ @mapper = Mappers::GenericMapper.new
29
+ @error_mapper = Mappers::ErrorMapper.new
30
+ end
31
+
32
+ # Find addresses and places.
33
+ #
34
+ # @param [Hash] options The options to find an address or a list of addresses.
35
+ # @option options [String] :text The search text to find. Ideally a postcode or the start of the address.
36
+ # @option options [String] countries A comma separated list of ISO 2 or 3 character country codes to limit
37
+ # the search within.
38
+ # @option options [String] origin A starting location for the search. This can be the name or ISO 2 or 3 character
39
+ # code of a country, WGS84 coordinates (comma separated) or IP address to search from.
40
+ # @option options [String] container A container for the search. This should only be another Id previously returned
41
+ # from this service when the Type of the result was not 'Address'.
42
+ # @option options [Integer] limit The maximum number of results to return.
43
+ # @option options [String] language The preferred language for results. This should be a 2 or 4 character language
44
+ # code e.g. (en, fr, en-gb, en-us etc).
45
+ #
46
+ # @example Retrieving an address in the UK
47
+ # address = address_gateway.find(countries: 'GB', text: 'Scrubs Lane')
48
+ #
49
+ # @return [Array<Address>] A list of addresses
50
+ #
51
+ def find(options)
52
+ response = client.get(FIND_ENDPOINT, options)
53
+
54
+ response.errors? && build_errors_from(response.items) || build_address_from(response.items)
55
+ end
56
+
57
+ # Returns the full address details based on the id.
58
+ #
59
+ # @param [Hash] options The options to retrieve the address.
60
+ # @option options [String] :id The Id from a Find method to retrieve the details for.
61
+ # @option options [String] :field_1_format a.
62
+ # @option options [String] :field_2_format b.
63
+ # @option options [String] :field_3_format c.
64
+ # @option options [String] :field_4_format d.
65
+ # @option options [String] :field_5_format e.
66
+ #
67
+ # @example Retrieving the details of an address
68
+ # detailed_address = gateway.retrieve(id: 'GB|RM|ENG|6RB-NW10')
69
+ #
70
+ # @return [Array<DetailedAddress>] A list of detailed addresses
71
+ #
72
+ def retrieve(options)
73
+ response = client.get(RETRIEVE_ENDPOINT, options)
74
+
75
+ response.errors? && build_errors_from(response.items) || build_detailed_addresses_from(response.items)
76
+ end
77
+
78
+ private
79
+
80
+ # @api private
81
+ attr_reader :client, :mapper, :error_mapper
82
+
83
+ # @api private
84
+ def build_errors_from(items)
85
+ errors = error_mapper.map(items)
86
+ Failure(errors)
87
+ end
88
+
89
+ # @api private
90
+ def build_address_from(items)
91
+ address = mapper.map(items, Address)
92
+ Success(address)
93
+ end
94
+
95
+ # @api private
96
+ def build_detailed_addresses_from(items)
97
+ detailed_address = mapper.map(items, DetailedAddress)
98
+ Success(detailed_address)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,26 @@
1
+ module Loqate
2
+ # Generic response of a request to Loqate's API
3
+ class APIResult
4
+ # Array of hashes representing each item in the response body
5
+ #
6
+ # @return [Array<Hash>]
7
+ #
8
+ attr_reader :items
9
+
10
+ # Instantiates a new API result
11
+ #
12
+ # @param [Array<Hash>] items Array of hashes representing each item in the response body
13
+ #
14
+ def initialize(items)
15
+ @items = items
16
+ end
17
+
18
+ # Whether the response contains errors
19
+ #
20
+ # @return [Boolean] true if the response has errors and false otherwise
21
+ #
22
+ def errors?
23
+ !items.dig(0, 'Error').nil?
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,60 @@
1
+ require 'http'
2
+ require 'loqate/configuration'
3
+ require 'loqate/api_result'
4
+
5
+ module Loqate
6
+ # Responsible for the HTTP interactions. The only entity aware of HTTP concerns such as status codes and headers.
7
+ class Client
8
+ # Instantiates a new client
9
+ #
10
+ # @example
11
+ # configuration = Configuration.new(api_key: 'EA69-CD23-CV99-HL18')
12
+ # client = Client.new(configuration)
13
+ #
14
+ # @param [Configuration] configuration The configuration of the gem
15
+ #
16
+ def initialize(configuration)
17
+ @configuration = configuration
18
+ end
19
+
20
+ # Performs a GET request to Loqate's API. Every request returns the status code 200.
21
+ #
22
+ # @example Retrieving a resource
23
+ # client = Client.new
24
+ # result = client.get('/Capture/Interactive/Find/v1.00/json3.ws')
25
+ #
26
+ # @example Retrieving a resource using options
27
+ # client = Client.new
28
+ # result = client.get('/Capture/Interactive/Find/v1.00/json3.ws', countries: 'GB', text: 'Scrubs Lane')
29
+ #
30
+ # @param [String] endpoint URL of the API endpoint where the GET request
31
+ # @param [Hash] params Options for the GET request
32
+ #
33
+ # @return [APIResult] Generic response of a request to Loqate's API
34
+ #
35
+ def get(endpoint, params = {})
36
+ authenticated_params = authenticate_params(params)
37
+ formatted_params = format_params(authenticated_params)
38
+
39
+ response = HTTP.get(configuration.host + endpoint, params: formatted_params)
40
+
41
+ body = JSON.parse(response.body)
42
+ APIResult.new(body.fetch('Items'))
43
+ end
44
+
45
+ private
46
+
47
+ # @api private
48
+ attr_reader :configuration
49
+
50
+ # @api private
51
+ def authenticate_params(params)
52
+ params.merge(key: configuration.api_key)
53
+ end
54
+
55
+ # @api private
56
+ def format_params(params)
57
+ params.transform_keys { |key| Util.camelize(key) }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ module Loqate
2
+ # Configuration for the gem
3
+ class Configuration
4
+ DEFAULT_LANGUAGE = 'en-gb'.freeze
5
+ DEFAULT_HOST = 'https://api.addressy.com'.freeze
6
+
7
+ # API key that will give you access to all services.
8
+ #
9
+ # @return [String]
10
+ attr_reader :api_key
11
+
12
+ # Base URL for Loqate's services. Defaults to https://api.addressy.com
13
+ #
14
+ # @return [String]
15
+ attr_reader :host
16
+
17
+ # Preferred language for results. This should be a 2 or 4 character language code e.g. (en, fr, en-gb, etc).
18
+ #
19
+ # @return [String]
20
+ attr_reader :language
21
+
22
+ # Instantiates the gem configuration
23
+ #
24
+ # @param [String] api_key API key that will give you access to all services
25
+ # @param [String] host Base URL for Loqate's services
26
+ # @param [String] language Preferred language for results
27
+ #
28
+ def initialize(api_key:, host: DEFAULT_HOST, language: DEFAULT_LANGUAGE)
29
+ @api_key = api_key
30
+ @host = host
31
+ @language = language
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,83 @@
1
+ module Loqate
2
+ # A result from the address retrieve service.
3
+ class DetailedAddress
4
+ # For the first version, this will be a flat structure, exactly as it is defined in Loqate's API.
5
+ # But this many attributes is too much for a single object to hold.
6
+ #
7
+ # @api private
8
+ #
9
+ ATTRIBUTES = %i[
10
+ admin_area_code
11
+ admin_area_name
12
+ barcode
13
+ block
14
+ building_name
15
+ building_number
16
+ city
17
+ company
18
+ country_iso2
19
+ country_iso3
20
+ country_iso_number
21
+ country_name
22
+ data_level
23
+ department
24
+ district
25
+ domestic_id
26
+ field1
27
+ field2
28
+ field3
29
+ field4
30
+ field5
31
+ field6
32
+ field7
33
+ field8
34
+ field9
35
+ field10
36
+ field11
37
+ field12
38
+ field13
39
+ field14
40
+ field15
41
+ field16
42
+ field17
43
+ field18
44
+ field19
45
+ field20
46
+ id
47
+ label
48
+ language
49
+ language_alternatives
50
+ line1
51
+ line2
52
+ line3
53
+ line4
54
+ line5
55
+ neighbourhood
56
+ po_box_number
57
+ postal_code
58
+ province
59
+ province_code
60
+ province_name
61
+ secondary_street
62
+ sorting_number1
63
+ sorting_number2
64
+ street
65
+ sub_building
66
+ type
67
+ ].freeze
68
+
69
+ ATTRIBUTES.each do |attribute|
70
+ attr_reader attribute
71
+ end
72
+
73
+ def initialize(options = {})
74
+ options.each_pair do |key, value|
75
+ instance_variable_set("@#{key}", value) if ATTRIBUTES.include?(key)
76
+ end
77
+ end
78
+
79
+ def ==(other)
80
+ other.is_a?(DetailedAddress) && id == other.id
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,54 @@
1
+ module Loqate
2
+ # Generic error returned from an API call
3
+ class Error
4
+ # Unique identifier for the error
5
+ #
6
+ # @return [Integer]
7
+ #
8
+ attr_reader :id
9
+
10
+ # Descriptive information about the error
11
+ #
12
+ # @return [String]
13
+ #
14
+ attr_reader :description
15
+
16
+ # What caused the error
17
+ #
18
+ # @return [String]
19
+ #
20
+ attr_reader :cause
21
+
22
+ # How to solve the error
23
+ #
24
+ # @return [String]
25
+ #
26
+ attr_reader :resolution
27
+
28
+ # Instantiates an error
29
+ #
30
+ # @param [Integer] id Unique identifier for the error
31
+ # @param [String] description Descriptive information about the error
32
+ # @param [String] cause What caused the error
33
+ # @param [String] resolution How to solve the error
34
+ #
35
+ def initialize(id:, description:, cause:, resolution:)
36
+ @id = id
37
+ @cause = cause
38
+ @resolution = resolution
39
+ @description = description
40
+ end
41
+
42
+ # Compare the equality of this error with +other+. Two errors are considered equal if they have the same +id+.
43
+ #
44
+ # @param [Error] other Another error
45
+ #
46
+ # @return [Boolean]
47
+ #
48
+ # @api private
49
+ #
50
+ def ==(other)
51
+ other.is_a?(Error) && id == other.id
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ require 'loqate/address_gateway'
2
+
3
+ module Loqate
4
+ # Acts as a single point of entry for a defined group of API's.
5
+ class Gateway
6
+ # The gem configuration.
7
+ #
8
+ # @return [Configuration]
9
+ #
10
+ attr_reader :config
11
+
12
+ # Creates a new gateway.
13
+ #
14
+ # @param [Hash] options Options to configure the gem.
15
+ # @option options [String] :api_key API key that will give you access to all services
16
+ # @option options [String] :host Base URL for Loqate's services
17
+ # @option options [String] :language Preferred language for results
18
+ #
19
+ # @see Configuration
20
+ #
21
+ def initialize(options)
22
+ @config = Configuration.new(options)
23
+ @client = Client.new(config)
24
+ end
25
+
26
+ # Gateway to the Address APIs.
27
+ #
28
+ # @return [AddressGateway] An instance of an address gateway.
29
+ #
30
+ def address
31
+ @address ||= AddressGateway.new(client)
32
+ end
33
+
34
+ private
35
+
36
+ # @api private
37
+ attr_reader :client
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ require 'loqate/mappers/generic_mapper'
2
+ require 'loqate/error'
3
+ require 'loqate/util'
4
+
5
+ module Loqate
6
+ module Mappers
7
+ # Transforms erroneous responses into concrete error objects.
8
+ #
9
+ # @api private
10
+ #
11
+ class ErrorMapper
12
+ # Creates errors from an API response
13
+ #
14
+ # @return [Array<Error>] An array of errors
15
+ #
16
+ def map(items)
17
+ items.map do |item|
18
+ attributes = item.transform_keys { |attribute| Util.underscore(attribute) }
19
+ attributes[:id] = attributes.delete(:error).to_i
20
+
21
+ Loqate::Error.new(attributes)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'loqate/util'
2
+
3
+ module Loqate
4
+ module Mappers
5
+ # Transforms arrays of hashes into concrete instances of classes.
6
+ #
7
+ # @api private
8
+ #
9
+ class GenericMapper
10
+ # Creates objects from an API response. The objects will be an instance of +object_class+.
11
+ #
12
+ # @param [Class] object_class Class of the final objects
13
+ # @param [Array<Hash>] items Array of attributes to instantiate the final objects
14
+ #
15
+ # @return An array of objects of the given class
16
+ #
17
+ def map(items, object_class)
18
+ items.map do |item|
19
+ attributes = item.transform_keys { |attribute| Util.underscore(attribute) }
20
+ object_class.new(attributes)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,102 @@
1
+ module Loqate
2
+ # Generic return value of an operation. Provides a consistent way to determine if an operation
3
+ # has succeeded or failed.
4
+ class Result
5
+ # Creates a new Result
6
+ #
7
+ # @param [Object] value The original return value of an operation.
8
+ # @param [Symbol] code A code to describe the kind of result.
9
+ #
10
+ # @example Creating a new +Result+
11
+ # Result.new(value: person.errors, code: :not_updated)
12
+ #
13
+ def initialize(value:, code: nil)
14
+ @value = value
15
+ @code = code
16
+ end
17
+
18
+ # A code to describe the kind of result.
19
+ #
20
+ # @return [Symbol]
21
+ #
22
+ attr_reader :code
23
+
24
+ # The original return value of an operation.
25
+ #
26
+ # @return [Object]
27
+ #
28
+ attr_reader :value
29
+
30
+ # Wraps the result of a successful operation.
31
+ class Success < Result
32
+ # Always true
33
+ #
34
+ # @return [Boolean]
35
+ #
36
+ def success?
37
+ true
38
+ end
39
+
40
+ # Always false
41
+ #
42
+ # @return [Boolean]
43
+ #
44
+ def failure?
45
+ false
46
+ end
47
+ end
48
+
49
+ # Wraps the result of a failed operation.
50
+ class Failure < Result
51
+ # Always false
52
+ #
53
+ # @return [Boolean]
54
+ #
55
+ def success?
56
+ false
57
+ end
58
+
59
+ # Always true
60
+ #
61
+ # @return [Boolean]
62
+ #
63
+ def failure?
64
+ true
65
+ end
66
+ end
67
+
68
+ # Utility methods to conveniently return +Success+ or +Failure+ results.
69
+ module Mixin
70
+ Success = Success
71
+ Failure = Failure
72
+
73
+ # Wraps the result of an operation in a +Success+ result.
74
+ #
75
+ # @param [Object] value The original return value of an operation.
76
+ #
77
+ # @example Creating a success result
78
+ # Success(value: Person.new)
79
+ #
80
+ # @return [Success]
81
+ #
82
+ def Success(value)
83
+ Success.new(value: value)
84
+ end
85
+
86
+ # Wraps the result of an operation in a +Failure+ result.
87
+ #
88
+ # @param [Object] value The original return value of an operation.
89
+ # @param [Symbol] code A unique
90
+ #
91
+ # @example Creating a failure result
92
+ # error = { message: 'Name cannot be blank' }
93
+ # Success(value: error, code: :not_persisted)
94
+ #
95
+ # @return [Success]
96
+ #
97
+ def Failure(value, code: nil)
98
+ Failure.new(value: value, code: code)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ module Loqate
2
+ # Provides methods for string manipulation
3
+ #
4
+ # @api private
5
+ module Util
6
+ module_function
7
+
8
+ # Converts a string to snake case
9
+ #
10
+ # @param [String|Symbol] term The term to be converted.
11
+ #
12
+ def underscore(term)
13
+ term
14
+ .to_s
15
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
16
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
17
+ .tr('-', '_')
18
+ .downcase
19
+ .to_sym
20
+ end
21
+
22
+ # Converts a string to camel case
23
+ #
24
+ # @param [String|Symbol] term The term to be converted.
25
+ #
26
+ def camelize(term)
27
+ string = term.to_s
28
+ string = string.sub(/^[a-z\d]*/, &:capitalize)
29
+ string.gsub!(%r{(?:_|(\/))([a-z\d]*)}) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
30
+ string.gsub!('/'.freeze, '::'.freeze)
31
+ string
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loqate
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/loqate.gemspec CHANGED
@@ -24,6 +24,8 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ['lib']
26
26
 
27
+ spec.add_runtime_dependency 'http', '~> 3.3.0'
28
+
27
29
  spec.add_development_dependency 'bundler', '~> 1.16'
28
30
  spec.add_development_dependency 'bundler-audit', '~> 0.6'
29
31
  spec.add_development_dependency 'guard', '~> 2.14'
@@ -39,6 +41,8 @@ Gem::Specification.new do |spec|
39
41
  spec.add_development_dependency 'rubocop-rspec', '~> 1.29'
40
42
  spec.add_development_dependency 'simplecov', '~> 0.16'
41
43
  spec.add_development_dependency 'simplecov-console', '~> 0.4'
44
+ spec.add_development_dependency 'vcr', '~> 4.0'
45
+ spec.add_development_dependency 'webmock', '~> 3.4'
42
46
  spec.add_development_dependency 'yard', '~> 0.9'
43
47
  spec.add_development_dependency 'yard-junk', '~> 0.0.7'
44
48
  spec.add_development_dependency 'yardstick', '~> 0.9'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loqate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wilson Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-29 00:00:00.000000000 Z
11
+ date: 2018-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.3.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -220,6 +234,34 @@ dependencies:
220
234
  - - "~>"
221
235
  - !ruby/object:Gem::Version
222
236
  version: '0.4'
237
+ - !ruby/object:Gem::Dependency
238
+ name: vcr
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '4.0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '4.0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: webmock
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: '3.4'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: '3.4'
223
265
  - !ruby/object:Gem::Dependency
224
266
  name: yard
225
267
  requirement: !ruby/object:Gem::Requirement
@@ -274,12 +316,12 @@ files:
274
316
  - ".overcommit.yml"
275
317
  - ".rspec"
276
318
  - ".rubocop.yml"
319
+ - ".ruby-version"
277
320
  - ".travis.yml"
278
321
  - ".yardopts"
279
322
  - ".yardstick.yml"
280
323
  - CHANGELOG.md
281
324
  - Gemfile
282
- - Gemfile.lock
283
325
  - Guardfile
284
326
  - README.md
285
327
  - ROADMAP.md
@@ -287,6 +329,18 @@ files:
287
329
  - bin/console
288
330
  - bin/setup
289
331
  - lib/loqate.rb
332
+ - lib/loqate/address.rb
333
+ - lib/loqate/address_gateway.rb
334
+ - lib/loqate/api_result.rb
335
+ - lib/loqate/client.rb
336
+ - lib/loqate/configuration.rb
337
+ - lib/loqate/detailed_address.rb
338
+ - lib/loqate/error.rb
339
+ - lib/loqate/gateway.rb
340
+ - lib/loqate/mappers/error_mapper.rb
341
+ - lib/loqate/mappers/generic_mapper.rb
342
+ - lib/loqate/result.rb
343
+ - lib/loqate/util.rb
290
344
  - lib/loqate/version.rb
291
345
  - loqate.gemspec
292
346
  homepage: https://github.com/wilsonsilva/loqate
data/Gemfile.lock DELETED
@@ -1,147 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- loqate (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- ansi (1.5.0)
10
- ast (2.4.0)
11
- backports (3.11.4)
12
- bundler-audit (0.6.0)
13
- bundler (~> 1.2)
14
- thor (~> 0.18)
15
- childprocess (0.9.0)
16
- ffi (~> 1.0, >= 1.0.11)
17
- coderay (1.1.2)
18
- diff-lcs (1.3)
19
- docile (1.3.1)
20
- ffi (1.9.25)
21
- formatador (0.2.5)
22
- guard (2.14.2)
23
- formatador (>= 0.2.4)
24
- listen (>= 2.7, < 4.0)
25
- lumberjack (>= 1.0.12, < 2.0)
26
- nenv (~> 0.1)
27
- notiffany (~> 0.0)
28
- pry (>= 0.9.12)
29
- shellany (~> 0.0)
30
- thor (>= 0.18.1)
31
- guard-bundler (2.1.0)
32
- bundler (~> 1.0)
33
- guard (~> 2.2)
34
- guard-compat (~> 1.1)
35
- guard-bundler-audit (0.1.4)
36
- bundler-audit (>= 0.3.1)
37
- guard (~> 2.0)
38
- guard-compat (1.2.1)
39
- guard-rspec (4.7.3)
40
- guard (~> 2.1)
41
- guard-compat (~> 1.1)
42
- rspec (>= 2.99.0, < 4.0)
43
- guard-rubocop (1.3.0)
44
- guard (~> 2.0)
45
- rubocop (~> 0.20)
46
- hirb (0.7.3)
47
- iniparse (1.4.4)
48
- jaro_winkler (1.5.1)
49
- json (2.1.0)
50
- listen (3.1.5)
51
- rb-fsevent (~> 0.9, >= 0.9.4)
52
- rb-inotify (~> 0.9, >= 0.9.7)
53
- ruby_dep (~> 1.2)
54
- lumberjack (1.0.13)
55
- method_source (0.9.0)
56
- nenv (0.3.0)
57
- notiffany (0.1.1)
58
- nenv (~> 0.1)
59
- shellany (~> 0.0)
60
- overcommit (0.46.0)
61
- childprocess (~> 0.6, >= 0.6.3)
62
- iniparse (~> 1.4)
63
- parallel (1.12.1)
64
- parser (2.5.1.2)
65
- ast (~> 2.4.0)
66
- powerpack (0.1.2)
67
- pry (0.11.3)
68
- coderay (~> 1.1.0)
69
- method_source (~> 0.9.0)
70
- rainbow (3.0.0)
71
- rake (10.5.0)
72
- rb-fsevent (0.10.3)
73
- rb-inotify (0.9.10)
74
- ffi (>= 0.5.0, < 2)
75
- rspec (3.7.0)
76
- rspec-core (~> 3.7.0)
77
- rspec-expectations (~> 3.7.0)
78
- rspec-mocks (~> 3.7.0)
79
- rspec-core (3.7.1)
80
- rspec-support (~> 3.7.0)
81
- rspec-expectations (3.7.0)
82
- diff-lcs (>= 1.2.0, < 2.0)
83
- rspec-support (~> 3.7.0)
84
- rspec-mocks (3.7.0)
85
- diff-lcs (>= 1.2.0, < 2.0)
86
- rspec-support (~> 3.7.0)
87
- rspec-support (3.7.1)
88
- rubocop (0.59.2)
89
- jaro_winkler (~> 1.5.1)
90
- parallel (~> 1.10)
91
- parser (>= 2.5, != 2.5.1.1)
92
- powerpack (~> 0.1)
93
- rainbow (>= 2.2.2, < 4.0)
94
- ruby-progressbar (~> 1.7)
95
- unicode-display_width (~> 1.0, >= 1.0.1)
96
- rubocop-rspec (1.29.1)
97
- rubocop (>= 0.58.0)
98
- ruby-progressbar (1.10.0)
99
- ruby_dep (1.5.0)
100
- shellany (0.0.1)
101
- simplecov (0.16.1)
102
- docile (~> 1.1)
103
- json (>= 1.8, < 3)
104
- simplecov-html (~> 0.10.0)
105
- simplecov-console (0.4.2)
106
- ansi
107
- hirb
108
- simplecov
109
- simplecov-html (0.10.2)
110
- thor (0.20.0)
111
- tty-color (0.4.2)
112
- unicode-display_width (1.4.0)
113
- yard (0.9.16)
114
- yard-junk (0.0.7)
115
- backports
116
- rainbow
117
- tty-color
118
- yard
119
- yardstick (0.9.9)
120
- yard (~> 0.8, >= 0.8.7.2)
121
-
122
- PLATFORMS
123
- ruby
124
-
125
- DEPENDENCIES
126
- bundler (~> 1.16)
127
- bundler-audit (~> 0.6)
128
- guard (~> 2.14)
129
- guard-bundler (~> 2.1)
130
- guard-bundler-audit (~> 0.1)
131
- guard-rspec (~> 4.7)
132
- guard-rubocop (~> 1.2)
133
- loqate!
134
- overcommit (~> 0.46)
135
- pry (~> 0.11)
136
- rake (~> 10.0)
137
- rspec (~> 3.0)
138
- rubocop (~> 0.59)
139
- rubocop-rspec (~> 1.29)
140
- simplecov (~> 0.16)
141
- simplecov-console (~> 0.4)
142
- yard (~> 0.9)
143
- yard-junk (~> 0.0.7)
144
- yardstick (~> 0.9)
145
-
146
- BUNDLED WITH
147
- 1.16.5