clever_elements 0.0.1

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/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: []