citygrid_api 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.7
1
+ 0.0.9
data/citygrid_api.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "citygrid_api"
8
- s.version = "0.0.7"
8
+ s.version = "0.0.9"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Elpizo Choi", "Joseph Chen"]
12
- s.date = "2012-02-27"
12
+ s.date = "2012-03-01"
13
13
  s.description = "Ruby wrapper for CityGrid APIs"
14
14
  s.email = "Joseph.Chen@citygridmedia.com"
15
15
  s.extra_rdoc_files = [
@@ -105,6 +105,7 @@ Gem::Specification.new do |s|
105
105
  "lib/citygrid/api/mutable.rb",
106
106
  "lib/citygrid/api/response.rb",
107
107
  "lib/citygrid/api/searchable.rb",
108
+ "lib/citygrid/citygrid_exceptions.rb",
108
109
  "lib/citygrid/details.rb",
109
110
  "lib/citygrid/listing.rb",
110
111
  "lib/citygrid/offers.rb",
@@ -13,11 +13,11 @@ class CityGrid
13
13
  :body => {"mutateOperationListResource" => [
14
14
  {
15
15
  "operand" => {
16
- "imageType" => type,
17
- "imageName" => name,
18
- "imageFormat" => format,
19
- "image" => image_data
20
- },
16
+ "imageType" => type,
17
+ "imageName" => name,
18
+ "imageFormat" => format,
19
+ "image" => image_data
20
+ },
21
21
  "operator" => "ADD",
22
22
  "userId" => user_id
23
23
  }
data/lib/citygrid/api.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require "httparty"
2
2
  require "json"
3
+ require "citygrid/citygrid_exceptions"
3
4
 
4
5
  class CityGrid
5
6
  class API
6
7
  include HTTParty
8
+ include CityGridExceptions
7
9
  #debug_output $stderr
8
10
 
9
11
  DEFAULT_HEADERS = {
@@ -83,128 +85,146 @@ class CityGrid
83
85
  "options" => Net::HTTP::Options
84
86
  }
85
87
 
86
- # Transform response into API::Response object
87
- # or throw exception if an error exists
88
- def request_and_handle http_method, path, options
89
- if http_method.is_a?(String) || http_method.is_a?(Symbol)
90
- http_method = HTTP_METHODS[http_method.to_s]
91
- raise "Unknown http method: #{http_method}" unless http_method
92
- end
93
-
94
- req_options = default_options.dup
95
- req_options = req_options.merge(options)
96
-
97
- raise ConfigurationError.new "No endpoint defined" if !path || path.empty?
98
- raise ConfigurationError.new "No hostname defined" if !req_options[:base_uri] || req_options[:base_uri].empty?
99
-
100
- req = HTTParty::Request.new http_method, path, req_options
101
-
102
-
103
- begin
104
- response = req.perform
105
- rescue => ex
106
- raise RequestError.new req, ex
107
- ensure
108
- if defined?(Rails.logger)
109
- Rails.logger.info req.to_curl
110
- else
111
- puts req.to_curl
112
- end
113
- end
114
-
115
- if !response.parsed_response.is_a?(Hash)
116
- raise ResponseParseError.new req, response
117
- elsif response["errors"]
118
- raise ResponseError.new req, response["errors"], response
119
- elsif response["message"] && response["message"] == "Invalid Token or Expired"
120
- raise InvalidAuthToken.new
88
+ def strip_unsafe_params options
89
+ puts "OPTIONS: #{options}"
90
+ unsafe_params = {
91
+ :password => "[FILTERED]", :securityCode => "[FILTERED]",
92
+ :cardNumber => "[FILTERED]", :expirationMonth => "[FILTERED]",
93
+ :expirationYear => "[FILTERED]"
94
+ }
95
+ if !options[:query].nil?
96
+ to_merge[:query] = options[:query].merge(unsafe_params.select { |k| options.keys.include? k })
97
+ return options.merge(to_merge)
98
+ end
99
+ end
100
+
101
+ #
102
+ def parse_multiple_responses response
103
+ parsing = response.values.select{ |x| x.is_a? Array }.first
104
+ if parsing.nil? || parsing == []
105
+ ap "Response was too hard to parse... letting it through..."
106
+ return parsing
107
+ elsif parsing != nil && parsing != []
108
+ if parsing[0]["response"]
109
+ parsing = [parsing[0]["response"]["code"], parsing[0]["response"]["message"]]
110
+ return parsing
121
111
  else
122
- return CityGrid::API::Response.new response
112
+ # this accomodates geocode response which does not contain a response node
113
+ ap "Response was too hard to parse... letting it through..."
114
+ return nil
123
115
  end
124
-
125
- rescue => ex
126
- raise ex if CityGrid.raise_errors?
116
+ else
117
+ # We should figure out a better way to do this
118
+ raise CityGridExceptions::APIError.new "Received a JSON error code but it could not be parsed: #{response}"
127
119
  end
128
120
  end
129
121
 
130
- # ERRORS
131
- class APIError < StandardError
132
- attr_accessor :request
133
-
134
- def initialize msg, request
135
- super msg
122
+ # Transform response into API::Response object
123
+ # or throw exception if an error exists
124
+ def request_and_handle http_method, path, options
125
+ if http_method.is_a?(String) || http_method.is_a?(Symbol)
126
+ http_method = HTTP_METHODS[http_method.to_s]
127
+ raise "Unknown http method: #{http_method}" unless http_method
136
128
  end
137
- end
138
129
 
139
- class ResponseError < APIError
140
- attr_accessor :errors, :response
130
+ req_options = default_options.dup
131
+ req_options = req_options.merge(options)
141
132
 
142
- def initialize request, errors, response
143
- self.errors = errors
144
- self.response = response
145
-
146
- super "API returned error message", request
147
- end
148
- end
149
-
150
- class RequestError < APIError
151
- attr_accessor :inner_exception
133
+ raise ConfigurationError.new "No endpoint defined" if !path || path.empty?
134
+ raise ConfigurationError.new "No hostname defined" if !req_options[:base_uri] || req_options[:base_uri].empty?
152
135
 
153
- def initialize request, inner_exception, msg = nil
154
- self.inner_exception = inner_exception
155
- self.request = request
156
- super msg || "Error while performing request: #{inner_exception.message}", request
136
+ # prepare request and sanitized request for logs
137
+ #puts "Options after strip unsafe: #{strip_unsafe_params(req_options)}"
138
+ #puts "options before that: #{req_options}"
139
+ #safe_req_options = strip_unsafe_params(req_options)
140
+ req = HTTParty::Request.new http_method, path, req_options
141
+ #req_to_output = HTTParty::Request.new http_method, path, safe_req_options
142
+ # ap "HERE ARE THE INGREDIENTS OF THE REQUEST:"
143
+ # ap "http_method is: #{http_method}"
144
+ # ap "path is: #{path}"
145
+ # ap "req_options is: #{req_options}"
146
+
147
+ begin
148
+ response = req.perform
149
+ rescue => ex
150
+ raise CityGridExceptions::RequestError.new req, ex
151
+ ensure
152
+ if defined?(Rails.logger)
153
+ Rails.logger.info req.to_curl
154
+ else
155
+ puts req.to_curl
156
+ ap response
157
+ end
157
158
  end
158
- end
159
-
160
- class ResponseParseError < APIError
161
- attr_accessor :server_msg, :description, :raw_response
162
- def initialize request, response
163
- self.raw_response = response
164
- # parse Tomcat error report
165
- if response.match /<title>Apache Tomcat.* - Error report<\/title>/
166
- response.scan(/<p><b>(message|description)<\/b> *<u>(.*?)<\/u><\/p>/).each do |match|
167
- case match[0]
168
- when "message"
169
- self.server_msg = match[1]
170
- when "description"
171
- self.description = match[1]
172
- end
173
- end
174
-
175
- error_body = response.match(/<body>(.*?)<\/body>/m)[1]
176
-
177
- msg = <<-EOS
178
- Unexpected response format. Expected response to be a hash, but was instead:\n#{error_body}\n
179
- EOS
180
159
 
181
- super msg, request
160
+ begin
161
+ # catch unparsable responses (html etc)
162
+ if !response.parsed_response.is_a?(Hash)
163
+ ap "[gem] the response was unparsable (response was not a hash)"
164
+ raise CityGridExceptions::ResponseParseError.new req, response
165
+ # catch responses not in new response format
166
+ elsif response["errors"]
167
+ ap "[gem] An error in the old response format was caught. Raising a general response error..."
168
+ raise CityGridExceptions::ResponseError.new req, response["errors"], response
169
+
170
+ # Parse and handle new response codes
171
+ elsif (response["response"] && response["response"]["code"] != "SUCCESS") &&
172
+ (response["response"] && response["response"]["code"] != 200) &&
173
+ (response["response"] && response["response"]["code"] != 400)
174
+ error_code = response["response"]["code"]
175
+ ap "[gem] The response was contained in the first level of the response hash. Below:"
176
+ ap response
177
+ ap "found error code: #{error_code}"
178
+ ap "****************************************************************************"
179
+ raise CityGridExceptions.appropriate_error(error_code).new req, response, response["response"]["message"].to_s #+ " " + CityGridExceptions.print_superclasses(error_code)
180
+ # if the response is a nested hash/nested hash containing arrays
181
+ elsif response["totalNumEntries"] && response["response"].nil?
182
+ ap "[gem] now parsing a response with multiple entries: #{response}"
183
+ error_code = parse_multiple_responses(response)
184
+ ap "the error code that came back is #{error_code}"
185
+ if error_code.nil? || error_code == []
186
+ ap "[gem] passing over this for now"
187
+ return CityGrid::API::Response.new response # pass over for now
188
+ elsif error_code[0] == "SUCCESS" || error_code[0] == 200 || error_code[0] == 400
189
+ return CityGrid::API::Response.new response
190
+ else
191
+ ap "[gem] we found an error and it was #{error_code[1]}"
192
+ raise CityGridExceptions.appropriate_error(error_code[0]).new req, response, error_code[1].to_s + " "# + CityGridExceptions.print_superclasses(error_code[0])
193
+ end
182
194
  else
183
- msg = <<-EOS
184
- Unexpected response format. Expected response to be a hash, but was instead:\n#{response.parsed_response}\n
185
- EOS
186
-
187
- super msg, request
195
+ return CityGrid::API::Response.new response
188
196
  end
197
+ rescue => ex
198
+ ap "The gem threw an error: #{ex}"
199
+ raise ex if CityGrid.raise_errors?
189
200
  end
190
201
  end
191
202
 
192
- class InvalidAuthToken < StandardError
193
- def initialize message = "Invalid Token or Expired"
194
- super message
195
- end
196
- end
203
+ # Errors
197
204
 
198
- class MissingAuthToken < StandardError
199
- def initialize
200
- super "Missing authToken - token is required"
205
+ class MUSHPendingChanges <StandardError
206
+ def initialize message = "The are currently pending changes in the mush. Cannot update."
207
+ super message
201
208
  end
202
209
  end
203
210
 
211
+ # class InvalidAuthToken < StandardError
212
+ # def initialize message = "Invalid Token or Expired"
213
+ # super message
214
+ # end
215
+ # end
216
+
217
+ # class MissingAuthToken < StandardError
218
+ # def initialize
219
+ # super "Missing authToken - token is required"
220
+ # end
221
+ # end
222
+
204
223
  class ConfigurationError < StandardError
205
224
  def initialize message = "Invalid Configuration"
206
225
  super message
207
226
  end
208
227
  end
209
228
  end
210
- end
229
+ end
230
+ end
@@ -0,0 +1,199 @@
1
+ module CityGridExceptions
2
+
3
+ # Define parent error classes
4
+ # All errors thrown in the API should extend APIError - Level 1
5
+ class APIError < StandardError
6
+ attr_accessor :request
7
+
8
+ def initialize request, response, msg = "An API error occurred"
9
+ super msg
10
+ end
11
+ end
12
+
13
+ # Level 2 - These represent three different error scenarios:
14
+ # 1. Response is totally not parsable to JSON
15
+ # 2. The API call/parameters were malformed
16
+ # 3. The request was fine but their was an error API side
17
+
18
+ class ResponseParseError < APIError
19
+ attr_accessor :server_msg, :description, :raw_response
20
+ def initialize request, response, msg = nil
21
+ self.raw_response = response
22
+ # parse Tomcat error report
23
+ if response.match /<title>Apache Tomcat.* - Error report<\/title>/
24
+ response.scan(/<p><b>(message|description)<\/b> *<u>(.*?)<\/u><\/p>/).each do |match|
25
+ case match[0]
26
+ when "message"
27
+ self.server_msg = match[1]
28
+ when "description"
29
+ self.description = match[1]
30
+ end
31
+ end
32
+
33
+ error_body = response.match(/<body>(.*?)<\/body>/m)[1]
34
+
35
+ msg = <<-EOS
36
+ Unexpected response format. Expected response to be a hash, but was instead:\n#{error_body}\n
37
+ EOS
38
+
39
+ super msg, request
40
+ else
41
+ msg = <<-EOS
42
+ Unexpected response format. Expected response to be a hash, but was instead:\n#{response.parsed_response}\n
43
+ EOS
44
+
45
+ super msg, request
46
+ end
47
+ end
48
+ end
49
+
50
+ class RequestError < APIError
51
+ attr_accessor :inner_exception
52
+
53
+ def initialize request, response, msg = nil
54
+ self.inner_exception = inner_exception
55
+ self.request = request
56
+ super msg, request
57
+ #super msg || "Error while performing request: #{inner_exception.message}", request
58
+ end
59
+ end
60
+
61
+ class ResponseError < APIError
62
+ attr_accessor :errors, :response
63
+
64
+ def initialize request, errors, response
65
+ self.errors = errors
66
+ self.response = response
67
+
68
+ super "API returned error message", request
69
+ end
70
+ end
71
+
72
+ # Level 3
73
+ class GeneralError < APIError; end
74
+ class HeaderError < APIError; end
75
+ class AuthenticationError < APIError; end
76
+ class AuthorizationError < APIError; end
77
+ class OperatorError < RequestError; end
78
+ class GeneralDataError < APIError; end
79
+ class SpecificDataError < APIError; end
80
+
81
+ # Level 4
82
+
83
+ # General Errors
84
+ class SystemErrorTryAgainError < GeneralError; end
85
+ class SystemErrorUnknownError < GeneralError; end
86
+ class BadRequestTypeError < GeneralError; end
87
+
88
+
89
+
90
+ # HeaderErrors < RequestError
91
+ class ContentTypeRequiredError < HeaderError; end
92
+ class ContentTypeInvalidError < HeaderError; end
93
+ class AcceptRequiredError < HeaderError; end
94
+ class AcceptInvalidError < HeaderError; end
95
+
96
+ # Authentication Error
97
+ class AuthTokenInvalidError < AuthenticationError; end
98
+ class AuthTokenExpiredError < AuthenticationError; end
99
+ class AuthTokenNoneError < AuthenticationError; end
100
+ class UsernameRequiredError < AuthenticationError; end
101
+ class PasswordRequiredError < AuthenticationError; end
102
+ class AccountNotFoundError < AuthenticationError; end
103
+
104
+ #Authorization Error
105
+ class PermissionDeniedError < AuthorizationError; end
106
+ class NoPermissionsError < AuthorizationError; end
107
+
108
+ # Request Error
109
+ class ParameterRequiredError < RequestError; end
110
+ class ParameterRequiredConditionalError < RequestError; end
111
+ class ParameterInvalidError < RequestError; end
112
+ class ParameterFormatError < RequestError; end
113
+ class ParameterNotSupportedError < RequestError; end
114
+ class ParameterRangeTooLowError < RequestError; end
115
+ class ParameterRangeTooHighError < RequestError; end
116
+ class ParameterSizeLimitExceededError < RequestError; end
117
+ class ParameterCannotBeZeroError < RequestError; end
118
+ class ParameterOnlyOne < RequestError; end
119
+
120
+ # Operator Error
121
+ class OperatorInvalidError < OperatorError; end
122
+
123
+ # General Data Errors
124
+ class EntityNotFoundError < GeneralDataError; end
125
+ class EntityExistsError < GeneralDataError; end
126
+ class EntityLimitError < GeneralDataError; end
127
+ class EntityAlreadyInUseError < GeneralDataError; end
128
+ class EntityExpiredError < GeneralDataError; end
129
+ class EntityInactiveError < GeneralDataError; end
130
+ class EntityNotEligibleError < GeneralDataError; end
131
+ class EntityNotModifiedError < GeneralDataError; end
132
+ class EntityStateInvalidError < GeneralDataError; end
133
+ class EntityMissingDataError < GeneralDataError; end
134
+ class DataNotFoundError < GeneralDataError; end
135
+ class AssociationExistsError < GeneralDataError; end
136
+ class NoAssociationExistsError < GeneralDataError; end
137
+ class DuplicateError < GeneralDataError; end
138
+ class DateBeforeDateError < GeneralDataError; end
139
+ class RemoveNotAllowedError < GeneralDataError; end
140
+
141
+ #data errors - specific
142
+ class MopExpiredError < SpecificDataError; end
143
+ class AccountInactiveError < SpecificDataError; end
144
+ class AccountDelinquentError < SpecificDataError; end
145
+ class MonthlyBudgetReachedError < SpecificDataError; end
146
+ class QuotaExceededError < SpecificDataError; end
147
+ class RateExceededError < SpecificDataError; end
148
+
149
+ # unused errors
150
+ #400 => RequestError,
151
+ @possible_errors =
152
+ {
153
+ 0 => ResponseError, nil => ResponseParseError, "" => ResponseParseError,
154
+ 401 => AuthenticationError, 403 => RequestError, 405 => RequestError, 406 => HeaderError,
155
+ 500 => ResponseError, "SYSTEM_ERROR_TRY_AGAIN" => SystemErrorTryAgainError,
156
+ "SYSTEM_ERROR_UNKNOWN" => SystemErrorUnknownError, "BAD_REQUEST_TYPE" => BadRequestTypeError,
157
+ "HEADER_CONTENT_TYPE_IS_REQUIRED" => ContentTypeRequiredError, "HEADER_CONTENT_TYPE_INVALID" => ContentTypeInvalidError,
158
+ "HEADER_ACCEPT_IS_REQUIRED" => AcceptRequiredError, "HEADER_ACCEPT_INVALID" => AcceptInvalidError,
159
+ "AUTH_TOKEN_INVALID" => AuthTokenInvalidError, "AUTH_TOKEN_EXPIRED" => AuthTokenExpiredError,
160
+ "AUTH_TOKEN_NONE" => AuthTokenNoneError,
161
+ "USERNAME_IS_REQUIRED" => UsernameRequiredError, "PASSWORD_IS_REQUIRED" => PasswordRequiredError,
162
+ "ACCOUNT_NOT_FOUND" => AccountNotFoundError, "PERMISSION_DENIED" => PermissionDeniedError,
163
+ "NO_PERMISSIONS" => NoPermissionsError, "PARAMETER_REQUIRED" => ParameterRequiredError,
164
+ "PARAMETER_REQUIRED_CONDITIONAL" => ParameterRequiredConditionalError, "PARAMETER_INVALID" => ParameterInvalidError,
165
+ "PARAMETER_FORMAT" => ParameterFormatError, "PARAMETER_NOT_SUPPORTED" => ParameterNotSupportedError,
166
+ "PARAMETER_RANGE_TOO_LOW" => ParameterRangeTooLowError, "PARAMETER_RANGE_TOO_HIGH" => ParameterRangeTooHighError,
167
+ "PARAMETER_SIZE_LIMIT_EXCEEDED" => ParameterSizeLimitExceededError, "PARAMETER_CANNOT_BE_ZERO" => ParameterCannotBeZeroError,
168
+ "PARAMETER_ONLY_ONE" => ParameterOnlyOne, "OPERATOR_INVALID" => OperatorInvalidError,
169
+ "ENTITY_NOT_FOUND" => EntityNotFoundError, "ENTITY_EXISTS" => EntityExistsError,
170
+ "ENTITY_LIMIT" => EntityLimitError, "ENTITY_ALREADY_IN_USE" => EntityAlreadyInUseError,
171
+ "ENTITY_EXPIRED" => EntityExpiredError, "ENTITY_INACTIVE" => EntityInactiveError,
172
+ "ENTITY_NOT_ELIGIBLE" => EntityNotEligibleError, "ENTITY_NOT_MODIFIED" => EntityNotModifiedError,
173
+ "ENTITY_STATE_INVALID" => EntityStateInvalidError, "ENTITY_MISSING_DATA" => EntityMissingDataError,
174
+ "DATA_NOT_FOUND" => DataNotFoundError, "ASSOCIATION_EXISTS" => AssociationExistsError,
175
+ "NO_ASSOCIATION_EXISTS" => NoAssociationExistsError, "DUPLICATE" => DuplicateError,
176
+ "DATE_BEFORE_DATE" => DateBeforeDateError, "REMOVE_NOT_ALLOWED" => RemoveNotAllowedError,
177
+ "MOP_EXPIRED" => MopExpiredError, "ACCOUNT_INACTIVE" => AccountInactiveError,
178
+ "ACCOUNT_DELINQUENT" => AccountDelinquentError, "MONTHLY_BUDGET_REACHED" => MonthlyBudgetReachedError,
179
+ "QUOTA_EXCEEDED" => QuotaExceededError,"RATE_EXCEEDED" => RateExceededError
180
+ }
181
+
182
+ def CityGridExceptions.appropriate_error error_code
183
+ if @possible_errors.include?(error_code)
184
+ return @possible_errors[error_code]
185
+ else
186
+ return APIError
187
+ end
188
+ end
189
+
190
+ def CityGridExceptions.print_superclasses error_code
191
+ begin
192
+ raise appropriate_error[error_code]
193
+ rescue => ex
194
+ class_hierarchy = ex.class.ancestors
195
+ class_hierarchy.slice!(class_hierarchy.index(StandardError)..-1)
196
+ return class_hierarchy.reverse.join("::")
197
+ end
198
+ end
199
+ end
data/test/helper.rb CHANGED
@@ -22,9 +22,9 @@ unless defined? IN_DASHBOARD
22
22
 
23
23
  # load default config
24
24
  CityGrid.load_config File.expand_path(File.join(File.dirname(__FILE__), '..', 'citygrid_api.yml.sample'))
25
-
25
+
26
26
  # CityGrid.load_config File.expand_path(File.join(File.dirname(__FILE__), '..', 'citygrid_api.yml.sandbox'))
27
-
27
+
28
28
  # Run code with rescue so that exceptions
29
29
  # will be printed, but won't stop test suite
30
30
  def run_with_rescue
@@ -45,10 +45,10 @@ unless defined? IN_DASHBOARD
45
45
  false # return false
46
46
  end
47
47
  end
48
-
48
+
49
49
  # patch in VCR
50
50
  require 'vcr'
51
-
51
+
52
52
  VCR.config do |c|
53
53
  c.cassette_library_dir = 'fixtures/vcr_cassettes'
54
54
  c.stub_with :webmock
@@ -69,8 +69,8 @@ unless defined? IN_DASHBOARD
69
69
  # end
70
70
  # end
71
71
  end
72
-
72
+
73
73
  end
74
74
  end
75
-
75
+
76
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: citygrid_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-02-27 00:00:00.000000000Z
13
+ date: 2012-03-01 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
17
- requirement: &70338570510360 !ruby/object:Gem::Requirement
17
+ requirement: &70321245070140 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 0.8.1
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70338570510360
25
+ version_requirements: *70321245070140
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: json
28
- requirement: &70338570509420 !ruby/object:Gem::Requirement
28
+ requirement: &70321245069540 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - =
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 1.5.3
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70338570509420
36
+ version_requirements: *70321245069540
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: riot
39
- requirement: &70338570508480 !ruby/object:Gem::Requirement
39
+ requirement: &70321245068820 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 0.12.4
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *70338570508480
47
+ version_requirements: *70321245068820
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: awesome_print
50
- requirement: &70338570507560 !ruby/object:Gem::Requirement
50
+ requirement: &70321245068080 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 0.4.0
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *70338570507560
58
+ version_requirements: *70321245068080
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: bundler
61
- requirement: &70338570506520 !ruby/object:Gem::Requirement
61
+ requirement: &70321245067160 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 1.0.0
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70338570506520
69
+ version_requirements: *70321245067160
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: jeweler
72
- requirement: &70338570505260 !ruby/object:Gem::Requirement
72
+ requirement: &70321245063220 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: 1.6.2
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70338570505260
80
+ version_requirements: *70321245063220
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rcov
83
- requirement: &70338570503500 !ruby/object:Gem::Requirement
83
+ requirement: &70321245062700 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,10 +88,10 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70338570503500
91
+ version_requirements: *70321245062700
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: vcr
94
- requirement: &70338570501600 !ruby/object:Gem::Requirement
94
+ requirement: &70321245061660 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
97
  - - ! '>='
@@ -99,10 +99,10 @@ dependencies:
99
99
  version: '0'
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *70338570501600
102
+ version_requirements: *70321245061660
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: webmock
105
- requirement: &70338570500360 !ruby/object:Gem::Requirement
105
+ requirement: &70321245059820 !ruby/object:Gem::Requirement
106
106
  none: false
107
107
  requirements:
108
108
  - - ! '>='
@@ -110,7 +110,7 @@ dependencies:
110
110
  version: '0'
111
111
  type: :development
112
112
  prerelease: false
113
- version_requirements: *70338570500360
113
+ version_requirements: *70321245059820
114
114
  description: Ruby wrapper for CityGrid APIs
115
115
  email: Joseph.Chen@citygridmedia.com
116
116
  executables: []
@@ -207,6 +207,7 @@ files:
207
207
  - lib/citygrid/api/mutable.rb
208
208
  - lib/citygrid/api/response.rb
209
209
  - lib/citygrid/api/searchable.rb
210
+ - lib/citygrid/citygrid_exceptions.rb
210
211
  - lib/citygrid/details.rb
211
212
  - lib/citygrid/listing.rb
212
213
  - lib/citygrid/offers.rb