garage_client 2.1.1

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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +40 -0
  5. data/Gemfile +8 -0
  6. data/Guardfile +7 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +196 -0
  9. data/Rakefile +8 -0
  10. data/garage_client.gemspec +36 -0
  11. data/gemfiles/Gemfile.faraday-0.8.x +4 -0
  12. data/lib/garage_client.rb +33 -0
  13. data/lib/garage_client/cachers/base.rb +44 -0
  14. data/lib/garage_client/client.rb +93 -0
  15. data/lib/garage_client/configuration.rb +51 -0
  16. data/lib/garage_client/error.rb +37 -0
  17. data/lib/garage_client/request.rb +38 -0
  18. data/lib/garage_client/request/json_encoded.rb +59 -0
  19. data/lib/garage_client/resource.rb +63 -0
  20. data/lib/garage_client/response.rb +123 -0
  21. data/lib/garage_client/response/cacheable.rb +27 -0
  22. data/lib/garage_client/response/raise_http_exception.rb +34 -0
  23. data/lib/garage_client/version.rb +3 -0
  24. data/spec/features/configuration_spec.rb +46 -0
  25. data/spec/fixtures/example.yaml +56 -0
  26. data/spec/fixtures/examples.yaml +60 -0
  27. data/spec/fixtures/examples_dictionary.yaml +60 -0
  28. data/spec/fixtures/examples_without_pagination.yaml +58 -0
  29. data/spec/garage_client/cacher_spec.rb +55 -0
  30. data/spec/garage_client/client_spec.rb +228 -0
  31. data/spec/garage_client/configuration_spec.rb +106 -0
  32. data/spec/garage_client/error_spec.rb +37 -0
  33. data/spec/garage_client/request/json_encoded_spec.rb +66 -0
  34. data/spec/garage_client/resource_spec.rb +102 -0
  35. data/spec/garage_client/response_spec.rb +450 -0
  36. data/spec/garage_client_spec.rb +48 -0
  37. data/spec/spec_helper.rb +56 -0
  38. metadata +275 -0
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Configuration of GarageClient" do
4
+ let(:client) { GarageClient::Client.new(options) }
5
+ let(:options) { {} }
6
+
7
+ describe "header configuration" do
8
+ before do
9
+ options[:headers] = { "User-Agent" => "my agent" }
10
+ end
11
+
12
+ it "sends configured User-Agent" do
13
+ stub_request(:get, "https://garage.example.com/v1/me").
14
+ with(headers: { 'User-Agent'=>'my agent' }).
15
+ to_return(:status => 200, :body => "", :headers => {})
16
+
17
+ client.get('/v1/me')
18
+ end
19
+ end
20
+
21
+ describe "header configuration with global configuration" do
22
+ around do |example|
23
+ prev = GarageClient.configuration
24
+ GarageClient.instance_variable_set(
25
+ :@configuration,
26
+ GarageClient::Configuration.new(endpoint: "https://garage.example.com")
27
+ )
28
+
29
+ example.run
30
+
31
+ GarageClient.instance_variable_set(:@configuration, prev)
32
+ end
33
+
34
+ it "uses global configured User-Agent" do
35
+ GarageClient.configure do |c|
36
+ c.headers = { "User-Agent" => "my agent" }
37
+ end
38
+
39
+ stub_request(:get, "https://garage.example.com/v1/me").
40
+ with(headers: { 'User-Agent'=>'my agent' }).
41
+ to_return(:status => 200, :body => "", :headers => {})
42
+
43
+ client.get('/v1/me')
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ headers:
2
+ Content-Type: application/json; charset=utf-8
3
+ body: |
4
+ {
5
+ "id": 1,
6
+ "created": "2000-01-01T00:00:00+09:00",
7
+ "updated": "2000-01-01T00:00:00+09:00",
8
+ "name": "recipe title",
9
+ "url": "http://example.com/examples/1",
10
+ "user": {
11
+ "id": 2294,
12
+ "url": "http://example.com/kitchen/2294",
13
+ "name": "Fern1",
14
+ "_links": {
15
+ "self": {
16
+ "href": "/v1/users/2294"
17
+ },
18
+ "bookmarks": {
19
+ "href": "/v1/users/2294/bookmarks"
20
+ },
21
+ "bookmark_tags": {
22
+ "href": "/v1/users/2294/bookmark_tags"
23
+ },
24
+ "canonical": {
25
+ "href": "/v1/users/2294"
26
+ },
27
+ "diary_entries": {
28
+ "href": "/v1/users/2294/diary_entries"
29
+ },
30
+ "feedbacks": {
31
+ "href": "/v1/users/2294/feedbacks"
32
+ },
33
+ "recipes": {
34
+ "href": "/v1/users/2294/recipes"
35
+ }
36
+ }
37
+ },
38
+ "description": "this is my favorite recipe.",
39
+ "serving": null,
40
+ "published": "2000-01-01T00:00:00+09:00",
41
+ "edited": null,
42
+ "tier": "example.com:User",
43
+ "ingredients": [],
44
+ "steps": [],
45
+ "_links": {
46
+ "self": {
47
+ "href": "/examples/1"
48
+ },
49
+ "canonical": {
50
+ "href": "/examples/1"
51
+ },
52
+ "nested_examples": {
53
+ "href": "/examples/1/nested_examples"
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,60 @@
1
+ headers:
2
+ Content-Type: application/json; charset=utf-8
3
+ X-List-TotalCount: "1"
4
+ Link: </v1/examples?page=2&per_page=1>; rel="next"
5
+ body: |
6
+ [
7
+ {
8
+ "id": 1,
9
+ "created": "2000-01-01T00:00:00+09:00",
10
+ "updated": "2000-01-01T00:00:00+09:00",
11
+ "name": "recipe title",
12
+ "url": "http://example.com/examples/1",
13
+ "user": {
14
+ "id": 2294,
15
+ "url": "http://example.com/kitchen/2294",
16
+ "name": "Fern1",
17
+ "_links": {
18
+ "self": {
19
+ "href": "/v1/users/2294"
20
+ },
21
+ "bookmarks": {
22
+ "href": "/v1/users/2294/bookmarks"
23
+ },
24
+ "bookmark_tags": {
25
+ "href": "/v1/users/2294/bookmark_tags"
26
+ },
27
+ "canonical": {
28
+ "href": "/v1/users/2294"
29
+ },
30
+ "diary_entries": {
31
+ "href": "/v1/users/2294/diary_entries"
32
+ },
33
+ "feedbacks": {
34
+ "href": "/v1/users/2294/feedbacks"
35
+ },
36
+ "recipes": {
37
+ "href": "/v1/users/2294/recipes"
38
+ }
39
+ }
40
+ },
41
+ "descriptien": "this is my favorite recipe.",
42
+ "serving": null,
43
+ "published": "2000-01-01T00:00:00+09:00",
44
+ "edited": null,
45
+ "tier": "example.com:User",
46
+ "ingredients": [],
47
+ "steps": [],
48
+ "_links": {
49
+ "self": {
50
+ "href": "/examples/1"
51
+ },
52
+ "canonical": {
53
+ "href": "/examples/1"
54
+ },
55
+ "nested_examples": {
56
+ "href": "/examples/1/nested_examples"
57
+ }
58
+ }
59
+ }
60
+ ]
@@ -0,0 +1,60 @@
1
+ headers:
2
+ Content-Type: application/vnd.cookpad.dictionary+json; charset=utf-8
3
+ X-List-TotalCount: "1"
4
+ Link: </v1/examples?page=2&per_page=1>; rel="next"
5
+ body: |
6
+ {
7
+ "1": {
8
+ "id": 1,
9
+ "created": "2000-01-01T00:00:00+09:00",
10
+ "updated": "2000-01-01T00:00:00+09:00",
11
+ "name": "recipe title",
12
+ "url": "http://example.com/examples/1",
13
+ "user": {
14
+ "id": 2294,
15
+ "url": "http://example.com/kitchen/2294",
16
+ "name": "Fern1",
17
+ "_links": {
18
+ "self": {
19
+ "href": "/v1/users/2294"
20
+ },
21
+ "bookmarks": {
22
+ "href": "/v1/users/2294/bookmarks"
23
+ },
24
+ "bookmark_tags": {
25
+ "href": "/v1/users/2294/bookmark_tags"
26
+ },
27
+ "canonical": {
28
+ "href": "/v1/users/2294"
29
+ },
30
+ "diary_entries": {
31
+ "href": "/v1/users/2294/diary_entries"
32
+ },
33
+ "feedbacks": {
34
+ "href": "/v1/users/2294/feedbacks"
35
+ },
36
+ "recipes": {
37
+ "href": "/v1/users/2294/recipes"
38
+ }
39
+ }
40
+ },
41
+ "descriptien": "this is my favorite recipe.",
42
+ "serving": null,
43
+ "published": "2000-01-01T00:00:00+09:00",
44
+ "edited": null,
45
+ "tier": "cookpad.com:User",
46
+ "ingredients": [],
47
+ "steps": [],
48
+ "_links": {
49
+ "self": {
50
+ "href": "/examples/1"
51
+ },
52
+ "canonical": {
53
+ "href": "/examples/1"
54
+ },
55
+ "nested_examples": {
56
+ "href": "/examples/1/nested_examples"
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,58 @@
1
+ headers:
2
+ Content-Type: application/json; charset=utf-8
3
+ body: |
4
+ [
5
+ {
6
+ "id": 1,
7
+ "created": "2000-01-01T00:00:00+09:00",
8
+ "updated": "2000-01-01T00:00:00+09:00",
9
+ "name": "recipe title",
10
+ "url": "http://example.com/examples/1",
11
+ "user": {
12
+ "id": 2294,
13
+ "url": "http://example.com/kitchen/2294",
14
+ "name": "Fern1",
15
+ "_links": {
16
+ "self": {
17
+ "href": "/v1/users/2294"
18
+ },
19
+ "bookmarks": {
20
+ "href": "/v1/users/2294/bookmarks"
21
+ },
22
+ "bookmark_tags": {
23
+ "href": "/v1/users/2294/bookmark_tags"
24
+ },
25
+ "canonical": {
26
+ "href": "/v1/users/2294"
27
+ },
28
+ "diary_entries": {
29
+ "href": "/v1/users/2294/diary_entries"
30
+ },
31
+ "feedbacks": {
32
+ "href": "/v1/users/2294/feedbacks"
33
+ },
34
+ "recipes": {
35
+ "href": "/v1/users/2294/recipes"
36
+ }
37
+ }
38
+ },
39
+ "description": "this is my favorite recipe.",
40
+ "serving": null,
41
+ "published": "2000-01-01T00:00:00+09:00",
42
+ "edited": null,
43
+ "tier": "cookpad.com:User",
44
+ "ingredients": [],
45
+ "steps": [],
46
+ "_links": {
47
+ "self": {
48
+ "href": "/examples/1"
49
+ },
50
+ "canonical": {
51
+ "href": "/v1/examples/1"
52
+ },
53
+ "nested_examples": {
54
+ "href": "/examples/1/nested_examples"
55
+ }
56
+ }
57
+ }
58
+ ]
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe GarageClient::Cachers::Base do
4
+ let(:client) do
5
+ GarageClient::Client.new(cacher: cacher_class)
6
+ end
7
+
8
+ let(:cacher_class) do
9
+ store = store
10
+ Class.new(GarageClient::Cachers::Base) do
11
+ private
12
+
13
+ def key
14
+ @env[:url].to_s
15
+ end
16
+
17
+ def read_from_cache?
18
+ true
19
+ end
20
+
21
+ def written_to_cache?
22
+ true
23
+ end
24
+
25
+ def store
26
+ Class.new do
27
+ def initialize
28
+ @table = {}
29
+ end
30
+
31
+ def read(key, options = {})
32
+ @table[key]
33
+ end
34
+
35
+ def write(key, value, options = {})
36
+ @table[key] = value
37
+ end
38
+ end.new
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "caching" do
44
+ context "with cache-enabled GarageClient::Client" do
45
+ it "caches response along passed cacher class" do
46
+ stub_get("/examples").to_return(fixture("examples.yaml")).times(1)
47
+ stub_get("/example").to_return(fixture("example.yaml")).times(1)
48
+ client.get("/examples").body.should be_a Array
49
+ client.get("/examples").body.should be_a Array
50
+ client.get("/example").body.should be_a GarageClient::Resource
51
+ client.get("/example").body.should be_a GarageClient::Resource
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,228 @@
1
+ require 'spec_helper'
2
+
3
+ describe GarageClient::Client do
4
+ let(:client) { GarageClient::Client.new(options) }
5
+ let(:options) { {} }
6
+
7
+ describe "#adapter" do
8
+ context "without :adapter option value" do
9
+ it "returns default value" do
10
+ client.adapter.should == GarageClient.configuration.adapter
11
+ end
12
+ end
13
+
14
+ context "with :adapter option value" do
15
+ before do
16
+ options[:adapter] = :test
17
+ end
18
+
19
+ it "returns it" do
20
+ client.adapter.should == :test
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#endpoint" do
26
+ context "without :endpoint option value" do
27
+ it "returns default value" do
28
+ client.endpoint.should == GarageClient.configuration.endpoint
29
+ end
30
+ end
31
+
32
+ context "with :endpoint option value" do
33
+ before do
34
+ options[:endpoint] = "http://example.com"
35
+ end
36
+
37
+ it "returns it" do
38
+ client.endpoint.should == "http://example.com"
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#path_prefix" do
44
+ context "without :path_prefix option value" do
45
+ it "returns default value" do
46
+ client.path_prefix.should == GarageClient.configuration.path_prefix
47
+ end
48
+ end
49
+
50
+ context "with :path_prefix option value" do
51
+ before do
52
+ options[:path_prefix] = "/v2"
53
+ end
54
+
55
+ it "returns it" do
56
+ client.path_prefix.should == "/v2"
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#verbose" do
62
+ context "without :verbose option value" do
63
+ it "returns default value" do
64
+ client.verbose.should == GarageClient.configuration.verbose
65
+ end
66
+ end
67
+
68
+ context "with :verbose option value" do
69
+ before do
70
+ options[:verbose] = nil
71
+ end
72
+
73
+ it "returns it" do
74
+ client.verbose.should == nil
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "#headers" do
80
+ context "without :headers option value" do
81
+ it "returns default value" do
82
+ client.headers.should == GarageClient.configuration.headers
83
+ end
84
+ end
85
+
86
+ context "with :headers option value" do
87
+ before do
88
+ options[:headers] = { "Content-Type" => "text/plain" }
89
+ end
90
+
91
+ it "returns headers merged with default values" do
92
+ client.headers.should == {
93
+ "Accept" => "application/json",
94
+ "Content-Type" => "text/plain",
95
+ "User-Agent" => "garage_client #{GarageClient::VERSION}"
96
+ }
97
+ end
98
+ end
99
+
100
+ context "with User-Agent header option" do
101
+ before do
102
+ options[:headers] = { "User-Agent" => "my agent" }
103
+ end
104
+
105
+ it "returns changed User-Agent" do
106
+ client.headers["User-Agent"].should eq "my agent"
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "authorization" do
112
+ before do
113
+ client.access_token = 'abc'
114
+ stub_get('/example').with(
115
+ headers: {
116
+ 'Accept' => 'application/json',
117
+ 'Accept-Encoding' => /.*/,
118
+ 'User-Agent' => /.*/,
119
+ 'Authorization' => 'Bearer abc'
120
+ }
121
+ ).to_return(fixture('example.yaml'))
122
+ end
123
+
124
+ it "requests with expected beaer authorization header" do
125
+ expect { client.get('/example') }.not_to raise_error
126
+ end
127
+ end
128
+
129
+ describe "#adapter=" do
130
+ it "overwrites it" do
131
+ client.adapter = :test
132
+ client.adapter.should == :test
133
+ end
134
+ end
135
+
136
+ describe "#endpoint=" do
137
+ it "overwrites it" do
138
+ client.adapter = "http://example.com"
139
+ client.adapter.should == "http://example.com"
140
+ end
141
+ end
142
+
143
+ describe "#headers=" do
144
+ it "updates merged headers" do
145
+ client.headers = { "Content-Type" => "text/plain" }
146
+ client.headers.should == { "Content-Type" => "text/plain" }
147
+ end
148
+ end
149
+
150
+ describe "#path_prefix=" do
151
+ it "overwrites it" do
152
+ client.adapter = "/v2"
153
+ client.adapter.should == "/v2"
154
+ end
155
+ end
156
+
157
+ describe "#verbose=" do
158
+ it "overwrites it" do
159
+ client.verbose = nil
160
+ client.verbose.should == nil
161
+ end
162
+ end
163
+
164
+ describe '#get' do
165
+ context 'with collection resource' do
166
+ before do
167
+ stub_get('/examples').to_return(fixture('examples.yaml'))
168
+ end
169
+
170
+ it 'returns response' do
171
+ response = client.get('/examples')
172
+ response.should be_kind_of(GarageClient::Response)
173
+ response.body.should be_kind_of(Array)
174
+ response.body.first.should be_kind_of(GarageClient::Resource)
175
+ end
176
+ end
177
+
178
+ context 'with single resource' do
179
+ before do
180
+ stub_get('/examples/1').to_return(fixture('example.yaml'))
181
+ end
182
+
183
+ it 'returns response' do
184
+ response = client.get('/examples/1')
185
+ response.should be_kind_of(GarageClient::Response)
186
+ response.body.should be_kind_of(GarageClient::Resource)
187
+ end
188
+ end
189
+ end
190
+
191
+ describe '#post' do
192
+ before do
193
+ stub_post('/examples').to_return(fixture('example.yaml'))
194
+ end
195
+
196
+ it 'returns created resource' do
197
+ response = client.post('/examples', :name => 'example name')
198
+ response.should be_kind_of(GarageClient::Response)
199
+ response.body.should be_kind_of(GarageClient::Resource)
200
+ end
201
+ end
202
+
203
+ describe '#me' do
204
+ before do
205
+ stub_get('/me').to_return(fixture('example.yaml'))
206
+ end
207
+
208
+ it 'returns response' do
209
+ response = client.me
210
+ response.should be_kind_of(GarageClient::Response)
211
+ response.body.should be_kind_of(GarageClient::Resource)
212
+ end
213
+ end
214
+
215
+ describe 'validation' do
216
+ context 'when endpoint configuration is missing' do
217
+ around do |example|
218
+ old, GarageClient.endpoint = GarageClient.endpoint, nil
219
+ example.run
220
+ GarageClient.endpoint = old
221
+ end
222
+
223
+ it 'raises RuntimeError' do
224
+ expect { client }.to raise_error(RuntimeError, /missing endpoint/)
225
+ end
226
+ end
227
+ end
228
+ end