icontact-api 0.1
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.tar.gz.sig +3 -0
- data/History.txt +4 -0
- data/Manifest.txt +13 -0
- data/README.rdoc +82 -0
- data/Rakefile +27 -0
- data/lib/icontact-api.rb +5 -0
- data/lib/icontact/api.rb +112 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/api_spec.rb +177 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/rspec.rake +21 -0
- metadata +119 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
= icontact-api
|
2
|
+
|
3
|
+
== DESCRIPTION:
|
4
|
+
|
5
|
+
This gem provides a thin wrapper around the iContact 2.0 API.
|
6
|
+
|
7
|
+
== FEATURES/PROBLEMS:
|
8
|
+
|
9
|
+
* automatically signs each request
|
10
|
+
* handles packing and unpacking into json
|
11
|
+
* query params can be passed as an options hash
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
Normally you will want to subclass the Icontact::Api class and define your API_KEY and API_SECRET, and optionally you may wish to hard-code a username and password for the API.
|
16
|
+
|
17
|
+
@api = MyApi.new(username, password)
|
18
|
+
@api.get('/a/000000/c/000000/messages', :limit=>10, :offset=>0)
|
19
|
+
|
20
|
+
Please see the icontact developer documentation for a full listing of the supported resources and url structure.
|
21
|
+
|
22
|
+
This gem is not officially supported by iContact. Please contact the author with bug reports, feature requests, or questions about this gem.
|
23
|
+
|
24
|
+
|
25
|
+
== REQUIREMENTS:
|
26
|
+
|
27
|
+
* requires JSON gem
|
28
|
+
|
29
|
+
== INSTALL:
|
30
|
+
|
31
|
+
* sudo gem install icontact-api
|
32
|
+
|
33
|
+
== USAGE
|
34
|
+
|
35
|
+
for convenience create a class like this that keeps track of your API_KEY, API_SECRET, and optionally an api username and password:
|
36
|
+
|
37
|
+
require 'icontact-api'
|
38
|
+
class BetaApi < Icontact::Api
|
39
|
+
API_USERNAME='username'
|
40
|
+
API_PASSWORD='password'
|
41
|
+
API_KEY = 'YOUR_API_KEY'
|
42
|
+
API_SECRET = 'YOUR_API_SECRET'
|
43
|
+
DOMAIN = "http://app.beta.icontact.com/icp"
|
44
|
+
|
45
|
+
def initialize(username = BetaApi::API_USERNAME, password = BetaApi::API_PASSWORD)
|
46
|
+
super(username, password)
|
47
|
+
self.key = API_KEY
|
48
|
+
self.secret = API_SECRET
|
49
|
+
self.domain = DOMAIN
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Then you can use it like:
|
54
|
+
|
55
|
+
@api = BetaApi.new
|
56
|
+
@api.get('/a')
|
57
|
+
@api.post('/a/00000/c/0000/messages', data)
|
58
|
+
|
59
|
+
== LICENSE:
|
60
|
+
|
61
|
+
(The MIT License)
|
62
|
+
|
63
|
+
Copyright (c) 2009 Kevin Olbrich
|
64
|
+
|
65
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
66
|
+
a copy of this software and associated documentation files (the
|
67
|
+
'Software'), to deal in the Software without restriction, including
|
68
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
69
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
70
|
+
permit persons to whom the Software is furnished to do so, subject to
|
71
|
+
the following conditions:
|
72
|
+
|
73
|
+
The above copyright notice and this permission notice shall be
|
74
|
+
included in all copies or substantial portions of the Software.
|
75
|
+
|
76
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
77
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
78
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
79
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
80
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
81
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
82
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/icontact-api'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('icontact-api', Icontact::Api::VERSION) do |p|
|
7
|
+
p.developer('Kevin Olbrich', 'kevin.olbrich@gmail.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = p.name # TODO this is default value
|
10
|
+
p.extra_deps = [
|
11
|
+
['json','>= 1.1.3'],
|
12
|
+
]
|
13
|
+
p.extra_dev_deps = [
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
+
]
|
16
|
+
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# task :default => [:spec, :features]
|
data/lib/icontact-api.rb
ADDED
data/lib/icontact/api.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'net/http'
|
3
|
+
require 'digest'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class Icontact
|
7
|
+
class Api
|
8
|
+
VERSION = "0.1"
|
9
|
+
API_VERSION = "2.0"
|
10
|
+
DOMAIN = 'http://app.icontact.com/icp'
|
11
|
+
API_KEY = "API_KEY"
|
12
|
+
API_SECRET = "API_SECRET"
|
13
|
+
attr_accessor :username
|
14
|
+
attr_accessor :password
|
15
|
+
attr_accessor :key
|
16
|
+
attr_accessor :secret
|
17
|
+
attr_accessor :domain
|
18
|
+
|
19
|
+
def initialize(username, password)
|
20
|
+
self.username = username
|
21
|
+
self.password = password
|
22
|
+
self.domain = DOMAIN
|
23
|
+
self.key = API_KEY
|
24
|
+
self.secret = API_SECRET
|
25
|
+
end
|
26
|
+
|
27
|
+
# Package up any options into a query string and format it properly for the server
|
28
|
+
# arrays become comma separated lists and Times are expressed as iso8601
|
29
|
+
def self.package_query_params(params={})
|
30
|
+
return nil if params.nil? || params.empty?
|
31
|
+
massaged_params = params.map do |key, value|
|
32
|
+
case value
|
33
|
+
when Array:
|
34
|
+
"#{key}=#{value.join(',')}"
|
35
|
+
when Time:
|
36
|
+
"#{key}=#{value.strftime("%Y-%m-%dT%H:%M:%S")}#{"%+0.2d:00" % (value.gmtoff / 3600)}"
|
37
|
+
else
|
38
|
+
"#{key}=#{value}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
"?#{massaged_params.join('&')}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# override this to use a different encoding method for sending data (like xml)
|
45
|
+
def self.body_encoder(data)
|
46
|
+
data.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
# override this method to use a different response parser (like REXML for an xml response)
|
50
|
+
# when the response is not actually a JSON object an exception is thrown and the raw response is returned in the body instead.
|
51
|
+
def self.parse_response(code, response)
|
52
|
+
{'code'=>code, 'body' => (JSON.parse(response) rescue response)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def request_signature(method, timestamp, random, url)
|
56
|
+
Digest::SHA1.hexdigest("#{secret}#{password}#{timestamp}#{random}#{method.to_s.upcase}#{url}")
|
57
|
+
end
|
58
|
+
|
59
|
+
# populate headers required by the icontact server on each request for authentication
|
60
|
+
# Accept and Content-Type are set to application/json to use JSON objects for the
|
61
|
+
# data exchange. Also accepts text/xml for either, but then you have to deal with XML encoding and decoding
|
62
|
+
# manually
|
63
|
+
def apply_headers(method, req, url)
|
64
|
+
timestamp = Time.now.getgm.to_i
|
65
|
+
random = Kernel.rand(999999)
|
66
|
+
req.add_field('API_VERSION', API_VERSION)
|
67
|
+
req.add_field('ACCEPT','application/json')
|
68
|
+
req.add_field('Content-Type','application/json')
|
69
|
+
req.add_field('API_KEY', self.key)
|
70
|
+
req.add_field('API_USERNAME', self.username)
|
71
|
+
req.add_field('API_TIMESTAMP', timestamp)
|
72
|
+
req.add_field('API_NUMBER', random)
|
73
|
+
req.add_field('API_SIGNATURE', request_signature(method, timestamp, random, url))
|
74
|
+
return req
|
75
|
+
end
|
76
|
+
|
77
|
+
# dynamically create methods for get and delete. These methods do not require a body to be sent
|
78
|
+
[:get, :delete].each do |meth|
|
79
|
+
define_method(meth) do |url, *options|
|
80
|
+
request(meth, url, nil, options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# dynamically create methods for get and delete. These methods require a body to be sent
|
85
|
+
[:post, :put].each do |meth|
|
86
|
+
define_method(meth) do |url, data, *options|
|
87
|
+
request(meth, url, data, options)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Actually make the get, put, post, or delete request
|
92
|
+
def request(kind, url, data, options)
|
93
|
+
# options passed as optional parameter show up as an array
|
94
|
+
options = options.first if options.kind_of? Array
|
95
|
+
query_options = self.class.package_query_params(options)
|
96
|
+
full_url = URI.parse("#{self.domain}#{url}#{query_options}")
|
97
|
+
|
98
|
+
# create an object of the class required to process this method
|
99
|
+
klass = Object.module_eval("::Net::HTTP::#{kind.to_s.capitalize}", __FILE__, __LINE__)
|
100
|
+
request = klass.new([full_url.path, full_url.query].compact.join('?'))
|
101
|
+
request = apply_headers(kind, request, full_url)
|
102
|
+
|
103
|
+
# take passed data and encode it
|
104
|
+
request.body = self.class.body_encoder(data) if data
|
105
|
+
|
106
|
+
Net::HTTP.start(full_url.host, full_url.port) do |http|
|
107
|
+
response = http.request(request)
|
108
|
+
return self.class.parse_response(response.code, response.body)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/icontact-api.rb'}"
|
9
|
+
puts "Loading icontact-api gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Icontact::Api do
|
4
|
+
before(:each) do
|
5
|
+
@mock_time = mock('Time', :getgm => 999999)
|
6
|
+
Time.stub!(:now).and_return(@mock_time)
|
7
|
+
Kernel.stub!(:rand).and_return(111)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "api" do
|
11
|
+
before(:each) do
|
12
|
+
@api = Icontact::Api.new('mock_username', 'mock_password')
|
13
|
+
@response = mock('response',:code=>200, :body=>"body")
|
14
|
+
@mock_http_request = mock('http', :request=>@response)
|
15
|
+
Net::HTTP.stub!(:start).and_yield(@mock_http_request)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "an http request", :shared=>true do
|
19
|
+
it "should apply headers to the request" do
|
20
|
+
@mock_request = mock("Request", :body= => true)
|
21
|
+
@api.should_receive(:apply_headers).and_return(@mock_request)
|
22
|
+
do_request
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should package query params" do
|
26
|
+
@api.class.should_receive(:package_query_params)
|
27
|
+
do_request(:limit=>10)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should send the request" do
|
31
|
+
Net::HTTP.should_receive(:start)
|
32
|
+
do_request
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should parse the server response into a hash" do
|
36
|
+
@api.class.should_receive(:parse_response)
|
37
|
+
do_request
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "an http request with a body", :shared=>true do
|
42
|
+
it_should_behave_like "an http request"
|
43
|
+
|
44
|
+
it "should package the data into the body" do
|
45
|
+
@api.class.should_receive(:body_encoder)
|
46
|
+
do_request
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe ".get" do
|
51
|
+
|
52
|
+
def do_request(options={})
|
53
|
+
@api.get("/a", options)
|
54
|
+
end
|
55
|
+
|
56
|
+
it_should_behave_like "an http request"
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ".post" do
|
60
|
+
def do_request(options={})
|
61
|
+
@api.post("/a", {}, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
it_should_behave_like "an http request with a body"
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".put" do
|
68
|
+
def do_request(options={})
|
69
|
+
@api.put("/a", {}, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
it_should_behave_like "an http request with a body"
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.delete' do
|
76
|
+
def do_request(options={})
|
77
|
+
@api.delete("/a", options)
|
78
|
+
end
|
79
|
+
|
80
|
+
it_should_behave_like "an http request"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".request_signature" do
|
85
|
+
before(:each) do
|
86
|
+
@api = Icontact::Api.new('username','password')
|
87
|
+
Kernel.stub!(:rand).and_return(111)
|
88
|
+
@mock_time = mock('Time', :getgm => 999999)
|
89
|
+
Time.stub!(:now).and_return(@mock_time)
|
90
|
+
Digest::SHA1.stub!(:hexdigest).and_return('api_signature')
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should generate an API Signature" do
|
94
|
+
Digest::SHA1.should_receive(:hexdigest).with('API_SECRETpasswordtimestamp/urlrandomGET').and_return('api_signature')
|
95
|
+
@api.request_signature('get','timestamp''/url', 'random', '').should == "api_signature"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe ".apply_headers" do
|
100
|
+
before(:each) do
|
101
|
+
@request = mock('Request', :add_field=>true)
|
102
|
+
@api = Icontact::Api.new('username','password')
|
103
|
+
end
|
104
|
+
|
105
|
+
def do_apply_headers
|
106
|
+
@api.apply_headers(:get, @request, 'http://fakeurl.com')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should add an API_VERSION header to indicate we are using the 2.0 version of the API" do
|
110
|
+
@request.should_receive(:add_field).with('API_VERSION', @api.class::API_VERSION)
|
111
|
+
do_apply_headers
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should add an ACCEPT header indicating we want a JSON object back" do
|
115
|
+
@request.should_receive(:add_field).with('ACCEPT', 'application/json')
|
116
|
+
do_apply_headers
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should add a Content-Type header indicating that passed data is JSON encoded" do
|
120
|
+
@request.should_receive(:add_field).with('Content-Type', 'application/json')
|
121
|
+
do_apply_headers
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should add an API_KEY header to verify that our application is authrorized to use the API" do
|
125
|
+
@request.should_receive(:add_field).with('API_KEY', @api.class::API_KEY)
|
126
|
+
do_apply_headers
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should add an API_USERNAME header to verify that our user is authorized to use the API" do
|
130
|
+
@request.should_receive(:add_field).with('API_USERNAME', 'username')
|
131
|
+
do_apply_headers
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should add an API_TIMESTAMP header to prevent old requests from being replayed" do
|
135
|
+
@request.should_receive(:add_field).with('API_TIMESTAMP', 999999)
|
136
|
+
do_apply_headers
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should add an API_NUMBER header" do
|
140
|
+
@request.should_receive(:add_field).with('API_NUMBER', 111)
|
141
|
+
do_apply_headers
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should add an API_SIGNATURE header to ensure this api request is valid" do
|
145
|
+
@api.should_receive(:request_signature).and_return('api_signature')
|
146
|
+
@request.should_receive(:add_field).with('API_SIGNATURE', 'api_signature')
|
147
|
+
do_apply_headers
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "class methods" do
|
152
|
+
it ".body_encoder should encode the data to be sent in JSON" do
|
153
|
+
data = {:messageIds=>[1,2,3,4,5]}
|
154
|
+
Icontact::Api::body_encoder(data).should == data.to_json
|
155
|
+
end
|
156
|
+
|
157
|
+
it ".parse_response should parse the JSON response into a hash" do
|
158
|
+
body = {:messageIds=>[1,2,3,4,5]}.to_json
|
159
|
+
Icontact::Api::parse_response(200, body).should == {"body"=>{"messageIds"=>[1, 2, 3, 4, 5]}, "code"=>200}
|
160
|
+
end
|
161
|
+
|
162
|
+
describe ".package_query_params" do
|
163
|
+
# the "string".split('').sort trick is used because the string may come back in a different order each time because
|
164
|
+
# it is generated from an unordered hash, this trick allows comparison
|
165
|
+
it "should package a hash into a query string according to the API requirements" do
|
166
|
+
options = {:limit=>10, :offset=>10, :messageId=>[1,2,3,4], :createdAt=>Time.at(1234567890)}
|
167
|
+
Icontact::Api::package_query_params(options).split('').sort.should ==
|
168
|
+
"?limit=10&messageId=1,2,3,4&createdAt=2009-02-13T18:31:30-05:00&offset=10".split('').sort
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return nil if passed an empty hash" do
|
172
|
+
Icontact::Api::package_query_params({}).should be_nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: icontact-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Olbrich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDPDCCAiSgAwIBAgIBADANBgkqhkiG9w0BAQUFADBEMRYwFAYDVQQDDA1rZXZp
|
14
|
+
bi5vbGJyaWNoMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
|
15
|
+
FgNjb20wHhcNMDkwMzA3MTUwMzQ5WhcNMTAwMzA3MTUwMzQ5WjBEMRYwFAYDVQQD
|
16
|
+
DA1rZXZpbi5vbGJyaWNoMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
|
17
|
+
k/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvza3W
|
18
|
+
J5Dloirbhpge6teyZNfvOzuo478vtqnU/c6S8o2EerBkmWm88J+ipItO3U2HDPgP
|
19
|
+
a7THrx41/QEj1oG15mO6wtBLf0Z+AOJFAmf+Th8x3b46b3Voo1+suON67fCqCz+U
|
20
|
+
L5V2I5NBNywCte5Nzjh7knLkHr+rUCCKAIPK6vq+sASMIgmlhzZUuQfdRZMhRVZP
|
21
|
+
clMwf9sIp7LUTq2b/LCdP2dlGFydb1cyvaHjho+X/pk5uWKcr1OkrZtYFdxyeGU4
|
22
|
+
ZTtqEd5vaNTacYf0vGBfPfiolIfbmOa8Pgfyj8zkjBug/iukM/YGmwL1KAk8Qs+/
|
23
|
+
gInxSkyPxa7zkYTzAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0G
|
24
|
+
A1UdDgQWBBS+y2GRZjETz6d3jHKosqOZ6Ll+zjANBgkqhkiG9w0BAQUFAAOCAQEA
|
25
|
+
f3hCt6NBuESjAN4fkpbcfi3/Nj06aNl6CAfryfKaOaIfLcypT5nqP1qtqqGih5ne
|
26
|
+
QCe7jmtZHOGFC/SSlCS8obHSkCGn2cfUl/InfpF/YQnZV0Kp8esihuVmEjO0SgEU
|
27
|
+
yf46wW9Nh4byKctkgKMlWnIqwAyk4G1dD/le6MGJutb4pv5hT9qm1vaTFkAcy1Qu
|
28
|
+
1VMZa+MH6xnINGdFyV8T7AYL5BaQTbp2WYsfbOvy3ZfxbpP5O3wc3wter/Cyp8pu
|
29
|
+
Wvn0/+13roT+v5T5yFIpu6NbSpuqvRaKZvS+n2ZgwS10rUPFydzn+335umE4vsfH
|
30
|
+
4Umf0rf1f8h+GJia5xXYww==
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
|
33
|
+
date: 2009-03-24 00:00:00 -04:00
|
34
|
+
default_executable:
|
35
|
+
dependencies:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: json
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.1.3
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: newgem
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.3
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: hoe
|
58
|
+
type: :development
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.8.0
|
65
|
+
version:
|
66
|
+
description: This gem provides a thin wrapper around the iContact 2.0 API.
|
67
|
+
email:
|
68
|
+
- kevin.olbrich@gmail.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files:
|
74
|
+
- History.txt
|
75
|
+
- Manifest.txt
|
76
|
+
- README.rdoc
|
77
|
+
files:
|
78
|
+
- History.txt
|
79
|
+
- Manifest.txt
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- lib/icontact-api.rb
|
83
|
+
- lib/icontact/api.rb
|
84
|
+
- script/console
|
85
|
+
- script/destroy
|
86
|
+
- script/generate
|
87
|
+
- spec/api_spec.rb
|
88
|
+
- spec/spec.opts
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- tasks/rspec.rake
|
91
|
+
has_rdoc: true
|
92
|
+
homepage:
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options:
|
95
|
+
- --main
|
96
|
+
- README.rdoc
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project: icontact-api
|
114
|
+
rubygems_version: 1.3.1
|
115
|
+
signing_key:
|
116
|
+
specification_version: 2
|
117
|
+
summary: This gem provides a thin wrapper around the iContact 2.0 API.
|
118
|
+
test_files: []
|
119
|
+
|
metadata.gz.sig
ADDED
Binary file
|