active_rest_client 0.9.58

Sign up to get free protection for your applications and to get access to all the features.
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