restfolia 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.travis.yml +3 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +11 -0
- data/Readme.md +101 -0
- data/ReadmeDeveloper.md +30 -0
- data/lib/restfolia/entry_point.rb +156 -0
- data/lib/restfolia/exceptions.rb +109 -0
- data/lib/restfolia/http/behaviour.rb +164 -0
- data/lib/restfolia/http/configuration.rb +74 -0
- data/lib/restfolia/http/request.rb +54 -0
- data/lib/restfolia/http.rb +135 -0
- data/lib/restfolia/resource.rb +109 -0
- data/lib/restfolia/resource_creator.rb +97 -0
- data/lib/restfolia/version.rb +3 -0
- data/lib/restfolia.rb +97 -0
- data/restfolia.gemspec +28 -0
- data/samples/changing_behaviour.rb +32 -0
- data/samples/changing_links_parse.rb +38 -0
- data/samples/cookies_options.rb +23 -0
- data/samples/headers_options.rb +27 -0
- data/samples/http_behaviour.rb +52 -0
- data/samples/using_custom_factory.rb +25 -0
- data/samples/using_custom_resource.rb +25 -0
- data/test/restfolia/entry_point_test.rb +123 -0
- data/test/restfolia/http_behaviour_test.rb +86 -0
- data/test/restfolia/http_configuration_test.rb +45 -0
- data/test/restfolia/resource_creator_test.rb +54 -0
- data/test/restfolia/resource_test.rb +89 -0
- data/test/restfolia_test.rb +10 -0
- data/test/support/json_samples.rb +41 -0
- data/test/support/stub_helpers.rb +36 -0
- data/test/test_helper.rb +13 -0
- metadata +182 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module Restfolia::HTTP
|
2
|
+
|
3
|
+
# Public: Wraps Net::HTTP interface.
|
4
|
+
class Request
|
5
|
+
|
6
|
+
# Public: Do a HTTP Request.
|
7
|
+
#
|
8
|
+
# method - HTTP verb to be used. Options: :get, :post, :put, :delete
|
9
|
+
# url - a String to request. (ex: http://fake.com/service)
|
10
|
+
# args - Hash options to build request (default: {}):
|
11
|
+
# :query - String to be set with url (optional).
|
12
|
+
# :body - String to be set with request (optional).
|
13
|
+
# :headers - Hash with headers to be sent in request (optional).
|
14
|
+
#
|
15
|
+
# Returns an instance of Net::HTTPResponse.
|
16
|
+
#
|
17
|
+
# Raises URI::InvalidURIError if url attribute is invalid.
|
18
|
+
def self.do_request(method, url, args = {})
|
19
|
+
query = args[:query]
|
20
|
+
body = args[:body]
|
21
|
+
|
22
|
+
uri = URI.parse(url)
|
23
|
+
uri.query = query if query
|
24
|
+
|
25
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
26
|
+
verb = case method
|
27
|
+
when :get
|
28
|
+
Net::HTTP::Get.new(uri.request_uri)
|
29
|
+
when :post
|
30
|
+
Net::HTTP::Post.new(uri.request_uri)
|
31
|
+
when :put
|
32
|
+
Net::HTTP::Put.new(uri.request_uri)
|
33
|
+
when :delete
|
34
|
+
Net::HTTP::Delete.new(uri.request_uri)
|
35
|
+
else
|
36
|
+
msg = "Method have to be one of: :get, post, :put, :delete"
|
37
|
+
raise ArgumentError, msg
|
38
|
+
end
|
39
|
+
verb.body = body if body
|
40
|
+
if (headers = args[:headers])
|
41
|
+
headers.each do |header, value|
|
42
|
+
verb[header] = value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if (cookies = args[:cookies])
|
46
|
+
verb["Cookie"] = cookies
|
47
|
+
end
|
48
|
+
|
49
|
+
http.request(verb)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Restfolia
|
2
|
+
|
3
|
+
# Public: Store and execute behaviours defined by user. Behaviour is action
|
4
|
+
# for one or more HTTP code. See "default behaviours" below.
|
5
|
+
#
|
6
|
+
# Examples
|
7
|
+
#
|
8
|
+
# default behaviours
|
9
|
+
# behaviours do
|
10
|
+
#
|
11
|
+
# # 2xx
|
12
|
+
# on(200...300) do |http_response|
|
13
|
+
# content_type = (http_response["content-type"] =~ /application\/json/)
|
14
|
+
# if !content_type
|
15
|
+
# msg_error = "Response \"content-type\" header should be \"application/json\""
|
16
|
+
# raise Restfolia::ResponseError.new(msg_error, caller, http_response)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# http_body = http_response.body.to_s
|
20
|
+
# if !http_body.empty?
|
21
|
+
# json_parsed = helpers.parse_json(http_response)
|
22
|
+
# Restfolia.create_resource(json_parsed)
|
23
|
+
# elsif (location = http_response["location"])
|
24
|
+
# helpers.follow_url(location)
|
25
|
+
# else
|
26
|
+
# nil
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # 3xx
|
31
|
+
# on(300...400) do |http_response|
|
32
|
+
# if (location = http_response["location"])
|
33
|
+
# helpers.follow_url(location)
|
34
|
+
# else
|
35
|
+
# msg_error = "HTTP status #{http_response.code} not supported"
|
36
|
+
# raise Restfolia::ResponseError.new(msg_error, caller, http_response)
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # 4xx
|
41
|
+
# on(400...500) do |http_response|
|
42
|
+
# raise Restfolia::ResponseError.new("Resource not found.",
|
43
|
+
# caller, http_response)
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# # 5xx
|
47
|
+
# on(500...600) do |http_response|
|
48
|
+
# raise Restfolia::ResponseError.new("Internal Server Error",
|
49
|
+
# caller, http_response)
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
module HTTP
|
55
|
+
autoload :Behaviour, "restfolia/http/behaviour"
|
56
|
+
autoload :Configuration, "restfolia/http/configuration"
|
57
|
+
autoload :Request, "restfolia/http/request"
|
58
|
+
|
59
|
+
# Public: Execute behaviour from HTTP Response code.
|
60
|
+
#
|
61
|
+
# http_response - Net::HTTPResponse with code attribute.
|
62
|
+
#
|
63
|
+
# Returns value from "block behaviour".
|
64
|
+
def self.response_by_status_code(http_response)
|
65
|
+
Behaviour.store.execute(http_response)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: It's a nice way to define configurations for
|
69
|
+
# your behaves using a block.
|
70
|
+
#
|
71
|
+
# block - Required block to customize your behaves. Below are
|
72
|
+
# the methods available inside block:
|
73
|
+
# #on - See #on
|
74
|
+
#
|
75
|
+
# Examples
|
76
|
+
#
|
77
|
+
# Restfolia::HTTP.behaviours do
|
78
|
+
# on(200) { '200 behaviour' }
|
79
|
+
# on([201, 204]) { 'behaviour for 201 and 204 codes' }
|
80
|
+
# on(300...400) { '3xx range' }
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# Returns nothing.
|
84
|
+
def self.behaviours(&block)
|
85
|
+
Behaviour.store.behaviours(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
#default behaviours
|
89
|
+
behaviours do
|
90
|
+
|
91
|
+
# 2xx
|
92
|
+
on(200...300) do |http_response|
|
93
|
+
content_type = (http_response["content-type"] =~ /application\/json/)
|
94
|
+
if !content_type
|
95
|
+
msg_error = "Response \"content-type\" header should be \"application/json\""
|
96
|
+
raise Restfolia::ResponseError.new(msg_error, caller, http_response)
|
97
|
+
end
|
98
|
+
|
99
|
+
http_body = http_response.body.to_s
|
100
|
+
if !http_body.empty?
|
101
|
+
json_parsed = helpers.parse_json(http_response)
|
102
|
+
Restfolia.create_resource(json_parsed)
|
103
|
+
elsif (location = http_response["location"])
|
104
|
+
helpers.follow_url(location)
|
105
|
+
else
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# 3xx
|
111
|
+
on(300...400) do |http_response|
|
112
|
+
if (location = http_response["location"])
|
113
|
+
helpers.follow_url(location)
|
114
|
+
else
|
115
|
+
msg_error = "HTTP status #{http_response.code} not supported"
|
116
|
+
raise Restfolia::ResponseError.new(msg_error, caller, http_response)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# 4xx
|
121
|
+
on(400...500) do |http_response|
|
122
|
+
raise Restfolia::ResponseError.new("Resource not found.",
|
123
|
+
caller, http_response)
|
124
|
+
end
|
125
|
+
|
126
|
+
# 5xx
|
127
|
+
on(500...600) do |http_response|
|
128
|
+
raise Restfolia::ResponseError.new("Internal Server Error",
|
129
|
+
caller, http_response)
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Restfolia
|
2
|
+
|
3
|
+
# Public: Resource is the representation of JSON response. It transforms
|
4
|
+
# all JSON attributes in attributes and provides a "links" method, to
|
5
|
+
# help with hypermedia navigation.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# resource = Resource.new(:attr_test => "test")
|
10
|
+
# resource.attr_test # => "test"
|
11
|
+
# resource.links # => []
|
12
|
+
#
|
13
|
+
# resource = Resource.new(:attr_test => "test",
|
14
|
+
# :links => {:href => "http://service.com",
|
15
|
+
# :rel => "self",
|
16
|
+
# :type => "application/json"})
|
17
|
+
# resource.attr_test # => "test"
|
18
|
+
# resource.links # => [#<Restfolia::EntryPoint ...>]
|
19
|
+
#
|
20
|
+
# By default, "links" method, expects from JSON to be the following formats:
|
21
|
+
#
|
22
|
+
# # Array de Links
|
23
|
+
# "links" : [{ "href" : "http://fakeurl.com/some/service",
|
24
|
+
# "rel" : "self",
|
25
|
+
# "type" : "application/json"
|
26
|
+
# }]
|
27
|
+
#
|
28
|
+
# # OR 'single' Links
|
29
|
+
# "links" : { "href" : "http://fakeurl.com/some/service",
|
30
|
+
# "rel" : "self",
|
31
|
+
# "type" : "application/json"
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# # OR node 'Link', that can be Array or single too
|
35
|
+
# "link" : { "href" : "http://fakeurl.com/some/service",
|
36
|
+
# "rel" : "self",
|
37
|
+
# "type" : "application/json"
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
class Resource
|
41
|
+
|
42
|
+
# Public: Returns the Hash that represents parsed JSON.
|
43
|
+
attr_reader :_json
|
44
|
+
|
45
|
+
# Public: Initialize a Resource.
|
46
|
+
#
|
47
|
+
# json - Hash that represents parsed JSON.
|
48
|
+
#
|
49
|
+
# Raises ArgumentError if json parameter is not a Hash object.
|
50
|
+
def initialize(json)
|
51
|
+
unless json.is_a?(Hash)
|
52
|
+
raise(ArgumentError, "json parameter have to be a Hash object", caller)
|
53
|
+
end
|
54
|
+
@_json = json
|
55
|
+
|
56
|
+
#Add json keys as methods of Resource
|
57
|
+
#http://blog.jayfields.com/2008/02/ruby-replace-methodmissing-with-dynamic.html
|
58
|
+
@_json.each do |method, value|
|
59
|
+
next if self.respond_to?(method) #avoid method already defined
|
60
|
+
|
61
|
+
(class << self; self; end).class_eval do
|
62
|
+
define_method(method) do |*args|
|
63
|
+
value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Read links from Resource. Links are optional.
|
70
|
+
# See Resource root doc for details.
|
71
|
+
#
|
72
|
+
# rel - Optional String parameter. Filter links by rel attribute.
|
73
|
+
#
|
74
|
+
# Returns Empty Array or Array of EntryPoints, if "rel" is informed
|
75
|
+
# it returns nil or an instance of EntryPoint.
|
76
|
+
def links(rel = nil)
|
77
|
+
@links ||= parse_links(@_json)
|
78
|
+
|
79
|
+
return nil if @links.empty? && !rel.nil?
|
80
|
+
return @links if @links.empty? || rel.nil?
|
81
|
+
|
82
|
+
@links.detect { |ep| ep.rel == rel }
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
# Internal: Parse links from hash. Always normalize to return
|
88
|
+
# an Array of EntryPoints. Check if link has :href and :rel
|
89
|
+
# keys.
|
90
|
+
#
|
91
|
+
# Returns Array of EntryPoints or Empty Array if :links not exist.
|
92
|
+
# Raises RuntimeError if link doesn't have :href and :rel keys.
|
93
|
+
def parse_links(json)
|
94
|
+
links = json[:links] || json[:link]
|
95
|
+
return [] if links.nil?
|
96
|
+
|
97
|
+
links = [links] unless links.is_a?(Array)
|
98
|
+
links.map do |link|
|
99
|
+
if link[:href].nil? || link[:rel].nil?
|
100
|
+
msg = "Invalid hash link: #{link.inspect}"
|
101
|
+
raise(RuntimeError, msg, caller)
|
102
|
+
end
|
103
|
+
EntryPoint.new(link[:href], link[:rel])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Restfolia
|
2
|
+
|
3
|
+
# Public: Call a Factory of Resources. This is a good place to override and
|
4
|
+
# returns a custom Resource Factory. By default, Restfolia uses
|
5
|
+
# Restfolia::ResourceCreator.
|
6
|
+
#
|
7
|
+
# json - Hash parsed from Response body.
|
8
|
+
#
|
9
|
+
# Returns Resource instance, configured at ResourceCreator.
|
10
|
+
# Raises ArgumentError if json is not a Hash.
|
11
|
+
def self.create_resource(json)
|
12
|
+
@creator ||= Restfolia::ResourceCreator.new
|
13
|
+
@creator.create(json)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Factory of Resources. It transforms all JSON objects in Resources.
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# factory = Restfolia::ResourceCreator.new
|
21
|
+
# resource = factory.create(:attr_test => "test",
|
22
|
+
# :attr_tags => ["tag1", "tag2"],
|
23
|
+
# :attr_array_obj => [{:nested => "nested"}],
|
24
|
+
# :links => [{:href => "http://service.com",
|
25
|
+
# :rel => "contacts",
|
26
|
+
# :type => "application/json"},
|
27
|
+
# {:href => "http://another.com",
|
28
|
+
# :rel => "relations",
|
29
|
+
# :type => "application/json"}
|
30
|
+
# ])
|
31
|
+
# resource.attr_test # => "test"
|
32
|
+
# resource.attr_tags # => ["tag1", "tag2"]
|
33
|
+
# resource.attr_array_obj # => [#<Restfolia::Resource ...>]
|
34
|
+
#
|
35
|
+
class ResourceCreator
|
36
|
+
|
37
|
+
# Public: By default, returns Restfolia::Resource. You can use
|
38
|
+
# this method to override and returns a custom Resource. See examples.
|
39
|
+
#
|
40
|
+
# Examples
|
41
|
+
#
|
42
|
+
# # using a custom Resource
|
43
|
+
# class Restfolia::ResourceCreator
|
44
|
+
# def resource_class
|
45
|
+
# OpenStruct #dont forget to require 'ostruct'
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# Returns class of Resource to be constructed.
|
50
|
+
def resource_class
|
51
|
+
Restfolia::Resource
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: creates Resource looking recursively for JSON
|
55
|
+
# objects and transforming in Resources. To create Resource,
|
56
|
+
# this method use #resource_class.new(json).
|
57
|
+
#
|
58
|
+
# json - Hash parsed from Response body.
|
59
|
+
#
|
60
|
+
# Returns Resource from #resource_class.
|
61
|
+
# Raises ArgumentError if json is not a Hash.
|
62
|
+
def create(json)
|
63
|
+
unless json.is_a?(Hash)
|
64
|
+
raise(ArgumentError, "JSON parameter have to be a Hash object", caller)
|
65
|
+
end
|
66
|
+
|
67
|
+
json_parsed = {}
|
68
|
+
json.each do |attr, value|
|
69
|
+
json_parsed[attr] = look_for_resource(value)
|
70
|
+
end
|
71
|
+
resource_class.new(json_parsed)
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
# Internal: Check if value is eligible to become a Restfolia::Resource.
|
77
|
+
# If value is Array object, looks inner contents, using rules below.
|
78
|
+
# If value is Hash object, it becomes a Restfolia::Resource.
|
79
|
+
# Else return itself.
|
80
|
+
#
|
81
|
+
# value - object to be checked.
|
82
|
+
#
|
83
|
+
# Returns value itself or Resource.
|
84
|
+
def look_for_resource(value)
|
85
|
+
if value.is_a?(Array)
|
86
|
+
value = value.inject([]) do |resources, array_obj|
|
87
|
+
resources << look_for_resource(array_obj)
|
88
|
+
end
|
89
|
+
elsif value.is_a?(Hash)
|
90
|
+
value = resource_class.new(value)
|
91
|
+
end
|
92
|
+
value
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/lib/restfolia.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "multi_json"
|
6
|
+
|
7
|
+
require "restfolia/version"
|
8
|
+
require "restfolia/exceptions"
|
9
|
+
require "restfolia/http"
|
10
|
+
require "restfolia/entry_point"
|
11
|
+
require "restfolia/resource_creator"
|
12
|
+
require "restfolia/resource"
|
13
|
+
|
14
|
+
# Public: Restfolia: a REST client to consume and interact with Hypermedia API.
|
15
|
+
#
|
16
|
+
# Against the grain, Restfolia is very opinionated about some REST's concepts:
|
17
|
+
# * Aims only *JSON Media Type*.
|
18
|
+
# * All responses are parsed and returned as Restfolia::Resource.
|
19
|
+
# * Less is more. Restfolia is very proud to be small, easy to maintain and evolve.
|
20
|
+
# * Restfolia::Resource is Ruby object with attributes from JSON and can optionally contains *hypermedia links* which have to be a specific format. See the examples below.
|
21
|
+
# * All code is very well documented, using "TomDoc":http://tomdoc.org style.
|
22
|
+
#
|
23
|
+
# Obs: This is a draft version. Not ready for production (yet!).
|
24
|
+
#
|
25
|
+
# References
|
26
|
+
#
|
27
|
+
# You can find more information about arquitecture REST below:
|
28
|
+
# * "Roy Fielding's":http://roy.gbiv.com/untangled see this post for example: "REST APIs must be hypertext-driven":http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
|
29
|
+
# * "Rest in Practice":http://restinpractice.com, especially the chapter titled "Hypermedia Formats".
|
30
|
+
# * "Mike Amundsen's Blog":http://amundsen.com/blog/
|
31
|
+
# * ROAR - "Resource Oriented Arquitectures in Ruby":https://github.com/apotonick/roar it seems really good to build a hypermedia API, of course you can go with Sinatra+rabl solutions too.
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# # GET http://localhost:9292/recursos/busca
|
36
|
+
# { "itens_por_pagina" : 10,
|
37
|
+
# "paginal_atual" : 1,
|
38
|
+
# "paginas_totais" : 1,
|
39
|
+
# "query" : "",
|
40
|
+
# "total_resultado" : 100,
|
41
|
+
# "resultado" : [ { "id" : 1,
|
42
|
+
# "name" : "Test1",
|
43
|
+
# "links" : [ { "href" : "http://localhost:9292/recursos/id/1",
|
44
|
+
# "rel" : "recurso",
|
45
|
+
# "type" : "application/json"
|
46
|
+
# } ]
|
47
|
+
# },
|
48
|
+
# { "id" : 2,
|
49
|
+
# "name" : "Test2",
|
50
|
+
# "links" : [ { "href" : "http://localhost:9292/recursos/id/2",
|
51
|
+
# "rel" : "recurso",
|
52
|
+
# "type" : "application/json"
|
53
|
+
# } ]
|
54
|
+
# }
|
55
|
+
# ],
|
56
|
+
# "links" : { "href" : "http://localhost:9292/recursos/busca",
|
57
|
+
# "rel" : "self",
|
58
|
+
# "type" : "application/json"
|
59
|
+
# },
|
60
|
+
# }
|
61
|
+
#
|
62
|
+
# # GET http://localhost:9292/recursos/id/1
|
63
|
+
# { "id" : 1,
|
64
|
+
# "name" : "Test1",
|
65
|
+
# "links" : { "href" : "http://localhost:9292/recursos/id/1",
|
66
|
+
# "rel" : "self",
|
67
|
+
# "type" : "application/json"
|
68
|
+
# }
|
69
|
+
# }
|
70
|
+
#
|
71
|
+
# # getting a resource
|
72
|
+
# resource = Restfolia.at('http://localhost:9292/recursos/busca').get
|
73
|
+
# resource.pagina_atual # => 1
|
74
|
+
# resource.resultado # => [#<Resource ...>, #<Resource ...>]
|
75
|
+
#
|
76
|
+
# # example of hypermedia navigation
|
77
|
+
# r1 = resource.resultado.first
|
78
|
+
# r1 = r1.links("recurso").get # => #<Resource ...>
|
79
|
+
# r1.name # => "Test1"
|
80
|
+
#
|
81
|
+
module Restfolia
|
82
|
+
|
83
|
+
# Public: Start point for getting the first Resource.
|
84
|
+
#
|
85
|
+
# url - String with the address of service to be accessed.
|
86
|
+
#
|
87
|
+
# Examples
|
88
|
+
#
|
89
|
+
# entry_point = Restfolia.at("http://localhost:9292/recursos/busca")
|
90
|
+
# entry_point.get # => #<Resource ...>
|
91
|
+
#
|
92
|
+
# Returns Restfolia::EntryPoint object.
|
93
|
+
def self.at(url)
|
94
|
+
EntryPoint.new(url)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/restfolia.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "restfolia/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "restfolia"
|
7
|
+
s.version = Restfolia::VERSION
|
8
|
+
s.authors = `git log --raw | grep Author: | awk -F ': | <|>' '{ print $2 }' | sort | uniq`.split("\n")
|
9
|
+
s.email = ["roger.barreto@gmail.com"]
|
10
|
+
s.homepage = "http://rogerleite.github.com/restfolia"
|
11
|
+
s.summary = %q{REST client to consume and interact with Hypermedia API}
|
12
|
+
s.description = %q{REST client to consume and interact with Hypermedia API}
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.rubyforge_project = "restfolia"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency "multi_json", "~> 1.3.0"
|
23
|
+
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
s.add_development_dependency "minitest"
|
26
|
+
s.add_development_dependency "minitest-reporters"
|
27
|
+
s.add_development_dependency "webmock"
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
# Run this sample from root project:
|
3
|
+
# $ ruby samples/changing_behaviour.rb
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
$LOAD_PATH << "lib"
|
7
|
+
require "restfolia"
|
8
|
+
|
9
|
+
begin
|
10
|
+
# Default behaviour to redirect 3xx is raise Error
|
11
|
+
Restfolia.at("http://google.com.br").get
|
12
|
+
rescue Restfolia::ResponseError => ex
|
13
|
+
puts ex.message
|
14
|
+
end
|
15
|
+
|
16
|
+
module Restfolia::HTTP::Behaviour
|
17
|
+
|
18
|
+
# We can change behaviour of many HTTP status
|
19
|
+
# on_2xx(http_response)
|
20
|
+
# on_3xx(http_response)
|
21
|
+
# on_4xx(http_response)
|
22
|
+
# on_5xx(http_response)
|
23
|
+
|
24
|
+
# Here we change 3xx behaviour to return a Resource
|
25
|
+
def on_3xx(http_response)
|
26
|
+
Restfolia.create_resource(:redirected => "I'm free! :D")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
resource = Restfolia.at("http://google.com.br").get
|
32
|
+
puts resource.redirected
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Run this sample from root project:
|
3
|
+
# $ ruby samples/changing_links_parse.rb
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
$LOAD_PATH << "lib"
|
7
|
+
require "restfolia"
|
8
|
+
|
9
|
+
resource = Restfolia::Resource.new(:attr_test => "test",
|
10
|
+
:custom_links => [
|
11
|
+
{:url => "http://test.com", :rel => "rel"}
|
12
|
+
])
|
13
|
+
puts resource.links.inspect # => []
|
14
|
+
|
15
|
+
# Now let's change the way Resource parses links
|
16
|
+
class Restfolia::Resource
|
17
|
+
|
18
|
+
def parse_links(json)
|
19
|
+
links = json[:custom_links]
|
20
|
+
return [] if links.nil?
|
21
|
+
|
22
|
+
links = [links] unless links.is_a?(Array)
|
23
|
+
links.map do |link|
|
24
|
+
if link[:url].nil? || link[:rel].nil?
|
25
|
+
msg = "Invalid hash link: #{link.inspect}"
|
26
|
+
raise(RuntimeError, msg, caller)
|
27
|
+
end
|
28
|
+
Restfolia::EntryPoint.new(link[:url], link[:rel])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
resource = Restfolia::Resource.new(:attr_test => "test",
|
35
|
+
:custom_links => [
|
36
|
+
{:url => "http://test.com", :rel => "rel"}
|
37
|
+
])
|
38
|
+
puts resource.links.inspect # => [#<Restfolia::EntryPoint ...>]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
# Run this sample from root project:
|
3
|
+
# $ ruby samples/cookies_options.rb
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
$LOAD_PATH << "lib"
|
7
|
+
require "restfolia"
|
8
|
+
|
9
|
+
SERVICE_URL = "http://localhost:9292/recursos/busca"
|
10
|
+
|
11
|
+
sample_cookie = "PREF=ID=988f14fa5edd3243:TM=1335470032:LM=1335470032:S=KVBslNbyz6bG0DqU; expires=Sat, 26-Apr-2014 19:53:52 GMT; path=/; domain=.google.com, NID=59=peUyZQuLWQ_0gELr1yDf0FT4ZlT7ZdITNrO5OhkEnAvp_8MZ4TT6pHq7_q_Su-puTw7vGml_Ok6du8fLreGHzfpMs4Qh1v-qBCFYGuCNbzpwN670x5MFbGKy0KUXA3WP; expires=Fri, 26-Oct-2012 19:53:52 GMT; path=/; domain=.google.com; HttpOnly"
|
12
|
+
|
13
|
+
# accessing cookies attribute
|
14
|
+
entry_point = Restfolia.at(SERVICE_URL)
|
15
|
+
entry_point.cookies = sample_cookie
|
16
|
+
resource = entry_point.get
|
17
|
+
|
18
|
+
# adding in a fancy way
|
19
|
+
resource = Restfolia.at(SERVICE_URL).
|
20
|
+
set_cookies(sample_cookie).get
|
21
|
+
|
22
|
+
puts "Done!"
|
23
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
# Run this sample from root project:
|
3
|
+
# $ ruby samples/headers_options.rb
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
$LOAD_PATH << "lib"
|
7
|
+
require "restfolia"
|
8
|
+
|
9
|
+
SERVICE_URL = "http://localhost:9292/recursos/busca"
|
10
|
+
|
11
|
+
# accessing headers attribute
|
12
|
+
entry_point = Restfolia.at(SERVICE_URL)
|
13
|
+
entry_point.headers["ContentyType"] = "application/json"
|
14
|
+
resource = entry_point.get
|
15
|
+
|
16
|
+
# adding in a fancy way
|
17
|
+
resource = Restfolia.at(SERVICE_URL).
|
18
|
+
with_headers("Content-Type" => "application/json",
|
19
|
+
"Accept" => "application/json").
|
20
|
+
get
|
21
|
+
|
22
|
+
# helper that sets Content-Type and Accept headers
|
23
|
+
resource = Restfolia.at(SERVICE_URL).
|
24
|
+
as("application/json").
|
25
|
+
get
|
26
|
+
|
27
|
+
puts "Done!"
|