www_app 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +84 -0
  6. data/TODO.md +13 -0
  7. data/VERSION +1 -0
  8. data/bin/www_app +46 -0
  9. data/doc/Design.md +123 -0
  10. data/doc/Why_this_arch.rb +104 -0
  11. data/lib/public/jquery-2.1.1.js +4 -0
  12. data/lib/public/jquery.serialize-object.min.js +8 -0
  13. data/lib/public/underscore-1.7.0.js +6 -0
  14. data/lib/public/underscore-min.map +1 -0
  15. data/lib/public/underscore.string-2.3.0.js +1 -0
  16. data/lib/public/www_app.js +824 -0
  17. data/lib/www_app/Clean.rb +169 -0
  18. data/lib/www_app/dsl.rb +86 -0
  19. data/lib/www_app/source.rb +53 -0
  20. data/lib/www_app.rb +1024 -0
  21. data/specs/as_ruby/0000-new.rb +23 -0
  22. data/specs/as_ruby/0010-attrs.rb +29 -0
  23. data/specs/as_ruby/0011-class.rb +39 -0
  24. data/specs/as_ruby/0011-href.rb +37 -0
  25. data/specs/as_ruby/0011-id.rb +39 -0
  26. data/specs/as_ruby/0020-tag.rb +21 -0
  27. data/specs/as_ruby/0020-tag_content.rb +43 -0
  28. data/specs/as_ruby/0021-body.rb +16 -0
  29. data/specs/as_ruby/0021-form.rb +22 -0
  30. data/specs/as_ruby/0021-link.rb +26 -0
  31. data/specs/as_ruby/0021-script.rb +44 -0
  32. data/specs/as_ruby/0030-mustache.rb +113 -0
  33. data/specs/as_ruby/0040-css.rb +174 -0
  34. data/specs/as_ruby/0050-on.rb +64 -0
  35. data/specs/client-side/index.html +90 -0
  36. data/specs/client-side/index.js +777 -0
  37. data/specs/lib/config.ru +96 -0
  38. data/specs/lib/helpers.rb +230 -0
  39. data/specs/lib/qunit/qunit-1.15.0.css +237 -0
  40. data/specs/lib/qunit/qunit-1.15.0.js +2495 -0
  41. data/specs/lib/sample.rb +23 -0
  42. data/specs/sampe.2.rb +14 -0
  43. data/specs/sample.3.rb +17 -0
  44. data/specs/sample.rb +44 -0
  45. data/www_app.gemspec +38 -0
  46. metadata +271 -0
@@ -0,0 +1,23 @@
1
+
2
+
3
+ describe "IS_DEV" do
4
+
5
+ before {
6
+ @orig = ENV['IS_DEV']
7
+ }
8
+
9
+ after {
10
+ ENV['IS_DEV'] = @orig
11
+ }
12
+
13
+ it "raises RuntimeError if passed a block and non-IS_DEV" do
14
+ should.raise(RuntimeError) {
15
+ ENV['IS_DEV'] = nil
16
+ WWW_App.new {
17
+ div {}
18
+ }
19
+ }.message.should.match /non-DEV/i
20
+ end
21
+
22
+ end # === describe IS_DEV ===
23
+
@@ -0,0 +1,29 @@
1
+
2
+ describe "all attrs" do
3
+
4
+ it "raises a RuntimeError if tag has an unknown attribute" do
5
+ should.raise(RuntimeError) {
6
+ actual {
7
+ a.href('/href') {
8
+ tag![:attrs][:hi] = 'hiya'
9
+ "here"
10
+ }
11
+ }
12
+ }.message.should.match /Unknown attr: :hi/
13
+ end
14
+
15
+ it "escapes attributes" do
16
+ target %^<a rel="&lt;hello">hello</a>^
17
+ actual {
18
+ a.rel('<hello') { 'hello' }
19
+ }
20
+ end
21
+
22
+ end # === describe all attrs
23
+
24
+
25
+
26
+
27
+
28
+
29
+
@@ -0,0 +1,39 @@
1
+
2
+
3
+
4
+ describe :^ do
5
+
6
+ it "raises Invalid if class has invalid chars" do
7
+ should.raise(Escape_Escape_Escape::Invalid) {
8
+ actual {
9
+ div.^('hi)o') { 'hello' }
10
+ }
11
+ }.message.should.match /hi\)o/
12
+ end
13
+
14
+
15
+ # ==========================================================================
16
+ # =========== end sanitization specs =====================================
17
+ # ==========================================================================
18
+
19
+
20
+ it "adds 'class' attribute: a.^(:warning, :red) { }" do
21
+ target '<a class="warning red" href="&#47;here">Here</a>'
22
+
23
+ actual do
24
+ a.^(:warning, :red).href('/here') { "Here" }
25
+ end
26
+ end
27
+
28
+ it "merges classes: a.^(:super).^(:low)" do
29
+ target '<a class="super low" href="&#47;now">Now</a>'
30
+
31
+ actual do
32
+ a.^(:super).^(:low).href("/now") { "Now" }
33
+ end
34
+ end
35
+
36
+ end # === describe :^
37
+
38
+
39
+
@@ -0,0 +1,37 @@
1
+
2
+
3
+ describe :href do
4
+
5
+ it "produces 'a' elements with 'href'" do
6
+ target '<a href="&#47;here">Here</a>'
7
+
8
+ actual do
9
+ a.href('/here') { "Here" }
10
+ end
11
+ end
12
+
13
+ it "escapes chars in 'href' attributes as a url" do
14
+ target %^<a href="&#47;home&#47;?a&amp;b">home</a>^
15
+
16
+ actual do
17
+ a.href('/home/?a&b') { "home" }
18
+ end
19
+ end
20
+
21
+ it "raises Invalid_HREF for :href: javacript:" do
22
+ should.raise(Escape_Escape_Escape::Invalid_HREF) {
23
+ actual do
24
+ a.href('javascript://alert()') { 'hello' }
25
+ end
26
+ }.message.should.match /javascript/
27
+ end
28
+
29
+ it "raises Escape_Escape_Escape::Invalid_Relative_HREF if not relative using :link" do
30
+ should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
31
+ actual {
32
+ link.href('http://www.google.com/s.css')./
33
+ }
34
+ }.message.should.match /google/
35
+ end
36
+
37
+ end # === describe :href
@@ -0,0 +1,39 @@
1
+
2
+
3
+
4
+ describe :* do
5
+
6
+ it "raises Invalid if id has invalid chars" do
7
+ should.raise(Escape_Escape_Escape::Invalid) {
8
+ actual do
9
+ div.*('a<&a') { 'hello' }
10
+ end
11
+ }.message.should.match /a<&a/
12
+ end
13
+
14
+ it "raises HTML_ID_Duplicate if id is used more than once" do
15
+ should.raise(WWW_App::HTML_ID_Duplicate) {
16
+ actual do
17
+ div.*(:my_id) { '1' }
18
+ div.*(:my_id) { '2' }
19
+ end
20
+ }.message.should.match /my_id/
21
+ end
22
+
23
+ # ==========================================================================
24
+ # =========== end sanitization specs =====================================
25
+ # ==========================================================================
26
+
27
+
28
+ it "adds 'id' attribute: a.*(:warning)(...) { }" do
29
+ target '<a id="warning" href="&#47;there">There</a>'
30
+
31
+ actual do
32
+ a.*(:warning).href('/there') { "There" }
33
+ end
34
+ end
35
+
36
+ end # === describe WWW_App.new ===
37
+
38
+
39
+
@@ -0,0 +1,21 @@
1
+
2
+ describe :tag do
3
+
4
+ it "raises Invalid if tag starts w/ a number" do
5
+ should.raise(Escape_Escape_Escape::Invalid) {
6
+ actual do
7
+ div.*('0a') { 'hello' }
8
+ end
9
+ }.message.should.match /0a/
10
+ end
11
+
12
+ it "raises Invalid if tag is unknown: e.g. :footers" do
13
+ should.raise(StandardError) {
14
+ actual do
15
+ tag(:footers) { 'bye' }
16
+ end
17
+ }.message.should.match /footers/
18
+ end
19
+
20
+ end # === describe :tag ===
21
+
@@ -0,0 +1,43 @@
1
+
2
+ describe "HTML contents" do
3
+
4
+ it "uses last String value as content" do
5
+ target %^<p>Hello</p>^
6
+
7
+ actual do
8
+ p { "Hello" }
9
+ end
10
+ end
11
+
12
+ it "closes tag with :/" do
13
+ target %^<p></p>^
14
+ actual {
15
+ p./
16
+ }
17
+ end
18
+
19
+ it "does not give end-tags to void tags (self-closing tags)" do
20
+ target %^<br />\n<img />^
21
+ actual {
22
+ br./
23
+ img./
24
+ }
25
+ end
26
+
27
+ it "escapes inner text" do
28
+ target %^<p>&amp; here lies jack</p>^
29
+
30
+ actual do
31
+ p { "& here lies jack" }
32
+ end
33
+ end
34
+
35
+ it "strips out W3C unallowed Unicode chars" do
36
+ target %^<div>hello hello</div>^
37
+ actual do
38
+ div { "hello \u0340\u0341\u17a3\u17d3\u2028\u2029 hello" }
39
+ end
40
+ end
41
+
42
+ end # === describe HTML contents
43
+
@@ -0,0 +1,16 @@
1
+
2
+ describe "body with inner style" do
3
+
4
+ it "uses 'body' instead of id" do
5
+ target :style, %^
6
+ body {
7
+ border-width: 3px;
8
+ }
9
+ ^
10
+
11
+ actual {
12
+ border_width '3px'
13
+ }
14
+ end
15
+
16
+ end # === describe
@@ -0,0 +1,22 @@
1
+
2
+ describe :forms do
3
+
4
+ it "requires a CSRF token" do
5
+ should.raise(Mustache::ContextMiss) {
6
+ actual do
7
+ form.action('/home') {
8
+ }
9
+ end
10
+ }.message.should.match /auth_token/
11
+ end
12
+
13
+ it "raises Invalid_Relative_HREF if :action is not a relative url" do
14
+ should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
15
+ actual :auth_token => 'mytoken' do
16
+ form.action('http://www.google.com/') {}
17
+ end
18
+ }.message.should.match /google.com/
19
+ end
20
+
21
+ end # === describe :forms ===
22
+
@@ -0,0 +1,26 @@
1
+
2
+ describe :link do
3
+
4
+ it "raises Invalid_Relative_HREF" do
5
+ should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
6
+ actual do
7
+ link.href('http://www.google.com/')./
8
+ end
9
+ }.message.should.match /google/
10
+ end
11
+
12
+ it "escapes slashes in :href" do
13
+ target %^<link href="&#47;css&#47;css&#47;styles.css" />^
14
+ actual {
15
+ link.href('/css/css/styles.css')./
16
+ }
17
+ end
18
+
19
+ it "allows a relative :href" do
20
+ target %^<link href="&#47;css.css" />^
21
+ actual {
22
+ link.href('/css.css')./
23
+ }
24
+ end
25
+
26
+ end # === describe :link
@@ -0,0 +1,44 @@
1
+
2
+ describe :script do
3
+
4
+ it "raises Invalid_Relative_HREF if :src is not relative" do
5
+ should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
6
+ actual do
7
+ script.src('http://www.example.org/file.js')./
8
+ end
9
+ }.message.should.match /example\.org/
10
+ end
11
+
12
+ it "allows a relative :src" do
13
+ target %^<script src="&#47;file.js"></script>^
14
+ actual {
15
+ script.src('/file.js')./
16
+ }
17
+ end
18
+
19
+ it "escapes slashes in attr :src" do
20
+ target %^<script src="&#47;dir&#47;file.js"></script>^
21
+ actual {
22
+ script.src('/dir/file.js')./
23
+ }
24
+ end
25
+
26
+ it "does not allow vars in :script :src attribute" do
27
+ target :body, <<-EOF
28
+ <script src="help"></script>
29
+ EOF
30
+
31
+ actual do
32
+ script.src(:help)
33
+ end
34
+ end
35
+
36
+ it "ignores text for :script block" do
37
+ target %^<script src="&#47;hello.js"></script>^
38
+ actual {
39
+ script.src('/hello.js') { 'hello' }
40
+ }
41
+ end
42
+
43
+
44
+ end # === describe :JS ===
@@ -0,0 +1,113 @@
1
+
2
+ describe :mustache do
3
+
4
+ it "replaces values with hash" do
5
+ target <<-EOF
6
+ <div>hello</div>
7
+ EOF
8
+
9
+ actual greeting: 'hello' do
10
+ div { :greeting }
11
+ end
12
+ end
13
+
14
+ it "raises ContextMiss for unauthorized methods" do
15
+ should.raise(Mustache::ContextMiss) {
16
+ target "nothing"
17
+ actual name: 'Bob' do
18
+ div { :object_id }
19
+ end
20
+ }.message.should.match /Can't find \.html\(:object_id\)/i
21
+
22
+ should.raise(Mustache::ContextMiss) {
23
+ target "nothing"
24
+ actual name: 'Bob' do
25
+ div { :to_s }
26
+ end
27
+ }.message.should.match /Can't find \.html\(:to_s\)/i
28
+ end
29
+
30
+ it "raises ContextMiss when an unknown value is requested" do
31
+ should.raise(Mustache::ContextMiss) {
32
+ actual name: 'Bob' do
33
+ div { :object_ids }
34
+ end
35
+ }.message.should.match /object_ids/
36
+ end
37
+
38
+ it "renders if inverted section and key is not defined" do
39
+ target <<-EOF
40
+ <div>hello</div>
41
+ EOF
42
+
43
+ actual {
44
+ render_unless(:here) { div { 'hello' } }
45
+ }
46
+ end
47
+
48
+ it "escapes html" do
49
+ target <<-EOF
50
+ <div>&amp; &#47; hello</div>
51
+ EOF
52
+
53
+ actual :hello=>'& / hello' do
54
+ div { :hello }
55
+ end
56
+ end
57
+
58
+ it "escapes html in nested values" do
59
+ target <<-EOF
60
+ <div id="my_box"><span>&amp; hello 1</span>
61
+ <span>&amp;&amp; hello 2</span></div>
62
+ EOF
63
+
64
+ actual(:hello=>{:msg=>'& hello 1', :goodbye=>nil}) {
65
+ div.*(:my_box) {
66
+ render_if(:hello) {
67
+ span { :msg }
68
+ render_unless(:goodbye) { span { '&& hello 2' } }
69
+ }
70
+ }
71
+ }
72
+ end
73
+
74
+ it "escapes :href in nested values" do
75
+ target %^<div><div><a href="&#47;hello">hello</a></div></div>^
76
+ actual(o: {url: '/hello', msg: 'hello'}) {
77
+ div {
78
+ render_if(:o) {
79
+ div { a.href(:url) { :msg } }
80
+ }
81
+ }
82
+ }
83
+ end
84
+
85
+ it "does not allow vars to be used in form :action" do
86
+ target %^<form action="url"><input type="hidden" name="auth_token" value="hello" /></form>^
87
+
88
+ actual :auth_token => 'hello' do
89
+ form.action(:url) {}
90
+ end
91
+ end
92
+
93
+ it "does not allow vars to be used in :link :href" do
94
+ target %^<link href="hello" />^
95
+ actual {
96
+ link.href(:hello)./
97
+ }
98
+ end
99
+
100
+ it "raises ContextMiss if encounters unescaped value" do
101
+ should.raise(Mustache::ContextMiss) {
102
+ actual(blue: 'hello<') {
103
+ div { '-- {{{ blue }}}' }
104
+ }
105
+ }.message.should.match /blue/
106
+ end
107
+
108
+ end # === describe Sanitize mustache ===
109
+
110
+
111
+
112
+
113
+
@@ -0,0 +1,174 @@
1
+
2
+ describe :css do
3
+
4
+ it "raises Invalid if class name has invalid chars: class=\"hi;o\"" do
5
+ should.raise(Escape_Escape_Escape::Invalid) {
6
+ actual {
7
+ div.^('hi;o') {
8
+ border '1px solid #fff'
9
+ 'hi o'
10
+ }
11
+ }
12
+ }.message.should.match /hi;o/
13
+ end
14
+
15
+ it "raises Invalid if css value has invalid chars: something *" do
16
+ should.raise(Escape_Escape_Escape::Invalid) {
17
+ actual do
18
+ div {
19
+ background 'something *'
20
+ }
21
+ end
22
+ }.message.should.match /something \*/
23
+ end
24
+
25
+ it 'raises Invalid if css selector has invalid chars: s*s' do
26
+ should.raise(Escape_Escape_Escape::Invalid) {
27
+ actual do
28
+ div.^("s*s") { border '1px'}
29
+ end
30
+ }.message.should.match /s\*s/
31
+ end
32
+
33
+ it "raises Invalid if contains 'expression:'" do
34
+ should.raise(Escape_Escape_Escape::Invalid) {
35
+ actual do
36
+ div {
37
+ background 'solid expression:'
38
+ }
39
+ end
40
+ }.message.should.match /expression:/
41
+ end
42
+
43
+ it "raises Invalid if contains 'expression&'" do
44
+ should.raise(Escape_Escape_Escape::Invalid) {
45
+ actual do
46
+ div {
47
+ background 'solid expression&'
48
+ }
49
+ end
50
+ }.message.should.match /expression&/
51
+ end
52
+
53
+ it "sanitizes urls" do
54
+ target :style, <<-EOF
55
+ div.box {
56
+ background-image: url(http:&#47;&#47;www.example.com&#47;back.png);
57
+ }
58
+ EOF
59
+
60
+ actual do
61
+ div.^(:box) {
62
+ background_image 'http://www.example.com/back.png'
63
+ }
64
+ end
65
+ end
66
+
67
+
68
+ # ==========================================================================
69
+ # =========== end sanitization specs =====================================
70
+ # ==========================================================================
71
+
72
+
73
+ it 'allows css selectors with valid chars: #my_box div.box' do
74
+ target :style, <<-EOF
75
+ #my_box div.box {
76
+ border: 1px;
77
+ }
78
+ EOF
79
+
80
+ actual {
81
+ div.*(:my_box) {
82
+ div.^(:box) { border '1px' }
83
+ }
84
+ }
85
+ end
86
+
87
+ it "adds a 'style' tag to 'head'" do
88
+ target :outer, :style, %^
89
+ <style type="text/css">
90
+ #the_box {
91
+ border-width: 10px;
92
+ }
93
+ </style>
94
+ ^
95
+
96
+ actual do
97
+ div.*(:the_box) {
98
+ border_width '10px'
99
+ }
100
+ end
101
+ end
102
+
103
+ it "uses id of element to add style" do
104
+ target :style, %^
105
+ #my_box {
106
+ border-width: 1px;
107
+ }
108
+ ^
109
+
110
+ actual do
111
+ div.*(:my_box) {
112
+ border_width '1px'
113
+ }
114
+ end
115
+ end
116
+
117
+ it "uses tag hierarchy if no id found" do
118
+ target :style, %^
119
+ div div span {
120
+ width: 20px;
121
+ }
122
+ ^
123
+
124
+ actual do
125
+ div { div { span { width '20px' } } }
126
+ end
127
+ end
128
+
129
+ it "does not include parents when element has id" do
130
+ target :style, <<-EOF
131
+ #my_box div.box {
132
+ border: 15px;
133
+ }
134
+ EOF
135
+
136
+ actual do
137
+ div.^(:top) {
138
+ div.*(:my_box) {
139
+ div.^(:box) { border '15px' }
140
+ }
141
+ }
142
+ end
143
+ end
144
+
145
+
146
+ it "does not accept vars for css values" do
147
+ target :style, %^
148
+ div {
149
+ border: something;
150
+ }
151
+ ^
152
+ actual {
153
+ div {
154
+ border :something
155
+ }
156
+ }
157
+ end
158
+
159
+ it "does not accept vars for css -image values" do
160
+ target :style, %^
161
+ div {
162
+ background-image: url(something);
163
+ }
164
+ ^
165
+ actual {
166
+ div {
167
+ background_image :something
168
+ }
169
+ }
170
+ end
171
+
172
+ end # === sanitize css selectors
173
+
174
+
@@ -0,0 +1,64 @@
1
+
2
+ describe "HTML :on" do
3
+
4
+ it "escapes text as :html" do
5
+ target :script, <<-EOF
6
+ WWW_App.compile(
7
+ ["create_event",["div","click","add_class",["red&lt;red"]]]
8
+ );
9
+ EOF
10
+
11
+ actual do
12
+ div {
13
+ on(:click) { add_class "red<red" }
14
+ }
15
+ end
16
+ end
17
+
18
+ it "renders js" do
19
+ target :script, <<-EOF
20
+ WWW_App.compile(
21
+ #{
22
+ Escape_Escape_Escape.json_encode( ["create_event", [ "#my_box", "click", "add_class", ["hello"] ] ] )
23
+ }
24
+ );
25
+ EOF
26
+
27
+ actual {
28
+ div.*(:my_box) {
29
+ on(:click) { add_class :hello }
30
+ }
31
+ }
32
+ end
33
+
34
+ it "adds class to id of element: #id.class" do
35
+ target :style, %^
36
+ #me.highlight {
37
+ border-color: #fff;
38
+ }
39
+ ^
40
+
41
+ actual do
42
+ div.*(:me) {
43
+ on(:highlight) { border_color '#fff' }
44
+ }
45
+ end
46
+ end
47
+
48
+ it "adds a psuedo class if passed a String" do
49
+ target :style, <<-EOF
50
+ a:hover {
51
+ border: 12px;
52
+ }
53
+ EOF
54
+
55
+ actual {
56
+ a.href('/home') {
57
+ on(':hover') { border '12px' }
58
+ }
59
+ }
60
+ end
61
+
62
+ end # === describe
63
+
64
+