active_rest_client 0.9.58

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +4 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +9 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +585 -0
  9. data/Rakefile +3 -0
  10. data/active_rest_client.gemspec +34 -0
  11. data/lib/active_rest_client.rb +23 -0
  12. data/lib/active_rest_client/base.rb +128 -0
  13. data/lib/active_rest_client/caching.rb +84 -0
  14. data/lib/active_rest_client/configuration.rb +69 -0
  15. data/lib/active_rest_client/connection.rb +76 -0
  16. data/lib/active_rest_client/connection_manager.rb +21 -0
  17. data/lib/active_rest_client/headers_list.rb +47 -0
  18. data/lib/active_rest_client/instrumentation.rb +62 -0
  19. data/lib/active_rest_client/lazy_association_loader.rb +95 -0
  20. data/lib/active_rest_client/lazy_loader.rb +23 -0
  21. data/lib/active_rest_client/logger.rb +67 -0
  22. data/lib/active_rest_client/mapping.rb +65 -0
  23. data/lib/active_rest_client/proxy_base.rb +143 -0
  24. data/lib/active_rest_client/recording.rb +24 -0
  25. data/lib/active_rest_client/request.rb +412 -0
  26. data/lib/active_rest_client/request_filtering.rb +52 -0
  27. data/lib/active_rest_client/result_iterator.rb +66 -0
  28. data/lib/active_rest_client/validation.rb +60 -0
  29. data/lib/active_rest_client/version.rb +3 -0
  30. data/spec/lib/base_spec.rb +245 -0
  31. data/spec/lib/caching_spec.rb +179 -0
  32. data/spec/lib/configuration_spec.rb +105 -0
  33. data/spec/lib/connection_manager_spec.rb +36 -0
  34. data/spec/lib/connection_spec.rb +73 -0
  35. data/spec/lib/headers_list_spec.rb +61 -0
  36. data/spec/lib/instrumentation_spec.rb +59 -0
  37. data/spec/lib/lazy_association_loader_spec.rb +118 -0
  38. data/spec/lib/lazy_loader_spec.rb +25 -0
  39. data/spec/lib/logger_spec.rb +63 -0
  40. data/spec/lib/mapping_spec.rb +48 -0
  41. data/spec/lib/proxy_spec.rb +154 -0
  42. data/spec/lib/recording_spec.rb +34 -0
  43. data/spec/lib/request_filtering_spec.rb +72 -0
  44. data/spec/lib/request_spec.rb +471 -0
  45. data/spec/lib/result_iterator_spec.rb +104 -0
  46. data/spec/lib/validation_spec.rb +113 -0
  47. data/spec/spec_helper.rb +22 -0
  48. metadata +265 -0
@@ -0,0 +1,47 @@
1
+ module ActiveRestClient
2
+ class HeadersList
3
+ STORE_MULTIPLE_VALUES = ["set-cookie"]
4
+ def initialize
5
+ @store = {}
6
+ end
7
+
8
+ def []=(key,value)
9
+ key = find_existing(key)
10
+ if STORE_MULTIPLE_VALUES.include?(key.downcase)
11
+ @store[key] ||= []
12
+ @store[key] << value
13
+ else
14
+ @store[key] = value
15
+ end
16
+ end
17
+
18
+ def [](key)
19
+ key = find_existing(key)
20
+ @store[key]
21
+ end
22
+
23
+ def each(split_multiple_headers = false)
24
+ @store.keys.each do |key|
25
+ value = @store[key]
26
+ if value.is_a?(Array) && split_multiple_headers
27
+ value.each do |inner_value|
28
+ yield(key, inner_value)
29
+ end
30
+ else
31
+ yield(key, value)
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def find_existing(key)
39
+ key_downcase = key.downcase
40
+ @store.keys.each do |found_key|
41
+ return found_key if found_key.downcase == key_downcase
42
+ end
43
+ key
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ module ActiveRestClient
2
+ class Instrumentation < ActiveSupport::LogSubscriber
3
+ def request_call(event)
4
+ self.class.time_spent += event.duration
5
+ self.class.calls_made += 1
6
+ name = '%s (%.1fms)' % [ActiveRestClient::NAME, event.duration]
7
+ ActiveRestClient::Logger.debug " \033[1;4;32m#{name}\033[0m #{event.payload[:name]}"
8
+ end
9
+
10
+ def self.time_spent=(value)
11
+ @@time_spent = value
12
+ end
13
+
14
+ def self.time_spent
15
+ @@time_spent ||= 0
16
+ end
17
+
18
+ def self.calls_made=(value)
19
+ @@calls_made = value
20
+ end
21
+
22
+ def self.calls_made
23
+ @@calls_made ||= 0
24
+ end
25
+
26
+ def self.reset
27
+ @@time_spent = 0
28
+ @@calls_made = 0
29
+ end
30
+
31
+ def logger
32
+ ActiveRestClient::Logger
33
+ end
34
+ end
35
+
36
+ module ControllerInstrumentation
37
+ extend ActiveSupport::Concern
38
+
39
+ protected
40
+
41
+ def append_info_to_payload(payload)
42
+ super
43
+ payload[:active_rest_client_time_spent] = ActiveRestClient::Instrumentation.time_spent
44
+ payload[:active_rest_client_calls_made] = ActiveRestClient::Instrumentation.calls_made
45
+ end
46
+
47
+ module ClassMethods
48
+ def log_process_action(payload)
49
+ messages, time_spent, calls_made = super, payload[:active_rest_client_time_spent], payload[:active_rest_client_calls_made]
50
+ messages << ("#{ActiveRestClient::NAME}: %.1fms for %d calls" % [time_spent.to_f, calls_made]) if calls_made
51
+ ActiveRestClient::Instrumentation.reset
52
+ messages
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ ActiveRestClient::Instrumentation.attach_to :active_rest_client
59
+
60
+ ActiveSupport.on_load(:action_controller) do
61
+ include ActiveRestClient::ControllerInstrumentation
62
+ end
@@ -0,0 +1,95 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+
3
+ module ActiveRestClient
4
+ class LazyAssociationLoader
5
+ include Enumerable
6
+
7
+ def initialize(name, value, request, options = {})
8
+ @name = name
9
+ @request = request
10
+ @object = nil
11
+ @options = options
12
+ if value.is_a? Array
13
+ @subloaders = value.map {|url| LazyAssociationLoader.new(name, url, request, options)}
14
+ elsif value.is_a?(Hash) && (value.has_key?("url") || value.has_key?(:url))
15
+ @url = (value["url"] || value[:url])
16
+ elsif value.is_a?(Hash) && (value.has_key?("href") || value.has_key?(:href)) # HAL
17
+ @url = (value["href"] || value[:href])
18
+ @_hal_attributes = HashWithIndifferentAccess.new(value)
19
+ elsif value.is_a?(Hash)
20
+ mapped = {}
21
+ value.each do |k,v|
22
+ mapped[k.to_sym] = LazyAssociationLoader.new(name, v, request, options)
23
+ end
24
+ @subloaders = mapped
25
+ # Need to also ensure that the hash/wrapped object is returned when the property is accessed
26
+ elsif value.is_a? String
27
+ @url = value
28
+ else
29
+ raise InvalidLazyAssociationContentException.new("Invalid content for #{@name}, expected Array, String or Hash containing 'url' key")
30
+ end
31
+ end
32
+
33
+ def _hal_attributes(key)
34
+ @_hal_attributes[key]
35
+ end
36
+
37
+ def size
38
+ if @subloaders
39
+ @subloaders.size
40
+ else
41
+ ensure_lazy_loaded
42
+ @object.size
43
+ end
44
+ end
45
+
46
+ def each
47
+ if @subloaders
48
+ if @subloaders.is_a? Array
49
+ @subloaders.each do |loader|
50
+ yield loader
51
+ end
52
+ elsif @subloaders.is_a? Hash
53
+ @subloaders.each do |key,value|
54
+ yield key, value
55
+ end
56
+ end
57
+ else
58
+ ensure_lazy_loaded
59
+ @object.each do |obj|
60
+ yield obj
61
+ end
62
+ end
63
+ end
64
+
65
+ def keys
66
+ @subloaders.keys
67
+ end
68
+
69
+ def method_missing(name, *args)
70
+ if @subloaders.is_a? Hash
71
+ return @subloaders[name.to_sym]
72
+ end
73
+ ensure_lazy_loaded
74
+ if @object
75
+ @object.send(name, *args)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def ensure_lazy_loaded
82
+ if @object.nil?
83
+ @request.method[:method] = :get
84
+ @request.method[:options][:url] = @url
85
+ @request.method[:options][:overriden_name] = @options[:overriden_name]
86
+ request = ActiveRestClient::Request.new(@request.method, @request.object)
87
+ request.url = request.forced_url = @url
88
+ @object = request.call
89
+ end
90
+ end
91
+ end
92
+
93
+ class InvalidLazyAssociationContentException < Exception ; end
94
+
95
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRestClient
2
+ class LazyLoader
3
+ def initialize(request, params = nil)
4
+ @request = request
5
+ @params = params
6
+ @result = nil
7
+ end
8
+
9
+ def method_missing(name, *args)
10
+ if @result.nil?
11
+ @result = @request.call(@params)
12
+ end
13
+ @result.send(name, *args)
14
+ end
15
+
16
+ def respond_to?(name)
17
+ if @result.nil?
18
+ @result = @request.call(@params)
19
+ end
20
+ @result.respond_to?(name)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ module ActiveRestClient
2
+ class Logger
3
+ @logfile = nil
4
+ @messages = []
5
+
6
+ def self.logfile=(value)
7
+ @logfile = value
8
+ end
9
+
10
+ def self.messages
11
+ @messages
12
+ end
13
+
14
+ def self.reset!
15
+ @logfile = nil
16
+ @messages = []
17
+ end
18
+
19
+ def self.debug(message)
20
+ if defined?(Rails) && Rails.respond_to?(:logger)
21
+ Rails.logger.debug(message)
22
+ elsif @logfile
23
+ File.open(@logfile, "a") do |f|
24
+ f << message
25
+ end
26
+ else
27
+ @messages << message
28
+ end
29
+ end
30
+
31
+ def self.info(message)
32
+ if defined?(Rails) && Rails.respond_to?(:logger)
33
+ Rails.logger.info(message)
34
+ elsif @logfile
35
+ File.open(@logfile, "a") do |f|
36
+ f << message
37
+ end
38
+ else
39
+ @messages << message
40
+ end
41
+ end
42
+
43
+ def self.warn(message)
44
+ if defined?(Rails) && Rails.respond_to?(:logger)
45
+ Rails.logger.warn(message)
46
+ elsif @logfile
47
+ File.open(@logfile, "a") do |f|
48
+ f << message
49
+ end
50
+ else
51
+ @messages << message
52
+ end
53
+ end
54
+
55
+ def self.error(message)
56
+ if defined?(Rails) && Rails.respond_to?(:logger)
57
+ Rails.logger.error(message)
58
+ elsif @logfile
59
+ File.open(@logfile, "a") do |f|
60
+ f << message
61
+ end
62
+ else
63
+ @messages << message
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,65 @@
1
+ module ActiveRestClient
2
+ module Mapping
3
+ module ClassMethods
4
+ def get(name, url, options = {})
5
+ _map_call(name, url:url, method: :get, options:options)
6
+ end
7
+
8
+ def put(name, url, options = {})
9
+ _map_call(name, url:url, method: :put, options:options)
10
+ end
11
+
12
+ def post(name, url, options = {})
13
+ _map_call(name, url:url, method: :post, options:options)
14
+ end
15
+
16
+ def delete(name, url, options = {})
17
+ _map_call(name, url:url, method: :delete, options:options)
18
+ end
19
+
20
+ def _map_call(name, details)
21
+ _calls[name] = {name:name}.merge(details)
22
+ _calls["lazy_#{name}".to_sym] = {name:name}.merge(details)
23
+ self.class.send(:define_method, name) do |options={}|
24
+ _call(name, options)
25
+ end
26
+ self.class.send(:define_method, "lazy_#{name}".to_sym) do |options={}|
27
+ _call("lazy_#{name}", options)
28
+ end
29
+ end
30
+
31
+ def _call(name, options)
32
+ mapped = _calls[name]
33
+ lazy_forced = false
34
+ if mapped.nil? && name.to_s[/^lazy_/]
35
+ mapped = _calls[name.to_s.gsub(/^lazy_/, '').to_sym]
36
+ lazy_forced = true
37
+ end
38
+ request = Request.new(mapped, self, options)
39
+ if lazy_load? || lazy_forced
40
+ ActiveRestClient::LazyLoader.new(request)
41
+ else
42
+ request.call
43
+ end
44
+ end
45
+
46
+ def _calls
47
+ @_calls
48
+ end
49
+
50
+ def _mapped_method(name)
51
+ _calls[name]
52
+ end
53
+
54
+ def inherited(subclass)
55
+ subclass.instance_variable_set(:@_calls, {})
56
+ end
57
+
58
+ end
59
+
60
+ def self.included(base)
61
+ base.extend(ClassMethods)
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,143 @@
1
+ module ActiveRestClient
2
+ class ProxyBase
3
+ cattr_accessor :mappings, :request, :original_handler
4
+ cattr_accessor :original_body, :original_get_params, :original_post_params, :original_url
5
+
6
+ module ClassMethods
7
+ def get(match, &block)
8
+ add_mapping(:get, match, block)
9
+ end
10
+
11
+ def post(match, &block)
12
+ add_mapping(:post, match, block)
13
+ end
14
+
15
+ def put(match, &block)
16
+ add_mapping(:put, match, block)
17
+ end
18
+
19
+ def delete(match, &block)
20
+ add_mapping(:delete, match, block)
21
+ end
22
+
23
+ def add_mapping(method_type, match, block)
24
+ @mappings ||= []
25
+
26
+ if match.is_a?(String) && (param_keys = match.scan(/:\w+/)) && param_keys.any?
27
+ param_keys.each do |key|
28
+ match.gsub!(key, "([^/]+)")
29
+ end
30
+ param_keys = param_keys.map {|k| k.gsub(":", "").to_sym}
31
+ match = Regexp.new(match)
32
+ end
33
+
34
+ @mappings << OpenStruct.new(http_method:method_type, match:match, block:block, param_keys:param_keys)
35
+ end
36
+
37
+ def body(value = nil)
38
+ @body = value if value
39
+ @body
40
+ end
41
+
42
+ def url(value = nil)
43
+ @url = value if value
44
+ @url
45
+ end
46
+
47
+ def get_params(value = nil)
48
+ @get_params = value if value
49
+ @get_params
50
+ end
51
+
52
+ def post_params(value = nil)
53
+ @post_params = value if value
54
+ @post_params
55
+ end
56
+
57
+ def params(value = nil)
58
+ @params = value if value
59
+ @params
60
+ end
61
+
62
+ def passthrough
63
+ rebuild_request
64
+ @original_handler.call(@request)
65
+ end
66
+
67
+ def translate(result, options = {})
68
+ result.headers["content-type"] = "application/hal+json"
69
+ result = OpenStruct.new(status:result.status, headers:result.headers, body:result.body)
70
+ obj = Oj.load(result.body)
71
+ result.body = yield obj
72
+ result
73
+ end
74
+
75
+ def rebuild_request
76
+ if @url != @original_url
77
+ @request.forced_url = @request.url = @url
78
+ end
79
+ if @body != @original_body
80
+ @request.body = @body
81
+ elsif @post_params != @original_post_params
82
+ @request.body = nil
83
+ @request.prepare_request_body(@post_params)
84
+ end
85
+ if @get_params != @original_get_params
86
+ @request.get_params = @get_params
87
+ @request.prepare_url
88
+ @request.append_get_parameters
89
+ end
90
+ end
91
+
92
+ def handle(request, &block)
93
+ @request = request
94
+ @original_handler = block
95
+
96
+ @original_body = request.body
97
+ @body = @original_body.dup
98
+
99
+ @original_get_params = request.get_params
100
+ @get_params = @original_get_params.dup
101
+
102
+ @original_post_params = request.post_params
103
+ @post_params = (@original_post_params || {}).dup
104
+
105
+ @original_url = request.url
106
+ @url = @original_url.dup
107
+
108
+ if mapping = find_mapping_for_current_request
109
+ self.class_eval(&mapping.block)
110
+ else
111
+ passthrough
112
+ end
113
+ end
114
+
115
+ def find_mapping_for_current_request
116
+ uri = URI.parse(@original_url)
117
+ @mappings ||= []
118
+ @params = {}
119
+ @mappings.each do |mapping|
120
+ match = mapping.match
121
+ if (match_data = uri.path.match(match)) && @request.http_method.to_sym == mapping.http_method
122
+ matches = match_data.to_a
123
+ matches.shift
124
+ matches.each_with_index do |value, index|
125
+ @params[mapping.param_keys[index]] = value
126
+ end
127
+ return mapping
128
+ end
129
+ end
130
+ nil
131
+ end
132
+
133
+ def render(body, status=200, content_type="application/javascript", headers={})
134
+ headers["Content-type"] = content_type
135
+ OpenStruct.new(body:body, status:status, headers:headers, proxied:true)
136
+ end
137
+ end
138
+
139
+ def self.inherited(base)
140
+ base.extend(ClassMethods)
141
+ end
142
+ end
143
+ end