flexirest 1.2.0
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.simplecov +4 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +37 -0
- data/CONTRIBUTING.md +62 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +846 -0
- data/Rakefile +13 -0
- data/doc/ActiveRestClient Internals.graffle +1236 -0
- data/doc/ActiveRestClient Internals.png +0 -0
- data/flexirest.gemspec +39 -0
- data/lib/flexirest.rb +25 -0
- data/lib/flexirest/base.rb +189 -0
- data/lib/flexirest/caching.rb +92 -0
- data/lib/flexirest/configuration.rb +209 -0
- data/lib/flexirest/connection.rb +103 -0
- data/lib/flexirest/connection_manager.rb +36 -0
- data/lib/flexirest/headers_list.rb +47 -0
- data/lib/flexirest/instrumentation.rb +62 -0
- data/lib/flexirest/lazy_association_loader.rb +97 -0
- data/lib/flexirest/lazy_loader.rb +23 -0
- data/lib/flexirest/logger.rb +67 -0
- data/lib/flexirest/mapping.rb +69 -0
- data/lib/flexirest/monkey_patching.rb +7 -0
- data/lib/flexirest/proxy_base.rb +193 -0
- data/lib/flexirest/recording.rb +24 -0
- data/lib/flexirest/request.rb +573 -0
- data/lib/flexirest/request_delegator.rb +44 -0
- data/lib/flexirest/request_filtering.rb +62 -0
- data/lib/flexirest/result_iterator.rb +85 -0
- data/lib/flexirest/validation.rb +60 -0
- data/lib/flexirest/version.rb +3 -0
- data/spec/lib/base_spec.rb +389 -0
- data/spec/lib/caching_spec.rb +217 -0
- data/spec/lib/configuration_spec.rb +234 -0
- data/spec/lib/connection_manager_spec.rb +43 -0
- data/spec/lib/connection_spec.rb +159 -0
- data/spec/lib/headers_list_spec.rb +61 -0
- data/spec/lib/instrumentation_spec.rb +58 -0
- data/spec/lib/lazy_association_loader_spec.rb +135 -0
- data/spec/lib/lazy_loader_spec.rb +25 -0
- data/spec/lib/logger_spec.rb +63 -0
- data/spec/lib/mapping_spec.rb +52 -0
- data/spec/lib/proxy_spec.rb +189 -0
- data/spec/lib/recording_spec.rb +34 -0
- data/spec/lib/request_filtering_spec.rb +84 -0
- data/spec/lib/request_spec.rb +711 -0
- data/spec/lib/result_iterator_spec.rb +140 -0
- data/spec/lib/validation_spec.rb +113 -0
- data/spec/lib/xml_spec.rb +74 -0
- data/spec/spec_helper.rb +88 -0
- metadata +347 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Flexirest
|
2
|
+
class RequestDelegator < Delegator
|
3
|
+
def initialize(obj)
|
4
|
+
super
|
5
|
+
@delegate_obj = obj
|
6
|
+
end
|
7
|
+
|
8
|
+
def __getobj__
|
9
|
+
@delegate_obj
|
10
|
+
end
|
11
|
+
|
12
|
+
def __setobj__(obj)
|
13
|
+
@delegate_obj = obj
|
14
|
+
end
|
15
|
+
|
16
|
+
def class
|
17
|
+
@delegate_obj.class
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(name, *args, &block)
|
21
|
+
# Handles issue with private method 'test' on base Ruby Object
|
22
|
+
return @delegate_obj.test if name.to_sym == :test
|
23
|
+
|
24
|
+
# Forward request to delegate
|
25
|
+
@delegate_obj.send(name, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def kind_of?(obj)
|
29
|
+
@delegate_obj.kind_of?(obj)
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_a?(obj)
|
33
|
+
@delegate_obj.is_a?(obj)
|
34
|
+
end
|
35
|
+
|
36
|
+
def instance_of?(obj)
|
37
|
+
@delegate_obj.instance_of?(obj)
|
38
|
+
end
|
39
|
+
|
40
|
+
def _delegate?
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Flexirest
|
2
|
+
module RequestFiltering
|
3
|
+
module ClassMethods
|
4
|
+
def before_request(method_name = nil, &block)
|
5
|
+
@before_filters ||= []
|
6
|
+
if block
|
7
|
+
@before_filters << block
|
8
|
+
elsif method_name
|
9
|
+
@before_filters << method_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_request(method_name = nil, &block)
|
14
|
+
@after_filters ||= []
|
15
|
+
if block
|
16
|
+
@after_filters << block
|
17
|
+
elsif method_name
|
18
|
+
@after_filters << method_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _filter_request(type, name, param)
|
23
|
+
_handle_super_class_filters(type, name, param)
|
24
|
+
filters = (type == :before ? @before_filters : @after_filters)
|
25
|
+
filters ||= []
|
26
|
+
filters.each do |filter|
|
27
|
+
if filter.is_a? Symbol
|
28
|
+
if self.respond_to?(filter)
|
29
|
+
self.send(filter, name, param)
|
30
|
+
else
|
31
|
+
instance = self.new
|
32
|
+
instance.send(filter, name, param)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
filter.call(name, param)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def _handle_super_class_filters(type, name, request)
|
41
|
+
@parents ||= []
|
42
|
+
@parents.each do |parent|
|
43
|
+
parent._filter_request(type, name, request)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def _parents
|
48
|
+
@parents ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
def inherited(subclass)
|
52
|
+
subclass._parents << self
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.included(base)
|
58
|
+
base.extend(ClassMethods)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Flexirest
|
2
|
+
class ResultIterator
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_accessor :_status
|
6
|
+
attr_reader :items, :_headers
|
7
|
+
|
8
|
+
def initialize(response = nil)
|
9
|
+
@_status = response.try :status
|
10
|
+
@_headers = response.try :response_headers
|
11
|
+
@items = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(item)
|
15
|
+
@items << item
|
16
|
+
end
|
17
|
+
|
18
|
+
def size
|
19
|
+
@items.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def index(value)
|
23
|
+
@items.index(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
size == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def reverse
|
31
|
+
@reversed_items ||= @items.reverse
|
32
|
+
end
|
33
|
+
|
34
|
+
def each
|
35
|
+
@items.each do |el|
|
36
|
+
yield el
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def last
|
41
|
+
@items.last
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](key)
|
45
|
+
@items[key]
|
46
|
+
end
|
47
|
+
|
48
|
+
def shuffle
|
49
|
+
@items = @items.shuffle
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def parallelise(method=nil)
|
54
|
+
collected_responses = []
|
55
|
+
threads = []
|
56
|
+
@items.each do |item|
|
57
|
+
threads << Thread.new do
|
58
|
+
ret = item.send(method) if method
|
59
|
+
ret = yield(item) if block_given?
|
60
|
+
Thread.current[:response] = ret
|
61
|
+
end
|
62
|
+
end
|
63
|
+
threads.each do |t|
|
64
|
+
t.join
|
65
|
+
collected_responses << t[:response]
|
66
|
+
end
|
67
|
+
collected_responses
|
68
|
+
end
|
69
|
+
|
70
|
+
def paginate(options = {})
|
71
|
+
raise WillPaginateNotAvailableException.new unless Object.constants.include?(:WillPaginate)
|
72
|
+
|
73
|
+
page = options[:page] || 1
|
74
|
+
per_page = options[:per_page] || WillPaginate.per_page
|
75
|
+
total = options[:total_entries] || @items.length
|
76
|
+
|
77
|
+
WillPaginate::Collection.create(page, per_page, total) do |pager|
|
78
|
+
pager.replace @items[pager.offset, pager.per_page].to_a
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class WillPaginateNotAvailableException < StandardError ; end
|
85
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Flexirest
|
2
|
+
module Validation
|
3
|
+
module ClassMethods
|
4
|
+
def validates(field_name, options={}, &block)
|
5
|
+
@_validations ||= []
|
6
|
+
@_validations << {field_name:field_name, options:options, block:block}
|
7
|
+
end
|
8
|
+
|
9
|
+
def _validations
|
10
|
+
@_validations ||= []
|
11
|
+
@_validations
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
@errors = Hash.new {|h,k| h[k] = []}
|
21
|
+
self.class._validations.each do |validation|
|
22
|
+
value = self.send(validation[:field_name])
|
23
|
+
validation[:options].each do |type, options|
|
24
|
+
if type == :presence
|
25
|
+
if value.nil?
|
26
|
+
@errors[validation[:field_name]] << "must be present"
|
27
|
+
end
|
28
|
+
elsif type == :length
|
29
|
+
if options[:within]
|
30
|
+
@errors[validation[:field_name]] << "must be within range #{options[:within]}" unless options[:within].include?(value.to_s.length )
|
31
|
+
end
|
32
|
+
if options[:minimum]
|
33
|
+
@errors[validation[:field_name]] << "must be at least #{options[:minimum]} characters long" unless value.to_s.length >= options[:minimum]
|
34
|
+
end
|
35
|
+
if options[:maximum]
|
36
|
+
@errors[validation[:field_name]] << "must be no more than #{options[:minimum]} characters long" unless value.to_s.length <= options[:maximum]
|
37
|
+
end
|
38
|
+
elsif type == :numericality
|
39
|
+
numeric = (true if Float(value) rescue false)
|
40
|
+
@errors[validation[:field_name]] << "must be numeric" unless numeric
|
41
|
+
elsif type == :minimum && !value.nil?
|
42
|
+
@errors[validation[:field_name]] << "must be at least #{options}" unless value.to_f >= options.to_f
|
43
|
+
elsif type == :maximum && !value.nil?
|
44
|
+
@errors[validation[:field_name]] << "must be no more than #{options}" unless value.to_f <= options.to_f
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if validation[:block]
|
48
|
+
validation[:block].call(self, validation[:field_name], value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@errors.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def _errors
|
55
|
+
@errors ||= Hash.new {|h,k| h[k] = []}
|
56
|
+
@errors
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,389 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class EmptyExample < Flexirest::Base
|
4
|
+
whiny_missing true
|
5
|
+
end
|
6
|
+
|
7
|
+
class TranslatorExample
|
8
|
+
def self.all(object)
|
9
|
+
ret = {}
|
10
|
+
ret["first_name"] = object["name"]
|
11
|
+
ret
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class AlteringClientExample < Flexirest::Base
|
16
|
+
translator TranslatorExample
|
17
|
+
base_url "http://www.example.com"
|
18
|
+
|
19
|
+
get :all, "/all", fake:"{\"name\":\"Billy\"}"
|
20
|
+
get :list, "/list", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
|
21
|
+
get :iterate, "/iterate", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
|
22
|
+
get :find, "/find/:id"
|
23
|
+
end
|
24
|
+
|
25
|
+
class RecordResponseExample < Flexirest::Base
|
26
|
+
base_url "http://www.example.com"
|
27
|
+
|
28
|
+
record_response do |url, response|
|
29
|
+
raise Exception.new("#{url}|#{response.body}")
|
30
|
+
end
|
31
|
+
|
32
|
+
get :all, "/all"
|
33
|
+
end
|
34
|
+
|
35
|
+
class NonHostnameBaseUrlExample < Flexirest::Base
|
36
|
+
base_url "http://www.example.com/v1/"
|
37
|
+
get :all, "/all"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe Flexirest::Base do
|
41
|
+
it 'should instantiate a new descendant' do
|
42
|
+
expect{EmptyExample.new}.to_not raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should not instantiate a new base class" do
|
46
|
+
expect{Flexirest::Base.new}.to raise_error(Exception)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should save attributes passed in constructor" do
|
50
|
+
client = EmptyExample.new(:test => "Something")
|
51
|
+
expect(client._attributes[:test]).to be_a(String)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow attribute reading using missing method names" do
|
55
|
+
client = EmptyExample.new(:test => "Something")
|
56
|
+
expect(client.test).to eq("Something")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should allow attribute reading using [] array notation" do
|
60
|
+
client = EmptyExample.new(:test => "Something")
|
61
|
+
expect(client["test"]).to eq("Something")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "allows iteration over attributes using each" do
|
65
|
+
client = AlteringClientExample.iterate
|
66
|
+
expect(client).to be_respond_to(:each)
|
67
|
+
keys = []
|
68
|
+
values = []
|
69
|
+
client.each do |key, value|
|
70
|
+
keys << key ; values << value
|
71
|
+
end
|
72
|
+
expect(keys).to eq(%w{name country}.map(&:to_sym))
|
73
|
+
expect(values).to eq(["Billy", "United Kingdom"])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should automatically parse ISO 8601 format date and time" do
|
77
|
+
t = Time.now
|
78
|
+
client = EmptyExample.new(:test => t.iso8601)
|
79
|
+
expect(client["test"]).to be_an_instance_of(DateTime)
|
80
|
+
expect(client["test"].to_s).to eq(t.to_datetime.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should automatically parse ISO 8601 format date and time with milliseconds" do
|
84
|
+
t = Time.now
|
85
|
+
client = EmptyExample.new(:test => t.iso8601(3))
|
86
|
+
expect(client["test"]).to be_an_instance_of(DateTime)
|
87
|
+
expect(client["test"].to_s).to eq(t.to_datetime.to_s)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should automatically parse ISO 8601 format dates" do
|
91
|
+
d = Date.today
|
92
|
+
client = EmptyExample.new(:test => d.iso8601)
|
93
|
+
expect(client["test"]).to be_an_instance_of(Date)
|
94
|
+
expect(client["test"]).to eq(d)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should store attributes set using missing method names and mark them as dirty" do
|
98
|
+
client = EmptyExample.new()
|
99
|
+
client.test = "Something"
|
100
|
+
expect(client.test.to_s).to eq("Something")
|
101
|
+
expect(client).to be_dirty
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should store attribute set using []= array notation and mark them as dirty" do
|
105
|
+
client = EmptyExample.new()
|
106
|
+
client["test"] = "Something"
|
107
|
+
expect(client["test"].to_s).to eq("Something")
|
108
|
+
expect(client).to be_dirty
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should overwrite attributes already set and mark them as dirty" do
|
112
|
+
client = EmptyExample.new(:hello => "World")
|
113
|
+
client._clean!
|
114
|
+
expect(client).to_not be_dirty
|
115
|
+
|
116
|
+
client.hello = "Everybody"
|
117
|
+
expect(client).to be_dirty
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should respond_to? attributes defined in the response' do
|
121
|
+
client = EmptyExample.new(:hello => "World")
|
122
|
+
expect(client.respond_to?(:hello)).to be_truthy
|
123
|
+
expect(client.respond_to?(:world)).to be_falsey
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should save the base URL for the API server" do
|
127
|
+
class BaseExample < Flexirest::Base
|
128
|
+
base_url "https://www.example.com/api/v1"
|
129
|
+
end
|
130
|
+
expect(BaseExample.base_url).to eq("https://www.example.com/api/v1")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should allow changing the base_url while running" do
|
134
|
+
class OutsideBaseExample < Flexirest::Base ; end
|
135
|
+
|
136
|
+
Flexirest::Base.base_url = "https://www.example.com/api/v1"
|
137
|
+
expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v1")
|
138
|
+
|
139
|
+
Flexirest::Base.base_url = "https://www.example.com/api/v2"
|
140
|
+
expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v2")
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should include the Mapping module" do
|
144
|
+
expect(EmptyExample).to respond_to(:_calls)
|
145
|
+
expect(EmptyExample).to_not respond_to(:_non_existant)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should be able to easily clean all attributes" do
|
149
|
+
client = EmptyExample.new(hello:"World", goodbye:"Everyone")
|
150
|
+
expect(client).to be_dirty
|
151
|
+
client._clean!
|
152
|
+
expect(client).to_not be_dirty
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should not overly pollute the instance method namespace to reduce chances of clashing (<10 instance methods)" do
|
156
|
+
instance_methods = EmptyExample.instance_methods - Object.methods
|
157
|
+
instance_methods = instance_methods - instance_methods.grep(/^_/)
|
158
|
+
expect(instance_methods.size).to be < 10
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should raise an exception for missing attributes if whiny_missing is enabled" do
|
162
|
+
expect{EmptyExample.new.first_name}.to raise_error(Flexirest::NoAttributeException)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should be able to lazy instantiate an object from a prefixed lazy_ method call" do
|
166
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
167
|
+
example = AlteringClientExample.lazy_find(1)
|
168
|
+
expect(example).to be_an_instance_of(Flexirest::LazyLoader)
|
169
|
+
expect(example.first_name).to eq("Billy")
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should be able to lazy instantiate an object from a prefixed lazy_ method call from an instance" do
|
173
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
174
|
+
example = AlteringClientExample.new.lazy_find(1)
|
175
|
+
expect(example).to be_an_instance_of(Flexirest::LazyLoader)
|
176
|
+
expect(example.first_name).to eq("Billy")
|
177
|
+
end
|
178
|
+
|
179
|
+
context "#inspect output" do
|
180
|
+
it "displays a nice version" do
|
181
|
+
object = EmptyExample.new(id: 1, name: "John Smith")
|
182
|
+
expect(object.inspect).to match(/#<EmptyExample id: 1, name: "John Smith"/)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "shows dirty attributes as a list of names at the end" do
|
186
|
+
object = EmptyExample.new(id: 1, name: "John Smith")
|
187
|
+
expect(object.inspect).to match(/#<EmptyExample id: 1, name: "John Smith" \(unsaved: id, name\)/)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "doesn't show an empty list of dirty attributes" do
|
191
|
+
object = EmptyExample.new(id: 1, name: "John Smith")
|
192
|
+
object.instance_variable_set(:@dirty_attributes, Set.new)
|
193
|
+
expect(object.inspect).to_not match(/\(unsaved: id, name\)/)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "shows dates in a nice format" do
|
197
|
+
object = EmptyExample.new(dob: Time.new(2015, 01, 02, 03, 04, 05))
|
198
|
+
expect(object.inspect).to match(/#<EmptyExample dob: "2015\-01\-02 03:04:05"/)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "shows the etag if one is set" do
|
202
|
+
object = EmptyExample.new(id: 1)
|
203
|
+
object.instance_variable_set(:@_etag, "sample_etag")
|
204
|
+
expect(object.inspect).to match(/#<EmptyExample id: 1, ETag: sample_etag/)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "shows the HTTP status code if one is set" do
|
208
|
+
object = EmptyExample.new(id: 1)
|
209
|
+
object.instance_variable_set(:@_status, 200)
|
210
|
+
expect(object.inspect).to match(/#<EmptyExample id: 1, Status: 200/)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "shows [uninitialized] for new objects" do
|
214
|
+
object = EmptyExample.new
|
215
|
+
expect(object.inspect).to match(/#<EmptyExample \[uninitialized\]/)
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
context "accepts a Translator to reformat JSON" do
|
221
|
+
it "should log a deprecation warning when using a translator" do
|
222
|
+
expect(Flexirest::Logger).to receive(:warn) do |message|
|
223
|
+
expect(message).to start_with("DEPRECATION")
|
224
|
+
end
|
225
|
+
Proc.new do
|
226
|
+
class DummyExample < Flexirest::Base
|
227
|
+
translator TranslatorExample
|
228
|
+
end
|
229
|
+
end.call
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should call Translator#method when calling the mapped method if it responds to it" do
|
233
|
+
expect(TranslatorExample).to receive(:all).with(an_instance_of(Hash)).and_return({})
|
234
|
+
AlteringClientExample.all
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should not raise errors when calling Translator#method if it does not respond to it" do
|
238
|
+
expect {AlteringClientExample.list}.to_not raise_error
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should translate JSON returned through the Translator" do
|
242
|
+
ret = AlteringClientExample.all
|
243
|
+
expect(ret.first_name).to eq("Billy")
|
244
|
+
expect(ret.name).to be_nil
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should return original JSON for items that aren't handled by the Translator" do
|
248
|
+
ret = AlteringClientExample.list
|
249
|
+
expect(ret.name).to eq("Billy")
|
250
|
+
expect(ret.first_name).to be_nil
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "directly call a URL, rather than via a mapped method" do
|
255
|
+
it "should be able to directly call a URL" do
|
256
|
+
expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
257
|
+
EmptyExample._request("http://api.example.com/")
|
258
|
+
end
|
259
|
+
|
260
|
+
it "runs filters as usual" do
|
261
|
+
expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
262
|
+
expect(EmptyExample).to receive(:_filter_request).with(any_args).exactly(2).times
|
263
|
+
EmptyExample._request("http://api.example.com/")
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should make an HTTP request" do
|
267
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
268
|
+
EmptyExample._request("http://api.example.com/")
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should make an HTTP request including the path in the base_url" do
|
272
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/v1/all', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
273
|
+
NonHostnameBaseUrlExample.all
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should map the response from the directly called URL in the normal way" do
|
277
|
+
expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
278
|
+
example = EmptyExample._request("http://api.example.com/")
|
279
|
+
expect(example.first_name).to eq("Billy")
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
|
283
|
+
response_body = "This is another non-JSON string"
|
284
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response_body)))
|
285
|
+
expect(EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})).to eq(response_body)
|
286
|
+
end
|
287
|
+
|
288
|
+
context "Simulating Faraday connection in_parallel" do
|
289
|
+
it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
|
290
|
+
response_body = "This is another non-JSON string"
|
291
|
+
response = ::FaradayResponseMock.new(
|
292
|
+
OpenStruct.new(status:200, response_headers:{}, body:response_body),
|
293
|
+
false)
|
294
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(response)
|
295
|
+
result = EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})
|
296
|
+
|
297
|
+
expect(result).to eq(nil)
|
298
|
+
|
299
|
+
response.finish
|
300
|
+
expect(result).to eq(response_body)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should cache plain requests separately" do
|
305
|
+
perform_caching = EmptyExample.perform_caching
|
306
|
+
cache_store = EmptyExample.cache_store
|
307
|
+
begin
|
308
|
+
response = "This is a non-JSON string"
|
309
|
+
other_response = "This is another non-JSON string"
|
310
|
+
allow_any_instance_of(Flexirest::Connection).to receive(:get) do |instance, url, others|
|
311
|
+
if url == "/?test=1"
|
312
|
+
::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response))
|
313
|
+
else
|
314
|
+
::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:other_response))
|
315
|
+
end
|
316
|
+
end
|
317
|
+
EmptyExample.perform_caching = true
|
318
|
+
EmptyExample.cache_store = TestCacheStore.new
|
319
|
+
expect(EmptyExample._plain_request("http://api.example.com/?test=1")).to eq(response)
|
320
|
+
expect(EmptyExample._plain_request("http://api.example.com/?test=2")).to eq(other_response)
|
321
|
+
ensure
|
322
|
+
EmptyExample.perform_caching = perform_caching
|
323
|
+
EmptyExample.cache_store = cache_store
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should be able to lazy load a direct URL request" do
|
328
|
+
expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
329
|
+
example = EmptyExample._lazy_request("http://api.example.com/")
|
330
|
+
expect(example).to be_an_instance_of(Flexirest::LazyLoader)
|
331
|
+
expect(example.first_name).to eq("Billy")
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should be able to specify a method and parameters for the call" do
|
335
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
336
|
+
EmptyExample._request("http://api.example.com/", :post, {id:1234})
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should be able to use mapped methods to create a request to pass in to _lazy_request" do
|
340
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
341
|
+
request = AlteringClientExample._request_for(:find, :id => 1)
|
342
|
+
example = AlteringClientExample._lazy_request(request)
|
343
|
+
expect(example.first_name).to eq("Billy")
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context "Recording a response" do
|
348
|
+
it "calls back to the record_response callback with the url and response body" do
|
349
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"Hello world")))
|
350
|
+
expect{RecordResponseExample.all}.to raise_error(Exception, "/all|Hello world")
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "JSON output" do
|
355
|
+
let(:student1) { EmptyExample.new(name:"John Smith", age:31) }
|
356
|
+
let(:student2) { EmptyExample.new(name:"Bob Brown", age:29) }
|
357
|
+
let(:location) { EmptyExample.new(place:"Room 1408") }
|
358
|
+
let(:lazy) { Laz }
|
359
|
+
let(:object) { EmptyExample.new(name:"Programming 101", location:location, students:[student1, student2]) }
|
360
|
+
let(:json_parsed_object) { MultiJson.load(object.to_json) }
|
361
|
+
|
362
|
+
it "should be able to export to valid json" do
|
363
|
+
expect(object.to_json).to_not be_blank
|
364
|
+
expect{MultiJson.load(object.to_json)}.to_not raise_error
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should not be using Object's #to_json method" do
|
368
|
+
expect(json_parsed_object["dirty_attributes"]).to be_nil
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should recursively convert nested objects" do
|
372
|
+
expect(json_parsed_object["location"]["place"]).to eq(location.place)
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should include arrayed objects" do
|
376
|
+
expect(json_parsed_object["students"]).to be_an_instance_of(Array)
|
377
|
+
expect(json_parsed_object["students"].size).to eq(2)
|
378
|
+
expect(json_parsed_object["students"].first["name"]).to eq(student1.name)
|
379
|
+
expect(json_parsed_object["students"].second["name"]).to eq(student2.name)
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should set integers as a native JSON type" do
|
383
|
+
expect(json_parsed_object["students"].first["age"]).to eq(student1.age)
|
384
|
+
expect(json_parsed_object["students"].second["age"]).to eq(student2.age)
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|