love 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|