waxx 0.1.2
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/LICENSE +201 -0
- data/README.md +879 -0
- data/bin/waxx +120 -0
- data/lib/waxx/app.rb +173 -0
- data/lib/waxx/conf.rb +54 -0
- data/lib/waxx/console.rb +204 -0
- data/lib/waxx/csrf.rb +14 -0
- data/lib/waxx/database.rb +80 -0
- data/lib/waxx/encrypt.rb +38 -0
- data/lib/waxx/error.rb +60 -0
- data/lib/waxx/html.rb +33 -0
- data/lib/waxx/http.rb +268 -0
- data/lib/waxx/init.rb +273 -0
- data/lib/waxx/irb.rb +44 -0
- data/lib/waxx/irb_env.rb +18 -0
- data/lib/waxx/json.rb +23 -0
- data/lib/waxx/mongodb.rb +221 -0
- data/lib/waxx/mysql2.rb +234 -0
- data/lib/waxx/object.rb +115 -0
- data/lib/waxx/patch.rb +138 -0
- data/lib/waxx/pdf.rb +69 -0
- data/lib/waxx/pg.rb +246 -0
- data/lib/waxx/process.rb +270 -0
- data/lib/waxx/req.rb +116 -0
- data/lib/waxx/res.rb +98 -0
- data/lib/waxx/server.rb +304 -0
- data/lib/waxx/sqlite3.rb +237 -0
- data/lib/waxx/supervisor.rb +47 -0
- data/lib/waxx/test.rb +162 -0
- data/lib/waxx/util.rb +57 -0
- data/lib/waxx/version.rb +3 -0
- data/lib/waxx/view.rb +389 -0
- data/lib/waxx/waxx.rb +73 -0
- data/lib/waxx/x.rb +103 -0
- data/lib/waxx.rb +50 -0
- data/skel/README.md +11 -0
- data/skel/app/app/app.rb +39 -0
- data/skel/app/app/error/app_error.rb +16 -0
- data/skel/app/app/error/dhtml.rb +9 -0
- data/skel/app/app/error/html.rb +8 -0
- data/skel/app/app/error/json.rb +8 -0
- data/skel/app/app/error/pdf.rb +13 -0
- data/skel/app/app/log/app_log.rb +13 -0
- data/skel/app/app.rb +20 -0
- data/skel/app/home/home.rb +16 -0
- data/skel/app/home/html.rb +145 -0
- data/skel/app/html.rb +192 -0
- data/skel/app/usr/email.rb +66 -0
- data/skel/app/usr/html.rb +115 -0
- data/skel/app/usr/list.rb +51 -0
- data/skel/app/usr/password.rb +54 -0
- data/skel/app/usr/record.rb +98 -0
- data/skel/app/usr/usr.js +67 -0
- data/skel/app/usr/usr.rb +277 -0
- data/skel/app/waxx/waxx.rb +109 -0
- data/skel/bin/README.md +1 -0
- data/skel/db/README.md +11 -0
- data/skel/db/app/0-init.sql +88 -0
- data/skel/lib/README.md +1 -0
- data/skel/log/README.md +1 -0
- data/skel/opt/dev/config.yaml +1 -0
- data/skel/opt/prod/config.yaml +1 -0
- data/skel/opt/stage/config.yaml +1 -0
- data/skel/opt/test/config.yaml +1 -0
- data/skel/private/README.md +1 -0
- data/skel/public/lib/site.css +202 -0
- data/skel/public/lib/waxx/w.ico +0 -0
- data/skel/public/lib/waxx/w.png +0 -0
- data/skel/public/lib/waxx/waxx.js +111 -0
- data/skel/tmp/pids/README.md +1 -0
- data.tar.gz.sig +0 -0
- metadata +140 -0
- metadata.gz.sig +3 -0
data/lib/waxx/x.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Waxx Copyright (c) 2016-2017 ePark labs Inc. & Daniel J. Fitzpatrick <dan@eparklabs.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
module Waxx
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# The X Struct gets instanciated with each request (x)
|
|
19
|
+
# ```
|
|
20
|
+
# x.req # The request object (Instance of Waxx::Req)
|
|
21
|
+
# x.res # The response object (Instance of Waxx::Res)
|
|
22
|
+
# x.usr # The user session cookie
|
|
23
|
+
# x.usr['id'] # Get the id value.
|
|
24
|
+
# x.usr['name'] = value # Set a user session value
|
|
25
|
+
# x.ua # The User Agent / Client cookie
|
|
26
|
+
# x.ua['la'] # Get the last activity time of the user agent
|
|
27
|
+
# x.ua['name'] = value # Set a user session value
|
|
28
|
+
# x.db # The hash of database connections (0 or more)
|
|
29
|
+
# x.db.app.exec(sql, [arg1, arg2, argn]) # Run a query on the app database
|
|
30
|
+
# x.meth # The request method as a symbol: :get, :post, :put, :patch, :delete
|
|
31
|
+
# # Path parameters example.com/app/act/[*args]
|
|
32
|
+
# x.app # The (app or module) The first path parameter
|
|
33
|
+
# x.act # The act (defined in Object.runs())
|
|
34
|
+
# x.oid # The third arg as an int. Given example.com/person/record/42.json, then oid is 42
|
|
35
|
+
# x.args # An array of arguments after /app/act/. Given POST example.com/customer/bill/10/dev/350.json, then args is ['10','dev','350']
|
|
36
|
+
# # When defining a the run proc, args are splatted into the function. So given the example request above and:
|
|
37
|
+
# module Customer
|
|
38
|
+
# extend Waxx::Postgres
|
|
39
|
+
# extend self
|
|
40
|
+
# runs(
|
|
41
|
+
# bill: {
|
|
42
|
+
# desc: "Bill a customer",
|
|
43
|
+
# acl: "internal",
|
|
44
|
+
# post: -> (x, customer_id, category, amount) {
|
|
45
|
+
# # The variables here are:
|
|
46
|
+
# # customer_id = '10'
|
|
47
|
+
# # category = 'dev'
|
|
48
|
+
# # amount = '350'
|
|
49
|
+
# # Note: All passed in args are strings
|
|
50
|
+
# # but x.oid = 10 (as an int)
|
|
51
|
+
# }
|
|
52
|
+
# }
|
|
53
|
+
# )
|
|
54
|
+
# end
|
|
55
|
+
# x.ext # The extension of the request: .json, .html, .pdf, etc. Default defined in Waxx['default']['extension']
|
|
56
|
+
# # Background jobs (executed after the response is returned to the client. For example to deliver an email.)
|
|
57
|
+
# x.jobs # An array of jobs
|
|
58
|
+
# # Jobs are added as procs with optional arguments (proc, *args).
|
|
59
|
+
# x.job(->(x, id){ App::Email.deliver(x, id) }, x, id)
|
|
60
|
+
# ```
|
|
61
|
+
X = Struct.new(
|
|
62
|
+
:req,
|
|
63
|
+
:res,
|
|
64
|
+
:usr,
|
|
65
|
+
:ua,
|
|
66
|
+
:db,
|
|
67
|
+
:meth,
|
|
68
|
+
:app,
|
|
69
|
+
:act,
|
|
70
|
+
:oid,
|
|
71
|
+
:args,
|
|
72
|
+
:ext,
|
|
73
|
+
:jobs
|
|
74
|
+
) do
|
|
75
|
+
def << str
|
|
76
|
+
res << str.to_s
|
|
77
|
+
end
|
|
78
|
+
def [](k)
|
|
79
|
+
req.post[k.to_s] || req.get[k.to_s]
|
|
80
|
+
end
|
|
81
|
+
def /(k)
|
|
82
|
+
req.post[k.to_s] || req.get[k.to_s]
|
|
83
|
+
end
|
|
84
|
+
def usr?
|
|
85
|
+
not (usr['id'].nil? or usr['id'].to_i == 0)
|
|
86
|
+
end
|
|
87
|
+
def write?
|
|
88
|
+
['put', 'post', 'patch', 'delete'].include? meth
|
|
89
|
+
end
|
|
90
|
+
def group? g
|
|
91
|
+
return false unless usr?
|
|
92
|
+
usr['grp'].include? g.to_s
|
|
93
|
+
end
|
|
94
|
+
def groups?(*args)
|
|
95
|
+
args.inject(0){|total, g| total + (group?(g) ? 1 : 0)} == args.size
|
|
96
|
+
end
|
|
97
|
+
def job(j, *args)
|
|
98
|
+
jobs << [j, *args]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
data/lib/waxx.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Waxx Copyright (c) 2016-2017 ePark labs Inc. & Daniel J. Fitzpatrick <dan@eparklabs.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# Libs (core & std)
|
|
16
|
+
require 'socket'
|
|
17
|
+
require 'thread'
|
|
18
|
+
require 'openssl'
|
|
19
|
+
require 'base64'
|
|
20
|
+
require 'json'
|
|
21
|
+
require 'time'
|
|
22
|
+
require 'fileutils'
|
|
23
|
+
require 'yaml'
|
|
24
|
+
# Require ruby files in waxx/ (except irb stuff)
|
|
25
|
+
require_relative 'waxx/waxx'
|
|
26
|
+
require_relative 'waxx/version'
|
|
27
|
+
require_relative 'waxx/x'
|
|
28
|
+
require_relative 'waxx/req'
|
|
29
|
+
require_relative 'waxx/res'
|
|
30
|
+
require_relative 'waxx/app'
|
|
31
|
+
require_relative 'waxx/conf'
|
|
32
|
+
require_relative 'waxx/console'
|
|
33
|
+
require_relative 'waxx/csrf'
|
|
34
|
+
require_relative 'waxx/database'
|
|
35
|
+
require_relative 'waxx/encrypt'
|
|
36
|
+
require_relative 'waxx/error'
|
|
37
|
+
require_relative 'waxx/html'
|
|
38
|
+
require_relative 'waxx/http'
|
|
39
|
+
require_relative 'waxx/json'
|
|
40
|
+
require_relative 'waxx/object'
|
|
41
|
+
require_relative 'waxx/patch'
|
|
42
|
+
require_relative 'waxx/pdf'
|
|
43
|
+
require_relative 'waxx/process'
|
|
44
|
+
require_relative 'waxx/pg'
|
|
45
|
+
require_relative 'waxx/mysql2'
|
|
46
|
+
require_relative 'waxx/sqlite3'
|
|
47
|
+
require_relative 'waxx/server'
|
|
48
|
+
require_relative 'waxx/supervisor'
|
|
49
|
+
require_relative 'waxx/util'
|
|
50
|
+
require_relative 'waxx/view'
|
data/skel/README.md
ADDED
data/skel/app/app/app.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module App::App
|
|
2
|
+
extend Waxx::Object
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
runs(
|
|
6
|
+
default: "js",
|
|
7
|
+
js: {
|
|
8
|
+
desc: "Serve the application javascript files.",
|
|
9
|
+
get: -> (x) {
|
|
10
|
+
x.res.no_cookies = true
|
|
11
|
+
x << "app = {uid:#{x.usr['id']},cid:#{x.usr['cid']}};\n"
|
|
12
|
+
# Read each file in the list and include it
|
|
13
|
+
%w(usr/usr.js).each{|f|
|
|
14
|
+
x << File.read("#{Waxx/:opts/:base}/app/#{f}")
|
|
15
|
+
}
|
|
16
|
+
# JS for logged in users
|
|
17
|
+
if x.usr?
|
|
18
|
+
%w().each{|f|
|
|
19
|
+
x << File.read("#{Waxx/:opts/:base}/app/#{f}")
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
ok: {
|
|
25
|
+
desc: "Ping the app to see if it is ok",
|
|
26
|
+
get: -> (x) {
|
|
27
|
+
if x.db.app.exec("select 1+1 as two").first['two'] == 2
|
|
28
|
+
x << 'true'
|
|
29
|
+
else
|
|
30
|
+
x.res.status = 500
|
|
31
|
+
x << false
|
|
32
|
+
end
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
require_relative 'log/app_log'
|
|
39
|
+
require_relative 'error/app_error'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module App::AppError
|
|
2
|
+
extend Waxx::Object
|
|
3
|
+
extend self
|
|
4
|
+
runs(
|
|
5
|
+
request: {
|
|
6
|
+
desc: "Display a request error (400)",
|
|
7
|
+
get: lambda{|x, title, message=""|
|
|
8
|
+
const_get(x.ext.capitalize).get(x, title, message)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
require_relative 'dhtml'
|
|
14
|
+
require_relative 'html'
|
|
15
|
+
require_relative 'json'
|
|
16
|
+
require_relative 'pdf'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module App::AppError::Pdf
|
|
2
|
+
extend Waxx::Pdf
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def get(x, title, message)
|
|
6
|
+
x.res['Content-Type'] = Waxx::Http.content_types[:pdf]
|
|
7
|
+
pdf = new_doc
|
|
8
|
+
pdf.text "<b>#{title}</b>"
|
|
9
|
+
pdf.text "#{message}"
|
|
10
|
+
render_file(x, pdf)
|
|
11
|
+
return_file(x)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module App::AppLog
|
|
2
|
+
extend Waxx::Object
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def log(x, cat:'', name:'', value:'', id:nil)
|
|
6
|
+
x.db.exec("INSERT INTO app_log
|
|
7
|
+
(usr_id, category, name, value, related_id, ip_address)
|
|
8
|
+
VALUES ($1, $2, $3, $4, $5, $6)",
|
|
9
|
+
[x.usr['id'], cat, name, value, id, x.req.env['X-Forwarded-For']]
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
data/skel/app/app.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Uncomment these to use them or add additional gems
|
|
2
|
+
# require 'pg'
|
|
3
|
+
# require 'mysql2'
|
|
4
|
+
# require 'sqlite3'
|
|
5
|
+
# require 'mongodb'
|
|
6
|
+
# require 'mail'
|
|
7
|
+
|
|
8
|
+
module App
|
|
9
|
+
extend Waxx::App
|
|
10
|
+
extend Waxx::Util
|
|
11
|
+
extend Waxx::Encrypt
|
|
12
|
+
extend Waxx::Server
|
|
13
|
+
extend self
|
|
14
|
+
init
|
|
15
|
+
|
|
16
|
+
# App methods here that your apps share
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Require layout engines
|
|
20
|
+
require_relative 'html'
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
module App::Home::Html
|
|
2
|
+
extend Waxx::Html
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def welcome(x)
|
|
6
|
+
App::Html.render(x, title: "Welcome to Waxx", content: %(
|
|
7
|
+
#{carousel(x)}
|
|
8
|
+
<div class="container marketing">
|
|
9
|
+
#{main_points(x)}
|
|
10
|
+
#{featurettes(x)}
|
|
11
|
+
</div>
|
|
12
|
+
))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def carousel(x)
|
|
17
|
+
%(
|
|
18
|
+
<!-- Carousel
|
|
19
|
+
================================================== -->
|
|
20
|
+
<div id="myCarousel" class="carousel slide" data-ride="carousel">
|
|
21
|
+
<!-- Indicators -->
|
|
22
|
+
<ol class="carousel-indicators">
|
|
23
|
+
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
|
|
24
|
+
<li data-target="#myCarousel" data-slide-to="1"></li>
|
|
25
|
+
<li data-target="#myCarousel" data-slide-to="2"></li>
|
|
26
|
+
</ol>
|
|
27
|
+
<div class="carousel-inner" role="listbox">
|
|
28
|
+
<div class="item active">
|
|
29
|
+
<img class="first-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="First slide">
|
|
30
|
+
<div class="container">
|
|
31
|
+
<div class="carousel-caption">
|
|
32
|
+
<h1>Fast and Smooth</h1>
|
|
33
|
+
<p>Waxx makes web development fast and smooth. You can edit this page at <br><code>app/home/html.rb</code></p>
|
|
34
|
+
<p><a class="btn btn-lg btn-primary" href="#" role="button">Sign up today</a></p>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="item">
|
|
39
|
+
<img class="second-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Second slide">
|
|
40
|
+
<div class="container">
|
|
41
|
+
<div class="carousel-caption">
|
|
42
|
+
<h1>Another example headline.</h1>
|
|
43
|
+
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
|
44
|
+
<p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a></p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="item">
|
|
49
|
+
<img class="third-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Third slide">
|
|
50
|
+
<div class="container">
|
|
51
|
+
<div class="carousel-caption">
|
|
52
|
+
<h1>One more for good measure.</h1>
|
|
53
|
+
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
|
54
|
+
<p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a></p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
|
|
60
|
+
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
|
|
61
|
+
<span class="sr-only">Previous</span>
|
|
62
|
+
</a>
|
|
63
|
+
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
|
|
64
|
+
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
|
65
|
+
<span class="sr-only">Next</span>
|
|
66
|
+
</a>
|
|
67
|
+
</div><!-- /.carousel -->
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def main_points(x)
|
|
72
|
+
%(
|
|
73
|
+
<!-- Main Points
|
|
74
|
+
================================================== -->
|
|
75
|
+
|
|
76
|
+
<!-- Three columns of text below the carousel -->
|
|
77
|
+
<div class="row">
|
|
78
|
+
<div class="col-lg-4">
|
|
79
|
+
<img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
|
|
80
|
+
<h2>Heading</h2>
|
|
81
|
+
<p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
|
|
82
|
+
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
|
83
|
+
</div><!-- /.col-lg-4 -->
|
|
84
|
+
<div class="col-lg-4">
|
|
85
|
+
<img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
|
|
86
|
+
<h2>Heading</h2>
|
|
87
|
+
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
|
|
88
|
+
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
|
89
|
+
</div><!-- /.col-lg-4 -->
|
|
90
|
+
<div class="col-lg-4">
|
|
91
|
+
<img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
|
|
92
|
+
<h2>Heading</h2>
|
|
93
|
+
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
|
|
94
|
+
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
|
95
|
+
</div><!-- /.col-lg-4 -->
|
|
96
|
+
</div><!-- /.row -->
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def featurettes(x)
|
|
101
|
+
%(
|
|
102
|
+
<!-- START THE FEATURETTES -->
|
|
103
|
+
|
|
104
|
+
<hr class="featurette-divider">
|
|
105
|
+
|
|
106
|
+
<div class="row featurette">
|
|
107
|
+
<div class="col-md-7">
|
|
108
|
+
<h2 class="featurette-heading">First featurette heading. <span class="text-muted">It'll blow your mind.</span></h2>
|
|
109
|
+
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="col-md-5">
|
|
112
|
+
<img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<hr class="featurette-divider">
|
|
117
|
+
|
|
118
|
+
<div class="row featurette">
|
|
119
|
+
<div class="col-md-7 col-md-push-5">
|
|
120
|
+
<h2 class="featurette-heading">Oh yeah, it's that good. <span class="text-muted">See for yourself.</span></h2>
|
|
121
|
+
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="col-md-5 col-md-pull-7">
|
|
124
|
+
<img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<hr class="featurette-divider">
|
|
129
|
+
|
|
130
|
+
<div class="row featurette">
|
|
131
|
+
<div class="col-md-7">
|
|
132
|
+
<h2 class="featurette-heading">And lastly, this one. <span class="text-muted">Checkmate.</span></h2>
|
|
133
|
+
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="col-md-5">
|
|
136
|
+
<img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<hr class="featurette-divider">
|
|
141
|
+
|
|
142
|
+
<!-- /END THE FEATURETTES -->
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
end
|
data/skel/app/html.rb
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
module App::Html
|
|
2
|
+
extend Waxx::Html
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def standard_css
|
|
6
|
+
[
|
|
7
|
+
"/lib/bootstrap/css/bootstrap.min.css",
|
|
8
|
+
"/lib/bootstrap/css/carousel.css",
|
|
9
|
+
]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def standard_js
|
|
13
|
+
[
|
|
14
|
+
"/lib/jquery-3.1.0.min.js",
|
|
15
|
+
"/lib/bootstrap/js/bootstrap.min.js",
|
|
16
|
+
"/app/js.js",
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(x, title:"Untitled", content:nil, body_class: "", message:{type:nil, message:nil}, css:[], js:[], js_ready: nil)
|
|
21
|
+
x << head(x, title: title, css:css, body_class: body_class)
|
|
22
|
+
x << nav(x)
|
|
23
|
+
x.res.error.each{|e| x << alert(e[:type], e[:message]) }
|
|
24
|
+
x << content
|
|
25
|
+
x << app_modal(x)
|
|
26
|
+
x << foot(x)
|
|
27
|
+
x << script(x, js:js, js_ready: js_ready)
|
|
28
|
+
x << '</body></html>'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def page(x, title:nil, content:nil, message:{type:"alert", message: nil}, css:[], js:[], js_ready:nil)
|
|
32
|
+
render(x, title:title, message: message, css: css, js: js, js_ready: js_ready, content:%(
|
|
33
|
+
#{
|
|
34
|
+
#App::Website::Html.chrome
|
|
35
|
+
}
|
|
36
|
+
<div class="container">
|
|
37
|
+
<h1>#{h title}</h1>
|
|
38
|
+
#{alert(message[:type], message[:message]) if message[:message]}
|
|
39
|
+
#{content||page['content']}
|
|
40
|
+
</div>
|
|
41
|
+
))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def head(x, title:"Untitled", description:"", author:"", css:[], body_class:"")
|
|
45
|
+
%(
|
|
46
|
+
<!DOCTYPE html>
|
|
47
|
+
<!-- Copyright (c) #{Time.new.year} #{h Waxx/:site/:name} -->
|
|
48
|
+
<meta charset="utf-8">
|
|
49
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
50
|
+
<title>#{h title}</title>
|
|
51
|
+
<meta name="description" content="#{h description.to_s.gsub('"',"'")}">
|
|
52
|
+
<meta name="author" content="#{h author}">
|
|
53
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0">
|
|
54
|
+
<link rel="shortcut icon" type="image/x-icon" href="/lib/waxx/img/w.ico">
|
|
55
|
+
<link rel="apple-touch-icon" sizes="200x200" href="/lib/waxx/img/w.png">
|
|
56
|
+
#{(standard_css + css).map{|f| %(<link href="#{f}" rel="stylesheet">) }.join}
|
|
57
|
+
<body class="#{body_class}">
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def nav(x)
|
|
62
|
+
%(
|
|
63
|
+
<div class="navbar-wrapper">
|
|
64
|
+
<div class="container">
|
|
65
|
+
|
|
66
|
+
<nav class="navbar navbar-inverse navbar-static-top">
|
|
67
|
+
<div class="container">
|
|
68
|
+
<div class="navbar-header">
|
|
69
|
+
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
70
|
+
<span class="sr-only">Toggle navigation</span>
|
|
71
|
+
<span class="icon-bar"></span>
|
|
72
|
+
<span class="icon-bar"></span>
|
|
73
|
+
<span class="icon-bar"></span>
|
|
74
|
+
</button>
|
|
75
|
+
<a class="navbar-brand" href="#">#{h Waxx/:site/:name}</a>
|
|
76
|
+
</div>
|
|
77
|
+
<div id="navbar" class="navbar-collapse collapse">
|
|
78
|
+
<ul class="nav navbar-nav">
|
|
79
|
+
<li class="active"><a href="#">Home</a></li>
|
|
80
|
+
<li><a href="#about">About</a></li>
|
|
81
|
+
<li><a href="#contact">Contact</a></li>
|
|
82
|
+
<li class="dropdown">
|
|
83
|
+
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
|
|
84
|
+
<ul class="dropdown-menu">
|
|
85
|
+
<li><a href="#">Action</a></li>
|
|
86
|
+
<li><a href="#">Another action</a></li>
|
|
87
|
+
<li><a href="#">Something else here</a></li>
|
|
88
|
+
<li role="separator" class="divider"></li>
|
|
89
|
+
<li class="dropdown-header">Nav header</li>
|
|
90
|
+
<li><a href="#">Separated link</a></li>
|
|
91
|
+
<li><a href="#">One more separated link</a></li>
|
|
92
|
+
</ul>
|
|
93
|
+
</li>
|
|
94
|
+
</ul>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</nav>
|
|
98
|
+
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def usr_menu(x)
|
|
105
|
+
return "" #if not x.usr?
|
|
106
|
+
cls = %w(usr).include?(x.app) ? "active" : ""
|
|
107
|
+
cls = "active" if x.app == 'letter' and x.act == 'list'
|
|
108
|
+
%(<a class="#{cls}" href="/dashboard"><span class="glyphicon glyphicon-cog"></span></a>)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def admin(x, title:nil, content:nil, message:{type:"alert", message: nil}, css:[], js:[], js_ready:nil)
|
|
112
|
+
render(x, title:title, content_class: "container", message: message, css: css, js: js, js_ready: js_ready,
|
|
113
|
+
content: %(
|
|
114
|
+
<div style="background-color: white">
|
|
115
|
+
<div class="container">
|
|
116
|
+
#{admin_menu(x)}
|
|
117
|
+
<div id="admin-panel">#{content}</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def admin_menu(x)
|
|
125
|
+
re = [%(<!-- admin_menu -->\n<ul class="nav nav-tabs">)]
|
|
126
|
+
re << [
|
|
127
|
+
["/admin","Admin","admin"],
|
|
128
|
+
["/usr","People","admin"],
|
|
129
|
+
["/content","Content","admin"],
|
|
130
|
+
["/website_page","Webpages","admin"]
|
|
131
|
+
].map{|m|
|
|
132
|
+
next if m[2] and not x.group? m[2]
|
|
133
|
+
cls = x.req.uri =~ /^#{m[0]}/ ? "active" : ""
|
|
134
|
+
%(<li role="presentation" class="#{cls}"><a href="#{m[0]}">#{h m[1]}</a></li>)
|
|
135
|
+
}.compact
|
|
136
|
+
re << "</ul>"
|
|
137
|
+
re.join("")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Type can be: success info warning danger
|
|
141
|
+
def alert(type="info", message="")
|
|
142
|
+
return "" if message.empty?
|
|
143
|
+
signs = {success: "info", info: "info", warning: "warning", danger: "warning"}
|
|
144
|
+
%(<div class="alert alert-#{type}" role="alert"><strong>
|
|
145
|
+
<span class="glyphicon glyphicon-#{signs[type.to_sym]}-sign"></span> #{message.h}</strong></div>)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def app_modal(x)
|
|
149
|
+
%(
|
|
150
|
+
<div style="display: none;" class="modal fade in" id="apps-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
151
|
+
<div class="modal-sm modal-dialog modal-dialog-top">
|
|
152
|
+
<div class="modal-content">
|
|
153
|
+
<div class="block block-themed block-transparent">
|
|
154
|
+
<div class="block-header bg-primary-dark">
|
|
155
|
+
<ul class="block-options">
|
|
156
|
+
<li>
|
|
157
|
+
<button data-dismiss="modal" type="button"><i class="si si-close"></i></button>
|
|
158
|
+
</li>
|
|
159
|
+
</ul>
|
|
160
|
+
<h3 class="block-title"></h3>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="block-content"></div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def foot(x)
|
|
171
|
+
%(
|
|
172
|
+
<!-- FOOTER -->
|
|
173
|
+
<footer class="container">
|
|
174
|
+
<p class="pull-right"><a href="#">Back to top</a></p>
|
|
175
|
+
<p>© #{Time.new.year} #{h Waxx/:site/:name} · <a href="#">Privacy</a> · <a href="#">Terms</a></p>
|
|
176
|
+
<p><a href="https://www.waxx.io/">Delivered by Waxx in #{'%.1f' % ((Time.new - x.req.start_time) * 1000)} ms</a>.</p>
|
|
177
|
+
</footer>
|
|
178
|
+
)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def script(x, js:[], js_ready:"")
|
|
182
|
+
%(
|
|
183
|
+
#{(standard_js + js).map{|f| %(<script src="#{f}" type="text/javascript"></script>) }.join}
|
|
184
|
+
<script type="text/javascript">
|
|
185
|
+
$(document).ready(function(){
|
|
186
|
+
#{js_ready}
|
|
187
|
+
})
|
|
188
|
+
</script>
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
end
|