action_kit_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +23 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +18 -0
- data/Rakefile +1 -0
- data/action_kit_api.gemspec +25 -0
- data/features/create_a_user.feature +11 -0
- data/features/find_a_page.feature +14 -0
- data/features/find_a_user.feature +15 -0
- data/features/step_definitions/general_api_steps.rb +6 -0
- data/features/step_definitions/page_steps.rb +11 -0
- data/features/step_definitions/user_steps.rb +25 -0
- data/features/support/env.rb +3 -0
- data/lib/action_kit_api.rb +14 -0
- data/lib/action_kit_api/action.rb +41 -0
- data/lib/action_kit_api/actionkit_ca_chain.pem +167 -0
- data/lib/action_kit_api/connection.rb +50 -0
- data/lib/action_kit_api/data_model.rb +64 -0
- data/lib/action_kit_api/page.rb +24 -0
- data/lib/action_kit_api/page_types/import.rb +14 -0
- data/lib/action_kit_api/page_types/petition.rb +10 -0
- data/lib/action_kit_api/page_types/signup.rb +10 -0
- data/lib/action_kit_api/page_types/unsubscribe.rb +10 -0
- data/lib/action_kit_api/searchable.rb +84 -0
- data/lib/action_kit_api/user.rb +41 -0
- data/lib/action_kit_api/version.rb +3 -0
- data/spec/connections/action_kit_api_connection_spec.rb +24 -0
- data/spec/remote_certificates_spec.rb +26 -0
- data/spec/spec_helper.rb +10 -0
- metadata +124 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
environment_id="ruby-1.8.7-p357@actionkitapi"
|
4
|
+
|
5
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]; then
|
6
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
7
|
+
|
8
|
+
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]; then
|
9
|
+
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
10
|
+
fi
|
11
|
+
else
|
12
|
+
if ! rvm --create "$environment_id"; then
|
13
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
14
|
+
return 1
|
15
|
+
fi
|
16
|
+
fi
|
17
|
+
|
18
|
+
if [[ $- == *i* ]]; then
|
19
|
+
echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)"a
|
20
|
+
else
|
21
|
+
echo "Using: $GEM_HOME"
|
22
|
+
fi
|
23
|
+
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2012 Democracy For America
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
## Post-clone Instructions
|
2
|
+
To use the spec and cucumber feature tests you need to have valid ActionKit
|
3
|
+
API credentials. These credentials need to be places in the file
|
4
|
+
/features/support/credentials.rb for RSpec and Cucumber to call. These calls
|
5
|
+
will create test users, and eventually pages, sign users up for things and
|
6
|
+
otherwise affect potentially live data.
|
7
|
+
|
8
|
+
***Please be aware of what the tests are doing before running them.***
|
9
|
+
|
10
|
+
Now that you've read the warning this is what the contents of the credentials
|
11
|
+
file should look like.
|
12
|
+
|
13
|
+
# These are credentials that need to be set for testing against
|
14
|
+
# live ActionKit. These will never live in the repository
|
15
|
+
|
16
|
+
ACTIONKIT_HOSTNAME=""
|
17
|
+
ACTIONKIT_USERNAME=""
|
18
|
+
ACTIONKIT_PASSWORD=""
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "action_kit_api/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "action_kit_api"
|
7
|
+
s.version = ActionKitApi::VERSION
|
8
|
+
s.authors = ["Sam Stelfox"]
|
9
|
+
s.email = ["sstelfox@democracyforamerica.com"]
|
10
|
+
s.homepage = "http://democracyforamerica.com/"
|
11
|
+
s.license = "MIT"
|
12
|
+
|
13
|
+
s.summary = "Wrapper for the ActionKit API"
|
14
|
+
s.description = "Provides encapsulated object access through the ActionKit API"
|
15
|
+
|
16
|
+
s.rubyforge_project = "action_kit_api"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "cucumber"
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Feature: Create a user
|
2
|
+
In order to add a user to a mailing lists
|
3
|
+
As a developer
|
4
|
+
I should be able to create a user through the API
|
5
|
+
|
6
|
+
Scenario: Create a user
|
7
|
+
Given I have instantiated the API
|
8
|
+
When I create a user with the email "tuser@example.org", first name "Test", last name "User", and zipcode "12345"
|
9
|
+
And I search for user email "tuser@example.org"
|
10
|
+
Then I should have a valid user
|
11
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Find a page
|
2
|
+
In order to act on page
|
3
|
+
As a developer
|
4
|
+
I should be able to find a page
|
5
|
+
|
6
|
+
Scenario: Find a page by name
|
7
|
+
Given I have instantiated the API
|
8
|
+
When I search for page name "jstreetdonation"
|
9
|
+
Then I should have a valid page
|
10
|
+
|
11
|
+
Scenario: Find a page by id
|
12
|
+
Given I have instantiated the API
|
13
|
+
When I search for page ID "1"
|
14
|
+
Then I should have a valid page
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: Find a user
|
2
|
+
In order to update a user
|
3
|
+
As a developer
|
4
|
+
I should be able to find a user
|
5
|
+
|
6
|
+
Scenario: Find a user by email
|
7
|
+
Given I have instantiated the API
|
8
|
+
When I search for user email "tuser@example.org"
|
9
|
+
Then I should have a valid user
|
10
|
+
|
11
|
+
Scenario: Find a user by ID
|
12
|
+
Given I have instantiated the API
|
13
|
+
When I search for user ID "39221"
|
14
|
+
Then I should have a valid user
|
15
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
When /^I search for page name "([^"]*)"$/ do |name|
|
2
|
+
@page = ActionKitApi::Page.find_by_name(name)
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I search for page ID "([^"]*)"$/ do |id|
|
6
|
+
@page = ActionKitApi::Page.find_by_id(id)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^I should have a valid page$/ do
|
10
|
+
@page.should be_valid
|
11
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
When /^I create a user with the email "([^"]*)", first name "([^"]*)", last name "([^"]*)", and zipcode "([^"]*)"$/ do |email, first_name, last_name, zip|
|
2
|
+
attrs = {
|
3
|
+
:email => email,
|
4
|
+
:first_name => first_name,
|
5
|
+
:last_name => last_name,
|
6
|
+
:zip => :zip
|
7
|
+
}
|
8
|
+
|
9
|
+
@user = ActionKitApi::User.new(attrs)
|
10
|
+
@user.save
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I search for user email "([^"]*)"$/ do |email|
|
14
|
+
@user = ActionKitApi::User.find_by_email(email)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I search for user ID "([^"]*)"$/ do |id|
|
18
|
+
@user = ActionKitApi::User.find_by_id(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^I should have a valid user$/ do
|
22
|
+
@user.should be_valid
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# These are the primary portions of the API wrapper runtime
|
2
|
+
require "action_kit_api/connection"
|
3
|
+
require "action_kit_api/searchable"
|
4
|
+
require "action_kit_api/data_model"
|
5
|
+
|
6
|
+
# Extensions to the data model for specific information
|
7
|
+
require "action_kit_api/user"
|
8
|
+
require "action_kit_api/page"
|
9
|
+
|
10
|
+
# Page types
|
11
|
+
require "action_kit_api/page_types/petition"
|
12
|
+
require "action_kit_api/page_types/signup"
|
13
|
+
require "action_kit_api/page_types/unsubscribe"
|
14
|
+
require "action_kit_api/page_types/import"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'action_kit_api'
|
2
|
+
|
3
|
+
module ActionKitApi
|
4
|
+
class Action < ApiDataModel
|
5
|
+
include Searchable
|
6
|
+
|
7
|
+
# Required
|
8
|
+
attr_accessor :id, :page, :user
|
9
|
+
|
10
|
+
# Other/Active
|
11
|
+
attr_accessor :created_at, :created_user, :custom_fields, :link, :mailing,
|
12
|
+
:opq_id, :referring_mailing, :referring_user, :source, :status,
|
13
|
+
:subscribed_user, :taf_emails_sent, :updated_at
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
@required_attrs = [:page, :user]
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Takes a page object and a user object and a hash of any additional
|
21
|
+
# attributes and records the action. Will return a Action object with
|
22
|
+
# status information and unique identifiers
|
23
|
+
def act(page, user, args = {})
|
24
|
+
return false unless page.valid? and user.valid?
|
25
|
+
|
26
|
+
# Ensure we have an ActionKit ID before performing the action
|
27
|
+
user.save if user.akid.nil?
|
28
|
+
|
29
|
+
# Include the supplied arguments overiding akid and page name if they
|
30
|
+
# were supplied with those in the page and user
|
31
|
+
act_attrs = args.update({
|
32
|
+
"page" => page.name,
|
33
|
+
"akid" => user.akid,
|
34
|
+
})
|
35
|
+
|
36
|
+
response = ActionKitApi::Connection.call('act', act_attrs)
|
37
|
+
|
38
|
+
self.new(response)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
Certificate:
|
2
|
+
Data:
|
3
|
+
Version: 3 (0x2)
|
4
|
+
Serial Number: 145105 (0x236d1)
|
5
|
+
Signature Algorithm: sha1WithRSAEncryption
|
6
|
+
Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
|
7
|
+
Validity
|
8
|
+
Not Before: Feb 19 22:45:05 2010 GMT
|
9
|
+
Not After : Feb 18 22:45:05 2020 GMT
|
10
|
+
Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA
|
11
|
+
Subject Public Key Info:
|
12
|
+
Public Key Algorithm: rsaEncryption
|
13
|
+
Public-Key: (2048 bit)
|
14
|
+
Modulus:
|
15
|
+
00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3:
|
16
|
+
fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24:
|
17
|
+
43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6:
|
18
|
+
41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd:
|
19
|
+
5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8:
|
20
|
+
e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37:
|
21
|
+
e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47:
|
22
|
+
69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a:
|
23
|
+
e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c:
|
24
|
+
03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79:
|
25
|
+
f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc:
|
26
|
+
b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad:
|
27
|
+
f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67:
|
28
|
+
58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b:
|
29
|
+
c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b:
|
30
|
+
2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18:
|
31
|
+
5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1:
|
32
|
+
50:8b
|
33
|
+
Exponent: 65537 (0x10001)
|
34
|
+
X509v3 extensions:
|
35
|
+
X509v3 Key Usage: critical
|
36
|
+
Certificate Sign, CRL Sign
|
37
|
+
X509v3 Subject Key Identifier:
|
38
|
+
6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30
|
39
|
+
X509v3 Authority Key Identifier:
|
40
|
+
keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
|
41
|
+
|
42
|
+
X509v3 Basic Constraints: critical
|
43
|
+
CA:TRUE, pathlen:0
|
44
|
+
X509v3 CRL Distribution Points:
|
45
|
+
|
46
|
+
Full Name:
|
47
|
+
URI:http://crl.geotrust.com/crls/gtglobal.crl
|
48
|
+
|
49
|
+
Authority Information Access:
|
50
|
+
OCSP - URI:http://ocsp.geotrust.com
|
51
|
+
|
52
|
+
Signature Algorithm: sha1WithRSAEncryption
|
53
|
+
ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98:
|
54
|
+
f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2:
|
55
|
+
2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5:
|
56
|
+
03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9:
|
57
|
+
0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63:
|
58
|
+
a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad:
|
59
|
+
39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0:
|
60
|
+
c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74:
|
61
|
+
4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51:
|
62
|
+
59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c:
|
63
|
+
fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3:
|
64
|
+
ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d:
|
65
|
+
99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6:
|
66
|
+
4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54:
|
67
|
+
6c:7a:b7:b7
|
68
|
+
-----BEGIN CERTIFICATE-----
|
69
|
+
MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
70
|
+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
71
|
+
YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG
|
72
|
+
EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM
|
73
|
+
IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0
|
74
|
+
l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e
|
75
|
+
6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb
|
76
|
+
ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8
|
77
|
+
N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5
|
78
|
+
HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd
|
79
|
+
gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC
|
80
|
+
St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w
|
81
|
+
EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js
|
82
|
+
Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw
|
83
|
+
JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B
|
84
|
+
AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x
|
85
|
+
/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O
|
86
|
+
SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61
|
87
|
+
04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4
|
88
|
+
knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK
|
89
|
+
LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw==
|
90
|
+
-----END CERTIFICATE-----
|
91
|
+
Certificate:
|
92
|
+
Data:
|
93
|
+
Version: 3 (0x2)
|
94
|
+
Serial Number: 144470 (0x23456)
|
95
|
+
Signature Algorithm: sha1WithRSAEncryption
|
96
|
+
Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
|
97
|
+
Validity
|
98
|
+
Not Before: May 21 04:00:00 2002 GMT
|
99
|
+
Not After : May 21 04:00:00 2022 GMT
|
100
|
+
Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
|
101
|
+
Subject Public Key Info:
|
102
|
+
Public Key Algorithm: rsaEncryption
|
103
|
+
Public-Key: (2048 bit)
|
104
|
+
Modulus:
|
105
|
+
00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
|
106
|
+
3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
|
107
|
+
43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
|
108
|
+
bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
|
109
|
+
60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
|
110
|
+
ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
|
111
|
+
2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
|
112
|
+
80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
|
113
|
+
15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
|
114
|
+
d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
|
115
|
+
d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
|
116
|
+
5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
|
117
|
+
19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
|
118
|
+
9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
|
119
|
+
fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
|
120
|
+
eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
|
121
|
+
36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
|
122
|
+
e4:f9
|
123
|
+
Exponent: 65537 (0x10001)
|
124
|
+
X509v3 extensions:
|
125
|
+
X509v3 Basic Constraints: critical
|
126
|
+
CA:TRUE
|
127
|
+
X509v3 Subject Key Identifier:
|
128
|
+
C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
|
129
|
+
X509v3 Authority Key Identifier:
|
130
|
+
keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
|
131
|
+
|
132
|
+
Signature Algorithm: sha1WithRSAEncryption
|
133
|
+
35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f:
|
134
|
+
78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf:
|
135
|
+
dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb:
|
136
|
+
64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2:
|
137
|
+
b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20:
|
138
|
+
8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5:
|
139
|
+
ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db:
|
140
|
+
15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f:
|
141
|
+
b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1:
|
142
|
+
c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52:
|
143
|
+
b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9:
|
144
|
+
5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5:
|
145
|
+
e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17:
|
146
|
+
b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80:
|
147
|
+
a1:cb:e6:33
|
148
|
+
-----BEGIN CERTIFICATE-----
|
149
|
+
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
150
|
+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
151
|
+
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
|
152
|
+
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
|
153
|
+
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
|
154
|
+
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
|
155
|
+
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
|
156
|
+
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
|
157
|
+
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
|
158
|
+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
|
159
|
+
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
|
160
|
+
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
|
161
|
+
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
|
162
|
+
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
|
163
|
+
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
|
164
|
+
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
|
165
|
+
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
|
166
|
+
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
|
167
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'xmlrpc/client'
|
2
|
+
|
3
|
+
# This file adds the connection information for the ActionKit API
|
4
|
+
# which is used throughout the rest of the Gem (a connection being
|
5
|
+
# required for ... well every operation ... this is a remote API
|
6
|
+
# gem :)
|
7
|
+
|
8
|
+
module ActionKitApi
|
9
|
+
class NoConnection < Exception
|
10
|
+
# Nothing special, will be raised in the event something
|
11
|
+
# trys to use a connection and one doesn't exist
|
12
|
+
end
|
13
|
+
|
14
|
+
class Connection
|
15
|
+
# This was need to prevent empty responses from the remote
|
16
|
+
# server from triggering an exception on calls
|
17
|
+
XMLRPC::Config::ENABLE_NIL_PARSER = true
|
18
|
+
|
19
|
+
@@connection = nil
|
20
|
+
|
21
|
+
def self.connect(user, password, host)
|
22
|
+
# Build the arguments for the XMLRPC::Client object
|
23
|
+
conn_args = {
|
24
|
+
:user => user,
|
25
|
+
:password => password,
|
26
|
+
:host => host,
|
27
|
+
:use_ssl => true,
|
28
|
+
:path => "/api"
|
29
|
+
}
|
30
|
+
|
31
|
+
@@connection = XMLRPC::Client.new_from_hash(conn_args)
|
32
|
+
|
33
|
+
# Unfortunately XMLRPC::Client does not expose it's SSL settings so
|
34
|
+
# we need to dive into it's instance methods to set SSL verification
|
35
|
+
# and add a CA to authenticate against
|
36
|
+
@@connection.instance_variable_get("@http").verify_mode = OpenSSL::SSL::VERIFY_PEER
|
37
|
+
@@connection.instance_variable_get("@http").ca_file = File.join(File.dirname(__FILE__), "actionkit_ca_chain.pem")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.call(command, args = {})
|
41
|
+
raise NoConnection if @@connection.nil?
|
42
|
+
|
43
|
+
@@connection.call(command, args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.version
|
47
|
+
self.call("version")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'action_kit_api'
|
2
|
+
|
3
|
+
module ActionKitApi
|
4
|
+
class MissingRequiredAttributeException < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
class ApiDataModel
|
8
|
+
|
9
|
+
def initialize(hash = {})
|
10
|
+
@required_attrs = []
|
11
|
+
|
12
|
+
self.update(hash)
|
13
|
+
end
|
14
|
+
|
15
|
+
def save
|
16
|
+
class_name = self.class.to_s.split("::").last
|
17
|
+
|
18
|
+
raise MissingRequiredAttributeException "Unable to save incomplete object" unless self.valid?
|
19
|
+
|
20
|
+
response = ActionKitApi::Connection.call("#{class_name}.save_or_create", self.to_hash)
|
21
|
+
|
22
|
+
# Update ourselves to include the data that the server populated
|
23
|
+
self.update(response)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Updates all the instance variables in our local object with
|
27
|
+
# the values in the hash. This will selectively update only the
|
28
|
+
# keys in the hash that is passed, and will not update/add non-existant
|
29
|
+
# attributes
|
30
|
+
def update(hash = {})
|
31
|
+
hash.each do |k,v|
|
32
|
+
# The name of the setter for the value of k
|
33
|
+
setter = "#{k}="
|
34
|
+
|
35
|
+
# Check if there is a matching setter
|
36
|
+
if self.respond_to?(setter)
|
37
|
+
# Yes, there is. Call the setter with the value
|
38
|
+
self.send(setter, v)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?
|
44
|
+
@required_attrs.each do |k|
|
45
|
+
return false if self.send(k).nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_hash
|
52
|
+
user_hash = {}
|
53
|
+
|
54
|
+
self.instance_variables.each do |iv|
|
55
|
+
key = iv.delete("@").to_sym
|
56
|
+
next if key == :required_attrs
|
57
|
+
user_hash[key] = self.instance_variable_get(iv)
|
58
|
+
end
|
59
|
+
|
60
|
+
user_hash
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'action_kit_api'
|
2
|
+
|
3
|
+
module ActionKitApi
|
4
|
+
# Please note that this class should almost never be directly called
|
5
|
+
# every 'real' page is a specific sub-page type that should extend this
|
6
|
+
# class. Please refer to the ActionKit API documentation for more
|
7
|
+
# information about pages
|
8
|
+
class Page < ApiDataModel
|
9
|
+
include Searchable
|
10
|
+
|
11
|
+
# Required
|
12
|
+
attr_accessor :id, :name, :title
|
13
|
+
attr_reader :type # Page type can't change
|
14
|
+
|
15
|
+
# Other/Active
|
16
|
+
attr_accessor :goal, :goal_type, :lang, :list, :required_fields,
|
17
|
+
:status, :tags, :url
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
@required_attrs = [:name, :title, :type]
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#require 'action_kit_api'
|
2
|
+
|
3
|
+
# This page is a bit more complicated and might be the one that we want
|
4
|
+
# unfotunately, I need to read through ActionKitApi's "Importing User Data"
|
5
|
+
# notes before implementing this page type
|
6
|
+
|
7
|
+
#module ActionKitApi
|
8
|
+
# class ImportPage < Page
|
9
|
+
# def initialize(*args)
|
10
|
+
# @type = 'import'
|
11
|
+
# super
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ActionKitApi
|
2
|
+
module Searchable
|
3
|
+
# This layer is needed to have the methods added at a class level
|
4
|
+
# rather than an instance level
|
5
|
+
def self.included(class_name)
|
6
|
+
|
7
|
+
# Override's method missing to dynamically add find_by_* and
|
8
|
+
# find_all_by_*.
|
9
|
+
def class_name.method_missing(method, *args, &block)
|
10
|
+
case method.to_s
|
11
|
+
when /^find_by_(.+)$/
|
12
|
+
run_find_by_method($1, *args)
|
13
|
+
when /^find_all_by_(.+)$/
|
14
|
+
run_find_all_by_method($1, *args, &block)
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Perform an API search for an individual object
|
21
|
+
def class_name.run_find_by_method(attrs, *args)
|
22
|
+
# Get the name of the class that this module has been mixed into
|
23
|
+
class_name = self.to_s.split("::").last
|
24
|
+
|
25
|
+
search = parse_attributes(attrs, args)
|
26
|
+
response = ActionKitApi::Connection.call("#{class_name}.get", search)
|
27
|
+
|
28
|
+
# Build a new object with the response, this might be an empty
|
29
|
+
# object if the search failed or was invalid
|
30
|
+
self.new(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Perform an API search for all objects that match the criteria, remote
|
34
|
+
# API limit is 1000 results per request so this is added explicitely to
|
35
|
+
# the call to prevent errors, in the future this request probably could
|
36
|
+
# be paginated in a loop and using count to get all of the results that
|
37
|
+
# match.
|
38
|
+
def class_name.run_find_all_by_method(attrs, *args, &block)
|
39
|
+
# Get the name of the class that this module has been mixed into
|
40
|
+
class_name = self.to_s.split("::").last
|
41
|
+
|
42
|
+
search = parse_attributes(attrs, args)
|
43
|
+
|
44
|
+
# For when I get to retrieving all results, through pagination
|
45
|
+
# Would be a good candidate for yielding (probably in batches of 50-100)
|
46
|
+
#response_count = ActionKitApi::Connection.call("#{class_name}.count", search)
|
47
|
+
search["limit"] = 1000
|
48
|
+
#search["offset"] = 0
|
49
|
+
|
50
|
+
response = ActionKitApi::Connection.call("#{class_name}.get", search)
|
51
|
+
|
52
|
+
response_objects = []
|
53
|
+
response.each do |obj|
|
54
|
+
response_objects << self.new(obj)
|
55
|
+
end
|
56
|
+
|
57
|
+
response_objects
|
58
|
+
end
|
59
|
+
|
60
|
+
# Helper method for method_missing and the find_* virtual methods that
|
61
|
+
# parses part of a method name and arguments into a hash usuable for the
|
62
|
+
# API calls
|
63
|
+
def class_name.parse_attributes(attrs, args)
|
64
|
+
attrs = attrs.split('_and_')
|
65
|
+
attrs_with_args = attrs.zip(args)
|
66
|
+
Hash[attrs_with_args]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Provide a means to check if the searchable module can respond to
|
70
|
+
# virtual methods, could be more accurate by checking to ensure the
|
71
|
+
# attributes exist
|
72
|
+
def class_name.respond_to?(method)
|
73
|
+
case method.to_s
|
74
|
+
when /^find_by_(.*)$/
|
75
|
+
true
|
76
|
+
when /^find_all_by_(.*)$/
|
77
|
+
true
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'action_kit_api'
|
2
|
+
|
3
|
+
module ActionKitApi
|
4
|
+
class User < ApiDataModel
|
5
|
+
include Searchable
|
6
|
+
|
7
|
+
# Required
|
8
|
+
attr_accessor :id, :email, :first_name, :last_name, :zip
|
9
|
+
|
10
|
+
# Other/Active
|
11
|
+
attr_accessor :address1, :address2, :akid, :city, :country, :created_at,
|
12
|
+
:custom_fields, :lang, :middle_name, :password, :plus4,
|
13
|
+
:postal, :prefix, :rand_id, :region, :state_name, :source,
|
14
|
+
:state, :subscription_status, :suffix, :token, :updated_at
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
@required_attrs = [:email, :first_name, :last_name, :zip]
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
# Below are methods that haven't been completed yet, but shouldn't
|
22
|
+
# be difficult the functionality is all there in the API, they just weren't
|
23
|
+
# important for us to use RIGHT NOW
|
24
|
+
|
25
|
+
#def subscribed?(list_id = nil)
|
26
|
+
#end
|
27
|
+
|
28
|
+
#def subscriptions
|
29
|
+
#end
|
30
|
+
|
31
|
+
#def subscription_history
|
32
|
+
#end
|
33
|
+
|
34
|
+
#def unsubscribe(list_id)
|
35
|
+
#end
|
36
|
+
|
37
|
+
#def unsubscribe_all
|
38
|
+
#end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper.rb"
|
2
|
+
|
3
|
+
describe "ActionKitApi Connection" do
|
4
|
+
it "should be able to create a new connection" do
|
5
|
+
ActionKitApi::Connection.connect('testuser', 'testpass',
|
6
|
+
'host.example.local')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Tests That Need a Connection", :remote => true do
|
10
|
+
before(:all) do
|
11
|
+
ActionKitApi::Connection.connect(ACTIONKIT_USERNAME,
|
12
|
+
ACTIONKIT_PASSWORD, ACTIONKIT_HOSTNAME)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be able to ask ActionKit for the version of it's API" do
|
16
|
+
ActionKitApi::Connection.version.should =~ /ActionKit \d\.\d\.\d+/
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be able to make arbitrary calls to the remote API" do
|
20
|
+
ActionKitApi::Connection.call("version").should =~ /ActionKit \d\.\d\.\d+/
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# These are tricky because they don't really verify much on our end and use
|
4
|
+
# command line utilities to do the verification, however if we aren't checking
|
5
|
+
# this if ActionKit changes their server certificate it will break our gem and
|
6
|
+
# need to be fixed by supplying new Certificate Authority information.
|
7
|
+
|
8
|
+
# These tests are not run by default. To run them add the --tag @certificates
|
9
|
+
|
10
|
+
describe "Remote API Server Certificate", :certificates => true do
|
11
|
+
it "should be valid using system's certificates" do
|
12
|
+
command_output = `echo "" |
|
13
|
+
openssl s_client -connect dfa.actionkit.com:443 2>&1 |
|
14
|
+
grep "Verify return code"`
|
15
|
+
command_output =~ /[a-zA-Z]+: ([0-9]+)/
|
16
|
+
$1.to_i.should == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be valid using this gem's certificates" do
|
20
|
+
command_output = `echo "" |
|
21
|
+
openssl s_client -CAfile lib/action_kit_api/actionkit_ca_chain.pem -connect dfa.actionkit.com:443 2>&1 |
|
22
|
+
grep "Verify return code"`
|
23
|
+
command_output =~ /[a-zA-Z]+: ([0-9]+)/
|
24
|
+
$1.to_i.should == 0
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: action_kit_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Sam Stelfox
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-02-17 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: cucumber
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
description: Provides encapsulated object access through the ActionKit API
|
45
|
+
email:
|
46
|
+
- sstelfox@democracyforamerica.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- .rvmrc
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- action_kit_api.gemspec
|
61
|
+
- features/create_a_user.feature
|
62
|
+
- features/find_a_page.feature
|
63
|
+
- features/find_a_user.feature
|
64
|
+
- features/step_definitions/general_api_steps.rb
|
65
|
+
- features/step_definitions/page_steps.rb
|
66
|
+
- features/step_definitions/user_steps.rb
|
67
|
+
- features/support/env.rb
|
68
|
+
- lib/action_kit_api.rb
|
69
|
+
- lib/action_kit_api/action.rb
|
70
|
+
- lib/action_kit_api/actionkit_ca_chain.pem
|
71
|
+
- lib/action_kit_api/connection.rb
|
72
|
+
- lib/action_kit_api/data_model.rb
|
73
|
+
- lib/action_kit_api/page.rb
|
74
|
+
- lib/action_kit_api/page_types/import.rb
|
75
|
+
- lib/action_kit_api/page_types/petition.rb
|
76
|
+
- lib/action_kit_api/page_types/signup.rb
|
77
|
+
- lib/action_kit_api/page_types/unsubscribe.rb
|
78
|
+
- lib/action_kit_api/searchable.rb
|
79
|
+
- lib/action_kit_api/user.rb
|
80
|
+
- lib/action_kit_api/version.rb
|
81
|
+
- spec/connections/action_kit_api_connection_spec.rb
|
82
|
+
- spec/remote_certificates_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
has_rdoc: true
|
85
|
+
homepage: http://democracyforamerica.com/
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
requirements: []
|
108
|
+
|
109
|
+
rubyforge_project: action_kit_api
|
110
|
+
rubygems_version: 1.3.6
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: Wrapper for the ActionKit API
|
114
|
+
test_files:
|
115
|
+
- features/create_a_user.feature
|
116
|
+
- features/find_a_page.feature
|
117
|
+
- features/find_a_user.feature
|
118
|
+
- features/step_definitions/general_api_steps.rb
|
119
|
+
- features/step_definitions/page_steps.rb
|
120
|
+
- features/step_definitions/user_steps.rb
|
121
|
+
- features/support/env.rb
|
122
|
+
- spec/connections/action_kit_api_connection_spec.rb
|
123
|
+
- spec/remote_certificates_spec.rb
|
124
|
+
- spec/spec_helper.rb
|