cryx-g5k 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/g5k/parsing/parser.rb +47 -0
- data/lib/g5k/parsing.rb +7 -0
- data/lib/g5k/rack/accept_format.rb +46 -0
- data/lib/g5k/rack/jsonp.rb +43 -0
- data/lib/g5k/sinatra/helpers.rb +32 -0
- data/lib/g5k.rb +5 -0
- data/spec/g5k_spec.rb +4 -0
- data/spec/parsing_spec.rb +44 -0
- data/spec/spec_helper.rb +9 -0
- metadata +68 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module G5K
|
2
|
+
module Parsing
|
3
|
+
class UnsupportedFormat < StandardError; end
|
4
|
+
class Parser
|
5
|
+
@@parsers = []
|
6
|
+
def self.add(*parsers)
|
7
|
+
parsers.each{ |parser|
|
8
|
+
parser.dependencies
|
9
|
+
@@parsers << parser
|
10
|
+
}
|
11
|
+
self
|
12
|
+
end
|
13
|
+
def self.select(parser_format)
|
14
|
+
raise UnsupportedFormat, "The format cannot be nil." if parser_format.nil?
|
15
|
+
parsers = @@parsers.select{|parser| parser.supported_formats.include?(parser_format.to_sym)}
|
16
|
+
if parsers.empty?
|
17
|
+
raise UnsupportedFormat, "No parser found for '#{parser_format}' format."
|
18
|
+
else
|
19
|
+
parsers.first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def self.available_parsers; @@parsers; end
|
23
|
+
end
|
24
|
+
|
25
|
+
class JSONParser
|
26
|
+
def self.dependencies
|
27
|
+
require 'json'
|
28
|
+
end
|
29
|
+
def self.supported_formats
|
30
|
+
[:json]
|
31
|
+
end
|
32
|
+
def self.dump(object, options = {:format => :pretty})
|
33
|
+
case options[:format]
|
34
|
+
when :pretty
|
35
|
+
JSON.pretty_generate object
|
36
|
+
else
|
37
|
+
object.to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
def self.load(object)
|
41
|
+
JSON.parse(object)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Parser.add(JSONParser)
|
46
|
+
end
|
47
|
+
end
|
data/lib/g5k/parsing.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module G5K
|
2
|
+
module Rack
|
3
|
+
#
|
4
|
+
# A Rack middleware for automatically adding a <tt>format</tt> token at the end of the request path
|
5
|
+
# when there is none. It can detect formats passed in the HTTP_ACCEPT header to populate this token.
|
6
|
+
#
|
7
|
+
# e.g.:
|
8
|
+
# GET /some/resource HTTP/1.1
|
9
|
+
# Accept: application/json
|
10
|
+
# ->
|
11
|
+
# GET /some/resource.json HTTP/1.1
|
12
|
+
# Accept: application/json
|
13
|
+
#
|
14
|
+
# You can add custom types with this kind of function (taken from sinatra):
|
15
|
+
# def mime(ext, type)
|
16
|
+
# ext = ".#{ext}" unless ext.to_s[0] == ?.
|
17
|
+
# Rack::Mime::MIME_TYPES[ext.to_s] = type
|
18
|
+
# end
|
19
|
+
# and then:
|
20
|
+
# mime :json, 'application/json'
|
21
|
+
#
|
22
|
+
# Note: it does not take into account multiple media types in the Accept header.
|
23
|
+
# The first media type takes precedence over all the others.
|
24
|
+
#
|
25
|
+
# MIT-License - Cyril Rohr
|
26
|
+
#
|
27
|
+
class AcceptFormat
|
28
|
+
# Constants
|
29
|
+
DEFAULT_EXTENSION = ".html"
|
30
|
+
|
31
|
+
def initialize(app)
|
32
|
+
@app = app
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
req = Rack::Request.new(env)
|
37
|
+
unless req.path_info =~ /(.*)\.(.+)/
|
38
|
+
accept = env['HTTP_ACCEPT'].scan(/[^;,\s]*\/[^;,\s]*/)[0] rescue ""
|
39
|
+
extension = Rack::Mime::MIME_TYPES.invert[accept] || DEFAULT_EXTENSION
|
40
|
+
req.path_info = req.path_info+"#{extension}"
|
41
|
+
end
|
42
|
+
@app.call(env)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module G5K
|
2
|
+
module Rack
|
3
|
+
|
4
|
+
# A Rack middleware for providing JSON-P support.
|
5
|
+
#
|
6
|
+
# Full credit to Flinn Mueller (http://actsasflinn.com/) for this contribution.
|
7
|
+
#
|
8
|
+
class JSONP
|
9
|
+
|
10
|
+
def initialize(app)
|
11
|
+
@app = app
|
12
|
+
end
|
13
|
+
|
14
|
+
# Proxies the request to the application, stripping out the JSON-P callback
|
15
|
+
# method and padding the response with the appropriate callback format.
|
16
|
+
#
|
17
|
+
# Changes nothing if no <tt>callback</tt> param is specified.
|
18
|
+
#
|
19
|
+
def call(env)
|
20
|
+
status, headers, response = @app.call(env)
|
21
|
+
request = Rack::Request.new(env)
|
22
|
+
if request.params.include?('callback')
|
23
|
+
response = pad(request.params.delete('callback'), response)
|
24
|
+
headers['Content-Length'] = response.length.to_s
|
25
|
+
end
|
26
|
+
[status, headers, response]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Pads the response with the appropriate callback format according to the
|
30
|
+
# JSON-P spec/requirements.
|
31
|
+
#
|
32
|
+
# The Rack response spec indicates that it should be enumerable. The method
|
33
|
+
# of combining all of the data into a single string makes sense since JSON
|
34
|
+
# is returned as a full string.
|
35
|
+
#
|
36
|
+
def pad(callback, response, body = "")
|
37
|
+
response.each{ |s| body << s }
|
38
|
+
"#{callback}(#{body})"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module G5K
|
2
|
+
module Sinatra
|
3
|
+
module Helpers
|
4
|
+
def returning(thing = nil)
|
5
|
+
yield thing
|
6
|
+
thing
|
7
|
+
end
|
8
|
+
|
9
|
+
def provides *formats
|
10
|
+
if params[:format].nil? || !formats.include?(params[:format].to_sym)
|
11
|
+
throw :halt, [406, "The accepted types are: #{formats.join(", ")}"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def formatted_error(http_status, options = {})
|
16
|
+
body = {:code => (options[:code] || http_status), :message => options[:message], :title => options[:title]}
|
17
|
+
format = options[:format] || params[:format] || "txt"
|
18
|
+
content_type format.to_sym, :charset => 'utf-8'
|
19
|
+
if (parser = Parser.select(format.to_sym))
|
20
|
+
body = parser.dump(body)
|
21
|
+
else
|
22
|
+
body.map!{|(k,v)| [k,v].join("=")}.join(";")
|
23
|
+
end
|
24
|
+
error(http_status, body)
|
25
|
+
end
|
26
|
+
|
27
|
+
def compute_etag(*differentiators)
|
28
|
+
Digest::SHA1.hexdigest(differentiators.join("."))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/g5k.rb
ADDED
data/spec/g5k_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/g5k/parsing'
|
3
|
+
|
4
|
+
include G5K::Parsing
|
5
|
+
describe "Parsing" do
|
6
|
+
before do
|
7
|
+
require 'rubygems'
|
8
|
+
end
|
9
|
+
it "should add the json parser" do
|
10
|
+
Parser.add(JSONParser).available_parsers.should include(JSONParser)
|
11
|
+
end
|
12
|
+
it "should return an error if a parser is not available for the required format" do
|
13
|
+
Parser.add(JSONParser)
|
14
|
+
lambda{Parser.select(:xml)}.should raise_error(UnsupportedFormat)
|
15
|
+
end
|
16
|
+
it "should raise an UnsupportedFormat if the given format is nil" do
|
17
|
+
lambda{Parser.select(nil)}.should raise_error(UnsupportedFormat)
|
18
|
+
end
|
19
|
+
it "should select the parser for the given format, if made available" do
|
20
|
+
Parser.add(JSONParser)
|
21
|
+
Parser.select(:json).should == JSONParser
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe JSONParser do
|
26
|
+
before do
|
27
|
+
require 'rubygems'
|
28
|
+
end
|
29
|
+
it "should successfully parse a json object" do
|
30
|
+
object = {'a' => 1, 'b' => {'c' => 2, 'd' => [3]}}
|
31
|
+
JSONParser.load(object.to_json).should == object
|
32
|
+
end
|
33
|
+
it "should pretty generate an object" do
|
34
|
+
object = {'a' => 1, 'b' => {'c' => 2, 'd' => [3]}}
|
35
|
+
JSON.should_receive(:pretty_generate).with(object)
|
36
|
+
JSONParser.dump(object, :format => :pretty)
|
37
|
+
end
|
38
|
+
it "should not pretty generate an object" do
|
39
|
+
object = {'a' => 1, 'b' => {'c' => 2, 'd' => [3]}}
|
40
|
+
JSON.should_not_receive(:pretty_generate).with(object)
|
41
|
+
object.should_receive(:to_json)
|
42
|
+
JSONParser.dump(object, :format => :raw)
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cryx-g5k
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cyril Rohr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-02 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Collection of tools and libs for Grid5000 APIs.
|
17
|
+
email: cyril.rohr@irisa.fr
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/g5k
|
27
|
+
- lib/g5k/parsing
|
28
|
+
- lib/g5k/parsing/parser.rb
|
29
|
+
- lib/g5k/parsing.rb
|
30
|
+
- lib/g5k/rack
|
31
|
+
- lib/g5k/rack/accept_format.rb
|
32
|
+
- lib/g5k/rack/jsonp.rb
|
33
|
+
- lib/g5k/schema
|
34
|
+
- lib/g5k/sinatra
|
35
|
+
- lib/g5k/sinatra/helpers.rb
|
36
|
+
- lib/g5k.rb
|
37
|
+
- spec/g5k_spec.rb
|
38
|
+
- spec/parsing_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/cryx/g5k
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --inline-source
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.2.0
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: TODO
|
67
|
+
test_files: []
|
68
|
+
|