love 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile.lock +11 -1
- data/README.rdoc +8 -4
- data/lib/love.rb +106 -78
- data/love.gemspec +4 -4
- data/spec/love_spec.rb +72 -0
- data/spec/spec_helper.rb +8 -0
- metadata +32 -12
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
love (0.0.
|
4
|
+
love (0.0.2)
|
5
5
|
activesupport
|
6
6
|
yajl-ruby
|
7
7
|
|
@@ -9,7 +9,16 @@ GEM
|
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
11
|
activesupport (3.0.3)
|
12
|
+
diff-lcs (1.1.2)
|
12
13
|
rake (0.8.7)
|
14
|
+
rspec (2.2.0)
|
15
|
+
rspec-core (~> 2.2)
|
16
|
+
rspec-expectations (~> 2.2)
|
17
|
+
rspec-mocks (~> 2.2)
|
18
|
+
rspec-core (2.2.1)
|
19
|
+
rspec-expectations (2.2.0)
|
20
|
+
diff-lcs (~> 1.1.2)
|
21
|
+
rspec-mocks (2.2.0)
|
13
22
|
yajl-ruby (0.7.8)
|
14
23
|
|
15
24
|
PLATFORMS
|
@@ -19,4 +28,5 @@ DEPENDENCIES
|
|
19
28
|
activesupport
|
20
29
|
love!
|
21
30
|
rake
|
31
|
+
rspec (~> 2)
|
22
32
|
yajl-ruby
|
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= Love
|
2
2
|
|
3
|
-
This library accesses the Tender REST API. It is my ode and thanks
|
4
|
-
|
3
|
+
This library accesses the Tender REST API. It is my ode to and thanks for the tender
|
4
|
+
lovemaking Aaron Patterson is doing on ActiveRecord.
|
5
5
|
|
6
6
|
It is currently read-only, and made especially for scripts that import data from
|
7
7
|
Tender to use in your own application. Feel free to fork and add missing API calls.
|
@@ -16,10 +16,14 @@ I previously used HTTParty to connect to the Tender API, but I ran into two issu
|
|
16
16
|
receive, invalid UTF-8 characters can get into the system, which will break Ruby
|
17
17
|
1.9. Love handles character encoding manually, and will replace all the invalid
|
18
18
|
UTF-8 characters by the UTF "unknown character" character.
|
19
|
-
* I
|
19
|
+
* I had to handle paging manually to get all data. Love will automatically
|
20
20
|
send multiple requests to get all the pages to iterate over all the available
|
21
21
|
objects.
|
22
22
|
|
23
|
+
== Installation
|
24
|
+
|
25
|
+
Run <tt>gem install love</tt> or add <tt>gem 'love'</tt> to your Gemfile.
|
26
|
+
|
23
27
|
== Usage
|
24
28
|
|
25
29
|
require 'love'
|
@@ -31,7 +35,7 @@ I previously used HTTParty to connect to the Tender API, but I ran into two issu
|
|
31
35
|
end
|
32
36
|
|
33
37
|
# Also available:
|
34
|
-
tender.
|
38
|
+
tender.each_user { |c| ... }
|
35
39
|
tender.each_queue { |q| ... }
|
36
40
|
tender.each_category { |c| ... }
|
37
41
|
|
data/lib/love.rb
CHANGED
@@ -1,113 +1,141 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/https'
|
3
|
-
require 'active_support'
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors.rb'
|
4
4
|
require 'yajl'
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
module Love
|
7
|
+
|
8
8
|
# Create a custom exception class.
|
9
9
|
class Exception < StandardError; end
|
10
10
|
|
11
11
|
# Class for unauthorized exceptions
|
12
12
|
class Unauthorized < Love::Exception; end
|
13
13
|
|
14
|
-
attr_accessor :logger
|
15
|
-
|
16
|
-
attr_reader :account
|
17
|
-
attr_reader :api_key
|
18
|
-
|
19
|
-
attr_accessor :sleep_between_requests
|
20
|
-
|
21
|
-
def initialize(account, api_key, options = {})
|
22
|
-
@account, @api_key = account, api_key
|
23
|
-
|
24
|
-
# Handle options
|
25
|
-
@sleep_between_requests = options[:sleep_between_requests] || 0.5
|
26
|
-
end
|
27
14
|
|
28
15
|
def self.connect(account, api_key, options = {})
|
29
|
-
new(account, api_key, options)
|
16
|
+
Love::Client.new(account, api_key, options)
|
30
17
|
end
|
18
|
+
|
19
|
+
mattr_accessor :logger
|
31
20
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
21
|
+
module ResourceURI
|
22
|
+
def collection_uri(input)
|
23
|
+
case input.to_s
|
24
|
+
when /^[\w-]+$/
|
25
|
+
::URI.parse("https://api.tenderapp.com/#{account}/#{input}")
|
26
|
+
when %r[^https?://api\.tenderapp\.com/#{account}/[\w-]+]
|
27
|
+
::URI.parse(input.to_s)
|
28
|
+
else
|
29
|
+
raise Love::Exception, "This does not appear to be a valid Tender category URI!"
|
30
|
+
end
|
38
31
|
end
|
39
|
-
end
|
40
32
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
def singleton_uri(input, kind)
|
34
|
+
case input.to_s
|
35
|
+
when /^\d+/
|
36
|
+
::URI.parse("https://api.tenderapp.com/#{account}/#{kind}/#{input}")
|
37
|
+
when %r[^https?://api\.tenderapp\.com/#{account}/#{kind}/\d+]
|
38
|
+
::URI.parse(input.to_s)
|
39
|
+
else
|
40
|
+
raise Love::Exception, "This does not appear to be a Tender #{kind} URI or ID!"
|
41
|
+
end
|
47
42
|
end
|
48
43
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
|
45
|
+
class Client
|
46
|
+
|
47
|
+
include Love::ResourceURI
|
53
48
|
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
attr_reader :account
|
50
|
+
attr_reader :api_key
|
51
|
+
|
52
|
+
attr_accessor :sleep_between_requests
|
53
|
+
|
54
|
+
def initialize(account, api_key, options = {})
|
55
|
+
@account, @api_key = account, api_key
|
56
|
+
|
57
|
+
# Handle options
|
58
|
+
@sleep_between_requests = options[:sleep_between_requests] || 0.5
|
59
|
+
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
def get_user(id_or_href, options = {})
|
62
|
+
get(singleton_uri(id_or_href, 'users'))
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
def get_discussion(id_or_href, options = {})
|
66
|
+
get(singleton_uri(id_or_href, 'discussions'))
|
67
|
+
end
|
65
68
|
|
66
|
-
|
69
|
+
def get_category(id_or_href, options = {})
|
70
|
+
get(singleton_uri(id_or_href, 'categories'))
|
71
|
+
end
|
67
72
|
|
68
|
-
|
69
|
-
|
73
|
+
def get_queue(id_or_href, options = {})
|
74
|
+
get(singleton_uri(id_or_href, 'queues'), options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def each_category(options = {}, &block)
|
78
|
+
buffered_each(collection_uri('categories'), 'categories', options, &block)
|
79
|
+
end
|
70
80
|
|
71
|
-
|
81
|
+
def each_queue(options = {}, &block)
|
82
|
+
buffered_each(collection_uri('queues'), 'named_queues', options, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def each_user(options = {}, &block)
|
86
|
+
buffered_each(collection_uri('users'), 'users', options, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def each_discussion(options = {}, &block)
|
90
|
+
buffered_each(collection_uri('discussions'), 'discussions', options, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def get(uri)
|
96
|
+
Love.logger.debug "GET #{url}" if Love.logger
|
72
97
|
|
73
|
-
|
74
|
-
|
75
|
-
|
98
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
99
|
+
if uri.scheme = 'https'
|
100
|
+
http.use_ssl = true
|
101
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
102
|
+
end
|
76
103
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
104
|
+
req = Net::HTTP::Get.new(uri.request_uri, {
|
105
|
+
"Accept" => "application/vnd.tender-v1+json",
|
106
|
+
"X-Tender-Auth" => api_key
|
107
|
+
})
|
81
108
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
109
|
+
response = http.request(req)
|
110
|
+
case response.code
|
111
|
+
when /^2\d\d/
|
112
|
+
converter = Encoding::Converter.new('binary', 'utf-8', :invalid => :replace, :undef => :replace)
|
113
|
+
Yajl::Parser.new.parse(converter.convert(response.body))
|
114
|
+
when '401'
|
115
|
+
raise Love::Unauthorized, "Invalid credentials used!"
|
116
|
+
else
|
117
|
+
raise Love::Exception, "#{response.code}: #{response.body}"
|
118
|
+
end
|
91
119
|
end
|
92
|
-
end
|
93
120
|
|
94
|
-
|
95
|
-
|
96
|
-
|
121
|
+
def buffered_each(uri, list_key, options = {}, &block)
|
122
|
+
query_options = {}
|
123
|
+
query_options[:since] = options[:since].to_date.to_s(:db) if options[:since]
|
97
124
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
# Print out some initial debugging information
|
125
|
+
initial_result = get(path, :query => query_options)
|
126
|
+
start_page = [options[:start_page].to_i, 1].max rescue 1
|
127
|
+
max_page = (initial_result['total'].to_f / initial_result['per_page'].to_f).ceil
|
128
|
+
end_page = options[:end_page].nil? ? max_page : [options[:end_page].to_i, max_page].min
|
104
129
|
|
105
|
-
|
130
|
+
# Print out some initial debugging information
|
131
|
+
Love.logger.debug "Paged requests to #{path}: #{max_page} total pages, importing #{start_page} upto #{end_page}." if Love.logger
|
106
132
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
133
|
+
start_page.upto(end_page) do |page|
|
134
|
+
uri.query = query_options.map { |k, v| "#{k}=#{v}" }.join('&')
|
135
|
+
result = get(uri)
|
136
|
+
result[list_key].each { |record| yield(record) }
|
137
|
+
sleep(sleep_between_requests) if sleep_between_requests
|
138
|
+
end
|
111
139
|
end
|
112
140
|
end
|
113
141
|
end
|
data/love.gemspec
CHANGED
@@ -3,7 +3,7 @@ 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.
|
6
|
+
s.version = "0.0.2"
|
7
7
|
s.date = "2010-11-29"
|
8
8
|
|
9
9
|
s.summary = "Ruby library to access the Tender REST API."
|
@@ -21,13 +21,13 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_runtime_dependency('yajl-ruby')
|
22
22
|
|
23
23
|
s.add_development_dependency('rake')
|
24
|
-
|
24
|
+
s.add_development_dependency('rspec', '~> 2')
|
25
25
|
|
26
26
|
s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
|
27
27
|
s.extra_rdoc_files = ['README.rdoc']
|
28
28
|
|
29
29
|
# Do not change the files and test_files fields by hand. This will be done
|
30
30
|
# automatically by the gem release script.
|
31
|
-
s.files = %w(.gitignore Gemfile Gemfile.lock LICENSE README.rdoc Rakefile lib/love.rb love.gemspec tasks/github-gem.rb)
|
32
|
-
s.test_files = %w()
|
31
|
+
s.files = %w(.gitignore Gemfile Gemfile.lock LICENSE README.rdoc Rakefile lib/love.rb love.gemspec spec/love_spec.rb spec/spec_helper.rb tasks/github-gem.rb)
|
32
|
+
s.test_files = %w(spec/love_spec.rb)
|
33
33
|
end
|
data/spec/love_spec.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Love::ResourceURI do
|
4
|
+
|
5
|
+
include Love::ResourceURI
|
6
|
+
def account; "mysupport"; end
|
7
|
+
|
8
|
+
describe '#collection_uri' do
|
9
|
+
|
10
|
+
it "should build a valid URI based on the resource name" do
|
11
|
+
collection_uri('foos').should be_kind_of(::URI)
|
12
|
+
collection_uri('bars').to_s.should == 'https://api.tenderapp.com/mysupport/bars'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should accept a valid URI string" do
|
16
|
+
collection_uri('https://api.tenderapp.com/mysupport/bars').should be_kind_of(::URI)
|
17
|
+
collection_uri('https://api.tenderapp.com/mysupport/bars').to_s.should == 'https://api.tenderapp.com/mysupport/bars'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept a valid URI object" do
|
21
|
+
uri = URI.parse('https://api.tenderapp.com/mysupport/bars')
|
22
|
+
collection_uri(uri).should be_kind_of(::URI)
|
23
|
+
collection_uri(uri).should == uri
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not accept a URI for another account" do
|
27
|
+
lambda { collection_uri('https://api.tenderapp.com/other/bars') }.should raise_error(Love::Exception)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not accept an unrelated URI" do
|
31
|
+
lambda { collection_uri('http://www.vanbergen.org/mysupport/foos') }.should raise_error(Love::Exception)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not weird resource names" do
|
35
|
+
lambda { collection_uri('%!&') }.should raise_error(Love::Exception)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#singleton_uri' do
|
40
|
+
|
41
|
+
it "should build a valid URI based on the resource ID" do
|
42
|
+
singleton_uri(123, 'foos').should be_kind_of(::URI)
|
43
|
+
singleton_uri('456', 'bars').to_s.should == 'https://api.tenderapp.com/mysupport/bars/456'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should accept a valid URI string" do
|
47
|
+
singleton_uri('https://api.tenderapp.com/mysupport/foos/123', 'foos').should be_kind_of(::URI)
|
48
|
+
singleton_uri('https://api.tenderapp.com/mysupport/bars/456', 'bars').to_s.should == 'https://api.tenderapp.com/mysupport/bars/456'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should accept a valid URI object" do
|
52
|
+
uri = URI.parse('https://api.tenderapp.com/mysupport/bars/789')
|
53
|
+
singleton_uri(uri, 'bars').should be_kind_of(::URI)
|
54
|
+
singleton_uri(uri, 'bars').should == uri
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not accept a URI for another account" do
|
58
|
+
lambda { singleton_uri('https://api.tenderapp.com/other/bars/123', 'bars') }.should raise_error(Love::Exception)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not accept an unrelated URI" do
|
62
|
+
lambda { singleton_uri('http://www.vanbergen.org/mysupport/foos', 'foos') }.should raise_error(Love::Exception)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not weird resource IDs" do
|
66
|
+
lambda { singleton_uri('%!&', 'bars') }.should raise_error(Love::Exception)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe Love::Client do
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: love
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Willem van Bergen
|
@@ -18,44 +19,61 @@ date: 2010-11-29 00:00:00 -05:00
|
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !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
|
27
28
|
segments:
|
28
29
|
- 0
|
29
30
|
version: "0"
|
30
|
-
|
31
|
+
name: activesupport
|
31
32
|
prerelease: false
|
33
|
+
type: :runtime
|
32
34
|
version_requirements: *id001
|
33
35
|
- !ruby/object:Gem::Dependency
|
34
|
-
name: yajl-ruby
|
35
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
36
37
|
none: false
|
37
38
|
requirements:
|
38
39
|
- - ">="
|
39
40
|
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
40
42
|
segments:
|
41
43
|
- 0
|
42
44
|
version: "0"
|
43
|
-
|
45
|
+
name: yajl-ruby
|
44
46
|
prerelease: false
|
47
|
+
type: :runtime
|
45
48
|
version_requirements: *id002
|
46
49
|
- !ruby/object:Gem::Dependency
|
47
|
-
name: rake
|
48
50
|
requirement: &id003 !ruby/object:Gem::Requirement
|
49
51
|
none: false
|
50
52
|
requirements:
|
51
53
|
- - ">="
|
52
54
|
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
53
56
|
segments:
|
54
57
|
- 0
|
55
58
|
version: "0"
|
56
|
-
|
59
|
+
name: rake
|
57
60
|
prerelease: false
|
61
|
+
type: :development
|
58
62
|
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 7
|
70
|
+
segments:
|
71
|
+
- 2
|
72
|
+
version: "2"
|
73
|
+
name: rspec
|
74
|
+
prerelease: false
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
59
77
|
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"
|
60
78
|
email:
|
61
79
|
- willem@railsdoctors.com
|
@@ -74,6 +92,8 @@ files:
|
|
74
92
|
- Rakefile
|
75
93
|
- lib/love.rb
|
76
94
|
- love.gemspec
|
95
|
+
- spec/love_spec.rb
|
96
|
+
- spec/spec_helper.rb
|
77
97
|
- tasks/github-gem.rb
|
78
98
|
has_rdoc: true
|
79
99
|
homepage: http://github.com/wvanbergen/love
|
@@ -94,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
114
|
requirements:
|
95
115
|
- - ">="
|
96
116
|
- !ruby/object:Gem::Version
|
97
|
-
hash:
|
117
|
+
hash: 3
|
98
118
|
segments:
|
99
119
|
- 0
|
100
120
|
version: "0"
|
@@ -103,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
123
|
requirements:
|
104
124
|
- - ">="
|
105
125
|
- !ruby/object:Gem::Version
|
106
|
-
hash:
|
126
|
+
hash: 3
|
107
127
|
segments:
|
108
128
|
- 0
|
109
129
|
version: "0"
|
@@ -114,5 +134,5 @@ rubygems_version: 1.3.7
|
|
114
134
|
signing_key:
|
115
135
|
specification_version: 3
|
116
136
|
summary: Ruby library to access the Tender REST API.
|
117
|
-
test_files:
|
118
|
-
|
137
|
+
test_files:
|
138
|
+
- spec/love_spec.rb
|