cupid 0.2.4 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +1 -3
- data/README.md +18 -61
- data/Rakefile +11 -4
- data/cupid.gemspec +12 -14
- data/lib/cupid.rb +36 -5
- data/lib/cupid/create.rb +67 -0
- data/lib/cupid/delete.rb +16 -0
- data/lib/cupid/gyoku.rb +2 -0
- data/lib/cupid/response.rb +23 -0
- data/lib/cupid/response/caster.rb +34 -0
- data/lib/cupid/response/data.rb +48 -0
- data/lib/cupid/response/data_folder.rb +56 -0
- data/lib/cupid/response/format.rb +53 -0
- data/lib/cupid/response/object.rb +40 -0
- data/lib/cupid/retrieve.rb +63 -0
- data/lib/cupid/schedule.rb +58 -0
- data/lib/cupid/server.rb +62 -0
- data/lib/cupid/update.rb +15 -0
- data/lib/cupid/version.rb +2 -2
- data/spec/cupid/create_spec.rb +48 -0
- data/spec/cupid/delete_spec.rb +21 -0
- data/spec/cupid/response/caster_spec.rb +19 -0
- data/spec/cupid/response/data_spec.rb +51 -0
- data/spec/cupid/response/format_spec.rb +45 -0
- data/spec/cupid/response/object_spec.rb +23 -0
- data/spec/cupid/retrieve_spec.rb +12 -0
- data/spec/cupid/server_spec.rb +22 -0
- data/spec/cupid_spec.rb +8 -0
- data/spec/spec_helper.rb +11 -0
- metadata +95 -91
- data/Gemfile.lock +0 -37
- data/lib/cupid/configuration.rb +0 -19
- data/lib/cupid/methods.rb +0 -3
- data/lib/cupid/methods/email.rb +0 -215
- data/lib/cupid/methods/list.rb +0 -13
- data/lib/cupid/methods/subscriber.rb +0 -56
- data/lib/cupid/session.rb +0 -78
- data/spec/cupid/cupid_spec.rb +0 -0
- data/tmp/dancing_with_ET.rb +0 -235
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -6,91 +6,47 @@ Sponsored by Evil Martians <http://evilmartians.com>
|
|
6
6
|
|
7
7
|
## Usage
|
8
8
|
|
9
|
-
|
9
|
+
Get a new cupid object for every account you want to work with (usually
|
10
|
+
it's the only one):
|
10
11
|
|
11
12
|
``` ruby
|
12
|
-
Cupid.
|
13
|
-
config.username = 'username'
|
14
|
-
config.password = 'password'
|
15
|
-
config.account = 'default_client_id'
|
16
|
-
end
|
13
|
+
Cupid::MyAccount = Cupid.new :username, :password, :account
|
17
14
|
```
|
18
15
|
|
19
|
-
|
16
|
+
Now you can send requests to ExactTarget through this object.
|
20
17
|
|
21
|
-
|
22
|
-
# Creating session
|
23
|
-
et_translator = Cupid::Session.new
|
24
|
-
|
25
|
-
# Retrieving folders for not default account (your_account_id can be nil - default account on ET)
|
26
|
-
folders = et_translator.retrieve_email_folders
|
27
|
-
|
28
|
-
# Retrieving email copies for not default account (your_account_id can be nil - default account on ET)
|
29
|
-
copies = et_translator.retrieve_email_copies(name)
|
30
|
-
|
31
|
-
# Retrieving emails from list for not default account (your_account_id can be nil - default account on ET)
|
32
|
-
copies = et_translator.retrieve_emails_from_list(list_id)
|
33
|
-
|
34
|
-
# Creating new folder
|
35
|
-
# not required fields for folder: description, content_type, is_active, is_editable, allow_children
|
36
|
-
new_folder_id = et_translator.create_folder('title', :parent => parent_directory_id)
|
37
|
-
|
38
|
-
# Creating folders
|
39
|
-
# not required fields for folders: description, content_type, is_active, is_editable, allow_children
|
40
|
-
new_folder_ids = et_translator.create_folders(['title', 'title2'], :parent => parent_directory_id)
|
41
|
-
|
42
|
-
# Creating new email
|
43
|
-
# not required fields for email: email_type, is_html_paste, character_set, name, description, category_id
|
44
|
-
new_email_id = et_translator.create_email('subject', 'body')
|
45
|
-
|
46
|
-
# Creating emails
|
47
|
-
# not required fields for emails: email_type, is_html_paste, character_set, name, description, category_id
|
48
|
-
new_email_ids = et_translator.create_emails({'subject' => 'body', 'subject2' => 'body2'})
|
18
|
+
Small example:
|
49
19
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# Creating new subscriber
|
59
|
-
# not required fields for subscriber: first_name, last_name, client_id
|
60
|
-
new_subscriber_id = et_translator.create_subscriber(user_object)
|
61
|
-
|
62
|
-
# Creating subscribers
|
63
|
-
# not required fields for subscriber: first_name, last_name, client_id
|
64
|
-
new_subscriber_ids = et_translator.create_subscribers([user_object1, user_object2...])
|
65
|
-
|
66
|
-
# Send email to list
|
67
|
-
# not required fields for send: account
|
68
|
-
send_tracking_id = et_translator.send_email_to_list(email_id, list_id)
|
69
|
-
|
70
|
-
# Send emails to lists
|
71
|
-
# not required fields for send: account
|
72
|
-
send_tracking_ids = et_translator.send_emails_to_lists({'email_id' => 'list_id','email_id' => 'list_id'...})
|
20
|
+
``` ruby
|
21
|
+
Cupid::MyAccount.tap do |it|
|
22
|
+
list = it.lists.last
|
23
|
+
new_email = it.create_email :subject, :body
|
24
|
+
it.create_delivery list, new_email
|
25
|
+
it.delete_emails *it.emails
|
26
|
+
end
|
73
27
|
```
|
74
28
|
|
29
|
+
There is much more underneath. See specs for some examples.
|
30
|
+
|
75
31
|
## Installation
|
76
32
|
|
77
33
|
Puts this line into `Gemfile` then run `$ bundle`:
|
78
34
|
|
79
35
|
``` ruby
|
80
|
-
gem 'cupid'
|
36
|
+
gem 'cupid'
|
81
37
|
```
|
82
38
|
|
83
39
|
Or if you are old-school Rails 2 developer put this into `config/environment.rb` and run `$ rake gems:install`:
|
84
40
|
|
85
41
|
``` ruby
|
86
|
-
config.gem 'cupid'
|
42
|
+
config.gem 'cupid'
|
87
43
|
```
|
88
44
|
|
89
45
|
Or manually install cupid gem: `$ gem install cupid`
|
90
46
|
|
91
47
|
## Contributors
|
92
48
|
|
93
|
-
* @gazay
|
49
|
+
* @gazay, @brainopia
|
94
50
|
|
95
51
|
## License
|
96
52
|
|
@@ -103,3 +59,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
103
59
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
104
60
|
|
105
61
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
62
|
+
|
data/Rakefile
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
desc 'Clean up test account'
|
5
|
+
task :clean_et do
|
6
|
+
Bundler.require :default, :development
|
7
|
+
require './spec/spec_helper'
|
8
|
+
api = Cupid::Test
|
9
|
+
api.delete_emails *api.emails
|
10
|
+
api.delete_folders *api.folders.reject(&:root?)
|
11
|
+
end
|
data/cupid.gemspec
CHANGED
@@ -1,25 +1,23 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require "cupid/version"
|
2
|
+
require File.expand_path('../lib/cupid/version', __FILE__)
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
7
|
-
s.
|
8
|
-
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ['gazay']
|
5
|
+
s.name = 'cupid'
|
6
|
+
s.authors = ['gazay', 'brainopia']
|
10
7
|
s.email = ['gazay@evilmartians.com']
|
11
|
-
s.homepage =
|
12
|
-
s.summary =
|
13
|
-
s.description =
|
8
|
+
s.homepage = 'http://github.com/evilmartians/cupid'
|
9
|
+
s.summary = 'Create, organize and send emails through Exact Target SOAP API'
|
10
|
+
s.description = 'Send love, not war. This version of cupid works with ET SOAP API s4.'
|
14
11
|
|
15
|
-
s.
|
12
|
+
s.add_dependency 'builder', '>= 2'
|
13
|
+
s.add_dependency 'nokogiri', '~> 1'
|
14
|
+
s.add_dependency 'savon', '~> 0.9'
|
16
15
|
|
17
|
-
s.
|
18
|
-
s.add_dependency("nokogiri", ">= 1.4.1")
|
19
|
-
s.add_dependency("savon", ">= 0.9.0")
|
16
|
+
s.add_development_dependency 'rspec', '~> 2'
|
20
17
|
|
21
18
|
s.files = `git ls-files`.split("\n")
|
22
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
-
s.require_paths = [
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
s.version = Cupid::VERSION
|
25
23
|
end
|
data/lib/cupid.rb
CHANGED
@@ -1,10 +1,41 @@
|
|
1
|
-
require
|
1
|
+
require 'savon'
|
2
|
+
Dir[File.expand_path '../cupid/**/*.rb', __FILE__].each {|it| require it }
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
class Cupid
|
5
|
+
NAMESPACE = 'http://exacttarget.com/wsdl/partnerAPI'
|
6
|
+
ENDPOINT = 'https://webservice.s4.exacttarget.com/Service.asmx'
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
+
include Create, Update, Delete, Retrieve , Schedule
|
9
|
+
|
10
|
+
attr_reader :client, :server
|
11
|
+
|
12
|
+
def initialize(username, password, account)
|
13
|
+
@client = client_with username, password
|
14
|
+
@server = Server.new account
|
15
|
+
end
|
16
|
+
|
17
|
+
def resources(action, xml)
|
18
|
+
Response.parse raw_request(action, xml).body
|
19
|
+
end
|
20
|
+
|
21
|
+
def resource(*args)
|
22
|
+
resources(*args).first
|
8
23
|
end
|
9
24
|
|
25
|
+
private
|
26
|
+
|
27
|
+
def raw_request(action, xml)
|
28
|
+
client.request action do
|
29
|
+
soap.input = server.input action
|
30
|
+
soap.body = xml
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def client_with(username, password)
|
35
|
+
Savon::Client.new.tap do |client|
|
36
|
+
client.wsdl.namespace = NAMESPACE
|
37
|
+
client.wsdl.endpoint = ENDPOINT
|
38
|
+
client.wsse.credentials username, password
|
39
|
+
end
|
40
|
+
end
|
10
41
|
end
|
data/lib/cupid/create.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Create
|
3
|
+
def create(type, data)
|
4
|
+
resource :create, server.object(type, data)
|
5
|
+
end
|
6
|
+
|
7
|
+
def create_folder(title, parent, options={})
|
8
|
+
create 'DataFolder', folder(title, parent, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_email(title, body, options={})
|
12
|
+
create 'Email', email(title, body, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_delivery(email, list)
|
16
|
+
create 'Send', delivery(email, list)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_path(*folder_names)
|
20
|
+
children = folders.select(&:root?)
|
21
|
+
folder_names.inject(nil) do |parent, name|
|
22
|
+
folder = children.find {|it| it.name == name }
|
23
|
+
children = folder ? folder.children : []
|
24
|
+
folder or create_folder(name, parent)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def folder(title, parent, options)
|
31
|
+
raise ArgumentError unless title and parent and options
|
32
|
+
|
33
|
+
{
|
34
|
+
:name => title,
|
35
|
+
:content_type => :email,
|
36
|
+
:description => nil,
|
37
|
+
:is_active => true,
|
38
|
+
:is_editable => true,
|
39
|
+
:allow_children => true,
|
40
|
+
:parent_folder => {
|
41
|
+
'ID' => parent
|
42
|
+
}
|
43
|
+
}.merge options
|
44
|
+
end
|
45
|
+
|
46
|
+
def email(title, body, options)
|
47
|
+
raise ArgumentError unless title and body and options
|
48
|
+
|
49
|
+
{
|
50
|
+
:email_type => 'HTML',
|
51
|
+
:character_set => 'utf-8',
|
52
|
+
:subject => title,
|
53
|
+
'HTMLBody' => body,
|
54
|
+
'IsHTMLPaste' => true
|
55
|
+
}.merge options
|
56
|
+
end
|
57
|
+
|
58
|
+
def delivery(email, list)
|
59
|
+
raise ArgumentError unless email and list
|
60
|
+
|
61
|
+
{
|
62
|
+
:email => { 'ID' => email },
|
63
|
+
:list => { 'ID' => list }
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/cupid/delete.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Delete
|
3
|
+
def delete_folders(*objects)
|
4
|
+
resources :delete, server.folders(objects)
|
5
|
+
end
|
6
|
+
|
7
|
+
def delete_emails(*objects)
|
8
|
+
resources :delete, server.emails(objects)
|
9
|
+
end
|
10
|
+
|
11
|
+
def delete_emails_like(name)
|
12
|
+
objects = emails name
|
13
|
+
objects.empty? ? [] : delete_emails(*objects)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/cupid/gyoku.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Response
|
3
|
+
class << self
|
4
|
+
def parse(body)
|
5
|
+
cast formatted extracted body
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def extracted(body)
|
11
|
+
Data.from body
|
12
|
+
end
|
13
|
+
|
14
|
+
def formatted(raw_data)
|
15
|
+
raw_data.map {|it| Format.apply it }
|
16
|
+
end
|
17
|
+
|
18
|
+
def cast(data)
|
19
|
+
data.map {|it| Caster.create it }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Response
|
3
|
+
module Caster
|
4
|
+
class << self
|
5
|
+
def create(data)
|
6
|
+
klass = class_for data[:type]
|
7
|
+
klass.new data
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def class_for(type)
|
13
|
+
fetch_constant class_name(type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_constant(name)
|
17
|
+
if namespace.const_defined? name
|
18
|
+
namespace.const_get name
|
19
|
+
else
|
20
|
+
namespace::Object
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def namespace
|
25
|
+
Cupid::Response
|
26
|
+
end
|
27
|
+
|
28
|
+
def class_name(type)
|
29
|
+
(type || :object).to_s.camelcase
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Response
|
3
|
+
class Data
|
4
|
+
Error = Class.new StandardError
|
5
|
+
|
6
|
+
def self.from(wrapped_body)
|
7
|
+
new(wrapped_body).results
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(wrapped_body)
|
11
|
+
extract wrapped_body
|
12
|
+
check_status!
|
13
|
+
end
|
14
|
+
|
15
|
+
def results
|
16
|
+
[raw_results].compact.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :body
|
22
|
+
|
23
|
+
def extract(wrapped_body)
|
24
|
+
@body = wrapped_body.values.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_status!
|
28
|
+
raise Error.new error_message unless success?
|
29
|
+
end
|
30
|
+
|
31
|
+
def success?
|
32
|
+
%w(MoreDataAvailable OK).include? status
|
33
|
+
end
|
34
|
+
|
35
|
+
def status
|
36
|
+
body[:overall_status]
|
37
|
+
end
|
38
|
+
|
39
|
+
def raw_results
|
40
|
+
body[:results]
|
41
|
+
end
|
42
|
+
|
43
|
+
def error_message
|
44
|
+
raw_results ? raw_results[:status_message] : status
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'cupid/response/object'
|
2
|
+
|
3
|
+
class Cupid
|
4
|
+
module Response
|
5
|
+
class DataFolder < Object
|
6
|
+
ALL = []
|
7
|
+
|
8
|
+
attr_reader :parent, :children
|
9
|
+
fields :parent_data, :name
|
10
|
+
|
11
|
+
def self.new(data)
|
12
|
+
find(data[:id]) || super
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find(id)
|
16
|
+
ALL.find {|it| it.id == id }
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(data)
|
20
|
+
super
|
21
|
+
assign_children
|
22
|
+
extract_parent
|
23
|
+
add_to_identity_map
|
24
|
+
end
|
25
|
+
|
26
|
+
def root?
|
27
|
+
not parent
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def assign_children
|
33
|
+
@children = ALL.select {|it| it.parent == self }
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_parent
|
37
|
+
if parent_data
|
38
|
+
create_parent
|
39
|
+
attach_to_parent
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_to_identity_map
|
44
|
+
ALL << self
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_parent
|
48
|
+
@parent = self.class.new parent_data
|
49
|
+
end
|
50
|
+
|
51
|
+
def attach_to_parent
|
52
|
+
parent.children << self
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|