your_membership 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.env +5 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +1 -0
  5. data/CHANGELOG.md +10 -0
  6. data/README.md +12 -10
  7. data/lib/httparty/patch.rb +33 -33
  8. data/lib/your_membership/base.rb +197 -197
  9. data/lib/your_membership/commerce.rb +25 -25
  10. data/lib/your_membership/config.rb +41 -41
  11. data/lib/your_membership/convert.rb +24 -24
  12. data/lib/your_membership/error.rb +21 -21
  13. data/lib/your_membership/events.rb +60 -60
  14. data/lib/your_membership/feeds.rb +37 -37
  15. data/lib/your_membership/member.rb +399 -397
  16. data/lib/your_membership/members.rb +124 -124
  17. data/lib/your_membership/people.rb +38 -38
  18. data/lib/your_membership/profile.rb +92 -85
  19. data/lib/your_membership/sa.rb +6 -6
  20. data/lib/your_membership/sa_auth.rb +34 -34
  21. data/lib/your_membership/sa_certifications.rb +22 -22
  22. data/lib/your_membership/sa_commerce.rb +22 -22
  23. data/lib/your_membership/sa_events.rb +66 -66
  24. data/lib/your_membership/sa_export.rb +195 -195
  25. data/lib/your_membership/sa_groups.rb +30 -30
  26. data/lib/your_membership/sa_member.rb +49 -49
  27. data/lib/your_membership/sa_members.rb +180 -179
  28. data/lib/your_membership/sa_nonmembers.rb +41 -41
  29. data/lib/your_membership/sa_people.rb +92 -92
  30. data/lib/your_membership/session.rb +148 -152
  31. data/lib/your_membership/version.rb +1 -1
  32. data/spec/fixtures/vcr_cassettes/sa_members_all_getids_timestamp_multiple.yml +51 -0
  33. data/spec/fixtures/vcr_cassettes/sa_members_all_getids_timestamp_none.yml +51 -0
  34. data/spec/fixtures/vcr_cassettes/sa_members_all_getids_timestamp_single.yml +51 -0
  35. data/spec/lib/{profile_spec.rb → your_membership/profile_spec.rb} +232 -197
  36. data/spec/lib/your_membership/sa_members_spec.rb +38 -0
  37. data/spec/spec_helper.rb +101 -78
  38. data/your_membership.gemspec +4 -0
  39. metadata +85 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c93432ed686e1f80df9f1d4fa5d33eb38785b02b
4
- data.tar.gz: dffe41b0e69376c7669a30b184a76c405e3b2751
3
+ metadata.gz: a2114fa42d26afb1c73747800e9aba7e90513d97
4
+ data.tar.gz: bf1322fe1e0c1e8493b90d082062ae4e0d8af826
5
5
  SHA512:
6
- metadata.gz: d7dae1c53e40a3d2c24d7dc8715a07c7fa41c917b316ed4b33afe3cb13239ea647e977b3bbead6bc42d1c48850d60fb6afec4620f14c552148db055c44fb2eb7
7
- data.tar.gz: 1668ab62de7584be9e23d67016560b36c8bda85e9fcd36a9b40f40f421d5e26bf529991eb8cae686418ed55057a6645ffe49a4b75607b13ffeab9cb8d8b31a60
6
+ metadata.gz: 58f41d9ba44581eb27c2e97d0927766fd21c6ca6772a098a44962b9f084c71b5f5c85c3cdb73afa32a6f7d9b71af7c4de88ae358c183754b30d0c5c5a363517c
7
+ data.tar.gz: 524ff0de4cc6d178a4e2823ed316e8a928768fcfcda3f4134d60ae9e9a0a8b93236626d6d703e5c317e46347a950e60cb43304a43ee63d00cacfeef751a57eea
data/.env ADDED
@@ -0,0 +1,5 @@
1
+ # Copy this file to .env.local and add your keys if you need to test against
2
+ # the real API.
3
+ YM_API_PUBLIC_KEY: redacted
4
+ YM_API_PRIVATE_KEY: redacted
5
+ YM_API_SA_PASSCODE: redacted
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .env.local
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.1.1] - 2016-03-23
8
+ ### Fixed
9
+ - Crash in `Profile#parse_custom_field_responses` when there are no
10
+ CustomFieldResponses present.
data/README.md CHANGED
@@ -23,7 +23,7 @@ Or install it yourself as:
23
23
 
24
24
  ## Usage
25
25
 
26
- Every effort has been made to expose the YourMembership.com API as transparently as possible while still providing a natively Ruby interface.
26
+ Every effort has been made to expose the YourMembership.com API as transparently as possible while still providing a natively Ruby interface.
27
27
 
28
28
  ### Scope and Naming
29
29
 
@@ -94,7 +94,7 @@ You can pass strings as keys if you do not prefer to use Symbols.
94
94
 
95
95
  ##### SDK-Specific Implementation of Argument Types
96
96
 
97
- You should use the standard Ruby types to represent data, for instance:
97
+ You should use the standard Ruby types to represent data, for instance:
98
98
 
99
99
  `:Timestamp` and other date or time arguments should be passed as DateTime objects rather than formatted strings, the formatting will be done for you.
100
100
 
@@ -104,7 +104,7 @@ You should use the standard Ruby types to represent data, for instance:
104
104
 
105
105
  #### Returned Values
106
106
 
107
- The SDK translates the returned data into Ruby Objects of only a few types. Nearly all methods return an Array, this may be an Array of Strings or, more likely, it will be an Array of Hashes.
107
+ The SDK translates the returned data into Ruby Objects of only a few types. Nearly all methods return an Array, this may be an Array of Strings or, more likely, it will be an Array of Hashes.
108
108
 
109
109
  If only a single record can ever be retrieved at a time through a method a Hash object will be returned instead of an Array.
110
110
 
@@ -162,7 +162,7 @@ The Member object builds upon the Session object to more fully represent an auth
162
162
  ##### Instantiation:
163
163
  ```RUBY
164
164
  # Members can be created by authenticating directly with the YourMembership API,
165
- # the session is automatically created and bound to the Member instance.
165
+ # the session is automatically created and bound to the Member instance.
166
166
  member = YourMembership::Member.create_by_authentication 'username', 'password'
167
167
 
168
168
  # Members can also be created by passing in an already existing Session instance
@@ -213,7 +213,7 @@ An *outdated* list of standard fields can be found here: https://api.yourmembers
213
213
 
214
214
  ##### Data Access
215
215
  + **Profile#data** `Hash` This internal hash can be read and written to and should contain only fields that are standard in the YourMembership system.
216
- + **Profile#custom_data** `Hash` This internal hash can be read and written to and should contain the fields that are specified as custom responses in a specific YourMembership community.
216
+ + **Profile#custom_data** `Hash` This internal hash can be read and written to and should contain the fields that are specified as custom responses in a specific YourMembership community.
217
217
  + **Profile#to_h** `Hash` is a read-only method for returning a single nested hash of both standard and custom fields.
218
218
 
219
219
  #### YourMembership::Export
@@ -238,7 +238,7 @@ when :complete
238
238
  # Export is ready
239
239
  donations.export_uri
240
240
  end
241
- ```
241
+ ```
242
242
 
243
243
  ## About The Author(s)
244
244
 
@@ -259,7 +259,9 @@ If you find a problem with this library or would like to contribute an improveme
259
259
  If you're a developer that would like to help solve world hunger problems with some of your spare time, we are always looking for talented volunteers. Contact Nate Flood by email: nate [at] echonet [dot] org
260
260
 
261
261
  1. Fork it ( https://github.com/ECHOInternational/your_membership/fork )
262
- 2. Create your feature branch (`git checkout -b my-new-feature`)
263
- 3. Commit your changes (`git commit -am 'Add some feature'`)
264
- 4. Push to the branch (`git push origin my-new-feature`)
265
- 5. Create a new Pull Request
262
+ 2. If you need to make real API calls while testing, see the .env file
263
+ 3. Create your feature branch (`git checkout -b my-new-feature`)
264
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
265
+ 5. Ensure the specs pass (`bundle exec rspec`)
266
+ 6. Push to the branch (`git push origin my-new-feature`)
267
+ 7. Create a new Pull Request
@@ -1,33 +1,33 @@
1
- require 'cgi'
2
-
3
- # Monkey Patch for the HTTParty Gem
4
- module HTTParty
5
- # In order to parse the HTML encoded documents returned by the YourMembership
6
- # API we need to HTML decode the <![CDATA[ tags. This is a bug workaround.
7
- class Request
8
- # This is the encode_body method from HTTParty's Request Class adding an additional method call to fix
9
- # the CDATA elements that are improperly formatted in the YourMembership API's XML. This is done here to ensure that
10
- # the fix is in place before the data is parsed.
11
- def encode_body(body)
12
- body = fix_cdata body
13
- if ''.respond_to?(:encoding)
14
- _encode_body(body)
15
- else
16
- body
17
- end
18
- end
19
-
20
- # Bug Fix for HTML encoded < and > in XML body.
21
- # @param body [String] an XML document that needs to be checked for this specific issue.
22
- # @return [String] If the HTML encoding issue is found it is repaired and the document is returned.
23
- def fix_cdata(body)
24
- # <![CDATA[ = &lt;![CDATA[
25
- # ]]> = ]]&gt;
26
- if body.include? '&lt;![CDATA['
27
- body.gsub! '&lt;![CDATA[', '<![CDATA['
28
- body.gsub! ']]&gt', ']]>'
29
- end
30
- body
31
- end
32
- end
33
- end
1
+ require 'cgi'
2
+
3
+ # Monkey Patch for the HTTParty Gem
4
+ module HTTParty
5
+ # In order to parse the HTML encoded documents returned by the YourMembership
6
+ # API we need to HTML decode the <![CDATA[ tags. This is a bug workaround.
7
+ class Request
8
+ # This is the encode_body method from HTTParty's Request Class adding an additional method call to fix
9
+ # the CDATA elements that are improperly formatted in the YourMembership API's XML. This is done here to ensure that
10
+ # the fix is in place before the data is parsed.
11
+ def encode_body(body)
12
+ body = fix_cdata body
13
+ if ''.respond_to?(:encoding)
14
+ _encode_body(body)
15
+ else
16
+ body
17
+ end
18
+ end
19
+
20
+ # Bug Fix for HTML encoded < and > in XML body.
21
+ # @param body [String] an XML document that needs to be checked for this specific issue.
22
+ # @return [String] If the HTML encoding issue is found it is repaired and the document is returned.
23
+ def fix_cdata(body)
24
+ # <![CDATA[ = &lt;![CDATA[
25
+ # ]]> = ]]&gt;
26
+ if body.include? '&lt;![CDATA['
27
+ body.gsub! '&lt;![CDATA[', '<![CDATA['
28
+ body.gsub! ']]&gt', ']]>'
29
+ end
30
+ body
31
+ end
32
+ end
33
+ end
@@ -1,197 +1,197 @@
1
- module YourMembership
2
- # Base Class inherited by all Your Membership SDK Classes.
3
- class Base
4
- include HTTParty
5
-
6
- base_uri YourMembership.config[:baseUri]
7
-
8
- # rubocop:disable Style/ClassVars
9
-
10
- # Call IDs are usually tied to sessions, this is a unique call id for use whenever a session is not needed.
11
- @@genericCallID = nil
12
-
13
- # @return [Integer] Auto Increments ad returns the genericCallID as required by the YourMembership.com API
14
- def self.new_call_id
15
- if @@genericCallID.nil?
16
- # We start with a very high number to avoid conflicts when initiating a new session.
17
- @@genericCallID = 10_000
18
- else
19
- @@genericCallID += 1
20
- end
21
- @@genericCallID
22
- end
23
- # rubocop:enable Style/ClassVars
24
-
25
- # A Guard Method that returns true if the response from the API can be processed and raises an exception if not.
26
- # @param [Hash] response
27
- # @return [Boolean] true if no errors found.
28
- # @raise [HTTParty::ResponseError] if a communication error is found.
29
- def self.response_valid?(response)
30
- if response.success?
31
- !response_ym_error?(response)
32
- else
33
- raise HTTParty::ResponseError.new(response), 'Connection to YourMembership API failed.'
34
- end
35
- end
36
-
37
- # Checks for error codes in the API response and raises an exception if an error is found.
38
- # @param [Hash] response
39
- # @return [Boolean] false if no error is found
40
- # @raise [YourMembership::Error] if an error is found
41
- def self.response_ym_error?(response)
42
- if response['YourMembership_Response']['ErrCode'] != '0'
43
- raise YourMembership::Error.new(
44
- response['YourMembership_Response']['ErrCode'],
45
- response['YourMembership_Response']['ErrDesc']
46
- )
47
- else
48
- return false
49
- end
50
- end
51
-
52
- # Creates an XML string to send to the API
53
- # @todo THIS SHOULD BE MARKED PRIVATE and refactored to DRY up the calls.
54
- def self.build_XML_request(callMethod, session = nil, params = {}) # rubocop:disable Style/MethodLength, Style/MethodName
55
- builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
56
- # Root Node Is always <YourMembership>
57
- xml.YourMembership do
58
- # API Version
59
- xml.Version_ YourMembership.config[:version]
60
-
61
- # API Key: For System Administrative tasks it is the private key and
62
- # passcode, for all others it is the public key
63
- if callMethod.downcase.start_with?('sa.')
64
- xml.ApiKey_ YourMembership.config[:privateKey]
65
- xml.SaPasscode_ YourMembership.config[:saPasscode]
66
- else
67
- xml.ApiKey_ YourMembership.config[:publicKey]
68
- end
69
-
70
- # Pass Session ID and Session Call ID unless there is no session, then
71
- # send call id from class
72
- if session
73
- if session.is_a? YourMembership::Session
74
- xml.SessionID_ session.session_id
75
- else
76
- xml.SessionID_ session
77
- end
78
- xml.CallID_ session.call_id
79
- else
80
- xml.CallID_ new_call_id
81
- end
82
-
83
- xml.Call(:Method => callMethod) do
84
- params.each do |key, value|
85
- xml_process(key, value, xml)
86
- end
87
- end
88
- end
89
- end
90
-
91
- builder.to_xml
92
- end
93
-
94
- # This is a helper method to always return an array (potentially empty) of responses (Hashes) for methods that can
95
- # have multiple results. The default behavior of the API is to return a nil if no records are found, a hash if one
96
- # record is found and an array if multiple records are found.
97
- #
98
- # @param [Hash] response_body This is the nodeset that returns nil if an empty data set is returned.
99
- # @param [Array] keys This is a list of keys, in order that are nested inside the response body, these keys will be
100
- # traversed in order before retrieving the data associated with the last key in the array
101
- # @return [Array] A single dimension array of hashes.
102
- def self.response_to_array_of_hashes(response_body, keys = [])
103
- return_array = []
104
- if response_body
105
- # http://stackoverflow.com/questions/13259181/what-is-the-most-ruby-ish-way-of-accessing-nested-hash-values-at-arbitrary-depth
106
- response_body_items = keys.reduce(response_body) { |h, key| h[key] }
107
- if response_body_items.class == Array
108
- response_body_items.each do |response_item|
109
- return_array.push response_item
110
- end
111
- else
112
- return_array.push response_body_items
113
- end
114
- end
115
- return_array
116
- end
117
-
118
- # Converts the desired portion of the XML response to a single dimension array.
119
- # This is useful when you don't have a need for key, value pairs and want a clean array of values to work with.
120
- #
121
- # @param [Hash] response_body This is the nodeset that returns nil if an empty data set is returned.
122
- # @param [Array] keys This is a list of keys, in order that are nested inside the response body, these keys will be
123
- # traversed in order before retrieving the data associated with the key_for_array
124
- # @param [String] key_for_array this is the key that represents the list of items you want to turn into an array.
125
- # @return [Array] A single dimension array of values.
126
- def self.response_to_array(response_body, keys = [], key_for_array)
127
- return_array = []
128
- response_hash_array = response_to_array_of_hashes(response_body, keys)
129
- response_hash_array.each do |item|
130
- return_array.push item[key_for_array]
131
- end
132
- return_array
133
- end
134
-
135
- private
136
-
137
- # Convenience method to convert Ruby DateTime objects into ODBC canonical strings as are expected by the API
138
- # @param [DateTime] dateTime
139
- # @return [String] An ODBC canonical string representation of a date as is expected by the API
140
- def self.format_date_string(dateTime)
141
- dateTime.strftime('%Y-%m-%d %H:%M:%S')
142
- end
143
-
144
- def self.xml_process(key, value, xml)
145
- case value
146
- when Array
147
- xml_process_array(key, value, xml)
148
- when Hash
149
- xml_process_hash(key, value, xml)
150
- when YourMembership::Profile
151
- xml_process_profile(value, xml)
152
- when DateTime
153
- xml.send(key, format_date_string(value))
154
- else
155
- xml.send(key, value)
156
- end
157
- end
158
-
159
- def self.xml_process_profile(profile, xml)
160
- profile.data.each do |k, v|
161
- xml_process(k, v, xml)
162
- end
163
- xml.send('CustomFieldResponses') do
164
- profile.custom_data.each do |k, v|
165
- xml_process_custom_field_responses(k, v, xml)
166
- end
167
- end
168
- end
169
-
170
- def self.xml_process_hash(key, value, xml)
171
- xml.send(key) do
172
- value.each { |k, v| xml_process(k, v, xml) }
173
- end
174
- end
175
-
176
- def self.xml_process_array(key, value, xml)
177
- xml.send(key) do
178
- value.each { |tag| xml.send(tag[0], tag[1]) }
179
- end
180
- end
181
-
182
- def self.xml_process_custom_field_responses(key, value, xml)
183
- xml.send('CustomFieldResponse', :FieldCode => key) do
184
- xml.send('Values') do
185
- case value
186
- when Array
187
- value.each do | item |
188
- xml.send('Value', item)
189
- end
190
- else
191
- xml.send('Value', value)
192
- end
193
- end
194
- end
195
- end
196
- end
197
- end
1
+ module YourMembership
2
+ # Base Class inherited by all Your Membership SDK Classes.
3
+ class Base
4
+ include HTTParty
5
+
6
+ base_uri YourMembership.config[:baseUri]
7
+
8
+ # rubocop:disable Style/ClassVars
9
+
10
+ # Call IDs are usually tied to sessions, this is a unique call id for use whenever a session is not needed.
11
+ @@genericCallID = nil
12
+
13
+ # @return [Integer] Auto Increments ad returns the genericCallID as required by the YourMembership.com API
14
+ def self.new_call_id
15
+ if @@genericCallID.nil?
16
+ # We start with a very high number to avoid conflicts when initiating a new session.
17
+ @@genericCallID = 10_000
18
+ else
19
+ @@genericCallID += 1
20
+ end
21
+ @@genericCallID
22
+ end
23
+ # rubocop:enable Style/ClassVars
24
+
25
+ # A Guard Method that returns true if the response from the API can be processed and raises an exception if not.
26
+ # @param [Hash] response
27
+ # @return [Boolean] true if no errors found.
28
+ # @raise [HTTParty::ResponseError] if a communication error is found.
29
+ def self.response_valid?(response)
30
+ if response.success?
31
+ !response_ym_error?(response)
32
+ else
33
+ raise HTTParty::ResponseError.new(response), 'Connection to YourMembership API failed.'
34
+ end
35
+ end
36
+
37
+ # Checks for error codes in the API response and raises an exception if an error is found.
38
+ # @param [Hash] response
39
+ # @return [Boolean] false if no error is found
40
+ # @raise [YourMembership::Error] if an error is found
41
+ def self.response_ym_error?(response)
42
+ if response['YourMembership_Response']['ErrCode'] != '0'
43
+ raise YourMembership::Error.new(
44
+ response['YourMembership_Response']['ErrCode'],
45
+ response['YourMembership_Response']['ErrDesc']
46
+ )
47
+ else
48
+ return false
49
+ end
50
+ end
51
+
52
+ # Creates an XML string to send to the API
53
+ # @todo THIS SHOULD BE MARKED PRIVATE and refactored to DRY up the calls.
54
+ def self.build_XML_request(callMethod, session = nil, params = {}) # rubocop:disable Style/MethodLength, Style/MethodName
55
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
56
+ # Root Node Is always <YourMembership>
57
+ xml.YourMembership do
58
+ # API Version
59
+ xml.Version_ YourMembership.config[:version]
60
+
61
+ # API Key: For System Administrative tasks it is the private key and
62
+ # passcode, for all others it is the public key
63
+ if callMethod.downcase.start_with?('sa.')
64
+ xml.ApiKey_ YourMembership.config[:privateKey]
65
+ xml.SaPasscode_ YourMembership.config[:saPasscode]
66
+ else
67
+ xml.ApiKey_ YourMembership.config[:publicKey]
68
+ end
69
+
70
+ # Pass Session ID and Session Call ID unless there is no session, then
71
+ # send call id from class
72
+ if session
73
+ if session.is_a? YourMembership::Session
74
+ xml.SessionID_ session.session_id
75
+ else
76
+ xml.SessionID_ session
77
+ end
78
+ xml.CallID_ session.call_id
79
+ else
80
+ xml.CallID_ new_call_id
81
+ end
82
+
83
+ xml.Call(:Method => callMethod) do
84
+ params.each do |key, value|
85
+ xml_process(key, value, xml)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ builder.to_xml
92
+ end
93
+
94
+ # This is a helper method to always return an array (potentially empty) of responses (Hashes) for methods that can
95
+ # have multiple results. The default behavior of the API is to return a nil if no records are found, a hash if one
96
+ # record is found and an array if multiple records are found.
97
+ #
98
+ # @param [Hash] response_body This is the nodeset that returns nil if an empty data set is returned.
99
+ # @param [Array] keys This is a list of keys, in order that are nested inside the response body, these keys will be
100
+ # traversed in order before retrieving the data associated with the last key in the array
101
+ # @return [Array] A single dimension array of hashes.
102
+ def self.response_to_array_of_hashes(response_body, keys = [])
103
+ return_array = []
104
+ if response_body
105
+ # http://stackoverflow.com/questions/13259181/what-is-the-most-ruby-ish-way-of-accessing-nested-hash-values-at-arbitrary-depth
106
+ response_body_items = keys.reduce(response_body) { |h, key| h[key] }
107
+ if response_body_items.class == Array
108
+ response_body_items.each do |response_item|
109
+ return_array.push response_item
110
+ end
111
+ else
112
+ return_array.push response_body_items
113
+ end
114
+ end
115
+ return_array
116
+ end
117
+
118
+ # Converts the desired portion of the XML response to a single dimension array.
119
+ # This is useful when you don't have a need for key, value pairs and want a clean array of values to work with.
120
+ #
121
+ # @param [Hash] response_body This is the nodeset that returns nil if an empty data set is returned.
122
+ # @param [Array] keys This is a list of keys, in order that are nested inside the response body, these keys will be
123
+ # traversed in order before retrieving the data associated with the key_for_array
124
+ # @param [String] key_for_array this is the key that represents the list of items you want to turn into an array.
125
+ # @return [Array] A single dimension array of values.
126
+ def self.response_to_array(response_body, keys = [], key_for_array)
127
+ return_array = []
128
+ response_hash_array = response_to_array_of_hashes(response_body, keys)
129
+ response_hash_array.each do |item|
130
+ return_array.push item[key_for_array]
131
+ end
132
+ return_array
133
+ end
134
+
135
+ private
136
+
137
+ # Convenience method to convert Ruby DateTime objects into ODBC canonical strings as are expected by the API
138
+ # @param [DateTime] dateTime
139
+ # @return [String] An ODBC canonical string representation of a date as is expected by the API
140
+ def self.format_date_string(dateTime)
141
+ dateTime.strftime('%Y-%m-%d %H:%M:%S')
142
+ end
143
+
144
+ def self.xml_process(key, value, xml)
145
+ case value
146
+ when Array
147
+ xml_process_array(key, value, xml)
148
+ when Hash
149
+ xml_process_hash(key, value, xml)
150
+ when YourMembership::Profile
151
+ xml_process_profile(value, xml)
152
+ when DateTime
153
+ xml.send(key, format_date_string(value))
154
+ else
155
+ xml.send(key, value)
156
+ end
157
+ end
158
+
159
+ def self.xml_process_profile(profile, xml)
160
+ profile.data.each do |k, v|
161
+ xml_process(k, v, xml)
162
+ end
163
+ xml.send('CustomFieldResponses') do
164
+ profile.custom_data.each do |k, v|
165
+ xml_process_custom_field_responses(k, v, xml)
166
+ end
167
+ end
168
+ end
169
+
170
+ def self.xml_process_hash(key, value, xml)
171
+ xml.send(key) do
172
+ value.each { |k, v| xml_process(k, v, xml) }
173
+ end
174
+ end
175
+
176
+ def self.xml_process_array(key, value, xml)
177
+ xml.send(key) do
178
+ value.each { |tag| xml.send(tag[0], tag[1]) }
179
+ end
180
+ end
181
+
182
+ def self.xml_process_custom_field_responses(key, value, xml)
183
+ xml.send('CustomFieldResponse', :FieldCode => key) do
184
+ xml.send('Values') do
185
+ case value
186
+ when Array
187
+ value.each do | item |
188
+ xml.send('Value', item)
189
+ end
190
+ else
191
+ xml.send('Value', value)
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end