www_app 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +36 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +84 -0
- data/TODO.md +13 -0
- data/VERSION +1 -0
- data/bin/www_app +46 -0
- data/doc/Design.md +123 -0
- data/doc/Why_this_arch.rb +104 -0
- data/lib/public/jquery-2.1.1.js +4 -0
- data/lib/public/jquery.serialize-object.min.js +8 -0
- data/lib/public/underscore-1.7.0.js +6 -0
- data/lib/public/underscore-min.map +1 -0
- data/lib/public/underscore.string-2.3.0.js +1 -0
- data/lib/public/www_app.js +824 -0
- data/lib/www_app/Clean.rb +169 -0
- data/lib/www_app/dsl.rb +86 -0
- data/lib/www_app/source.rb +53 -0
- data/lib/www_app.rb +1024 -0
- data/specs/as_ruby/0000-new.rb +23 -0
- data/specs/as_ruby/0010-attrs.rb +29 -0
- data/specs/as_ruby/0011-class.rb +39 -0
- data/specs/as_ruby/0011-href.rb +37 -0
- data/specs/as_ruby/0011-id.rb +39 -0
- data/specs/as_ruby/0020-tag.rb +21 -0
- data/specs/as_ruby/0020-tag_content.rb +43 -0
- data/specs/as_ruby/0021-body.rb +16 -0
- data/specs/as_ruby/0021-form.rb +22 -0
- data/specs/as_ruby/0021-link.rb +26 -0
- data/specs/as_ruby/0021-script.rb +44 -0
- data/specs/as_ruby/0030-mustache.rb +113 -0
- data/specs/as_ruby/0040-css.rb +174 -0
- data/specs/as_ruby/0050-on.rb +64 -0
- data/specs/client-side/index.html +90 -0
- data/specs/client-side/index.js +777 -0
- data/specs/lib/config.ru +96 -0
- data/specs/lib/helpers.rb +230 -0
- data/specs/lib/qunit/qunit-1.15.0.css +237 -0
- data/specs/lib/qunit/qunit-1.15.0.js +2495 -0
- data/specs/lib/sample.rb +23 -0
- data/specs/sampe.2.rb +14 -0
- data/specs/sample.3.rb +17 -0
- data/specs/sample.rb +44 -0
- data/www_app.gemspec +38 -0
- metadata +271 -0
data/specs/lib/config.ru
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require 'cuba'
|
4
|
+
require 'da99_rack_protect'
|
5
|
+
require 'multi_json'
|
6
|
+
|
7
|
+
Cuba.use Da99_Rack_Protect.config { |c|
|
8
|
+
c.config :host, [:localhost, 'www_app.com']
|
9
|
+
}
|
10
|
+
|
11
|
+
Cuba.use Rack::ShowExceptions
|
12
|
+
|
13
|
+
Cuba.use(Class.new {
|
14
|
+
def initialize app
|
15
|
+
@app = app
|
16
|
+
end
|
17
|
+
|
18
|
+
def call env
|
19
|
+
results = @app.call(env)
|
20
|
+
if results.first == 404
|
21
|
+
if env['PATH_INFO'] == '/'
|
22
|
+
return [
|
23
|
+
200,
|
24
|
+
{"Content-Type" => "text/html"},
|
25
|
+
[File.read("./specs/client-side/index.html").gsub("{token}", env['rack.session']['csrf'])]
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
[
|
30
|
+
Rack::Directory.new(File.expand_path('./specs/lib')),
|
31
|
+
Rack::Directory.new(File.expand_path('./specs/client-side')),
|
32
|
+
Rack::Directory.new(File.expand_path('./lib/public'))
|
33
|
+
].detect { |r|
|
34
|
+
status, headers, body = r.call(env)
|
35
|
+
return [status, headers, body] if status != 404
|
36
|
+
false
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
results
|
41
|
+
end
|
42
|
+
})
|
43
|
+
|
44
|
+
Cuba.define do
|
45
|
+
|
46
|
+
on post do
|
47
|
+
data = (req.env["rack.request.form_hash"]).dup
|
48
|
+
data.delete('authenticity_token')
|
49
|
+
|
50
|
+
res['Content-Type'] = 'application/json';
|
51
|
+
|
52
|
+
on "repeat/error_msg" do
|
53
|
+
res.write MultiJson.dump({
|
54
|
+
'data' => data,
|
55
|
+
'clean_html' => {
|
56
|
+
'error_msg' => "<span>#{data['error_msg'] || 'Unknown error.'}</span>"
|
57
|
+
}
|
58
|
+
})
|
59
|
+
end
|
60
|
+
|
61
|
+
on "repeat/success_msg" do
|
62
|
+
res.write MultiJson.dump({
|
63
|
+
'data' => data,
|
64
|
+
'clean_html' => {
|
65
|
+
'success_msg' => "<span>#{ data['success_msg'] || 'Unknown success'}</span>"
|
66
|
+
}
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
on "repeat/vals" do
|
71
|
+
res.write MultiJson.dump({
|
72
|
+
'data' => data.select { |k,v| k['val'] },
|
73
|
+
'clean_html' => {
|
74
|
+
'success_msg' => 'Success in repeating vals.'
|
75
|
+
}
|
76
|
+
})
|
77
|
+
end
|
78
|
+
|
79
|
+
on(default) {
|
80
|
+
res.status = 404
|
81
|
+
res.write 'Missing'
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
on get do
|
86
|
+
|
87
|
+
on(default) {
|
88
|
+
res.status = 404
|
89
|
+
res.write 'Missing'
|
90
|
+
}
|
91
|
+
|
92
|
+
end # === on get
|
93
|
+
|
94
|
+
end # === Cuba.define
|
95
|
+
|
96
|
+
run Cuba
|
@@ -0,0 +1,230 @@
|
|
1
|
+
|
2
|
+
require 'Bacon_Colored'
|
3
|
+
require 'www_app'
|
4
|
+
require 'pry'
|
5
|
+
require "differ"
|
6
|
+
require "sanitize"
|
7
|
+
require "escape_escape_escape"
|
8
|
+
|
9
|
+
Differ.format = :color
|
10
|
+
|
11
|
+
TEMPLATE = File.read(__FILE__).
|
12
|
+
split('__END__').
|
13
|
+
last.
|
14
|
+
strip
|
15
|
+
|
16
|
+
def norm ugly
|
17
|
+
ugly.
|
18
|
+
strip.
|
19
|
+
split("\n").
|
20
|
+
map(&:strip).
|
21
|
+
join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def strip_each_line str
|
25
|
+
str.split("\n").map(&:strip).join "\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_content tag, html
|
29
|
+
( html.match(/\<#{tag}\>(.+)\<\/#{tag}\>/)[1] || '' ).
|
30
|
+
split("\n").
|
31
|
+
map(&:strip).
|
32
|
+
join "\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def style html
|
36
|
+
get_content 'style', html
|
37
|
+
end
|
38
|
+
|
39
|
+
def script html
|
40
|
+
get_content 'script', html
|
41
|
+
end
|
42
|
+
|
43
|
+
def body html
|
44
|
+
get_content 'body', html
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_html h
|
48
|
+
TEMPLATE.gsub(/!([a-z\_]+)/) { |sub|
|
49
|
+
key = $1.to_sym
|
50
|
+
case key
|
51
|
+
when :style
|
52
|
+
Sanitize::CSS.stylesheet(
|
53
|
+
(h[key] || '').strip,
|
54
|
+
Escape_Escape_Escape::CONFIG
|
55
|
+
)
|
56
|
+
when :body
|
57
|
+
Escape_Escape_Escape.html(h[key] || '')
|
58
|
+
when :title
|
59
|
+
Escape_Escape_Escape.html(h[key] || '[No Title]')
|
60
|
+
else
|
61
|
+
fail "Unknown key: #{key.inspect}"
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def should_equal target, &blok
|
67
|
+
a = norm(WWW_App.new(&blok).to_html)
|
68
|
+
t = norm( target )
|
69
|
+
return(a.should == t) if a == t
|
70
|
+
|
71
|
+
puts " ======== ACTUAL =========="
|
72
|
+
puts a
|
73
|
+
puts " ======== TARGET =========="
|
74
|
+
puts t
|
75
|
+
puts " =========================="
|
76
|
+
puts Differ.diff_by_word(a,t)
|
77
|
+
puts " =========================="
|
78
|
+
fail "No match"
|
79
|
+
end
|
80
|
+
|
81
|
+
module Bacon
|
82
|
+
class Context
|
83
|
+
|
84
|
+
def target *args
|
85
|
+
@target_args = args
|
86
|
+
end
|
87
|
+
|
88
|
+
def actual vals = {}, &blok
|
89
|
+
if !@target_args
|
90
|
+
return WWW_App.new(&blok).render(vals)
|
91
|
+
end
|
92
|
+
|
93
|
+
include_tag = if @target_args.first == :outer
|
94
|
+
!!@target_args.shift
|
95
|
+
end
|
96
|
+
|
97
|
+
@target_args.unshift(:body) if @target_args.size == 1
|
98
|
+
norm_target = norm @target_args.last
|
99
|
+
|
100
|
+
tag = @target_args.first
|
101
|
+
html = WWW_App.new(&blok).render(vals)
|
102
|
+
section = case
|
103
|
+
when include_tag
|
104
|
+
html[/(<#{tag}[^\>]*>.+<\/#{tag}>)/m] && $1
|
105
|
+
else
|
106
|
+
html[/<#{tag}[^\>]*>(.+)<\/#{tag}>/m] && $1
|
107
|
+
end || html
|
108
|
+
norm_actual = norm section
|
109
|
+
|
110
|
+
if norm_target != norm_actual
|
111
|
+
puts " ======== TARGET =========="
|
112
|
+
puts norm_target
|
113
|
+
puts " ======== ACTUAL =========="
|
114
|
+
puts norm_actual
|
115
|
+
puts " ====== ORIGINAL ACTUAL ==="
|
116
|
+
puts html
|
117
|
+
puts " =========================="
|
118
|
+
end
|
119
|
+
|
120
|
+
norm_actual.should == norm_target
|
121
|
+
end
|
122
|
+
|
123
|
+
end # === class Context ===
|
124
|
+
end # === module Bacon ===
|
125
|
+
|
126
|
+
class WWW_App_Test
|
127
|
+
|
128
|
+
def initialize applet, output
|
129
|
+
@app = applet
|
130
|
+
@err = nil
|
131
|
+
@test = WWW_App.new("__main_test___", output)
|
132
|
+
@test.extend Computers
|
133
|
+
@test.send :test_app, @app
|
134
|
+
end
|
135
|
+
|
136
|
+
def run
|
137
|
+
begin
|
138
|
+
@app.run
|
139
|
+
rescue Object => e
|
140
|
+
fail_expected = @test.tokens.detect { |v|
|
141
|
+
v.is_a?(String) && WWW_App.standard_key(v) == "SHOULD RAISE"
|
142
|
+
}
|
143
|
+
raise e unless fail_expected
|
144
|
+
@test.send :test_err, e
|
145
|
+
end
|
146
|
+
@test.run
|
147
|
+
end
|
148
|
+
|
149
|
+
module Computers
|
150
|
+
|
151
|
+
class << self
|
152
|
+
def aliases
|
153
|
+
@map ||= {}
|
154
|
+
end
|
155
|
+
end # === class self
|
156
|
+
|
157
|
+
private
|
158
|
+
def test_app app = :none
|
159
|
+
if app != :none
|
160
|
+
@test_app = app
|
161
|
+
end
|
162
|
+
@test_app
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_err e = :none
|
166
|
+
if e != :none
|
167
|
+
@test_err = e
|
168
|
+
end
|
169
|
+
@test_err
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
public
|
174
|
+
aliases[:value_should_equals_equals] = "value should =="
|
175
|
+
def value_should_equals_equals sender, to, args
|
176
|
+
name = sender.stack.last
|
177
|
+
target = args.last
|
178
|
+
@test_app.get(name).should == target
|
179
|
+
end
|
180
|
+
|
181
|
+
def should_raise sender, to, args
|
182
|
+
err_name = args.last
|
183
|
+
@test_err.message.should.match /#{Regexp.escape err_name}/
|
184
|
+
@test_err.message
|
185
|
+
end
|
186
|
+
|
187
|
+
def message_should_match sender, to, args
|
188
|
+
str_regex = args.last
|
189
|
+
msg = sender.stack.last
|
190
|
+
msg.should.match /#{Regexp.escape str_regex}/i
|
191
|
+
end
|
192
|
+
|
193
|
+
aliases[:stack_should_equal_equal] = "stack should =="
|
194
|
+
def stack_should_equal_equal sender, to, args
|
195
|
+
@test_app.stack.should == args
|
196
|
+
end
|
197
|
+
|
198
|
+
def should_not_raise sender, to, args
|
199
|
+
@err.should == nil
|
200
|
+
end
|
201
|
+
|
202
|
+
aliases[:last_console_message_should_equal_equal] = "last console message should =="
|
203
|
+
def last_console_message_should_equal_equal sender, to, args
|
204
|
+
@test_app.console.last.should == args.last
|
205
|
+
end
|
206
|
+
|
207
|
+
aliases[:console_should_equal_equal] = "console should =="
|
208
|
+
def console_should_equal_equal sender, to, args
|
209
|
+
@test_app.console.should == args
|
210
|
+
end
|
211
|
+
end # === module Computers
|
212
|
+
|
213
|
+
end # === class
|
214
|
+
|
215
|
+
|
216
|
+
__END__
|
217
|
+
<!DOCTYPE html>
|
218
|
+
<html lang="en">
|
219
|
+
<head>
|
220
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
221
|
+
<title>!title</title>
|
222
|
+
<style type="text/css">
|
223
|
+
!style
|
224
|
+
</style>
|
225
|
+
</head>
|
226
|
+
<body>!body</body>
|
227
|
+
</html>
|
228
|
+
|
229
|
+
|
230
|
+
|
@@ -0,0 +1,237 @@
|
|
1
|
+
/*!
|
2
|
+
* QUnit 1.15.0
|
3
|
+
* http://qunitjs.com/
|
4
|
+
*
|
5
|
+
* Copyright 2014 jQuery Foundation and other contributors
|
6
|
+
* Released under the MIT license
|
7
|
+
* http://jquery.org/license
|
8
|
+
*
|
9
|
+
* Date: 2014-08-08T16:00Z
|
10
|
+
*/
|
11
|
+
|
12
|
+
/** Font Family and Sizes */
|
13
|
+
|
14
|
+
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
|
15
|
+
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
|
16
|
+
}
|
17
|
+
|
18
|
+
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
|
19
|
+
#qunit-tests { font-size: smaller; }
|
20
|
+
|
21
|
+
|
22
|
+
/** Resets */
|
23
|
+
|
24
|
+
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
|
25
|
+
margin: 0;
|
26
|
+
padding: 0;
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
/** Header */
|
31
|
+
|
32
|
+
#qunit-header {
|
33
|
+
padding: 0.5em 0 0.5em 1em;
|
34
|
+
|
35
|
+
color: #8699A4;
|
36
|
+
background-color: #0D3349;
|
37
|
+
|
38
|
+
font-size: 1.5em;
|
39
|
+
line-height: 1em;
|
40
|
+
font-weight: 400;
|
41
|
+
|
42
|
+
border-radius: 5px 5px 0 0;
|
43
|
+
}
|
44
|
+
|
45
|
+
#qunit-header a {
|
46
|
+
text-decoration: none;
|
47
|
+
color: #C2CCD1;
|
48
|
+
}
|
49
|
+
|
50
|
+
#qunit-header a:hover,
|
51
|
+
#qunit-header a:focus {
|
52
|
+
color: #FFF;
|
53
|
+
}
|
54
|
+
|
55
|
+
#qunit-testrunner-toolbar label {
|
56
|
+
display: inline-block;
|
57
|
+
padding: 0 0.5em 0 0.1em;
|
58
|
+
}
|
59
|
+
|
60
|
+
#qunit-banner {
|
61
|
+
height: 5px;
|
62
|
+
}
|
63
|
+
|
64
|
+
#qunit-testrunner-toolbar {
|
65
|
+
padding: 0.5em 1em 0.5em 1em;
|
66
|
+
color: #5E740B;
|
67
|
+
background-color: #EEE;
|
68
|
+
overflow: hidden;
|
69
|
+
}
|
70
|
+
|
71
|
+
#qunit-userAgent {
|
72
|
+
padding: 0.5em 1em 0.5em 1em;
|
73
|
+
background-color: #2B81AF;
|
74
|
+
color: #FFF;
|
75
|
+
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
|
76
|
+
}
|
77
|
+
|
78
|
+
#qunit-modulefilter-container {
|
79
|
+
float: right;
|
80
|
+
}
|
81
|
+
|
82
|
+
/** Tests: Pass/Fail */
|
83
|
+
|
84
|
+
#qunit-tests {
|
85
|
+
list-style-position: inside;
|
86
|
+
}
|
87
|
+
|
88
|
+
#qunit-tests li {
|
89
|
+
padding: 0.4em 1em 0.4em 1em;
|
90
|
+
border-bottom: 1px solid #FFF;
|
91
|
+
list-style-position: inside;
|
92
|
+
}
|
93
|
+
|
94
|
+
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
|
95
|
+
display: none;
|
96
|
+
}
|
97
|
+
|
98
|
+
#qunit-tests li strong {
|
99
|
+
cursor: pointer;
|
100
|
+
}
|
101
|
+
|
102
|
+
#qunit-tests li a {
|
103
|
+
padding: 0.5em;
|
104
|
+
color: #C2CCD1;
|
105
|
+
text-decoration: none;
|
106
|
+
}
|
107
|
+
#qunit-tests li a:hover,
|
108
|
+
#qunit-tests li a:focus {
|
109
|
+
color: #000;
|
110
|
+
}
|
111
|
+
|
112
|
+
#qunit-tests li .runtime {
|
113
|
+
float: right;
|
114
|
+
font-size: smaller;
|
115
|
+
}
|
116
|
+
|
117
|
+
.qunit-assert-list {
|
118
|
+
margin-top: 0.5em;
|
119
|
+
padding: 0.5em;
|
120
|
+
|
121
|
+
background-color: #FFF;
|
122
|
+
|
123
|
+
border-radius: 5px;
|
124
|
+
}
|
125
|
+
|
126
|
+
.qunit-collapsed {
|
127
|
+
display: none;
|
128
|
+
}
|
129
|
+
|
130
|
+
#qunit-tests table {
|
131
|
+
border-collapse: collapse;
|
132
|
+
margin-top: 0.2em;
|
133
|
+
}
|
134
|
+
|
135
|
+
#qunit-tests th {
|
136
|
+
text-align: right;
|
137
|
+
vertical-align: top;
|
138
|
+
padding: 0 0.5em 0 0;
|
139
|
+
}
|
140
|
+
|
141
|
+
#qunit-tests td {
|
142
|
+
vertical-align: top;
|
143
|
+
}
|
144
|
+
|
145
|
+
#qunit-tests pre {
|
146
|
+
margin: 0;
|
147
|
+
white-space: pre-wrap;
|
148
|
+
word-wrap: break-word;
|
149
|
+
}
|
150
|
+
|
151
|
+
#qunit-tests del {
|
152
|
+
background-color: #E0F2BE;
|
153
|
+
color: #374E0C;
|
154
|
+
text-decoration: none;
|
155
|
+
}
|
156
|
+
|
157
|
+
#qunit-tests ins {
|
158
|
+
background-color: #FFCACA;
|
159
|
+
color: #500;
|
160
|
+
text-decoration: none;
|
161
|
+
}
|
162
|
+
|
163
|
+
/*** Test Counts */
|
164
|
+
|
165
|
+
#qunit-tests b.counts { color: #000; }
|
166
|
+
#qunit-tests b.passed { color: #5E740B; }
|
167
|
+
#qunit-tests b.failed { color: #710909; }
|
168
|
+
|
169
|
+
#qunit-tests li li {
|
170
|
+
padding: 5px;
|
171
|
+
background-color: #FFF;
|
172
|
+
border-bottom: none;
|
173
|
+
list-style-position: inside;
|
174
|
+
}
|
175
|
+
|
176
|
+
/*** Passing Styles */
|
177
|
+
|
178
|
+
#qunit-tests li li.pass {
|
179
|
+
color: #3C510C;
|
180
|
+
background-color: #FFF;
|
181
|
+
border-left: 10px solid #C6E746;
|
182
|
+
}
|
183
|
+
|
184
|
+
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
|
185
|
+
#qunit-tests .pass .test-name { color: #366097; }
|
186
|
+
|
187
|
+
#qunit-tests .pass .test-actual,
|
188
|
+
#qunit-tests .pass .test-expected { color: #999; }
|
189
|
+
|
190
|
+
#qunit-banner.qunit-pass { background-color: #C6E746; }
|
191
|
+
|
192
|
+
/*** Failing Styles */
|
193
|
+
|
194
|
+
#qunit-tests li li.fail {
|
195
|
+
color: #710909;
|
196
|
+
background-color: #FFF;
|
197
|
+
border-left: 10px solid #EE5757;
|
198
|
+
white-space: pre;
|
199
|
+
}
|
200
|
+
|
201
|
+
#qunit-tests > li:last-child {
|
202
|
+
border-radius: 0 0 5px 5px;
|
203
|
+
}
|
204
|
+
|
205
|
+
#qunit-tests .fail { color: #000; background-color: #EE5757; }
|
206
|
+
#qunit-tests .fail .test-name,
|
207
|
+
#qunit-tests .fail .module-name { color: #000; }
|
208
|
+
|
209
|
+
#qunit-tests .fail .test-actual { color: #EE5757; }
|
210
|
+
#qunit-tests .fail .test-expected { color: #008000; }
|
211
|
+
|
212
|
+
#qunit-banner.qunit-fail { background-color: #EE5757; }
|
213
|
+
|
214
|
+
|
215
|
+
/** Result */
|
216
|
+
|
217
|
+
#qunit-testresult {
|
218
|
+
padding: 0.5em 1em 0.5em 1em;
|
219
|
+
|
220
|
+
color: #2B81AF;
|
221
|
+
background-color: #D2E0E6;
|
222
|
+
|
223
|
+
border-bottom: 1px solid #FFF;
|
224
|
+
}
|
225
|
+
#qunit-testresult .module-name {
|
226
|
+
font-weight: 700;
|
227
|
+
}
|
228
|
+
|
229
|
+
/** Fixture */
|
230
|
+
|
231
|
+
#qunit-fixture {
|
232
|
+
position: absolute;
|
233
|
+
top: -10000px;
|
234
|
+
left: -10000px;
|
235
|
+
width: 1000px;
|
236
|
+
height: 1000px;
|
237
|
+
}
|