qm 1.1.11 → 1.1.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e4b2cd330c69ee8a8d56b14249fb06e9d1b3d2a
4
- data.tar.gz: 00c4657ea4b0dfbc005c6f01203f68392b111b9c
3
+ metadata.gz: c404fd6af1628ffecc1b74c37bb6ea79510a0838
4
+ data.tar.gz: a5ce742f60e5b7234af3019321d8472a1babdeed
5
5
  SHA512:
6
- metadata.gz: 715e30c6223fd2daf8d63f23b973c1f5741692266b0bd17e51fe7e644a9e026d48ab47f7c21690b94a9b2e586e1930c3246d0af72ba9cf464c26e6fb116ffcbf
7
- data.tar.gz: ccc3852764ffdfdd44b7945470fe4237c90127f4d4052eb35a361d30718a3274f7a6a89c2340e1c4f3ba39bf25a9fc32d6e7b171eaf17af695e0ff856d02c388
6
+ metadata.gz: e808c306e2e806c964c5425eeb1925ad9795ab4afaa52b0d54c4528c62353daf506c9637b5f329d9559fc0e6b6aab322ece6b0d2901dc6105c4b0c39ebbe88c2
7
+ data.tar.gz: 1ad66076c0d3b6b516fb71ae9ab15918562add2176eb941d29cd3a981801e900995aac6299b201698d9000efb844b3da84101a5a7f63c456e5e9447178c4fad4
data/README.md CHANGED
@@ -1,15 +1,17 @@
1
1
  # qm
2
2
 
3
3
  The `qm` gem for [Ruby](http://www.ruby-lang.org/) implements both the API
4
- server and web server components of [QMachine](https://www.qmachine.org) (QM),
5
- but currently it is incomplete, especially with regard to configuration.
4
+ server and web server components of [QMachine](https://www.qmachine.org) (QM).
5
+ The gem currently only supports [MongoDB](http://www.mongodb.org/) for
6
+ persistent storage, although the repository contains working definitions for
7
+ [SQLite](https://www.sqlite.org/).
6
8
 
7
9
  Install
8
10
  -------
9
11
 
10
12
  To install the latest release, run
11
13
 
12
- gem install qm
14
+ $ gem install qm
13
15
 
14
16
  ===
15
17
 
data/lib/defs-mongo.rb ADDED
@@ -0,0 +1,84 @@
1
+ #- Ruby source code
2
+
3
+ #- defs-mongo.rb ~~
4
+ # ~~ (c) SRW, 16 Jul 2014
5
+ # ~~ last updated 18 Jul 2014
6
+
7
+ require 'bson'
8
+ require 'json'
9
+ require 'mongo'
10
+ require 'sinatra/base'
11
+ require 'uri'
12
+
13
+ module Sinatra
14
+
15
+ module MongoConnect
16
+
17
+ def mongo_connect()
18
+ # This function needs documentation.
19
+ db = URI.parse(settings.persistent_storage[:mongo])
20
+ db_name = db.path.gsub(/^\//, '')
21
+ conn = Mongo::Connection.new(db.host, db.port).db(db_name)
22
+ unless db.user.nil? or db.password.nil?
23
+ conn.authenticate(db.user, db.password)
24
+ end
25
+ set db: conn
26
+ settings.db['avars'].ensure_index('box_status', {
27
+ background: true,
28
+ sparse: true
29
+ })
30
+ settings.db['avars'].ensure_index('exp_date', {
31
+ expireAfterSeconds: settings.avar_ttl
32
+ })
33
+ return
34
+ end
35
+
36
+ end
37
+
38
+ module MongoDefs
39
+
40
+ def get_avar(params)
41
+ # This helper function needs documentation.
42
+ bk, db = "#{params[0]}&#{params[1]}", settings.db
43
+ x = db['avars'].find_one({_id: bk})
44
+ y = (x.nil?) ? '{}' : x['body']
45
+ return y
46
+ end
47
+
48
+ def get_list(params)
49
+ # This helper function needs documentation.
50
+ bs, db, x = "#{params[0]}&#{params[1]}", settings.db, []
51
+ db['avars'].find({box_status: bs}).each do |doc|
52
+ # This block needs documentation.
53
+ x.push(doc['key'])
54
+ end
55
+ y = (x.length == 0) ? '[]' : x.to_json
56
+ return y
57
+ end
58
+
59
+ def set_avar(params)
60
+ # This helper function needs documentation.
61
+ db = settings.db
62
+ doc = {
63
+ _id: "#{params[0]}&#{params[1]}",
64
+ body: params[2],
65
+ exp_date: Time.now,
66
+ key: params[1]
67
+ }
68
+ options = {upsert: true}#, w: 1}
69
+ if (params.length == 4) then
70
+ doc['body'] = params[3]
71
+ doc['box_status'] = "#{params[0]}&#{params[2]}"
72
+ end
73
+ db['avars'].update({_id: doc[:_id]}, doc, options)
74
+ return
75
+ end
76
+
77
+ end
78
+
79
+ helpers MongoDefs
80
+ register MongoConnect
81
+
82
+ end
83
+
84
+ #- vim:set syntax=ruby:
data/lib/qm.rb CHANGED
@@ -2,34 +2,49 @@
2
2
 
3
3
  #- qm.rb ~~
4
4
  # ~~ (c) SRW, 12 Apr 2013
5
- # ~~ last updated 13 Jul 2014
5
+ # ~~ last updated 18 Jul 2014
6
6
 
7
7
  module QM
8
8
 
9
- private
10
-
11
- class QM_Client
12
-
13
- def initialize
14
- # This function needs documentation.
15
- # ...
16
- end
17
-
18
- end
19
-
20
- public
21
-
22
- def self::launch_client()
9
+ def self::launch_client(options = {})
23
10
  # This function needs documentation.
24
- puts '(placeholder: `launch_client`)';
25
- return;
11
+ puts '(placeholder: `launch_client`)'
12
+ return
26
13
  end
27
14
 
28
- def self::launch_service(*obj)
29
- # This function needs documentation.
30
- require 'api-server.rb'
31
- #puts '(placeholder: `launch_service`)';
32
- return;
15
+ def self::launch_service(options = {})
16
+ # This function creates, configures, and launches a fresh Sinatra app
17
+ # that inherits from the original "teaching version".
18
+ require 'service.rb'
19
+ require 'defs-mongo'
20
+ #require 'defs-sqlite'
21
+ app = Sinatra.new(QMachineService) do
22
+ register Sinatra::MongoConnect
23
+ #register Sinatra::SQLiteConnect
24
+ configure do
25
+ convert = lambda do |x|
26
+ # This converts all keys in a hash to symbols recursively.
27
+ if (x.is_a?(Hash)) then
28
+ x = x.inject({}) do |memo, (k, v)|
29
+ memo[k.to_sym] = convert.call(v)
30
+ memo
31
+ end
32
+ end
33
+ return x
34
+ end
35
+ options = convert.call(options)
36
+ set options
37
+ set bind: :hostname, run: false, static: :enable_web_server
38
+ if (settings.persistent_storage.has_key?(:mongo)) then
39
+ helpers Sinatra::MongoDefs
40
+ mongo_connect
41
+ #elsif (settings.persistent_storage.has_key?(:sqlite)) then
42
+ # helpers Sinatra::SQLiteDefs
43
+ # sqlite_connect
44
+ end
45
+ end
46
+ end
47
+ return app.run!
33
48
  end
34
49
 
35
50
  end
data/lib/service.rb ADDED
@@ -0,0 +1,133 @@
1
+ #- Ruby source code
2
+
3
+ #- service.rb ~~
4
+ #
5
+ # This file is derived from the original "teaching version" of QMachine,
6
+ # which used Sinatra and SQLite in a self-contained way. Where that version
7
+ # sought to abbreviate the original Node.js codebase as succinctly as
8
+ # possible, this version attempts to provide a more similar interface and
9
+ # level of configurability. Performance will *never* be a priority in the
10
+ # Ruby port.
11
+ #
12
+ # NOTE: Using a "%" character incorrectly in a URL will cause you great
13
+ # anguish, and there isn't a good way for me to handle this problem "softly"
14
+ # because it is the expected behavior (http://git.io/bmKr2w). Thus, you will
15
+ # tend to see "Bad Request" on your screen if you insist on using "%" as part
16
+ # of a 'box', 'key', or 'status' value.
17
+ #
18
+ # ~~ (c) SRW, 24 Apr 2013
19
+ # ~~ last updated 17 Jul 2014
20
+
21
+ require 'sinatra'
22
+ require 'sinatra/cross_origin'
23
+
24
+ class QMachineService < Sinatra::Base
25
+
26
+ register Sinatra::CrossOrigin
27
+
28
+ configure do
29
+
30
+ # QMachine options
31
+
32
+ set avar_ttl: 86400, # seconds
33
+ enable_api_server: false,
34
+ enable_CORS: false,
35
+ enable_web_server: false,
36
+ hostname: '0.0.0.0',
37
+ persistent_storage: {},
38
+ port: 8177,
39
+ public_folder: 'public'
40
+
41
+ # Sinatra mappings and options needed by QMachine.
42
+
43
+ mime_type webapp: 'application/x-web-app-manifest+json'
44
+ set bind: :hostname, run: false, static: :enable_web_server
45
+
46
+ # See also: http://www.sinatrarb.com/configuration.html
47
+
48
+ end
49
+
50
+ error do
51
+ # This "route" handles errors that occur as part of the server-side code.
52
+ hang_up
53
+ end
54
+
55
+ helpers do
56
+ # This block defines "subfunctions" for use inside the route definitions.
57
+ # The most important ones are the three functions for interacting with
58
+ # persistent storage: `get_avar`, `get_list`, and `set_avar`. Those three
59
+ # functions are not defined here -- they are defined separately in modules
60
+ # that are loaded at runtime by `QM::launch_service`.
61
+
62
+ def hang_up
63
+ # This helper method "hangs up" on a request by sending a nondescript
64
+ # 444 response back to the client, a convention taken from nginx.
65
+ halt [444, {'Content-Type' => 'text/plain'}, ['']]
66
+ end
67
+
68
+ end
69
+
70
+ not_found do
71
+ # This "route" handles requests that didn't match.
72
+ hang_up
73
+ end
74
+
75
+ # Route definitions
76
+
77
+ before '/*/*' do |version, box|
78
+ # When any request matches the pattern given, this block will execute
79
+ # before the route that corresponds to its HTTP method. The code here
80
+ # will validate the request's parameters and store them as instance
81
+ # variables that will be available to the corresponding route's code.
82
+ @box, @key, @status = box, params[:key], params[:status]
83
+ hang_up unless (settings.enable_api_server?) and
84
+ ((version == 'box') or (version == 'v1')) and
85
+ (@box.match(/^[\w\-]+$/)) and
86
+ ((@key.is_a?(String) and @key.match(/^[A-Za-z0-9]+$/)) or
87
+ (@status.is_a?(String) and @status.match(/^[A-Za-z0-9]+$/)))
88
+ cross_origin if settings.enable_CORS?
89
+ end
90
+
91
+ get '/:version/:box' do
92
+ # This route responds to API calls that "read" from persistent storage,
93
+ # such as when checking for new tasks to run or downloading results.
94
+ hang_up unless (@key.is_a?(String) ^ @status.is_a?(String))
95
+ if @key.is_a?(String) then
96
+ # This arm runs when a client requests the value of a specific avar.
97
+ y = get_avar([@box, @key])
98
+ else
99
+ # This arm runs when a client requests a task queue.
100
+ y = get_list([@box, @status])
101
+ end
102
+ return [200, {'Content-Type' => 'application/json'}, [y]]
103
+ end
104
+
105
+ post '/:version/:box' do
106
+ # This route responds to API calls that "write" to persistent storage,
107
+ # such as when uploading results or submitting new tasks.
108
+ hang_up unless @key.is_a?(String) and not @status.is_a?(String)
109
+ body = request.body.read
110
+ x = JSON.parse(body)
111
+ hang_up unless (@box == x['box']) and (@key == x['key'])
112
+ if x['status'].is_a?(String) then
113
+ # This arm runs only when a client writes an avar which represents a
114
+ # task description.
115
+ hang_up unless x['status'].match(/^[A-Za-z0-9]+$/)
116
+ set_avar([@box, @key, x['status'], body])
117
+ else
118
+ # This arm runs when a client is writing a "regular avar".
119
+ set_avar([@box, @key, body])
120
+ end
121
+ return [201, {'Content-Type' => 'text/plain'}, ['']]
122
+ end
123
+
124
+ get '/' do
125
+ # This route enables a static index page to be served from the public
126
+ # folder, if and only if QM's web server has been enabled.
127
+ hang_up unless settings.enable_web_server?
128
+ send_file(File.join(settings.public_folder, 'index.html'))
129
+ end
130
+
131
+ end
132
+
133
+ #- vim:set syntax=ruby:
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.11
4
+ version: 1.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Wilkinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-13 00:00:00.000000000 Z
11
+ date: 2014-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bson
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.10.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.10.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bson_ext
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.10.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.10.2
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: json
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -24,6 +52,20 @@ dependencies:
24
52
  - - '='
25
53
  - !ruby/object:Gem::Version
26
54
  version: 1.8.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: mongo
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.10.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.10.2
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: sinatra
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +81,19 @@ dependencies:
39
81
  - !ruby/object:Gem::Version
40
82
  version: 1.4.5
41
83
  - !ruby/object:Gem::Dependency
42
- name: sqlite3
84
+ name: sinatra-cross_origin
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
87
  - - '='
46
88
  - !ruby/object:Gem::Version
47
- version: 1.3.9
89
+ version: 0.3.2
48
90
  type: :runtime
49
91
  prerelease: false
50
92
  version_requirements: !ruby/object:Gem::Requirement
51
93
  requirements:
52
94
  - - '='
53
95
  - !ruby/object:Gem::Version
54
- version: 1.3.9
96
+ version: 0.3.2
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: thin
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +108,7 @@ dependencies:
66
108
  - - '='
67
109
  - !ruby/object:Gem::Version
68
110
  version: 1.6.2
69
- description: This is an incomplete port of the QMachine web service.
111
+ description: This is a port of the QMachine web service.
70
112
  email: sean@mathbiol.org
71
113
  executables: []
72
114
  extensions: []
@@ -76,11 +118,12 @@ extra_rdoc_files:
76
118
  files:
77
119
  - LICENSE
78
120
  - README.md
79
- - lib/api-server.rb
121
+ - lib/defs-mongo.rb
80
122
  - lib/qm.rb
123
+ - lib/service.rb
81
124
  homepage: https://www.qmachine.org
82
125
  licenses:
83
- - Apache 2.0
126
+ - Apache-2.0
84
127
  metadata:
85
128
  issue_tracker: https://github.com/qmachine/qm-ruby/issues
86
129
  post_install_message:
@@ -99,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
142
  version: '0'
100
143
  requirements: []
101
144
  rubyforge_project:
102
- rubygems_version: 2.3.0
145
+ rubygems_version: 2.4.1
103
146
  signing_key:
104
147
  specification_version: 4
105
148
  summary: A platform for World Wide Computing
data/lib/api-server.rb DELETED
@@ -1,198 +0,0 @@
1
- #- Ruby source code / Rack configuration file
2
-
3
- #- api-server.rb ~~
4
- #
5
- # This file is a hacked-up version of the "teaching version" of QMachine,
6
- # and the rest of this introduction reflects that.
7
- #
8
- # This is a self-contained Rack app that uses Sinatra's domain-specific
9
- # language (DSL) in tandem with SQLite to implement a teaching version of
10
- # QMachine. The idea here is to pack most of the functionality of the
11
- # original Node.js codebase into a single file that reads like pseudo-code.
12
- #
13
- # Of course, there are some caveats. This version succeeds in abbreviating
14
- # the original codebase, but it doesn't support all of the original options
15
- # yet. The code can also be hard to modify if you're unfamiliar with Sinatra,
16
- # because Ruby's scoping rules are very different from JavaScript's, and
17
- # Sinatra's DSL makes things even "worse", to be honest. My advice here is,
18
- # don't think too hard about it. Just enjoy it.
19
- #
20
- # I do plan to merge this program with the Ruby gem in the future, which is
21
- # why the database schema matches the Node.js implementation's (which is not
22
- # as straight-forward as it could be). For now, it serves its purpose, and it
23
- # does so with just 95 lines of source code ;-)
24
- #
25
- # NOTE: Using a "%" character incorrectly in a URL will cause you great
26
- # anguish, and there isn't a good way for me to handle this problem "softly"
27
- # because it is the expected behavior (http://git.io/bmKr2w). Thus, you will
28
- # tend to see "Bad Request" on your screen if you insist on using "%" as part
29
- # of a 'box', 'key', or 'status' value.
30
- #
31
- # ~~ (c) SRW, 24 Apr 2013
32
- # ~~ last updated 13 Jul 2014
33
-
34
- require 'json'
35
- require 'sinatra'
36
- #require 'sinatra/cross_origin'
37
- require 'sqlite3'
38
-
39
- class QMachineService < Sinatra::Base
40
-
41
- configure do
42
-
43
- # QMachine options
44
-
45
- set avar_ttl: 86400, # seconds
46
- enable_api_server: true,
47
- enable_CORS: true,
48
- enable_web_server: true,
49
- hostname: '0.0.0.0',
50
- persistent_storage: 'qm.db',
51
- port: ENV['PORT'] || 8177,
52
- public_folder: 'public'
53
-
54
- # Sinatra mappings and options needed by QMachine -- leave these alone ;-)
55
-
56
- mime_type webapp: 'application/x-web-app-manifest+json'
57
- set bind: :hostname, run: false, static: :enable_web_server
58
-
59
- # See also: http://www.sinatrarb.com/configuration.html
60
-
61
- end
62
-
63
- error do
64
- # This "route" handles errors that occur as part of the server-side code.
65
- hang_up
66
- end
67
-
68
- helpers do
69
- # This block defines subfunctions for use inside the route definitions.
70
-
71
- def sqlite(query)
72
- # This helper method helps DRY out the code for database queries, and
73
- # it does so in an incredibly robust and inefficient way -- by
74
- # creating the table and evicting expired rows before every single
75
- # query. A caveat, of course, is that the special ":memory:" database
76
- # doesn't work correctly, but ":memory:" isn't *persistent* storage
77
- # anyway. Also, I have omitted indexing on `box_status` for obvious
78
- # reasons :-P
79
- begin
80
- db = SQLite3::Database.open(settings.persistent_storage)
81
- db.execute_batch <<-sql
82
- CREATE TABLE IF NOT EXISTS avars (
83
- body TEXT NOT NULL,
84
- box_key TEXT NOT NULL PRIMARY KEY,
85
- box_status TEXT,
86
- exp_date INTEGER NOT NULL,
87
- key TEXT
88
- );
89
- DELETE FROM avars WHERE (exp_date < #{now_plus(0)})
90
- sql
91
- # We have to execute the query code `query` separately because
92
- # the `db.execute_batch` function always returns `nil`, which
93
- # prevents us from being able to retrieve the results of the
94
- # query.
95
- x = db.execute(query)
96
- rescue SQLite3::Exception => err
97
- puts "Exception occurred: #{err}"
98
- ensure
99
- db.close if db
100
- end
101
- return x
102
- end
103
-
104
- def hang_up
105
- # This helper method "hangs up" on a request by sending a nondescript
106
- # 444 response back to the client, a convention taken from nginx.
107
- halt [444, {'Content-Type' => 'text/plain'}, ['']]
108
- end
109
-
110
- def now_plus(dt)
111
- # This helper method computes a date (in milliseconds) that is
112
- # specified by an offset `dt` (in seconds).
113
- return (1000 * (Time.now.to_f + dt)).to_i
114
- end
115
-
116
- end
117
-
118
- not_found do
119
- # This "route" handles requests that didn't match.
120
- hang_up
121
- end
122
-
123
- if settings.enable_api_server? then
124
-
125
- # Here, we set up "routes" to handle incoming GET and POST requests.
126
-
127
- before '/*/*' do |version, box|
128
- # When any request matches the pattern given, this block will execute
129
- # before the route that corresponds to its HTTP method. The code here
130
- # will validate the request's parameters and store them as instance
131
- # variables that will be available to the corresponding route's code.
132
- @box, @key, @status = box, params[:key], params[:status]
133
- hang_up unless ((version == 'box') or (version == 'v1')) and
134
- (@box.match(/^[\w\-]+$/)) and
135
- ((@key.is_a?(String) and @key.match(/^[A-Za-z0-9]+$/)) or
136
- (@status.is_a?(String) and @status.match(/^[A-Za-z0-9]+$/)))
137
- #cross_origin if settings.enable_CORS?
138
- end
139
-
140
- get '/:version/:box' do
141
- # This route responds to API calls that "read" from persistent
142
- # storage, such as when checking for new tasks to run or downloading
143
- # results.
144
- hang_up unless (@key.is_a?(String) ^ @status.is_a?(String))
145
- bk, bs = "#{@box}&#{@key}", "#{@box}&#{@status}"
146
- if @key.is_a?(String) then
147
- # This arm runs when a client requests the value of a specific
148
- # avar.
149
- x = sqlite("SELECT body FROM avars WHERE box_key = '#{bk}'")
150
- y = (x.length == 0) ? '{}' : x[0][0]
151
- else
152
- # This arm runs when a client requests a task queue.
153
- x = sqlite("SELECT key FROM avars WHERE box_status = '#{bs}'")
154
- y = (x.length == 0) ? '[]' : (x.map {|row| row[0]}).to_json
155
- end
156
- return [200, {'Content-Type' => 'application/json'}, [y]]
157
- end
158
-
159
- post '/:version/:box' do
160
- # This route responds to API calls that "write" to persistent
161
- # storage, such as when uploading results or submitting new tasks.
162
- hang_up unless @key.is_a?(String) and not @status.is_a?(String)
163
- body, ed = request.body.read, now_plus(settings.avar_ttl)
164
- x = JSON.parse(body)
165
- hang_up unless (@box == x['box']) and (@key == x['key'])
166
- bk, bs = "#{@box}&#{@key}", "#{@box}&#{x['status']}"
167
- if x['status'].is_a?(String) then
168
- # This arm runs only when a client writes an avar which
169
- # represents a task description.
170
- hang_up unless x['status'].match(/^[A-Za-z0-9]+$/)
171
- sqlite("INSERT OR REPLACE INTO avars
172
- (body, box_key, box_status, exp_date, key)
173
- VALUES ('#{body}', '#{bk}', '#{bs}', #{ed}, '#{@key}')")
174
- else
175
- # This arm runs when a client is writing a "regular avar".
176
- sqlite("INSERT OR REPLACE INTO avars (body, box_key, exp_date)
177
- VALUES ('#{body}', '#{bk}', #{ed})")
178
- end
179
- return [201, {'Content-Type' => 'text/plain'}, ['']]
180
- end
181
-
182
- end
183
-
184
- if settings.enable_web_server? then
185
-
186
- get '/' do
187
- # This route enables a static index page to be served from the public
188
- # folder, if and only if QM's web server has been enabled.
189
- send_file(File.join(settings.public_folder, 'index.html'))
190
- end
191
-
192
- end
193
-
194
- end
195
-
196
- QMachineService.run!
197
-
198
- #- vim:set syntax=ruby: