Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
supports creating subscribers and triggered sends
  • Loading branch information
David Dawson committed Feb 8, 2012
0 parents commit 871e15d
Show file tree
Hide file tree
Showing 32 changed files with 1,145 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/gems
/gems_dev
/.rbx
.*.swp
6 changes: 6 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
= ExactTarget SDK

== Version 0.0.0
* Initial release
* Supports Create method
* Supports Subscriber and TriggeredSend objects, allowing subscribers to be created and triggered emails to be sent to them
14 changes: 14 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
source 'http://rubygems.org'

gem 'activemodel', '~> 3.1'
gem 'guid', '~> 0.1'
gem 'savon', '~> 0.9'

group :rake do
gem 'simple_gem', :require => 'tasks/simple_gem'
end

group :test do
gem 'rspec', '~> 2.8'
end

56 changes: 56 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
GEM
remote: http://rubygems.org/
specs:
activemodel (3.2.1)
activesupport (= 3.2.1)
builder (~> 3.0.0)
activesupport (3.2.1)
i18n (~> 0.6)
multi_json (~> 1.0)
akami (1.0.0)
gyoku (>= 0.4.0)
builder (3.0.0)
diff-lcs (1.1.3)
guid (0.1.1)
gyoku (0.4.4)
builder (>= 2.1.2)
httpi (0.9.5)
rack
i18n (0.6.0)
multi_json (1.0.4)
nokogiri (1.5.0)
nori (1.0.2)
rack (1.4.1)
rake (0.9.2.2)
rspec (2.8.0)
rspec-core (~> 2.8.0)
rspec-expectations (~> 2.8.0)
rspec-mocks (~> 2.8.0)
rspec-core (2.8.0)
rspec-expectations (2.8.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.8.0)
savon (0.9.7)
akami (~> 1.0)
builder (>= 2.1.2)
gyoku (>= 0.4.0)
httpi (~> 0.9)
nokogiri (>= 1.4.0)
nori (~> 1.0)
wasabi (~> 2.0)
simple_gem (0.0.2)
activesupport (~> 3.0)
rake (>= 0.8.7)
rspec (~> 2.8)
wasabi (2.0.0)
nokogiri (>= 1.4.0)

PLATFORMS
ruby

DEPENDENCIES
activemodel (~> 3.1)
guid (~> 0.1)
rspec (~> 2.8)
savon (~> 0.9)
simple_gem
7 changes: 7 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright (c) 2012 RevPAR Collective, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

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.
36 changes: 36 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
= ExactTarget SDK

An object-oriented wrapper for the ExactTarget SOAP API.

The ExactTarget web service guide can be viewed here:
http://docs.code.exacttarget.com/020_Web_Service_Guide

With few exceptions, ruby conventions for capitalization are ignored and those
outlined in the guide linked above are used. This is done in an attempt to be
as transparent as possible, so that the API may be used by referring only to
the guide linked above.

Note this SDK is currently very incomplete, allowing you only to create
subscribers and trigger sends. The framework is in place, however, to very
easily implement new objects by simply declaring their properties.

== Synopsis:

ExactTargetSDK.config(:username => 'foo', :password => 'mypass')

client = ExactTargetSDK::Client.new
definition = TriggeredSendDefinition.new('CustomerKey' => 'my_triggered_send')
subscriber = Subscriber.new('EmailAddress' => 'me@example.com')
triggered_send = TriggeredSend.new('TriggeredSendDefinition' => definition)
triggered_send.subscribers << subscriber

# Creates subscriber record, then executes the "my_triggered_send" trigger to
# that subscriber.
response = client.Create(subscriber, triggered_send)

puts "response status: #{response.OverallStatus}"
response.Results.each do |result|
puts "result..."
puts " result code: #{result.StatusCode}"
puts " result message: #{result.StatusMessage}"
end
8 changes: 8 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'bundler'
Bundler.require(:rake)

# Configure gem building
require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'exact_target_sdk', 'version'))
SimpleGem.current_version = ExactTargetSDK::VERSION
SimpleGem.current_gemspec = 'exact_target_sdk.gemspec'

31 changes: 31 additions & 0 deletions exact_target_sdk.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'exact_target_sdk', 'version'))

Gem::Specification.new do |s|

# definition
s.name = %q{exact_target_sdk}
s.version = ExactTargetSDK::VERSION

# details
s.date = %q{2012-01-30}
s.summary = %q{A simple wrapper for the ExactTarget SOAP API.}
s.description = %q{Provides an easy-to-use ruby interface into the ExactTarget SOAP API, using the Savon client.}
s.authors = [ 'David Dawson' ]
s.email = %q{daws23@gmail.com}
s.homepage = %q{https://github.com/daws/exact_target_sdk}
s.require_paths = [ 'lib' ]

# documentation
s.has_rdoc = true
s.extra_rdoc_files = %w( README.rdoc CHANGELOG.rdoc LICENSE.txt )
s.rdoc_options = %w( --main README.rdoc )

# files to include
s.files = Dir[ 'lib/**/*.rb', 'README.rdoc', 'CHANGELOG.rdoc', 'LICENSE.txt' ]

# dependencies
s.add_dependency 'activemodel', '~> 3.1'
s.add_dependency 'guid', '~> 0.1'
s.add_dependency 'savon', '~> 0.9'

end
14 changes: 14 additions & 0 deletions lib/exact_target_sdk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'exact_target_sdk/config'
require 'exact_target_sdk/errors'

module ExactTargetSDK

autoload :APIObject, 'exact_target_sdk/api_object'
autoload :Client, 'exact_target_sdk/client'
autoload :CreateResponse, 'exact_target_sdk/create_response'
autoload :CreateResult, 'exact_target_sdk/create_result'
autoload :Subscriber, 'exact_target_sdk/subscriber'
autoload :TriggeredSend, 'exact_target_sdk/triggered_send'
autoload :TriggeredSendDefinition, 'exact_target_sdk/triggered_send_definition'

end
147 changes: 147 additions & 0 deletions lib/exact_target_sdk/api_object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
require 'active_model'

module ExactTargetSDK

# Parent class of all ExactTarget API objects (listed here:
# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects). Provides
# class-level declarations, validation, and rendering that makes modeling
# each object easy.
class APIObject

include ::ActiveModel::Validations

class << self

# Declares a property of this object, optionally requiring it upon
# validation.
#
# Provides a getter and setter for this property, keeping track of
# whether or not it has been set and registering it for rendering.
def property(name, required = false)
name = name.to_s
attr_reader name.to_sym
class_eval <<-__EOF__
def #{name}=(value)
@_set_#{name} = true
@#{name} = value
end
__EOF__
if required
validates name.to_sym, :presence => true
end
register_property!(name)
end

# Declares a property as an array of values.
#
# Provides a getter and setter for this property. The getter will
# always return an array (not null), so the client may simply append
# to this property.
#
# Note that once the property has been either read or written to, it
# will be rendered.
def array_property(name)
# TODO: type validation would be nice
name = name.to_s
class_eval <<-__EOF__
def #{name}
@_set_#{name} = true
@#{name} ||= []
end
def #{name}=(value)
@_set_#{name} = true
@#{name} = value
end
__EOF__
register_property!(name)
end

# Same as #property, adding validation the the provided value is an
# integer.
def int_property(name, required = false)
property(name, required)
validates name.to_sym, :numericality => { :allow_nil => true, :only_integer => true }
end

# Takes one or more method names as symbols, and executes them in order
# before validation occurs on this object.
def before_validation(*args)
before_validation_methods.concat(args)
end

# Returns an array of all registered properties.
def properties
@properties || []
end

private

# Returns the method names declared using #before_validation.
def before_validation_methods
@before_validation_methods ||= []
end

# Stores the given property name to be used at render time.
def register_property!(name)
@properties ||= []
@properties << name
@properties.uniq!
end

end

# By default, any properties may be passed and set.
#
# May be overridden.
def initialize(properties = {})
properties.each do |key, value|
self.send "#{key}=", value
end
end

# By default, returns the name of the class.
#
# May be overridden.
def type_name
self.class.name.split('::').last
end

# By default, runs validation and executes #render_properties!.
#
# If overridden, the child class should execute the before_validation
# methods, check wehter or not the object is valid, and then render
# the object.
def render!(xml)
self.class.before_validation_methods.each { |method| self.send(method) }
raise(InvalidAPIObject, self) if invalid?
render_properties!(xml)
end

# By default, loops through all registered properties, and renders
# each that has been explicitly set.
#
# May be overridden.
def render_properties!(xml)
self.class.properties.each do |property|
next unless instance_variable_get("@_set_#{property}")

property_value = self.send(property)

if property_value.is_a?(APIObject)
xml.__send__(property) do
property_value.render!(xml)
end
elsif property_value.is_a?(Array)
property_value.each do |current|
xml.__send__(property) do
current.render!(xml)
end
end
else
xml.__send__(property, property_value)
end
end
end

end
end
Loading

0 comments on commit 871e15d

Please sign in to comment.