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
@@ -0,0 +1,53 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Response
|
3
|
+
class Format
|
4
|
+
IGNORE = [:partner_key, :object_id]
|
5
|
+
RENAME = {
|
6
|
+
:new_id => :id,
|
7
|
+
:'@xsi:type' => :type,
|
8
|
+
:parent_folder => :parent_data
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.apply(data)
|
12
|
+
formatted = new data
|
13
|
+
formatted.data
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :data
|
17
|
+
|
18
|
+
def initialize(data)
|
19
|
+
@data = data
|
20
|
+
extract_nested_data
|
21
|
+
rename_fields
|
22
|
+
delete_ignored_fields
|
23
|
+
delete_empty_parents
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def extract_nested_data
|
29
|
+
data.merge! data.delete(:object) if data[:object]
|
30
|
+
end
|
31
|
+
|
32
|
+
def rename_fields
|
33
|
+
RENAME.each do |old_name, new_name|
|
34
|
+
next unless data[old_name]
|
35
|
+
data[new_name] = data.delete old_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_ignored_fields(data=@data)
|
40
|
+
data.delete_if do |key, value|
|
41
|
+
delete_ignored_fields value if value.is_a? Hash
|
42
|
+
IGNORE.include? key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_empty_parents
|
47
|
+
if (data[:parent_data] || {})[:id] == '0'
|
48
|
+
data.delete :parent_data
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Response
|
3
|
+
class Object
|
4
|
+
def self.fields(*names)
|
5
|
+
names.each do |name|
|
6
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
7
|
+
def #{name}
|
8
|
+
data[:#{name}]
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :data
|
15
|
+
fields :id, :customer_key
|
16
|
+
|
17
|
+
def initialize(data)
|
18
|
+
@data = data
|
19
|
+
end
|
20
|
+
|
21
|
+
# Hook into gyoku
|
22
|
+
def call
|
23
|
+
id
|
24
|
+
end
|
25
|
+
|
26
|
+
def type
|
27
|
+
instance_of?(Object) ? self[:type] : self.class
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(object)
|
31
|
+
return false unless object.is_a? Cupid::Response::Object
|
32
|
+
id and [id, type] == [object.id, object.type]
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](field)
|
36
|
+
data[field]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Retrieve
|
3
|
+
LIST_FIELDS = %w(ID ListName CustomerKey)
|
4
|
+
EMAIL_FIELDS = %w(ID Name)
|
5
|
+
FOLDER_FIELDS = %w(ID Name ParentFolder.ID ParentFolder.Name)
|
6
|
+
DELIVERY_FIELDS = %w(ID Status)
|
7
|
+
UI_EMAIL_FIELDS = %w(CustomerKey Name Email.ID EmailSubject CategoryID)
|
8
|
+
|
9
|
+
def emails(name=nil, *fields)
|
10
|
+
retrieve 'Email',
|
11
|
+
EMAIL_FIELDS + fields,
|
12
|
+
filter_email_like(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ui_emails(folder=nil, *fields)
|
16
|
+
retrieve 'EmailSendDefinition',
|
17
|
+
UI_EMAIL_FIELDS + fields,
|
18
|
+
filter_by_folder(folder)
|
19
|
+
end
|
20
|
+
|
21
|
+
def folders(*fields)
|
22
|
+
data_folders 'email', *fields
|
23
|
+
end
|
24
|
+
|
25
|
+
def ui_folders(*fields)
|
26
|
+
data_folders 'userinitiated', *fields
|
27
|
+
end
|
28
|
+
|
29
|
+
def lists(*fields)
|
30
|
+
retrieve 'List', LIST_FIELDS + fields
|
31
|
+
end
|
32
|
+
|
33
|
+
def deliveries(*fields)
|
34
|
+
retrieve 'Send', DELIVERY_FIELDS + fields
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def retrieve(type, fields, options={})
|
40
|
+
resources :retrieve, :retrieve_request => {
|
41
|
+
:object_type => type,
|
42
|
+
:properties => fields,
|
43
|
+
'ClientIDs' => { 'ID' => server.account }
|
44
|
+
}.merge(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def data_folders(type, *fields)
|
48
|
+
retrieve 'DataFolder', FOLDER_FIELDS + fields, filter_folders(type)
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter_folders(type)
|
52
|
+
server.filter 'ContentType', 'like', type
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_email_like(name)
|
56
|
+
server.filter 'Name', 'like', name
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter_by_folder(id)
|
60
|
+
server.filter 'CategoryID', 'equals', id
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Schedule
|
3
|
+
|
4
|
+
def schedule(ui, time, options={})
|
5
|
+
resource :schedule, :action => 'start',
|
6
|
+
:options => schedule_options,
|
7
|
+
:schedule => schedule_fields(time.iso8601, options),
|
8
|
+
:interactions => interactions_fields(ui.customer_key)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def schedule_fields(time, options)
|
14
|
+
{
|
15
|
+
:recurrence => {
|
16
|
+
:daily_recurrence_pattern_type => 'Interval',
|
17
|
+
:day_interval => 1
|
18
|
+
},
|
19
|
+
:attributes! => {
|
20
|
+
:recurrence => {
|
21
|
+
'xsi:type' => 'DailyRecurrence'
|
22
|
+
}
|
23
|
+
},
|
24
|
+
:recurrence_type => 'Daily',
|
25
|
+
:recurrence_range_type => 'EndAfter',
|
26
|
+
:start_date_time => time,
|
27
|
+
:occurrences => 1
|
28
|
+
}.merge options
|
29
|
+
end
|
30
|
+
|
31
|
+
def schedule_options
|
32
|
+
{
|
33
|
+
:client => {
|
34
|
+
'ID' => server.account
|
35
|
+
},
|
36
|
+
:attributes! => {
|
37
|
+
:client => {
|
38
|
+
'xsi:type' => 'ClientID'
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def interactions_fields(ui_key)
|
45
|
+
{
|
46
|
+
:interaction => {
|
47
|
+
:customer_key => ui_key
|
48
|
+
},
|
49
|
+
:attributes! => {
|
50
|
+
:interaction => {
|
51
|
+
'xsi:type' => 'EmailSendDefinition'
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/cupid/server.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
class Cupid
|
2
|
+
class Server
|
3
|
+
INPUT_NAMES = {
|
4
|
+
:version_info => 'VersionInfoRequestMsg',
|
5
|
+
:get_system_status => 'SystemStatusRequestMsg',
|
6
|
+
:retrieve => 'RetrieveRequestMsg',
|
7
|
+
:schedule => 'ScheduleRequestMsg',
|
8
|
+
:create => 'CreateRequest',
|
9
|
+
:delete => 'DeleteRequest',
|
10
|
+
:update => 'UpdateRequest'
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_reader :account
|
14
|
+
|
15
|
+
def initialize(account)
|
16
|
+
@account = account
|
17
|
+
end
|
18
|
+
|
19
|
+
def input(action)
|
20
|
+
[INPUT_NAMES.fetch(action), { :xmlns => Cupid::NAMESPACE }]
|
21
|
+
end
|
22
|
+
|
23
|
+
def filter(field, operator, value)
|
24
|
+
return {} unless value
|
25
|
+
{
|
26
|
+
:filter => {
|
27
|
+
:property => field,
|
28
|
+
:simple_operator => operator,
|
29
|
+
:value => value
|
30
|
+
},
|
31
|
+
:attributes! => {
|
32
|
+
:filter => { 'xsi:type' => 'SimpleFilterPart' }
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def objects(type, objects)
|
38
|
+
{
|
39
|
+
:objects => objects.map {|object|
|
40
|
+
{
|
41
|
+
:client => { 'ID' => account },
|
42
|
+
}.merge(object)
|
43
|
+
},
|
44
|
+
:attributes! => {
|
45
|
+
:objects => { 'xsi:type' => type }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def object(type, options)
|
51
|
+
objects type, [options]
|
52
|
+
end
|
53
|
+
|
54
|
+
def emails(ids)
|
55
|
+
objects 'Email', ids.map {|it| { 'ID' => it }}
|
56
|
+
end
|
57
|
+
|
58
|
+
def folders(ids)
|
59
|
+
objects 'DataFolder', ids.map {|it| { 'ID' => it }}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/cupid/update.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Cupid
|
2
|
+
module Update
|
3
|
+
def ui_set_email(ui, email)
|
4
|
+
resource :update, ui_data(ui, email)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def ui_data(ui, email)
|
10
|
+
server.object 'EmailSendDefinition',
|
11
|
+
:customer_key => ui.customer_key,
|
12
|
+
:email => { 'ID' => email }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/cupid/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION =
|
1
|
+
class Cupid
|
2
|
+
VERSION = "0.3.2"
|
3
3
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
describe Cupid::Create do
|
2
|
+
subject { Cupid::Test }
|
3
|
+
|
4
|
+
def uniq_name(label)
|
5
|
+
"#{label} #{Time.now.to_i}"
|
6
|
+
end
|
7
|
+
|
8
|
+
shared_examples_for :creation do
|
9
|
+
it { creation.should be }
|
10
|
+
it { expect { creation }.to change { subject.send(items).size }.by 1 }
|
11
|
+
end
|
12
|
+
|
13
|
+
context '#create_folder' do
|
14
|
+
let(:parent) { subject.folders.first }
|
15
|
+
let(:name) { uniq_name :folder }
|
16
|
+
let(:items) { :folders }
|
17
|
+
let(:creation) { subject.create_folder name, parent }
|
18
|
+
|
19
|
+
it_should_behave_like :creation
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#create_email' do
|
23
|
+
let(:name) { uniq_name :subject }
|
24
|
+
let(:items) { :emails }
|
25
|
+
let(:creation) { subject.create_email name, :body }
|
26
|
+
|
27
|
+
it_should_behave_like :creation
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#create_delivery' do
|
31
|
+
let(:email) { subject.emails.last }
|
32
|
+
let(:list) { subject.lists.find {|it| it[:list_name] == 'Test send' }}
|
33
|
+
let(:items) { :deliveries }
|
34
|
+
let(:creation) { subject.create_delivery email, list }
|
35
|
+
|
36
|
+
it_should_behave_like :creation
|
37
|
+
end
|
38
|
+
|
39
|
+
context '#create_path' do
|
40
|
+
let(:new_folders) {['my emails', Time.now.to_i, 'level2', 'level3']}
|
41
|
+
|
42
|
+
it do
|
43
|
+
expect {
|
44
|
+
subject.create_path *new_folders
|
45
|
+
}.to change { subject.folders.size }.by new_folders.size - 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
describe Cupid::Delete do
|
2
|
+
subject { Cupid::Test }
|
3
|
+
|
4
|
+
shared_examples_for 'deletion' do
|
5
|
+
let(:email) { subject.create_email email_name, 'body' }
|
6
|
+
let(:email_name) { 'subject' }
|
7
|
+
|
8
|
+
it { deletion.should be }
|
9
|
+
it { subject.emails.should_not include(email) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#delete_emails' do
|
13
|
+
let(:deletion) { subject.delete_emails email }
|
14
|
+
it_should_behave_like 'deletion'
|
15
|
+
end
|
16
|
+
|
17
|
+
context '#delete_emails_like' do
|
18
|
+
let(:deletion) { subject.delete_emails_like email_name }
|
19
|
+
it_should_behave_like 'deletion'
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe Cupid::Response::Caster, '.create' do
|
2
|
+
subject { described_class.create :type => type }
|
3
|
+
Cupid::Response::SomeType = Class.new Cupid::Response::Object
|
4
|
+
|
5
|
+
context 'no type' do
|
6
|
+
let(:type) { nil }
|
7
|
+
it { should be_a Cupid::Response::Object }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'type without class' do
|
11
|
+
let(:type) { :type_without_class }
|
12
|
+
it { should be_a Cupid::Response::Object }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'type with class' do
|
16
|
+
let(:type) { :some_type }
|
17
|
+
it { should be_a Cupid::Response::SomeType }
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
describe Cupid::Response::Data do
|
2
|
+
subject { described_class.from response }
|
3
|
+
|
4
|
+
let(:response) {{
|
5
|
+
:body => {
|
6
|
+
:overall_status => status,
|
7
|
+
:results => result
|
8
|
+
}
|
9
|
+
}}
|
10
|
+
|
11
|
+
describe 'unsuccessful response' do
|
12
|
+
let(:status) { 'some error' }
|
13
|
+
|
14
|
+
shared_examples_for 'response error' do
|
15
|
+
it { expect { subject }.to raise_exception described_class::Error, error_text }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with status message' do
|
19
|
+
let(:result) {{ :status_message => error_text }}
|
20
|
+
let(:error_text) { 'bad_request' }
|
21
|
+
|
22
|
+
it_should_behave_like 'response error'
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'without status message' do
|
26
|
+
let(:result) { nil }
|
27
|
+
let(:error_text) { status }
|
28
|
+
|
29
|
+
it_should_behave_like 'response error'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'successful reponse' do
|
34
|
+
let(:status) { 'OK' }
|
35
|
+
|
36
|
+
context 'without results' do
|
37
|
+
let(:result) { nil }
|
38
|
+
it { should == [] }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with one result' do
|
42
|
+
let(:result) {{ :field => :value }}
|
43
|
+
it { should == [{ :field => :value }] }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with multiple results' do
|
47
|
+
let(:result) { [:object1, :object2] }
|
48
|
+
it { should == [:object1, :object2] }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|