Git Product home page Git Product logo

hyperclient's Issues

Dealing With Other Media Types

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?

Using Faraday HTTP Cache (or just using Faraday middleware)

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.

Advice For Use In Testing?

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.

Irregular Use of Faraday?

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.

Honour Allow headers

If the server sends and Allow header, honour it and don't let the client send unallowed requests

How do I retrieve standalone instances?

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?

Extending this to support JSON-API

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.

Provide abstraction of linking vs. embedding?

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?

Intermittent test failure

  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.

HTTP status/code not accessible

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?

Accept-Encoding: gzip

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.

Bad interaction between URITemplate and Faraday parameter parsing means dropped values from queries including multiple values for a parameter

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.

Loading full representations of embedded resources

If the changes I proposed in #34 happen, it will be common for a consumer to unknowingly receive a partial representation of a resource.

HAL draft, Section 4.1.2:

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.

Incorrect interpretation of the HAL RFC wrt curies

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

Reducing inconsistency in object vs. hash approach

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?

Follow redirects

I think hyperclient should by default follow redirects, which means adding to

faraday.adapter :net_http
:

        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?

Resource / Attributes is mutating the response.body

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 breaks compatibility with Faraday < v0.9

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.

Content type hal+json doesn't agree with Faraday MIME json type

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

Rename Hyperclient to HyperClient?

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 do I shut off Futuruscope?

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.

Error parsing response

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.

new release

care to release a new gem version with the faraday changes included?

Link method_missing delegation wrongly returns nil when field value is false

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?

Between a _post and a _get, need a fresh client?

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)

Changing settings to deal with "end of file reached" after one minute

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?

A simple identity map for resources

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.

Async background requests with futuroscope have not been async since 2014

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:

  • Would it make sense for async: false to be the default behavior instead, since that's how people have been using it for a long time?
  • Would it make sense to consider removing the background/async functionality altogether?
  • Alternatively, would it make sense to consider replacing futuroscope using the 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?

Hyperclient::Resource#to_h != Hyperclient::Resource#to_hash

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

Merge links & embedded

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

Multiple copies of the same resource

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.

Link#post return Resource?

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:

  1. Hand-build the Resource object and then go to work.
  2. Call 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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.