GithubHelp home page GithubHelp logo

sawyer's Introduction

Sawyer

Sawyer is an experimental hypermedia agent for Ruby built on top of Faraday.

Installation

Add this line to your application's Gemfile:

gem 'sawyer'

And then execute:

bundle

Or install it yourself as:

gem install sawyer

Usage

require "sawyer"

# Create a Sawyer agent
agent = Sawyer::Agent.new("https://api.github.com",
  links_parser: Sawyer::LinkParsers::Simple.new)

# Fetch the root of the API
root = agent.root.data

# Access a resource directly
contributors = agent.call(:get, "repos/lostisland/sawyer/contributors").data

# Load a hypermedia relation
top_contributor = contributors.first
followers = top_contributor.rels[:followers].get.data

For more information, check out the documentation.

Development

After checking out the repo, run script/test to bootstrap the project and run the tests. You can also run script/console for an interactive prompt that will allow you to experiment.

To package the gem, run script/package. To release a new version, update the version number in lib/sawyer.rb, and then run script/release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Check out the contributing guide for more information on contributing.

License

The gem is available as open source under the terms of the MIT License.

sawyer's People

Contributors

alyssais avatar andrew avatar casao avatar chytreg avatar dasch avatar davidcornu avatar hbu50 avatar imactia avatar ioquatix avatar matadon avatar mrpinsky avatar pengwynn avatar pseudomuto avatar pvdb avatar rochefort avatar sarahhodne avatar skryukov avatar spacewander avatar tas50 avatar technoweenie avatar timoschilling avatar tkowark avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sawyer's Issues

Update README to be useful

Currently the README for this project is at best useless. Can one of the minatanters be responsible and update it so that it is actually useful for the community? I personally spent more time that I would like to admit trying to figure out why this gem was not doing what I expected it to do and realized the strange behavior by looking into the source code, a README of value would have avoided what I went though.

Expand on README?

Ok firstly, the README is awesome. However, as someone arriving from octokit, trying to understand how this gem fits in, it's not very helpful in understand what this gem adds :)

Any chance you might think about expanding on it?

Failing to restore resource.rels[:x].get after marshal_load

Finding it difficult to restore functionality after serializing/deserialzing a Sawyer::Resource.

For example, starting with the github Octokit gem

repo = Octokit.repo('kubernetes/kubernetes');
repo.class #=> Sawyer::Resource

users = repo.rels[:contributors].get.data;
users.class #=> Array

dumped = Marshal.dump( repo.marshal_dump );
dumped.class #=> String

agent = Sawyer::Agent.new('https://api.github.com/', links_parser: Sawyer::LinkParsers::Simple.new)
resource = Sawyer::Resource.new(agent)
resource.marshal_load(Marshal.restore(dumped))
resource.class #=> Sawyer::Resource
resource.rels.class #=> Sawyer::Relation::Map
resource.rels[:contributors]
#=> #<Sawyer::Relation: contributors: get #<Addressable::Template:0x000055be59bf7030>>

resource.rels[:contributors].get
NoMethodError: undefined method `get' for nil:NilClass
Did you mean?  gets
               gem
from {HOME}/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/sawyer-0.8.1/lib/sawyer/agent.rb:94:in `call'

How to serialize a Sawyer::Resource object?

The new release of Octokit uses Sawyer to replace Hashie::Mash. I would like to cache some results as such pull requests. How should I serialize Sawyer::Resource objects and save them to disk?

I tried YAML and it complaint can't dump anonymous class. I also tried Marshal and it complaint singleton can't be dumped.

Same behavior as Mashie?

Some GitHub users don't have names, which means this fails:

Octokit.user('maccman').name

Before Octokit 2.0 this would have just return nil (from Mashie). I propose we either follow Mashie's way of doing things, or allow

Octokit.user('maccman').name? #=> 'Alex' or nil

If that sounds agreeable I'll submit a pull request?

Slashes matter in calls?

Get for instance this Pry session:

โžœ  ~  pry
[1] pry(main)> require 'sawyer'
=> true
[2] pry(main)> agent = Sawyer::Agent.new('http://eval.so/api')
=> <Sawyer::Agent http://eval.so/api>
[3] pry(main)> agent.call :get, 'languages'
=> #<Sawyer::Response: 200 @rels={} @data=#<Sawyer::Resource:0x007ffa460e6148 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:csharp, :rust, :clojure, :python2, :idris, :chickenscheme, :jruby18, :haskell, :perl, :smalltalk, :python3, :java, :factor, :"c++", :go, :jruby19, :php, :lua, :scala, :fsharp, :io, :c, :lolcode, :"sml-mlton", :perl6, :ruby, :elixir, :vbnet, :bash}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e6148>>, @attrs={:csharp=>#<Sawyer::Resource:0x007ffa460e5f90 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e5f90>>, @attrs={:display_name=>"C#"}>, :rust=>#<Sawyer::Resource:0x007ffa460e59a0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e59a0>>, @attrs={:display_name=>"Rust 0.7"}>, :clojure=>#<Sawyer::Resource:0x007ffa460e5568 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e5568>>, @attrs={:display_name=>"Clojure"}>, :python2=>#<Sawyer::Resource:0x007ffa460e5130 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e5130>>, @attrs={:display_name=>"Python 2"}>, :idris=>#<Sawyer::Resource:0x007ffa460e4cf8 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e4cf8>>, @attrs={:display_name=>"Idris"}>, :chickenscheme=>#<Sawyer::Resource:0x007ffa460e48c0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e48c0>>, @attrs={:display_name=>"Chicken Scheme"}>, :jruby18=>#<Sawyer::Resource:0x007ffa460e4370 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460e4370>>, @attrs={:display_name=>"JRuby (1.8 mode)"}>, :haskell=>#<Sawyer::Resource:0x007ffa460d7f08 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d7f08>>, @attrs={:display_name=>"Haskell"}>, :perl=>#<Sawyer::Resource:0x007ffa460d79e0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d79e0>>, @attrs={:display_name=>"Perl"}>, :smalltalk=>#<Sawyer::Resource:0x007ffa460d7490 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d7490>>, @attrs={:display_name=>"Smalltalk"}>, :python3=>#<Sawyer::Resource:0x007ffa460d6ef0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d6ef0>>, @attrs={:display_name=>"Python 3"}>, :java=>#<Sawyer::Resource:0x007ffa460d69a0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d69a0>>, @attrs={:display_name=>"Java"}>, :factor=>#<Sawyer::Resource:0x007ffa460d6478 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d6478>>, @attrs={:display_name=>"Factor"}>, :"c++"=>#<Sawyer::Resource:0x007ffa460d5fa0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d5fa0>>, @attrs={:display_name=>"C++"}>, :go=>#<Sawyer::Resource:0x007ffa460d5a00 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d5a00>>, @attrs={:display_name=>"Go"}>, :jruby19=>#<Sawyer::Resource:0x007ffa460d55a0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d55a0>>, @attrs={:display_name=>"JRuby (1.9 mode)"}>, :php=>#<Sawyer::Resource:0x007ffa460d5140 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d5140>>, @attrs={:display_name=>"PHP"}>, :lua=>#<Sawyer::Resource:0x007ffa460d4d08 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d4d08>>, @attrs={:display_name=>"Lua"}>, :scala=>#<Sawyer::Resource:0x007ffa460d48d0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d48d0>>, @attrs={:display_name=>"Scala"}>, :fsharp=>#<Sawyer::Resource:0x007ffa460d44e8 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d44e8>>, @attrs={:display_name=>"F#"}>, :io=>#<Sawyer::Resource:0x007ffa460d40b0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa460d40b0>>, @attrs={:display_name=>"Io"}>, :c=>#<Sawyer::Resource:0x007ffa4609fbd0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609fbd0>>, @attrs={:display_name=>"C"}>, :lolcode=>#<Sawyer::Resource:0x007ffa4609f748 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609f748>>, @attrs={:display_name=>"LOLCODE (via lci)"}>, :"sml-mlton"=>#<Sawyer::Resource:0x007ffa4609f310 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609f310>>, @attrs={:display_name=>"SML (MLton)"}>, :perl6=>#<Sawyer::Resource:0x007ffa4609eed8 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609eed8>>, @attrs={:display_name=>"Perl 6 (Rakudo Star)"}>, :ruby=>#<Sawyer::Resource:0x007ffa4609eaa0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609eaa0>>, @attrs={:display_name=>"Ruby"}>, :elixir=>#<Sawyer::Resource:0x007ffa4609e618 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609e618>>, @attrs={:display_name=>"Elixir"}>, :vbnet=>#<Sawyer::Resource:0x007ffa4609e1e0 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609e1e0>>, @attrs={:display_name=>"VB.NET"}>, :bash=>#<Sawyer::Resource:0x007ffa4609dd58 @_agent=<Sawyer::Agent http://eval.so/api>, @_rels=#<Sawyer::Relation::Map: []>, @_fields=#<Set: {:display_name}>, @_metaclass=#<Class:#<Sawyer::Resource:0x007ffa4609dd58>>, @attrs={:display_name=>"Bash"}>}>>
[4] pry(main)> agent.call :get, '/languages'
=> #<Sawyer::Response: 404 @rels={} @data="\n\n<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>Action not found</title>\n\t\t<link rel=\"shortcut icon\" href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlFJREFUeNqUU8tOFEEUPVVdNV3dPe8xYRBnjGhmBgKjKzCIiQvBoIaNbly5Z+PSv3Aj7DSiP2B0rwkLGVdGgxITSCRIJGSMEQWZR3eVt5sEFBgTb/dN1yvnnHtPNTPG4PqdHgCMXnPRSZrpSuH8vUJu4DE4rYHDGAZDX62BZttHqTiIayM3gGiXQsgYLEvATaqxU+dy1U13YXapXptpNHY8iwn8KyIAzm1KBdtRZWErpI5lEWTXp5Z/vHpZ3/wyKKwYGGOdAYwR0EZwoezTYApBEIObyELl/aE1/83cp40Pt5mxqCKrE4Ck+mVWKKcI5tA8BLEhRBKJLjez6a7MLq7XZtp+yyOawwCBtkiBVZDKzRk4NN7NQBMYPHiZDFhXY+p9ff7F961vVcnl4R5I2ykJ5XFN7Ab7Gc61VoipNBKF+PDyztu5lfrSLT/wIwCxq0CAGtXHZTzqR2jtwQiXONma6hHpj9sLT7YaPxfTXuZdBGA02Wi7FS48YiTfj+i2NhqtdhP5RC8mh2/Op7y0v6eAcWVLFT8D7kWX5S9mepp+C450MV6aWL1cGnvkxbwHtLW2B9AOkLeUd9KEDuh9fl/7CEj7YH5g+3r/lWfF9In7tPz6T4IIwBJOr1SJyIGQMZQbsh5P9uBq5VJtqHh2mo49pdw5WFoEwKWqWHacaWOjQXWGcifKo6vj5RGS6zykI587XeUIQDqJSmAp+lE4qt19W5P9o8+Lma5DcjsC8JiT607lMVkdqQ0Vyh3lHhmh52tfNy78ajXv0rgYzv8nfwswANuk+7sD/Q0aAAAAAElFTkSuQmCC\">\n\t    <style>\n\t\t    html, body, pre {\n\t\t        margin: 0;\n\t\t        padding: 0;\n\t\t        font-family: Monaco, 'Lucida Console', monospace;\n\t\t        background: #ECECEC;\n\t\t    }\n\t\t    h1 {\n\t\t        margin: 0;\n\t\t        background: #AD632A;\n\t\t        padding: 20px 45px;\n\t\t        color: #fff;\n\t\t        text-shadow: 1px 1px 1px rgba(0,0,0,.3);\n\t\t        border-bottom: 1px solid #9F5805;\n\t\t        font-size: 28px;\n\t\t    }\n\t\t    p#detail {\n\t\t        margin: 0;\n\t\t        padding: 15px 45px;\n\t\t        background: #F6A960;\n\t\t        border-top: 4px solid #D29052;\n\t\t        color: #733512;\n\t\t        text-shadow: 1px 1px 1px rgba(255,255,255,.3);\n\t\t        font-size: 14px;\n\t\t        border-bottom: 1px solid #BA7F5B;\n\t\t    }\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<h1>Action not found</h1>\n\n\t\t<p id=\"detail\">\n\t\t\tFor request 'GET /languages'\n\t\t</p>\n\n\t</body>\n</html>\n\n\n\n\n\n\n\n">

It seems like calling GET on the path languages is successful (200), but /languages fails (404). Is this a bug or a feature? Wouldn't it make sense for them to be optionally included (or required based on the API endpoint - whether it has a / at the end or not)?

Crash on response 'Link" header parsing

If a response 'Link' header not exactly formatted as:

Link: < uri-reference >; rel="value";

parsing it (as done at https://github.com/lostisland/sawyer/blob/master/lib/sawyer/response.rb#L52) results in a crash:

NoMethodError: undefined method `captures' for nil:NilClass
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/response.rb:52:in `block in process_rels'
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/response.rb:51:in `map'
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/response.rb:51:in `process_rels'
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/response.rb:20:in `initialize'
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/agent.rb:107:in `new'
.../vendor/bundle/ruby/2.6.0/gems/sawyer-0.8.2/lib/sawyer/agent.rb:107:in `call'
.../vendor/bundle/ruby/2.6.0/gems/octokit-4.19.0/lib/octokit/connection.rb:156:in `request'
.../vendor/bundle/ruby/2.6.0/gems/octokit-4.19.0/lib/octokit/connection.rb:28:in `post'
.../vendor/bundle/ruby/2.6.0/gems/octokit-4.19.0/lib/octokit/client/users.rb:57:in `exchange_code_for_token'

By specification (see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) the generic format is:

Link: < uri-reference >; param1=value1; param2="value2"

where:

  • the rel param is not required to be 1st
  • the value is not required to be quoted

In fact recent change in GitHub's API the returned header is like:

link: "<https://github.githubassets.com/assets/chunk-frameworks-aaac5e7e.js>; integrity=sha512-qqxefjEZzhP8x0ed+F3sDEdSogyDs+0ThK6EYXtqye9PJ26t5C996Q9EuRBr+296TAbc1rimsOSeZjZXJac1cA==; rel=preload; crossorigin=anonymous; as=script,<https://github.githubassets.com/assets/chunk-vendor-6594a208.js>; integrity=sha512-ZZSiCHJ85gkTIyggKfuBXsl3a7e+GPhw6iUguJ6eki2dXkGnOFBpQlUUG+gcTSJzgE8ii1cYyM7aLYXZeSeDbA==; rel=preload; crossorigin=anonymous; as=script,<https://github.githubassets.com/assets/behaviors-bf28db4d.js>; integrity=sha512-vyjbTdEGTiemoiT41KyVSh3+NxJv285wa1qlccyGrTsQsAAvhBSUrT0t1LZbJVEfccSTx+zLAyTekiljOX2wKA==; rel=preload; crossorigin=anonymous; as=script,<https://github.githubassets.com/assets/environment-f0adafbf.js>; integrity=sha512-8K2vvwbW+6H27Nad5ydg8PA2/aMD/LKq+EiK9s0U0hhVZxCI2tWBsYk9beAtisRw2j+Or5k2/F+6dk02nmj/PA==; rel=preload; crossorigin=anonymous; as=script"

Notice:

  • there is integrity before rel
  • rel is not quoted
  • there is no space after a , (comma)

Sawyer::Resource.as_json returns nested Arrays

I don't grok why this library exists or why octokit felt the need to use it, but it gets a very fundamental thing wrong. Sawyer::Resource.as_json returns nested Arrays rather than a nested Hash. This makes it really hard to work with out of the box.

image

Faraday dependency

faraday gem dependency is currently ['~> 0.8', '< 1.0']

It should be ['>= 0.8', '< 1.0'] ? Currently version 0.9 of faraday is not allowed.

Relation `get` method can't take `options`

Description

Unlike every other HTTP verb, get takes data but not options. Although named options in the method listed below, when passed to call this is interpreted as data. This means that you can't set custom headers or anything else from a Relation.

https://github.com/lostisland/sawyer/blob/master/lib/sawyer/relation.rb#L160

Given that the comment above the method shows both data and options this might just be an innocent copy+paste issue. If so, please let me know and I'll issue a PR.

Workaround

Until fixed, just call the call method directly.

# GET params as first hash, method and headers as second hash.
rel.call({get_param: "value"}, {method: :get, headers: {accept: 'application/json'}}).data

Unexpected method was called when receive some JSON response

Hi, I have a trouble. Please see below.

An API is expected to return following result as JSON.

{
  "age_range": {
    "max": 30,
    "min": 20
  }
}

A Sawyer::Agent instance returns result as Sawyer::Resource.

agent = Sawyer::Agent.new('https://example.com')
result = agent.call(:get, 'age_range')
result[:max] # => 30

However, if the API returns following result, above execution will unexpected result.

{
  "age_range": {
    "min": 20
  }
}
result = agent.call(:get, 'age_range')
result[:max] # => [:min, 20]

I think the cause is into the Sawyer::Resource implementation. Sawyer::Resouce#[] calls #method_missing and searches the key from inner fields. Ordinarily it will return nil if does not find the key.

However, :max is Enumerable module instance so it will happen that calls Enumerable#max method.

allow faraday 2.x?

The latest release as well as master branch of sawyer do not allow Faraday 2.x.

spec.add_dependency 'faraday', "~> 1.0"

Faraday 2.0 was released on January 04 2022, and is already up to 2.2.0.

Sawyer is deep in my dependency tree -- like 5 or 6 levels down, dependency of a dependency of a etc. And is preventing my app from upgrading to faraday 2.0.

Is there any chance of a sawyer release that allows both faraday 1.x and 2.x? Any backwards breaking changes were relatively minor, it should be possible to allow both 1.x and 2.x -- which I really recommend, updating to support only 2.x will actually be challenging since so many projects have so many indirect faraday dependencies, getting them all to agree is hard.

If there is a maintainer reading this, appreciate hearing from you! Thank you!

Relax dependency on addressable.

Hey folks, I see sawyer has a hard dependency on addressable < 2.6. Is that for a good reason? Can we relax the dependency? Maybe ~> 2.3?

YAML marshal broken in Ruby 2.5+ (Psych 3.0+)

Please see below, it seems to be a breaking change with Psych from Ruby 2.4 to Ruby 2.5.

Ruby 2.4 shipped Psych 2.2.2 and those tests passed.

Ruby 2.5 shipped Psych 3.0.0 and those tests are failing.

admin@ip-172-31-24-33:~/sawyer$ bundle exec rake
/home/admin/.rubies/ruby-2.5.0/bin/ruby -w -I"lib:lib:test" -I"/home/admin/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/rake-12.3.0/lib" "/home/admin/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/rake-12.3.0/lib/rake/rake_test_loader.rb" "test/agent_test.rb" "test/relation_test.rb" "test/resource_test.rb" "test/response_test.rb" 
/home/admin/.gem/ruby/2.5.0/gems/addressable-2.5.2/lib/addressable/idna/pure.rb:154: warning: assigned but unused variable - startercc
Run options: --seed 34678

# Running:

WARNING: Unexpected middleware set after the adapter. This won't be supported from Faraday 1.0.
.........E............E.........................

Finished in 0.391983s, 122.4542 runs/s, 497.4702 assertions/s.

  1) Error:
Sawyer::AgentTest#test_handle_yaml_dump_and_load:
TypeError: allocator undefined for Method
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:373:in `allocate'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:373:in `revive'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:16:in `visit'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:6:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:32:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:338:in `block in revive_hash'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `each'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `each_slice'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `revive_hash'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:374:in `revive'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:16:in `visit'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:6:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:32:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:338:in `block in revive_hash'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `each'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `each_slice'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:336:in `revive_hash'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:374:in `revive'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:16:in `visit'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:6:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:32:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:311:in `visit_Psych_Nodes_Document'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:16:in `visit'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/visitor.rb:6:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/to_ruby.rb:32:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/nodes/node.rb:50:in `to_ruby'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych.rb:264:in `load'
    /home/admin/sawyer/test/agent_test.rb:174:in `test_handle_yaml_dump_and_load'

  2) Error:
Sawyer::ResourceTest#test_handle_yaml_dump:
TypeError: can't dump anonymous class: #<Class:#<Sawyer::Resource:0x00005593f935e980>>
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:343:in `visit_Class'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:136:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:546:in `block in dump_ivars'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:544:in `each'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:544:in `dump_ivars'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:163:in `visit_Object'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:136:in `accept'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych/visitors/yaml_tree.rb:118:in `push'
    /home/admin/.rubies/ruby-2.5.0/lib/ruby/2.5.0/psych.rb:441:in `dump'
    /home/admin/sawyer/test/resource_test.rb:172:in `test_handle_yaml_dump'

48 runs, 195 assertions, 0 failures, 2 errors, 0 skips
rake aborted!
Command failed with status (1): [ruby -w -I"lib:lib:test" -I"/home/admin/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/rake-12.3.0/lib" "/home/admin/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/rake-12.3.0/lib/rake/rake_test_loader.rb" "test/agent_test.rb" "test/relation_test.rb" "test/resource_test.rb" "test/response_test.rb" ]
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:75:in `load'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:75:in `kernel_load'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:28:in `run'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:424:in `exec'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:27:in `dispatch'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:18:in `start'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/exe/bundle:30:in `block in <top (required)>'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/friendly_errors.rb:122:in `with_friendly_errors'
/home/admin/.gem/ruby/2.5.0/gems/bundler-1.16.1/exe/bundle:22:in `<top (required)>'
/home/admin/.gem/ruby/2.5.0/bin/bundle:23:in `load'
/home/admin/.gem/ruby/2.5.0/bin/bundle:23:in `<main>'
Tasks: TOP => default => test
(See full trace by running task with --trace)
admin@ip-172-31-24-33:~/sawyer$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
admin@ip-172-31-24-33:~/sawyer$ 

Use sawyer with multipart POST?

Hi, I use your great gem to make an API client ruby. It works well when I make basic requests but I have one probleme when I use multipart post. It seems that body isn't parsed by multipart-post.

When I try this with Faraday it works like a charm :

conn = Faraday.new(:url => "http://localhost:8080/chouette_iev/") do |conn|
          # POST/PUT params encoders:
        conn.request :multipart
        conn.request :url_encoded

        conn.adapter :net_http
    end

    action_params_io = Faraday::UploadIO.new("/home/luc/parameters_import_neptune.json", "application/json", "parameters.json")    

    transport_data_io = Faraday::UploadIO.new("/home/luc/demo/15568799.xml", "application/xml", "15568799.xml")

    payload = {
                                                  :file1 => action_params_io,
                                                  :file2 => transport_data_io,
                        }                       

    conn.post "referentials/test2/importer/neptune", payload

But when I user this sawyer configuration body is not splitted in parts :

      opts = {
        :links_parser => Sawyer::LinkParsers::Hal.new
      }
      conn_opts = {
          :headers => {
            :accept => "multipart/form-data",
            :user_agent => " Ievkit Ruby Gem 0.1.0"
          }
        }
      conn_opts[:builder] =  Faraday::RackBuilder.new do |builder|                  
      builder.use Faraday::Request::Multipart
      builder.use Faraday::Request::UrlEncoded
      builder.use Ievkit::Response::RaiseError
      builder.use FaradayMiddleware::FollowRedirects
      builder.use Faraday::Response::Logger

      builder.adapter Faraday.default_adapter
    end
      conn_opts[:proxy] = {}
      opts[:faraday] = Faraday.new(conn_opts)
      opts

@agent ||= Sawyer::Agent.new(api_endpoint, sawyer_options) do |http|
        http.headers[:accept] = default_media_type
        http.headers[:content_type] = "application/json"
        http.headers[:user_agent] = user_agent

Is it possible to make a multipart-post request? Is there a specific way to call?

How can I encode the following hash in my query in Ruby with Sawyer?

The encoding method of this library is killing me. I have a data structure like this:

{paths: [['location', 'ohana-1']]}

Yes, that's an array in an array. Currently it's encoded into JSON like this:

{'paths[][]': [ 'location', 'ohana-1' ] }

Expected encoding:

{paths: [['location', 'ohana-1']]}

Is there any hope here?

How do I keep certain fields from a Sawyer::Resource?

Given I have this:

forks = Octokit.forks('rafalchmiel/friction').first
forks.class # => Sawyer::Resource

How would I keep certain fields (from an Array such as [:id, :full_name]) and delete others from the forks variable? So all I want to be left with, is a Sawyer::Resource with only the :id and :full_name fields.

Adding a small method like delete to the Sawyer::Resource class would be helpful:

def delete(key)
  @_fields.delete key
  @attrs.delete key
end

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.