codegram / hyperclient Goto Github PK
View Code? Open in Web Editor NEWHyperClient is a Ruby Hypermedia API client.
License: MIT License
HyperClient is a Ruby Hypermedia API client.
License: MIT License
From #99 (comment)
Imagine I get some HAL from a server that looks like this (in part, maybe):
{
'_links': {
'self': { 'href': '/users/12345' },
'profile_image': { 'href': '/user/12345/face' }
}
}
And I am super interested in profile images, so I want to get that sucker. Right now, if I call, you know, user.links.profile_image.resource
I get back a sort of confused Hyperclient::Resource
that can't tell me the media type is jpeg or whatever. I could call get
instead of resource
, but then I have to know more about the foreign API.
I wonder if we shouldn't make it so that Hyperclient, when it sees a response from a server that isn't Content-Type: application/hal+json
(or maybe a list to include normal application/json
and that a user could add to), it just hands over the Faraday::Response
object or something similar. Basically, it punts and says, "This isn't hypermedia I know about, you handle this bullshit."
Thoughts?
I'm trying to use faraday-http-cache with Hyperclient, but it isn't proving easy.
faraday-http-cache expects to sit before :net_http
in the middleware stack. A working example from their docs looks like this:
client = Faraday.new('https://api.github.com') do |stack|
stack.response :json, content_type: /\bjson$/
stack.use :http_cache, logger: ActiveSupport::Logger.new(STDOUT)
stack.adapter Faraday.default_adapter
end
# which has this middleware:
[FaradayMiddleware::ParseJson, Faraday::HttpCache, Faraday::Adapter::NetHttp]
However, trying to achieve the same thing in Hyperclient doesn't work as expected. The following setup, which I would expect to work, results in duplicate :net_http
in the middleware.
client = Hyperclient.new('https://api.github.com')
client.connection.use :http_cache, logger: ActiveSupport::Logger.new(STDOUT)
client.connection.adapter Faraday.default_adapter
# which has this middleware (see the duplicate NetHttp):
[FaradayMiddleware::EncodeJson, FaradayMiddleware::ParseJson, Faraday::Adapter::NetHttp, Faraday::HttpCache, Faraday::Adapter::NetHttp]
This is because of the way Hyperclient initialises the Faraday connection (see default_faraday_block) with the :net_http
adapter when it creates the connection.
The only way I've got this working was by monkey patching or subclassing EntryPoint and overriding the default_faraday_block
so the :http_cache
middleware is inserted before :net_http
.
class Hyperclient::EntryPoint
def default_faraday_block
lambda do |faraday|
faraday.request :json
faraday.response :json, content_type: /\bjson$/
faraday.use :http_cache, logger: ActiveSupport::Logger.new(STDOUT)
faraday.adapter :net_http
end
end
end
Advice? This is less than ideal.
When creating a new resource, return a Resource instance instead of the raw JSON response.
Does anyone have any pointers for using Hyperclient in testing? I'm writing an API (using Rails) and would love to use Hyperclient in my high level tests (RSpec request specs) in a similar manner to doing Capybara stuff (RSpec feature specs). I just don't know enough about the environment to set the entry point right so that it hits the local app and doesn't make real requests.
I'd be happy to make a PR adding any collective wisdom to the README. Or if code needs to be tweaked, I'd be happy to do that, too, given a little guidance.
It would be awesome if version 0.8.2 was released, so we at @Talkdesk could use the new async disable option.
I'm using Hyperclient and have configured the underlying Faraday instance to use Net::HTTP::Persistent which, when you're making a ton of requests to the same server, can be a huge win for a process that's talking to an external API.
In my testing, I'm using WebMock to stub out and set expectations about my usage of the external API. I ran into a weird issue: When I'm using Net::Http::Persistent, it seems like Hyperclient is making 2 requests where I would expect 1.
Example script:
require 'net/http/persistent'
require 'webmock'
require 'pry'
require 'webmock/rspec'
require 'rspec'
require 'hyperclient'
uri = "http://example.com"
http = Hyperclient.new(uri).tap do |client|
client.headers['Accept'] = 'application/hal+json'
client.connection.adapter :net_http_persistent
end
WebMock.disable_net_connect!
describe "Using WebMock and Net::HTTP::Persistent together" do
it "makes only one request" do
WebMock.stub_request(:get, uri)
http.get
expect(WebMock).to have_requested(:get, uri)
end
end
When I run this, behold:
❱ rspec -fd net_http_persistent_web_mock.rb
Using WebMock and Net::HTTP::Persistent together
makes only one request (FAILED - 1)
Failures:
1) Using WebMock and Net::HTTP::Persistent together makes only one request
Failure/Error: expect(WebMock).to have_requested(:get, uri)
The request GET http://example.com/ was expected to execute 1 time but it executed 2 times
The following requests were made:
GET http://example.com/ with headers {'Accept'=>'application/hal+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.8.8'} was made 1 time
GET http://example.com/ with headers {'Accept'=>'application/hal+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Connection'=>'keep-alive', 'Content-Type'=>'application/json', 'Keep-Alive'=>'30', 'User-Agent'=>'Faraday v0.8.8'} was made 1 time
============================================================
# ./net_http_persistent_web_mock.rb:23:in `block (2 levels) in <top (required)>'
Finished in 0.00544 seconds
1 example, 1 failure
Failed examples:
rspec ./net_http_persistent_web_mock.rb:18 # Using WebMock and Net::HTTP::Persistent together makes only one request
If you comment out line 12 of the test script, it works fine, and if I use Faraday directly (with Net::HTTP::Persistent configured), it works fine.
Any ideas, here? If someone can point me in the right direction, I'll gladly investigate on my own and work up a fix. I'd hate to have to abandon persistent HTTP connections because hypermedia APIs tend to be pretty chatty.
If the server sends and Allow header, honour it and don't let the client send unallowed requests
I am new to HAL, so maybe my API is not doing the right thing per spec, will be glad to change it if needed.
I have a a collection of applications and the following API root.
{
_links: {
self: {
href: "http://localhost:3000/api/"
},
applications: {
href: "http://localhost:3000/api/applications"
}
}
}
I can do api.links.applications
which calls /api/applications
which returns an embedded collection of applications, all good. Each application has a link to "self".
Now, I have a Rails app that needs to edit one of those applications. So I get some application ID. How am I supposed to fetch a single application? I do have a route /api/applications/:id
which returns the application JSON directly, with its own links.
{
id: "53fc980f64626c2dd9000000",
created_at: "2014-08-26T14:22:07.000Z",
name: "test",
...
_links: {
self: {
href: "http://localhost:3000/api/applications/53fc980f64626c2dd9000000"
}
}
Am I missing a template at the root? What should that be? How can I retrieve that with hyperclient?
We're big fans of json-api @balanced and I think your code could be something core to the balanced/balanced-ruby project.
We currently do not support HAL however I would like to extend HyperClient for json-api support.
Wanted to get your thoughts on creating a milestone that we can contribute to.
The idea here is that the client shouldn't care whether an object is linked or embedded. Hyperclient could provide an API that could navigate to an object regardless of which way the server provided it. It would work with the GET verb only (if it's a link). Does this sound like an idea that has merit? Do you think it would be acceptable as a new feature of Hyperclient?
Scenario: Send JSON data
✔ Given I use the default hyperclient config # features/steps/default_config.rb:4
✔ When I send some data to the API # features/steps/default_config.rb:12
! Then it should have been encoded as JSON # features/steps/default_config.rb:17
The request POST http://api.example.org/posts with body "{\"title\":\"My first blog post\"}" was expected to execute 1 time but it executed 0 times
The following requests were made:
GET http://api.example.org/ with headers {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.0'} was made 1 time
============================================================
/Users/dblock/.rvm/rubies/ruby-2.0.0-p353/lib/ruby/gems/2.0.0/gems/webmock-1.18.0/lib/webmock/assertion_failure.rb:7:in `failure'
Scenario: Parse JSON data
✔ Given I use the default hyperclient config # features/steps/default_config.rb:4
✔ When I get some data from the API # features/steps/default_config.rb:21
✔ Then it should have been parsed as JSON # features/steps/default_config.rb:25
Error summary:
Errors (1)
Default config :: Send JSON data :: Then it should have been encoded as JSON
This is intermittent, 3 out of 10 runs. Looks suspicious.
Within link#resource(), when get() is called, only the body is saved, and the status code is discarded. That's not very handy. I'd like to add an attribute to Hyperclient::Resource for the status. Does that sound ok? Is there anything I should consider before making a PR?
The homepage link in the gemspec, which is also used on rubygems.org, points to http://codegram.github.com/hyperclient/, which is currently blank.
Probably going to fix it myself (point at github), but wanted to open an issue to track it.
Hi all,
Sometimes I use the Hyperclient::Resource#[] as a hash, but I would like to avoid if statements when the attribute does not exist for some reason.
Did you consider implement the method fetch with similar behaviour of Hash#fetch in ruby?
Thanks!
The change in #87 to fix some eager skipping of links may have caused a regression. See dblock/slack-gamebot@a148310. The .count
method is not being delegated to a fetched embedded collection anymore.
From what I gleaned from the issue tracker, HTTParty should support this already so it shouldn't be too hard to add it.
This cuts HAL payloads considerably, especially with several embedded resources.
The uri_template
gem, which hyperclient uses to build urls, implements RFC6570, that expands a parameter in the {?foo*}
format as ?foo=a&foo=b&foo=c
.
Unfortunately, faraday's default argument parser (Faraday::Utils.default_params_encoder
) is set to the NestedParamsEncoder
which only considers parameters to be arrays if they have []
in the key.
The result is that values for a parameter are dropped in this process:
require 'faraday'
require 'uri_template'
Faraday::Utils.default_params_encoder.decode(
URITemplate.new('http://example.com/{?foo*}').expand(foo: ['a', 'b']).split('?').last
)
=> {"foo"=>"b"}
And thus the resulting faraday request will be missing some of the values.
The solution is to use faraday's other parameter encoder (the FlatParamsEncoder
).
I'm working on a PR to fix this, but wanted to open the issue to have something to reference.
If the changes I proposed in #34 happen, it will be common for a consumer to unknowingly receive a partial representation of a resource.
Embedded Resources MAY be a full, partial, or inconsistent version of the representation served from the target URI.
For example, say a root resource has the an embedded categories
relation:
{
"_embedded": {
"categories": [
{"name": "Hypermedia", "_links": {"self": {"href": "http://example.com/hypermedia"}}}
]
}
Following the self link on the embedded category gives something like this:
{
"id": 1,
"name": "Hypermedia",
"_embedded": {
"talks": [
{"title": "Introduction to Hypermedia", "_links": {"self": {"href": "..."}}}
]
}
If the proposed changes in #34 happen, I would imagine a consumer doing something like:
Hyperclient.new(url).categories.each do |category|
puts category.attributes.name
category.talks.each do |talk|
puts "* #{talk.attributes.title}"
end
end
The problem is that this could would blow up when the consumer calls category.talks
because the category resource has no clue that it is only a partial representation.
It would be amazing if hyperclient were aware of this and could attempt to reload the partial resource (which it knows is partial because it came from an embed) from the self
link if an unknown attribute or relation is requested.
This should be fairly easy to implement, and still be clean, but I wanted to get your thoughts before starting on it.
Im having trouble with curies and what appears to be a difference in behavior in hyperclient vs hal-browser. Hyperclient is templating urls when not expected.
My server returns:
"curies": [
{
"name": "osdi",
"href": "http://opensupporter.github.io/osdi-docs/{rel}",
"templated": true
}
],
"osdi:tags": {
"href": "http://demo.osdi.io/api/v1/tags",
"title": "The collection of tags in the system"
},
In the hal browser, the curie is just used for documentation links and it does not reformat the link URL, which is already absolute.
But in hyperclient, it is reformatting the url for the link itself as:
http://opensupporter.github.io/osdi-docs/http://demo.osdi.io/api/v1/tags
How do I reconcile this? Can I disable curie reformatting in hyperclient?
Reference: #64
Any guidance on the best practices for supporting paging (next links) with hyperclient?
Hi,
I'm running into a code smell in my Hyperclient. It has to do with Hyperclient::Attributes being returned from top-level attributes of a resource, but not for anything beneath.
Thus, on a top-level resource, you can do: my_resource.attributes.foo
or my_resource.attributes['foo']
but not:
my_resource.attributes.foo.bar
Instead you must:
my_resource.attributes.foo['bar']
or the more consistent but not enforced and less concise my_resource.attributes['foo']['bar']
That's not great, but it's ok. But there are other smells. For example, you can:
my_resource.attributes.respond_to?(:foo)
but not my_resource.attributes.foo.respond_to?(:bar)
Also you can my_resource.attributes.foo.has_key?('bar')
, but you can't do my_resource.attributes.has_key?('foo')
(even though you can access foo by key)
One way to reduce this smell is to change Hyperclient::Collection to extend Hash. The majority of the methods could be removed except for method_missing & responds_to_missing? See, for example, Thor::CoreExt::HashWithIndifferentAccess. This approach would allow .has_key?() on the top-level Attributes object.
Another way would be to provide an option to not use the Attribute class, ensuring consistency because everything past .attributes
is a simple Hash.
There's also the recursive-open-struct gem, but I'm not sure that's a great idea.
Would you accept a PR adding an option to disable returning an Attribute object (returning the hash instead)? If not, would you accept a PR that somehow allows .has_key?()
on Attribute?
The examples that were in this project died with the heroku apps. Add a demo using http://api.m.ox.ac.uk or others.
I think hyperclient should by default follow redirects, which means adding to
: faraday.use FaradayMiddleware::FollowRedirects
Currently monkey-patching
require 'hyperclient/entry_point'
module Hyperclient
class EntryPoint < Link
def default_faraday_block
lambda do |faraday|
faraday.use FaradayMiddleware::FollowRedirects
faraday.request :json
faraday.response :json, content_type: /\bjson$/
faraday.adapter :net_http
end
end
end
end
Thoughts?
I'd like to be able to access the raw JSON response from a Resource. I've noticed that the Resource class now has an attribute accessor for the response object. Unfortunately there seems to be a bug caused by the Attributes class deleting the '_link' and '_embedded' keys from the representation parameter. When these keys are removed from the representation it mutates the response.body.
I think this can be fixed fairly easily by calling .dup
on the response.body when initialising the Resource. Does this sounds like a reasonable solution?
I'm happy to put a pull request together if you agree with the approach.
Hyperclient v0.8.3 implements FlatParamsEncoder, which was implemented in Faraday v0.9 and above. This breaks compatibility with Faraday less than v0.9. I wanted to see if this change was intensional, and is so, if you'd consider adding the minimum version of faraday in your gemspec so that bundler can figure out what version of hyperclient to use based on the app's version of Faraday.
Porting from TODO.
If you are making a request from a spec with setup that is something like this
Hyperclient.new('http://example.org/') do |client|
client.connection(default: false) do |conn|
conn.request :json
conn.response :json
conn.use Faraday::Adapter::Rack, app
end
end
FaradayMiddleware::EncodeJson will not convert the request to json because it isn't aware of 'application/hal+json'
We monkeypatched it locally to work around the problem but it still isn't pretty.
module FaradayMiddleware
class EncodeJson < Faraday::Middleware
MIME_TYPE = 'application/hal+json'.freeze
end
end
Alternatively, resetting the client's content type to application/json will work.
Hyperclient.new('http://example.org/') do |client|
client.headers['Content-Type'] = 'application/json'
client.connection(default: false) do |conn|
conn.request :json
conn.response :json
conn.use Faraday::Adapter::Rack, app
end
end
Hyperclient is spelled with a lowercase c, but in some places, like in the description of this repo and in .gemspec it says HyperClient. While we're making backwards incompatible changes all over the place, curious what @oriolgual and others think about doing the rename into HyperClient? I personally always try to spell it this way.
How can I tell hyperclient to either not use futuroscope or to stop using a thread pool?
In production i can see the use of it, but in development debugging it makes it near impossible to step through what is going on.
Hi all, I'm trying to use Hyperclient to consume the API of a server that it's build with perl+Catalyst and exposes outside as JSON+HAL, but I'm stuck with a parsing error that Hyperclient throw me and I'm unnable to figure what's happen.
This is the full response when I query the resource 'subscribers':
{
"_embedded" : {
"ngcp:subscribers" : [
{
"_links" : {
"collection" : {
"href" : "/api/subscribers/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"ngcp:callforwards" : {
"href" : "/api/callforwards/52"
},
"ngcp:calls" : {
"href" : "/api/calls/?subscriber_id=52"
},
"ngcp:customers" : {
"href" : "/api/customers/3"
},
"ngcp:domains" : {
"href" : "/api/domains/3"
},
"ngcp:journal" : {
"href" : "/api/subscribers/52/journal/"
},
"ngcp:reminders" : {
"href" : "/api/reminders/?subscriber_id=52"
},
"ngcp:subscriberpreferences" : {
"href" : "/api/subscriberpreferences/52"
},
"ngcp:subscriberregistrations" : {
"href" : "/api/subscriberregistrations/?subscriber_id=52"
},
"ngcp:voicemailsettings" : {
"href" : "/api/voicemailsettings/52"
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/subscribers/52"
}
},
"administrative" : false,
"alias_numbers" : [],
"customer_id" : 3,
"domain" : "proxy.testserver.org",
"domain_id" : 3,
"email" : null,
"external_id" : null,
"id" : 52,
"password" : "testtest",
"primary_number" : {
"ac" : "949",
"cc" : "1",
"sn" : "3465301"
},
"profile_id" : null,
"profile_set_id" : null,
"status" : "active",
"username" : "test.fax",
"uuid" : "45cdcf6f-3f39-4245-bccb-3db775622701",
"webpassword" : null,
"webusername" : "test.fax"
},
{
"_links" : {
"collection" : {
"href" : "/api/subscribers/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"ngcp:callforwards" : {
"href" : "/api/callforwards/55"
},
"ngcp:calls" : {
"href" : "/api/calls/?subscriber_id=55"
},
"ngcp:customers" : {
"href" : "/api/customers/3"
},
"ngcp:domains" : {
"href" : "/api/domains/3"
},
"ngcp:journal" : {
"href" : "/api/subscribers/55/journal/"
},
"ngcp:reminders" : {
"href" : "/api/reminders/?subscriber_id=55"
},
"ngcp:subscriberpreferences" : {
"href" : "/api/subscriberpreferences/55"
},
"ngcp:subscriberregistrations" : {
"href" : "/api/subscriberregistrations/?subscriber_id=55"
},
"ngcp:voicemailsettings" : {
"href" : "/api/voicemailsettings/55"
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/subscribers/55"
}
},
"administrative" : false,
"alias_numbers" : [],
"customer_id" : 3,
"domain" : "proxy.testserver.org",
"domain_id" : 3,
"email" : null,
"external_id" : null,
"id" : 55,
"password" : "tfp6LzOxTFa2OWHF",
"primary_number" : {
"ac" : "919",
"cc" : "1",
"sn" : "8675959"
},
"profile_id" : null,
"profile_set_id" : null,
"status" : "active",
"username" : "7WOkIWgU",
"uuid" : "df0ed22e-d0db-406b-b1cf-0f068f5778d4",
"webpassword" : null,
"webusername" : "7WOkIWgU"
}
]
},
"_links" : {
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"ngcp:subscribers" : [
{
"href" : "/api/subscribers/52"
},
{
"href" : "/api/subscribers/55"
}
],
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/subscribers/?page=1&rows=10"
}
},
"total_count" : 2
}
And this is the error Hyperclient throw me when I try to do a simple iteration over api.subscribers.each to just puts the subscriber.id:
RuntimeError: Invalid response for LinkCollection. The response was: "_links"
from /usr/local/rvm/gems/ruby-2.3.3/gems/hyperclient-0.8.5/lib/hyperclient/link_collection.rb:18:in `initialize'
Why it's unable the full parse the response and let my iterate over the subscribers collection ? ... I've checked that the response is ok.
Add your project to the wiki: https://github.com/codegram/hyperclient/wiki
care to release a new gem version with the faraday changes included?
When a resource includes some field that is false
, trying to read it through the Link
class will result in the value being returned as nil
instead of false
.
Test case:
diff --git a/test/hyperclient/link_test.rb b/test/hyperclient/link_test.rb
index 823efa6..20e8872 100644
--- a/test/hyperclient/link_test.rb
+++ b/test/hyperclient/link_test.rb
@@ -479,6 +479,16 @@ module Hyperclient
resource.orders.first.id.must_equal 1
end
+ it 'can handle false values in the response' do
+ resource = Resource.new({ '_links' => { 'orders' => { 'href' => '/orders' } } }, entry_point)
+
+ stub_request(entry_point.connection) do |stub|
+ stub.get('http://api.example.org/orders') { [200, {}, { 'any' => false }] }
+ end
+
+ resource.orders.any.must_equal false
+ end
+
it "doesn't delegate when link key doesn't match" do
resource = Resource.new({ '_links' => { 'foos' => { 'href' => '/orders' } } }, entry_point)
Current result:
Hyperclient::Link::method_missing::delegation
FAIL (0:00:00.145) test_0002_can handle false values in the response
Expected: false
Actual: nil
@ (eval):8:in `must_equal'
test/hyperclient/link_test.rb:489:in `block (4 levels) in <module:Hyperclient>'
This seems to me to be due to a bad interaction in Link#method_missing
(https://github.com/codegram/hyperclient/blob/master/lib/hyperclient/link.rb#L129):
# Internal: Delegate the method further down the API if the resource cannot serve it.
def method_missing(method, *args, &block)
if _resource.respond_to?(method.to_s)
_resource.send(method, *args, &block) || delegate_method(method, *args, &block)
else
super
end
end
In this case, _resource.send(method, *args, &block)
is returning false
, which we are mistaking for the case where we're trying to call an array method on the resource, and thus instead of returning false
we are calling delegate_method
(which will then return the nil
).
I was thinking about it and it seems to me that changing it to treat false
differently would fix the issue:
diff --git a/lib/hyperclient/link.rb b/lib/hyperclient/link.rb
index 1fd1169..cc5b635 100644
--- a/lib/hyperclient/link.rb
+++ b/lib/hyperclient/link.rb
@@ -126,7 +126,8 @@ module Hyperclient
# Internal: Delegate the method further down the API if the resource cannot serve it.
def method_missing(method, *args, &block)
if _resource.respond_to?(method.to_s)
- _resource.send(method, *args, &block) || delegate_method(method, *args, &block)
+ result = _resource.send(method, *args, &block)
+ result.nil? ? delegate_method(method, *args, &block) : result
else
super
end
But I was not entirely sure, hence I decided to open an issue first, instead of jumping straight to a PR. Any thoughts on this approach?
See ruby-grape/grape-with-roar#9.
Doesn't work:
require 'hyperclient'
client = Hyperclient.new('http://localhost:9292/api')
3.times do |i|
client.splines._post(spline: { name: i.to_s, reticulated: [true, false].sample })
end
client.splines.each do |spline|
puts "spline #{spline.id} #{spline.reticulated ? 'is' : 'is not'} reticulated"
end
client.splines.each(&:_delete)
Works:
require 'hyperclient'
client = Hyperclient.new('http://localhost:9292/api')
3.times do |i|
client.splines._post(spline: { name: i.to_s, reticulated: [true, false].sample })
end
client = Hyperclient.new('http://localhost:9292/api')
client.splines.each do |spline|
puts "spline #{spline.id} #{spline.reticulated ? 'is' : 'is not'} reticulated"
end
client = Hyperclient.new('http://localhost:9292/api')
client.splines.each(&:_delete)
I am getting "Faraday::Error::ConnectionFailed: end of file reached" if my request takes any longer than one minute.
However, I can't figure out how to get options to Faraday through the Hyperclient API. Hyperclient::EntryPoint.connection calls Faraday.new with a hard-coded options hash, and from there I can't figure out how I might access/modify that hash. Any ideas?
Implement a simple Resource identity map. Check if there's already a Resource created with that url and return that instance instead of creating it.
While working on #122 I started wondering if Link#http_method
was correct when using the Futuroscope::Future
:
def http_method(method, body = nil)
@resource = begin
response =
if @entry_point.options[:async]
Futuroscope::Future.new do
@entry_point.connection.run_request(method, _url, body, nil)
end
else
@entry_point.connection.run_request(method, _url, body, nil)
end
Resource.new(response.body, @entry_point, response)
end
end
More specifically, we create a new Futuroscope::Future
, store it in response
, but then immediately ask for it's resource.body
when creating a new resource. This means that we basically block the current thread while waiting for the future to resolve so we can call body
on it. After the request finishes executing in the background, we finally get the body
, and are able to create an return the new Resource
.
Looking at git history, this has been broken since the Resource
was introduced in 5de6f84, which was released in version 0.6 in 2014.
So this means that for all of almost three years nobody really noticed that this was broken, which leads me to make some observations:
async: false
to be the default behavior instead, since that's how people have been using it for a long time?concurrent-ruby
which has been adopted by the likes of activesupport
, dry-*
, graphql
, hanami
, rom
, sidekiq
, etc. and thus most ruby applications probably already have it as a direct or indirect dependency?Hi all, have a slight conundrum.
Looking at the Hyperclient::Link
https://github.com/codegram/hyperclient/blob/master/lib/hyperclient/link.rb#L84-L91
the _get call on it cannot pass any parameters as params unlike the other options (post, put, and patch)
Where get params are required it cannot be achieved.
Hi guys, I'm using v0.8.1 and am having a discrepancy when using #to_h in place of #to_hash on a Hyperclient::Resource.
cluster_meters = Source.clusters._get.by_id(id: id)._get.meters._embedded
meter = gateway_meters.all.first
expect(meter.to_h).to eq(meter.to_hash)
However the call to #to_h returns nil, while the call to #to_hash returns the expected hash
This is a major refactor, but Hyperclient should not differntiate between links
and embeded
they are all resource with more or less information.
Instead of accessing client.links.posts.first
or client.embedded.posts.first
one should be able to simply access client.resources.posts.first
I'm getting the weirdest bug with my Hyperscore client.
If I do:
Hyperscore.new.links.news.resources
#=> a bunch of different news
I can see that everything is fine, as the resources are as they should.
But, once I try to access them, either of the following ways, then every resource turns into the newest one:
Hyperscore.new.links.news.resources.news
#=> a bunch of copies of news 19
Do you have any idea what's going on? It's really confusing to me.
I don't know that Link#post
should actually return a Resource
since you'd lose access to the Faraday response (though, if #32 gets sorted, that'll fade away). But somehow, it would be nice to be able to make a POST request and still get a rich Hyperclient object back, rather than the Faraday response.
Honestly, if #32 were resolved and you had the response stored with a Resource
, you could get rid of Link#resource
(deprecate it first, of course) and just use methods named for the HTTP verbs and have them all return a Resource
. That would be perfect, I think.
Imagine a scenario:
### Rels
#### create_article
This rel will create a new blog post. You must POST to it and include the `body` and `title` parameters. All others are optional.
c = Hyperclient.new('http://example.com/')
response = c.links.create_article.expand(title: 'Awesome Article', body: awesome_article_body).post
Say that works and returns you 201 with a response body that is hal describing the newly create article. It'll have links you might want to follow, etc. However, if you really want to follow them you currently have two options:
Resource
object and then go to work.resource
on the expanded link to get the representation anew, using another HTTP request to refetch data the server already sent you.Thoughts? Concerns? Pitchforks?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.