action_kit_api 0.1.11 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ Feature: Establish a Connection
2
+ In order to use the ActionKitAPI
3
+ As a developer
4
+ I want to establish a connection
5
+
6
+ Scenario: Connection with valid credentials should success
7
+ When authenticating with valid credentials
8
+ Then I can get the remote API version
9
+
@@ -0,0 +1,13 @@
1
+ When /^authenticating with valid credentials$/ do
2
+ # These globals are set in the file features/support/credentials.rb you'll need
3
+ # to already have a valid API user with ActionKit to use these tests
4
+ ActionKitApi.connect(ACTIONKIT_USERNAME, ACTIONKIT_PASSWORD, ACTIONKIT_HOSTNAME)
5
+ end
6
+
7
+ Then /^I can get the remote API version$/ do
8
+ ActionKitApi.connection.version.should =~ /ActionKit \d\.\d\.\d+/
9
+ end
10
+
11
+ Given /^I have an active connection$/ do
12
+ ActionKitApi.connect(ACTIONKIT_USERNAME, ACTIONKIT_PASSWORD, ACTIONKIT_HOSTNAME)
13
+ end
@@ -1,25 +1,57 @@
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)
1
+ Given /^I have found an existing user$/ do
2
+ @user = ActionKitApi::User.find_by_email('arealaddress@ipromise.com')
3
+ end
4
+
5
+ When /^I create a user with valid attributes$/ do
6
+ @user = ActionKitApi::User.new({
7
+ "email" => "arealaddress@ipromise.com",
8
+ "first_name" => "Real",
9
+ "last_name" => "Name",
10
+ "zip" => "00001"
11
+ })
12
+ end
13
+
14
+ When /^I save the user$/ do
10
15
  @user.save
11
16
  end
12
17
 
13
- When /^I search for user email "([^"]*)"$/ do |email|
14
- @user = ActionKitApi::User.find_by_email(email)
18
+ When /^I create a user missing an email address$/ do
19
+ @user = ActionKitApi::User.new({
20
+ "first_name" => "Real",
21
+ "last_name" => "Name",
22
+ "zip" => "00001"
23
+ })
24
+ end
25
+
26
+ When /^I change their zip code$/ do
27
+ @new_zip = @user.zip.reverse
28
+ @user.zip = @new_zip
29
+ end
30
+
31
+ When /^I search for a user that doesn't exist$/ do
32
+ @user = ActionKitApi::User.find_by_email('thisisnteven@arealtld.lorp')
15
33
  end
16
34
 
17
- When /^I search for user ID "([^"]*)"$/ do |id|
18
- @user = ActionKitApi::User.find_by_id(id)
35
+ When /^I search a user that has already been created$/ do
36
+ @user = ActionKitApi::User.find_by_email('arealaddress@ipromise.com')
19
37
  end
20
38
 
21
- Then /^I should have a valid user$/ do
39
+ Then /^I should receive a valid user$/ do
22
40
  @user.should be_valid
23
41
  end
24
42
 
43
+ Then /^I should get an error when I save$/ do
44
+ lambda{ @user.save }.should raise_error
45
+ end
46
+
47
+ Then /^the user should have an ActionKit ID$/ do
48
+ @user.akid.should_not be_nil
49
+ end
25
50
 
51
+ Then /^I should receive nil$/ do
52
+ @user.should be_nil
53
+ end
54
+
55
+ Then /^the user should have the new zip code$/ do
56
+ @user.zip.should eql(@new_zip)
57
+ end
@@ -0,0 +1,15 @@
1
+ Feature: Create a new user through the API
2
+ In order to track a person's actions
3
+ As a developer
4
+ I want to create a user for them
5
+
6
+ Scenario: Create a valid user
7
+ Given I have an active connection
8
+ When I create a user with valid attributes
9
+ And I save the user
10
+ Then the user should have an ActionKit ID
11
+
12
+ Scenario: Create an invalid user
13
+ Given I have an active connection
14
+ When I create a user missing an email address
15
+ Then I should get an error when I save
@@ -0,0 +1,14 @@
1
+ Feature: Find a user by email address
2
+ In order to act on people we already know about
3
+ As a developer
4
+ I should be able to find a user using their email address
5
+
6
+ Scenario: Find an existing user
7
+ Given I have an active connection
8
+ When I search a user that has already been created
9
+ Then I should receive a valid user
10
+
11
+ Scenario: Find a non-existant user
12
+ Given I have an active connection
13
+ When I search for a user that doesn't exist
14
+ Then I should receive nil
@@ -0,0 +1,11 @@
1
+ Feature: Update a user's information in ActionKit
2
+ In order to keep user's information up to date
3
+ As a developer
4
+ I should be able to make persistant changes to a user's instance
5
+
6
+ Scenario: Update a user's zip code
7
+ Given I have an active connection
8
+ And I have found an existing user
9
+ When I change their zip code
10
+ And I save the user
11
+ Then the user should have the new zip code
@@ -16,6 +16,8 @@ require "action_kit_api/page_types/unsubscribe"
16
16
  require "action_kit_api/page_types/import"
17
17
 
18
18
  module ActionKitApi
19
+ @@connection = nil
20
+
19
21
  # Takes a page object and a user object and a hash of any additional
20
22
  # attributes and records the action. Will return a Action object with
21
23
  # status information and unique identifiers
@@ -32,8 +34,18 @@ module ActionKitApi
32
34
  "akid" => user.akid,
33
35
  })
34
36
 
35
- response = ActionKitApi::Connection.call('act', act_attrs)
37
+ @@connection.call('act', act_attrs)
36
38
 
37
39
  ActionKitApi::Action.new(response)
38
40
  end
41
+
42
+ # Takes username, password, and hostname. This is simply a wrapper to
43
+ # ActionKitAPi::Connection.connect to make the gem a bit easier to use
44
+ def self.connect(username, password, hostname)
45
+ @@connection = ActionKitApi::Connection.new(username, password, hostname)
46
+ end
47
+
48
+ def self.connection
49
+ @@connection
50
+ end
39
51
  end
@@ -1,11 +1,11 @@
1
1
  require 'xmlrpc/client'
2
2
 
3
- module XMLRPC
4
- # This file adds the connection information for the ActionKit API
5
- # which is used throughout the rest of the Gem (a connection being
6
- # required for ... well every operation ... this is a remote API
7
- # gem :)
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 :)
8
7
 
8
+ module XMLRPC
9
9
  # This seems silly and dangerous right? Overwriting something in the ruby
10
10
  # standard libraries? Well ActionKit is doing XMLRPC wrong. This is almost
11
11
  # identical to the Ruby standard library version however it drops the type
@@ -36,22 +36,18 @@ module XMLRPC
36
36
  remove_const(:USER_AGENT)
37
37
  USER_AGENT = "DFA Ruby action_kit_api/#{ActionKitApi::VERSION} ( http://code.democracyforamerica.com/ )"
38
38
  end
39
- end
40
39
 
41
- module ActionKitApi
42
- class NoConnection < Exception
43
- # Nothing special, will be raised in the event something
44
- # trys to use a connection and one doesn't exist
40
+ # This was need to prevent empty responses from the remote
41
+ # server from triggering an exception on calls
42
+ module Config
43
+ remove_const(:ENABLE_NIL_PARSER)
44
+ ENABLE_NIL_PARSER = true
45
45
  end
46
+ end
46
47
 
48
+ module ActionKitApi
47
49
  class Connection
48
- # This was need to prevent empty responses from the remote
49
- # server from triggering an exception on calls
50
- XMLRPC::Config::ENABLE_NIL_PARSER = true
51
-
52
- @@connection = nil
53
-
54
- def self.connect(user, password, host)
50
+ def initialize(user, password, host)
55
51
  # Build the arguments for the XMLRPC::Client object
56
52
  conn_args = {
57
53
  :user => user,
@@ -61,29 +57,28 @@ module ActionKitApi
61
57
  :path => "/api"
62
58
  }
63
59
 
64
- @@connection = XMLRPC::Client.new_from_hash(conn_args)
60
+ @connection = XMLRPC::Client.new_from_hash(conn_args)
65
61
 
66
62
  # Unfortunately XMLRPC::Client does not expose it's SSL settings so
67
63
  # we need to dive into it's instance methods to set SSL verification
68
64
  # and add a CA to authenticate against
69
- @@connection.instance_variable_get("@http").verify_mode = OpenSSL::SSL::VERIFY_PEER
70
- @@connection.instance_variable_get("@http").ca_file = File.join(File.dirname(__FILE__), "actionkit_ca_chain.pem")
65
+ @connection.instance_variable_get("@http").verify_mode = OpenSSL::SSL::VERIFY_PEER
66
+ @connection.instance_variable_get("@http").ca_file = File.join(File.dirname(__FILE__), "actionkit_ca_chain.pem")
71
67
 
72
68
  return nil
73
69
  end
74
70
 
75
- def self.call(command, args)
76
- raise NoConnection if @@connection.nil?
77
-
71
+ def call(command, args)
78
72
  # XMLRPC::Client doesn't flush it's IO buffer which occasionally
79
73
  # causes a second command on the same connection to fail with an
80
74
  # EOFError. Using the _async version of this causes XMLRPC::Client
81
75
  # to use a new connection each time eliminating this issue
82
- @@connection.call_async(command, args)
76
+ @connection.call_async(command, args)
83
77
  end
84
78
 
85
- def self.version
86
- self.call("version")
79
+ def version
80
+ self.call("version", {})
87
81
  end
82
+
88
83
  end
89
84
  end
@@ -18,9 +18,12 @@ module ActionKitApi
18
18
 
19
19
  # TODO: Add blacklisting to the model to prevent read-only attributes from syncing
20
20
  # this is a temporary fix.
21
- hash = self.to_hash
22
- hash.delete("subscription_status")
23
- response = ActionKitApi::Connection.call("#{class_name}.save_or_create", hash)
21
+ attrs_hash = self.to_hash
22
+ attrs_hash.delete_if do |k, v|
23
+ k == :subscription_status
24
+ end
25
+
26
+ response = ActionKitApi.connection.call("#{class_name}.save_or_create", attrs_hash)
24
27
 
25
28
  # Update ourselves to include the data that the server populated
26
29
  self.update(response)
@@ -62,7 +65,7 @@ module ActionKitApi
62
65
 
63
66
  # XMLRPC::Client doesn't like empty values
64
67
  user_hash.delete_if do |k, v|
65
- v.to_s.empty?
68
+ v.to_s.empty? || v.nil?
66
69
  end
67
70
 
68
71
  user_hash
@@ -25,7 +25,7 @@ module ActionKitApi
25
25
  search = self.parse_attributes(attrs, args)
26
26
 
27
27
  begin
28
- response = ActionKitApi::Connection.call("#{class_name}.get", search)
28
+ response = ActionKitApi.connection.call("#{class_name}.get", search)
29
29
  rescue XMLRPC::FaultException => exception
30
30
  if exception.faultCode == "DoesNotExist"
31
31
  return nil
@@ -56,7 +56,7 @@ module ActionKitApi
56
56
  search["limit"] = 1000
57
57
  #search["offset"] = 0
58
58
 
59
- response = ActionKitApi::Connection.call("#{class_name}.get", search)
59
+ response = ActionKitApi.connection.call("#{class_name}.get", search)
60
60
 
61
61
  response_objects = []
62
62
  response.each do |obj|
@@ -67,7 +67,7 @@ module ActionKitApi
67
67
  end
68
68
 
69
69
  #def class_name.count(search)
70
- # ActionKitApi::Connection.call("#{class_name}.count", search)
70
+ # ActionKitApi.connection.call("#{class_name}.count", search)
71
71
  #end
72
72
 
73
73
  # Helper method for method_missing and the find_* virtual methods that
@@ -1,3 +1,3 @@
1
1
  module ActionKitApi
2
- VERSION = "0.1.11"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActionKitApi::Connection do
4
+
5
+ it "should raise an error when there is no connection" do
6
+ lambda { ActionKitApi.connection.version }.should raise_error
7
+ end
8
+
9
+ it "should use asynchronous calls on the remote API" do
10
+ pending
11
+ end
12
+
13
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,8 +3,19 @@ require 'action_kit_api'
3
3
  require 'features/support/credentials'
4
4
 
5
5
  RSpec.configure do |config|
6
- config.filter_run_excluding :certificates => true, :remote => true
7
-
8
6
  config.color_enabled = true
9
7
  config.formatter = 'documentation'
10
8
  end
9
+
10
+ # This was taken from Rails master commit 1eecd94, it's so useful for testing and
11
+ # I was so sad to find it was not a part of the Ruby native Hash implementation
12
+ class Hash
13
+ def except(*keys)
14
+ dup.except!(*keys)
15
+ end
16
+
17
+ def except!(*keys)
18
+ keys.each { |key| delete(key) }
19
+ self
20
+ end
21
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module AKAUserSpecHelper
4
+ def validUserAttributes
5
+ {
6
+ :email => "tuser@example.org",
7
+ :first_name => "Test",
8
+ :last_name => "User",
9
+ :zip => "00001"
10
+ }
11
+ end
12
+ end
13
+
14
+ describe ActionKitApi::User do
15
+ include AKAUserSpecHelper
16
+
17
+ before(:each) do
18
+ @user = ActionKitApi::User.new
19
+ end
20
+
21
+ it "should verify that an email is required" do
22
+ @user.update(validUserAttributes.except(:email))
23
+ @user.should_not be_valid
24
+ end
25
+
26
+ it "should verify that a zip is required" do
27
+ @user.update(validUserAttributes.except(:zip))
28
+ @user.should_not be_valid
29
+ end
30
+
31
+ it "should verify that first_name is required" do
32
+ @user.update(validUserAttributes.except(:first_name))
33
+ @user.should_not be_valid
34
+ end
35
+
36
+ it "should verify that last_name is required" do
37
+ @user.update(validUserAttributes.except(:last_name))
38
+ @user.should_not be_valid
39
+ end
40
+
41
+ it "should not allow arbitrary attributes to be added" do
42
+ @user.update({"fake_attr" => "with some data"})
43
+ user_hash = @user.to_hash
44
+
45
+ user_hash.should_not include("fake_attr")
46
+ end
47
+
48
+ it "should return it's attributes as a hash" do
49
+ @user.update(validUserAttributes)
50
+ user_hash = @user.to_hash
51
+
52
+ user_hash.should eql(validUserAttributes)
53
+ end
54
+
55
+ it "should not try to save subscription_status" do
56
+ pending
57
+ end
58
+
59
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 2
7
8
  - 1
8
- - 11
9
- version: 0.1.11
9
+ version: 0.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Sam Stelfox
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-02-27 00:00:00 -05:00
17
+ date: 2012-03-05 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -58,13 +58,13 @@ files:
58
58
  - README.md
59
59
  - Rakefile
60
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
61
+ - features/connections/establish_a_connection.feature
62
+ - features/step_definitions/connection_steps.rb
66
63
  - features/step_definitions/user_steps.rb
67
64
  - features/support/env.rb
65
+ - features/users/create_a_user.feature
66
+ - features/users/find_a_user.feature
67
+ - features/users/update_a_user.feature
68
68
  - lib/action_kit_api.rb
69
69
  - lib/action_kit_api/action.rb
70
70
  - lib/action_kit_api/actionkit_ca_chain.pem
@@ -78,9 +78,10 @@ files:
78
78
  - lib/action_kit_api/searchable.rb
79
79
  - lib/action_kit_api/user.rb
80
80
  - lib/action_kit_api/version.rb
81
- - spec/connections/action_kit_api_connection_spec.rb
81
+ - spec/connection/connection_spec.rb
82
82
  - spec/remote_certificates_spec.rb
83
83
  - spec/spec_helper.rb
84
+ - spec/user/user_model_spec.rb
84
85
  has_rdoc: true
85
86
  homepage: http://code.democracyforamerica.com/
86
87
  licenses:
@@ -112,13 +113,14 @@ signing_key:
112
113
  specification_version: 3
113
114
  summary: Wrapper for the ActionKit API
114
115
  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
116
+ - features/connections/establish_a_connection.feature
117
+ - features/step_definitions/connection_steps.rb
120
118
  - features/step_definitions/user_steps.rb
121
119
  - features/support/env.rb
122
- - spec/connections/action_kit_api_connection_spec.rb
120
+ - features/users/create_a_user.feature
121
+ - features/users/find_a_user.feature
122
+ - features/users/update_a_user.feature
123
+ - spec/connection/connection_spec.rb
123
124
  - spec/remote_certificates_spec.rb
124
125
  - spec/spec_helper.rb
126
+ - spec/user/user_model_spec.rb
@@ -1,11 +0,0 @@
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
-
@@ -1,14 +0,0 @@
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
@@ -1,15 +0,0 @@
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
-
@@ -1,6 +0,0 @@
1
- Given /^I have instantiated the API$/ do
2
- # Uses information provided in credential file to setup the API's connection
3
- ActionKitApi::Connection.connect(ACTIONKIT_USERNAME,
4
- ACTIONKIT_PASSWORD, ACTIONKIT_HOSTNAME)
5
- end
6
-
@@ -1,11 +0,0 @@
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
@@ -1,24 +0,0 @@
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
-