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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/README.md +42 -24
  4. data/VERSION +1 -1
  5. data/bin/www_app +15 -4
  6. data/lib/public/vendor/hogan-3.0.2.min.js +5 -0
  7. data/lib/public/vendor/instruct_instruct_instruct.js +247 -0
  8. data/lib/public/vendor/jquery-2.1.3.min.js +4 -0
  9. data/lib/public/vendor/lodash.min.js +89 -0
  10. data/lib/public/www_app.js +885 -625
  11. data/lib/www_app/CSS.rb +310 -0
  12. data/lib/www_app/HTML.rb +219 -0
  13. data/lib/www_app/JavaScript.rb +51 -0
  14. data/lib/www_app/TO.rb +897 -0
  15. data/lib/www_app.rb +324 -945
  16. data/playground/config.ru +102 -0
  17. data/specs/client-side/index.html +1 -1
  18. data/specs/client-side/index.js +31 -379
  19. data/specs/lib/config.ru +60 -31
  20. data/specs/lib/helpers.rb +24 -2
  21. data/specs/server-side/0000-new.rb +1 -1
  22. data/specs/server-side/0001-underscore-double.rb +38 -0
  23. data/specs/server-side/0001-underscore.rb +73 -0
  24. data/specs/server-side/0010-attrs.rb +3 -4
  25. data/specs/server-side/0011-id.rb +5 -5
  26. data/specs/server-side/0020-tag.rb +2 -2
  27. data/specs/server-side/0020-tag_content.rb +1 -1
  28. data/specs/server-side/0021-body.rb +1 -1
  29. data/specs/server-side/0021-script.rb +66 -20
  30. data/specs/server-side/{0021-page_title.rb → 0021-title.rb} +5 -3
  31. data/specs/server-side/0030-mustache.rb +27 -20
  32. data/specs/server-side/0030-style.rb +64 -21
  33. data/specs/server-side/0040-css.rb +4 -4
  34. data/specs/server-side/0041-pseudo.rb +55 -0
  35. data/specs/server-side/0042-slash.rb +20 -0
  36. data/specs/server-side/0060-text.rb +26 -0
  37. metadata +18 -13
  38. data/lib/public/jquery-2.1.1.js +0 -4
  39. data/lib/public/underscore-1.7.0.js +0 -6
  40. data/lib/public/underscore-min.map +0 -1
  41. data/lib/public/underscore.string-2.3.0.js +0 -1
  42. data/lib/www_app/Clean.rb +0 -169
  43. data/lib/www_app/dsl.rb +0 -86
  44. data/lib/www_app/source.rb +0 -53
  45. data/specs/server-side/0050-on.rb +0 -64
  46. data/specs/server-side/0060-string.rb +0 -32
  47. /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
- Cuba.use Da99_Rack_Protect.config { |c|
8
- c.config :host, [:localhost, 'www_app.com']
9
- }
8
+ PATH = File.expand_path(File.dirname(__FILE__) + '../../..')
10
9
 
11
- Cuba.use Rack::ShowExceptions
10
+ Rack::Mime::MIME_TYPES.merge!({".map" => "application/json"})
12
11
 
13
- Cuba.use(Class.new {
14
- def initialize app
15
- @app = app
16
- end
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
- 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
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
- end
62
+ }
63
+ }
39
64
 
40
- results
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).render(vals)
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).render(vals)
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 ===
@@ -7,7 +7,7 @@ describe "it runs" do
7
7
  div {
8
8
  "test"
9
9
  }
10
- }.render.should == "<div>test</div>"
10
+ }.to_html.should == "<div>test</div>"
11
11
  end
12
12
 
13
13
  end # === describe IS_DEV ===
@@ -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 unknown attribute" do
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 /Unknown attr: :hi/
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 :* do
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.*('a<&a') { 'hello' }
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.*(:my_id) { '1' }
18
- div.*(:my_id) { '2' }
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="&#47;there">There</a>'
30
30
 
31
31
  actual do
32
- a.*(:warning).href('/there') { "There" }
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.*('0a') { 'hello' }
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
- tag(:footers) { 'bye' }
15
+ footers { 'bye' }
16
16
  end
17
17
  }.message.should.match /footers/
18
18
  end
@@ -1,5 +1,5 @@
1
1
 
2
- describe "HTML contents" do
2
+ describe 'tag content' do
3
3
 
4
4
  it "uses last String value as content" do
5
5
  target %^<p>Hello</p>^
@@ -1,7 +1,7 @@
1
1
 
2
2
  describe "body with inner style" do
3
3
 
4
- it "uses 'body' instead of id" do
4
+ it "adds styles to page as: body { ... }" do
5
5
  target :style, %^
6
6
  body {
7
7
  border-width: 3px;
@@ -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.src('http://www.example.org/file.js')./
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
- target %^<script src="&#47;file.js"></script>^
14
- actual {
15
- script.src('/file.js')./
16
- }
22
+ actual = WWW_App.new {
23
+ script('/file.js')
24
+ }.to_html
25
+
26
+ script_srcs(actual).last.should == %^&#47;file.js^
17
27
  end
18
28
 
19
29
  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
30
+ actual = WWW_App.new {
31
+ script('/dir/file.js')
32
+ }.to_html
25
33
 
26
- it "does not allow vars in :script :src attribute" do
27
- target :body, <<-EOF
28
- <script src="help"></script>
29
- EOF
34
+ script_srcs(actual).last.should == %^&#47;dir&#47;file.js^
35
+ end
30
36
 
31
- actual do
32
- script.src(:help)
33
- end
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 "ignores text for :script block" do
37
- target %^<script src="&#47;hello.js"></script>^
45
+ it "renders :type when given a block" do
46
+ target %^<script type="text/text">hello</script>^
38
47
  actual {
39
- script.src('/hello.js') { 'hello' }
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
- page_title { 'yo' }
7
- }.render.scan(/<title>[^>]+<\/title>/).
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
- 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) {
59
+ actual = WWW_App.new() {
60
+ div.id(:my_box) {
66
61
  render_if(:hello) {
67
62
  span { :msg }
68
- render_unless(:goodbye) { span { '&& hello 2' } }
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>&amp; hello 1</span>
72
+ <span>&amp;&amp; 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
- target %^<div><div><a href="&#47;hello">hello</a></div></div>^
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="&#47;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
- target %^<form action="url"><input type="hidden" name="auth_token" value="hello" /></form>^
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
- target %^<link href="hello" />^
95
- actual {
96
- link.href(:hello)./
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