cupid 0.2.4 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ .test_account
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ tmp
17
+ example.rb
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in cupid.gemspec
1
+ source 'http://rubygems.org'
4
2
  gemspec
data/README.md CHANGED
@@ -6,91 +6,47 @@ Sponsored by Evil Martians <http://evilmartians.com>
6
6
 
7
7
  ## Usage
8
8
 
9
- Add cupid initializer to your config/initializers and now there only two parameters to configure:
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.configure do |config|
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
- After that you can create Cupid::Session object and do some stuff through it:
16
+ Now you can send requests to ExactTarget through this object.
20
17
 
21
- ``` ruby
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
- # User object:
51
- {
52
- :email => 'email@email.com',
53
- :lists => [list_id1, list_id2...],
54
- :first_name => 'Name',
55
- :last_name => 'Lastname'
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', '0.2.4'
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', :version => '0.2.4'
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
- require 'rubygems'
2
- require 'rake'
3
- require 'bundler'
4
- Bundler::GemHelper.install_tasks
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
@@ -1,25 +1,23 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
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 = "cupid"
7
- s.version = Cupid::VERSION
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 = %q{Create, organize and send emails through Exact Target SOAP API}
13
- s.description = %q{Send love, not war. This version of cupid can only work with ET SOAP API with s4.}
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.rubyforge_project = "cupid"
12
+ s.add_dependency 'builder', '>= 2'
13
+ s.add_dependency 'nokogiri', '~> 1'
14
+ s.add_dependency 'savon', '~> 0.9'
16
15
 
17
- s.add_dependency("builder", ">= 2.1.2")
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 = ["lib"]
21
+ s.require_paths = ['lib']
22
+ s.version = Cupid::VERSION
25
23
  end
@@ -1,10 +1,41 @@
1
- require("cupid/configuration")
1
+ require 'savon'
2
+ Dir[File.expand_path '../cupid/**/*.rb', __FILE__].each {|it| require it }
2
3
 
3
- module Cupid
4
- extend Configuration
4
+ class Cupid
5
+ NAMESPACE = 'http://exacttarget.com/wsdl/partnerAPI'
6
+ ENDPOINT = 'https://webservice.s4.exacttarget.com/Service.asmx'
5
7
 
6
- def self.configure
7
- yield self if block_given?
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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ # ExactTarget follows camelcase convention for soap objects
2
+ Gyoku.convert_symbols_to :camelcase
@@ -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