www_app 1.3.0 → 2.0.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.
- 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
|