restfully 0.6.3 → 0.7.0.pre
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/README.md +166 -0
- data/Rakefile +35 -35
- data/bin/restfully +68 -10
- data/lib/restfully.rb +8 -14
- data/lib/restfully/collection.rb +70 -90
- data/lib/restfully/error.rb +2 -0
- data/lib/restfully/http.rb +3 -3
- data/lib/restfully/http/error.rb +1 -20
- data/lib/restfully/http/helper.rb +49 -0
- data/lib/restfully/http/request.rb +60 -24
- data/lib/restfully/http/response.rb +55 -24
- data/lib/restfully/link.rb +32 -24
- data/lib/restfully/media_type.rb +70 -0
- data/lib/restfully/media_type/abstract_media_type.rb +162 -0
- data/lib/restfully/media_type/application_json.rb +21 -0
- data/lib/restfully/media_type/application_vnd_bonfire_xml.rb +177 -0
- data/lib/restfully/media_type/application_x_www_form_urlencoded.rb +33 -0
- data/lib/restfully/media_type/grid5000.rb +67 -0
- data/lib/restfully/media_type/wildcard.rb +27 -0
- data/lib/restfully/rack.rb +1 -0
- data/lib/restfully/rack/basic_auth.rb +26 -0
- data/lib/restfully/resource.rb +134 -197
- data/lib/restfully/session.rb +127 -70
- data/lib/restfully/version.rb +3 -0
- data/spec/fixtures/bonfire-collection-with-fragments.xml +6 -0
- data/spec/fixtures/bonfire-compute-existing.xml +43 -0
- data/spec/fixtures/bonfire-empty-collection.xml +4 -0
- data/spec/fixtures/bonfire-experiment-collection.xml +51 -0
- data/spec/fixtures/bonfire-network-collection.xml +35 -0
- data/spec/fixtures/bonfire-network-existing.xml +6 -0
- data/spec/fixtures/bonfire-root.xml +5 -0
- data/spec/fixtures/grid5000-rennes-jobs.json +988 -146
- data/spec/fixtures/grid5000-rennes.json +63 -0
- data/spec/restfully/collection_spec.rb +87 -0
- data/spec/restfully/http/helper_spec.rb +18 -0
- data/spec/restfully/http/request_spec.rb +97 -0
- data/spec/restfully/http/response_spec.rb +53 -0
- data/spec/restfully/link_spec.rb +80 -0
- data/spec/restfully/media_type/application_vnd_bonfire_xml_spec.rb +153 -0
- data/spec/restfully/media_type_spec.rb +117 -0
- data/spec/restfully/resource_spec.rb +109 -0
- data/spec/restfully/session_spec.rb +229 -0
- data/spec/spec_helper.rb +10 -9
- metadata +162 -83
- data/.document +0 -5
- data/CHANGELOG +0 -62
- data/README.rdoc +0 -146
- data/TODO.rdoc +0 -3
- data/VERSION +0 -1
- data/examples/grid5000.rb +0 -33
- data/examples/scratch.rb +0 -37
- data/lib/restfully/extensions.rb +0 -34
- data/lib/restfully/http/adapters/abstract_adapter.rb +0 -29
- data/lib/restfully/http/adapters/patron_adapter.rb +0 -16
- data/lib/restfully/http/adapters/rest_client_adapter.rb +0 -75
- data/lib/restfully/http/headers.rb +0 -20
- data/lib/restfully/parsing.rb +0 -66
- data/lib/restfully/special_array.rb +0 -5
- data/lib/restfully/special_hash.rb +0 -5
- data/restfully.gemspec +0 -114
- data/spec/collection_spec.rb +0 -120
- data/spec/fixtures/configuration_file.yml +0 -4
- data/spec/fixtures/grid5000-sites.json +0 -540
- data/spec/http/error_spec.rb +0 -18
- data/spec/http/headers_spec.rb +0 -17
- data/spec/http/request_spec.rb +0 -49
- data/spec/http/response_spec.rb +0 -19
- data/spec/http/rest_client_adapter_spec.rb +0 -35
- data/spec/link_spec.rb +0 -61
- data/spec/parsing_spec.rb +0 -40
- data/spec/resource_spec.rb +0 -320
- data/spec/restfully_spec.rb +0 -16
- data/spec/session_spec.rb +0 -171
data/README.rdoc
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
= restfully
|
2
|
-
|
3
|
-
An attempt at dynamically providing "clever" wrappers on top of RESTful APIs that follow the principle of Hyperlinks As The Engine Of Application State (HATEOAS). It does not require to use specific (and often complex) server-side libraries, but a few constraints and conventions must be followed:
|
4
|
-
1. Return sensible HTTP status codes;
|
5
|
-
2. Make use of GET, POST, PUT, DELETE HTTP methods;
|
6
|
-
3. Return a Location HTTP header in 201 or 202 responses;
|
7
|
-
4. Return a <tt>links</tt> property in all responses to a GET request, that contains a list of link objects:
|
8
|
-
|
9
|
-
{
|
10
|
-
"property": "value",
|
11
|
-
"links": [
|
12
|
-
{"rel": "self", "href": "uri/to/resource", "type": "application/vnd.whatever+json;level=1,application/json"},
|
13
|
-
{"rel": "parent", "href": "uri/to/parent/resource", "type": "application/json"}
|
14
|
-
{"rel": "collection", "href": "uri/to/collection", "title": "my_collection", "type": "application/json"},
|
15
|
-
{"rel": "member", "href": "uri/to/member", "title": "member_title", "type": "application/json"}
|
16
|
-
]
|
17
|
-
}
|
18
|
-
|
19
|
-
* Adding a <tt>parent</tt> link automatically creates a <tt>#parent</tt> method on the current resource.
|
20
|
-
* Adding a <tt>collection</tt> link automatically creates a <tt>#my_collection</tt> method that will fetch the Collection when called.
|
21
|
-
* Adding a <tt>member</tt> link automatically creates a <tt>#member_title</tt> method that will fetch the Resource when called.
|
22
|
-
5. Advertise allowed HTTP methods in the response to GET requests by returning a <tt>Allow</tt> HTTP header containing a comma-separated list of the HTTP methods that can be used on the resource. This will allow the automatic generation of methods to interact with the resource. e.g.: advertising a <tt>POST</tt> method (<tt>Allow: GET, POST</tt>) will result in the creation of a <tt>submit</tt> method on the resource.
|
23
|
-
|
24
|
-
== Installation
|
25
|
-
$ gem install restfully --source http://gemcutter.org
|
26
|
-
|
27
|
-
== Usage
|
28
|
-
=== Command line
|
29
|
-
$ export RUBYOPT="-rubygems"
|
30
|
-
$ restfully base_uri [-u username] [-p password]
|
31
|
-
|
32
|
-
e.g., for the Grid5000 API:
|
33
|
-
$ restfully https://api.grid5000.fr/sid/grid5000 -u username -p password
|
34
|
-
|
35
|
-
If the connection was successful, you should get a prompt. You may enter
|
36
|
-
irb(main):001:0> pp root
|
37
|
-
|
38
|
-
to get back a pretty-printed output of the root resource:
|
39
|
-
#<Restfully::Resource:0x91f08c
|
40
|
-
@uri=#<URI::HTTP:0x123e30c URL:http://api.local/sid/grid5000>
|
41
|
-
LINKS
|
42
|
-
@environments=#<Restfully::Collection:0x917666>,
|
43
|
-
@sites=#<Restfully::Collection:0x9170d0>,
|
44
|
-
@version=#<Restfully::Resource:0x91852a>,
|
45
|
-
@versions=#<Restfully::Collection:0x917e68>
|
46
|
-
PROPERTIES
|
47
|
-
"uid"=>"grid5000",
|
48
|
-
"type"=>"grid",
|
49
|
-
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8">
|
50
|
-
|
51
|
-
You can see the LINKS and PROPERTIES headers that respectively indicate what links you can follow from there (by calling <tt>root.link_name</tt>) and what properties are available (by calling <tt>root[property_name]</tt>).
|
52
|
-
|
53
|
-
Let's say you want to access the collection of +sites+, you would enter:
|
54
|
-
irb(main):002:0> pp root.sites
|
55
|
-
|
56
|
-
and get back:
|
57
|
-
#<Restfully::Collection:0x9170d0
|
58
|
-
@uri=#<URI::HTTP:0x122e128 URL:http://api.local/sid/grid5000/sites>
|
59
|
-
LINKS
|
60
|
-
@version=#<Restfully::Resource:0x8f553e>,
|
61
|
-
@versions=#<Restfully::Collection:0x8f52be>
|
62
|
-
PROPERTIES
|
63
|
-
"total"=>9,
|
64
|
-
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
|
65
|
-
"offset"=>0
|
66
|
-
ITEMS (0..9)/9
|
67
|
-
#<Restfully::Resource:0x9058bc uid="bordeaux">,
|
68
|
-
#<Restfully::Resource:0x903d0a uid="grenoble">,
|
69
|
-
#<Restfully::Resource:0x901cc6 uid="lille">,
|
70
|
-
#<Restfully::Resource:0x8fff0c uid="lyon">,
|
71
|
-
#<Restfully::Resource:0x8fe288 uid="nancy">,
|
72
|
-
#<Restfully::Resource:0x8fc4a6 uid="orsay">,
|
73
|
-
#<Restfully::Resource:0x8fa782 uid="rennes">,
|
74
|
-
#<Restfully::Resource:0x8f8bb2 uid="sophia">,
|
75
|
-
#<Restfully::Resource:0x8f6c9a uid="toulouse">>
|
76
|
-
|
77
|
-
A Restfully::Collection is a special kind of Resource, which includes the Enumerable module, which means you can call all of its methods on the Restfully::Collection object. For example:
|
78
|
-
irb(main):003:0> pp root.sites.find{|s| s['uid'] == 'rennes'}
|
79
|
-
#<Restfully::Resource:0x8fa782
|
80
|
-
@uri=#<URI::HTTP:0x11f4e64 URL:http://api.local/sid/grid5000/sites/rennes>
|
81
|
-
LINKS
|
82
|
-
@environments=#<Restfully::Collection:0x8f9ab2>,
|
83
|
-
@parent=#<Restfully::Resource:0x8f981e>,
|
84
|
-
@deployments=#<Restfully::Collection:0x8f935a>,
|
85
|
-
@clusters=#<Restfully::Collection:0x8f9d46>,
|
86
|
-
@version=#<Restfully::Resource:0x8fa354>,
|
87
|
-
@versions=#<Restfully::Collection:0x8fa0b6>,
|
88
|
-
@status=#<Restfully::Collection:0x8f95ee>
|
89
|
-
PROPERTIES
|
90
|
-
"name"=>"Rennes",
|
91
|
-
"latitude"=>48.1,
|
92
|
-
"location"=>"Rennes, France",
|
93
|
-
"security_contact"=>"rennes-staff@lists.grid5000.fr",
|
94
|
-
"uid"=>"rennes",
|
95
|
-
"type"=>"site",
|
96
|
-
"user_support_contact"=>"rennes-staff@lists.grid5000.fr",
|
97
|
-
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
|
98
|
-
"description"=>"",
|
99
|
-
"longitude"=>-1.6667,
|
100
|
-
"compilation_server"=>false,
|
101
|
-
"email_contact"=>"rennes-staff@lists.grid5000.fr",
|
102
|
-
"web"=>"http://www.irisa.fr",
|
103
|
-
"sys_admin_contact"=>"rennes-staff@lists.grid5000.fr">
|
104
|
-
|
105
|
-
or:
|
106
|
-
irb(main):006:0> root.sites.map{|s| s['uid']}.grep(/re/)
|
107
|
-
=> ["grenoble", "rennes"]
|
108
|
-
|
109
|
-
A shortcut is available to find a specific entry in a collection, by entering the searched uid as a Symbol:
|
110
|
-
irb(main):007:0> root.sites[:rennes]
|
111
|
-
# will find the item whose uid is "rennes"
|
112
|
-
|
113
|
-
For ease of use and better security, you may prefer to use a configuration file to avoid re-entering the options every time you use the client:
|
114
|
-
$ echo '
|
115
|
-
base_uri: https://api.grid5000.fr/sid/grid5000
|
116
|
-
username: MYLOGIN
|
117
|
-
password: MYPASSWORD
|
118
|
-
' > ~/.restfully/api.grid5000.fr.yml && chmod 600 ~/.restfully/api.grid5000.fr.yml
|
119
|
-
|
120
|
-
And then:
|
121
|
-
$ restfully -c ~/.restfully/api.grid5000.fr.yml
|
122
|
-
|
123
|
-
=== As a library
|
124
|
-
See the +examples+ directory for examples.
|
125
|
-
|
126
|
-
== Discovering the API capabilities
|
127
|
-
A Restfully::Resource (and by extension its child Restfully::Collection) has the following methods available for introspection:
|
128
|
-
* <tt>links</tt> will return a hash whose keys are the name of the methods that can be called to navigate between resources;
|
129
|
-
* <tt>http_methods</tt> will return an array containing the list of the HTTP methods that are allowed on the resource;
|
130
|
-
|
131
|
-
== Note on Patches/Pull Requests
|
132
|
-
|
133
|
-
* Fork the project.
|
134
|
-
* Make your feature addition or bug fix.
|
135
|
-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
136
|
-
* Commit, do not mess with rakefile, version, or history (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull).
|
137
|
-
* Send me a pull request. Bonus points for topic branches.
|
138
|
-
|
139
|
-
== Testing
|
140
|
-
|
141
|
-
* rake spec, or
|
142
|
-
* run autotest in the project directory.
|
143
|
-
|
144
|
-
== Copyright
|
145
|
-
|
146
|
-
Copyright (c) 2009 Cyril Rohr, INRIA Rennes - Bretagne Atlantique. See LICENSE for details.
|
data/TODO.rdoc
DELETED
@@ -1,3 +0,0 @@
|
|
1
|
-
* Detects methods allowed on a resource using the Allow HTTP header, and generate corresponding wrappers.
|
2
|
-
* On a 406, detects accepted content based on a (custom) HTTP header, and retry (if possible) with another Accept header for which a parser is available.
|
3
|
-
* Investigate the possibility of using a :include => {} when loading resources or collection so that Restfully prefetches the included associations (using threads or events).
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.6.3
|
data/examples/grid5000.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
-
require 'pp'
|
4
|
-
|
5
|
-
require File.dirname(__FILE__)+'/../lib/restfully'
|
6
|
-
|
7
|
-
logger = Logger.new(STDOUT)
|
8
|
-
logger.level = Logger::WARN
|
9
|
-
|
10
|
-
RestClient.log = 'stdout'
|
11
|
-
|
12
|
-
# This yaml file contains the following attributes:
|
13
|
-
# username: my_username
|
14
|
-
# password: my_password
|
15
|
-
options = YAML.load_file(File.expand_path('~/.restfully/api.grid5000.fr.yml'))
|
16
|
-
options[:base_uri] = 'https://api.grid5000.fr/sid/grid5000'
|
17
|
-
options[:logger] = logger
|
18
|
-
Restfully::Session.new(options) do |grid, session|
|
19
|
-
grid_stats = {'hardware' => {}, 'system' => {}}
|
20
|
-
grid.sites.each do |site|
|
21
|
-
site_stats = site.status.inject({'hardware' => {}, 'system' => {}}) {|accu, node_status|
|
22
|
-
accu['hardware'][node_status['hardware_state']] = (accu['hardware'][node_status['hardware_state']] || 0) + 1
|
23
|
-
accu['system'][node_status['system_state']] = (accu['system'][node_status['system_state']] || 0) + 1
|
24
|
-
accu
|
25
|
-
} rescue {'hardware' => {}, 'system' => {}}
|
26
|
-
grid_stats['hardware'].merge!(site_stats['hardware']) { |key,oldval,newval| oldval+newval }
|
27
|
-
grid_stats['system'].merge!(site_stats['system']) { |key,oldval,newval| oldval+newval }
|
28
|
-
p [site['uid'], site_stats]
|
29
|
-
end
|
30
|
-
p [:total, grid_stats]
|
31
|
-
puts "Getting status of a few nodes in rennes:"
|
32
|
-
pp grid.sites.find{|s| s['uid'] == 'rennes'}.status(:query => {:only => ['paradent-1', 'paradent-10', 'paramount-3']})
|
33
|
-
end
|
data/examples/scratch.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
m = MediaType.new('application/vnd.grid5000+json')
|
2
|
-
m.parser = JSON
|
3
|
-
|
4
|
-
class Grid5000MediaType < Restfully::MediaType
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def supports?(given_type)
|
8
|
-
if given_type.kind_of?(Restfully::MediaType)
|
9
|
-
given_type = given_type.type
|
10
|
-
end
|
11
|
-
case given_type
|
12
|
-
when Regexp
|
13
|
-
given_type =~ type
|
14
|
-
else
|
15
|
-
given_type == type.to_s || given_type =~ Regexp.new(type.to_s)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
get 'https://api.grid5000.fr/sid/grid5000'
|
25
|
-
type = 'application/vnd.grid5000+json'
|
26
|
-
m = MediaType.find(type)
|
27
|
-
if m.nil?
|
28
|
-
"does not know how to interpret #{type}"
|
29
|
-
else
|
30
|
-
grid5000 = Resource.new(uri, links, properties)
|
31
|
-
grid5000.sites[:rennes]
|
32
|
-
|
33
|
-
m.links
|
34
|
-
m.properties
|
35
|
-
end
|
36
|
-
|
37
|
-
|
data/lib/restfully/extensions.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
class BasicObject #:nodoc:
|
2
|
-
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
|
3
|
-
end unless defined?(BasicObject)
|
4
|
-
|
5
|
-
class Object
|
6
|
-
# Taken from ActiveSupport
|
7
|
-
def to_query(key)
|
8
|
-
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
9
|
-
"#{CGI.escape(key.to_s)}=#{CGI.escape(to_params.to_s)}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_params
|
13
|
-
to_s
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Hash
|
18
|
-
# Converts a hash into a string suitable for use as a URL query string.
|
19
|
-
# An optional <tt>namespace</tt> can be passed to enclose the param names.
|
20
|
-
# Taken from ActiveSupport
|
21
|
-
def to_params(namespace = nil)
|
22
|
-
collect do |key, value|
|
23
|
-
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
24
|
-
end * '&'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class Array
|
29
|
-
# Taken from ActiveSupport
|
30
|
-
def to_query(key)
|
31
|
-
prefix = "#{key}[]"
|
32
|
-
collect { |value| value.to_query(prefix) }.join '&'
|
33
|
-
end
|
34
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Restfully
|
2
|
-
module HTTP
|
3
|
-
module Adapters
|
4
|
-
|
5
|
-
class AbstractAdapter
|
6
|
-
attr_reader :logger, :options
|
7
|
-
def initialize(base_uri, options = {})
|
8
|
-
@options = options.symbolize_keys
|
9
|
-
@logger = @options.delete(:logger) || Restfully::NullLogger.new
|
10
|
-
@base_uri = base_uri
|
11
|
-
end
|
12
|
-
|
13
|
-
def get(request)
|
14
|
-
raise NotImplementedError, "GET is not supported by your adapter."
|
15
|
-
end
|
16
|
-
def post(request)
|
17
|
-
raise NotImplementedError, "POST is not supported by your adapter."
|
18
|
-
end
|
19
|
-
def put(request)
|
20
|
-
raise NotImplementedError, "PUT is not supported by your adapter."
|
21
|
-
end
|
22
|
-
def delete(request)
|
23
|
-
raise NotImplementedError, "DELETE is not supported by your adapter."
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'restfully/http/adapters/abstract_adapter'
|
2
|
-
require 'patron'
|
3
|
-
|
4
|
-
module Restfully
|
5
|
-
module HTTP
|
6
|
-
module Adapters
|
7
|
-
class PatronAdapter < AbstractAdapter
|
8
|
-
|
9
|
-
def initialize(base_url, options = {})
|
10
|
-
super(base_url, options)
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'restfully/http/adapters/abstract_adapter'
|
2
|
-
require 'restclient'
|
3
|
-
|
4
|
-
module Restfully
|
5
|
-
module HTTP
|
6
|
-
module Adapters
|
7
|
-
class RestClientAdapter < AbstractAdapter
|
8
|
-
|
9
|
-
def initialize(base_uri, options = {})
|
10
|
-
super(base_uri, options)
|
11
|
-
@options[:user] = @options.delete(:username)
|
12
|
-
RestClient.log = logger
|
13
|
-
end # def initialize
|
14
|
-
|
15
|
-
def head(request)
|
16
|
-
in_order_to_get_the_response_to(request) do |resource|
|
17
|
-
resource.head(convert_header_keys_into_symbols(request.headers))
|
18
|
-
end
|
19
|
-
end # def head
|
20
|
-
|
21
|
-
def get(request)
|
22
|
-
in_order_to_get_the_response_to(request) do |resource|
|
23
|
-
resource.get(convert_header_keys_into_symbols(request.headers))
|
24
|
-
end
|
25
|
-
end # def get
|
26
|
-
|
27
|
-
def delete(request)
|
28
|
-
in_order_to_get_the_response_to(request) do |resource|
|
29
|
-
resource.delete(convert_header_keys_into_symbols(request.headers))
|
30
|
-
end
|
31
|
-
end # def delete
|
32
|
-
|
33
|
-
def put(request)
|
34
|
-
in_order_to_get_the_response_to(request) do |resource|
|
35
|
-
resource.put(request.raw_body, convert_header_keys_into_symbols(request.headers))
|
36
|
-
end
|
37
|
-
end # def put
|
38
|
-
|
39
|
-
def post(request)
|
40
|
-
in_order_to_get_the_response_to(request) do |resource|
|
41
|
-
resource.post(request.raw_body, convert_header_keys_into_symbols(request.headers))
|
42
|
-
end
|
43
|
-
end # def post
|
44
|
-
|
45
|
-
protected
|
46
|
-
def in_order_to_get_the_response_to(request, &block)
|
47
|
-
begin
|
48
|
-
resource = RestClient::Resource.new(request.uri.to_s, @options)
|
49
|
-
response = block.call(resource)
|
50
|
-
headers = response.headers
|
51
|
-
body = response.to_s
|
52
|
-
headers.delete(:status)
|
53
|
-
status = response.code
|
54
|
-
rescue RestClient::ExceptionWithResponse => e
|
55
|
-
body = e.response.to_s
|
56
|
-
headers = e.response.headers rescue {}
|
57
|
-
status = e.http_code
|
58
|
-
end
|
59
|
-
Response.new(status, headers, body)
|
60
|
-
end # def in_order_to_get_the_response_to
|
61
|
-
|
62
|
-
# there is a bug in RestClient, when passing headers whose keys are string that are already defined as default headers, they get overwritten.
|
63
|
-
def convert_header_keys_into_symbols(headers)
|
64
|
-
headers.inject({}) do |final, (key,value)|
|
65
|
-
key = key.to_s.gsub(/-/, "_").downcase.to_sym
|
66
|
-
final[key] = value
|
67
|
-
final
|
68
|
-
end
|
69
|
-
end # def convert_header_keys_into_symbols
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Restfully
|
2
|
-
module HTTP
|
3
|
-
module Headers
|
4
|
-
def sanitize_http_headers(headers = {})
|
5
|
-
sanitized_headers = {}
|
6
|
-
headers.each do |key, value|
|
7
|
-
sanitized_key = key.to_s.downcase.gsub(/[_-]/, ' ').split(' ').map{|word| word.capitalize}.join("-")
|
8
|
-
sanitized_value = case value
|
9
|
-
when Array
|
10
|
-
value.join(", ")
|
11
|
-
else
|
12
|
-
value
|
13
|
-
end
|
14
|
-
sanitized_headers[sanitized_key] = sanitized_value
|
15
|
-
end
|
16
|
-
sanitized_headers
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/restfully/parsing.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Restfully
|
4
|
-
|
5
|
-
module Parsing
|
6
|
-
|
7
|
-
class ParserNotFound < Restfully::Error; end
|
8
|
-
|
9
|
-
PARSERS = [
|
10
|
-
{
|
11
|
-
:supported_types => [/^application\/.*?json/i],
|
12
|
-
:parse => lambda{|object, options|
|
13
|
-
require 'json'
|
14
|
-
JSON.parse(object)
|
15
|
-
},
|
16
|
-
:dump => lambda{|object, options|
|
17
|
-
require 'json'
|
18
|
-
JSON.dump(object)
|
19
|
-
},
|
20
|
-
:object => true
|
21
|
-
},
|
22
|
-
{
|
23
|
-
:supported_types => [/^text\/.*?(plain|html)/i],
|
24
|
-
:parse => lambda{|object, options| object},
|
25
|
-
:dump => lambda{|object, options| object}
|
26
|
-
},
|
27
|
-
{ # just store the binary data in a 'raw' property
|
28
|
-
:supported_types => ["application/zip"],
|
29
|
-
:parse => lambda{|object, options| {'raw' => object}},
|
30
|
-
:dump => lambda{|object, options| object['raw']}
|
31
|
-
}
|
32
|
-
]
|
33
|
-
|
34
|
-
def unserialize(object, options = {})
|
35
|
-
content_type = options[:content_type]
|
36
|
-
content_type ||= object.headers['Content-Type'] if object.respond_to?(:headers)
|
37
|
-
parser = select_parser_for(content_type)
|
38
|
-
if parser
|
39
|
-
parser[:parse].call(object, options)
|
40
|
-
else
|
41
|
-
raise ParserNotFound.new("Cannot find a parser to parse '#{content_type}' content.")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def serialize(object, options = {})
|
46
|
-
content_type = options[:content_type]
|
47
|
-
content_type ||= object.headers['Content-Type'] if object.respond_to?(:headers)
|
48
|
-
parser = select_parser_for(content_type)
|
49
|
-
if parser
|
50
|
-
parser[:dump].call(object, options)
|
51
|
-
else
|
52
|
-
raise ParserNotFound.new("Cannot find a parser to dump object into '#{content_type}' content.")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def select_parser_for(content_type)
|
57
|
-
raise ParserNotFound.new("The Content-Type HTTP header of the resource is empty. Cannot find a parser.") if content_type.nil? || content_type.empty?
|
58
|
-
content_type.split(",").each do |type|
|
59
|
-
parser = PARSERS.find{|parser| parser[:supported_types].find{|supported_type| type =~ (supported_type.kind_of?(String) ? Regexp.new(supported_type) : supported_type) }}
|
60
|
-
return parser unless parser.nil?
|
61
|
-
end
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|