harpy 1.0.0 → 1.1.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 +5 -5
- data/.gitignore +2 -1
- data/.travis.yml +4 -6
- data/README.md +5 -5
- data/harpy.gemspec +6 -5
- data/lib/harpy.rb +3 -2
- data/lib/harpy/gem_version.rb +14 -0
- data/lib/harpy/resource.rb +109 -93
- data/lib/harpy/version.rb +9 -0
- data/spec/harpy/client_spec.rb +27 -20
- data/spec/harpy/entry_point_spec.rb +14 -14
- data/spec/harpy/resource_spec.rb +224 -224
- data/spec/harpy_spec.rb +20 -20
- metadata +52 -52
- data/Gemfile.lock +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f6437acd381d436843e94394aa9d1c6df605c4ca43ca5fb3e32746a4f6d805ed
|
4
|
+
data.tar.gz: b03c709ef5b4297a059280926773fb376b5f9feb77ab6656ea9f7d169cd69c6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 074b8e393b71cb510d5f8acf96aa2473395689458d4b7b1ca58965b44a1a902be536f77f452a0bd1d5d675b456244d52cf0937b1c266abdc019f16eaed71b475
|
7
|
+
data.tar.gz: 96996c71087173593a8eaed967fdd500a505ab92b6ddcf0e2025cb62ab87735a29748e9e8958ed37573e74ea96c3bdfb8c4afabe5de4865fd5316598c19b705d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -9,11 +9,11 @@ Client for REST API with HATEOAS
|
|
9
9
|
Dependencies
|
10
10
|
------------
|
11
11
|
|
12
|
-
* Ruby
|
13
|
-
* gem "typhoeus", "
|
14
|
-
* gem "activesupport", ">=
|
15
|
-
* gem "activemodel", ">=
|
16
|
-
* gem "hash-deep-merge", "
|
12
|
+
* Ruby >= 2.4.
|
13
|
+
* gem "typhoeus", ">= 0.6.5"
|
14
|
+
* gem "activesupport", ">= 5.2.0"
|
15
|
+
* gem "activemodel", ">= 5.2.0"
|
16
|
+
* gem "hash-deep-merge", ">= 0.1.1"
|
17
17
|
|
18
18
|
Usage
|
19
19
|
-----
|
data/harpy.gemspec
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "harpy"
|
3
4
|
|
4
5
|
Gem::Specification.new do |s|
|
5
6
|
s.name = "harpy"
|
6
|
-
s.version =
|
7
|
+
s.version = Harpy::VERSION::STRING
|
7
8
|
s.platform = Gem::Platform::RUBY
|
8
9
|
s.authors = ["Joseph HALTER", "Jonathan TRON"]
|
9
10
|
s.email = ["joseph.halter@thetalentbox.com", "jonathan.tron@thetalentbox.com"]
|
@@ -17,10 +18,10 @@ Gem::Specification.new do |s|
|
|
17
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
19
|
s.require_paths = ["lib"]
|
19
20
|
|
20
|
-
s.add_runtime_dependency "typhoeus", "
|
21
|
-
s.add_runtime_dependency "activesupport", [">=
|
22
|
-
s.add_runtime_dependency "activemodel", [">=
|
23
|
-
s.add_runtime_dependency "hash-deep-merge", ["
|
21
|
+
s.add_runtime_dependency "typhoeus", ">= 0.6.5"
|
22
|
+
s.add_runtime_dependency "activesupport", [">= 5.2.0"]
|
23
|
+
s.add_runtime_dependency "activemodel", [">= 5.2.0"]
|
24
|
+
s.add_runtime_dependency "hash-deep-merge", [">= 0.1.1"]
|
24
25
|
|
25
26
|
s.add_development_dependency "rake", [">= 0.8.7"]
|
26
27
|
s.add_development_dependency "rspec"
|
data/lib/harpy.rb
CHANGED
@@ -14,6 +14,7 @@ module Harpy
|
|
14
14
|
autoload :Resource, "harpy/resource"
|
15
15
|
autoload :BodyToBig, "harpy/resource"
|
16
16
|
autoload :UnknownResponseCode, "harpy/resource"
|
17
|
+
autoload :VERSION, "harpy/gem_version"
|
17
18
|
|
18
19
|
def self.client=(new_client)
|
19
20
|
@client = new_client
|
@@ -38,10 +39,10 @@ module Harpy
|
|
38
39
|
def self.entry_point
|
39
40
|
@entry_point || raise(EntryPointRequired, 'you can setup one with Harpy.entry_point_url = "http://localhost"')
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
def self.reset
|
43
44
|
@client = nil
|
44
45
|
@entry_point = nil
|
45
46
|
end
|
46
47
|
|
47
|
-
end
|
48
|
+
end
|
data/lib/harpy/resource.rb
CHANGED
@@ -16,6 +16,7 @@ module Harpy
|
|
16
16
|
base.send :include, ActiveModel::Validations
|
17
17
|
base.send :include, ActiveModel::Validations::Callbacks
|
18
18
|
base.define_model_callbacks :save, :create, :update, :destroy, :only => [:before, :after]
|
19
|
+
base.send :include, InstanceMethods
|
19
20
|
end
|
20
21
|
|
21
22
|
def self.from_url(hash)
|
@@ -93,6 +94,14 @@ module Harpy
|
|
93
94
|
delete_from_urn urn id
|
94
95
|
end
|
95
96
|
|
97
|
+
def uman_attribute_name(attr, options = {})
|
98
|
+
attr
|
99
|
+
end
|
100
|
+
|
101
|
+
def lookup_ancestors
|
102
|
+
[self]
|
103
|
+
end
|
104
|
+
|
96
105
|
private
|
97
106
|
|
98
107
|
def url
|
@@ -122,128 +131,135 @@ module Harpy
|
|
122
131
|
end
|
123
132
|
end
|
124
133
|
|
125
|
-
|
126
|
-
@attrs = {}
|
127
|
-
self.attributes = attrs || {}
|
128
|
-
end
|
134
|
+
module InstanceMethods
|
129
135
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
+
def initialize(attrs = nil)
|
137
|
+
@attrs = {}
|
138
|
+
self.attributes = attrs || {}
|
139
|
+
end
|
140
|
+
|
141
|
+
def attributes=(attrs)
|
142
|
+
attrs.each do |key, value|
|
143
|
+
if respond_to? "#{key}="
|
144
|
+
send "#{key}=", value
|
145
|
+
else
|
146
|
+
@attrs[key] = value
|
147
|
+
end
|
136
148
|
end
|
137
149
|
end
|
138
|
-
end
|
139
150
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
151
|
+
def as_json(*args)
|
152
|
+
hash = @attrs.dup
|
153
|
+
hash.delete "link"
|
154
|
+
hash.delete "urn"
|
155
|
+
hash
|
156
|
+
end
|
146
157
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
158
|
+
def save
|
159
|
+
if valid?
|
160
|
+
run_callbacks :save do
|
161
|
+
json = JSON.generate as_json
|
162
|
+
raise BodyToBig, "Size: #{json.bytesize} bytes (max 1MB)" if json.bytesize > 1.megabyte
|
163
|
+
persisted? ? update(json) : create(json)
|
164
|
+
end
|
165
|
+
else
|
166
|
+
false
|
153
167
|
end
|
154
|
-
else
|
155
|
-
false
|
156
168
|
end
|
157
|
-
end
|
158
169
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
170
|
+
def destroy
|
171
|
+
raise Harpy::UrlRequired unless url
|
172
|
+
run_callbacks :destroy do
|
173
|
+
process_response Harpy.client.delete(url), :destroy
|
174
|
+
end
|
163
175
|
end
|
164
|
-
end
|
165
176
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
177
|
+
def link(rel)
|
178
|
+
link = (@attrs["link"]||[]).detect{|l| l["rel"]==rel.to_s}
|
179
|
+
link["href"] if link
|
180
|
+
end
|
170
181
|
|
171
|
-
|
172
|
-
|
173
|
-
|
182
|
+
def url
|
183
|
+
link "self"
|
184
|
+
end
|
174
185
|
|
175
|
-
|
176
|
-
|
177
|
-
|
186
|
+
def url_collection
|
187
|
+
Harpy.entry_point.resource_url self.class.resource_name
|
188
|
+
end
|
178
189
|
|
179
|
-
|
180
|
-
|
181
|
-
|
190
|
+
def id
|
191
|
+
@attrs["urn"].split(":").last if @attrs["urn"]
|
192
|
+
end
|
182
193
|
|
183
|
-
|
184
|
-
|
185
|
-
|
194
|
+
def persisted?
|
195
|
+
@attrs["urn"].present?
|
196
|
+
end
|
186
197
|
|
187
|
-
|
188
|
-
|
189
|
-
|
198
|
+
def inspect
|
199
|
+
"<#{self.class.name} @attrs:#{@attrs.inspect} @errors:#{errors.full_messages.inspect} persisted:#{persisted?}>"
|
200
|
+
end
|
190
201
|
|
191
|
-
|
192
|
-
|
193
|
-
|
202
|
+
def has_key?(key)
|
203
|
+
@attrs.has_key? key.to_s
|
204
|
+
end
|
194
205
|
|
195
|
-
|
196
|
-
|
197
|
-
|
206
|
+
def hash
|
207
|
+
urn.hash
|
208
|
+
end
|
198
209
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
210
|
+
def ==(other)
|
211
|
+
other.equal?(self) || (urn && other.instance_of?(self.class) && other.urn == urn)
|
212
|
+
end
|
213
|
+
alias_method :eql?, :==
|
203
214
|
|
204
|
-
|
215
|
+
private
|
205
216
|
|
206
|
-
|
207
|
-
|
208
|
-
|
217
|
+
def create(json)
|
218
|
+
run_callbacks :create do
|
219
|
+
process_response Harpy.client.post(url_collection, :body => json), :create
|
220
|
+
end
|
209
221
|
end
|
210
|
-
end
|
211
222
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
223
|
+
def update(json)
|
224
|
+
run_callbacks :update do
|
225
|
+
raise Harpy::UrlRequired unless url
|
226
|
+
process_response Harpy.client.put(url, :body => json), :update
|
227
|
+
end
|
216
228
|
end
|
217
|
-
end
|
218
229
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
230
|
+
def process_response(response, context)
|
231
|
+
case response.code
|
232
|
+
when 200, 201, 302
|
233
|
+
@attrs.merge! JSON.parse(response.body)
|
234
|
+
true
|
235
|
+
when 204
|
236
|
+
context==:create ? Harpy.client.invalid_code(response) : true
|
237
|
+
when 401
|
238
|
+
raise Harpy::Unauthorized, "Server returned a 401 response code"
|
239
|
+
when 422
|
240
|
+
JSON.parse(response.body)["errors"].each do |attr, attr_errors|
|
241
|
+
attr_errors.each{|attr_error| errors.add(attr, :invalid, message: attr_error) }
|
242
|
+
end
|
243
|
+
false
|
244
|
+
else
|
245
|
+
Harpy.client.invalid_code response
|
231
246
|
end
|
232
|
-
false
|
233
|
-
else
|
234
|
-
Harpy.client.invalid_code response
|
235
247
|
end
|
236
|
-
end
|
237
248
|
|
238
|
-
|
239
|
-
key = method.to_s
|
240
|
-
if persisted? && !@attrs.has_key?(key)
|
241
|
-
super
|
242
|
-
elsif key=~/[\]=]\z/
|
243
|
-
super
|
244
|
-
else
|
249
|
+
def read_attribute_for_validation(key)
|
245
250
|
@attrs[key]
|
246
251
|
end
|
252
|
+
|
253
|
+
def method_missing(method, *args)
|
254
|
+
key = method.to_s
|
255
|
+
if persisted? && !@attrs.has_key?(key)
|
256
|
+
super
|
257
|
+
elsif key=~/[\]=]\z/
|
258
|
+
super
|
259
|
+
else
|
260
|
+
@attrs[key]
|
261
|
+
end
|
262
|
+
end
|
247
263
|
end
|
248
264
|
end
|
249
265
|
end
|
data/spec/harpy/client_spec.rb
CHANGED
@@ -5,13 +5,20 @@ describe Harpy::Client do
|
|
5
5
|
let(:users_url) { "http://localhost/users" }
|
6
6
|
|
7
7
|
context "by default" do
|
8
|
-
|
8
|
+
describe '#options' do
|
9
|
+
subject { super().options }
|
10
|
+
it { is_expected.to be_empty }
|
11
|
+
end
|
9
12
|
end
|
10
13
|
|
11
14
|
context "initialized with options" do
|
12
15
|
let(:options) { {:username => "harpy", :password => "spec"} }
|
13
16
|
subject { Harpy::Client.new(options) }
|
14
|
-
|
17
|
+
|
18
|
+
describe '#options' do
|
19
|
+
subject { super().options }
|
20
|
+
it { is_expected.to eq(options) }
|
21
|
+
end
|
15
22
|
end
|
16
23
|
|
17
24
|
[:get, :head, :post, :put, :patch, :delete].each do |method|
|
@@ -22,32 +29,32 @@ describe Harpy::Client do
|
|
22
29
|
Typhoeus.stub(entry_url, method: method){@expected}
|
23
30
|
end
|
24
31
|
it "sends a #{method.to_s.upcase} to the url" do
|
25
|
-
subject.send(method, entry_url).
|
32
|
+
expect(subject.send(method, entry_url)).to eq(@expected)
|
26
33
|
end
|
27
34
|
it "merges options" do
|
28
35
|
client = Harpy::Client.new :headers => {"Authorization" => "spec"}
|
29
36
|
Typhoeus.stub(entry_url, method: method){Typhoeus::Response.new :code => 200}
|
30
37
|
response = client.send method, entry_url, :headers => {"X-Files" => "Harpy"}
|
31
|
-
response.request.options[:headers].
|
38
|
+
expect(response.request.options[:headers]).to include({"X-Files" => "Harpy", "Authorization" => "spec"})
|
32
39
|
end
|
33
40
|
end
|
34
41
|
context "with multiple urls" do
|
35
42
|
it "does not execute requests" do
|
36
|
-
|
43
|
+
expect {
|
37
44
|
subject.send method, [entry_url, users_url]
|
38
|
-
}.
|
45
|
+
}.not_to raise_error
|
39
46
|
end
|
40
47
|
it "returns one requests per url" do
|
41
48
|
requests = subject.send method, [entry_url, users_url]
|
42
|
-
requests.size.
|
43
|
-
requests.collect{|r| r.options[:method]}.
|
44
|
-
requests.collect(&:url).
|
49
|
+
expect(requests.size).to eq(2)
|
50
|
+
expect(requests.collect{|r| r.options[:method]}).to match_array([method, method])
|
51
|
+
expect(requests.collect(&:url)).to match_array([entry_url, users_url])
|
45
52
|
end
|
46
53
|
it "merges options" do
|
47
54
|
client = Harpy::Client.new :headers => {"Authorization" => "spec"}
|
48
55
|
requests = client.send method, [entry_url, users_url], :headers => {"X-Files" => "Harpy"}
|
49
56
|
requests.each do |request|
|
50
|
-
request.options[:headers].
|
57
|
+
expect(request.options[:headers]).to include({"X-Files" => "Harpy", "Authorization" => "spec"})
|
51
58
|
end
|
52
59
|
end
|
53
60
|
end
|
@@ -62,35 +69,35 @@ describe Harpy::Client do
|
|
62
69
|
Typhoeus.stub(users_url, method: :get){ @users_response }
|
63
70
|
end
|
64
71
|
it "executes requests in parallel" do
|
65
|
-
Typhoeus::Hydra.hydra.
|
72
|
+
expect(Typhoeus::Hydra.hydra).to receive(:run).once
|
66
73
|
subject.run subject.get([entry_url, users_url])
|
67
74
|
end
|
68
75
|
it "returns responses" do
|
69
76
|
responses = subject.run subject.get([entry_url, users_url])
|
70
|
-
responses.
|
77
|
+
expect(responses).to match_array([@entry_response, @users_response])
|
71
78
|
end
|
72
79
|
it "requests response is filled in" do
|
73
80
|
requests = subject.get([entry_url, users_url])
|
74
81
|
subject.run requests
|
75
|
-
requests[0].response.
|
76
|
-
requests[1].response.
|
82
|
+
expect(requests[0].response).to eq(@entry_response)
|
83
|
+
expect(requests[1].response).to eq(@users_response)
|
77
84
|
end
|
78
85
|
end
|
79
86
|
describe "#invalid_code(response)" do
|
80
87
|
it "raises Harpy::ClientTimeout on request timeout" do
|
81
|
-
|
88
|
+
expect {
|
82
89
|
subject.invalid_code double("Response", :timed_out? => true)
|
83
|
-
}.
|
90
|
+
}.to raise_error Harpy::ClientTimeout
|
84
91
|
end
|
85
92
|
it "raises Harpy::ClientError on code 0" do
|
86
|
-
|
93
|
+
expect {
|
87
94
|
subject.invalid_code double("Response", :timed_out? => false, :code => 0, :return_message => "Could not connect to server")
|
88
|
-
}.
|
95
|
+
}.to raise_error Harpy::ClientError, "Could not connect to server"
|
89
96
|
end
|
90
97
|
it "raises Harpy::InvalidResponseCode with code otherwise" do
|
91
|
-
|
98
|
+
expect {
|
92
99
|
subject.invalid_code double("Response", :timed_out? => false, :code => 404)
|
93
|
-
}.
|
100
|
+
}.to raise_error Harpy::InvalidResponseCode, "404"
|
94
101
|
end
|
95
102
|
end
|
96
103
|
end
|