api_client 0.5.24-java → 0.5.25-java

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/api_client/base.rb +77 -0
  4. data/lib/api_client/connection/abstract.rb +81 -0
  5. data/lib/api_client/connection/basic.rb +129 -0
  6. data/lib/api_client/connection/json.rb +14 -0
  7. data/lib/api_client/connection/middlewares/request/json.rb +34 -0
  8. data/lib/api_client/connection/middlewares/request/logger.rb +64 -0
  9. data/lib/api_client/connection/middlewares/request/oauth.rb +22 -0
  10. data/lib/api_client/connection/oauth.rb +18 -0
  11. data/lib/api_client/errors.rb +31 -0
  12. data/lib/api_client/mixins/configuration.rb +24 -0
  13. data/lib/api_client/mixins/connection_hooks.rb +24 -0
  14. data/lib/api_client/mixins/delegation.rb +23 -0
  15. data/lib/api_client/mixins/inheritance.rb +19 -0
  16. data/lib/api_client/mixins/instantiation.rb +29 -0
  17. data/lib/api_client/mixins/scoping.rb +49 -0
  18. data/lib/api_client/resource/base.rb +67 -0
  19. data/lib/api_client/resource/name_resolver.rb +37 -0
  20. data/lib/api_client/resource/scope.rb +73 -0
  21. data/lib/api_client/scope.rb +125 -0
  22. data/lib/api_client/utils.rb +18 -0
  23. data/lib/api_client/version.rb +3 -0
  24. data/spec/api_client/base/connection_hook_spec.rb +18 -0
  25. data/spec/api_client/base/delegation_spec.rb +15 -0
  26. data/spec/api_client/base/inheritance_spec.rb +44 -0
  27. data/spec/api_client/base/instantiation_spec.rb +55 -0
  28. data/spec/api_client/base/marshalling_spec.rb +33 -0
  29. data/spec/api_client/base/parsing_spec.rb +38 -0
  30. data/spec/api_client/base/scoping_spec.rb +60 -0
  31. data/spec/api_client/base_spec.rb +107 -0
  32. data/spec/api_client/connection/abstract_spec.rb +21 -0
  33. data/spec/api_client/connection/basic_spec.rb +191 -0
  34. data/spec/api_client/connection/oauth_spec.rb +27 -0
  35. data/spec/api_client/connection/request/json_spec.rb +30 -0
  36. data/spec/api_client/connection/request/logger_spec.rb +18 -0
  37. data/spec/api_client/connection/request/oauth_spec.rb +26 -0
  38. data/spec/api_client/resource/base_spec.rb +97 -0
  39. data/spec/api_client/resource/name_spec.rb +19 -0
  40. data/spec/api_client/resource/scope_spec.rb +122 -0
  41. data/spec/api_client/scope_spec.rb +204 -0
  42. data/spec/api_client/utils_spec.rb +32 -0
  43. data/spec/support/matchers.rb +5 -0
  44. metadata +62 -1
@@ -0,0 +1,18 @@
1
+ module ApiClient
2
+
3
+ module Connection
4
+
5
+ class Oauth < Basic
6
+
7
+ def finalize_handler
8
+ @handler.use Middlewares::Request::Logger, ApiClient.logger if ApiClient.logger
9
+ @handler.use Middlewares::Request::OAuth, @options[:oauth]
10
+ @handler.use Faraday::Request::UrlEncoded
11
+ @handler.adapter Faraday.default_adapter
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,31 @@
1
+ module ApiClient
2
+
3
+ module Errors
4
+ class ApiClientError < StandardError
5
+ def initialize(message = nil, request = nil, response = nil)
6
+ message ||= "Status code: #{response.status}" if response
7
+ super(message)
8
+ @request = request
9
+ @response = response
10
+ end
11
+
12
+ attr_reader :request, :response
13
+ end
14
+
15
+ class ConnectionFailed < ApiClientError; end
16
+ class Config < ApiClientError; end
17
+ class Unauthorized < ApiClientError; end
18
+ class Forbidden < ApiClientError; end
19
+ class NotFound < ApiClientError; end
20
+ class Redirect < ApiClientError; end
21
+ class BadRequest < ApiClientError; end
22
+ class Unsupported < ApiClientError; end
23
+ class Conflict < ApiClientError; end
24
+ class Gone < ApiClientError; end
25
+ class ServerError < ApiClientError; end
26
+ class UnprocessableEntity < ApiClientError; end
27
+ class Locked < ApiClientError; end
28
+ class TooManyRequests < ApiClientError; end
29
+ end
30
+
31
+ end
@@ -0,0 +1,24 @@
1
+ module ApiClient
2
+
3
+ module Mixins
4
+
5
+ module Configuration
6
+
7
+ def dsl_accessor(*names)
8
+ options = names.last.is_a?(Hash) ? names.pop : {}
9
+ names.each do |name|
10
+ returns = options[:return_self] ? "self" : "@#{name}"
11
+ class_eval <<-STR
12
+ def #{name}(value = nil)
13
+ value.nil? ? @#{name} : @#{name} = value
14
+ #{returns}
15
+ end
16
+ STR
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ module ApiClient
2
+
3
+ module Mixins
4
+
5
+ module ConnectionHooks
6
+
7
+ attr_accessor :connection_hooks
8
+
9
+ def connection(&block)
10
+ @connection_hooks ||= []
11
+ @connection_hooks.push(block) if block
12
+ @connection_hooks
13
+ end
14
+
15
+ def connection_hooks
16
+ @connection_hooks || []
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
@@ -0,0 +1,23 @@
1
+ module ApiClient
2
+
3
+ module Mixins
4
+
5
+ module Delegation
6
+
7
+ def delegate(*methods)
8
+ hash = methods.pop
9
+ to = hash[:to]
10
+ methods.each do |method|
11
+ class_eval <<-STR
12
+ def #{method}(*args, &block)
13
+ #{to}.#{method}(*args, &block)
14
+ end
15
+ STR
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,19 @@
1
+ module ApiClient
2
+
3
+ module Mixins
4
+
5
+ module Inheritance
6
+
7
+ def inherited(subclass)
8
+ subclass.default_scopes = self.default_scopes.dup
9
+ subclass.connection_hooks = self.connection_hooks.dup
10
+
11
+ subclass.namespace self.namespace
12
+ subclass.format self.format
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,29 @@
1
+ module ApiClient
2
+ module Mixins
3
+ module Instantiation
4
+ def self.extended(base)
5
+ base.instance_eval do
6
+ attr_accessor :original_scope
7
+ end
8
+ end
9
+
10
+ def build_one(hash)
11
+ instance = self.new self.namespace ? hash[namespace] : hash
12
+ instance.original_scope = self.scope.clone_only_headers
13
+ instance
14
+ end
15
+
16
+ def build_many(array)
17
+ array.collect { |one| build_one(one) }
18
+ end
19
+
20
+ def build(result_or_array)
21
+ if result_or_array.is_a?(Array)
22
+ build_many result_or_array
23
+ else
24
+ build_one result_or_array
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ module ApiClient
2
+
3
+ module Mixins
4
+
5
+ module Scoping
6
+
7
+ attr_accessor :default_scopes
8
+
9
+ # Default scoping
10
+ def always(&block)
11
+ default_scopes.push(block) if block
12
+ end
13
+
14
+ def default_scopes
15
+ @default_scopes || []
16
+ end
17
+
18
+ # Scoping
19
+ def scope(options = {})
20
+ scope_in_thread || Scope.new(self).params(options)
21
+ end
22
+
23
+ # Allow wrapping singleton methods in a scope
24
+ # Store the handler in a thread-local variable for thread safety
25
+ def scoped(scope)
26
+ Thread.current[scope_thread_attribute_name] ||= []
27
+ Thread.current[scope_thread_attribute_name].push scope
28
+ begin
29
+ yield
30
+ ensure
31
+ Thread.current[scope_thread_attribute_name] = nil
32
+ end
33
+ end
34
+
35
+ def scope_thread_attribute_name
36
+ "#{self.name}_scope"
37
+ end
38
+
39
+ def scope_in_thread
40
+ if found = Thread.current[scope_thread_attribute_name]
41
+ found.last
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,67 @@
1
+ module ApiClient
2
+
3
+ module Resource
4
+
5
+ class Base < ApiClient::Base
6
+
7
+ class << self
8
+ extend ApiClient::Mixins::Delegation
9
+ extend ApiClient::Mixins::Configuration
10
+
11
+ delegate :find_all, :find, :create, :update, :destroy, :path, :to => :scope
12
+
13
+ dsl_accessor :prefix
14
+
15
+ def inherited(subclass)
16
+ super
17
+ small_name = NameResolver.resolve(subclass.name)
18
+ subclass.namespace small_name
19
+ subclass.prefix self.prefix
20
+ subclass.always do
21
+ name = small_name
22
+ pre_fix = prefix
23
+ path ["", prefix, "#{name}s"].compact.join('/')
24
+ end
25
+ end
26
+
27
+ def scope(options = {})
28
+ scope_in_thread || ApiClient::Resource::Scope.new(self).params(options)
29
+ end
30
+
31
+ end
32
+
33
+ def persisted?
34
+ !!self.id
35
+ end
36
+
37
+ def save
38
+ self.persisted? ? remote_update : remote_create
39
+ end
40
+
41
+ def destroy
42
+ get_scope.destroy(self.id)
43
+ end
44
+
45
+ def payload
46
+ hash = self.to_hash
47
+ hash.delete('id') # This key is never required
48
+ hash
49
+ end
50
+
51
+ def remote_update
52
+ get_scope.update(self.id, payload)
53
+ end
54
+
55
+ def remote_create
56
+ get_scope.create(payload)
57
+ end
58
+
59
+ def get_scope
60
+ original_scope || self.class
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,37 @@
1
+ module ApiClient
2
+ module Resource
3
+ class NameResolver
4
+ def self.resolve(ruby_path)
5
+ new(ruby_path).resolve
6
+ end
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+
14
+ def resolve
15
+ select_last_item
16
+ underscorize
17
+ lowercase
18
+ name
19
+ end
20
+
21
+ private
22
+ def select_last_item
23
+ @name = @name.split('::').last
24
+ end
25
+
26
+ #Inspired by ActiveSupport::Inflector#underscore
27
+ def underscorize
28
+ @name.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
29
+ @name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
30
+ end
31
+
32
+ def lowercase
33
+ @name.downcase!
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ # This class includes methods for calling restful APIs
2
+ module ApiClient
3
+
4
+ module Resource
5
+
6
+ class Scope < ApiClient::Scope
7
+
8
+ dsl_accessor :path, :return_self => true
9
+
10
+ def format
11
+ @scopeable.format
12
+ end
13
+
14
+ def append_format(path)
15
+ format ? [path, format].join('.') : path
16
+ end
17
+
18
+ def find(id)
19
+ path = [@path, id].join('/')
20
+ path = append_format(path)
21
+ response = get(path)
22
+ scoped(self) do
23
+ raw? ? response : @scopeable.build(response)
24
+ end
25
+ end
26
+
27
+ def find_all(params = {})
28
+ path = append_format(@path)
29
+ response = get(path, params)
30
+ scoped(self) do
31
+ raw? ? response : @scopeable.build(response)
32
+ end
33
+ end
34
+
35
+ def create(params = {})
36
+ path = append_format(@path)
37
+ hash = if @scopeable.namespace
38
+ { @scopeable.namespace => params }
39
+ else
40
+ params
41
+ end
42
+ response = post(path, hash)
43
+ scoped(self) do
44
+ raw? ? response : @scopeable.build(response)
45
+ end
46
+ end
47
+
48
+ def update(id, params = {})
49
+ path = [@path, id].join('/')
50
+ path = append_format(path)
51
+ hash = if @scopeable.namespace
52
+ { @scopeable.namespace => params }
53
+ else
54
+ params
55
+ end
56
+ response = put(path, hash)
57
+ scoped(self) do
58
+ raw? ? response : @scopeable.build(response)
59
+ end
60
+ end
61
+
62
+ def destroy(id)
63
+ path = [@path, id].join('/')
64
+ path = append_format(path)
65
+ delete(path)
66
+ true
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,125 @@
1
+ module ApiClient
2
+
3
+ class Scope
4
+ extend ApiClient::Mixins::Configuration
5
+ extend ApiClient::Mixins::Delegation
6
+
7
+ delegate :prefix, :scoped, :to => :scopeable
8
+
9
+ dsl_accessor :endpoint, :adapter, :return_self => true
10
+
11
+ attr_reader :scopeable
12
+
13
+ def initialize(scopeable)
14
+ @scopeable = scopeable
15
+ @params = {}
16
+ @headers = {}
17
+ @options = {}
18
+ @scopeable.default_scopes.each do |default_scope|
19
+ self.instance_eval(&default_scope)
20
+ end
21
+ end
22
+
23
+ def connection
24
+ klass = Connection.const_get((@adapter || Connection.default).to_s.capitalize)
25
+ @connection = klass.new(@endpoint , @options || {})
26
+ hooks = @scopeable.connection_hooks || []
27
+ hooks.each { |hook| hook.call(@connection, self) }
28
+ @connection
29
+ end
30
+
31
+ def raw
32
+ @raw = true
33
+ self
34
+ end
35
+
36
+ def raw?
37
+ !!@raw
38
+ end
39
+
40
+ # 3 Pillars of scoping
41
+ # options - passed on the the adapter
42
+ # params - converted to query or request body
43
+ # headers - passed on to the request
44
+ def options(new_options = nil)
45
+ return @options if new_options.nil?
46
+ ApiClient::Utils.deep_merge(@options, new_options)
47
+ self
48
+ end
49
+
50
+ def params(options = nil)
51
+ return @params if options.nil?
52
+ ApiClient::Utils.deep_merge(@params, options) if options
53
+ self
54
+ end
55
+ alias :scope :params
56
+
57
+ def headers(options = nil)
58
+ return @headers if options.nil?
59
+ ApiClient::Utils.deep_merge(@headers, options) if options
60
+ self
61
+ end
62
+
63
+ def raw_body(options = nil)
64
+ return @raw_body if options.nil?
65
+ @raw_body = options
66
+ self
67
+ end
68
+
69
+ def clone_only_headers
70
+ self.class.new(self.scopeable).headers(self.headers)
71
+ end
72
+
73
+ # Half-level :)
74
+ # This is a swiss-army knife kind of method, extremely useful
75
+ def fetch(path, options = {})
76
+ scoped(self) do
77
+ @scopeable.build get(path, options)
78
+ end
79
+ end
80
+
81
+ # Low-level connection methods
82
+
83
+ def request(method, path, options = {})
84
+ options = options.dup
85
+
86
+ raw = raw? || options.delete(:raw)
87
+ params(options)
88
+ response = connection.send method, path, (@raw_body || @params), @headers
89
+ raw ? response : @scopeable.parse(response)
90
+ end
91
+
92
+ def get(path, options = {})
93
+ request(:get, path, options)
94
+ end
95
+
96
+ def post(path, options = {})
97
+ request(:post, path, options)
98
+ end
99
+
100
+ def patch(path, options = {})
101
+ request(:patch, path, options)
102
+ end
103
+
104
+ def put(path, options = {})
105
+ request(:put, path, options)
106
+ end
107
+
108
+ def delete(path, options = {})
109
+ request(:delete, path, options)
110
+ end
111
+
112
+ # Dynamic delegation of scopeable methods
113
+ def method_missing(method, *args, &block)
114
+ if @scopeable.respond_to?(method)
115
+ @scopeable.scoped(self) do
116
+ @scopeable.send(method, *args, &block)
117
+ end
118
+ else
119
+ super
120
+ end
121
+ end
122
+
123
+ end
124
+
125
+ end