www_app 1.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/README.md +42 -24
- data/VERSION +1 -1
- data/bin/www_app +15 -4
- data/lib/public/vendor/hogan-3.0.2.min.js +5 -0
- data/lib/public/vendor/instruct_instruct_instruct.js +247 -0
- data/lib/public/vendor/jquery-2.1.3.min.js +4 -0
- data/lib/public/vendor/lodash.min.js +89 -0
- data/lib/public/www_app.js +885 -625
- data/lib/www_app/CSS.rb +310 -0
- data/lib/www_app/HTML.rb +219 -0
- data/lib/www_app/JavaScript.rb +51 -0
- data/lib/www_app/TO.rb +897 -0
- data/lib/www_app.rb +324 -945
- data/playground/config.ru +102 -0
- data/specs/client-side/index.html +1 -1
- data/specs/client-side/index.js +31 -379
- data/specs/lib/config.ru +60 -31
- data/specs/lib/helpers.rb +24 -2
- data/specs/server-side/0000-new.rb +1 -1
- data/specs/server-side/0001-underscore-double.rb +38 -0
- data/specs/server-side/0001-underscore.rb +73 -0
- data/specs/server-side/0010-attrs.rb +3 -4
- data/specs/server-side/0011-id.rb +5 -5
- data/specs/server-side/0020-tag.rb +2 -2
- data/specs/server-side/0020-tag_content.rb +1 -1
- data/specs/server-side/0021-body.rb +1 -1
- data/specs/server-side/0021-script.rb +66 -20
- data/specs/server-side/{0021-page_title.rb → 0021-title.rb} +5 -3
- data/specs/server-side/0030-mustache.rb +27 -20
- data/specs/server-side/0030-style.rb +64 -21
- data/specs/server-side/0040-css.rb +4 -4
- data/specs/server-side/0041-pseudo.rb +55 -0
- data/specs/server-side/0042-slash.rb +20 -0
- data/specs/server-side/0060-text.rb +26 -0
- metadata +18 -13
- data/lib/public/jquery-2.1.1.js +0 -4
- data/lib/public/underscore-1.7.0.js +0 -6
- data/lib/public/underscore-min.map +0 -1
- data/lib/public/underscore.string-2.3.0.js +0 -1
- data/lib/www_app/Clean.rb +0 -169
- data/lib/www_app/dsl.rb +0 -86
- data/lib/www_app/source.rb +0 -53
- data/specs/server-side/0050-on.rb +0 -64
- data/specs/server-side/0060-string.rb +0 -32
- /data/lib/public/{jquery.serialize-object.min.js → vendor/jquery.serialize-object.min.js} +0 -0
data/specs/lib/config.ru
CHANGED
@@ -3,46 +3,75 @@
|
|
3
3
|
require 'cuba'
|
4
4
|
require 'da99_rack_protect'
|
5
5
|
require 'multi_json'
|
6
|
+
require 'www_app'
|
6
7
|
|
7
|
-
|
8
|
-
c.config :host, [:localhost, 'www_app.com']
|
9
|
-
}
|
8
|
+
PATH = File.expand_path(File.dirname(__FILE__) + '../../..')
|
10
9
|
|
11
|
-
|
10
|
+
Rack::Mime::MIME_TYPES.merge!({".map" => "application/json"})
|
12
11
|
|
13
|
-
Cuba.use
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
Cuba.use Da99_Rack_Protect do |c|
|
13
|
+
c.config :host, :localhost if ENV['IS_DEV']
|
14
|
+
end
|
15
|
+
|
16
|
+
Cuba.use Rack::ShowExceptions
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
18
|
+
# Cuba.use(Class.new {
|
19
|
+
# def initialize app
|
20
|
+
# @app = app
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def call env
|
24
|
+
# results = @app.call(env)
|
25
|
+
# if results.first == 404
|
26
|
+
# if env['PATH_INFO'] == '/old'
|
27
|
+
# return [
|
28
|
+
# 200,
|
29
|
+
# {"Content-Type" => "text/html"},
|
30
|
+
# [File.read("./specs/client-side/index.html").gsub("{token}", env['rack.session']['csrf'])]
|
31
|
+
# ]
|
32
|
+
# end
|
33
|
+
|
34
|
+
# [
|
35
|
+
# Rack::Directory.new(File.expand_path('./specs/lib')),
|
36
|
+
# Rack::Directory.new(File.expand_path('./specs/client-side')),
|
37
|
+
# Rack::Directory.new(File.expand_path('./lib/public'))
|
38
|
+
# ].detect { |r|
|
39
|
+
# status, headers, body = r.call(env)
|
40
|
+
# return [status, headers, body] if status != 404
|
41
|
+
# false
|
42
|
+
# }
|
43
|
+
# end
|
44
|
+
|
45
|
+
# results
|
46
|
+
# end
|
47
|
+
# })
|
48
|
+
|
49
|
+
|
50
|
+
PAGES = {
|
51
|
+
:root => WWW_App.new {
|
52
|
+
title { 'hello' }
|
53
|
+
div {
|
54
|
+
_.^(:happy) {
|
55
|
+
border '1px dashed red'
|
56
|
+
}
|
57
|
+
on(:click) {
|
58
|
+
add_class :happy
|
59
|
+
}
|
60
|
+
"Almost done."
|
37
61
|
}
|
38
|
-
|
62
|
+
}
|
63
|
+
}
|
39
64
|
|
40
|
-
|
41
|
-
end
|
42
|
-
})
|
65
|
+
Cuba.use Rack::Static, :urls=>["/www_app-#{File.read(PATH + '/VERSION').strip}"], :root=>'Public'
|
43
66
|
|
44
67
|
Cuba.define do
|
45
68
|
|
69
|
+
on get do
|
70
|
+
on root do
|
71
|
+
res.write PAGES[:root].to_html
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
46
75
|
on post do
|
47
76
|
data = (req.env["rack.request.form_hash"]).dup
|
48
77
|
data.delete('authenticity_token')
|
data/specs/lib/helpers.rb
CHANGED
@@ -21,6 +21,10 @@ def norm ugly
|
|
21
21
|
join("\n")
|
22
22
|
end
|
23
23
|
|
24
|
+
def norm_wo_lines str
|
25
|
+
str.split("\n").map(&:strip).join ""
|
26
|
+
end
|
27
|
+
|
24
28
|
def strip_each_line str
|
25
29
|
str.split("\n").map(&:strip).join "\n"
|
26
30
|
end
|
@@ -40,6 +44,10 @@ def script html
|
|
40
44
|
get_content 'script', html
|
41
45
|
end
|
42
46
|
|
47
|
+
def script_srcs str
|
48
|
+
str.scan(%r@<script src="([^"]+)"></script>@).flatten
|
49
|
+
end
|
50
|
+
|
43
51
|
def body html
|
44
52
|
get_content 'body', html
|
45
53
|
end
|
@@ -81,13 +89,25 @@ end
|
|
81
89
|
module Bacon
|
82
90
|
class Context
|
83
91
|
|
92
|
+
#
|
93
|
+
# NOTE: I know this is a lazy hack,
|
94
|
+
# but I was short on time.
|
95
|
+
#
|
96
|
+
alias_method :rr_wo_target_args, :run_requirement
|
97
|
+
def run_requirement *args
|
98
|
+
@target_args = nil
|
99
|
+
rr_wo_target_args(*args) {
|
100
|
+
yield if block_given?
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
84
104
|
def target *args
|
85
105
|
@target_args = args
|
86
106
|
end
|
87
107
|
|
88
108
|
def actual vals = {}, &blok
|
89
109
|
if !@target_args
|
90
|
-
return WWW_App.new(&blok).
|
110
|
+
return WWW_App.new(&blok).to_html(vals)
|
91
111
|
end
|
92
112
|
|
93
113
|
include_tag = if @target_args.first == :outer
|
@@ -98,7 +118,7 @@ module Bacon
|
|
98
118
|
norm_target = norm @target_args.last
|
99
119
|
|
100
120
|
tag = @target_args.first
|
101
|
-
html = WWW_App.new(&blok).
|
121
|
+
html = WWW_App.new(&blok).to_html(vals)
|
102
122
|
section = case
|
103
123
|
when include_tag
|
104
124
|
html[/(<#{tag}[^\>]*>.+<\/#{tag}>)/m] && $1
|
@@ -118,6 +138,8 @@ module Bacon
|
|
118
138
|
end
|
119
139
|
|
120
140
|
norm_actual.should == norm_target
|
141
|
+
|
142
|
+
@target_args = nil
|
121
143
|
end
|
122
144
|
|
123
145
|
end # === class Context ===
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
describe :underscore__double do
|
3
|
+
|
4
|
+
it "lets you combine different elements in a css selector" do
|
5
|
+
target :style, %^
|
6
|
+
div.bad div {
|
7
|
+
color: #mebad;
|
8
|
+
}
|
9
|
+
^
|
10
|
+
|
11
|
+
actual {
|
12
|
+
style {
|
13
|
+
div.^(:bad).__.div {
|
14
|
+
color '#mebad'
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end # === it lets you combine different elements in a css selector
|
19
|
+
|
20
|
+
it "can be used with :_ in :style: div.__._ { ... }" do
|
21
|
+
target :style, %^
|
22
|
+
div.outside #main.inner {
|
23
|
+
color: #deep;
|
24
|
+
}
|
25
|
+
^
|
26
|
+
|
27
|
+
actual do
|
28
|
+
div.id(:main) {
|
29
|
+
style {
|
30
|
+
div.^(:outside).__._.^(:inner) {
|
31
|
+
color '#deep'
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end # === it can be used with :_ in :style: div.__._ { ... }
|
37
|
+
|
38
|
+
end # === describe :underscore__double
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
describe :_ do
|
3
|
+
|
4
|
+
it "let's you add :id to the body" do
|
5
|
+
actual = WWW_App.new {
|
6
|
+
_.id(:the_body)
|
7
|
+
}.to_html
|
8
|
+
|
9
|
+
actual[/<body[^\<]+/].should == %!<body id="the_body">!
|
10
|
+
end # === it lets you add :id to the body
|
11
|
+
|
12
|
+
it "let's you add a :class to body" do
|
13
|
+
actual = WWW_App.new {
|
14
|
+
_.^(:sad)
|
15
|
+
}.to_html
|
16
|
+
|
17
|
+
actual[/<body[^\<]+/].should == %!<body class="sad">!
|
18
|
+
end # === it lets you add a :class to body
|
19
|
+
|
20
|
+
it "let's you create a style inside a tag, outside of :style" do
|
21
|
+
target :style, <<-EOF
|
22
|
+
#main.happy:link {
|
23
|
+
color: #happy;
|
24
|
+
}
|
25
|
+
|
26
|
+
#main.sad {
|
27
|
+
border: 1px dashed red;
|
28
|
+
}
|
29
|
+
EOF
|
30
|
+
|
31
|
+
actual do
|
32
|
+
div.id(:main) {
|
33
|
+
_.^(:happy)._link { color '#happy' }
|
34
|
+
_.^(:sad) { border '1px dashed red' }
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end # === it let's you create a style inside a tag, outside of :style
|
38
|
+
|
39
|
+
it "refers to the :body when used inside a parent-less :style" do
|
40
|
+
target :style, %!
|
41
|
+
body {
|
42
|
+
color: #abc;
|
43
|
+
}
|
44
|
+
!
|
45
|
+
|
46
|
+
actual do
|
47
|
+
style {
|
48
|
+
_ {
|
49
|
+
color '#abc'
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end # === it refers to the :body when used inside a parent-less :style
|
54
|
+
|
55
|
+
it "can be used with double-underscore: _.__div" do
|
56
|
+
target :style, %^
|
57
|
+
#main.sad div.happy {
|
58
|
+
color: #confused;
|
59
|
+
}
|
60
|
+
^
|
61
|
+
|
62
|
+
actual do
|
63
|
+
div.id(:main).^(:sad) {
|
64
|
+
style {
|
65
|
+
_.__.div.^(:happy) {
|
66
|
+
color '#confused'
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end # === it can be used with double-underscore: _.__div
|
72
|
+
|
73
|
+
end # === describe :_
|
@@ -1,15 +1,14 @@
|
|
1
1
|
|
2
2
|
describe "all attrs" do
|
3
3
|
|
4
|
-
it "raises a RuntimeError if tag has an
|
4
|
+
it "raises a RuntimeError if tag has an invalid attribute" do
|
5
5
|
should.raise(RuntimeError) {
|
6
6
|
actual {
|
7
|
-
a.href('/href') {
|
8
|
-
tag![:attrs][:hi] = 'hiya'
|
7
|
+
a.href('/href').src('file') {
|
9
8
|
"here"
|
10
9
|
}
|
11
10
|
}
|
12
|
-
}.message.should.match
|
11
|
+
}.message.should.match /:src not allowed to be set here/
|
13
12
|
end
|
14
13
|
|
15
14
|
it "escapes attributes" do
|
@@ -1,12 +1,12 @@
|
|
1
1
|
|
2
2
|
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe :id do
|
5
5
|
|
6
6
|
it "raises Invalid if id has invalid chars" do
|
7
7
|
should.raise(Escape_Escape_Escape::Invalid) {
|
8
8
|
actual do
|
9
|
-
div
|
9
|
+
div.id('a<&a') { 'hello' }
|
10
10
|
end
|
11
11
|
}.message.should.match /a<&a/
|
12
12
|
end
|
@@ -14,8 +14,8 @@ describe :* do
|
|
14
14
|
it "raises HTML_ID_Duplicate if id is used more than once" do
|
15
15
|
should.raise(WWW_App::HTML_ID_Duplicate) {
|
16
16
|
actual do
|
17
|
-
div
|
18
|
-
div
|
17
|
+
div.id(:my_id) { '1' }
|
18
|
+
div.id(:my_id) { '2' }
|
19
19
|
end
|
20
20
|
}.message.should.match /my_id/
|
21
21
|
end
|
@@ -29,7 +29,7 @@ describe :* do
|
|
29
29
|
target '<a id="warning" href="/there">There</a>'
|
30
30
|
|
31
31
|
actual do
|
32
|
-
a
|
32
|
+
a.id(:warning).href('/there') { "There" }
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -4,7 +4,7 @@ describe :tag do
|
|
4
4
|
it "raises Invalid if tag starts w/ a number" do
|
5
5
|
should.raise(Escape_Escape_Escape::Invalid) {
|
6
6
|
actual do
|
7
|
-
div
|
7
|
+
div.id('0a') { 'hello' }
|
8
8
|
end
|
9
9
|
}.message.should.match /0a/
|
10
10
|
end
|
@@ -12,7 +12,7 @@ describe :tag do
|
|
12
12
|
it "raises Invalid if tag is unknown: e.g. :footers" do
|
13
13
|
should.raise(StandardError) {
|
14
14
|
actual do
|
15
|
-
|
15
|
+
footers { 'bye' }
|
16
16
|
end
|
17
17
|
}.message.should.match /footers/
|
18
18
|
end
|
@@ -4,41 +4,87 @@ describe :script do
|
|
4
4
|
it "raises Invalid_Relative_HREF if :src is not relative" do
|
5
5
|
should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
|
6
6
|
actual do
|
7
|
-
script
|
7
|
+
script('http://www.example.org/file.js')
|
8
8
|
end
|
9
9
|
}.message.should.match /example\.org/
|
10
10
|
end
|
11
11
|
|
12
|
+
it "puts custom script files w/ :src at the after vendor files and www_app.js" do
|
13
|
+
actual = WWW_App.new {
|
14
|
+
p { 'paragraph' }
|
15
|
+
script 'my_script.js'
|
16
|
+
}.to_html
|
17
|
+
|
18
|
+
script_srcs(actual).last.should == 'my_script.js'
|
19
|
+
end # === it puts custom script files w/ :src at the after vendor files and www_app.js
|
20
|
+
|
12
21
|
it "allows a relative :src" do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
actual = WWW_App.new {
|
23
|
+
script('/file.js')
|
24
|
+
}.to_html
|
25
|
+
|
26
|
+
script_srcs(actual).last.should == %^/file.js^
|
17
27
|
end
|
18
28
|
|
19
29
|
it "escapes slashes in attr :src" do
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
}
|
24
|
-
end
|
30
|
+
actual = WWW_App.new {
|
31
|
+
script('/dir/file.js')
|
32
|
+
}.to_html
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
<script src="help"></script>
|
29
|
-
EOF
|
34
|
+
script_srcs(actual).last.should == %^/dir/file.js^
|
35
|
+
end
|
30
36
|
|
31
|
-
|
32
|
-
|
33
|
-
|
37
|
+
it "allowed to have a :class attribute" do
|
38
|
+
WWW_App.new {
|
39
|
+
script.^(:append) do
|
40
|
+
div {}
|
41
|
+
end
|
42
|
+
}.to_html.should.match /<script type="text\/mustache" class="append"/
|
34
43
|
end
|
35
44
|
|
36
|
-
it "
|
37
|
-
target %^<script
|
45
|
+
it "renders :type when given a block" do
|
46
|
+
target %^<script type="text/text">hello</script>^
|
38
47
|
actual {
|
39
|
-
script
|
48
|
+
script('text/text') { 'hello' }
|
40
49
|
}
|
41
50
|
end
|
42
51
|
|
52
|
+
it "includes client-side script files" do
|
53
|
+
actual = WWW_App.new {
|
54
|
+
div { }
|
55
|
+
script 'my.js'
|
56
|
+
}.to_html
|
57
|
+
|
58
|
+
targets = (
|
59
|
+
Dir.glob('lib/public/**/*.js').map { |f|
|
60
|
+
href = "/www_app-#{File.read('VERSION').strip}/#{f}".sub('/lib/public', '')
|
61
|
+
Escape_Escape_Escape.relative_href href
|
62
|
+
} + ['my.js']
|
63
|
+
)
|
64
|
+
|
65
|
+
script_srcs(actual).sort.should == targets.sort
|
66
|
+
end # === it includes client-side script files
|
67
|
+
|
68
|
+
it "is rendered inside a full document" do
|
69
|
+
actual = WWW_App.new {
|
70
|
+
div { }
|
71
|
+
script 'my.js'
|
72
|
+
}.to_html
|
73
|
+
|
74
|
+
actual['<body>'].should == '<body>'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "allows rendering of child elements" do
|
78
|
+
target :body, <<-EOF
|
79
|
+
<script type="text/mustache">
|
80
|
+
<div>!{ html.hello }!</div>
|
81
|
+
</script>
|
82
|
+
EOF
|
83
|
+
actual do
|
84
|
+
script 'text/mustache' do
|
85
|
+
div { :hello }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end # === it allows rendering of child elements
|
43
89
|
|
44
90
|
end # === describe :JS ===
|
@@ -2,9 +2,11 @@
|
|
2
2
|
describe "page_title" do
|
3
3
|
|
4
4
|
it "creates only one :title tag" do
|
5
|
-
WWW_App.new {
|
6
|
-
|
7
|
-
}.
|
5
|
+
html = WWW_App.new {
|
6
|
+
title { 'yo' }
|
7
|
+
}.to_html
|
8
|
+
|
9
|
+
html.scan(/<title>[^>]+<\/title>/).
|
8
10
|
should == ['<title>yo</title>']
|
9
11
|
end # === it creates on :title tag
|
10
12
|
|
@@ -56,45 +56,52 @@ describe :mustache do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "escapes html in nested values" do
|
59
|
-
|
60
|
-
|
61
|
-
<span>&& hello 2</span></div>
|
62
|
-
EOF
|
63
|
-
|
64
|
-
actual(:hello=>{:msg=>'& hello 1', :goodbye=>nil}) {
|
65
|
-
div.*(:my_box) {
|
59
|
+
actual = WWW_App.new() {
|
60
|
+
div.id(:my_box) {
|
66
61
|
render_if(:hello) {
|
67
62
|
span { :msg }
|
68
|
-
render_unless(:goodbye) {
|
63
|
+
render_unless(:goodbye) {
|
64
|
+
span { '&& hello 2' }
|
65
|
+
}
|
69
66
|
}
|
70
67
|
}
|
71
|
-
}
|
68
|
+
}.to_html(:hello=>{:msg=>'& hello 1', :goodbye=>nil})
|
69
|
+
|
70
|
+
target = <<-EOF
|
71
|
+
<div id="my_box"><span>& hello 1</span>
|
72
|
+
<span>&& hello 2</span></div>
|
73
|
+
EOF
|
74
|
+
|
75
|
+
norm_wo_lines(actual).should == norm_wo_lines(target)
|
72
76
|
end
|
73
77
|
|
74
78
|
it "escapes :href in nested values" do
|
75
|
-
|
76
|
-
actual(o: {url: '/hello', msg: 'hello'}) {
|
79
|
+
actual = WWW_App.new {
|
77
80
|
div {
|
78
81
|
render_if(:o) {
|
79
82
|
div { a.href(:url) { :msg } }
|
80
83
|
}
|
81
84
|
}
|
82
|
-
}
|
85
|
+
}.to_html(o: {url: '/hello', msg: 'hello'})
|
86
|
+
target = %^<div><div><a href="/hello">hello</a></div></div>^
|
87
|
+
norm_wo_lines(actual).should == norm_wo_lines(target)
|
83
88
|
end
|
84
89
|
|
85
90
|
it "does not allow vars to be used in form :action" do
|
86
|
-
|
91
|
+
should.raise(Escape_Escape_Escape::Invalid_Type) {
|
92
|
+
actual :url=>'not allowed', :auth_token => 'hello' do
|
93
|
+
form.action(:url) {}
|
94
|
+
end
|
95
|
+
}.message.should.match /:url/
|
87
96
|
|
88
|
-
actual :auth_token => 'hello' do
|
89
|
-
form.action(:url) {}
|
90
|
-
end
|
91
97
|
end
|
92
98
|
|
93
99
|
it "does not allow vars to be used in :link :href" do
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
100
|
+
should.raise(Escape_Escape_Escape::Invalid_Type) {
|
101
|
+
actual(:hello=>'not_allowed') {
|
102
|
+
link.href(:hello)./
|
103
|
+
}
|
104
|
+
}.message.should.match /:hello/
|
98
105
|
end
|
99
106
|
|
100
107
|
it "raises ContextMiss if encounters unescaped value" do
|