rubyrest 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -5
- data/README +18 -23
- data/Rakefile +4 -4
- data/lib/rubyrest/application.rb +221 -0
- data/lib/rubyrest/atom.rb +247 -223
- data/lib/rubyrest/client.rb +19 -29
- data/lib/rubyrest/engine.rb +34 -83
- data/lib/rubyrest/resource.rb +87 -0
- data/lib/rubyrest/webrick.rb +92 -0
- data/lib/rubyrest.rb +10 -15
- metadata +5 -9
- data/examples/hello.rb +0 -57
- data/lib/rubyrest/config.rb +0 -80
- data/lib/rubyrest/servlets.rb +0 -233
- data/lib/rubyrest/tools.rb +0 -72
data/lib/rubyrest/engine.rb
CHANGED
@@ -1,49 +1,45 @@
|
|
1
|
-
# RubyRest: $Id:$
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
1
|
module RubyRest
|
7
2
|
|
8
|
-
#
|
9
|
-
#
|
3
|
+
# The Ruby-on-Rest Engine is the glue between
|
4
|
+
# the custom application and the webserver implementation
|
10
5
|
class Engine
|
11
|
-
include Tools
|
12
6
|
|
13
|
-
#
|
14
|
-
|
15
|
-
attr_reader :config
|
7
|
+
# The application
|
8
|
+
attr_reader :app
|
16
9
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
params
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@config = to_class( service_module, "config" ).new( params )
|
29
|
-
@config[ :servicemodule ] = service_module
|
10
|
+
# Initializes the Ruby-on-Rest Engine, Application and Webserver.
|
11
|
+
# The Application must be installed as a rubygem so that
|
12
|
+
# the statement +require @service+ works.
|
13
|
+
def initialize( config )
|
14
|
+
params = intern_config( config )
|
15
|
+
require params[:service]
|
16
|
+
@app = Class.by_name( params[:module].capitalize + "::" + params[:service].capitalize + "::Application" ).new( params )
|
17
|
+
if params[:daemon ] != nil
|
18
|
+
@daemon = params[:daemon ]
|
19
|
+
else @daemon=true end
|
20
|
+
@server = Class.by_name( "RubyRest::" + params[:webserver].to_s.capitalize + "::Server" ).new( @app )
|
30
21
|
end
|
31
22
|
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
23
|
+
|
24
|
+
# Returns a new hash, with all the keys as symbols
|
25
|
+
def intern_config( hash )
|
26
|
+
interned = Hash.new
|
27
|
+
hash.each{ |k,v| interned[ k.intern ] = v }
|
28
|
+
return interned
|
29
|
+
end
|
30
|
+
|
31
|
+
# Starts the server, embedded or as a detached
|
32
|
+
# daemon
|
37
33
|
def start
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
else
|
34
|
+
if @daemon
|
35
|
+
@pid = fork do
|
36
|
+
@server.up
|
37
|
+
end
|
38
|
+
puts "#{self.to_s}: started - pid=#{@pid}, port=#{@app.config[:http_port]}"
|
39
|
+
else @server.up end
|
44
40
|
end
|
45
41
|
|
46
|
-
# Shutdowns the current server
|
42
|
+
# Shutdowns the current server.
|
47
43
|
# This is only useful when working in daemon mode.
|
48
44
|
def stop
|
49
45
|
return if @pid == nil
|
@@ -51,56 +47,11 @@ module RubyRest
|
|
51
47
|
puts "#{self.to_s}: killed process with pid=#{@pid}"
|
52
48
|
end
|
53
49
|
|
54
|
-
#
|
55
|
-
def configure_database
|
56
|
-
@config.connect_to_database
|
57
|
-
@config.setup_persistence
|
58
|
-
if @config[ :destroy ] == true
|
59
|
-
@config.init_schema
|
60
|
-
@config.load_initial_data
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Starts the server in another process
|
65
|
-
def start_daemon
|
66
|
-
@pid = fork do
|
67
|
-
start_server
|
68
|
-
end
|
69
|
-
puts "#{self.to_s}: started process with pid=#{@pid}"
|
70
|
-
end
|
71
|
-
|
72
|
-
# Starts the server
|
73
|
-
def start_server
|
74
|
-
[ "INT", "TERM" ].each { |signal|
|
75
|
-
trap( signal ) { @server.shutdown }
|
76
|
-
}
|
77
|
-
@server.start
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns the service name
|
50
|
+
# Returns the deployed application name
|
81
51
|
def to_s
|
82
|
-
"rubyrest: #{@
|
52
|
+
"rubyrest: #{@app}"
|
83
53
|
end
|
84
54
|
|
85
55
|
end
|
86
56
|
|
87
|
-
|
88
|
-
# Adds some extra functionnality to the actual server
|
89
|
-
# implementation.
|
90
|
-
#
|
91
|
-
class Server < WEBrick::HTTPServer
|
92
|
-
|
93
|
-
# the configuration options
|
94
|
-
attr_accessor :rubyrest
|
95
|
-
|
96
|
-
# Creates a new WEBrick instance with the specified
|
97
|
-
# configuration
|
98
|
-
def initialize( config )
|
99
|
-
super( :Port => config[ :serviceport ] )
|
100
|
-
@rubyrest = config
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
57
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RubyRest
|
2
|
+
|
3
|
+
# Base REST Resource class that acts as a wrapper for the actual
|
4
|
+
# business logic, data formatting (response time) and validations (request time)
|
5
|
+
class Resource
|
6
|
+
|
7
|
+
# Creates a new resource, under the given application
|
8
|
+
def initialize( app )
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
# Convenience method
|
13
|
+
def service
|
14
|
+
self.class.service
|
15
|
+
end
|
16
|
+
|
17
|
+
# Convenience method
|
18
|
+
def model
|
19
|
+
self.class.model
|
20
|
+
end
|
21
|
+
|
22
|
+
# Defines the url type the resource is going to
|
23
|
+
# handle
|
24
|
+
def self.with_mount_point path
|
25
|
+
@mount_point = path
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.mount_point
|
29
|
+
@mount_point
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.for_model model_klass
|
33
|
+
@model = model_klass
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.model
|
37
|
+
@model || Class.by_name( self.name + "Model" )
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.for_service service_klass
|
41
|
+
@service = service_klass
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.service
|
45
|
+
@service || Class.by_name( self.name + "Service" )
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.atom modifiers
|
49
|
+
self.props << modifiers
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the list of formatters
|
53
|
+
def self.props
|
54
|
+
@props = [] if !@props
|
55
|
+
return @props
|
56
|
+
end
|
57
|
+
|
58
|
+
# Declares a new link
|
59
|
+
def self.link modifiers
|
60
|
+
self.links << modifiers
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the array of links declared
|
64
|
+
# for the resource
|
65
|
+
def self.links
|
66
|
+
@links = [] if !@links
|
67
|
+
return @links
|
68
|
+
end
|
69
|
+
|
70
|
+
# Updates the specified object with the data found
|
71
|
+
# in the specified xml object, and the rules defined in
|
72
|
+
# the resource parsers
|
73
|
+
def bind( object, xml )
|
74
|
+
self.class.props.each{ |p|
|
75
|
+
@app.formatter(p).property( p ).parse( object, p, xml )
|
76
|
+
}
|
77
|
+
return object
|
78
|
+
end
|
79
|
+
|
80
|
+
# String representation of a resource
|
81
|
+
def to_s
|
82
|
+
"path #{self.class.mount_point}, service #{self.class.service}, model #{self.class.model}"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module RubyRest
|
2
|
+
|
3
|
+
module Webrick
|
4
|
+
|
5
|
+
|
6
|
+
# The Ruby-on-Rest Webrick adapter implementation
|
7
|
+
class Server < WEBrick::HTTPServer
|
8
|
+
|
9
|
+
attr_reader :app
|
10
|
+
|
11
|
+
# Builds a new Webrick adapter instance
|
12
|
+
# for the given application
|
13
|
+
def initialize( app )
|
14
|
+
super( :Port => app.config[:http_port] )
|
15
|
+
@app = app
|
16
|
+
servlet = RubyRest::Webrick::Servlet.new( self )
|
17
|
+
mount "/", servlet
|
18
|
+
end
|
19
|
+
|
20
|
+
# Starts the server
|
21
|
+
def up
|
22
|
+
[ "INT", "TERM" ].each { |signal|
|
23
|
+
trap( signal ) { shutdown }
|
24
|
+
}
|
25
|
+
start
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# A REST servlet for Webrick adapter
|
31
|
+
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
32
|
+
|
33
|
+
# Builds a new servlet, taking the application from it
|
34
|
+
def initialize( server )
|
35
|
+
@app = server.app
|
36
|
+
end
|
37
|
+
|
38
|
+
# A single servlet instance will be used
|
39
|
+
# for all requests
|
40
|
+
def get_instance( server )
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def decode_path( path )
|
45
|
+
path.gsub( "+", " ")
|
46
|
+
end
|
47
|
+
|
48
|
+
def args( request )
|
49
|
+
args = Hash.new
|
50
|
+
url_tokens = decode_path( request.path ).split( "/")
|
51
|
+
args[:target] = url_tokens[2] if url_tokens.length>2
|
52
|
+
args[:property] = url_tokens[3] if url_tokens.length>3
|
53
|
+
args[:path] = request.path
|
54
|
+
args[:body] = RubyRest::Atom::Document.new( request.body ) if request.body
|
55
|
+
return args
|
56
|
+
end
|
57
|
+
|
58
|
+
def do_GET( request, response )
|
59
|
+
params = args(request )
|
60
|
+
response.status = 200
|
61
|
+
xml = @app.retrieve( params )
|
62
|
+
response["content-type"]=params[:content_type]
|
63
|
+
xml.write( response.body )
|
64
|
+
end
|
65
|
+
|
66
|
+
def do_POST( request, response )
|
67
|
+
params = args(request )
|
68
|
+
response.status = 201
|
69
|
+
xml = @app.create( params )
|
70
|
+
response["content-type"]=params[:content_type]
|
71
|
+
xml.write( response.body )
|
72
|
+
end
|
73
|
+
|
74
|
+
def do_PUT( request, response )
|
75
|
+
params = args(request )
|
76
|
+
response.status = 200
|
77
|
+
xml = @app.update( params )
|
78
|
+
response["content-type"]=params[:content_type]
|
79
|
+
xml.write( response.body )
|
80
|
+
end
|
81
|
+
|
82
|
+
def do_DELETE( request, response )
|
83
|
+
params = args(request )
|
84
|
+
response.status = 200
|
85
|
+
@app.delete( params )
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/lib/rubyrest.rb
CHANGED
@@ -3,23 +3,18 @@
|
|
3
3
|
# Entry point to the framework.
|
4
4
|
# Loads all the files under the 'rubyrest' subdirectory
|
5
5
|
require "rubygems"
|
6
|
+
require "sequel"
|
7
|
+
require "sequel/postgres"
|
6
8
|
require "extensions/all"
|
7
|
-
require "builder"
|
8
9
|
require "rexml/document"
|
9
10
|
require "webrick"
|
11
|
+
require "find"
|
10
12
|
|
11
|
-
|
12
|
-
dir = File.join(
|
13
|
-
|
14
|
-
require File.join( dir, "
|
15
|
-
require File.join( dir, "
|
16
|
-
require File.join( dir, "servlets" )
|
17
|
-
require File.join( dir, "config" )
|
13
|
+
here = File.dirname( __FILE__ )
|
14
|
+
dir = File.join( here , 'rubyrest' )
|
15
|
+
require File.join( dir, "webrick" )
|
16
|
+
require File.join( dir, "resource" )
|
17
|
+
require File.join( dir, "application" )
|
18
18
|
require File.join( dir, "engine" )
|
19
|
-
require File.join( dir, "
|
20
|
-
|
21
|
-
module RubyRest #:nodoc:
|
22
|
-
class << self
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
19
|
+
require File.join( dir, "atom" )
|
20
|
+
require File.join( dir, "client" )
|
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.1.0
|
7
|
+
date: 2007-06-04 00:00:00 +02:00
|
8
8
|
summary: REST framework for Ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -37,13 +37,12 @@ files:
|
|
37
37
|
- doc/rdoc
|
38
38
|
- lib/rubyrest
|
39
39
|
- lib/rubyrest.rb
|
40
|
+
- lib/rubyrest/application.rb
|
40
41
|
- lib/rubyrest/atom.rb
|
41
42
|
- lib/rubyrest/client.rb
|
42
|
-
- lib/rubyrest/config.rb
|
43
43
|
- lib/rubyrest/engine.rb
|
44
|
-
- lib/rubyrest/
|
45
|
-
- lib/rubyrest/
|
46
|
-
- examples/hello.rb
|
44
|
+
- lib/rubyrest/resource.rb
|
45
|
+
- lib/rubyrest/webrick.rb
|
47
46
|
test_files: []
|
48
47
|
|
49
48
|
rdoc_options:
|
@@ -58,13 +57,10 @@ rdoc_options:
|
|
58
57
|
- --inline-source
|
59
58
|
- --exclude
|
60
59
|
- lib/rubyrest.rb
|
61
|
-
- --include
|
62
|
-
- examples/*.rb
|
63
60
|
extra_rdoc_files:
|
64
61
|
- README
|
65
62
|
- CHANGELOG
|
66
63
|
- COPYING
|
67
|
-
- examples/hello.rb
|
68
64
|
executables:
|
69
65
|
- rubyrest
|
70
66
|
extensions: []
|
data/examples/hello.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# Ruby-on-Rest HelloWorld application
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
module Acme
|
6
|
-
|
7
|
-
module Hello
|
8
|
-
|
9
|
-
# The configuration for the Acme::Hello service.
|
10
|
-
# This is a simple service that does not need database
|
11
|
-
# connectivity.
|
12
|
-
class Config < RubyRest::SimpleConfig
|
13
|
-
|
14
|
-
end
|
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
|
-
# The domain service that actually implements
|
27
|
-
# 'business' logic. In this case, it's just going to say "Hello!"
|
28
|
-
# by returning a HelloBean object
|
29
|
-
class Welcomeservice
|
30
|
-
|
31
|
-
# Returns a simple object that says hello
|
32
|
-
# This is the method invoked by RubyRest::CRUDServlet
|
33
|
-
# when it receives a GET request with no particular resource id
|
34
|
-
def self.rest_retrieve( principal )
|
35
|
-
HelloBean.new
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
# The object that encapsulates the so sophisticated
|
41
|
-
# message. By including RubyRest::Atom::Entry, we get some convenience
|
42
|
-
# methods that help to provide an atom entry representation
|
43
|
-
# of the bean, without too much effort.
|
44
|
-
class HelloBean
|
45
|
-
include RubyRest::Atom::DummyEntry
|
46
|
-
|
47
|
-
# The hello message, included as a 'message'
|
48
|
-
# node inside an Atom Entry content.
|
49
|
-
def atom_content( builder )
|
50
|
-
builder.message "Hello!"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
|
data/lib/rubyrest/config.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
# RubyRest: $Id:$
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
module RubyRest
|
6
|
-
|
7
|
-
# This class is meant to be subclassed with customized
|
8
|
-
# behaviour that will be invoked by the engine at startup.
|
9
|
-
# Subclasses can provide connectivity to popular database
|
10
|
-
# frameworks such as ActiveRecord, Sequel or Og.
|
11
|
-
#
|
12
|
-
# Example:
|
13
|
-
#
|
14
|
-
# module MyService
|
15
|
-
# class Config < RubyRest::SimpleConfig
|
16
|
-
#
|
17
|
-
# end
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
#
|
21
|
-
class SimpleConfig
|
22
|
-
include RubyRest::Tools
|
23
|
-
|
24
|
-
# Initializes a new configuration with the specified
|
25
|
-
# hash of data
|
26
|
-
def initialize( hash )
|
27
|
-
@hash = hash
|
28
|
-
end
|
29
|
-
|
30
|
-
# Overrides the specified configuration option
|
31
|
-
# with its new value
|
32
|
-
def []=( name, value )
|
33
|
-
@hash[ name ] = value
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns a given configuration option value
|
37
|
-
def [](name)
|
38
|
-
raise error( 000, name ) if !has( name )
|
39
|
-
return @hash[ name ]
|
40
|
-
end
|
41
|
-
|
42
|
-
# Returns true if the specified name matches
|
43
|
-
# a valid configuration option
|
44
|
-
def has( name )
|
45
|
-
@hash[ name ] != nil
|
46
|
-
end
|
47
|
-
|
48
|
-
# Dumps all the configuration options
|
49
|
-
def to_s
|
50
|
-
@hash.each{ |k,v| puts "key: #{k}, value: #{v}" }
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
# Specialization of SimpleConfig that provides
|
56
|
-
# hooks for database initialization during startup
|
57
|
-
class DatabaseConfig < SimpleConfig
|
58
|
-
|
59
|
-
# Connects and returns a database instance.
|
60
|
-
#
|
61
|
-
# This method
|
62
|
-
# will delegate to a more specialized method, according to the
|
63
|
-
# :adapter config option
|
64
|
-
def connect_to_database
|
65
|
-
adapter_method = "#{self[:adapter]}_connect"
|
66
|
-
error( 004, self, adapter_method ) if !self.respond_to?( adapter_method )
|
67
|
-
@db = self.method( adapter_method ).call
|
68
|
-
error( 005, self, adapter_method ) if @db == nil
|
69
|
-
end
|
70
|
-
|
71
|
-
# This method must be implemented in subclasses
|
72
|
-
# Intentionnaly left empty
|
73
|
-
def init_database
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
end
|