Skip to content

Commit

Permalink
Omit navigational elements of a HAL response.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Sep 27, 2014
1 parent ecaf3a6 commit af7de24
Show file tree
Hide file tree
Showing 15 changed files with 348 additions and 208 deletions.
22 changes: 15 additions & 7 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2014-09-17 21:23:50 -0400 using RuboCop version 0.26.0.
# on 2014-09-18 12:21:47 -0400 using RuboCop version 0.26.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand All @@ -8,28 +8,36 @@
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 106
Max: 118

# Offense count: 36
# Offense count: 2
Metrics/CyclomaticComplexity:
Max: 8

# Offense count: 39
# Configuration parameters: AllowURI.
Metrics/LineLength:
Max: 140
Max: 145

# Offense count: 1
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 14

# Offense count: 2
Metrics/PerceivedComplexity:
Max: 9

# Offense count: 1
Style/AsciiComments:
Enabled: false

# Offense count: 3
# Offense count: 2
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/ClassAndModuleChildren:
Enabled: false

# Offense count: 15
# Offense count: 14
Style/Documentation:
Enabled: false

Expand All @@ -41,7 +49,7 @@ Style/DoubleNegation:
Style/Lambda:
Enabled: false

# Offense count: 6
# Offense count: 5
# Configuration parameters: MaxSlashes.
Style/RegexpLiteral:
Enabled: false
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
== Next

* backwards incompatible changes
* All navigational structure methods, including `links`, `get` or `post`, have been renamed to `_link`, `_get`, or `_post` respectively (by @dblock).

* enhancements
* [#58](https://github.com/codegram/hyperclient/issues/58): Automatically follow redirects (by [@dblock](https://github.com/dblock)).
* [#63](https://github.com/codegram/hyperclient/pull/63): You can omit the navigational elements, `api.links.products` is now equivalent to `api.products` (by @dblock).
* Implemented Rubocop, Ruby-style linter (by @dblock).

* bug fixes
* Nothing.

* deprecations
* Nothing.

== 0.3.1

* backwards incompatible changes
* Nothing.

Expand Down
91 changes: 62 additions & 29 deletions Readme.md → README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ Example API client:
```ruby
api = Hyperclient.new('http://myapp.com/api').tap do |api|
api.digest_auth('user', 'password')
api.headers.update({'accept-encoding' => 'deflate, gzip'})
api.headers.update('accept-encoding' => 'deflate, gzip')
end
```

By default, Hyperclient adds `application/json` as `Content-Type` and `Accept`
headers. It will also sent requests as JSON and parse JSON responses.
By default, Hyperclient adds `application/json` as `Content-Type` and `Accept` headers. It will also sent requests as JSON and parse JSON responses.

[More examples][examples]

Expand All @@ -38,15 +37,22 @@ Hyperclient will try to fetch and discover the resources from your API.
Accessing the links for a given resource is quite straightforward:

```ruby
api.links.posts_categories
api._links.posts_categories
# => #<Resource ...>
```

Or omit `_links`, which will look for a link called "posts_categories" by default:

```ruby
api.posts_categories
# => #<Resource ...>
```

You can also iterate between all the links:

```ruby
api.links.each do |name, link|
puts name, link.url
api._links.each do |name, link|
puts name, link._url
end
```

Expand All @@ -55,15 +61,21 @@ Actually, you can call any [Enumerable][enumerable] method :D
If a Resource doesn't have friendly name you can always access it as a Hash:

```ruby
api.links['http://myapi.org/rels/post_categories']
api._links['http://myapi.org/rels/post_categories']
```

### Embedded resources

Accessing embedded resources is similar to accessing links:

```ruby
api.embedded.posts
api._embedded.posts
```

Or omit the `_embedded` keyword. By default Hyperclient will look for a "posts" link, then for an embedded "posts" collection:

```ruby
api.posts
```

And you can also iterate between them:
Expand All @@ -80,10 +92,25 @@ You can even chain different calls (this also applies for links):
api.embedded.posts.first.links.author
```

Or omit the navigational structures:

```ruby
api.posts.first.author
```

If you have a named link that retrieves an embedded collection of the same name, you can collapse the nested reference. The following statements produce identical results:

```ruby
api.links.posts.embedded.posts.first
```

```ruby
api.posts.first
```

### Attributes

Not only you might have links and embedded resources in a Resource, but also
its attributes:
Not only you might have links and embedded resources in a Resource, but also its attributes:

```ruby
api.embedded.posts.first.attributes
Expand All @@ -95,6 +122,9 @@ api.embedded.posts.first.attributes
You can access the attribute values via attribute methods, or as a hash:

```ruby
api.posts.first.title
# => 'Linting the hell out of your Ruby classes with Pelusa'

api.embedded.posts.first.attributes.title
# => 'Linting the hell out of your Ruby classes with Pelusa'

Expand All @@ -107,35 +137,38 @@ api.embedded.posts.first.attributes.fetch('title')

### HTTP

OK, navigating an API is really cool, but you may want to actually do something
with it, right?
OK, navigating an API is really cool, but you may want to actually do something with it, right?

Hyperclient uses [Faraday][faraday] under the hood to perform HTTP calls. You can
call any valid HTTP method on any Resource:
Hyperclient uses [Faraday][faraday] under the hood to perform HTTP calls. You can call any valid HTTP method on any Resource:

```ruby
post = api.embedded.posts.first
post.get
post.head
post.put({title: 'New title'})
post.patch({title: 'New title'})
post.delete
post.options

posts = api.links.posts
posts.post({title: "I'm a blogger!", body: 'Wohoo!!'})
post = api._embedded.posts.first
post._get
post._head
post._put(title: 'New title')
post._patch(title: 'New title')
post._delete
post._options

posts = api._links.posts
posts._post(title: "I'm a blogger!", body: 'Wohoo!!')
```

If you have a templated link you can expand it like so:

```ruby
api.links.post.expand(:id => 3).first
api._links.post._expand(id: 3).first
# => #<Resource ...>
```

You can omit the "_expand" keyword.

```ruby
api.post(id: 3).first
# => #<Resource ...>
```

You can access the Faraday connection (to add middlewares or do whatever
you want) by calling `connection` on the entry point. As an example, you could use the [faraday-http-cache-middleware](https://github.com/plataformatec/faraday-http-cache)
:
You can access the Faraday connection (to add middlewares or do whatever you want) by calling `connection` on the entry point. As an example, you could use the [faraday-http-cache-middleware](https://github.com/plataformatec/faraday-http-cache):

```ruby
api.connection.use :http_cache
Expand Down Expand Up @@ -168,7 +201,7 @@ There's also a PHP library named [HyperClient](https://github.com/FoxyCart/Hyper

## License

MIT License. Copyright 2012 [Codegram Technologies][codegram]
MIT License. Copyright 2012-2014 [Codegram Technologies][codegram]

[hal]: http://stateless.co/hal_specification.html
[contributors]: https://github.com/codegram/hyperclient/contributors
Expand Down
2 changes: 1 addition & 1 deletion examples/cyberscore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def print_links(links)
if link.is_a?(Array)
print_links(link)
else
puts %(Found "#{name}" at "#{link.url}" )
puts %(Found "#{name}" at "#{link._url}" )
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion examples/hal_shop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def print_resources(resources)
resources.each do |name, resource|
begin
puts %(Found #{name} at #{resource.url})
puts %(Found #{name} at #{resource._url})
rescue
puts %(Found #{name})
end
Expand Down
16 changes: 8 additions & 8 deletions features/steps/api_navigation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps
include API

step 'I should be able to navigate to posts and authors' do
api.links.posts.resource
api.links['api:authors'].resource
api._links.posts._resource
api._links['api:authors']._resource

assert_requested :get, 'http://api.example.org/posts'
assert_requested :get, 'http://api.example.org/authors'
end

step 'I search for a post with a templated link' do
api.links.search.expand(q: 'something').resource
api._links.search._expand(q: 'something')._resource
end

step 'the API should receive the request with all the params' do
assert_requested :get, 'http://api.example.org/search?q=something'
end

step 'I load a single post' do
@post = api.links.posts.links.last_post
@post = api._links.posts._links.last_post
end

step 'I should be able to access it\'s title and body' do
@post.attributes.title.wont_equal nil
@post.attributes.body.wont_equal nil
@post._attributes.title.wont_equal nil
@post._attributes.body.wont_equal nil
end

step 'I should also be able to access it\'s embedded comments' do
comment = @post.embedded.comments.first
comment.attributes.title.wont_equal nil
comment = @post._embedded.comments.first
comment._attributes.title.wont_equal nil
end
end
8 changes: 4 additions & 4 deletions features/steps/default_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ class Spinach::Features::DefaultConfig < Spinach::FeatureSteps

step 'I send some data to the API' do
stub_request(:post, 'http://api.example.org/posts')
assert_equal 200, api.links.posts.post(title: 'My first blog post').status
assert_equal 200, api._links.posts._post(title: 'My first blog post').status
end

step 'it should have been encoded as JSON' do
assert_requested :post, 'api.example.org/posts', body: '{"title":"My first blog post"}'
end

step 'I get some data from the API' do
@posts = api.links.posts
@posts = api._links.posts
end

step 'it should have been parsed as JSON' do
@posts.attributes.total_posts.to_i.must_equal 9
@posts.attributes['total_posts'].to_i.must_equal 9
@posts._attributes.total_posts.to_i.must_equal 9
@posts._attributes['total_posts'].to_i.must_equal 9
end
end
2 changes: 1 addition & 1 deletion features/support/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def api
end

step 'I connect to the API' do
api.links
api._links
end

after do
Expand Down
2 changes: 1 addition & 1 deletion lib/hyperclient/entry_point.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def initialize(url)
#
# Returns a Faraday::Connection.
def connection
@connection ||= Faraday.new(url, { headers: default_headers }, &default_faraday_block)
@connection ||= Faraday.new(_url, { headers: default_headers }, &default_faraday_block)
end

private
Expand Down
Loading

0 comments on commit af7de24

Please sign in to comment.