rubyrest 0.0.3 → 0.0.4
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/CHANGELOG +14 -1
- data/README +19 -6
- data/Rakefile +1 -1
- data/bin/rubyrest +12 -8
- data/examples/hello.rb +10 -10
- data/lib/rubyrest/atom.rb +7 -0
- data/lib/rubyrest/client.rb +122 -60
- data/lib/rubyrest/config.rb +9 -7
- data/lib/rubyrest/engine.rb +12 -8
- data/lib/rubyrest/servlets.rb +5 -1
- metadata +2 -2
data/CHANGELOG
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
== What's next?
|
|
2
|
+
|
|
3
|
+
* Introduce the concept of 'security authority' so that multiple deployed instances of rubyrest can be federated by a single authentication and authorization service.
|
|
4
|
+
* Use mongrel as the HTTP server implementation, instead of WEBrick
|
|
5
|
+
* Refactor code, style it more 'a la' Ruby.
|
|
6
|
+
* Provide more complex examples, such as Database Configurations
|
|
7
|
+
|
|
8
|
+
== Release 0.0.4
|
|
9
|
+
|
|
10
|
+
* Module RubyRest::Client has been redesigned into class RubyRest::Client::Default which offers a higher level api by translating create, retrieve, update and delete method calls into proper HTTP GET, POST, PUT and DELETE requests.
|
|
11
|
+
* Engine configurations are now externalized so they can be provided programmatically as Hashes or loaded from YAML files.
|
|
12
|
+
* The rubyrest command line now supports different deployment environments (dev, live)
|
|
13
|
+
|
|
1
14
|
== Release 0.0.3
|
|
2
15
|
|
|
3
16
|
* Added module RubyRest::Client, providing a convenience client to REST APIs deployed with Ruby-on-Rest
|
|
4
17
|
* Improved Atom representation of resources (feeds, entries and service documents)
|
|
5
|
-
* Dashboard (service document) can now be customized by
|
|
18
|
+
* Dashboard (service document) can now be customized by developers by implementing a class with name Dashboard.
|
|
6
19
|
|
|
7
20
|
== Release 0.0.2
|
|
8
21
|
|
data/README
CHANGED
|
@@ -30,15 +30,27 @@ If you have any comments or suggestions please send an email to pedro dot gutier
|
|
|
30
30
|
|
|
31
31
|
Please have a look at the examples provided, they are simple enough to let you grasp how rubyrest works.
|
|
32
32
|
|
|
33
|
-
=== Starting
|
|
33
|
+
=== Starting the service
|
|
34
34
|
|
|
35
35
|
Ruby-on-Rest provides a shell command. Open your console, and type the following:
|
|
36
36
|
|
|
37
|
-
rubyrest
|
|
37
|
+
rubyrest dev <my_service> (for development deployment)
|
|
38
|
+
rubyrest live <my_service> (for live deployment)
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
This will first look at a YAML file called <my_service>_dev.yaml or <my_service>_live.yaml under the
|
|
41
|
+
current path.
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
Ruby-on-Rest can also be embedded in ruby code. This is useful for testing purposes:
|
|
44
|
+
|
|
45
|
+
config = {
|
|
46
|
+
"service" => "hello",
|
|
47
|
+
"prefix" => "acme",
|
|
48
|
+
"serviceport" => 9003
|
|
49
|
+
"daemon" => true
|
|
50
|
+
"entities" => [ "welcomeservice" ]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
engine = RubyRest::Engine.new( config )
|
|
42
54
|
engine.start
|
|
43
55
|
# ...
|
|
44
56
|
# do some http calls here
|
|
@@ -48,7 +60,8 @@ Ruby-on-Rest can also be embedded in ruby code. This is useful for testing purpo
|
|
|
48
60
|
|
|
49
61
|
=== Configuring your service
|
|
50
62
|
|
|
51
|
-
Ruby-on-Rest will try to load a file name <my_service>.rb which
|
|
52
|
-
|
|
63
|
+
Ruby-on-Rest will try to load a file name <my_service>.rb which provides the implementation of the service.
|
|
64
|
+
|
|
65
|
+
|
|
53
66
|
|
|
54
67
|
|
data/Rakefile
CHANGED
|
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
|
6
6
|
include FileUtils
|
|
7
7
|
|
|
8
8
|
NAME = "rubyrest"
|
|
9
|
-
VERS = "0.0.
|
|
9
|
+
VERS = "0.0.4"
|
|
10
10
|
CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
|
|
11
11
|
RDOC_OPTS = ['--quiet', '--title', "Ruby-on-Rest: A simple REST framework for Ruby",
|
|
12
12
|
"--opname", "index.html",
|
data/bin/rubyrest
CHANGED
|
@@ -7,20 +7,24 @@ require 'rubygems'
|
|
|
7
7
|
require 'rubyrest'
|
|
8
8
|
|
|
9
9
|
service = ARGV[1]
|
|
10
|
-
|
|
11
|
-
action = ARGV[0]
|
|
10
|
+
deploy_mode = ARGV[0]
|
|
12
11
|
|
|
13
|
-
if
|
|
14
|
-
puts "Usage: rubyrest
|
|
12
|
+
if service == nil or deploy_mode == nil
|
|
13
|
+
puts "Usage: rubyrest dev|live <your_service>"
|
|
15
14
|
puts "Ruby-on-Rest: Simple REST for Ruby."
|
|
16
15
|
puts
|
|
17
16
|
puts "Examples:"
|
|
18
|
-
puts " rubyrest
|
|
19
|
-
puts " rubyrest
|
|
17
|
+
puts " rubyrest dev grape"
|
|
18
|
+
puts " rubyrest live grape"
|
|
20
19
|
puts
|
|
21
20
|
puts "For more information see http://rubyrest.rubyforge.org"
|
|
22
21
|
else
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
# load the yaml file for the
|
|
23
|
+
# deploy specific configuration, and build a hash out of it
|
|
24
|
+
params = YAML::load_file( "#{service}_#{deploy_mode}.yaml" )
|
|
25
|
+
params[ "service" ] = service
|
|
26
|
+
|
|
27
|
+
engine = RubyRest::Engine.new( params )
|
|
28
|
+
engine.start
|
|
25
29
|
end
|
|
26
30
|
|
data/examples/hello.rb
CHANGED
|
@@ -11,18 +11,18 @@ module Acme
|
|
|
11
11
|
# connectivity.
|
|
12
12
|
class Config < RubyRest::SimpleConfig
|
|
13
13
|
|
|
14
|
-
# Inits the internal hash of
|
|
15
|
-
# configuration options. This is the minimal expression
|
|
16
|
-
# of a Ruby-on-Rest configuration hash.
|
|
17
|
-
def initialize
|
|
18
|
-
@hash = {
|
|
19
|
-
:servicemodel => [ "welcomeservice" ],
|
|
20
|
-
:serviceport => 9001
|
|
21
|
-
}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
14
|
end
|
|
25
15
|
|
|
16
|
+
# The dashboard that provides an entry point to the
|
|
17
|
+
# application
|
|
18
|
+
class Dashboard
|
|
19
|
+
|
|
20
|
+
def self.rest_retrieve( principal )
|
|
21
|
+
[ "welcomeservice" ]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
26
|
# The domain service that actually implements
|
|
27
27
|
# 'business' logic. In this case, it's just going to say "Hello!"
|
|
28
28
|
# by returning a HelloBean object
|
data/lib/rubyrest/atom.rb
CHANGED
|
@@ -167,6 +167,13 @@ module RubyRest
|
|
|
167
167
|
def id
|
|
168
168
|
text( "/entry/moodisland:content/id" )
|
|
169
169
|
end
|
|
170
|
+
|
|
171
|
+
# Overrides the default implementation
|
|
172
|
+
# by returning a new Ruby-on-Rest Atom Document
|
|
173
|
+
def clone
|
|
174
|
+
self.class.new self.to_s
|
|
175
|
+
end
|
|
176
|
+
|
|
170
177
|
end
|
|
171
178
|
|
|
172
179
|
end
|
data/lib/rubyrest/client.rb
CHANGED
|
@@ -4,69 +4,131 @@ module RubyRest
|
|
|
4
4
|
# to REST services offered by Ruby-on-REST.
|
|
5
5
|
module Client
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def hash2entry( data )
|
|
17
|
-
doc = RubyRest::Atom::Entry::Document.new
|
|
18
|
-
entry = REXML::Element.new( "entry", doc )
|
|
19
|
-
content = REXML::Element.new( "content", entry )
|
|
20
|
-
data.each { |name,value|
|
|
21
|
-
body = REXML::Element.new( name.to_s, content )
|
|
22
|
-
body.text = value
|
|
23
|
-
}
|
|
24
|
-
return doc
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Parses the response body string as a Ruby-on-Rest Entry document,
|
|
28
|
-
def str2entry( rsp )
|
|
29
|
-
begin
|
|
30
|
-
RubyRest::Atom::Entry::Document.new( rsp.body ) if response.body
|
|
31
|
-
rescue => e
|
|
32
|
-
return nil
|
|
7
|
+
# Default client for web services deployed with
|
|
8
|
+
# Ruby-on-Rest. Translates create, retrieve, update and
|
|
9
|
+
# delete methods into POST, GET, PUT and DELETE http requests
|
|
10
|
+
class Default
|
|
11
|
+
|
|
12
|
+
# Configures the server name and port
|
|
13
|
+
def self.with_server name, port
|
|
14
|
+
@host = name
|
|
15
|
+
@port = port
|
|
33
16
|
end
|
|
17
|
+
|
|
18
|
+
# Returns the hostname of the service to
|
|
19
|
+
# connect to
|
|
20
|
+
def self.host
|
|
21
|
+
@host
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns the port number on which to
|
|
25
|
+
# connect to
|
|
26
|
+
def self.port
|
|
27
|
+
@port
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Converts the specified hash of data into a
|
|
31
|
+
# query string, then performs a GET http request.
|
|
32
|
+
# The response body is returned as an an Atom document if
|
|
33
|
+
# the response status code is 200.
|
|
34
|
+
def retrieve( path, data, api_key )
|
|
35
|
+
path << to_query_string( data ) if data
|
|
36
|
+
headers = prepare_headers( api_key )
|
|
37
|
+
rsp = http.get( path, headers )
|
|
38
|
+
return to_xml( rsp ) if rsp.code.to_i == 200
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Converts the specified hash of data into a
|
|
42
|
+
# simplified Atom Entry, then performs a POST http request.
|
|
43
|
+
# The response body is returned as an an Atom Entry if
|
|
44
|
+
# the response status code is 201.
|
|
45
|
+
def create( path, data, api_key )
|
|
46
|
+
body = nil
|
|
47
|
+
body = hash2entry( data ).to_s if data != nil
|
|
48
|
+
headers = prepare_headers( api_key )
|
|
49
|
+
rsp = http.post( path, body, headers )
|
|
50
|
+
return to_xml( rsp ) if rsp.code.to_i == 201
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Converts the specified hash of data into a
|
|
54
|
+
# simplified Atom Entry, then performs a PUT http request.
|
|
55
|
+
# The response body is returned as an an Atom Entry if
|
|
56
|
+
# the response status code is 200.
|
|
57
|
+
def update( path, data, api_key )
|
|
58
|
+
body = nil
|
|
59
|
+
body = hash2entry( data ).to_s if data != nil
|
|
60
|
+
headers = prepare_headers( api_key )
|
|
61
|
+
rsp = http.put( path, body, headers )
|
|
62
|
+
return to_xml( rsp ) if rsp.code.to_i == 200
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Converts the specified hash of data into a
|
|
66
|
+
# query string, then performs a DELETE http request.
|
|
67
|
+
# This method simply returns the response status code
|
|
68
|
+
def delete( path, data, api_key )
|
|
69
|
+
path << to_query_string( data ) if data
|
|
70
|
+
headers = prepare_headers( api_key )
|
|
71
|
+
rsp = http.delete( path, headers )
|
|
72
|
+
rsp.code.to_i
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Convenience method that returns the
|
|
76
|
+
# service document from the service endpoint
|
|
77
|
+
def dashboard( api_key )
|
|
78
|
+
retrieve( "/", nil, api_key )
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Convenience method that returns a new HTTP object
|
|
82
|
+
# for each call
|
|
83
|
+
def http
|
|
84
|
+
Net::HTTP.new( self.class.host, self.class.port )
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Builds a headers hash, with the specified
|
|
88
|
+
# authorization token if not nil
|
|
89
|
+
def prepare_headers( api_key = nil )
|
|
90
|
+
headers = { "Content-Type" => "text/xml; charset=utf-8" }
|
|
91
|
+
headers[ "token" ] = api_key if api_key != nil
|
|
92
|
+
return headers
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Converts the specified hash of data into a simplified
|
|
96
|
+
# Atom Entry document.
|
|
97
|
+
def hash2entry( data )
|
|
98
|
+
doc = RubyRest::Atom::Entry::Document.new
|
|
99
|
+
entry = REXML::Element.new( "entry", doc )
|
|
100
|
+
content = REXML::Element.new( "content", entry )
|
|
101
|
+
data.each { |name,value|
|
|
102
|
+
body = REXML::Element.new( name.to_s, content )
|
|
103
|
+
body.text = value
|
|
104
|
+
}
|
|
105
|
+
return doc
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Parses the response body string as a Ruby-on-Rest XML Document,
|
|
109
|
+
# which can be a Feed or Entry, or Service Document
|
|
110
|
+
def to_xml( rsp )
|
|
111
|
+
begin
|
|
112
|
+
RubyRest::Atom::Entry::Document.new( rsp.body ) if rsp.body
|
|
113
|
+
rescue => e
|
|
114
|
+
puts "unable to parse response body: " + e.message
|
|
115
|
+
puts "---- response body ----"
|
|
116
|
+
puts rsp.body
|
|
117
|
+
puts "-----------------------"
|
|
118
|
+
return nil
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Reusable method that creates a query string
|
|
123
|
+
# with the specified hash of data
|
|
124
|
+
def to_query_string( data )
|
|
125
|
+
qs = "?"
|
|
126
|
+
params.each{ |k,v| qs << "#{k}=#{v}&" }
|
|
127
|
+
qs.chop!
|
|
128
|
+
end
|
|
129
|
+
|
|
34
130
|
end
|
|
35
131
|
|
|
36
|
-
# Reusable method that creates a query string
|
|
37
|
-
# with the specified hash of data
|
|
38
|
-
def to_query_string( data )
|
|
39
|
-
qs = "?"
|
|
40
|
-
params.each{ |k,v| qs << "#{k}=#{v}&" }
|
|
41
|
-
qs.chop!
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Performs a POST request and returns the raw response object
|
|
45
|
-
# The data (a hash of name,value pairs) is converted to an
|
|
46
|
-
# simplified Atom Entry
|
|
47
|
-
def post( server, port, path, data, token )
|
|
48
|
-
body = nil
|
|
49
|
-
body = hash2entry( data ).to_s if data != nil
|
|
50
|
-
headers = prepare_headers( token )
|
|
51
|
-
rsp = Net::HTTP.new( server, port ).post( path, body, headers )
|
|
52
|
-
[ rsp.code.to_i, str2entry( rsp ) ]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Performs a GET request and returns the raw response object
|
|
56
|
-
# The data (a hash of name,value pairs) is converted to a query
|
|
57
|
-
# string, then concatenated to the path.
|
|
58
|
-
def get( server, port, path, data, token )
|
|
59
|
-
path << to_query_string( data ) if data
|
|
60
|
-
headers = prepare_headers( token )
|
|
61
|
-
rsp = Net::HTTP.new( server, port ).get( path, headers )
|
|
62
|
-
[ rsp.code.to_i, str2entry( rsp ) ]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
132
|
end
|
|
68
133
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
134
|
end
|
data/lib/rubyrest/config.rb
CHANGED
|
@@ -13,11 +13,7 @@ module RubyRest
|
|
|
13
13
|
#
|
|
14
14
|
# module MyService
|
|
15
15
|
# class Config < RubyRest::SimpleConfig
|
|
16
|
-
#
|
|
17
|
-
# @hash = {
|
|
18
|
-
# ... my config here ..
|
|
19
|
-
# }
|
|
20
|
-
# end
|
|
16
|
+
#
|
|
21
17
|
# end
|
|
22
18
|
# end
|
|
23
19
|
#
|
|
@@ -25,6 +21,12 @@ module RubyRest
|
|
|
25
21
|
class SimpleConfig
|
|
26
22
|
include RubyRest::Tools
|
|
27
23
|
|
|
24
|
+
# Initializes a new configuration with the specified
|
|
25
|
+
# hash of data
|
|
26
|
+
def initialize( hash )
|
|
27
|
+
@hash = hash
|
|
28
|
+
end
|
|
29
|
+
|
|
28
30
|
# Overrides the specified configuration option
|
|
29
31
|
# with its new value
|
|
30
32
|
def []=( name, value )
|
|
@@ -45,7 +47,7 @@ module RubyRest
|
|
|
45
47
|
|
|
46
48
|
# Dumps all the configuration options
|
|
47
49
|
def to_s
|
|
48
|
-
@hash.each{ |k,v| puts "#{k}
|
|
50
|
+
@hash.each{ |k,v| puts "key: #{k}, value: #{v}" }
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
end
|
|
@@ -60,7 +62,7 @@ module RubyRest
|
|
|
60
62
|
# will delegate to a more specialized method, according to the
|
|
61
63
|
# :adapter config option
|
|
62
64
|
def connect_to_database
|
|
63
|
-
adapter_method = "#{
|
|
65
|
+
adapter_method = "#{self[:adapter]}_connect"
|
|
64
66
|
error( 004, self, adapter_method ) if !self.respond_to?( adapter_method )
|
|
65
67
|
@db = self.method( adapter_method ).call
|
|
66
68
|
error( 005, self, adapter_method ) if @db == nil
|
data/lib/rubyrest/engine.rb
CHANGED
|
@@ -14,14 +14,18 @@ module RubyRest
|
|
|
14
14
|
# engine configuration
|
|
15
15
|
attr_reader :config
|
|
16
16
|
|
|
17
|
-
# Creates a new RubyRest engine, for the
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
# Creates a new RubyRest engine, for the specified
|
|
18
|
+
# hash of configuration data. The specified hash of params
|
|
19
|
+
# will be 'interned' so that config keys are accessible
|
|
20
|
+
# as symbols instead of strings.
|
|
21
|
+
def initialize( original )
|
|
22
|
+
params = {}
|
|
23
|
+
original.each{ |k,v| params[ k.intern ] = v }
|
|
24
|
+
@service = params[ :service ]
|
|
25
|
+
@prefix = params[ :prefix ]
|
|
22
26
|
service_module = to_module_name( @prefix, @service )
|
|
23
27
|
require @service
|
|
24
|
-
@config = to_class( service_module, "config" ).new
|
|
28
|
+
@config = to_class( service_module, "config" ).new( params )
|
|
25
29
|
@config[ :servicemodule ] = service_module
|
|
26
30
|
end
|
|
27
31
|
|
|
@@ -31,7 +35,7 @@ module RubyRest
|
|
|
31
35
|
# 2. launch the webserver, in daemon mode unless false is
|
|
32
36
|
# specified in the configuration
|
|
33
37
|
def start
|
|
34
|
-
configure_database if @config.has( :
|
|
38
|
+
configure_database if @config.has( :adapter )
|
|
35
39
|
@server = RubyRest::Server.new( @config )
|
|
36
40
|
@server.mount "/", CRUDServlet
|
|
37
41
|
if @config.has( :daemon ) and @config[ :daemon ] == false
|
|
@@ -51,7 +55,7 @@ module RubyRest
|
|
|
51
55
|
def configure_database
|
|
52
56
|
@config.connect_to_database
|
|
53
57
|
@config.setup_persistence
|
|
54
|
-
if @config[ :
|
|
58
|
+
if @config[ :destroy ] == true
|
|
55
59
|
@config.init_schema
|
|
56
60
|
@config.load_initial_data
|
|
57
61
|
end
|
data/lib/rubyrest/servlets.rb
CHANGED
|
@@ -38,7 +38,11 @@ module RubyRest
|
|
|
38
38
|
@model = params[1]
|
|
39
39
|
@id = params[2]
|
|
40
40
|
@property = params[3]
|
|
41
|
-
|
|
41
|
+
begin
|
|
42
|
+
@body = REXML::Document.new( request.body ) if request.body != nil
|
|
43
|
+
rescue => e
|
|
44
|
+
puts "unable to parse request body: #{request.body}"
|
|
45
|
+
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def resolve_custom_method
|
metadata
CHANGED
|
@@ -3,8 +3,8 @@ rubygems_version: 0.9.1
|
|
|
3
3
|
specification_version: 1
|
|
4
4
|
name: rubyrest
|
|
5
5
|
version: !ruby/object:Gem::Version
|
|
6
|
-
version: 0.0.
|
|
7
|
-
date: 2007-04-
|
|
6
|
+
version: 0.0.4
|
|
7
|
+
date: 2007-04-15 00:00:00 +02:00
|
|
8
8
|
summary: REST framework for Ruby.
|
|
9
9
|
require_paths:
|
|
10
10
|
- lib
|