Rest Service Client
Installation
Add this line to your application's Gemfile:
gem 'rest-service-client'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install rest-service-client
Usage
Add this line to top of your file
require 'rest-service-client'
Create a class, include the RestServiceClient and configure your service
class MyAwesomeService
include RestServiceClient
# Sets the host url
# You can use MyAwesomeService.new('https://jsonplaceholder.typicode.com')
# to set the host instead. Using host url via constructor has high priority.
host 'https://jsonplaceholder.typicode.com'
# Sets the default headers for every requests.
# Will be replaced using: MyAwesomeService.new.find_photo(id: 1, headers: { 'Authorization' => 'Bearer 123456' })
headers Authorization: 'OAuth 1111|111'
# Sets the default params for every requests.
# Will be replaced using: MyAwesomeService.new.find_photo(id: 20)
params id: 10
# Configure endpoints:
# _httpmethod _method_name, _path
# "get :photos, '/photos'" provides an ability
# to make GET request to 'https://jsonplaceholder.typicode.com/photos'
# using MyAwesomeService.new.photos
# Supported: get, post, put, patch, delete
get :photos, '/photos'
# You can assign placeholders for params to path.
# Example: MyAwesomeService.new.find_photo(id: 1)
get :find_photo, '/photos/:id'
post :add_photo, '/photos'
put :update_photo, '/photos/:id'
patch :update_photo_data, '/photos/:id'
delete :delete_photo, '/photos/:id'
end
Samples
Simple Service
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
get :photo, '/photos/:id'
get :first_photo, '/photos/1'
get :second_photo, '/photos/2'
end
service = SimpleService.new
p service.first_photo.result['id']
# 1
p service.second_photo.result['id']
# 2
p service.photo(id: 10).result['id']
# 10
GET
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
get :photo, '/photos/:id', params: { id: 1 }
end
service = SimpleService.new
p service.photo.result['id'] # 1
p service.photo(id: 2).result['id'] # 2
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
get :first_photo, '/photos/1', headers: { Authentication: 'my-token' }
end
service = SimpleService.new
# Sends the request to 'https://jsonplaceholder.typicode.com/photos/1'
# includes Headers: { 'Authentication': 'my-token' }
p service.first_photo.result['albumId'] # 1
# Sends the request to 'https://jsonplaceholder.typicode.com/photos/1'
# includes Headers: {
# 'Authentication': 'my-token',
# 'AnotherHeaderKey': 'AnotherHeaderValue'
# }
another_headers = { AnotherHeaderKey: 'AnotherHeaderValue' }
photo = service.first_photo(headers: another_headers).result
p photo['albumId'] # 1
DELETE
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
delete :delete_photo, '/photos/:id'
end
service = SimpleService.new
service.delete_photo(id: 1).result
POST
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
post :add_photo, '/photos'
end
service = SimpleService.new
photo = {
albumId: 1,
title: 'new photo',
url: 'http://placehold.it/600/92c952',
thumbnailUrl: 'http://placehold.it/150/92c952'
}
response = service.add_photo(payload: photo)
p response.status # 201
p response.headers # {:date=>"...}
p response.result
=begin
{
"albumId"=>1,
"title"=>"new photo",
"url"=>"http://placehold.it/600/92c952",
"thumbnailUrl"=>"http://placehold.it/150/92c952",
"id"=>5001
}
=end
PATCH
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
patch :update_photo_data, '/photos/:id'
end
service = SimpleService.new
updated_photo = service.update_photo_data id: 1, payload: { title: 'new title'}
p updated_photo.result['title']
# "new title"
Debug mode
You can debug mode on for see what is happening
class SimpleService
include RestServiceClient
debug true
host 'https://jsonplaceholder.typicode.com'
headers key1: 'value1', 'key2' => 'value2'
get :photo,
'/photos/:id',
params: { id: 1 },
headers: { key3: 'value3' },
payload: { data: { type: 'get_photo_by_id' }}
end
service = SimpleService.new
service.photo.result
=begin
______
|
| SimpleService is processing GET request to https://jsonplaceholder.typicode.com/photos/1
| Headers: {:key1=>"value1", "key2"=>"value2", :key3=>"value3"}
| Payload: {:data=>{:type=>"get_photo_by_id"}}
|
| SimpleService is processing the response on GET request to https://jsonplaceholder.typicode.com/photos/1
| Status: 200
| Body: {
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "http://placehold.it/600/92c952",
"thumbnailUrl": "http://placehold.it/150/92c952"
}
|______
=end
Serializer
By the default RestServiceClient uses JSON serializer and returns the arrays and hashes with key as string
You can use custom serializer.
For do this, create a serializer class with self.deserialize
method which
gets the one argument with response body and returns whatever you want.
And you need to add your serializer to your service using:
serializer MyAwesomSerializer
Errors
class SimpleService
include RestServiceClient
debug true
host 'https://jsonplaceholder.typicode.com'
get :bad_url, '/bad_url'
end
service = SimpleService.new
response = service.bad_url
p response.status # 404
p response.message # "404 Not Found"
Example
class Photo
attr_accessor :id, :title, :album_id
end
class PhotoSerializer
class Object::String
def underscore
gsub(/::/, '/')
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.tr('-', '_')
.downcase
end
end
def self.deserialize(json)
data = JSON.parse(json)
return build_photo(data) if data.is_a? Hash
return data.map { |p| build_photo(p) } if data.is_a? Array
end
def self.build_photo(data)
object = Photo.new
data.each_with_object(object) do |(k, v), o|
setter = "#{k.underscore}=".to_sym
o.send(setter, v) if o.respond_to?(setter)
end
object
end
end
class Post
attr_accessor :id, :title, :user_id
end
class PostSerializer
def self.deserialize(json)
data = JSON.parse(json)
object = Post.new
object.id = data['id']
object.title = data['title']
object.user_id = data['userId']
object
end
end
class MyService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
serializer PhotoSerializer
get :photos, '/photos'
get :find_photo, '/photos/:id'
get :find_post, '/posts/:id', serializer: PostSerializer
end
service = MyService.new
p service.find_photo(id: 1).result.album_id # 1
p service.photos.result.find { |p| p.id == 3 }.id # 1
p service.find_post(id: 1).result.user_id # 1
Response Object
Response
If request was completely sent, the class of response be RestServiceClient::Response
This object has :status
, :headers
, :body
and :result
fields.
:status
contains the number(integer) of http status code.
:headers
contains the http headers from response.
:body
will be a http body as plain text.
:result
will contain deserialized http body.
ResponseWithError
If the request failed, the class of response will be RestServiceClient::ResponseWithError
This object has :status
, :headers
, :body
, :result
and :message
All fields except of :message
are equals to RestServiceClient::Response
fields.
:message
will contain the string with error message
You are able to check the response before trying to use result to avoid exceptions:
class SimpleService
include RestServiceClient
host 'https://jsonplaceholder.typicode.com'
get :get_photo, '/photos/:id'
get :bad_url, '/bad_url'
end
service = SimpleService.new
response = service.get_photo id: 1
if response.status == 200
p "Photo with name: #{response.result['name']} has been loaded"
end
response = service.bad_url
if response.status != 200
p "Something was wrong. Reason: #{response.message}"
# "Something was wrong. Reason: 404 Not Found"
end
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/besya/rest-service-client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the ServiceClient project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.