cupid 0.2.4 → 0.3.2
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 +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
|