singleplatform 0.1.1 → 0.2.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
  SHA1:
3
- metadata.gz: 2cec3922463afab967e8fd8cb232e18b38b03a5b
4
- data.tar.gz: 4246d0e5f2c24643433faa462f324be89772c144
3
+ metadata.gz: c145adab5f968aa717643063bfad1482d70ed11e
4
+ data.tar.gz: d5311c4cb78d5363440580d00dde210609b578bb
5
5
  SHA512:
6
- metadata.gz: 28a77401efa0875e85a135be5f7742f90df1a873fe2774028af577a3e658eaae98c017998f358521aaed54351b37f9a4047579a5cb8bb8e08016bf425f557cde
7
- data.tar.gz: 8a331be44e7e42b30068d08bddac1c004e71dce901a5d2b3f8ce3b878258cd2c13115ad7efb990665f7a094226d79ea102bddc3712fe4042a15d53b95a72be91
6
+ metadata.gz: 2717d6cb948eb3732df5f9f1fed81598f83a12019d2d065f60cb8fed4a9293410b617032bab46e4c24f621c6f88aab3ce7f4b5afd99607783fe55d95335b0137
7
+ data.tar.gz: 919cfa303a3406c364ad532a646012114bb609b1333927502097814a4d9b54b5ae5131dffc7317e400645bcac61cf36ccfd5fc2674e2dab690732ad379543655
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .DS_Store
2
2
  .env
3
- singleplatform-0.1.0.gem
3
+ singleplatform-0.1.0.gem
4
+ singleplatform-0.1.1.gem
data/Gemfile CHANGED
@@ -2,4 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'bundler'
4
4
  gem 'httparty'
5
- gem 'hashie'
5
+ gem 'hashie'
6
+ gem 'rspec'
7
+ gem 'webmock'
@@ -1,23 +1,38 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- dotenv (2.1.1)
4
+ addressable (2.4.0)
5
+ crack (0.4.3)
6
+ safe_yaml (~> 1.0.0)
7
+ diff-lcs (1.2.5)
8
+ hashdiff (0.3.0)
5
9
  hashie (3.4.3)
6
10
  httparty (0.14.0)
7
11
  multi_xml (>= 0.5.2)
8
12
  multi_xml (0.5.5)
13
+ rspec (2.99.0)
14
+ rspec-core (~> 2.99.0)
15
+ rspec-expectations (~> 2.99.0)
16
+ rspec-mocks (~> 2.99.0)
17
+ rspec-core (2.99.2)
18
+ rspec-expectations (2.99.2)
19
+ diff-lcs (>= 1.1.3, < 2.0)
20
+ rspec-mocks (2.99.4)
21
+ safe_yaml (1.0.4)
22
+ webmock (2.1.0)
23
+ addressable (>= 2.3.6)
24
+ crack (>= 0.3.2)
25
+ hashdiff
9
26
 
10
27
  PLATFORMS
11
28
  ruby
12
29
 
13
30
  DEPENDENCIES
14
31
  bundler
15
- dotenv
16
32
  hashie
17
33
  httparty
18
-
19
- RUBY VERSION
20
- ruby 2.2.2p95
34
+ rspec
35
+ webmock
21
36
 
22
37
  BUNDLED WITH
23
38
  1.12.5
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Jeff Gharakhanian
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -42,13 +42,13 @@ With an initialized client, you can request information on locations in SinglePl
42
42
  client.location('nobu')
43
43
  ```
44
44
 
45
- This will return a Hashie::Mash pseudo object, which allows you to access attributes on the response using dot notation. Nested attributes continue to return Hashie::Mash objects until there are no children.
45
+ This will return a ```Singleplatform::Response``` object. Access the response body or code with ```.body``` and ```.code``` respectively. Calling ```.body``` returns a ```Hashie::Mash``` pseudo object which allows you to access all location attributes with dot notation.
46
46
 
47
47
  ```ruby
48
- location = client.location('nobu') # => #<Hashie::Mash ... >
49
- location.name # => "Nobu"
50
- location.attributes # => #<Hashie::Mash ... >
51
- location.attributes.drive_thru # => false
48
+ response = client.location('nobu') # => #<Singleplatform::Response ... >
49
+ response.body.name # => "Nobu"
50
+ response.body.attributes # => #<Hashie::Mash ... >
51
+ response.body.attributes.drive_thru # => false
52
52
  ```
53
53
 
54
54
  See SinglePlatform's API documentation for a full list of attributes.
@@ -58,27 +58,42 @@ See SinglePlatform's API documentation for a full list of attributes.
58
58
  You can retrieve locations en masse by calling:
59
59
 
60
60
  ```ruby
61
- client.locations_updated_since('2016-08-01', limit: 100)
61
+ response = client.locations_updated_since('2016-08-01', limit: 100)
62
62
  ```
63
63
 
64
- Results are paginated. The maximum (and default) limit per page is 5000.
64
+ Results are paginated. The maximum (and default) limit per page is 5000. To access the next page of results, call:
65
+
66
+ ```ruby
67
+ response.next
68
+ ```
65
69
 
66
70
  ### Menus
67
- SinglePlatform locations have menus or lists of products and services that you can access with a configured API client. The following call returns an Array of Hashie::Mash objects.
71
+ SinglePlatform locations have menus or lists of products and services that you can access with a configured API client. The following call returns a ```Singleplatform::Response``` object whose body contains an Array of ```Hashie::Mash``` objects.
68
72
 
69
73
  ```ruby
70
- menus = client.menus_for('nobu') # => #<Array ... >
71
- menus.first.name # => "Dinner Menu"
74
+ response = client.menus_for('nobu') # => #<Array ... >
75
+ response.body.first.name # => "Dinner Menu"
72
76
  ```
73
77
 
74
78
  See SinglePlatform's API documentation for the Menu schema with a full list of attributes.
75
79
 
76
80
  ### Photos
77
- Many SinglePlatform locations have photos, both at the business and menu-item level. Returns an Array of Hashie::Mash objects.
81
+
82
+ #### Fetching photos for a particular location
83
+
84
+ Many SinglePlatform locations have photos, both at the business and menu-item level. Returns a ```Singleplatform::Response``` object whose body is an Array of menus.
78
85
 
79
86
  ```ruby
80
- photos = client.photos_for('no') # => #<Array ... >
81
- photos.first.type # => "Product"
82
- photos.first.url # => "http://xyz.cloudfront.net/.../39bf7671bc7d006f4cef72d94eee24aeec7615d2.jpg"
87
+ response = client.photos_for('no') # => #<Singleplatform::Response ... >
88
+ response.body.first.type # => "Product"
89
+ response.body.first.url # => "http://xyz.cloudfront.net/.../39bf7671bc7d006f4cef72d94eee24aeec7615d2.jpg"
83
90
  ```
84
91
 
92
+ #### Fetching all photos updated since a given date
93
+
94
+ Similarly to ```locations_updated_since```, calling ```photos_updated_since``` returns a set of paginated results. Get the next page of results by calling ```next``` on the ```Singleplatform:::Response``` object.
95
+
96
+ ````ruby
97
+ response = client.photos_updated_since('2016-09-01') # => #<Singleplatform::Response ... >
98
+ response
99
+ ````
data/Rakefile CHANGED
@@ -1,9 +0,0 @@
1
- require 'rake/testtask'
2
-
3
- Rake::TestTask.new do |t|
4
- t.libs << ['test']
5
- t.pattern = 'test/**/test_*.rb'
6
- end
7
-
8
- desc "Run tests"
9
- task :default => :test
@@ -1,15 +1,12 @@
1
1
  require 'singleplatform/client'
2
2
 
3
3
  module Singleplatform
4
-
5
4
  class << self
6
-
7
5
  # Alias for Singleplatform::Client.new
8
6
  #
9
7
  # @return [Singleplatform::Client]
10
8
  def new(args = {})
11
9
  Singleplatform::Client.new(args)
12
10
  end
13
-
14
11
  end
15
12
  end
@@ -1,4 +1,6 @@
1
- require 'singleplatform/client/request'
1
+ require 'singleplatform/request'
2
+ require 'singleplatform/response'
3
+ require 'singleplatform/error'
2
4
  require 'singleplatform/client/locations'
3
5
  require 'singleplatform/client/menus'
4
6
  require 'singleplatform/client/photos'
@@ -9,9 +11,7 @@ module Singleplatform
9
11
  class Client
10
12
  attr_accessor :base_url, :client_id, :client_secret
11
13
 
12
- BASE_URL = 'http://publishing-api.singleplatform.com'
13
- CLIENT_ID = ENV['CLIENT_ID'].freeze
14
- CLIENT_SECRET = ENV['CLIENT_SECRET'].freeze
14
+ BASE_URL = 'http://publishing-api.singleplatform.com'
15
15
 
16
16
  include Singleplatform::Client::Locations
17
17
  include Singleplatform::Client::Menus
@@ -24,6 +24,7 @@ module Singleplatform
24
24
  @base_url = BASE_URL
25
25
  @client_id = args[:client_id]
26
26
  @client_secret = args[:client_secret]
27
+ raise Error::MissingCredentialsError if credentials_missing?
27
28
  end
28
29
 
29
30
  # Form the complete URL for a given endpoint
@@ -34,7 +35,7 @@ module Singleplatform
34
35
  # @param params [Hash]
35
36
  # @return [String]
36
37
  def generate_url(path, params = {})
37
- params['client'] = client_id
38
+ params['client'] ||= client_id
38
39
  signature_base_string = "#{path}?#{URI.encode_www_form(params)}"
39
40
  "#{base_url}#{signature_base_string}&signature=#{generate_signature(signature_base_string)}"
40
41
  end
@@ -47,5 +48,33 @@ module Singleplatform
47
48
  key = OpenSSL::HMAC.digest('sha1', client_secret, base_string)
48
49
  CGI::escape(Base64.encode64(key).chomp)
49
50
  end
51
+
52
+ # Helper method to determine if credentials are missing and, if
53
+ # so, raise Error::MissingCredentials
54
+ #
55
+ # @return [Boolean]
56
+ def credentials_missing?
57
+ client_id.nil? || client_secret.nil?
58
+ end
59
+
60
+ # Helper method to determine if any number of params are nil,
61
+ # empty or just spaces.
62
+ #
63
+ # @return [Boolean]
64
+ def valid_params?(*args)
65
+ args.map { |a| return false if a.nil? || a.to_s.gsub(/\s/, '').empty? }
66
+ true
67
+ end
68
+
69
+ # Helper method to determine if a date is valid
70
+ #
71
+ # @return [Boolean]
72
+ def valid_date?(date)
73
+ d = date.split('-')
74
+ d.map! { |d| d.to_i }
75
+ Date.valid_date?(d[0], d[1], d[2])
76
+ rescue TypeError
77
+ return false
78
+ end
50
79
  end
51
80
  end
@@ -4,11 +4,20 @@ module Singleplatform
4
4
  # Fetch information about a specific location
5
5
  #
6
6
  # @param id [String]
7
- # @param options [Hash]
8
- # @option options [String] :format Short menu ('short') available
9
- # @return [Hashie::Mash]
10
- def location(id, options = {})
11
- url = generate_url("/locations/#{id}/", options)
7
+ # @return [Singleplatform::Response]
8
+ def location(id)
9
+ raise Error::InvalidLocationError unless valid_params?(id)
10
+ url = generate_url("/locations/#{id}/")
11
+ Request.get(url)
12
+ end
13
+
14
+ # Fetch location, menus and photo data for a specific location
15
+ #
16
+ # @param id [String]
17
+ # @return [Singleplatform::Response]
18
+ def all_for(id)
19
+ raise Error::InvalidLocationError unless valid_params?(id)
20
+ url = generate_url("/locations/#{id}/all")
12
21
  Request.get(url)
13
22
  end
14
23
 
@@ -17,8 +26,9 @@ module Singleplatform
17
26
  # @param date [String]
18
27
  # @param options [Hash]
19
28
  # @option options [Fixnum] :limit Maximum (default) 5000 per page
20
- # @return [Hashie::Mash]
29
+ # @return [Singleplatform::Response]
21
30
  def locations_updated_since(date, options = {})
31
+ raise Error::InvalidDateError unless valid_date?(date)
22
32
  url = generate_url(
23
33
  '/locations/updated_since/',
24
34
  { date: date }.merge(options)
@@ -8,6 +8,7 @@ module Singleplatform
8
8
  # @option options [String] :format Short menu available ('short')
9
9
  # @return [Hashie::Mash]
10
10
  def menus_for(id, options = {})
11
+ raise Error::InvalidLocationError unless valid_params?(id)
11
12
  url = generate_url("/locations/#{id}/menus", options)
12
13
  Request.get(url)
13
14
  end
@@ -10,6 +10,7 @@ module Singleplatform
10
10
  # @option options [String] :type (interior, exterior, item, logo, uncategorized)
11
11
  # @return [Hashie::Mash]
12
12
  def photos_for(id, options = {})
13
+ raise Error::InvalidLocationError unless valid_params?(id)
13
14
  url = generate_url("/location/#{id}/photos", options)
14
15
  Request.get(url)
15
16
  end
@@ -17,9 +18,15 @@ module Singleplatform
17
18
  # Fetch photos added/updated since a given date
18
19
  #
19
20
  # @param date [String]
21
+ # @param options [Hash]
22
+ # @option options [Fixnum] :limit Maximum (default) 5000 per page
20
23
  # @return [Hashie::Mash]
21
- def photos_updated_since(date)
22
- url = generate_url('/photos/updated_since/', date: date)
24
+ def photos_updated_since(date, options = {})
25
+ raise Error::InvalidDateError unless valid_params?(date)
26
+ url = generate_url(
27
+ '/photos/updated_since/',
28
+ { date: date }.merge(options)
29
+ )
23
30
  Request.get(url)
24
31
  end
25
32
  end
@@ -0,0 +1,39 @@
1
+ module Singleplatform
2
+ module Error
3
+ class Base < StandardError
4
+ def initialize(msg)
5
+ super(msg)
6
+ end
7
+ end
8
+
9
+ class MissingCredentialsError < Base
10
+ def initialize(msg = "You must initialize a client with ID and Secret.")
11
+ super
12
+ end
13
+ end
14
+
15
+ class ApiError < Base
16
+ def initialize(msg = "Oops! There was an error.")
17
+ super
18
+ end
19
+ end
20
+
21
+ class MissingParametersError < Base
22
+ def initialize(msg = "Your request is missing required parameters.")
23
+ super
24
+ end
25
+ end
26
+
27
+ class InvalidLocationError < Base
28
+ def initialize(msg = "Your request must supply a valid location.")
29
+ super
30
+ end
31
+ end
32
+
33
+ class InvalidDateError < Base
34
+ def initialize(msg = "Your request must supply a valid date.")
35
+ super
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ require 'httparty'
2
+ require 'hashie'
3
+
4
+ module Singleplatform
5
+ class Request
6
+ # Make an HTTP get request to given URL
7
+ #
8
+ # @param url [String]
9
+ # @return [Singleplatform::Response]
10
+ def self.get(url)
11
+ tries ||= 3
12
+ response = HTTParty.get(url)
13
+ rescue
14
+ sleep 3
15
+ if tries -= 1 > 0
16
+ retry
17
+ end
18
+ raise(
19
+ Error::RequestError,
20
+ "Unable to transmit request to SinglePlatform. Try again later or contact technical support."
21
+ )
22
+ else
23
+ raise(
24
+ Error::ApiError,
25
+ "#{response.code}: #{response['errorMessage']}"
26
+ ) if response.code != 200
27
+ Response.new(
28
+ code: response.code,
29
+ body: self.parse_response_body(response.body),
30
+ # Pass the calling method to the Response object so it knows which
31
+ # method to call when API results are iterable
32
+ origin: caller_locations(1,1)[0].label
33
+ )
34
+ end
35
+
36
+ # Transform API JSON response to Hashie::Mash pseudo object
37
+ #
38
+ # @return [Hashie::Mash]
39
+ def self.parse_response_body(body)
40
+ return body unless JSON.parse(body)
41
+ Hashie::Mash.new(JSON.parse(body)).data
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,65 @@
1
+ require 'cgi'
2
+
3
+ module Singleplatform
4
+ class Response
5
+ attr_accessor :code, :body, :next_page, :origin
6
+
7
+ def initialize(args)
8
+ @code = args[:code]
9
+ @body = args[:body]
10
+ @next_page = args[:body].respond_to?(:next) ? args[:body].next : nil
11
+ @origin = args[:origin].to_s
12
+ end
13
+
14
+ # An iterator for retrieving the next page of results from
15
+ # API response.
16
+ #
17
+ # @note Will only work with Client#locations_updated_since and
18
+ # #photos_updated_since at this time.
19
+ #
20
+ # @return [Hashie::Mash]
21
+ def next
22
+ return nil if next_page.nil? || next_page.empty?
23
+ params = prepare_params(next_page)
24
+ client = Singleplatform.new(
25
+ client_id: ENV['CLIENT_ID'],
26
+ client_secret: ENV['CLIENT_SECRET']
27
+ )
28
+ new_page = client.public_send(
29
+ origin.to_sym,
30
+ params.delete('date'), params
31
+ )
32
+ refresh(new_page)
33
+ end
34
+
35
+ private
36
+
37
+ # Update Response instance variables
38
+ #
39
+ # @param [Singleplatform::Response]
40
+ # @return [Singleplatform::Response]
41
+ def refresh(response)
42
+ @code = response.code
43
+ @body = response.body
44
+ @next_page = response.next_page
45
+ response
46
+ end
47
+
48
+ # Take any given URL and parse its query params.
49
+ #
50
+ # @param url [String]
51
+ # @return [Hash]
52
+ def parse_params(url)
53
+ CGI::parse(url.split('?')[-1])
54
+ end
55
+
56
+ # Include additional params required for accessing API
57
+ #
58
+ # @return [Hash]
59
+ def prepare_params(url)
60
+ params = parse_params(url)
61
+ params['client'] = ENV['CLIENT_ID']
62
+ params
63
+ end
64
+ end
65
+ end