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.
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
+