action_kit_api 0.1.0
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/.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
|