clever_elements 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011, Marian Theisen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ clever\_elements
2
+ ================
3
+
4
+ [![Build Status](https://secure.travis-ci.org/cice/clever_elements.png)](http://travis-ci.org/cice/clever_elements)
5
+
6
+
7
+ **Under Development, any help appreciated!**
8
+
9
+ **clever\_elements** is a RubyGem to use the **CleverElements SOAP Api** with Ruby / Rails.
10
+
11
+ Rails 3 Quickstart
12
+ ------------------
13
+
14
+ The easiest way to use **clever\_elements** is with Rails 3. Just extend your Gemfile
15
+
16
+ gem 'clever_elements', '~> 0.0.1'
17
+
18
+ put a `clever_elements.yml` file in your `#{Rails.root}/config`:
19
+
20
+ user_id: <your user id>
21
+ api_key: <your api key>
22
+ mode: <test or live>
23
+
24
+ And use the `CleverElements::List` and `CleverElements::Subscriber` models, e.g.
25
+
26
+ # Get the ids of all lists in your account:
27
+ CleverElements::List.ids
28
+ # => ['73302']
29
+
30
+ # Get the list for a specific id:
31
+ CleverElements::List.find '73302'
32
+ # => <CleverElements::List:0x007fb78a8ab8f0 @id="73302", @name="ABC", @description="foobar", @subscriber="0", @unsubscriber="0">
33
+
34
+ # Get all lists in your account:
35
+ CleverElements::List.all
36
+ # => [<CleverElements::List:0x007fb78a8ab8f0 @id="73302", @name="ABC", @description="foobar", @subscriber="0", @unsubscriber="0">]
37
+
38
+ # Create a new list:
39
+ list = CleverElements::List.new :name => 'baz'
40
+ list.id
41
+ # => nil
42
+ list.create
43
+ # => true
44
+ list.id
45
+ # => 73303
46
+
47
+ # Remove a list:
48
+ list = CleverElements::List.find('73302')
49
+ list.id
50
+ # => 73302
51
+ list.destroy
52
+ # => true
53
+ list.id
54
+ # => nil
55
+
56
+ # Get the subscribers in a list
57
+ list = CleverElements::List.find('73302')
58
+ list.subscriber
59
+ # => [<CleverElements::Subscriber:0x007fb78a8ab8f0 @id='123123', @email='max@muster.de'>]
60
+
61
+ # or
62
+ CleverElements::Subscriber.all '73302'
63
+ # => [<CleverElements::Subscriber:0x007fb78a8ab8f0 @id='123123', @email='max@muster.de'>]
64
+
65
+ # Add a subscriber
66
+ subscriber = list.create_subscriber :email => 'max@muster.de'
67
+ # => <CleverElements::Subscriber:0x007fb78a8ab8f0 @id='123143', @email='max@muster.de'>
68
+
69
+ # Unsubscribe
70
+ subscriber.unsubscribe_from_all
71
+
72
+ ### What doesn't work / hasn't been implemented yet?
73
+ * custom fields
74
+ * unsubscribe\_from\_list (implemented but API request doesn't work)
75
+
76
+ ### What could be improved?
77
+ * extensions/wasabi\_parser.rb
78
+ * comprehensive exception handling
79
+ * environment dependent configuration (development, test, production)
80
+ * test / spec helpers
@@ -0,0 +1,34 @@
1
+ module CleverElements
2
+ class Client
3
+ API_VERSION = "1.0"
4
+
5
+ attr_reader :savon, :user_id, :api_key, :mode
6
+
7
+ def initialize user_id, api_key, mode = 'test'
8
+ @savon = Savon::Client.new "http://api.sendcockpit.com/server.php?wsdl"
9
+ @user_id, @api_key, @mode = user_id, api_key, mode
10
+ end
11
+
12
+ def request method, arguments = {}
13
+ savon.request method do
14
+ soap.body = arguments
15
+ soap.header = authentication_header
16
+ end
17
+ end
18
+
19
+ def authentication_header
20
+ {
21
+ :validate => {
22
+ :userid => user_id,
23
+ :apikey => api_key,
24
+ :version => API_VERSION,
25
+ :mode => mode
26
+ }
27
+ }
28
+ end
29
+
30
+ def proxy
31
+ @proxy ||= Proxy.new self
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,138 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'clever_elements/model'
3
+
4
+ module CleverElements
5
+ class List < Model
6
+ class << self
7
+ def find id
8
+ response = proxy.get_list_details :listID => id
9
+ return nil if response[:list_id].nil?
10
+
11
+ description = response[:description]
12
+ description = String === description ? description : ''
13
+ attributes = {
14
+ :id => response[:list_id],
15
+ :name => response[:list_name],
16
+ :description => description,
17
+ :subscriber_count => response[:list_subscriber],
18
+ :unsubscriber_count => response[:list_unsubscriber]
19
+ }
20
+
21
+ new attributes
22
+ end
23
+
24
+ def ids
25
+ response = proxy.get_list
26
+
27
+ ids = if Array === response[:item]
28
+ response[:item].map do |id_response|
29
+ id_response[:list_id]
30
+ end
31
+ elsif Hash === response[:item]
32
+ [response[:item][:list_id]]
33
+ else
34
+ []
35
+ end
36
+ rescue Savon::SOAP::Fault
37
+ []
38
+ end
39
+
40
+ def all
41
+ ids.map do |id|
42
+ find(id)
43
+ end
44
+ end
45
+
46
+ def first
47
+ first_id = ids.first
48
+
49
+ first_id && find(first_id)
50
+ end
51
+ end
52
+
53
+ Attributes = [:id, :name, :description, :subscriber_count, :unsubscriber_count]
54
+ attr_accessor *Attributes
55
+
56
+ def initialize attributes = {}
57
+ attributes = attributes.symbolize_keys
58
+
59
+ Attributes.each do |attribute|
60
+ instance_variable_set "@#{attribute}", attributes[attribute]
61
+ end
62
+ end
63
+
64
+ def create
65
+ return true if id
66
+
67
+ response = proxy.add_list list_attributes
68
+
69
+ if response == '200'
70
+ @id = self.class.ids.last
71
+ true
72
+ end
73
+ rescue Savon::SOAP::Fault
74
+ false
75
+ end
76
+
77
+ def destroy
78
+ response = proxy.delete_list :listID => id
79
+
80
+ if response == '200'
81
+ @id = nil
82
+ true
83
+ else
84
+ false
85
+ end
86
+ rescue Savon::SOAP::Fault
87
+ false
88
+ end
89
+
90
+ def subscriber
91
+ return [] unless id
92
+
93
+ @subscriber ||= CleverElements::Subscriber.all(id).tap do |s|
94
+ s.each do |subscriber|
95
+ assign_list_to_subscriber subscriber
96
+ end
97
+ end
98
+ end
99
+
100
+ def build_subscriber attributes = {}
101
+ CleverElements::Subscriber.new(attributes.merge(:list_id => id)).tap do |subscriber|
102
+ assign_list_to_subscriber subscriber
103
+ end
104
+ end
105
+
106
+ def create_subscriber attributes = {}
107
+ subscriber = build_subscriber attributes
108
+
109
+ if subscriber.create
110
+ subscriber
111
+ else
112
+ false
113
+ end
114
+ end
115
+
116
+ def create_subscriber_doi attributes = {}
117
+ subscriber = build_subscriber attributes
118
+
119
+ if subscriber.create_doi
120
+ subscriber
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ protected
127
+ def assign_list_to_subscriber subscriber
128
+ subscriber.instance_variable_set "@list", self
129
+ end
130
+
131
+ def list_attributes
132
+ {
133
+ :list_name => name,
134
+ :list_description => description
135
+ }
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
3
+ module CleverElements
4
+ class Model
5
+ class_attribute :proxy
6
+
7
+
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ require 'active_support/basic_object'
2
+
3
+ module CleverElements
4
+ class Proxy
5
+ attr_reader :client, :wsdl, :proxy_methods, :response_methods
6
+
7
+ def initialize client
8
+ @client = client
9
+ @wsdl = client.savon.wsdl
10
+ @proxy_methods = Module.new
11
+ @response_methods = Module.new
12
+
13
+ build_proxy
14
+ end
15
+
16
+ protected
17
+ def build_proxy
18
+ wsdl.operations.each_pair do |name, vals|
19
+ build_operation_proxy_for name
20
+ end
21
+
22
+ extend proxy_methods
23
+ extend response_methods
24
+ end
25
+
26
+ # Builds a response processor for a SOAP action:
27
+ # The response processor uses message and type information of the
28
+ # wsdl to "un-nest" the soap response as far as possible,
29
+ # e.g. if the soap response is
30
+ # :a => {
31
+ # :b => {
32
+ # :c => {
33
+ # :d => 1,
34
+ # :e => 2
35
+ # }
36
+ # }
37
+ # }
38
+ # the processor will return
39
+ # { :d => 1, :e => 2}
40
+ # (first non-trivial nesting level)
41
+ def build_response_processor_for name
42
+ method_name = "process_response_for_#{name}"
43
+ message_name, message = wsdl.parser.port_type[name][:output].first
44
+ key, type = message.first
45
+ types = wsdl.parser.types
46
+ keys = [message_name.snakecase.to_sym, key.snakecase.to_sym]
47
+ while type
48
+ type = type.split(':').last
49
+ typedef = types[type]
50
+
51
+ if typedef
52
+ _keys = (typedef.keys - [:namespace, :type])
53
+ break unless _keys.length == 1
54
+ key = _keys.first
55
+
56
+ type = typedef[key][:type]
57
+ keys << key.snakecase.to_sym
58
+ else
59
+ break
60
+ end
61
+ end
62
+
63
+ response_methods.send :define_method, method_name do |body|
64
+ keys.inject body do |b, key|
65
+ b[key] if b
66
+ end
67
+ end
68
+
69
+ method_name
70
+ end
71
+
72
+ def build_operation_proxy_for name
73
+ method_name = name.to_s.sub /^api_/, ''
74
+ input_message = wsdl.parser.port_type[name][:input].first
75
+
76
+ response_processor_name = build_response_processor_for name
77
+
78
+ proxy_methods.send :define_method, method_name do |*args|
79
+ arguments = args.first
80
+
81
+ if input_message
82
+ arguments = { input_message.to_sym => arguments }
83
+ end
84
+
85
+ response = client.request name, arguments
86
+ send response_processor_name, response.body
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ module CleverElements
2
+ class Railtie < Rails::Railtie
3
+ initializer 'clever_elements.initialize_client' do |app|
4
+ config_file = Rails.root.join 'config', 'clever_elements.yml'
5
+
6
+ if File.exists?(config_file)
7
+ configuration = YAML.load_file config_file
8
+
9
+ client = CleverElements::Client.new *configuration.values_at('user_id', 'api_key', 'mode')
10
+ CleverElements::Model.proxy = client.proxy
11
+ else
12
+ Rails.logger.warn 'Could not find a configuration file for CleverElements.'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,147 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'clever_elements/model'
3
+
4
+ module CleverElements
5
+ class Subscriber < Model
6
+ class << self
7
+ def all list_id
8
+ response = proxy.get_subscriber :listID => list_id
9
+
10
+ ids = if Array === response[:item]
11
+ response[:item].map do |subscriber_response|
12
+ instantiate subscriber_response, list_id
13
+ end
14
+ elsif Hash === response[:item]
15
+ [instantiate(response[:item], list_id)]
16
+ else
17
+ []
18
+ end
19
+ rescue Savon::SOAP::Fault
20
+ []
21
+ end
22
+
23
+ def all_unsubscribed list_id
24
+ response = proxy.get_subscriber_unsubscribes :listID => list_id
25
+
26
+ ids = if Array === response[:item]
27
+ response[:item].map do |subscriber_response|
28
+ instantiate subscriber_response, list_id
29
+ end
30
+ elsif Hash === response[:item]
31
+ [instantiate(response[:item], list_id)]
32
+ else
33
+ []
34
+ end
35
+ rescue Savon::SOAP::Fault
36
+ []
37
+ end
38
+
39
+ protected
40
+ def instantiate attributes, list_id
41
+ attributes = {
42
+ :id => attributes[:subscriber_id],
43
+ :email => attributes[:subscriber_email],
44
+ :list_id => list_id
45
+ }
46
+
47
+ new attributes
48
+ end
49
+ end
50
+
51
+ attr_accessor :id, :email, :list_id
52
+
53
+ def initialize attributes = {}
54
+ @id, @email, @list_id = attributes.symbolize_keys.values_at(:id, :email, :list_id)
55
+ end
56
+
57
+ def list
58
+ @list ||= list_id && CleverElements::List.find(list_id)
59
+ end
60
+
61
+ def create
62
+ create_with :add_subscriber
63
+ end
64
+
65
+ def create_doi
66
+ create_with :add_subscriber_doi
67
+ end
68
+
69
+ def destroy
70
+ response = proxy.delete_subscriber :subscriberIDListShort => { :item => { :subscriberID => id }}
71
+
72
+ if response == '200'
73
+ true
74
+ else
75
+ false
76
+ end
77
+ rescue Savon::SOAP::Fault
78
+ false
79
+ end
80
+
81
+ def unsubscribe
82
+ return false unless list_id
83
+
84
+ unsubscribe_from list_id
85
+ end
86
+
87
+ def unsubscribe_from list_or_id
88
+ raise 'FIXME'
89
+ list_id = if CleverElements::List === list_or_id
90
+ list_or_id.id
91
+ else
92
+ list_or_id
93
+ end
94
+
95
+ response = proxy.unsubscribe_subscriber_from_list :subscriberIDList => { :item => { :subscriberID => id, :listID => list_id}}
96
+
97
+ if response == '200'
98
+ @list = @list_id = nil if @list_id == list_id
99
+ true
100
+ else
101
+ false
102
+ end
103
+ rescue Savon::SOAP::Fault
104
+ false
105
+ end
106
+
107
+ def unsubscribe_from_all
108
+ response = proxy.unsubscribe_subscriber_from_all :subscriberIDList => { :item => { :subscriberID => id }}
109
+
110
+ if response == '200'
111
+ true
112
+ else
113
+ false
114
+ end
115
+ rescue Savon::SOAP::Fault
116
+ false
117
+ end
118
+
119
+ protected
120
+ def create_with method
121
+ return true if id
122
+
123
+ attributes = subscriber_attributes
124
+ return false unless attributes[:listID] && attributes[:email]
125
+
126
+ response = proxy.send method, :subscriber_list => {
127
+ :item => attributes
128
+ }
129
+
130
+ # FIXME: it should return 200, but i get a Hash, have to interpret that as success...
131
+ if response.is_a?(Hash)
132
+ @id = self.class.all(list_id).last.id
133
+ true
134
+ end
135
+ rescue Savon::SOAP::Fault
136
+ false
137
+ end
138
+
139
+ def subscriber_attributes
140
+ {
141
+ :listID => list_id,
142
+ :email => email,
143
+ :customFields => { :item => [] }
144
+ }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,14 @@
1
+ require 'extensions/wasabi_parser'
2
+ require 'savon'
3
+
4
+ if defined?(Rails)
5
+ require 'clever_elements/railtie'
6
+ end
7
+
8
+ require 'clever_elements/client'
9
+ require 'clever_elements/proxy'
10
+ require 'clever_elements/list'
11
+ require 'clever_elements/subscriber'
12
+
13
+ module CleverElements
14
+ end
@@ -0,0 +1,73 @@
1
+ require 'wasabi'
2
+ require 'active_support/concern'
3
+
4
+ # This is a rather bad monkeypatch extending Wasabi to read message definitions from the wsdl file.
5
+ Wasabi::Parser.class_eval do
6
+ attr_reader :port_type
7
+
8
+ def parse_port_type
9
+ operations = @document.xpath "s0:definitions/s0:portType/s0:operation", wsdl_ns
10
+
11
+ @port_type = {}
12
+ operations.each do |operation|
13
+ name = operation.attribute('name').to_s
14
+ key = name.snakecase.to_sym
15
+
16
+ hash = @port_type[key] = {}
17
+
18
+ if input = operation.at_xpath('s0:input', wsdl_ns)
19
+ message_name = input.attribute('message').to_s.split(':').last
20
+ input_message_parts = @document.xpath %Q{s0:definitions/s0:message[@name="#{message_name}"]/s0:part/@name}, wsdl_ns
21
+
22
+ hash[:input] = input_message_parts.map &:to_s
23
+ end
24
+
25
+ if output = operation.at_xpath('s0:output', wsdl_ns)
26
+ message_name = output.attribute('message').to_s.split(':').last
27
+ output_message_parts = @document.xpath %Q{s0:definitions/s0:message[@name="#{message_name}"]/s0:part}, wsdl_ns
28
+
29
+ message = {}.tap do |output|
30
+ output_message_parts.each do |part|
31
+ name = part.attribute('name').to_s
32
+ type = part.attribute('type').to_s
33
+
34
+ output[name] = type
35
+ end
36
+ end
37
+
38
+ hash[:output] = { message_name => message }
39
+ end
40
+ end
41
+ rescue
42
+ # Don't break Wasabi with bad monkeypatching
43
+ end
44
+
45
+ alias_method :process_type_without_all, :process_type
46
+ def process_type(type, name)
47
+ process_type_without_all type, name
48
+ return unless type
49
+
50
+ @types[name] ||= { :namespace => find_namespace(type) }
51
+
52
+ type.xpath("./xs:all/xs:element",
53
+ "xs" => "http://www.w3.org/2001/XMLSchema"
54
+ ).each do |inner_element|
55
+ @types[name][inner_element.attribute('name').to_s] = {
56
+ :type => inner_element.attribute('type').to_s
57
+ }
58
+ end
59
+ rescue
60
+ # Don't break Wasabi with bad monkeypatching
61
+ end
62
+
63
+ alias_method :parse_without_port_type, :parse
64
+ def parse
65
+ parse_without_port_type
66
+ parse_port_type
67
+ end
68
+
69
+ protected
70
+ def wsdl_ns
71
+ {"s0" => "http://schemas.xmlsoap.org/wsdl/", "xs" => "http://www.w3.org/2001/XMLSchema"}
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clever_elements
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Marian Theisen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-24 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ requirement: &70286679111680 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70286679111680
26
+ - !ruby/object:Gem::Dependency
27
+ name: savon
28
+ requirement: &70286679111220 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70286679111220
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ requirement: &70286679110840 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70286679110840
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec-mocks
50
+ requirement: &70286679110380 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70286679110380
59
+ - !ruby/object:Gem::Dependency
60
+ name: savon_spec
61
+ requirement: &70286679109960 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *70286679109960
70
+ description: Clever Elements API Client
71
+ email: marian.theisen@kayoom.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - LICENSE
78
+ - lib/clever_elements/client.rb
79
+ - lib/clever_elements/list.rb
80
+ - lib/clever_elements/model.rb
81
+ - lib/clever_elements/proxy.rb
82
+ - lib/clever_elements/railtie.rb
83
+ - lib/clever_elements/subscriber.rb
84
+ - lib/clever_elements.rb
85
+ - lib/extensions/wasabi_parser.rb
86
+ has_rdoc: true
87
+ homepage: http://github.com/kayoom/clever_elements
88
+ licenses: []
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 1.8.7
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements:
106
+ - none
107
+ rubyforge_project:
108
+ rubygems_version: 1.6.2
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Clever Elements API Client
112
+ test_files: []