firstclasspostcodes 0.0.1

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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.dependabot/config.yml +12 -0
  3. data/.github/workflows/gem.yml +53 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +78 -0
  7. data/Gemfile +25 -0
  8. data/LICENSE +21 -0
  9. data/Rakefile +11 -0
  10. data/firstclasspostcodes.gemspec +54 -0
  11. data/lib/firstclasspostcodes.rb +33 -0
  12. data/lib/firstclasspostcodes/client.rb +81 -0
  13. data/lib/firstclasspostcodes/configuration.rb +156 -0
  14. data/lib/firstclasspostcodes/events.rb +27 -0
  15. data/lib/firstclasspostcodes/operations.rb +4 -0
  16. data/lib/firstclasspostcodes/operations/get_lookup.rb +52 -0
  17. data/lib/firstclasspostcodes/operations/get_postcode.rb +40 -0
  18. data/lib/firstclasspostcodes/operations/methods/format_address.rb +38 -0
  19. data/lib/firstclasspostcodes/operations/methods/list_addresses.rb +29 -0
  20. data/lib/firstclasspostcodes/response_error.rb +29 -0
  21. data/lib/firstclasspostcodes/version.rb +5 -0
  22. data/spec/firstclasspostcodes/client_spec.rb +94 -0
  23. data/spec/firstclasspostcodes/events_spec.rb +41 -0
  24. data/spec/firstclasspostcodes/operations/get_lookup_spec.rb +103 -0
  25. data/spec/firstclasspostcodes/operations/get_postcode_spec.rb +58 -0
  26. data/spec/firstclasspostcodes/operations/methods/format_address_spec.rb +106 -0
  27. data/spec/firstclasspostcodes/operations/methods/list_addresses_spec.rb +75 -0
  28. data/spec/firstclasspostcodes/response_error_spec.rb +43 -0
  29. data/spec/firstclasspostcodes/version_spec.rb +9 -0
  30. data/spec/firstclasspostcodes_spec.rb +108 -0
  31. data/spec/spec_helper.rb +42 -0
  32. data/spec/support/events_examples.rb +11 -0
  33. metadata +123 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e926c2b2819dd25138a2df749f49930a486e220d9846766855a9b31dba92d8b
4
+ data.tar.gz: 3094812034c5a12258c3d8dccc87c0add4d9e173b4011657ac4c7dab564e314e
5
+ SHA512:
6
+ metadata.gz: 2ee17eead9837df323e51afcaa91c19b7d9263d59349c4364493ac0fd85f400c3a199cc041582834488e11324afa8946920c2023fa08c4332b153dc23293c39d
7
+ data.tar.gz: 48310e7b50ca7a3de522c393f835e678706e31a4c6ae14f1eda6c7ba5686a1099b18eb4bc2d94998bae8bc2a3c0605aa5de6e04d15636eb302a162c411175a29
@@ -0,0 +1,12 @@
1
+ version: 1
2
+
3
+ update_configs:
4
+
5
+ - package_manager: "ruby:bundler"
6
+ directory: "/"
7
+ update_schedule: "weekly"
8
+ version_requirement_updates: increase_versions
9
+ automerged_updates:
10
+ - match:
11
+ dependency_type: "all"
12
+ update_type: "all"
@@ -0,0 +1,53 @@
1
+ name: "Build, test and release"
2
+
3
+ on:
4
+ - push
5
+ - pull_request
6
+
7
+ jobs:
8
+ build:
9
+ name: Build + Publish
10
+ runs-on: ubuntu-latest
11
+
12
+ services:
13
+ mock-api:
14
+ image: firstclasspostcodes/mock:latest
15
+ ports:
16
+ - '80:3000'
17
+
18
+ steps:
19
+ - uses: actions/checkout@master
20
+ - name: Set up Ruby 2.6
21
+ uses: actions/setup-ruby@v1
22
+ with:
23
+ version: 2.6.x
24
+ - run: gem install bundler
25
+ - run: bundle install --jobs 4 --retry 3
26
+ - name: Rake
27
+ run: bundle exec rake
28
+ env:
29
+ API_URL: http://localhost:3000
30
+ API_KEY: '111111111111'
31
+ - name: Publish to GPR
32
+ if: github.ref == 'refs/heads/master'
33
+ run: |
34
+ mkdir -p $HOME/.gem
35
+ touch $HOME/.gem/credentials
36
+ chmod 0600 $HOME/.gem/credentials
37
+ printf -- "---\n:github: Bearer ${GITHUB_TOKEN}\n" > $HOME/.gem/credentials
38
+ gem build *.gemspec
39
+ gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
40
+ env:
41
+ OWNER: firstclasspostcodes
42
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
43
+ - name: Publish to RubyGems
44
+ if: github.ref == 'refs/heads/master'
45
+ run: |
46
+ mkdir -p $HOME/.gem
47
+ touch $HOME/.gem/credentials
48
+ chmod 0600 $HOME/.gem/credentials
49
+ printf -- "---\n:rubygems_api_key: ${RUBYGEMS_AUTH_TOKEN}\n" > $HOME/.gem/credentials
50
+ gem build *.gemspec
51
+ gem push *.gem
52
+ env:
53
+ RUBYGEMS_AUTH_TOKEN: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
@@ -0,0 +1,9 @@
1
+ /firstclasspostcodes-*.gem
2
+ /Gemfile.lock
3
+ .rvmrc
4
+ Gemfile.lock
5
+ tags
6
+ vendor
7
+ /.bundle/
8
+ coverage/
9
+ .idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,78 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ TargetRubyVersion: 2.3
4
+
5
+ Exclude:
6
+ # brandur: Exclude ephmeral script-like files that I use to try and
7
+ # reproduce problems with the library. If you know of a better way of doing
8
+ # this (e.g. exclude files not tracked by Git), feel free to change it.
9
+ - "test_*"
10
+
11
+ Layout/CaseIndentation:
12
+ EnforcedStyle: end
13
+
14
+ Layout/FirstArrayElementIndentation:
15
+ EnforcedStyle: consistent
16
+
17
+ Layout/FirstHashElementIndentation:
18
+ EnforcedStyle: consistent
19
+
20
+ # This can be re-enabled once we're 2.3+ only and can use the squiggly heredoc
21
+ # operator. Prior to that, Rubocop recommended bringing in a library like
22
+ # ActiveSupport to get heredoc indentation, which is just terrible.
23
+ Layout/HeredocIndentation:
24
+ Enabled: false
25
+
26
+ Layout/LineLength:
27
+ Max: 150
28
+ Exclude:
29
+ - "spec/**/*.rb"
30
+
31
+ Metrics/BlockLength:
32
+ Max: 40
33
+ Exclude:
34
+ # `context` in tests are blocks and get quite large, so exclude the test
35
+ # directory from having to adhere to this rule.
36
+ - "spec/**/*.rb"
37
+
38
+ Metrics/ClassLength:
39
+ Exclude:
40
+ # Test classes get quite large, so exclude the test directory from having
41
+ # to adhere to this rule.
42
+ - "test/**/*.rb"
43
+
44
+ Metrics/PerceivedComplexity:
45
+ Enabled: false
46
+
47
+ Metrics/CyclomaticComplexity:
48
+ Enabled: false
49
+
50
+ Metrics/MethodLength:
51
+ Enabled: false
52
+
53
+ Metrics/AbcSize:
54
+ Max: 50
55
+
56
+ Metrics/ModuleLength:
57
+ Enabled: false
58
+
59
+ Style/Documentation:
60
+ Enabled: false
61
+
62
+ Style/AccessModifierDeclarations:
63
+ EnforcedStyle: inline
64
+
65
+ Style/FrozenStringLiteralComment:
66
+ EnforcedStyle: always
67
+
68
+ Style/NumericPredicate:
69
+ Enabled: false
70
+
71
+ Style/StringLiterals:
72
+ EnforcedStyle: double_quotes
73
+
74
+ Style/TrailingCommaInArrayLiteral:
75
+ EnforcedStyleForMultiline: consistent_comma
76
+
77
+ Style/TrailingCommaInHashLiteral:
78
+ EnforcedStyleForMultiline: consistent_comma
data/Gemfile ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem "codecov", require: false
9
+ gem "coveralls", require: false
10
+ gem "rake"
11
+ gem "rspec"
12
+
13
+ # Rubocop changes pretty quickly: new cops get added and old cops change
14
+ # names or go into new namespaces. This is a library and we don't have
15
+ # `Gemfile.lock` checked in, so to prevent good builds from suddenly going
16
+ # bad, pin to a specific version number here. Try to keep this relatively
17
+ # up-to-date, but it's not the end of the world if it's not.
18
+ gem "rubocop", "0.79", require: false
19
+
20
+ platforms :mri do
21
+ gem "byebug"
22
+ gem "pry"
23
+ gem "pry-byebug"
24
+ end
25
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2018-2020 Firstclasspostcodes (https://firstclasspostcodes.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+ require "rspec/core/rake_task"
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ RuboCop::RakeTask.new
10
+
11
+ task default: %i[spec rubocop]
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(::File.join(::File.dirname(__FILE__), "lib"))
4
+
5
+ require "firstclasspostcodes/version"
6
+
7
+ REPO_URL = "github.com/firstclasspostcodes/firstclasspostcodes-ruby"
8
+
9
+ DOMAIN_NAME = "firstclasspostcodes.com"
10
+
11
+ Gem::Specification.new do |s|
12
+ s.name = "firstclasspostcodes"
13
+
14
+ s.version = Firstclasspostcodes::VERSION
15
+
16
+ s.required_ruby_version = ">= 2.3.0"
17
+
18
+ s.summary = <<-SUMMARY
19
+ Ruby bindings for the Firstclasspostcodes API
20
+ SUMMARY
21
+
22
+ s.description = <<-DESC
23
+ With 500 requests for free per month, get started with the
24
+ fastest and cheapest address lookup service in the UK.
25
+
26
+ See https://#{DOMAIN_NAME} for more details.
27
+ DESC
28
+
29
+ s.author = "Firstclasspostcodes"
30
+
31
+ s.email = "support@#{DOMAIN_NAME}"
32
+
33
+ s.homepage = "https://docs.#{DOMAIN_NAME}"
34
+
35
+ s.license = "MIT"
36
+
37
+ s.metadata = {
38
+ "bug_tracker_uri" => "https://#{REPO_URL}/issues",
39
+ "changelog_uri" => "https://#{REPO_URL}/blob/master/CHANGELOG.md",
40
+ "documentation_uri" => "https://docs.#{DOMAIN_NAME}",
41
+ "github_repo" => "ssh://#{REPO_URL}",
42
+ "homepage_uri" => "https://#{DOMAIN_NAME}",
43
+ "source_code_uri" => "https://#{REPO_URL}",
44
+ }
45
+
46
+ s.files = `git ls-files`.split("\n")
47
+ s.test_files = `git ls-files -- test/*`.split("\n")
48
+ s.executables = `git ls-files -- bin/*`.split("\n")
49
+ .map { |f| ::File.basename(f) }
50
+ s.require_paths = ["lib"]
51
+
52
+ s.add_runtime_dependency "json", "~> 2.2", ">= 2.2.0"
53
+ s.add_runtime_dependency "typhoeus", "~> 1.3", ">= 1.3.1"
54
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firstclasspostcodes/client"
4
+ require "firstclasspostcodes/configuration"
5
+ require "firstclasspostcodes/events"
6
+ require "firstclasspostcodes/operations"
7
+ require "firstclasspostcodes/version"
8
+
9
+ module Firstclasspostcodes
10
+ class << self
11
+ # Customize default settings for the SDK using block.
12
+ # Firstclasspostcodes.configure do |config|
13
+ # config.api_key = "xxx"
14
+ # end
15
+ # If no block given, return the default Configuration object.
16
+ def configure
17
+ if block_given?
18
+ yield(Configuration.default)
19
+ else
20
+ Configuration.default
21
+ end
22
+ end
23
+ end
24
+
25
+ class Client
26
+ # Utility module mixins
27
+ include Events
28
+
29
+ # Include all API operations below
30
+ include Operations::GetPostcode
31
+ include Operations::GetLookup
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "typhoeus"
5
+
6
+ require "firstclasspostcodes/response_error"
7
+
8
+ module Firstclasspostcodes
9
+ class Client
10
+ # The Configuration object holding settings to be used in the API client.
11
+ attr_accessor :config
12
+
13
+ # The User-agent for the library
14
+ attr_reader :user_agent
15
+
16
+ # @option config [Configuration] Configuration for initializing the object
17
+ def initialize(config = Configuration.default)
18
+ @config = config
19
+ @user_agent = "Firstclasspostcodes/ruby@#{Firstclasspostcodes::VERSION}"
20
+ on("request") { |req| @config.logger.debug(req) if @config.debug }
21
+ on("response") { |req| @config.logger.debug(req) if @config.debug }
22
+ on("error") { |req| @config.logger.error(req) }
23
+ end
24
+
25
+ def request(opts)
26
+ url = build_request_url(opts[:path])
27
+
28
+ request_params = {
29
+ params: opts[:query_params] || {},
30
+ method: opts[:method].to_sym.downcase,
31
+ }
32
+
33
+ request_params.merge!(@config.to_request_params)
34
+
35
+ emit("request", request_params)
36
+
37
+ response = call_request(url, request_params)
38
+
39
+ data = JSON.parse(response.body, symbolize_names: true)
40
+
41
+ emit("response", data)
42
+
43
+ data
44
+ rescue ResponseError => e
45
+ emit("error", e)
46
+ raise e
47
+ end
48
+
49
+ def call_request(*args)
50
+ response = Typhoeus::Request.new(*args).run
51
+
52
+ return response if response.success?
53
+
54
+ raise handle_request_error(response)
55
+ end
56
+
57
+ def handle_request_error(response)
58
+ raise ResponseError.new("Connection timed out", "timeout") if response.timed_out?
59
+
60
+ raise ResponseError.new(response.return_message, "liberror") if response.code == 0
61
+
62
+ error = begin
63
+ JSON.parse(response.body, symbolize_names: true)
64
+ rescue JSON::ParserError
65
+ response.body
66
+ end
67
+
68
+ raise ResponseError.new(error, "network-error")
69
+ end
70
+
71
+ def build_request_url(path)
72
+ # Add leading and trailing slashes to path
73
+ path = "/#{path}".gsub(%r{/+}, "/")
74
+ @config.base_url + path
75
+ end
76
+
77
+ def self.default
78
+ @default ||= Client.new
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Firstclasspostcodes
4
+ class Configuration
5
+ # Defines API keys used with API Key authentications.
6
+ #
7
+ # @return [String] the value of the API key being used
8
+ #
9
+ # @example parameter name is "api_key", API key is "xxx"
10
+ # config.api_key['api_key'] = 'xxx'
11
+ attr_accessor :api_key
12
+
13
+ # Defines url host
14
+ attr_reader :host
15
+
16
+ # Defines the content type requested and returned
17
+ attr_reader :content
18
+
19
+ # Defines HTTP protocol to be used
20
+ attr_reader :protocol
21
+
22
+ # Defines url base path
23
+ attr_reader :base_path
24
+
25
+ # Defines the logger used for debugging.
26
+ # Default to `Rails.logger` (when in Rails) or logging to STDOUT.
27
+ #
28
+ # @return [#debug]
29
+ attr_accessor :logger
30
+
31
+ # Set this to enable/disable debugging. When enabled (set to true), HTTP request/response
32
+ # details will be logged with `logger.debug` (see the `logger` attribute).
33
+ # Default to false.
34
+ #
35
+ # @return [true, false]
36
+ attr_accessor :debug
37
+
38
+ # The time limit for HTTP request in seconds.
39
+ # Default to 0 (never times out).
40
+ attr_accessor :timeout
41
+
42
+ ### TLS/SSL setting
43
+ # Set this to false to skip verifying SSL certificate when calling API from https server.
44
+ # Default to true.
45
+ #
46
+ # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks.
47
+ #
48
+ # @return [true, false]
49
+ attr_accessor :verify_ssl
50
+
51
+ ### TLS/SSL setting
52
+ # Set this to false to skip verifying SSL host name
53
+ # Default to true.
54
+ #
55
+ # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks.
56
+ #
57
+ # @return [true, false]
58
+ attr_accessor :verify_ssl_host
59
+
60
+ ### TLS/SSL setting
61
+ # Set this to customize the certificate file to verify the peer.
62
+ #
63
+ # @return [String] the path to the certificate file
64
+ #
65
+ # @see The `cainfo` option of Typhoeus, `--cert` option of libcurl. Related source code:
66
+ # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145
67
+ attr_accessor :ssl_ca_cert
68
+
69
+ ### TLS/SSL setting
70
+ # Client certificate file (for client certificate)
71
+ attr_accessor :cert_file
72
+
73
+ ### TLS/SSL setting
74
+ # Client private key file (for client certificate)
75
+ attr_accessor :key_file
76
+
77
+ def initialize
78
+ @api_key = nil
79
+ @debug = false
80
+ @host = "api.firstclasspostcodes.com"
81
+ @content = "json"
82
+ @protocol = "https"
83
+ @base_path = "/data"
84
+ @timeout = 0
85
+ @verify_ssl = true
86
+ @verify_ssl_host = true
87
+ @cert_file = nil
88
+ @key_file = nil
89
+ @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
90
+ yield(self) if block_given?
91
+ end
92
+
93
+ # The default Configuration object.
94
+ def self.default
95
+ @default ||= Configuration.new
96
+ end
97
+
98
+ def configure
99
+ yield(self) if block_given?
100
+ end
101
+
102
+ def debug?
103
+ debug
104
+ end
105
+
106
+ def geo_json?
107
+ content == "geo+json"
108
+ end
109
+
110
+ def content=(content)
111
+ raise StandardError, `"#{content}" is not a valid content-type` unless %w[json geo+json].include?(content)
112
+
113
+ @content = content
114
+ end
115
+
116
+ def protocol=(protocol)
117
+ # remove :// from protocol
118
+ @protocol = protocol.sub(%r{://}, "")
119
+ end
120
+
121
+ def host=(host)
122
+ # remove http(s):// and anything after a slash
123
+ @host = host.sub(%r{https?://}, "").split("/").first
124
+ end
125
+
126
+ def base_path=(base_path)
127
+ # Add leading and trailing slashes to base_path
128
+ @base_path = "/#{base_path}".gsub(%r{/+}, "/")
129
+ @base_path = "" if @base_path == "/"
130
+ end
131
+
132
+ def base_url
133
+ path = [host, base_path].join("/").gsub(%r{/+}, "/")
134
+ "#{protocol}://#{path}".sub(%r{/+\z}, "")
135
+ end
136
+
137
+ def to_request_params
138
+ params = {
139
+ headers: {
140
+ 'x-api-key': api_key,
141
+ accept: "application/#{content}; q=1.0, application/json; q=0.5",
142
+ },
143
+ timeout: timeout,
144
+ ssl_verifypeer: verify_ssl,
145
+ ssl_verifyhost: verify_ssl_host ? 2 : 0,
146
+ sslcert: cert_file,
147
+ sslkey: key_file,
148
+ verbose: debug,
149
+ }
150
+
151
+ params[:cainfo] = ssl_ca_cert if ssl_ca_cert
152
+
153
+ params
154
+ end
155
+ end
156
+ end