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 CHANGED
@@ -1,3 +1,4 @@
1
1
  .DS_store
2
2
  .bundle/
3
3
  .yardoc/
4
+ pkg/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- love (0.0.1)
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
@@ -1,7 +1,7 @@
1
1
  = Love
2
2
 
3
- This library accesses the Tender REST API. It is my ode and thanks to the many
4
- improvements Aaron Patterson did on ActiveRecord.
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 still had to handle paging manually to get all data. Love will automatically
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.each_users { |c| ... }
38
+ tender.each_user { |c| ... }
35
39
  tender.each_queue { |q| ... }
36
40
  tender.each_category { |c| ... }
37
41
 
@@ -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
- class Love
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
- def get_user(id_or_href, options = {})
33
- if id_or_href.to_s =~ /(\d+)$/
34
- get("users/#{$1}", options)
35
- else
36
- # TODO: use href
37
- nil
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
- def get_discussion(id_or_href, options = {})
42
- if id_or_href.to_s =~ /(\d+)$/
43
- get("discussions/#{$1}", options)
44
- else
45
- # TODO: use href
46
- nil
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
- def each_category(options = {}, &block)
51
- buffered_each('categories', 'categories', options, &block)
52
- end
44
+
45
+ class Client
46
+
47
+ include Love::ResourceURI
53
48
 
54
- def each_queue(options = {}, &block)
55
- buffered_each('queues', 'named_queues', options, &block)
56
- end
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
- def each_user(options = {}, &block)
59
- buffered_each('users', 'users', options, &block)
60
- end
61
+ def get_user(id_or_href, options = {})
62
+ get(singleton_uri(id_or_href, 'users'))
63
+ end
61
64
 
62
- def each_discussion(options = {}, &block)
63
- buffered_each('discussions', 'discussions', options, &block)
64
- end
65
+ def get_discussion(id_or_href, options = {})
66
+ get(singleton_uri(id_or_href, 'discussions'))
67
+ end
65
68
 
66
- protected
69
+ def get_category(id_or_href, options = {})
70
+ get(singleton_uri(id_or_href, 'categories'))
71
+ end
67
72
 
68
- def get(path, options = {})
69
- url = URI.parse("https://api.tenderapp.com/#{account}/#{path}")
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
- logger.debug "GET #{url.to_s}" if logger
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
- http = Net::HTTP.new(url.host, url.port)
74
- http.use_ssl = true
75
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
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
- req = Net::HTTP::Get.new(url.path, {
78
- "Accept" => "application/vnd.tender-v1+json",
79
- "X-Tender-Auth" => api_key
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
- response = http.request(req)
83
- case response.code
84
- when /2\d\d/
85
- converter = Encoding::Converter.new('binary', 'utf-8', :invalid => :replace, :undef => :replace)
86
- Yajl::Parser.new.parse(converter.convert(response.body))
87
- when '401'
88
- raise Love::Unauthorized, "Invalid credentials used!"
89
- else
90
- raise Love::Exception, "#{response.cody}: #{response.body}"
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
- def buffered_each(path, list_key, options = {}, &block)
95
- query_options = {}
96
- query_options[:since] = options[:since].to_date.to_s(:db) if options[:since]
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
- initial_result = get(path, :query => query_options)
99
- start_page = [options[:start_page].to_i, 1].max rescue 1
100
- max_page = (initial_result['total'].to_f / initial_result['per_page'].to_f).ceil
101
- end_page = options[:end_page].nil? ? max_page : [options[:end_page].to_i, max_page].min
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
- logger.debug "Paged requests to #{path}: #{max_page} total pages, importing #{start_page} upto #{end_page}." if logger
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
- start_page.upto(end_page) do |page|
108
- result = get(path, :query => query_options.merge(:page => page))
109
- result[list_key].each { |record| yield(record) }
110
- sleep(sleep_between_requests) if sleep_between_requests
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
@@ -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.1"
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
- # s.add_development_dependency('rspec', '~> 2.1')
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
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require(:default, :development)
5
+
6
+ RSpec.configure do |config|
7
+ # nothing here
8
+ end
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
- - 1
9
- version: 0.0.1
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
- type: :runtime
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
- type: :runtime
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
- type: :development
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: 1414303505794391607
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: 1414303505794391607
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