rubyrest 0.0.5 → 0.1.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/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
|