alf-rest 0.14.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.md +5 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +71 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +12 -0
- data/README.md +11 -0
- data/Rakefile +11 -0
- data/lib/alf-rest.rb +1 -0
- data/lib/alf/rest.rb +30 -0
- data/lib/alf/rest/alf-ext/renderer.rb +16 -0
- data/lib/alf/rest/alf-ext/unit_of_work.rb +3 -0
- data/lib/alf/rest/alf-ext/unit_of_work/delete.rb +21 -0
- data/lib/alf/rest/alf-ext/unit_of_work/insert.rb +22 -0
- data/lib/alf/rest/alf-ext/unit_of_work/update.rb +22 -0
- data/lib/alf/rest/config.rb +55 -0
- data/lib/alf/rest/errors.rb +5 -0
- data/lib/alf/rest/helpers.rb +68 -0
- data/lib/alf/rest/loader.rb +5 -0
- data/lib/alf/rest/middleware.rb +19 -0
- data/lib/alf/rest/payload.rb +12 -0
- data/lib/alf/rest/payload/client.rb +22 -0
- data/lib/alf/rest/request.rb +43 -0
- data/lib/alf/rest/response.rb +21 -0
- data/lib/alf/rest/test.rb +16 -0
- data/lib/alf/rest/test/client.rb +83 -0
- data/lib/alf/rest/test/ext.rb +7 -0
- data/lib/alf/rest/test/steps.rb +286 -0
- data/lib/alf/rest/version.rb +16 -0
- data/lib/sinatra/alf-rest.rb +73 -0
- data/spec/fixtures/sap.db +0 -0
- data/spec/integration/sinatra/rest_get/test_accept.rb +98 -0
- data/spec/integration/spec_helper.rb +27 -0
- data/spec/test_rest.rb +10 -0
- data/spec/unit/config/test_database.rb +28 -0
- data/spec/unit/config/test_viewpoint.rb +18 -0
- data/spec/unit/ext/renderer/test_from_http_accept.rb +50 -0
- data/spec/unit/ext/renderer/test_supported_media_types.rb +10 -0
- data/spec/unit/middleware/test_behavior.rb +55 -0
- data/spec/unit/request/test_to_relation.rb +56 -0
- data/spec/unit/spec_helper.rb +35 -0
- data/spec/unit/test_rest.rb +10 -0
- data/tasks/gem.rake +8 -0
- data/tasks/test.rake +17 -0
- metadata +251 -0
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
group :runtime do
|
4
|
+
gem "alf-core", "~> 0.14.0"
|
5
|
+
gem "sinatra", "~> 1.3", ">= 1.3.2"
|
6
|
+
gem "rack-accept", "~> 0.4.5"
|
7
|
+
end
|
8
|
+
|
9
|
+
group :development do
|
10
|
+
gem "rake", "~> 10.0"
|
11
|
+
gem "rspec", "~> 2.12"
|
12
|
+
end
|
13
|
+
|
14
|
+
group :test do
|
15
|
+
gem "cucumber", "~> 1.2"
|
16
|
+
#gem "rack-test", "~> 0.6.1"
|
17
|
+
gem "rack-test", :git => "git://github.com/brynary/rack-test.git"
|
18
|
+
gem "alf-sequel", "~> 0.14.0"
|
19
|
+
gem "sqlite3", "~> 1.3", :platforms => ['mri', 'rbx']
|
20
|
+
gem "jdbc-sqlite3", "~> 3.7", :platforms => ['jruby']
|
21
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/brynary/rack-test.git
|
3
|
+
revision: 280ff54f50d25dd70e2ec1c55049e5ef7de126f3
|
4
|
+
specs:
|
5
|
+
rack-test (0.6.2)
|
6
|
+
rack (>= 1.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
alf-core (0.14.0)
|
12
|
+
domain (~> 1.0)
|
13
|
+
myrrha (~> 3.0)
|
14
|
+
path (~> 1.3)
|
15
|
+
sexpr (~> 0.5.1)
|
16
|
+
alf-sequel (0.14.0)
|
17
|
+
alf-core (~> 0.14.0)
|
18
|
+
sequel (~> 3.48)
|
19
|
+
builder (3.2.2)
|
20
|
+
cucumber (1.3.5)
|
21
|
+
builder (>= 2.1.2)
|
22
|
+
diff-lcs (>= 1.1.3)
|
23
|
+
gherkin (~> 2.12.0)
|
24
|
+
multi_json (~> 1.7.5)
|
25
|
+
multi_test (>= 0.0.2)
|
26
|
+
diff-lcs (1.2.4)
|
27
|
+
domain (1.0.0)
|
28
|
+
gherkin (2.12.0)
|
29
|
+
multi_json (~> 1.3)
|
30
|
+
multi_json (1.7.7)
|
31
|
+
multi_test (0.0.2)
|
32
|
+
myrrha (3.0.0)
|
33
|
+
domain (~> 1.0)
|
34
|
+
path (1.3.3)
|
35
|
+
rack (1.5.2)
|
36
|
+
rack-accept (0.4.5)
|
37
|
+
rack (>= 0.4)
|
38
|
+
rack-protection (1.5.0)
|
39
|
+
rack
|
40
|
+
rake (10.1.0)
|
41
|
+
rspec (2.14.1)
|
42
|
+
rspec-core (~> 2.14.0)
|
43
|
+
rspec-expectations (~> 2.14.0)
|
44
|
+
rspec-mocks (~> 2.14.0)
|
45
|
+
rspec-core (2.14.4)
|
46
|
+
rspec-expectations (2.14.0)
|
47
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
48
|
+
rspec-mocks (2.14.1)
|
49
|
+
sequel (3.48.0)
|
50
|
+
sexpr (0.5.1)
|
51
|
+
sinatra (1.4.3)
|
52
|
+
rack (~> 1.4)
|
53
|
+
rack-protection (~> 1.4)
|
54
|
+
tilt (~> 1.3, >= 1.3.4)
|
55
|
+
sqlite3 (1.3.7)
|
56
|
+
tilt (1.4.1)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
alf-core (~> 0.14.0)
|
63
|
+
alf-sequel (~> 0.14.0)
|
64
|
+
cucumber (~> 1.2)
|
65
|
+
jdbc-sqlite3 (~> 3.7)
|
66
|
+
rack-accept (~> 0.4.5)
|
67
|
+
rack-test!
|
68
|
+
rake (~> 10.0)
|
69
|
+
rspec (~> 2.12)
|
70
|
+
sinatra (~> 1.3, >= 1.3.2)
|
71
|
+
sqlite3 (~> 1.3)
|
data/LICENCE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# The MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2012 - Bernard Lambeau
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Alf::Rest
|
2
|
+
|
3
|
+
[](http://travis-ci.org/alf-tool/alf-rest)
|
4
|
+
[](https://gemnasium.com/alf-tool/alf-rest)
|
5
|
+
|
6
|
+
Put your alf relational database on the web quickly, simply, safely.
|
7
|
+
|
8
|
+
## Links
|
9
|
+
|
10
|
+
http://github.com/alf-tool/alf
|
11
|
+
http://github.com/alf-tool/alf-rest
|
data/Rakefile
ADDED
data/lib/alf-rest.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "alf/rest"
|
data/lib/alf/rest.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'rest/version'
|
2
|
+
require_relative 'rest/loader'
|
3
|
+
require_relative 'rest/errors'
|
4
|
+
require_relative 'rest/alf-ext/renderer'
|
5
|
+
require_relative 'rest/alf-ext/unit_of_work'
|
6
|
+
module Alf
|
7
|
+
module Rest
|
8
|
+
|
9
|
+
# there are circular dependencies due to config default values :-(
|
10
|
+
class Agent; end
|
11
|
+
class ErrorApp < Sinatra::Base; end
|
12
|
+
|
13
|
+
RACK_CONFIG_KEY = 'alf-rest-config'
|
14
|
+
|
15
|
+
RACK_ERROR_KEY = 'alf-rest-error'
|
16
|
+
|
17
|
+
def self.new(app, config = Config.new)
|
18
|
+
yield(config) if block_given?
|
19
|
+
Middleware.new(app, config)
|
20
|
+
end
|
21
|
+
|
22
|
+
end # module Rest
|
23
|
+
end # module Alf
|
24
|
+
require_relative 'rest/payload'
|
25
|
+
require_relative 'rest/request'
|
26
|
+
require_relative 'rest/response'
|
27
|
+
require_relative 'rest/helpers'
|
28
|
+
|
29
|
+
require_relative 'rest/config'
|
30
|
+
require_relative 'rest/middleware'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Alf
|
2
|
+
class Renderer
|
3
|
+
|
4
|
+
def self.supported_media_types
|
5
|
+
each.map{|(_,_,r)| r.mime_type}.compact.sort
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.from_http_accept(accept)
|
9
|
+
media_type = Rack::Accept::MediaType.new(accept)
|
10
|
+
if best = media_type.best_of(supported_media_types)
|
11
|
+
each.find{|(name,_,r)| r.mime_type == best }.last
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end # class Renderer
|
16
|
+
end # module Alf
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
module UnitOfWork
|
4
|
+
class Delete
|
5
|
+
|
6
|
+
def rack_status
|
7
|
+
200
|
8
|
+
end
|
9
|
+
|
10
|
+
def rack_body
|
11
|
+
{status: "success", message: "deleted"}
|
12
|
+
end
|
13
|
+
|
14
|
+
def rack_location(request)
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Delete
|
19
|
+
end # module UnitOfWork
|
20
|
+
end # module Sequel
|
21
|
+
end # module Alf
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
module UnitOfWork
|
4
|
+
class Insert
|
5
|
+
|
6
|
+
def rack_status
|
7
|
+
201
|
8
|
+
end
|
9
|
+
|
10
|
+
def rack_body
|
11
|
+
{status: "success", message: "created"}
|
12
|
+
end
|
13
|
+
|
14
|
+
def rack_location(request)
|
15
|
+
ids = matching_relation.tuple_extract.to_hash.values
|
16
|
+
"#{request.path}/#{ids.join(',')}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class Insert
|
20
|
+
end # module UnitOfWork
|
21
|
+
end # module Sequel
|
22
|
+
end # module Alf
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
module UnitOfWork
|
4
|
+
class Update
|
5
|
+
|
6
|
+
def rack_status
|
7
|
+
200
|
8
|
+
end
|
9
|
+
|
10
|
+
def rack_body
|
11
|
+
{status: "success", message: "updated"}
|
12
|
+
end
|
13
|
+
|
14
|
+
def rack_location(request)
|
15
|
+
ids = pk_matching_relation.tuple_extract.to_hash.values
|
16
|
+
"#{request.path}/#{ids.join(',')}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class Insert
|
20
|
+
end # module UnitOfWork
|
21
|
+
end # module Sequel
|
22
|
+
end # module Alf
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Alf
|
2
|
+
module Rest
|
3
|
+
class Config < Support::Config
|
4
|
+
|
5
|
+
# The database instance to use for obtaining connections
|
6
|
+
option :database, Database, nil
|
7
|
+
|
8
|
+
# The connection options to use
|
9
|
+
option :connection_options, Hash, {}
|
10
|
+
|
11
|
+
# Enclose all requests in a single database transaction
|
12
|
+
option :transactional, Boolean, true
|
13
|
+
|
14
|
+
# The logger instance to use for logging
|
15
|
+
option :logger, Object, Logger.new(STDOUT)
|
16
|
+
|
17
|
+
# The current database connection
|
18
|
+
attr_reader :connection
|
19
|
+
|
20
|
+
# Yields the block with the database connection
|
21
|
+
def connect(&bl)
|
22
|
+
return yield unless database
|
23
|
+
database.connect(connection_options) do |conn|
|
24
|
+
@connection = conn
|
25
|
+
if transactional?
|
26
|
+
conn.in_transaction{ yield(conn) }
|
27
|
+
else
|
28
|
+
yield(conn)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Reconnect
|
34
|
+
def reconnect(opts)
|
35
|
+
connection.reconnect(opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the database, coercing it if required
|
39
|
+
def database=(db)
|
40
|
+
@database = db.is_a?(Database) ? db : Alf.database(db)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the default viewpoint to use
|
44
|
+
def viewpoint
|
45
|
+
connection_options[:viewpoint]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sets the default viewpoint on connection options
|
49
|
+
def viewpoint=(vp)
|
50
|
+
connection_options[:viewpoint] = vp
|
51
|
+
end
|
52
|
+
|
53
|
+
end # class Config
|
54
|
+
end # module Rest
|
55
|
+
end # module Alf
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Alf
|
2
|
+
module Rest
|
3
|
+
module Helpers
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def alf_config
|
7
|
+
env[Rest::RACK_CONFIG_KEY]
|
8
|
+
end
|
9
|
+
|
10
|
+
def db_conn
|
11
|
+
alf_config.connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_db_conn
|
15
|
+
yield(db_conn)
|
16
|
+
end
|
17
|
+
|
18
|
+
def_delegators :db_conn, :relvar,
|
19
|
+
:query,
|
20
|
+
:tuple_extract
|
21
|
+
|
22
|
+
def to_location(url, ids)
|
23
|
+
ids = ids.matching_relation if ids.respond_to?(:matching_relation)
|
24
|
+
ids = ids.tuple_extract if ids.respond_to?(:tuple_extract)
|
25
|
+
ids = ids.to_hash.values
|
26
|
+
"#{url}/#{ids.join(',')}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def location_set?
|
30
|
+
response.headers["Location"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def assert!(msg='an assertion failed', status=nil, &bl)
|
34
|
+
db_conn.assert!(msg, &bl)
|
35
|
+
rescue FactAssertionError => ex
|
36
|
+
ex.http_error_status = status
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
|
40
|
+
def deny!(msg='an assertion failed', status=nil, &bl)
|
41
|
+
db_conn.deny!(msg, &bl)
|
42
|
+
rescue FactAssertionError => ex
|
43
|
+
ex.http_error_status = status
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
|
47
|
+
def fact!(msg='an assertion failed', status=nil, &bl)
|
48
|
+
db_conn.fact!(msg, &bl)
|
49
|
+
rescue FactAssertionError => ex
|
50
|
+
ex.http_error_status = status
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
def no_duplicate!(&bl)
|
55
|
+
found = relvar(&bl)
|
56
|
+
unless found.empty?
|
57
|
+
ids = found.project(found.keys.first.to_attr_list)
|
58
|
+
halt Alf::Rest::Response.new(env){|r|
|
59
|
+
r.status = 200
|
60
|
+
r.body = {'status' => 'success', 'message' => 'skipped'}
|
61
|
+
r["Location"] = to_location(request.path, ids)
|
62
|
+
}.finish
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Alf
|
2
|
+
module Rest
|
3
|
+
class Middleware
|
4
|
+
|
5
|
+
def initialize(app, config)
|
6
|
+
@app = app
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
env[Rest::RACK_CONFIG_KEY] = cfg = @config.dup
|
12
|
+
cfg.connect do
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class Middleware
|
18
|
+
end # module Rest
|
19
|
+
end # module Alf
|