www_app 1.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 +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
|
+
}
|