love 0.0.4 → 0.0.5
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.
- data/Gemfile.lock +1 -1
- data/README.rdoc +8 -4
- data/lib/love.rb +221 -51
- data/love.gemspec +2 -2
- data/spec/love_spec.rb +30 -5
- metadata +13 -18
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
@@ -22,12 +22,12 @@ I previously used HTTParty to connect to the Tender API, but I ran into two issu
|
|
22
22
|
|
23
23
|
== Installation
|
24
24
|
|
25
|
-
Run <tt>gem install love</tt> or add <tt>gem
|
25
|
+
Run <tt>gem install love</tt> or add <tt>gem "love"</tt> to your Gemfile.
|
26
26
|
|
27
27
|
== Usage
|
28
28
|
|
29
29
|
require 'love'
|
30
|
-
tender = Love.connect('
|
30
|
+
tender = Love.connect('site', 'api_key')
|
31
31
|
|
32
32
|
# Loop over all discussions:
|
33
33
|
tender.each_discussion do |discussion|
|
@@ -35,11 +35,15 @@ Run <tt>gem install love</tt> or add <tt>gem 'love'</tt> to your Gemfile.
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Also available:
|
38
|
-
tender.each_user { |
|
38
|
+
tender.each_user { |u| ... }
|
39
39
|
tender.each_queue { |q| ... }
|
40
40
|
tender.each_category { |c| ... }
|
41
41
|
|
42
|
+
# Or get a single record, using an ID or HREF:
|
43
|
+
d = tender.get_discussion('12345')
|
44
|
+
u = tender.get_user('https://api.tenderapp.com/site/users/12345')
|
45
|
+
|
42
46
|
== About
|
43
47
|
|
44
|
-
This library is
|
48
|
+
This library is written by Willem van Bergen for Shopify, and is MIT licensed.
|
45
49
|
|
data/lib/love.rb
CHANGED
@@ -1,143 +1,313 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'cgi'
|
2
3
|
require 'net/https'
|
3
|
-
require 'active_support/core_ext/module/attribute_accessors
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
5
|
+
require 'active_support/core_ext/hash/keys'
|
4
6
|
require 'yajl'
|
5
7
|
|
8
|
+
# Love is a small Ruby library to interact with the Tender REST API.
|
9
|
+
# The main object to work with is {Love::Client}, which is returned
|
10
|
+
# by calling {Love.connect}.
|
11
|
+
#
|
12
|
+
# It is dedicated to the awesome work Aaeron Patterson has been giving
|
13
|
+
# to the Ruby community.
|
6
14
|
module Love
|
7
15
|
|
8
|
-
|
16
|
+
# The current gem version. Will be updated automatically by the gem
|
17
|
+
# release script.
|
18
|
+
VERSION = "0.0.5"
|
9
19
|
|
10
|
-
#
|
20
|
+
# Exception class a custom exception class.
|
11
21
|
class Exception < StandardError; end
|
12
22
|
|
13
|
-
#
|
23
|
+
# Exception class for unauthorized requests or resources that are forbidden
|
14
24
|
class Unauthorized < Love::Exception; end
|
15
25
|
|
26
|
+
# Exception class for resources that are not found
|
27
|
+
class NotFound < Love::Exception; end
|
16
28
|
|
17
|
-
|
18
|
-
|
29
|
+
|
30
|
+
# Connects to the Tender API to access a site using an API key.
|
31
|
+
#
|
32
|
+
# This method doesn't do any communication yet with the Tender API, it will
|
33
|
+
# only set up the client with the provided credentials and options.
|
34
|
+
#
|
35
|
+
# By default, the client will use a new connection for every request. It can also
|
36
|
+
# open a persistent connection if you want to do multiple requests, by passing
|
37
|
+
# <tt>:persistent => true</tt> as option. In this case, you need to call {Love::Client#close_connection}
|
38
|
+
# when you are done with your session. You can also provide a block to this method
|
39
|
+
# which will automatically open up a persistent connection and close it at the end
|
40
|
+
# of the block.
|
41
|
+
#
|
42
|
+
# @param (see Love::Client#initialize)
|
43
|
+
# @option (see Love::Client#initialize)
|
44
|
+
# @overload connect(site, api_key, options = {}) { ... }
|
45
|
+
# @yield [Love::Client] An API client to work with using a persistent connection.
|
46
|
+
# @return The return value of the block. The persistent connection will be closed
|
47
|
+
# @overload connect(site, api_key, options = {})
|
48
|
+
# @return [Love::Client] An API client to work with. By default, the returned
|
49
|
+
# client will not use a persistent TCP connection, so a new connection will
|
50
|
+
# be
|
51
|
+
# @see Love::Client#initialize
|
52
|
+
def self.connect(site, api_key, options = {}, &block)
|
53
|
+
if block_given?
|
54
|
+
begin
|
55
|
+
client = Love::Client.new(site, api_key, options.merge(:persistent => true))
|
56
|
+
block.call(client)
|
57
|
+
ensure
|
58
|
+
client.close_connection
|
59
|
+
end
|
60
|
+
else
|
61
|
+
Love::Client.new(site, api_key, options)
|
62
|
+
end
|
19
63
|
end
|
20
64
|
|
65
|
+
# @return [Logger] Set this attribute to a Logger instance to log the
|
66
|
+
# HTTP connectivity somewhere.
|
21
67
|
mattr_accessor :logger
|
22
68
|
|
69
|
+
# Module to work with Tender REST URIs.
|
23
70
|
module ResourceURI
|
71
|
+
|
72
|
+
# Returns a collection URI, based on an URI instance, a complete URI string or just a resource name.
|
73
|
+
# @return [URI] The URI on which the REST resource collection is accessible through the Tender REST API.
|
74
|
+
# @raise [Love::Exception] If the input cannot be converted into a resource collection URI.
|
24
75
|
def collection_uri(input)
|
25
76
|
case input.to_s
|
26
77
|
when /^[\w-]+$/
|
27
|
-
::URI.parse("https://api.tenderapp.com/#{
|
28
|
-
when %r[^https?://api\.tenderapp\.com/#{
|
78
|
+
::URI.parse("https://api.tenderapp.com/#{site}/#{input}")
|
79
|
+
when %r[^https?://api\.tenderapp\.com/#{site}/[\w-]+]
|
29
80
|
::URI.parse(input.to_s)
|
30
81
|
else
|
31
82
|
raise Love::Exception, "This does not appear to be a valid Tender category URI!"
|
32
83
|
end
|
33
84
|
end
|
34
|
-
|
85
|
+
|
86
|
+
# Returns a resource URI, based on an URI instance, a complete URI string or just a resource ID.
|
87
|
+
# @param [Object input The complete URI or just resource ID as URI, String or Integer.
|
88
|
+
# @param [String] kind The kind of resource.
|
89
|
+
# @return [URI] The URI on which the REST resource is accessible through the Tender REST API.
|
90
|
+
# @raise [Love::Exception] If the input cannot be converted into a resource URI.
|
35
91
|
def singleton_uri(input, kind)
|
36
92
|
case input.to_s
|
37
93
|
when /^\d+/
|
38
|
-
::URI.parse("https://api.tenderapp.com/#{
|
39
|
-
when %r[^https?://api\.tenderapp\.com/#{
|
94
|
+
::URI.parse("https://api.tenderapp.com/#{site}/#{kind}/#{input}")
|
95
|
+
when %r[^https?://api\.tenderapp\.com/#{site}/#{kind}/\d+]
|
40
96
|
::URI.parse(input.to_s)
|
41
97
|
else
|
42
98
|
raise Love::Exception, "This does not appear to be a Tender #{kind} URI or ID!"
|
43
99
|
end
|
44
100
|
end
|
45
101
|
|
46
|
-
|
102
|
+
# Appends GET parameters to a URI instance. Duplicate parameters will
|
103
|
+
# be replaced with the new value.
|
104
|
+
# @param [URI] base_uri The original URI to work with (will not be modified)
|
105
|
+
# @param [Hash] added_params To GET params to add.
|
106
|
+
# @return [URI] The URI with appended GET parameters
|
107
|
+
def append_query(base_uri, added_params = {})
|
47
108
|
base_params = base_uri.query ? CGI.parse(base_uri.query) : {}
|
48
|
-
get_params = base_params.merge(added_params
|
109
|
+
get_params = base_params.merge(added_params.stringify_keys)
|
49
110
|
base_uri.dup.tap do |uri|
|
50
|
-
|
111
|
+
assignments = get_params.map do |k, v|
|
112
|
+
case v
|
113
|
+
when Array; v.map { |val| "#{::CGI.escape(k.to_s)}=#{::CGI.escape(val.to_s)}" }.join('&')
|
114
|
+
else "#{::CGI.escape(k.to_s)}=#{::CGI.escape(v.to_s)}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
uri.query = assignments.join('&')
|
51
118
|
end
|
52
119
|
end
|
53
120
|
end
|
54
|
-
|
121
|
+
|
122
|
+
# The Love::Client class acts as a client to the Tender REST API. Obtain an instance of this
|
123
|
+
# class by calling {Love.connect} instead of instantiating this class directly.
|
124
|
+
#
|
125
|
+
# You can either fetch individual resources using {#get_user}, {#get_discussion}, and
|
126
|
+
# similar methods, or iterate over collections using {#each_discussion}, {#each_category}
|
127
|
+
# and similar methods.
|
55
128
|
class Client
|
56
129
|
|
130
|
+
# The Tender API host to connect to.
|
131
|
+
TENDER_API_HOST = 'api.tenderapp.com'
|
132
|
+
|
57
133
|
include Love::ResourceURI
|
58
134
|
|
59
|
-
|
135
|
+
# @return [String] The site to work with
|
136
|
+
attr_reader :site
|
137
|
+
|
138
|
+
# @return [String] The API key to authenticate with.
|
60
139
|
attr_reader :api_key
|
61
140
|
|
141
|
+
# @return [Float] The number of seconds to sleep between paged requests.
|
62
142
|
attr_accessor :sleep_between_requests
|
63
143
|
|
64
|
-
|
65
|
-
|
144
|
+
# Initializes the client.
|
145
|
+
# @param [String] site The site to work with.
|
146
|
+
# @param [String] api_key The API key for this site.
|
147
|
+
# @param [Hash] options Connectivity options.
|
148
|
+
# @option options [Boolean] :persistent (false) Whether to create a persistent TCP connection.
|
149
|
+
# @option options [Float] :sleep_between_requests (0.5) The time between requests in seconds.
|
150
|
+
# @see Love.connect
|
151
|
+
def initialize(site, api_key, options = {})
|
152
|
+
@site, @api_key = site, api_key
|
66
153
|
|
67
154
|
# Handle options
|
155
|
+
@persistent = !!options[:persistent]
|
68
156
|
@sleep_between_requests = options[:sleep_between_requests] || 0.5
|
69
157
|
end
|
70
158
|
|
71
|
-
|
159
|
+
# Returns a single Tender user.
|
160
|
+
# @param [URI, String, Integer] id_or_href The user ID or HREF. Can be either a URI
|
161
|
+
# instance, a string containing a URI, or a user ID as a numeric string or integer.
|
162
|
+
# @return [Hash] The user attributes in a Hash.
|
163
|
+
def get_user(id_or_href)
|
72
164
|
get(singleton_uri(id_or_href, 'users'))
|
73
165
|
end
|
74
|
-
|
75
|
-
|
166
|
+
|
167
|
+
# Returns a single Tender discussion.
|
168
|
+
# @param [URI, String, Integer] id_or_href The discussion ID or HREF. Can be either a URI
|
169
|
+
# instance, a string containing a URI, or a discussion ID as a numeric string or integer.
|
170
|
+
# @return [Hash] The discussion attributes in a Hash.
|
171
|
+
def get_discussion(id_or_href)
|
76
172
|
get(singleton_uri(id_or_href, 'discussions'))
|
77
173
|
end
|
78
174
|
|
79
|
-
|
175
|
+
# Returns a single Tender category.
|
176
|
+
# @param [URI, String, Integer] id_or_href The category ID or HREF. Can be either a URI
|
177
|
+
# instance, a string containing a URI, or a category ID as a numeric string or integer.
|
178
|
+
# @return [Hash] The category attributes in a Hash.
|
179
|
+
def get_category(id_or_href)
|
80
180
|
get(singleton_uri(id_or_href, 'categories'))
|
81
181
|
end
|
82
182
|
|
83
|
-
|
183
|
+
# Returns a single Tender queue.
|
184
|
+
# @param [URI, String, Integer] id_or_href The queue ID or HREF. Can be either a URI
|
185
|
+
# instance, a string containing a URI, or a queue ID as a numeric string or integer.
|
186
|
+
# @return [Hash] The queue attributes in a Hash.
|
187
|
+
def get_queue(id_or_href)
|
84
188
|
get(singleton_uri(id_or_href, 'queues'), options)
|
85
189
|
end
|
86
|
-
|
190
|
+
|
191
|
+
# Iterates over all Tender categories.
|
192
|
+
# @yield [Hash] The attributes of each category will be yielded as (nested) Hash.
|
193
|
+
# @option (see #paged_each)
|
194
|
+
# @return [nil]
|
87
195
|
def each_category(options = {}, &block)
|
88
|
-
|
196
|
+
paged_each(collection_uri('categories'), 'categories', options, &block)
|
89
197
|
end
|
90
198
|
|
199
|
+
# Iterates over all Tender users.
|
200
|
+
# @yield [Hash] The attributes of each user will be yielded as (nested) Hash.
|
201
|
+
# @option (see #paged_each)
|
202
|
+
# @return [nil]
|
91
203
|
def each_queue(options = {}, &block)
|
92
|
-
|
204
|
+
paged_each(collection_uri('queues'), 'named_queues', options, &block)
|
93
205
|
end
|
94
206
|
|
207
|
+
# Iterates over all Tender users.
|
208
|
+
# @yield [Hash] The attributes of each user will be yielded as (nested) Hash.
|
209
|
+
# @option (see #paged_each)
|
210
|
+
# @return [nil]
|
95
211
|
def each_user(options = {}, &block)
|
96
|
-
|
212
|
+
paged_each(collection_uri('users'), 'users', options, &block)
|
97
213
|
end
|
98
214
|
|
215
|
+
# Iterates over all Tender discussions.
|
216
|
+
# @yield [Hash] The attributes of each discussion will be yielded as (nested) Hash.
|
217
|
+
# @option (see #paged_each)
|
218
|
+
# @return [nil]
|
99
219
|
def each_discussion(options = {}, &block)
|
100
|
-
|
220
|
+
paged_each(collection_uri('discussions'), 'discussions', options, &block)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns a persistent connection to the server, reusing a connection of it was
|
224
|
+
# previously established.
|
225
|
+
#
|
226
|
+
# This method is mainly used for internal use but can be used to do advanced
|
227
|
+
# HTTP connectivity with the Tender API server.
|
228
|
+
#
|
229
|
+
# @return [Net::HTTP] The net/http connection instance.
|
230
|
+
def connection
|
231
|
+
@connection ||= Net::HTTP.new(TENDER_API_HOST, Net::HTTP.https_default_port).tap do |http|
|
232
|
+
http.use_ssl = true
|
233
|
+
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
234
|
+
http.start
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Closes the persistent connectio to the server
|
239
|
+
# @return [nil]
|
240
|
+
def close_connection
|
241
|
+
@connection.finish if connected?
|
242
|
+
end
|
243
|
+
|
244
|
+
# @return [Boolean] <tt>true</tt> iff the client currently has a TCP connection with the Tender API server.
|
245
|
+
def connected?
|
246
|
+
@connection && @connection.started?
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [Boolean] <tt>true</tt> iff the client is using persistent connections.
|
250
|
+
def persistent?
|
251
|
+
@persistent
|
101
252
|
end
|
102
253
|
|
103
254
|
protected
|
255
|
+
|
256
|
+
def request_headers
|
257
|
+
@request_headers ||= { "Accept" => "application/vnd.tender-v1+json", "X-Tender-Auth" => api_key }
|
258
|
+
end
|
104
259
|
|
105
260
|
def get(uri)
|
106
|
-
Love
|
261
|
+
raise Love::Exception, "This is not a Tender API URI." unless uri.host = TENDER_API_HOST
|
107
262
|
|
108
|
-
|
109
|
-
if uri.scheme = 'https'
|
110
|
-
http.use_ssl = true
|
111
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
112
|
-
end
|
263
|
+
Love.logger.debug "GET #{uri.request_uri}" if Love.logger
|
113
264
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
265
|
+
request = Net::HTTP::Get.new(uri.request_uri, request_headers)
|
266
|
+
response = connection.request(request)
|
267
|
+
case response
|
268
|
+
when Net::HTTPSuccess; Yajl::Parser.new.parse(safely_convert_to_utf8(response.body))
|
269
|
+
when Net::HTTPUnauthorized; raise Love::Unauthorized, "Invalid credentials used!"
|
270
|
+
when Net::HTTPForbidden; raise Love::Unauthorized, "You don't have permission to access this resource!"
|
271
|
+
when Net::NotFound; raise Love::NotFound, "The resource #{uri} was not found!"
|
272
|
+
else raise Love::Exception, "#{response.code}: #{response.body}"
|
273
|
+
end
|
274
|
+
ensure
|
275
|
+
close_connection unless persistent?
|
276
|
+
end
|
277
|
+
|
278
|
+
# Converts a binary, (alomst) UTF-8 string into an actual UTF-8 string.
|
279
|
+
# It will replace any unknown characters or unvalid byte sequences into a UTF-8
|
280
|
+
# "unknown character" question mark.
|
281
|
+
#
|
282
|
+
# @param [String] binary_string The input string, should have binary encoding
|
283
|
+
# @return [String] The string using UTF-8 encoding.
|
284
|
+
def safely_convert_to_utf8(binary_string)
|
285
|
+
if binary_string.respond_to?(:force_encoding)
|
286
|
+
# Ruby 1.9
|
122
287
|
converter = Encoding::Converter.new('binary', 'utf-8', :invalid => :replace, :undef => :replace)
|
123
|
-
|
124
|
-
when '401'
|
125
|
-
raise Love::Unauthorized, "Invalid credentials used!"
|
288
|
+
converter.convert(binary_string)
|
126
289
|
else
|
127
|
-
|
290
|
+
# Ruby 1.8 - currently don't do anything
|
291
|
+
binary_string
|
128
292
|
end
|
129
293
|
end
|
130
294
|
|
131
|
-
|
295
|
+
# Iterates over a collection, issuing multiple requests to get all the paged results.
|
296
|
+
#
|
297
|
+
# @option options [Date] :since Only include records updated since the provided date.
|
298
|
+
# Caution: not supported by all resources.
|
299
|
+
# @option options [Integer] :start_page The initial page number to request.
|
300
|
+
# @option options [Integer] :end_page The final page number to request.
|
301
|
+
def paged_each(uri, list_key, options = {}, &block)
|
132
302
|
query_params = {}
|
133
303
|
query_params[:since] = options[:since].to_date.to_s(:db) if options[:since]
|
134
304
|
query_params[:page] = [options[:start_page].to_i, 1].max rescue 1
|
135
305
|
|
136
|
-
initial_result = get(
|
306
|
+
initial_result = get(append_query(uri, query_params))
|
137
307
|
|
138
308
|
# Determine the amount of pages that is going to be requested.
|
139
|
-
max_page
|
140
|
-
end_page
|
309
|
+
max_page = (initial_result['total'].to_f / initial_result['per_page'].to_f).ceil
|
310
|
+
end_page = options[:end_page].nil? ? max_page : [options[:end_page].to_i, max_page].min
|
141
311
|
|
142
312
|
# Print out some initial debugging information
|
143
313
|
Love.logger.debug "Paged requests to #{uri}: #{max_page} total pages, importing #{query_params[:page]} upto #{end_page}." if Love.logger
|
@@ -151,7 +321,7 @@ module Love
|
|
151
321
|
start_page = query_params[:page].to_i + 1
|
152
322
|
start_page.upto(end_page) do |page|
|
153
323
|
query_params[:page] = page
|
154
|
-
result = get(
|
324
|
+
result = get(append_query(uri, query_params))
|
155
325
|
if result[list_key].kind_of?(Array)
|
156
326
|
result[list_key].each { |record| yield(record) }
|
157
327
|
sleep(sleep_between_requests) if sleep_between_requests
|
data/love.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
|
4
4
|
# Do not change the version and date fields by hand. This will be done
|
5
5
|
# automatically by the gem release script.
|
6
|
-
s.version = "0.0.
|
7
|
-
s.date = "2010-11-
|
6
|
+
s.version = "0.0.5"
|
7
|
+
s.date = "2010-11-30"
|
8
8
|
|
9
9
|
s.summary = "Ruby library to access the Tender REST API."
|
10
10
|
s.description = <<-EOT
|
data/spec/love_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Love::ResourceURI do
|
4
4
|
|
5
5
|
include Love::ResourceURI
|
6
|
-
def
|
6
|
+
def site; "mysupport"; end
|
7
7
|
|
8
8
|
describe '#collection_uri' do
|
9
9
|
|
@@ -23,7 +23,7 @@ describe Love::ResourceURI do
|
|
23
23
|
collection_uri(uri).should == uri
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should not accept a URI for another
|
26
|
+
it "should not accept a URI for another site" do
|
27
27
|
lambda { collection_uri('https://api.tenderapp.com/other/bars') }.should raise_error(Love::Exception)
|
28
28
|
end
|
29
29
|
|
@@ -52,9 +52,9 @@ describe Love::ResourceURI do
|
|
52
52
|
uri = URI.parse('https://api.tenderapp.com/mysupport/bars/789')
|
53
53
|
singleton_uri(uri, 'bars').should be_kind_of(::URI)
|
54
54
|
singleton_uri(uri, 'bars').should == uri
|
55
|
-
end
|
55
|
+
end
|
56
56
|
|
57
|
-
it "should not accept a URI for another
|
57
|
+
it "should not accept a URI for another site" do
|
58
58
|
lambda { singleton_uri('https://api.tenderapp.com/other/bars/123', 'bars') }.should raise_error(Love::Exception)
|
59
59
|
end
|
60
60
|
|
@@ -64,7 +64,32 @@ describe Love::ResourceURI do
|
|
64
64
|
|
65
65
|
it "should not weird resource IDs" do
|
66
66
|
lambda { singleton_uri('%!&', 'bars') }.should raise_error(Love::Exception)
|
67
|
-
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#append_query' do
|
71
|
+
before { @uri = URI.parse('https://api.tenderapp.com/') }
|
72
|
+
|
73
|
+
it "should return a URI instance" do
|
74
|
+
append_query(@uri, :a => 'b').should be_kind_of(URI)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should add a correctly quoted query string" do
|
78
|
+
adjusted = append_query(@uri, :a => 'some data')
|
79
|
+
adjusted.to_s.should == 'https://api.tenderapp.com/?a=some+data'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should keep existing query parameters intact" do
|
83
|
+
@uri.query = 'foo=bar'
|
84
|
+
adjusted = append_query(@uri, :a => 'some data')
|
85
|
+
adjusted.to_s.should == 'https://api.tenderapp.com/?foo=bar&a=some+data'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should overwrite existing parameters" do
|
89
|
+
@uri.query = 'foo=bar'
|
90
|
+
adjusted = append_query(@uri, :foo => 'baz')
|
91
|
+
adjusted.to_s.should == 'https://api.tenderapp.com/?foo=baz'
|
92
|
+
end
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: love
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 23
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
7
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
8
|
+
- 5
|
9
|
+
version: 0.0.5
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Willem van Bergen
|
@@ -15,64 +14,60 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-11-
|
17
|
+
date: 2010-11-30 00:00:00 -05:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
23
23
|
none: false
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
hash: 3
|
28
27
|
segments:
|
29
28
|
- 0
|
30
29
|
version: "0"
|
31
|
-
name: activesupport
|
32
|
-
prerelease: false
|
33
30
|
type: :runtime
|
31
|
+
prerelease: false
|
34
32
|
version_requirements: *id001
|
35
33
|
- !ruby/object:Gem::Dependency
|
34
|
+
name: yajl-ruby
|
36
35
|
requirement: &id002 !ruby/object:Gem::Requirement
|
37
36
|
none: false
|
38
37
|
requirements:
|
39
38
|
- - ">="
|
40
39
|
- !ruby/object:Gem::Version
|
41
|
-
hash: 3
|
42
40
|
segments:
|
43
41
|
- 0
|
44
42
|
version: "0"
|
45
|
-
name: yajl-ruby
|
46
|
-
prerelease: false
|
47
43
|
type: :runtime
|
44
|
+
prerelease: false
|
48
45
|
version_requirements: *id002
|
49
46
|
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
50
48
|
requirement: &id003 !ruby/object:Gem::Requirement
|
51
49
|
none: false
|
52
50
|
requirements:
|
53
51
|
- - ">="
|
54
52
|
- !ruby/object:Gem::Version
|
55
|
-
hash: 3
|
56
53
|
segments:
|
57
54
|
- 0
|
58
55
|
version: "0"
|
59
|
-
name: rake
|
60
|
-
prerelease: false
|
61
56
|
type: :development
|
57
|
+
prerelease: false
|
62
58
|
version_requirements: *id003
|
63
59
|
- !ruby/object:Gem::Dependency
|
60
|
+
name: rspec
|
64
61
|
requirement: &id004 !ruby/object:Gem::Requirement
|
65
62
|
none: false
|
66
63
|
requirements:
|
67
64
|
- - ~>
|
68
65
|
- !ruby/object:Gem::Version
|
69
|
-
hash: 7
|
70
66
|
segments:
|
71
67
|
- 2
|
72
68
|
version: "2"
|
73
|
-
name: rspec
|
74
|
-
prerelease: false
|
75
69
|
type: :development
|
70
|
+
prerelease: false
|
76
71
|
version_requirements: *id004
|
77
72
|
description: " A simple API wrapper for Tender, that handles paged results, uses yajl-ruby for\n JSON parsing, and manually handles UTF-8 encoding to circumvent the invalid UTF-8\n character problem in Ruby 1.9.\n"
|
78
73
|
email:
|
@@ -114,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
109
|
requirements:
|
115
110
|
- - ">="
|
116
111
|
- !ruby/object:Gem::Version
|
117
|
-
hash:
|
112
|
+
hash: 4412278213524451879
|
118
113
|
segments:
|
119
114
|
- 0
|
120
115
|
version: "0"
|
@@ -123,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
118
|
requirements:
|
124
119
|
- - ">="
|
125
120
|
- !ruby/object:Gem::Version
|
126
|
-
hash:
|
121
|
+
hash: 4412278213524451879
|
127
122
|
segments:
|
128
123
|
- 0
|
129
124
|
version: "0"
|