ps_pop_client 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f1e22adb89cd91257fbce292b29633fe8dd92ea
4
+ data.tar.gz: 22cd5234ef5b4f595d271c1064bf5c15ffc1c139
5
+ SHA512:
6
+ metadata.gz: 36431f30f538e11c299c552a440a20ce43462d8679b3a277e577e907f17ea95ff113e673f902f5d341bcc8aef40ffa234ff22d0b40921e68411df147d779db5f
7
+ data.tar.gz: 21e3dbb11e76723c8f80f0979e969b84fe4ce06b3fecc043aded436864afb4586d93e774a6cf5257bd6eaf953f5b26e90756c057383dcc2ae742e8d1b2b197e9
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in peerstreet_gem.gemspec
4
+ gemspec
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ps_pop_client (0.1.1)
5
+ json
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.3)
11
+ json (2.2.0)
12
+ rspec (3.7.0)
13
+ rspec-core (~> 3.7.0)
14
+ rspec-expectations (~> 3.7.0)
15
+ rspec-mocks (~> 3.7.0)
16
+ rspec-core (3.7.1)
17
+ rspec-support (~> 3.7.0)
18
+ rspec-expectations (3.7.0)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.7.0)
21
+ rspec-mocks (3.7.0)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.7.0)
24
+ rspec-support (3.7.1)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ ps_pop_client!
31
+ rspec
32
+
33
+ BUNDLED WITH
34
+ 1.14.6
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Harsh Singh
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,59 @@
1
+ ## PeerStreet Population Estimate Ruby Client
2
+ This is gem is built as Ruby client to access to the endpoint:</br>
3
+ https://pstreet-api.herokuapp.com/api/v1/cbsa/find?cbsa_ids[]=15540&cbsa_ids[]=11260&zip_codes[]=79607
4
+
5
+ ## Installation
6
+ 1. Rails App
7
+ #### Add the following line to Gemfile
8
+ ```
9
+ gem 'ps_pop_client'
10
+ ```
11
+
12
+ #### Execute 'bundle'
13
+ ```
14
+ bundle install
15
+ ```
16
+ 2. Cli
17
+ ```
18
+ gem install 'ps_pop_client'
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Send Request
24
+
25
+ ##### step 1.
26
+ ```ruby
27
+ required 'ps_pop_client'
28
+ ```
29
+ ##### step 2.
30
+ ```ruby
31
+ # request params
32
+ params = {:cbsa_ids => [11260,3232],
33
+ :zip_codes => [79607, 90254],
34
+ :name => "South Bay"
35
+ }
36
+ ## 1. default endpoint is "https://pstreet-api.herokuapp.com"
37
+ PSClient::Api.new.find(params)
38
+
39
+ ## 2. include your own endpoint:
40
+ PSClient::Api.new({:base_uri => "https://myown-api.com"}).find(params)
41
+ ```
42
+
43
+ ##### Params Fields Definition
44
+ | Items | Description | Type |
45
+ | :------------ |:---------------:| -----:|
46
+ | cbsa_ids | CBSA Codes| Array |
47
+ | zip_codes | Zip Codes | Array|
48
+ | name | MSA Name | String |
49
+
50
+ ### Run Rspec
51
+ ```
52
+ rspec
53
+ ```
54
+
55
+
56
+
57
+
58
+
59
+
@@ -0,0 +1,6 @@
1
+ require_relative 'ps_pop_client/version'
2
+ require_relative 'ps_pop_client/request'
3
+ require_relative 'ps_pop_client/response'
4
+ require_relative 'ps_pop_client/error'
5
+ require_relative 'ps_pop_client/base_client'
6
+ require_relative 'ps_pop_client/client'
@@ -0,0 +1,12 @@
1
+
2
+ module PSClient
3
+ class BaseApi
4
+ def initialize(args={ :base_uri => nil })
5
+ @base_uri = args[:base_uri]
6
+ end
7
+
8
+ def request(path)
9
+ APIConnection.new(@base_uri, path).get
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ DEFAULT_URI = 'https://pstreet-api.herokuapp.com'
2
+
3
+ module PSClient
4
+ require_relative 'tools/request_util'
5
+ class Api < BaseApi
6
+ def initialize(args={ :base_uri => DEFAULT_URI })
7
+ super(args)
8
+ end
9
+
10
+ def find(req={})
11
+ begin
12
+ parsed_req = parse_req_to_symbols(req)
13
+ check_request(parsed_req)
14
+ uri = encode_uri(parsed_req)
15
+ response = request(uri)
16
+ check_status(response)
17
+ fitlered_response = filter_records(parsed_req, response)
18
+ return Response.new(fitlered_response).parse
19
+ rescue => e
20
+ return Response.new(e).parse_error
21
+ end
22
+ end
23
+
24
+ private
25
+ def check_status(response)
26
+ return if (200..299).cover?(response.code.to_i)
27
+ raise Error.new(response.message, response.code)
28
+ end
29
+
30
+ def filter_records(req, response)
31
+ ResponseUtil.filter_records(req, response)
32
+ end
33
+
34
+ def parse_req_to_symbols(req)
35
+ RequestUtil.parse_to_symbol(req)
36
+ end
37
+ def check_request(req)
38
+ RequestUtil.validate_find_request(req)
39
+ end
40
+ def encode_uri(req)
41
+ cbsa_ids_uri = (req.keys.include?(:cbsa_ids) && req[:cbsa_ids].kind_of?(Array)) ? req[:cbsa_ids].inject("") { |s, a| s + "cbsa_ids[]=" + a.to_s + "&" } : ""
42
+ zipcodes_uri = (req.keys.include?(:zip_codes) && req[:zip_codes].kind_of?(Array)) ? req[:zip_codes].inject("") { |s, a| s + "zip_codes[]=" + a.to_s + "&" } : ""
43
+ name = (req.keys.include?(:name) && req[:name].kind_of?(String)) ? "name=" + req[:name] : ""
44
+ "/api/v1/cbsa/find?" + cbsa_ids_uri + zipcodes_uri + name
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,9 @@
1
+ module PSClient
2
+ class Error < StandardError
3
+ attr_reader :code
4
+ def initialize(message = nil, code = nil)
5
+ super(message)
6
+ @code = (code) ? code : 500
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module PSClient
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ class APIConnection
6
+ def initialize(uri=nil,path=nil)
7
+ @base_uri = uri
8
+ @path = path
9
+ end
10
+
11
+ def get
12
+ url = get_url
13
+ request = Net::HTTP::Get.new(url)
14
+ http = Net::HTTP.new(url.host, url.port)
15
+ http.use_ssl = true if url.scheme == 'https'
16
+ response = http.request(request)
17
+ return response
18
+ end
19
+
20
+ private
21
+ def get_url
22
+ URI.parse("#{@base_uri}#{@path}")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module PSClient
2
+ require 'json'
3
+ require_relative 'tools/error_util'
4
+ require_relative 'tools/response_util'
5
+ class Response
6
+ def initialize(response= {})
7
+ @response=response
8
+ end
9
+
10
+ def parse
11
+ res_body = JSON.parse(@response.body)
12
+ { status: @response.code.to_i , data:res_body["data"]}
13
+ end
14
+
15
+ def parse_error
16
+ ErrorUtil.parse_response(@response);
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module ErrorUtil
2
+ def self.parse_response(e)
3
+ response = {
4
+ status: (e.class == PSClient::Error) ? e.code.to_i : 500,
5
+ message: e.message
6
+ }
7
+ return response
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module RequestUtil
2
+ require_relative '../error'
3
+ def self.validate_find_request(req)
4
+ if !req.kind_of?(Hash)
5
+ raise PSClient::Error.new("Invalid request with wrong data type.", 400)
6
+ elsif !req.keys.include?(:cbsa_ids) && !req.keys.include?(:zip_codes) && !req.keys.include?(:name)
7
+ raise PSClient::Error.new("Must have at least one request on cbsa_ids, zip_codes or name params.", 400)
8
+ elsif (req.keys.include?(:cbsa_ids) && !req[:cbsa_ids].kind_of?(Array)) || (req.keys.include?(:zip_codes) && !req[:zip_codes].kind_of?(Array)) || (req.keys.include?(:name) && !req[:name].kind_of?(String))
9
+ raise PSClient::Error.new("Invalid request with wrong data type.", 400)
10
+ end
11
+ end
12
+ def self.parse_to_symbol(req)
13
+ raise PSClient::Error.new("Invalid request with wrong data type.", 400) if !req.kind_of?(Hash)
14
+ Hash[req.map{|(k,v)| [k.to_sym,v]}]
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module ResponseUtil
2
+ def self.filter_by_zipcodes(a_for, a_against)
3
+ return [] if !a_for.kind_of?(Array) || a_for.count == 0
4
+ result = a_for.select do |e|
5
+ a_against.include?(e)
6
+ end
7
+ return result
8
+ end
9
+
10
+ def self.filter_records(req,response)
11
+ return response unless req[:zip_codes].kind_of?(Array)
12
+ res_body = JSON.parse(response.body)
13
+ records = res_body["data"]["records"]
14
+ records.each do |record|
15
+ if self.should_filter?(req[:cbsa_ids], record["cbsa_id"],record["zip_code"], req[:zip_codes])
16
+ record["zip_code"] = self.filter_by_zipcodes(record["zip_code"], req[:zip_codes])
17
+ end
18
+ end
19
+ res_body["data"]["records"] = records
20
+ res_body["data"]["count"] = records.length
21
+ response.body = JSON.generate(res_body)
22
+ return response
23
+ end
24
+
25
+ def self.should_filter?(cbsa_ids_array, cbsa_id, res_zipcodes,req_zipcodes)
26
+ cbsa_ids = !cbsa_ids_array ? [] : cbsa_ids_array
27
+ !(cbsa_ids.include? cbsa_id.to_i) && self.compare_zipcodes(res_zipcodes,req_zipcodes)
28
+ end
29
+
30
+ def self.compare_zipcodes(a, b)
31
+ (a&b).size > 0
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module PSClient
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path("../lib/ps_pop_client/version", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'ps_pop_client'
5
+ s.version = PSClient::VERSION
6
+ s.date = '2019-09-16'
7
+ s.summary = "API To fetch population growth on CBSA"
8
+ s.description = "Ruby Client to interact with CBSA api as described on PeerStreet take home assignment."
9
+ s.authors = ["Steven Chow"]
10
+ s.email = 'stevenwchow@gmail.com'
11
+ # s.files = ['lib/ps_pop_client.rb',
12
+ # 'lib/ps_pop_client/*.rb',
13
+ # 'lib/ps_pop_client/tools/**/*']
14
+ s.files = `git ls-files`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) }
16
+ # s.files = ['lib/ps_pop_client.rb',
17
+ # 'lib/ps_pop_client/base_client.rb',
18
+ # 'lib/ps_pop_client/client.rb',
19
+ # 'lib/ps_pop_client/error.rb',
20
+ # 'lib/ps_pop_client/request.rb',
21
+ # 'lib/ps_pop_client/response.rb',
22
+ # 'lib/ps_pop_client/version.rb']
23
+ s.require_paths = ['lib']
24
+ s.homepage = 'https://github.com/schow6272003/ps_gem'
25
+ s.license = 'MIT'
26
+ s.add_dependency 'json'
27
+ s.add_development_dependency 'rspec'
28
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PSClient::VERSION do
4
+ it 'has ver. number' do
5
+ expect(PSClient::VERSION).not_to be_empty
6
+ end
7
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe PSClient::Api do
4
+ before do
5
+ @client = described_class.new
6
+ end
7
+
8
+ describe "#find" do
9
+ subject {@client.find(req)}
10
+
11
+ context "when valid request params" do
12
+ let(:req) { { "cbsa_ids" => [11260] }}
13
+ it "expects to return with 200 status code" do
14
+ expect(subject[:status]).to eq 200
15
+ end
16
+
17
+ it "expects to return 1 record" do
18
+ expect(subject[:data]["records"].count).to eq 1
19
+ end
20
+
21
+ it "expects cbsa code to be 11260" do
22
+ expect(subject[:data]["records"][0]["cbsa_id"]).to eq 11260
23
+ end
24
+
25
+ it "expects to include zipcodes" do
26
+ expect(subject[:data]["records"][0]["zip_code"]).to include(99501,99502,99503)
27
+ end
28
+
29
+ it "expects to include msa name" do
30
+ expect(subject[:data]["records"][0]["name"]).to eq "Anchorage, AK"
31
+ end
32
+
33
+ it "expects to include 2014 pop estimate" do
34
+ expect(subject[:data]["records"][0]["pop_estimate"][4]["number"]).to eq 398642
35
+ end
36
+ it "expects to include 2015 pop estimate" do
37
+ expect(subject[:data]["records"][0]["pop_estimate"][5]["number"]).to eq 399790
38
+ end
39
+ end
40
+
41
+ context "when search using zipcodes" do
42
+ let(:req) { { :zip_codes => [48813,5482] }}
43
+
44
+ it "expects to return with 200 status code" do
45
+ expect(subject[:status]).to eq 200
46
+ end
47
+
48
+ it "expects to return 2 record" do
49
+ expect(subject[:data]["count"]).to eq 2
50
+ end
51
+
52
+ it "expects first record to have zipcode 48813" do
53
+ expect(subject[:data]["records"][0]["zip_code"]).to include(48813)
54
+ end
55
+
56
+ it "expects first record to have zipcode 5482" do
57
+ expect(subject[:data]["records"][1]["zip_code"]).to include(5482)
58
+ end
59
+ end
60
+
61
+ context "when search msa name by keyword" do
62
+ let(:req) { { :name => "Burlington" }}
63
+
64
+ it "expects to return with 200 status code" do
65
+ expect(subject[:status]).to eq 200
66
+ end
67
+
68
+ it "expects to return 2 record" do
69
+ expect(subject[:data].count).to eq 2
70
+ end
71
+
72
+ it "expects to include 'Burlington' on msa name" do
73
+ expect(subject[:data]["records"][0]["name"]).to include("Burlington")
74
+ end
75
+ end
76
+
77
+ context "When no records are found" do
78
+ let (:req) {{:name => "Hello World!"}}
79
+
80
+ it "expects to return with 200 status code" do
81
+ expect(subject[:status]).to eq 200
82
+ end
83
+
84
+ it "expects to return 0 records" do
85
+ expect(subject[:data]["records"].count).to eq 0
86
+ end
87
+ end
88
+
89
+ context "when request is invalid" do
90
+ let(:req) { {}}
91
+
92
+ it "expect to response http status to be" do
93
+ expect(subject[:status]).to eq 400
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+ require 'ps_pop_client'
4
+
5
+ RSpec.configure do |config|
6
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ps_pop_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Steven Chow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Ruby Client to interact with CBSA api as described on PeerStreet take
42
+ home assignment.
43
+ email: stevenwchow@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitgnore"
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE.txt
52
+ - README.md
53
+ - lib/ps_pop_client.rb
54
+ - lib/ps_pop_client/base_client.rb
55
+ - lib/ps_pop_client/client.rb
56
+ - lib/ps_pop_client/error.rb
57
+ - lib/ps_pop_client/request.rb
58
+ - lib/ps_pop_client/response.rb
59
+ - lib/ps_pop_client/tools/error_util.rb
60
+ - lib/ps_pop_client/tools/request_util.rb
61
+ - lib/ps_pop_client/tools/response_util.rb
62
+ - lib/ps_pop_client/version.rb
63
+ - ps_pop_client.gemspec
64
+ - spec/ps_pop_client/client_spec.rb
65
+ - spec/ps_pop_client_spec.rb
66
+ - spec/spec_helper.rb
67
+ homepage: https://github.com/schow6272003/ps_gem
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.6.12
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: API To fetch population growth on CBSA
91
+ test_files: []