your_membership 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e167609f71a1eb9be5e05117ef8baa58039ba2c9
4
+ data.tar.gz: 46cb16feb9c4cf28145a18b4ef23a2ebc8f79f4e
5
+ SHA512:
6
+ metadata.gz: a9d00bfa496efd4a21c47bbc0a613f5fab98f245a3d87d3ca252837ebd9cdafc8fa5a4af2ca6a7c0a62e231d870833f55337ce00959fd8db22894523335063fb
7
+ data.tar.gz: c73d3f40ea5053b5dbebc54309be0bca063befcc2c6fa371007c393b8ff5840c166ad00969d40b6564fd5888c141a4deaac4265c0f9e1b2f9e37f4ae22ffd32d
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in your_membership.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ECHO Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,261 @@
1
+ YourMembership: Ruby SDK for the YourMembership.Com XML API
2
+ ===========================================
3
+
4
+ _This SDK for Version 2.00 of the YourMembership.com API_
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'your_membership'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install your_membership
19
+
20
+ ## Usage
21
+
22
+ Every effort has been made to expose the YourMembership.com API as transparently as possible while still providing a natively Ruby interface.
23
+
24
+ ### Scope and Naming
25
+
26
+ Everything is scoped under the `YourMembership` namespace.
27
+
28
+ Methods are named according to the naming conventions used in the API mapped to appropriate Ruby conventions:
29
+
30
+ `Events.All.Search` becomes `YourMembership::Events.all_search`
31
+ `Member.Commerce.Store.GetOrderIDs` becomes `YourMembership::Member.commerce_store_getOrderIDs`
32
+ `Sa.Members.All.GetIDs` becomes `YourMembership::Sa::Members.all_getIDs`
33
+
34
+ Note that the dot `.` notation has been converted to Ruby's preferred all-lowercase underscore "snake" `_` conventions, but that the method name camelCasing has been preserved for all characters other than the first.
35
+
36
+ ### Configuration
37
+
38
+ You can configure the gem in one of two ways:
39
+
40
+ #### YAML
41
+
42
+ Pass a YAML file through `YourMembership.configure_with(YourFilename.YAML)`
43
+
44
+ _Example YAML File_
45
+
46
+ ```YAML
47
+ ---
48
+ # Configuration settings for YourWebsiteHere.com
49
+ publicKey: 45G2E6DC-98NA-45W7-8493-D97C4E2C156A
50
+ privateKey: D74H44B2-2348-4ACT-B531-45W385TGB966
51
+ saPasscode: WPIkriJtqS4m
52
+ baseUri: 'https://api.yourmembership.com'
53
+ version: '2.00'
54
+ ```
55
+
56
+ #### Configuration Hash
57
+
58
+ Pass in one or more parameters through a hash of configuration keys
59
+
60
+ _Example Configuration Hash_
61
+
62
+ ```RUBY
63
+ YourMembership.configure :publicKey => 45G2E6DC-98NA-45W7-8493-D97C4E2C156A, :privateKey => D74H44B2-2348-4ACT-B531-45W385TGB966, :saPasscode => WPIkriJtqS4m
64
+ ```
65
+
66
+ *Note that the baseUri and version are both defaulted to the current API for the release version.
67
+
68
+ ### Method Arguments and Returned Value Types
69
+
70
+ #### Arguments
71
+
72
+ Method arguments marked as required in the YourMembership.com API must are implemented as required arguments to the SDK methods in this pattern:
73
+
74
+ `YourMembership::Namespace.some_methodName(sessionArgument, requiredArgument(s), optionsHash)`
75
+
76
+ The `sessionArgument` is required for most method calls in the API, but is sometimes not required. The ::Sa:: (System Administrator) namespaced methods usually do not require a session.
77
+
78
+ The options hash is present any time optional arguments can be passed to a method. These are sent as key-value pairs with the keys being `:symbol` objects named and cased in the way the API requires. For instance:
79
+
80
+ `YourMembership::Events.all_search` takes one required argument (a session object/key) and up to three optional arguments passed as a hash.
81
+
82
+ ```RUBY
83
+ session = YourMembership::Session.new
84
+ options = {:SearchText => "A string to search for", :PageSize => 5, :StartRecord => 6}
85
+ YourMembership::Events.all_search session, options
86
+ ```
87
+ **Note:** _Arguments passed as symbols need to be capitalized in the way that the API expects them. Many Rubyists prefer camelCased symbols, but in the case of the SDK you'll need to provide first-letter-capitalized CamelCase arguments._
88
+
89
+ You can pass strings as keys if you do not prefer to use Symbols.
90
+
91
+ ##### SDK-Specific Implementation of Argument Types
92
+
93
+ You should use the standard Ruby types to represent data, for instance:
94
+
95
+ `:Timestamp` and other date or time arguments should be passed as DateTime objects rather than formatted strings, the formatting will be done for you.
96
+
97
+ **A little added Syntactic Sugar:** Where a limited scope of static options is present some handy symbols have been added for convenience. For instance:
98
+
99
+ `YourMembership::Member.commerce_store_getOrderIDs` takes an optional `:Timestamp` argument (which should be passed as a Ruby `DateTime` object) and an optional `:Status` argument. The `:Status` argument can either be an integer that represents a status as documented in the API or you can use a symbol that will automatically be mapped through a helper method. In this case you can pass `:open`, `:processed`, `:shipped`, `:closed`, or `:cancelled`.
100
+
101
+ #### Returned Values
102
+
103
+ 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.
104
+
105
+ 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.
106
+
107
+ In the case that multiple records could be returned, but no records are returned you should receive an empty Array.
108
+
109
+ The return type you should expect is documented along with every method in the source.
110
+
111
+ **Special Case:** The `YourMembership::Feeds.feed_get` method returns a Nokogiri::XML object
112
+
113
+ --------------------------------------------------------------------------------
114
+
115
+ ### SDK Special Objects
116
+
117
+ The SDK implements some representative objects that provide convenience.
118
+
119
+ #### YourMembership::Session
120
+
121
+ **Note:** *It is important to note that the Auth Namespace has been consumed by Sessions in the SDK as sessions and authentication are inextricably linked.*
122
+
123
+ Session objects encapsulate the creation, storage, authentication, maintenance, and destruction of sessions in the YourMembership.com API.
124
+
125
+ Sessions can be **generic** (unauthenticated), **authenticated**, or **abandoned**.
126
+
127
+ + **Generic sessions** are used extensively whenever the scope of a specific user is not necessary.
128
+ + **Authenticated sessions** are used when the called method requires the scope of a specific user.
129
+ + **Abandoned sessions** are no longer usable and are essentially the same as logging out. A session can be forcibly abandoned and is automatically considered abandoned if it is not used for more than 20 minutes. If a session is needed for a length of more than 20 minutes the `ping` method can be called to extend the life-cycle for another 20 minutes.
130
+
131
+ ##### Examples:
132
+ ```RUBY
133
+ # Generic (unauthenticated) Session
134
+ session = YourMembership::Session.new
135
+
136
+ # Authenticated Session
137
+ auth_session = YourMembership::Session.new 'username', 'password'
138
+
139
+ # Sessions can also be authenticated after creation in one of two ways:
140
+
141
+ # By providing Credentials
142
+ session.authenticate 'username', 'password'
143
+
144
+ # Or through Token Authentication
145
+ token = session.createToken
146
+ ```
147
+
148
+ ##### Additional Methods
149
+ These additional methods report on the status of the session on the YourMembership.com server.
150
+
151
+ + `.valid?` indicates if a session is still _alive_ and able to be used to make calls
152
+ + `.authenticated?` indicates if a session is authenticated
153
+
154
+ #### YourMembership::Member
155
+
156
+ The Member object builds upon the Session object to more fully represent an authenticated session and provide an easy way to encapsulate the Member methods within the session's authenticated scope.
157
+
158
+ ##### Instantiation:
159
+ ```RUBY
160
+ # Members can be created by authenticating directly with the YourMembership API,
161
+ # the session is automatically created and bound to the Member instance.
162
+ member = YourMembership::Member.create_by_authentication 'username', 'password'
163
+
164
+ # Members can also be created by passing in an already existing Session instance
165
+ # this is especially useful when Sessions are authenticated through token
166
+ # authentication.
167
+ auth_session = YourMembership::Session.new 'username', 'password'
168
+ member = YourMembership::Member.create_from_session auth_session
169
+ ```
170
+ _Member objects can be created directly without a bound Session, but there is (as of yet) very little use for this._
171
+
172
+ #### Making use of a Member Instance
173
+
174
+ When a Member object is instantiated some basic properties of the represented member are cached in the object:
175
+ + id - The member's API ID String
176
+ + website_id - The member's unique identifier within your community
177
+ + first_name
178
+ + last_name
179
+ + email
180
+
181
+ These fields are useful in reducing the number of round trips to the API for some commonly used attributes, but the real benefit of the Member instance is to be able to access all of the Member namespaced methods within the scope of the member.
182
+
183
+ Any method in the Member Namespace can be called like these examples:
184
+ ```RUBY
185
+ member = YourMembership::Member.create_by_authentication 'username', 'password'
186
+
187
+ profile = member.profile_get
188
+ sent_messages = member.messages_getSent
189
+ ...
190
+ order = member.commerce_store_order_get member.commerce_store_getOrderIDs[0]
191
+ ```
192
+ **NOTE:** *Of course all of the methods of the Member namespace can be called without a Member instance, but an authorized session will need to be passed as the first argument.*
193
+
194
+ ##### Member Session Management:
195
+
196
+ The Member instance's bound Session instance is accessible as an attribute.
197
+
198
+ #### YourMembership::Profile
199
+
200
+ The Profile object provides a convenient abstraction that encapsulates a person's profile allowing clear and concise access to both the core fields provided by YourMembership and the custom fields added by site administrators.
201
+
202
+ The API has some required fields for new members so a specific method (Profile.create_new) exists for the convenience of quickly creating a new profile for a new person.
203
+
204
+ A profile can be loaded by passing a hash directly to the initializer (Profile.new) method. This can be useful in creating a profile object from an API response. This is how profile objects are created internally through the YourMembership::Member.profile_get, YourMembership::People.profile_get, and YourMembership::Sa::People.profile_get methods.
205
+
206
+ A profile can be created empty or by passing a hash for standard fields. This is useful for updating an existing profile without changing unnecessary records.
207
+
208
+ An *outdated* list of standard fields can be found here: https://api.yourmembership.com/reference/YM_API_Member_Field_Documentation.pdf
209
+
210
+ ##### Data Access
211
+ + **Profile#data** `Hash` This internal hash can be read and written to and should contain only fields that are standard in the YourMembership system.
212
+ + **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.
213
+ + **Profile#to_h** `Hash` is a read-only method for returning a single nested hash of both standard and custom fields.
214
+
215
+ #### YourMembership::Export
216
+
217
+ Export objects are returned when any YourMembership::Sa::Export method is called. This provides an easy way to keep track of the status of the export request. Export requests are handled asynchronously and therefore should be polled until the results are ready.
218
+
219
+ The Export object provides the Export#status method. It is necessary to call the Export#status method at least once in the export objects lifecycle as the export_uri will not be made available until status is called.
220
+
221
+ **Example:**
222
+
223
+ ```RUBY
224
+ donations = YourMembership::Sa::Export.donations_transactions(DateTime.now, True)
225
+
226
+ # Implement a loop to check status
227
+
228
+ case donations.status
229
+ when :failure, :error
230
+ # Stop looping, the process failed
231
+ when :unknown, :working
232
+ # Continue looping
233
+ when :complete
234
+ # Export is ready
235
+ donations.export_uri
236
+ end
237
+ ```
238
+
239
+ ## About The Author(s)
240
+
241
+ ![ECHO Inc.](http://static.squarespace.com/static/516da119e4b00686219e2473/t/51e95357e4b0db0cdaadcb4d/1407936664333/?format=1500w)
242
+
243
+ This library is an open-source project built by ECHO, an international nonprofit working to help those who are teaching farmers around the world know how to be more effective in producing enough to meet the needs of their families and their communities. ECHO is an information hub for international development practitioners specifically focused on sustainable, small-scale, agriculture. Through educational programs, publications, and networking ECHO is sharing solutions from around the world that are solving hunger problems.
244
+
245
+ Charity Navigator ranks ECHO as the #1 International Charity in the state of Florida (where their US operations are based) and among the top 100 in the US.
246
+
247
+ Thanks to grants and donations from people like you, ECHO is able to connect trainers and farmers with valuable (and often hard-to-find) resources. One of ECHO's greatest resources is the network of development practitioners, around the globe, that share help and specialized knowledge with each other. ECHO uses the YourMembership product to help facilitate these connections.
248
+
249
+ To find out more about ECHO, or to help with the work that is being done worldwide please visit http://www.echonet.org
250
+
251
+ ## Contributing
252
+
253
+ If you find a problem with this library or would like to contribute an improvement please fork and submit a pull request.
254
+
255
+ 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
256
+
257
+ 1. Fork it ( https://github.com/ECHOInternational/your_membership/fork )
258
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
259
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
260
+ 4. Push to the branch (`git push origin my-new-feature`)
261
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +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
@@ -0,0 +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
@@ -0,0 +1,25 @@
1
+ module YourMembership
2
+ # The Commerce Namespace does not currently have any methods in the
3
+ # YourMembership.com API. This is used for some helper methods.
4
+ class Commerce
5
+ # Converts some convenient Symbols to the Integer that the YourMembership API expects
6
+ # @param [Symbol] status Accepts :cancelled, :open, :processed, :shipped
7
+ # @return [Integer] Returns the integer that the API expects
8
+ def self.convert_order_status(status)
9
+ status_code = 0
10
+ case status
11
+ when :cancelled, :Cancelled
12
+ status_code = -1
13
+ when :open, :Open
14
+ status_code = 0
15
+ when :processed, :Processed
16
+ status_code = 1
17
+ when :shipped, :Shipped, :closed, :Closed
18
+ status_code = 2
19
+ else
20
+ status_code = status
21
+ end
22
+ status_code
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ require 'yaml'
2
+ module YourMembership
3
+ # Configuration defaults
4
+ @config = {
5
+ :publicKey => '',
6
+ :privateKey => '',
7
+ :saPasscode => '',
8
+ :baseUri => 'https://api.yourmembership.com',
9
+ :version => '2.00'
10
+ }
11
+
12
+ @valid_config_keys = @config.keys
13
+
14
+ # Configure through hash
15
+ # @example
16
+ # YourMembership.configure(:publicKey => 45G2E6DC-98NA-45W7-8493-D97C4E2C156A,
17
+ # :privateKey => D74H44B2-2348-4ACT-B531-45W385TGB966, :saPasscode => WPIkriJtqS4m)
18
+ # @note The baseUri and version are both defaulted to the current API for the release version.
19
+ def self.configure(opts = {})
20
+ opts.each { |k, v| @config[k.to_sym] = v if @valid_config_keys.include? k.to_sym }
21
+ end
22
+
23
+ # Configure through yaml file
24
+ # @example
25
+ # ---
26
+ # publicKey: 45G2E6DC-98NA-45W7-8493-D97C4E2C156A
27
+ # privateKey: D74H44B2-2348-4ACT-B531-45W385TGB966
28
+ # saPasscode: WPIkriJtqS4m
29
+ # baseUri: 'https://api.yourmembership.com'
30
+ # version: '2.00'
31
+ # @note The baseUri and version are both defaulted to the current API for the release version.
32
+ def self.configure_with(path_to_yaml_file)
33
+ config = YAML.load(IO.read(path_to_yaml_file))
34
+ configure(config)
35
+ end
36
+
37
+ # Access configuration variables by calling YourMembership.config[ :attribute ]
38
+ def self.config # rubocop:disable Style/TrivialAccessors
39
+ @config
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ module YourMembership
2
+ # YourMembership Convert Namespace
3
+ class Convert < YourMembership::Base
4
+ # Converts the given local time to current Eastern Time, factoring in adjustments for Daylight Savings Time.
5
+ # @see https://api.yourmembership.com/reference/2_00/Convert_ToEasternTime.htm
6
+ #
7
+ # @param [YourMembership::Session] session
8
+ # @param [DateTime] localTime A DateTime that you want to convert to US/Eastern Time
9
+ # @param [Integer] localGMTBias The Number of hours the local time zone is away from GMT
10
+ # @return [Hash] Returns an Hash with 'Converted' and 'ServerGmtBias'
11
+ # @note This is probably not necessary as Ruby will happily convert dates.
12
+ def self.ToEasternTime(session, localTime, localGMTBias) # rubocop:disable Style/MethodName
13
+ options = {}
14
+ options[:LocalTime] = format_date_string localTime
15
+ options[:LocalGmtBias] = localGMTBias
16
+
17
+ response = post('/', :body => build_XML_request('Convert.ToEasternTime', session, options))
18
+
19
+ if response_valid? response
20
+ response['YourMembership_Response']['Convert.ToEasternTime']
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module YourMembership
2
+ # Custom exception for YourMembership error codes.
3
+ # @attr [Integer] error_code The Error Code
4
+ # @attr [String] error_description A Description of what the error means.
5
+ class Error < StandardError
6
+ attr_accessor :error_code, :error_description
7
+
8
+ # @param [Integer] error_code The Error Code
9
+ # @param [String] error_description A Description of what the error means.
10
+ # @see https://api.yourmembership.com/reference/2_00/Error_Codes.htm
11
+ def initialize(error_code, error_description)
12
+ self.error_code = error_code
13
+ self.error_description = error_description
14
+ end
15
+
16
+ # @return [String] A highly readable error message.
17
+ def to_s
18
+ "Your Membership Returned An Error Code: #{error_code} With Message: #{error_description}"
19
+ end
20
+ end
21
+ end