ddy_remote_resource 0.4.2
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 +22 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +182 -0
- data/Rakefile +7 -0
- data/lib/extensions/ethon/easy/queryable.rb +36 -0
- data/lib/remote_resource.rb +64 -0
- data/lib/remote_resource/base.rb +126 -0
- data/lib/remote_resource/builder.rb +53 -0
- data/lib/remote_resource/collection.rb +31 -0
- data/lib/remote_resource/connection.rb +24 -0
- data/lib/remote_resource/connection_options.rb +41 -0
- data/lib/remote_resource/http_errors.rb +33 -0
- data/lib/remote_resource/querying/finder_methods.rb +34 -0
- data/lib/remote_resource/querying/persistence_methods.rb +38 -0
- data/lib/remote_resource/request.rb +106 -0
- data/lib/remote_resource/response.rb +69 -0
- data/lib/remote_resource/response_handeling.rb +48 -0
- data/lib/remote_resource/rest.rb +29 -0
- data/lib/remote_resource/url_naming.rb +34 -0
- data/lib/remote_resource/url_naming_determination.rb +39 -0
- data/lib/remote_resource/version.rb +3 -0
- data/remote_resource.gemspec +32 -0
- data/spec/lib/extensions/ethon/easy/queryable_spec.rb +135 -0
- data/spec/lib/remote_resource/base_spec.rb +388 -0
- data/spec/lib/remote_resource/builder_spec.rb +245 -0
- data/spec/lib/remote_resource/collection_spec.rb +148 -0
- data/spec/lib/remote_resource/connection_options_spec.rb +124 -0
- data/spec/lib/remote_resource/connection_spec.rb +61 -0
- data/spec/lib/remote_resource/querying/finder_methods_spec.rb +105 -0
- data/spec/lib/remote_resource/querying/persistence_methods_spec.rb +174 -0
- data/spec/lib/remote_resource/request_spec.rb +594 -0
- data/spec/lib/remote_resource/response_spec.rb +196 -0
- data/spec/lib/remote_resource/rest_spec.rb +98 -0
- data/spec/lib/remote_resource/url_naming_determination_spec.rb +225 -0
- data/spec/lib/remote_resource/url_naming_spec.rb +72 -0
- data/spec/lib/remote_resource/version_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- metadata +242 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module RemoteResource
|
2
|
+
module REST
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
ACTIONS = [:get, :put, :patch, :post]
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
RemoteResource::REST::ACTIONS.each do |action|
|
10
|
+
define_method action do |*args|
|
11
|
+
attributes = args[0] || {}
|
12
|
+
connection_options = args[1] || {}
|
13
|
+
|
14
|
+
RemoteResource::Request.new(self, action, attributes, connection_options).perform
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RemoteResource::REST::ACTIONS.each do |action|
|
20
|
+
define_method action do |*args|
|
21
|
+
attributes = args[0] || {}
|
22
|
+
connection_options = args[1] || {}
|
23
|
+
|
24
|
+
RemoteResource::Request.new(self, action, attributes, connection_options).perform
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RemoteResource
|
2
|
+
module UrlNaming
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :site, :version, :path_prefix, :path_postfix, :collection, :collection_name, instance_accessor: false
|
7
|
+
|
8
|
+
self.collection = false
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def app_host(app, env = 'development')
|
14
|
+
CONFIG[env.to_sym][:apps][app.to_sym]
|
15
|
+
end
|
16
|
+
|
17
|
+
def base_url
|
18
|
+
determined_url_naming.base_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def use_relative_model_naming?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def determined_url_naming
|
28
|
+
RemoteResource::UrlNamingDetermination.new self
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module RemoteResource
|
2
|
+
class UrlNamingDetermination
|
3
|
+
|
4
|
+
attr_reader :resource_klass, :connection_options
|
5
|
+
|
6
|
+
def initialize(resource_klass, connection_options = {})
|
7
|
+
@resource_klass = resource_klass
|
8
|
+
@connection_options = connection_options
|
9
|
+
end
|
10
|
+
|
11
|
+
def base_url(id = nil)
|
12
|
+
site = connection_options.fetch(:site, resource_klass.site)
|
13
|
+
version = connection_options.fetch(:version, resource_klass.version)
|
14
|
+
path_prefix = connection_options.fetch(:path_prefix, resource_klass.path_prefix)
|
15
|
+
path_postfix = connection_options.fetch(:path_postfix, resource_klass.path_postfix)
|
16
|
+
|
17
|
+
id = "/#{id}" if id.present?
|
18
|
+
|
19
|
+
"#{site}#{version.presence}#{path_prefix.presence}/#{url_safe_relative_name}#{id}#{path_postfix.presence}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def url_safe_relative_name
|
23
|
+
collection = connection_options.fetch(:collection, resource_klass.collection)
|
24
|
+
|
25
|
+
if collection
|
26
|
+
relative_name.underscore.downcase.pluralize
|
27
|
+
else
|
28
|
+
relative_name.underscore.downcase
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def relative_name
|
33
|
+
collection_name = connection_options.fetch(:collection_name, resource_klass.collection_name)
|
34
|
+
|
35
|
+
collection_name.to_s.presence || resource_klass.name.to_s.demodulize
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'remote_resource/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'ddy_remote_resource'
|
8
|
+
spec.version = RemoteResource::VERSION
|
9
|
+
spec.authors = ["Jan van der Pas"]
|
10
|
+
spec.email = ["jvanderpas@digidentity.eu"]
|
11
|
+
spec.summary = %q{RemoteResource, a gem to use resources with REST services.}
|
12
|
+
spec.description = %q{RemoteResource, a gem to use resources with REST services. A replacement for ActiveResource gem.}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'pry'
|
25
|
+
|
26
|
+
spec.add_runtime_dependency 'activesupport'
|
27
|
+
spec.add_runtime_dependency 'activemodel'
|
28
|
+
spec.add_runtime_dependency 'virtus'
|
29
|
+
spec.add_runtime_dependency 'mime-types', '>= 1.16'
|
30
|
+
spec.add_runtime_dependency 'ethon', '~> 0.7.1'
|
31
|
+
spec.add_runtime_dependency 'typhoeus'
|
32
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'ethon'
|
2
|
+
require_relative '../../../../../lib/extensions/ethon/easy/queryable'
|
3
|
+
|
4
|
+
describe Ethon::Easy::Queryable do
|
5
|
+
|
6
|
+
let(:hash) { {} }
|
7
|
+
let!(:easy) { Ethon::Easy.new }
|
8
|
+
let(:params) { Ethon::Easy::Params.new(easy, hash) }
|
9
|
+
|
10
|
+
describe "#build_query_pairs" do
|
11
|
+
let(:pairs) { params.method(:build_query_pairs).call(hash) }
|
12
|
+
|
13
|
+
context "when params is empty" do
|
14
|
+
it "returns empty array" do
|
15
|
+
expect(pairs).to eq([])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when params is string" do
|
20
|
+
let(:hash) { "{a: 1}" }
|
21
|
+
|
22
|
+
it "wraps it in an array" do
|
23
|
+
expect(pairs).to eq([hash])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when params is simple hash" do
|
28
|
+
let(:hash) { {:a => 1, :b => 2} }
|
29
|
+
|
30
|
+
it "transforms" do
|
31
|
+
expect(pairs).to include([:a, 1])
|
32
|
+
expect(pairs).to include([:b, 2])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when params is a nested hash" do
|
37
|
+
let(:hash) { {:a => 1, :b => {:c => 2}} }
|
38
|
+
|
39
|
+
it "transforms" do
|
40
|
+
expect(pairs).to include([:a, 1])
|
41
|
+
expect(pairs).to include(["b[c]", 2])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when params contains an array" do
|
46
|
+
let(:hash) { {:a => 1, :b => [2, 3]} }
|
47
|
+
|
48
|
+
it "transforms" do
|
49
|
+
expect(pairs).to include([:a, 1])
|
50
|
+
expect(pairs).to include(["b[]", 2])
|
51
|
+
expect(pairs).to include(["b[]", 3])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when params contains something nested in an array" do
|
56
|
+
context "when string" do
|
57
|
+
let(:hash) { {:a => {:b => ["hello", "world"]}} }
|
58
|
+
|
59
|
+
it "transforms" do
|
60
|
+
expect(pairs).to eq([["a[b][]", "hello"], ["a[b][]", "world"]])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when hash" do
|
65
|
+
let(:hash) { {:a => {:b => [{:c =>1}, {:d => 2}]}} }
|
66
|
+
|
67
|
+
it "transforms" do
|
68
|
+
expect(pairs).to eq([["a[b][][c]", 1], ["a[b][][d]", 2]])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when file" do
|
73
|
+
let(:file) { File.open("spec/spec_helper.rb") }
|
74
|
+
let(:file_info) { params.method(:file_info).call(file) }
|
75
|
+
let(:hash) { {:a => {:b => [file]}} }
|
76
|
+
let(:mime_type) { file_info[1] }
|
77
|
+
|
78
|
+
it "transforms" do
|
79
|
+
expect(pairs).to eq([["a[b][]", file_info]])
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when MIME" do
|
83
|
+
context "when mime type" do
|
84
|
+
it "sets mime type to text" do
|
85
|
+
expect(mime_type).to eq("application/x-ruby")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when no mime type" do
|
90
|
+
let(:file) { Tempfile.new("fubar") }
|
91
|
+
|
92
|
+
it "sets mime type to default application/octet-stream" do
|
93
|
+
Object.send(:remove_const, :MIME)
|
94
|
+
expect(mime_type).to eq("application/octet-stream")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when no MIME" do
|
100
|
+
it "sets mime type to default application/octet-stream" do
|
101
|
+
expect(mime_type).to eq("application/octet-stream")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
context "when params contains file" do
|
109
|
+
let(:file) { Tempfile.new("fubar") }
|
110
|
+
let(:file_info) { params.method(:file_info).call(file) }
|
111
|
+
let(:hash) { {:a => 1, :b => file} }
|
112
|
+
|
113
|
+
it "transforms" do
|
114
|
+
expect(pairs).to include([:a, 1])
|
115
|
+
expect(pairs).to include([:b, file_info])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when params key contains a null byte" do
|
120
|
+
let(:hash) { {:a => "1\0" } }
|
121
|
+
|
122
|
+
it "preserves" do
|
123
|
+
expect(pairs).to eq([[:a, "1\0"]])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "when params value contains a null byte" do
|
128
|
+
let(:hash) { {"a\0" => 1 } }
|
129
|
+
|
130
|
+
it "preserves" do
|
131
|
+
expect(pairs).to eq([["a\0", 1]])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,388 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteResource::Base do
|
4
|
+
|
5
|
+
module RemoteResource
|
6
|
+
class Dummy
|
7
|
+
include RemoteResource::Base
|
8
|
+
|
9
|
+
self.site = 'https://foobar.com'
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:dummy_class) { RemoteResource::Dummy }
|
15
|
+
let(:dummy) { dummy_class.new }
|
16
|
+
|
17
|
+
specify { expect(described_class.const_defined?('RemoteResource::Builder')).to be_truthy }
|
18
|
+
specify { expect(described_class.const_defined?('RemoteResource::UrlNaming')).to be_truthy }
|
19
|
+
specify { expect(described_class.const_defined?('RemoteResource::Connection')).to be_truthy }
|
20
|
+
specify { expect(described_class.const_defined?('RemoteResource::REST')).to be_truthy }
|
21
|
+
|
22
|
+
specify { expect(described_class.const_defined?('RemoteResource::Querying::FinderMethods')).to be_truthy }
|
23
|
+
specify { expect(described_class.const_defined?('RemoteResource::Querying::PersistenceMethods')).to be_truthy }
|
24
|
+
|
25
|
+
describe 'OPTIONS' do
|
26
|
+
let(:options) { [:base_url, :site, :headers, :version, :path_prefix, :path_postfix, :content_type, :collection, :collection_name, :root_element] }
|
27
|
+
|
28
|
+
specify { expect(described_class::OPTIONS).to eql options }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'attributes' do
|
32
|
+
it '#id' do
|
33
|
+
expect(dummy.attributes).to have_key :id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.global_headers=' do
|
38
|
+
let(:global_headers) do
|
39
|
+
{
|
40
|
+
'X-Locale' => 'en',
|
41
|
+
'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
after { described_class.global_headers = nil }
|
46
|
+
|
47
|
+
it 'sets the global headers Thread variable' do
|
48
|
+
expect{ described_class.global_headers = global_headers }.to change{ Thread.current[:global_headers] }.from(nil).to global_headers
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.global_headers' do
|
53
|
+
let(:global_headers) do
|
54
|
+
{
|
55
|
+
'X-Locale' => 'en',
|
56
|
+
'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
before { described_class.global_headers = global_headers }
|
61
|
+
after { described_class.global_headers = nil }
|
62
|
+
|
63
|
+
it 'returns the global headers Thread variable' do
|
64
|
+
expect(described_class.global_headers).to eql global_headers
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.connection_options' do
|
69
|
+
it 'instantiates as a RemoteResource::ConnectionOptions' do
|
70
|
+
expect(dummy_class.connection_options).to be_a RemoteResource::ConnectionOptions
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'uses the implemented class as base_class' do
|
74
|
+
expect(dummy_class.connection_options.base_class).to be RemoteResource::Dummy
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'sets the name of Thread variable with the implemented class' do
|
78
|
+
expect(dummy_class.connection_options).to eql Thread.current['remote_resource.dummy.connection_options']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '.threaded_connection_options' do
|
83
|
+
it 'instantiates as a Hash' do
|
84
|
+
expect(dummy_class.threaded_connection_options).to be_a Hash
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'sets the name of Thread variable with the implemented class' do
|
88
|
+
expect(dummy_class.threaded_connection_options).to eql Thread.current['remote_resource.dummy.threaded_connection_options']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '.with_connection_options' do
|
93
|
+
let(:connection_options) { {} }
|
94
|
+
|
95
|
+
let(:block_with_connection_options) do
|
96
|
+
dummy_class.with_connection_options(connection_options) do
|
97
|
+
dummy_class.find_by({ username: 'foobar' }, { content_type: '.json' })
|
98
|
+
dummy_class.create({ username: 'bazbar' }, { content_type: '.xml' })
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
before { allow_any_instance_of(Typhoeus::Request).to receive(:run) { double.as_null_object } }
|
103
|
+
|
104
|
+
it 'yields the block' do
|
105
|
+
expect(dummy_class).to receive(:find_by).with({ username: 'foobar' }, { content_type: '.json' }).and_call_original
|
106
|
+
expect(dummy_class).to receive(:create).with({ username: 'bazbar' }, { content_type: '.xml' }).and_call_original
|
107
|
+
block_with_connection_options
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'ensures to set the threaded_connection_options Thread variable to nil' do
|
111
|
+
dummy_class.threaded_connection_options
|
112
|
+
|
113
|
+
expect{ block_with_connection_options }.to change{ Thread.current['remote_resource.dummy.threaded_connection_options'] }.from(an_instance_of(Hash)).to nil
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when the given connection_options contain headers' do
|
117
|
+
let(:connection_options) do
|
118
|
+
{
|
119
|
+
headers: { "Foo" => "Bar" }
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'uses the headers of the given connection_options' do
|
124
|
+
expect(Typhoeus::Request).to receive(:get).with('https://foobar.com/dummy.json', params: { username: 'foobar' }, headers: { "Accept" => "application/json", "Foo" => "Bar" }).and_call_original
|
125
|
+
expect(Typhoeus::Request).to receive(:post).with('https://foobar.com/dummy.xml', body: { username: 'bazbar' }, headers: { "Accept" => "application/json", "Foo" => "Bar" }).and_call_original
|
126
|
+
block_with_connection_options
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when the given connection_options contain base_url' do
|
131
|
+
let(:connection_options) do
|
132
|
+
{
|
133
|
+
base_url: 'https://api.foobar.eu/dummy'
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'uses the base_url of the given connection_options' do
|
138
|
+
expect(Typhoeus::Request).to receive(:get).with('https://api.foobar.eu/dummy.json', params: { username: 'foobar' }, headers: { "Accept" => "application/json" }).and_call_original
|
139
|
+
expect(Typhoeus::Request).to receive(:post).with('https://api.foobar.eu/dummy.xml', body: { username: 'bazbar' }, headers: { "Accept" => "application/json" }).and_call_original
|
140
|
+
block_with_connection_options
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'when the given connection_options contain something else' do
|
145
|
+
let(:connection_options) do
|
146
|
+
{
|
147
|
+
collection: true,
|
148
|
+
path_prefix: '/api',
|
149
|
+
root_element: :bazbar
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'uses the given connection_options' do
|
154
|
+
expect(Typhoeus::Request).to receive(:get).with('https://foobar.com/api/dummies.json', params: { username: 'foobar' }, headers: { "Accept" => "application/json" }).and_call_original
|
155
|
+
expect(Typhoeus::Request).to receive(:post).with('https://foobar.com/api/dummies.xml', body: { 'bazbar' => { username: 'bazbar' } }, headers: { "Accept" => "application/json" }).and_call_original
|
156
|
+
block_with_connection_options
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#connection_options' do
|
162
|
+
it 'instanties as a RemoteResource::ConnectionOptions' do
|
163
|
+
expect(dummy.connection_options).to be_a RemoteResource::ConnectionOptions
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'uses the implemented class as base_class' do
|
167
|
+
expect(dummy.connection_options.base_class).to be RemoteResource::Dummy
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#empty?' do
|
172
|
+
before { allow(dummy).to receive(:_response) { response } }
|
173
|
+
|
174
|
+
context 'when the response is present' do
|
175
|
+
let(:response) { instance_double(RemoteResource::Response, sanitized_response_body: sanitized_response_body) }
|
176
|
+
|
177
|
+
context 'and the #sanitized_response_body is present' do
|
178
|
+
let(:sanitized_response_body) do
|
179
|
+
{ name: 'Mies' }
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'returns false' do
|
183
|
+
expect(dummy.empty?).to eql false
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'and the #sanitized_response_body is blank' do
|
188
|
+
let(:sanitized_response_body) do
|
189
|
+
{}
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'returns true' do
|
193
|
+
expect(dummy.empty?).to eql true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'and the #sanitized_response_body is NOT present' do
|
198
|
+
let(:sanitized_response_body) { nil }
|
199
|
+
|
200
|
+
it 'returns true' do
|
201
|
+
expect(dummy.empty?).to eql true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when the response is NOT present' do
|
207
|
+
let(:response) { nil }
|
208
|
+
|
209
|
+
it 'returns true' do
|
210
|
+
expect(dummy.empty?).to eql true
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#persisted?' do
|
216
|
+
context 'when id is present' do
|
217
|
+
it 'returns true' do
|
218
|
+
dummy.id = 10
|
219
|
+
expect(dummy.persisted?).to eql true
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'when is is NOT present' do
|
224
|
+
it 'returns false' do
|
225
|
+
expect(dummy.persisted?).to eql false
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#new_record?' do
|
231
|
+
context 'when instance persisted' do
|
232
|
+
it 'returns false' do
|
233
|
+
allow(dummy).to receive(:persisted?) { true }
|
234
|
+
|
235
|
+
expect(dummy.new_record?).to eql false
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'when instance does NOT persist' do
|
240
|
+
it 'returns true' do
|
241
|
+
allow(dummy).to receive(:persisted?) { false }
|
242
|
+
|
243
|
+
expect(dummy.new_record?).to eql true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe '#success?' do
|
249
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
250
|
+
|
251
|
+
before { allow(dummy).to receive(:_response) { response } }
|
252
|
+
|
253
|
+
context 'when response is successful' do
|
254
|
+
before { allow(response).to receive(:success?) { true } }
|
255
|
+
|
256
|
+
context 'and the resource has NO errors present' do
|
257
|
+
it 'returns true' do
|
258
|
+
expect(dummy.success?).to eql true
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'and the resource has errors present' do
|
263
|
+
it 'returns false' do
|
264
|
+
dummy.errors.add :id, 'must be present'
|
265
|
+
|
266
|
+
expect(dummy.success?).to eql false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'when response is NOT successful' do
|
272
|
+
before { allow(response).to receive(:success?) { false } }
|
273
|
+
|
274
|
+
it 'returns false' do
|
275
|
+
expect(dummy.success?).to eql false
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe '#errors?' do
|
281
|
+
context 'when resource has errors present' do
|
282
|
+
it 'returns true' do
|
283
|
+
dummy.errors.add :id, 'must be present'
|
284
|
+
|
285
|
+
expect(dummy.errors?).to eql true
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'when resource has NO errors present' do
|
290
|
+
it 'returns false' do
|
291
|
+
expect(dummy.errors?).to eql false
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe '#handle_response' do
|
297
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
298
|
+
|
299
|
+
before { allow(dummy).to receive(:rebuild_resource_from_response) { dummy } }
|
300
|
+
|
301
|
+
context 'when the response is a unprocessable_entity' do
|
302
|
+
before do
|
303
|
+
allow(response).to receive(:unprocessable_entity?) { true }
|
304
|
+
|
305
|
+
allow(dummy).to receive(:assign_errors_from_response)
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'rebuilds the resource from the response' do
|
309
|
+
expect(dummy).to receive(:rebuild_resource_from_response).with response
|
310
|
+
dummy.handle_response response
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'assigns the errors from the response to the resource' do
|
314
|
+
expect(dummy).to receive(:assign_errors_from_response).with response
|
315
|
+
dummy.handle_response response
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
context 'when the response is NOT a unprocessable_entity' do
|
320
|
+
before { allow(response).to receive(:unprocessable_entity?) { false } }
|
321
|
+
|
322
|
+
it 'rebuilds the resource from the response' do
|
323
|
+
expect(dummy).to receive(:rebuild_resource_from_response).with response
|
324
|
+
dummy.handle_response response
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe '#assign_response' do
|
330
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
331
|
+
|
332
|
+
it 'assigns the #_response' do
|
333
|
+
expect{ dummy.assign_response response }.to change{ dummy._response }.from(nil).to response
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe '#assign_errors_from_response' do
|
338
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
339
|
+
let(:error_messages_response_body) { double('error_messages_response_body') }
|
340
|
+
|
341
|
+
it 'calls the #assign_errors method with the #error_messages_response_body of the response' do
|
342
|
+
allow(response).to receive(:error_messages_response_body) { error_messages_response_body }
|
343
|
+
|
344
|
+
expect(dummy).to receive(:assign_errors).with error_messages_response_body
|
345
|
+
dummy.assign_errors_from_response response
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe '#assign_errors' do
|
350
|
+
context 'with errors in the error_messages' do
|
351
|
+
let(:error_messages) do
|
352
|
+
{
|
353
|
+
"foo" => ["is required"],
|
354
|
+
"bar" => ["must be greater than 5"]
|
355
|
+
}
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'assigns the error_messages as errors' do
|
359
|
+
dummy.send :assign_errors, error_messages
|
360
|
+
expect(dummy.errors.messages).to eql foo: ["is required"], bar: ["must be greater than 5"]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
context 'with an empty Hash in the error_messages' do
|
365
|
+
let(:error_messages) do
|
366
|
+
{}
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'does NOT assign the error_messages as errors' do
|
370
|
+
dummy.send :assign_errors, error_messages
|
371
|
+
expect(dummy.errors.messages).to eql({})
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'with a String in the error_messages' do
|
376
|
+
let(:error_messages) do
|
377
|
+
"unauthorized"
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'does NOT assign the error_messages as errors' do
|
381
|
+
dummy.send :assign_errors, error_messages
|
382
|
+
expect(dummy.errors.messages).to eql({})
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
|