alf-rest 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/Gemfile +21 -0
  3. data/Gemfile.lock +71 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +12 -0
  6. data/README.md +11 -0
  7. data/Rakefile +11 -0
  8. data/lib/alf-rest.rb +1 -0
  9. data/lib/alf/rest.rb +30 -0
  10. data/lib/alf/rest/alf-ext/renderer.rb +16 -0
  11. data/lib/alf/rest/alf-ext/unit_of_work.rb +3 -0
  12. data/lib/alf/rest/alf-ext/unit_of_work/delete.rb +21 -0
  13. data/lib/alf/rest/alf-ext/unit_of_work/insert.rb +22 -0
  14. data/lib/alf/rest/alf-ext/unit_of_work/update.rb +22 -0
  15. data/lib/alf/rest/config.rb +55 -0
  16. data/lib/alf/rest/errors.rb +5 -0
  17. data/lib/alf/rest/helpers.rb +68 -0
  18. data/lib/alf/rest/loader.rb +5 -0
  19. data/lib/alf/rest/middleware.rb +19 -0
  20. data/lib/alf/rest/payload.rb +12 -0
  21. data/lib/alf/rest/payload/client.rb +22 -0
  22. data/lib/alf/rest/request.rb +43 -0
  23. data/lib/alf/rest/response.rb +21 -0
  24. data/lib/alf/rest/test.rb +16 -0
  25. data/lib/alf/rest/test/client.rb +83 -0
  26. data/lib/alf/rest/test/ext.rb +7 -0
  27. data/lib/alf/rest/test/steps.rb +286 -0
  28. data/lib/alf/rest/version.rb +16 -0
  29. data/lib/sinatra/alf-rest.rb +73 -0
  30. data/spec/fixtures/sap.db +0 -0
  31. data/spec/integration/sinatra/rest_get/test_accept.rb +98 -0
  32. data/spec/integration/spec_helper.rb +27 -0
  33. data/spec/test_rest.rb +10 -0
  34. data/spec/unit/config/test_database.rb +28 -0
  35. data/spec/unit/config/test_viewpoint.rb +18 -0
  36. data/spec/unit/ext/renderer/test_from_http_accept.rb +50 -0
  37. data/spec/unit/ext/renderer/test_supported_media_types.rb +10 -0
  38. data/spec/unit/middleware/test_behavior.rb +55 -0
  39. data/spec/unit/request/test_to_relation.rb +56 -0
  40. data/spec/unit/spec_helper.rb +35 -0
  41. data/spec/unit/test_rest.rb +10 -0
  42. data/tasks/gem.rake +8 -0
  43. data/tasks/test.rake +17 -0
  44. metadata +251 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # 0.14.0 / 2013-10-01
2
+
3
+ * Enhancements
4
+
5
+ * Birthday!
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
@@ -0,0 +1,12 @@
1
+ rest.gemspec
2
+ rest.noespec
3
+ CHANGELOG.md
4
+ Gemfile
5
+ Gemfile.lock
6
+ lib/**/*
7
+ LICENCE.md
8
+ Manifest.txt
9
+ Rakefile
10
+ README.md
11
+ spec/**/*
12
+ tasks/**/*
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Alf::Rest
2
+
3
+ [![Build Status](https://secure.travis-ci.org/alf-tool/alf-rest.png)](http://travis-ci.org/alf-tool/alf-rest)
4
+ [![Dependency Status](https://gemnasium.com/alf-tool/alf-rest.png)](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
@@ -0,0 +1,11 @@
1
+ # We run tests by default
2
+ task :default => :test
3
+
4
+ #
5
+ # Install all tasks found in tasks folder
6
+ #
7
+ # See .rake files there for complete documentation.
8
+ #
9
+ Dir["tasks/*.rake"].each do |taskfile|
10
+ load taskfile
11
+ end
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,3 @@
1
+ require_relative 'unit_of_work/insert'
2
+ require_relative 'unit_of_work/update'
3
+ require_relative 'unit_of_work/delete'
@@ -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,5 @@
1
+ module Alf
2
+ class FactAssertionError
3
+ attr_accessor :http_error_status
4
+ end
5
+ end
@@ -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,5 @@
1
+ require 'json'
2
+ require 'logger'
3
+ require "alf-core"
4
+ require "sinatra"
5
+ require "rack/accept"
@@ -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