synapse_pay 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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +21 -0
  6. data/README.md +54 -0
  7. data/Rakefile +8 -0
  8. data/VERSION +1 -0
  9. data/bin/synapse_pay-console +7 -0
  10. data/gemfiles/default-with-activesupport.gemfile +10 -0
  11. data/gemfiles/json.gemfile +12 -0
  12. data/gemfiles/yajl.gemfile +12 -0
  13. data/lib/synapse_pay.rb +78 -0
  14. data/lib/synapse_pay/apibits/api_client.rb +29 -0
  15. data/lib/synapse_pay/apibits/api_endpoint.rb +9 -0
  16. data/lib/synapse_pay/apibits/api_list.rb +88 -0
  17. data/lib/synapse_pay/apibits/api_method.rb +97 -0
  18. data/lib/synapse_pay/apibits/api_object.rb +52 -0
  19. data/lib/synapse_pay/apibits/api_resource.rb +127 -0
  20. data/lib/synapse_pay/apibits/headers_builder.rb +47 -0
  21. data/lib/synapse_pay/apibits/params_builder.rb +30 -0
  22. data/lib/synapse_pay/apibits/path_builder.rb +38 -0
  23. data/lib/synapse_pay/apibits/requester.rb +104 -0
  24. data/lib/synapse_pay/apibits/util.rb +51 -0
  25. data/lib/synapse_pay/client.rb +89 -0
  26. data/lib/synapse_pay/endpoints/bank_endpoint.rb +47 -0
  27. data/lib/synapse_pay/endpoints/bank_mfa_device_endpoint.rb +5 -0
  28. data/lib/synapse_pay/endpoints/bank_mfa_questions_endpoint.rb +5 -0
  29. data/lib/synapse_pay/endpoints/bank_status_endpoint.rb +11 -0
  30. data/lib/synapse_pay/endpoints/card_endpoint.rb +26 -0
  31. data/lib/synapse_pay/endpoints/deposit_endpoint.rb +23 -0
  32. data/lib/synapse_pay/endpoints/mass_pay_endpoint.rb +26 -0
  33. data/lib/synapse_pay/endpoints/order_endpoint.rb +44 -0
  34. data/lib/synapse_pay/endpoints/user_endpoint.rb +26 -0
  35. data/lib/synapse_pay/endpoints/wire_endpoint.rb +29 -0
  36. data/lib/synapse_pay/endpoints/withdrawal_endpoint.rb +17 -0
  37. data/lib/synapse_pay/errors/api_connection_error.rb +4 -0
  38. data/lib/synapse_pay/errors/api_error.rb +32 -0
  39. data/lib/synapse_pay/errors/authentication_error.rb +4 -0
  40. data/lib/synapse_pay/errors/synapse_pay_error.rb +13 -0
  41. data/lib/synapse_pay/nested_list.rb +32 -0
  42. data/lib/synapse_pay/resources/bank.rb +46 -0
  43. data/lib/synapse_pay/resources/bank_mfa_device.rb +32 -0
  44. data/lib/synapse_pay/resources/bank_mfa_questions.rb +28 -0
  45. data/lib/synapse_pay/resources/bank_status.rb +21 -0
  46. data/lib/synapse_pay/resources/card.rb +32 -0
  47. data/lib/synapse_pay/resources/deposit.rb +25 -0
  48. data/lib/synapse_pay/resources/mass_pay.rb +38 -0
  49. data/lib/synapse_pay/resources/order.rb +63 -0
  50. data/lib/synapse_pay/resources/user.rb +80 -0
  51. data/lib/synapse_pay/resources/wire.rb +31 -0
  52. data/lib/synapse_pay/resources/withdrawal.rb +29 -0
  53. data/lib/synapse_pay/version.rb +3 -0
  54. data/synapse_pay.gemspec +29 -0
  55. data/test/synapse_pay/api_list_test.rb +23 -0
  56. data/test/synapse_pay/api_method_test.rb +89 -0
  57. data/test/synapse_pay/headers_builder_test.rb +39 -0
  58. data/test/synapse_pay/params_builder_test.rb +57 -0
  59. data/test/synapse_pay/path_builder_test.rb +50 -0
  60. data/test/synapse_pay/requester_test.rb +86 -0
  61. data/test/synapse_pay/util_test.rb +51 -0
  62. data/test/test_data.rb +72 -0
  63. data/test/test_helper.rb +41 -0
  64. metadata +220 -0
@@ -0,0 +1,52 @@
1
+ module SynapsePay
2
+ class APIObject
3
+ include Enumerable
4
+ attr_reader :json
5
+
6
+ def self.construct(json)
7
+ if json.is_a?(Array)
8
+ return json.map{ |a| APIObject.construct(a) }
9
+ elsif json.is_a?(Hash)
10
+ return APIObject.new(json)
11
+ else
12
+ return json
13
+ end
14
+ end
15
+
16
+ def initialize(json=nil)
17
+ refresh_from(json)
18
+ end
19
+
20
+ def refresh_from(json={})
21
+ @json = Util.sorta_deep_clone(json)
22
+ @json.each do |k, v|
23
+ @json[k] = APIObject.construct(v)
24
+ end
25
+ self
26
+ end
27
+
28
+ def inspect
29
+ @json.inspect
30
+ end
31
+
32
+ def to_json(*args)
33
+ JSON.generate(@json)
34
+ end
35
+
36
+ def method_missing(name, *args, &blk)
37
+ if name.to_s.end_with?('=')
38
+ attr = name.to_s[0...-1].to_sym
39
+ @json[attr] = args[0]
40
+ else
41
+ if @json.respond_to?(name)
42
+ @json.send(name, *args, &blk)
43
+ elsif @json.has_key?(name.to_sym)
44
+ return @json[name.to_sym]
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,127 @@
1
+ module SynapsePay
2
+ class APIResource
3
+ attr_reader :api_method
4
+ attr_reader :json
5
+ attr_reader :client
6
+
7
+ def initialize(json=nil, api_method=nil, client=nil)
8
+ refresh_from(json, api_method, client)
9
+ end
10
+
11
+ def refresh_from(json={}, api_method=nil, client=nil)
12
+ unless json.is_a?(Hash)
13
+ json = { :id => json }
14
+ end
15
+ json = Util.symbolize_keys(json)
16
+
17
+ # Clear or write over any old data
18
+ clear_api_attributes
19
+ @api_method = api_method
20
+ @json = Util.sorta_deep_clone(json)
21
+ @client = client
22
+
23
+ # Use json (not the @json, the cloned copy)
24
+ json.each do |k, v|
25
+ if self.class.api_attribute_names.include?(k.to_sym)
26
+ instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
27
+ end
28
+ end
29
+ self
30
+ end
31
+
32
+ def inspect
33
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
34
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> Attributes: " + JSON.pretty_generate(inspect_api_attributes)
35
+ end
36
+
37
+ def inspect_nested
38
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
39
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}>"
40
+ end
41
+
42
+ def to_json(*args)
43
+ JSON.generate(api_attributes)
44
+ end
45
+
46
+ def self.api_attribute_names
47
+ @api_attributes.map(&:first)
48
+ end
49
+
50
+ def api_attributes
51
+ ret = {}
52
+ self.class.api_attribute_names.each do |attribute|
53
+ ret[attribute] = self.send(attribute)
54
+ end
55
+ ret
56
+ end
57
+
58
+ def inspect_api_attributes
59
+ ret = {}
60
+ api_attributes.each do |k, v|
61
+ if v.is_a?(APIResource)
62
+ ret[k] = v.inspect_nested
63
+ else
64
+ ret[k] = v
65
+ end
66
+ end
67
+ ret
68
+ end
69
+
70
+ # TODO(joncalhoun): Make this work for nested class construction.
71
+ def changed_api_attributes
72
+ ret = {}
73
+ self.api_attributes.each do |name, value|
74
+ if @json[name] != value
75
+ ret[name] = value
76
+ end
77
+ end
78
+ ret
79
+ end
80
+
81
+ def clear_api_attributes
82
+ self.class.api_attribute_names.each do |name|
83
+ instance_variable_set("@#{name}", nil)
84
+ end
85
+ end
86
+
87
+ def self.determine_api_attribute_value(name, raw_value)
88
+ if @api_attributes[name] && @api_attributes[name].has_key?(:constructor)
89
+ klass = Util.constantize(@api_attributes[name][:constructor])
90
+ if(klass.respond_to?(:construct))
91
+ klass.construct(raw_value)
92
+ else
93
+ klass.new(raw_value)
94
+ end
95
+ else
96
+ APIObject.construct(raw_value)
97
+ end
98
+ end
99
+ def determine_api_attribute_value(name, raw_value)
100
+ self.class.determine_api_attribute_value(name, raw_value)
101
+ end
102
+
103
+
104
+ def self.api_subclasses
105
+ return @api_subclasses ||= Set.new
106
+ end
107
+
108
+ def self.api_subclass_fetch(name)
109
+ @api_subclasses_hash ||= {}
110
+ if @api_subclasses_hash.has_key?(name)
111
+ @api_subclasses_hash[name]
112
+ end
113
+ end
114
+
115
+ def self.register_api_subclass(subclass, name=nil)
116
+ @api_subclasses ||= Set.new
117
+ @api_subclasses << subclass
118
+
119
+ unless name.nil?
120
+ @api_subclasses_hash ||= {}
121
+ @api_subclasses_hash[name] = subclass
122
+ end
123
+ end
124
+
125
+ @api_attributes = {}
126
+ end
127
+ end
@@ -0,0 +1,47 @@
1
+ module SynapsePay
2
+ module HeadersBuilder
3
+
4
+ def self.build(headers)
5
+ headers ||= {}
6
+ default_headers.merge(headers)
7
+ end
8
+
9
+ def self.default_headers
10
+ headers = {
11
+ :user_agent => "SynapsePay/#{SynapsePay.api_version} RubyBindings/#{SynapsePay::VERSION}",
12
+ }.merge({
13
+ "Content-Type" => "application/json",
14
+ })
15
+
16
+ begin
17
+ headers.update(:x_synapse_pay_client_user_agent => JSON.generate(user_agent))
18
+ rescue => e
19
+ headers.update(:x_synapse_pay_client_raw_user_agent => user_agent.inspect,
20
+ :error => "#{e} (#{e.class})")
21
+ end
22
+ headers
23
+ end
24
+
25
+
26
+
27
+ def self.user_agent
28
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
29
+
30
+ {
31
+ :bindings_version => SynapsePay::VERSION,
32
+ :lang => 'ruby',
33
+ :lang_version => lang_version,
34
+ :platform => RUBY_PLATFORM,
35
+ :publisher => 'synapse_pay',
36
+ :uname => get_uname
37
+ }
38
+ end
39
+
40
+ def self.get_uname
41
+ `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
42
+ rescue Errno::ENOMEM => ex # couldn't create subprocess
43
+ "uname lookup failed"
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ module SynapsePay
2
+ module ParamsBuilder
3
+
4
+ def self.clean(params)
5
+ Util.symbolize_keys(params || {})
6
+ end
7
+
8
+ # Clean the params, and the hash to_merge, and then merge them.
9
+ # This ensures that we dont get something like { "id" => 123, :id => 321 }.
10
+ def self.merge(*args)
11
+ ret = {}
12
+ args.each do |arg|
13
+ ret = ret.merge(clean(arg))
14
+ end
15
+ ret
16
+ end
17
+
18
+ def self.build(params, api_key=nil, auth_key=nil)
19
+ default_params.merge(clean(params))
20
+ end
21
+
22
+ def self.default_params
23
+ params = {
24
+ :client_id => SynapsePay.client_id,
25
+ :client_secret => SynapsePay.client_secret,
26
+ }
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module SynapsePay
2
+ module PathBuilder
3
+
4
+ # Take a path like:
5
+ # ":path/:id/dogs/:dog_id"
6
+ # and convert it to:
7
+ # "#{object.path}/#{object.id}/dogs/#{params[:id]}" => "/objects/1/dogs/2"
8
+ #
9
+ # Path priority is:
10
+ # 1. Object - this will be a class or an instance of a class.
11
+ # 2. Params - this is a hash of key values. All keys *must* be symbolized.
12
+ def self.build(path, object, params)
13
+ ret = path.dup
14
+ if ret.include?(":")
15
+ matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
16
+ missing = Set.new(matches)
17
+
18
+ matches.each do |match|
19
+ value = determine_value(match, object, params)
20
+ missing.delete(match) unless value.nil?
21
+ ret.sub!(match.inspect, "#{value}")
22
+ end
23
+
24
+ if missing.any?
25
+ raise ArgumentError.new("Could not determine the full URL. The following values of the path are missing: #{missing.to_a.join(', ')}. Try setting them in your params.")
26
+ end
27
+ end
28
+ ret
29
+ end
30
+
31
+ def self.determine_value(match, object, params)
32
+ value = object.send(match) if object && object.respond_to?(match)
33
+ value ||= params[match] if params && params.has_key?(match)
34
+ value
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,104 @@
1
+ module SynapsePay
2
+ module Requester
3
+
4
+ def self.request(method, url, params, headers)
5
+ method = method.to_sym
6
+ url, params = prepare_params(method, url, params, headers)
7
+ request_opts = {
8
+ :method => method,
9
+ :url => url,
10
+ :headers => headers,
11
+ :payload => params,
12
+
13
+ :verify_ssl => false,
14
+ :open_timeout => 30,
15
+ :timeout => 60
16
+ }
17
+
18
+ execute_request(request_opts)
19
+ end
20
+
21
+ def self.execute_request(opts)
22
+ RestClient::Request.execute(opts)
23
+ end
24
+
25
+ def self.get(url, params, headers)
26
+ self.request(:get, url, params, headers)
27
+ end
28
+
29
+ def self.delete(url, params, headers)
30
+ self.request(:delete, url, params, headers)
31
+ end
32
+
33
+ def self.put(url, params, headers)
34
+ self.request(:put, url, params, headers)
35
+ end
36
+
37
+ def self.post(url, params, headers)
38
+ self.request(:post, url, params, headers)
39
+ end
40
+
41
+ def self.prepare_params(method, url, params, headers)
42
+ if [:get, :head, :delete].include?(method)
43
+ unless params.empty?
44
+ url += URI.parse(url).query ? '&' : '?' + query_string(params)
45
+ end
46
+ params = nil
47
+ else
48
+ if headers["Content-Type"] == "application/json"
49
+ params = JSON.generate(params)
50
+ else
51
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
52
+ if !RestClient::Payload.has_file?(params)
53
+ params = query_string(params)
54
+ end
55
+ end
56
+ end
57
+ [url, params]
58
+ end
59
+
60
+ def self.query_string(params)
61
+ params ||= {}
62
+ if params.any?
63
+ query_array(params).join('&')
64
+ else
65
+ ""
66
+ end
67
+ end
68
+
69
+ # Three major use cases (and nesting of them needs to be supported):
70
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
71
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
72
+ # { :a => "value" } => ["a=value"]
73
+ def self.query_array(params, key_prefix=nil)
74
+ ret = []
75
+ params.each do |key, value|
76
+ if params.is_a?(Array)
77
+ value = key
78
+ key = ''
79
+ end
80
+ key_suffix = escape(key)
81
+ full_key = key_prefix ? "#{key_prefix}[#{key_suffix}]" : key_suffix
82
+
83
+ if value.is_a?(Hash) || value.is_a?(Array)
84
+ # Handles the following cases:
85
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
86
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
87
+ ret += query_array(value, full_key)
88
+ elsif value.is_a?(APIObject)
89
+ ret += query_array(value.json, full_key)
90
+ else
91
+ # Handles the base case with just key and value:
92
+ # { :a => "value" } => ["a=value"]
93
+ ret << "#{full_key}=#{escape(value)}"
94
+ end
95
+ end
96
+ ret
97
+ end
98
+
99
+ def self.escape(val)
100
+ URI.escape(val.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,51 @@
1
+ module SynapsePay
2
+ module Util
3
+
4
+ def self.symbolize_keys(obj)
5
+ if obj.is_a?(Hash)
6
+ ret = {}
7
+ obj.each do |key, value|
8
+ ret[(key.to_sym rescue key) || key] = symbolize_keys(value)
9
+ end
10
+ return ret
11
+ elsif obj.is_a?(Array)
12
+ return obj.map{ |value| symbolize_keys(value) }
13
+ else
14
+ return obj
15
+ end
16
+ end
17
+
18
+ def self.sorta_deep_clone(json)
19
+ if json.is_a?(Hash)
20
+ ret = {}
21
+ json.each do |k, v|
22
+ ret[k] = sorta_deep_clone(v)
23
+ end
24
+ ret
25
+ elsif json.is_a?(Array)
26
+ json.map{ |j| sorta_deep_clone(j) }
27
+ else
28
+ begin
29
+ json.dup
30
+ rescue
31
+ json
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.constantize(str, prefix=false)
37
+ str = str.to_s
38
+ begin
39
+ str.split('::').reduce(Module, :const_get)
40
+ rescue NameError => e
41
+ if prefix
42
+ raise e
43
+ else
44
+ p = "#{self.name}".split("::").first
45
+ constantize("#{p}::#{str}", true)
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end