clearwater 1.0.0.beta5 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/clearwater/application.rb +17 -0
- data/lib/clearwater/black_box_node.rb +7 -0
- data/lib/clearwater/cached_render.rb +4 -0
- data/lib/clearwater/component.rb +104 -0
- data/lib/clearwater/dom_reference.rb +7 -0
- data/lib/clearwater/link.rb +14 -0
- data/lib/clearwater/version.rb +1 -1
- data/lib/clearwater.rb +8 -1
- data/opal/clearwater/application.rb +6 -0
- data/opal/clearwater/component.rb +21 -132
- data/opal/clearwater/virtual_dom.rb +24 -24
- data/shared/clearwater/component/html_tags.rb +117 -0
- data/{opal → shared}/clearwater/router/route.rb +6 -0
- data/{opal → shared}/clearwater/router/route_collection.rb +0 -0
- data/{opal → shared}/clearwater/router.rb +14 -6
- data/spec/clearwater/black_box_node_spec.rb +5 -48
- data/spec/clearwater/component_spec.rb +43 -0
- data/spec/clearwater/dom_reference_spec.rb +2 -6
- data/spec/spec_helper.rb +1 -0
- metadata +36 -19
- data/spec/clearwater/application_spec.rb +0 -62
- data/spec/clearwater/cached_render_spec.rb +0 -46
- data/spec/clearwater/router_spec.rb +0 -65
- data/spec/component_spec.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a977102139bfa209ce1a27d10bb2a54c62f8fde
|
4
|
+
data.tar.gz: 16d3f137ac9d4d7c613f9b3d5e82ea761d72bc62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bb2ad0849af13dd873b464a59d7c38c0d9f259b86277d6b0067b3ece77d94ff8ffd84b496f2833d778e643f6b8cf27aa70c0a5a60d53e53a3040a01414dc055
|
7
|
+
data.tar.gz: 8aaee323980734782f5751707c5082e4071ea777fede96a562ef63acf4c6b6b6aee6f1a062c610d48de84fb529ccc172031adaf578b2ba90e0b6af605eff690e
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Clearwater
|
2
|
+
class Application
|
3
|
+
attr_reader :component, :router
|
4
|
+
|
5
|
+
def initialize options={}
|
6
|
+
@router = options.fetch(:router) { Router.new }
|
7
|
+
@component = options.fetch(:component) { nil }
|
8
|
+
router.application = self
|
9
|
+
component.router = router
|
10
|
+
end
|
11
|
+
|
12
|
+
def render
|
13
|
+
router.set_outlets
|
14
|
+
component.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'clearwater/component/html_tags'
|
2
|
+
require 'clearwater/dom_reference'
|
3
|
+
|
4
|
+
module Clearwater
|
5
|
+
module Component
|
6
|
+
extend self
|
7
|
+
|
8
|
+
attr_accessor :outlet
|
9
|
+
attr_accessor :router
|
10
|
+
|
11
|
+
def render
|
12
|
+
end
|
13
|
+
|
14
|
+
HTML_TAGS.each do |tag_name|
|
15
|
+
define_method tag_name do |attributes=nil, content=nil|
|
16
|
+
tag(tag_name, attributes, content)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def tag tag_name, attributes=nil, content=nil
|
21
|
+
unless attributes.nil? || attributes.is_a?(Hash)
|
22
|
+
content = attributes
|
23
|
+
attributes = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
Tag.new(tag_name, attributes, content)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
html = render.to_s
|
31
|
+
if html.respond_to? :html_safe
|
32
|
+
html = html.html_safe
|
33
|
+
end
|
34
|
+
|
35
|
+
html
|
36
|
+
end
|
37
|
+
|
38
|
+
def params
|
39
|
+
router.params_for_path(router.current_path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def call &block
|
43
|
+
end
|
44
|
+
|
45
|
+
class Tag
|
46
|
+
def initialize tag_name, attributes=nil, content=nil
|
47
|
+
@tag_name = tag_name
|
48
|
+
@attributes = sanitize_attributes(attributes)
|
49
|
+
@content = content
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_html
|
53
|
+
html = "<#{@tag_name}"
|
54
|
+
if @attributes
|
55
|
+
@attributes.each do |attr, value|
|
56
|
+
html << " #{attr}=#{value.to_s.inspect}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if @content
|
60
|
+
html << '>'
|
61
|
+
html << sanitize_content(@content)
|
62
|
+
html << "</#{@tag_name}>"
|
63
|
+
else
|
64
|
+
html << '/>'
|
65
|
+
end
|
66
|
+
|
67
|
+
html
|
68
|
+
end
|
69
|
+
alias to_s to_html
|
70
|
+
|
71
|
+
def sanitize_attributes attributes
|
72
|
+
return attributes unless attributes.is_a? Hash
|
73
|
+
|
74
|
+
if attributes.key? :class_name or attributes.key? :className
|
75
|
+
attributes[:class] ||= attributes.delete(:class_name) || attributes.delete(:className)
|
76
|
+
end
|
77
|
+
|
78
|
+
if Hash === attributes[:style]
|
79
|
+
attributes[:style] = attributes[:style].map { |attr, value|
|
80
|
+
attr = attr.to_s.tr('_', '-')
|
81
|
+
"#{attr}:#{value}"
|
82
|
+
}.join(';')
|
83
|
+
end
|
84
|
+
|
85
|
+
attributes.reject! do |key, handler|
|
86
|
+
key[0, 2] == 'on' || DOMReference === handler
|
87
|
+
end
|
88
|
+
|
89
|
+
attributes
|
90
|
+
end
|
91
|
+
|
92
|
+
def sanitize_content content
|
93
|
+
case content
|
94
|
+
when Array
|
95
|
+
content.map { |c| sanitize_content c }.join
|
96
|
+
when String
|
97
|
+
content.gsub('<', '<')
|
98
|
+
else
|
99
|
+
content.to_s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/clearwater/version.rb
CHANGED
data/lib/clearwater.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
require "opal"
|
2
2
|
require "bowser"
|
3
|
+
require "clearwater/component"
|
4
|
+
require 'clearwater/link'
|
5
|
+
$:.unshift File.expand_path(File.join('..', '..', 'shared'), __FILE__)
|
6
|
+
require 'clearwater/router'
|
7
|
+
require 'clearwater/application'
|
3
8
|
|
4
9
|
module Clearwater
|
5
10
|
require_relative "clearwater/version"
|
6
11
|
end
|
7
12
|
|
8
|
-
|
13
|
+
%w(opal shared).each do |dir|
|
14
|
+
Opal.append_path(File.expand_path(File.join("..", "..", dir), __FILE__).untaint)
|
15
|
+
end
|
@@ -24,6 +24,12 @@ module Clearwater
|
|
24
24
|
router.application = self
|
25
25
|
component.router = router if component
|
26
26
|
|
27
|
+
if `#@component.type === 'Thunk' && typeof #@component.render === 'function'`
|
28
|
+
warn "Application root component (#{@component}) points to a cached " +
|
29
|
+
"component. Cached components must not be persistent components, " +
|
30
|
+
"such as application roots or routing targets."
|
31
|
+
end
|
32
|
+
|
27
33
|
@document.on 'visibilitychange' do
|
28
34
|
if @render_on_visibility_change
|
29
35
|
@render_on_visibility_change = false
|
@@ -1,123 +1,10 @@
|
|
1
1
|
require 'clearwater/virtual_dom'
|
2
|
+
require 'clearwater/component/html_tags'
|
2
3
|
|
3
4
|
module Clearwater
|
4
5
|
module Component
|
5
6
|
attr_accessor :router, :outlet
|
6
7
|
|
7
|
-
HTML_TAGS = %w(
|
8
|
-
a
|
9
|
-
abbr
|
10
|
-
address
|
11
|
-
area
|
12
|
-
article
|
13
|
-
aside
|
14
|
-
audio
|
15
|
-
b
|
16
|
-
base
|
17
|
-
bdi
|
18
|
-
bdo
|
19
|
-
blockquote
|
20
|
-
body
|
21
|
-
br
|
22
|
-
button
|
23
|
-
canvas
|
24
|
-
caption
|
25
|
-
cite
|
26
|
-
code
|
27
|
-
col
|
28
|
-
colgroup
|
29
|
-
command
|
30
|
-
data
|
31
|
-
datalist
|
32
|
-
dd
|
33
|
-
del
|
34
|
-
details
|
35
|
-
dfn
|
36
|
-
dialog
|
37
|
-
div
|
38
|
-
dl
|
39
|
-
dt
|
40
|
-
em
|
41
|
-
embed
|
42
|
-
fieldset
|
43
|
-
figcaption
|
44
|
-
figure
|
45
|
-
footer
|
46
|
-
form
|
47
|
-
h1
|
48
|
-
h2
|
49
|
-
h3
|
50
|
-
h4
|
51
|
-
h5
|
52
|
-
h6
|
53
|
-
head
|
54
|
-
header
|
55
|
-
hgroup
|
56
|
-
hr
|
57
|
-
html
|
58
|
-
i
|
59
|
-
iframe
|
60
|
-
img
|
61
|
-
input
|
62
|
-
ins
|
63
|
-
kbd
|
64
|
-
keygen
|
65
|
-
label
|
66
|
-
legend
|
67
|
-
li
|
68
|
-
link
|
69
|
-
main
|
70
|
-
map
|
71
|
-
mark
|
72
|
-
menu
|
73
|
-
meta
|
74
|
-
meter
|
75
|
-
nav
|
76
|
-
noscript
|
77
|
-
object
|
78
|
-
ol
|
79
|
-
optgroup
|
80
|
-
option
|
81
|
-
output
|
82
|
-
p
|
83
|
-
param
|
84
|
-
pre
|
85
|
-
progress
|
86
|
-
q
|
87
|
-
rp
|
88
|
-
rt
|
89
|
-
ruby
|
90
|
-
s
|
91
|
-
samp
|
92
|
-
script
|
93
|
-
section
|
94
|
-
select
|
95
|
-
small
|
96
|
-
source
|
97
|
-
span
|
98
|
-
strong
|
99
|
-
style
|
100
|
-
sub
|
101
|
-
summary
|
102
|
-
sup
|
103
|
-
table
|
104
|
-
tbody
|
105
|
-
td
|
106
|
-
textarea
|
107
|
-
tfoot
|
108
|
-
th
|
109
|
-
thead
|
110
|
-
time
|
111
|
-
title
|
112
|
-
tr
|
113
|
-
track
|
114
|
-
u
|
115
|
-
ul
|
116
|
-
var
|
117
|
-
video
|
118
|
-
wbr
|
119
|
-
)
|
120
|
-
|
121
8
|
def params
|
122
9
|
router.params
|
123
10
|
end
|
@@ -125,23 +12,23 @@ module Clearwater
|
|
125
12
|
def self.sanitize_attributes attributes
|
126
13
|
return attributes unless attributes.is_a? Hash
|
127
14
|
|
15
|
+
attributes.each do |key, value|
|
16
|
+
if `key.slice(0, 2)` == 'on'
|
17
|
+
attributes[key] = proc do |event|
|
18
|
+
value.call(Bowser::Event.new(event))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
128
23
|
# Allow specifying `class` instead of `class_name`.
|
129
24
|
# Note: `class_name` is still allowed
|
130
|
-
if attributes.key?
|
131
|
-
if attributes.key?
|
25
|
+
if attributes.key?(:class)
|
26
|
+
if attributes.key?(:class_name)
|
132
27
|
warn "You have both `class` and `class_name` attributes for this " +
|
133
28
|
"element. `class` takes precedence: #{attributes}"
|
134
29
|
end
|
135
30
|
|
136
|
-
attributes[:class_name] = attributes.delete
|
137
|
-
end
|
138
|
-
|
139
|
-
attributes.each do |key, handler|
|
140
|
-
if key[0, 2] == 'on'
|
141
|
-
attributes[key] = proc do |event|
|
142
|
-
handler.call(Bowser::Event.new(event))
|
143
|
-
end
|
144
|
-
end
|
31
|
+
attributes[:class_name] = attributes.delete :class
|
145
32
|
end
|
146
33
|
|
147
34
|
attributes
|
@@ -150,8 +37,8 @@ module Clearwater
|
|
150
37
|
def self.sanitize_content content
|
151
38
|
%x{
|
152
39
|
if(content && content.$$class) {
|
153
|
-
if(content.$$
|
154
|
-
return #{content.map { |c|
|
40
|
+
if(content.$$is_array) {
|
41
|
+
return #{content.map { |c| sanitize_content(c) }};
|
155
42
|
} else {
|
156
43
|
var render = content.$render;
|
157
44
|
|
@@ -177,16 +64,18 @@ module Clearwater
|
|
177
64
|
|
178
65
|
HTML_TAGS.each do |tag_name|
|
179
66
|
define_method(tag_name) do |attributes, content|
|
67
|
+
%x{
|
68
|
+
if(!(attributes === nil || attributes.$$is_hash)) {
|
69
|
+
content = attributes;
|
70
|
+
attributes = nil;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
180
74
|
tag(tag_name, attributes, content)
|
181
75
|
end
|
182
76
|
end
|
183
77
|
|
184
78
|
def tag tag_name, attributes=nil, content=nil
|
185
|
-
if !(`attributes.$$is_hash || attributes === #{nil}`)
|
186
|
-
content = attributes
|
187
|
-
attributes = nil
|
188
|
-
end
|
189
|
-
|
190
79
|
VirtualDOM.node(
|
191
80
|
tag_name,
|
192
81
|
Component.sanitize_attributes(attributes),
|
@@ -2,16 +2,20 @@ require 'clearwater/virtual_dom/js/virtual_dom.js'
|
|
2
2
|
|
3
3
|
module VirtualDOM
|
4
4
|
def self.node(tag_name, attributes=nil, content=nil)
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
%x{
|
6
|
+
return virtualDom.h(
|
7
|
+
tag_name,
|
8
|
+
#{HashUtils.camelized_native(attributes)},
|
9
|
+
#{sanitize_content(content)}
|
10
|
+
);
|
11
|
+
}
|
8
12
|
end
|
9
13
|
|
10
14
|
def self.svg(tag_name, attributes=nil, content=nil)
|
11
15
|
%x{
|
12
16
|
return virtualDom.svg(
|
13
17
|
tag_name,
|
14
|
-
#{HashUtils.
|
18
|
+
#{HashUtils.camelized_native(attributes)},
|
15
19
|
#{sanitize_content(content)}
|
16
20
|
);
|
17
21
|
}
|
@@ -67,31 +71,27 @@ module VirtualDOM
|
|
67
71
|
|
68
72
|
module StringUtils
|
69
73
|
def self.camelize string
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
`string.replace(/_(\w)/g, self.$_camelize_handler)`
|
75
|
+
end
|
76
|
+
|
77
|
+
def self._camelize_handler _, character_match
|
78
|
+
`character_match.toUpperCase()`
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
78
82
|
module HashUtils
|
79
|
-
def self.
|
80
|
-
return hash unless hash
|
83
|
+
def self.camelized_native hash
|
84
|
+
return hash.to_n unless `!!hash.$$is_hash`
|
81
85
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
camelized[key] = value
|
92
|
-
end
|
93
|
-
|
94
|
-
camelized
|
86
|
+
%x{
|
87
|
+
var v, keys = #{hash.keys}, key, js_obj = {};
|
88
|
+
for(var index = 0; index < keys.length; index++) {
|
89
|
+
key = keys[index];
|
90
|
+
v = #{hash[`key`]};
|
91
|
+
js_obj[#{StringUtils.camelize(`key`)}] = v.$$is_hash ? self.$camelized_native(v) : v
|
92
|
+
}
|
93
|
+
return js_obj;
|
94
|
+
}
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Clearwater
|
2
|
+
module Component
|
3
|
+
HTML_TAGS = %w(
|
4
|
+
a
|
5
|
+
abbr
|
6
|
+
address
|
7
|
+
area
|
8
|
+
article
|
9
|
+
aside
|
10
|
+
audio
|
11
|
+
b
|
12
|
+
base
|
13
|
+
bdi
|
14
|
+
bdo
|
15
|
+
blockquote
|
16
|
+
body
|
17
|
+
br
|
18
|
+
button
|
19
|
+
canvas
|
20
|
+
caption
|
21
|
+
cite
|
22
|
+
code
|
23
|
+
col
|
24
|
+
colgroup
|
25
|
+
command
|
26
|
+
data
|
27
|
+
datalist
|
28
|
+
dd
|
29
|
+
del
|
30
|
+
details
|
31
|
+
dfn
|
32
|
+
dialog
|
33
|
+
div
|
34
|
+
dl
|
35
|
+
dt
|
36
|
+
em
|
37
|
+
embed
|
38
|
+
fieldset
|
39
|
+
figcaption
|
40
|
+
figure
|
41
|
+
footer
|
42
|
+
form
|
43
|
+
h1
|
44
|
+
h2
|
45
|
+
h3
|
46
|
+
h4
|
47
|
+
h5
|
48
|
+
h6
|
49
|
+
head
|
50
|
+
header
|
51
|
+
hgroup
|
52
|
+
hr
|
53
|
+
html
|
54
|
+
i
|
55
|
+
iframe
|
56
|
+
img
|
57
|
+
input
|
58
|
+
ins
|
59
|
+
kbd
|
60
|
+
keygen
|
61
|
+
label
|
62
|
+
legend
|
63
|
+
li
|
64
|
+
link
|
65
|
+
main
|
66
|
+
map
|
67
|
+
mark
|
68
|
+
menu
|
69
|
+
meta
|
70
|
+
meter
|
71
|
+
nav
|
72
|
+
noscript
|
73
|
+
object
|
74
|
+
ol
|
75
|
+
optgroup
|
76
|
+
option
|
77
|
+
output
|
78
|
+
p
|
79
|
+
param
|
80
|
+
pre
|
81
|
+
progress
|
82
|
+
q
|
83
|
+
rp
|
84
|
+
rt
|
85
|
+
ruby
|
86
|
+
s
|
87
|
+
samp
|
88
|
+
script
|
89
|
+
section
|
90
|
+
select
|
91
|
+
small
|
92
|
+
source
|
93
|
+
span
|
94
|
+
strong
|
95
|
+
style
|
96
|
+
sub
|
97
|
+
summary
|
98
|
+
sup
|
99
|
+
table
|
100
|
+
tbody
|
101
|
+
td
|
102
|
+
textarea
|
103
|
+
tfoot
|
104
|
+
th
|
105
|
+
thead
|
106
|
+
time
|
107
|
+
title
|
108
|
+
tr
|
109
|
+
track
|
110
|
+
u
|
111
|
+
ul
|
112
|
+
var
|
113
|
+
video
|
114
|
+
wbr
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
@@ -9,6 +9,12 @@ module Clearwater
|
|
9
9
|
@key = options.fetch(:key)
|
10
10
|
@target = options.fetch(:target)
|
11
11
|
@parent = options.fetch(:parent)
|
12
|
+
|
13
|
+
if `#@target.type === 'Thunk' && typeof #@target.render === 'function'`
|
14
|
+
warn "Route '#{key}' points to a cached component. Cached " +
|
15
|
+
"components must not be persistent components, such as " +
|
16
|
+
"application roots or routing targets."
|
17
|
+
end
|
12
18
|
end
|
13
19
|
|
14
20
|
def route *args, &block
|
File without changes
|
@@ -6,9 +6,13 @@ module Clearwater
|
|
6
6
|
attr_accessor :application
|
7
7
|
|
8
8
|
def initialize options={}, &block
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if RUBY_ENGINE == 'opal'
|
10
|
+
@window = options.fetch(:window) { Bowser.window }
|
11
|
+
@location = options.fetch(:location) { window.location }
|
12
|
+
@history = options.fetch(:history) { window.history }
|
13
|
+
else
|
14
|
+
@location = options.fetch(:location)
|
15
|
+
end
|
12
16
|
@routes = RouteCollection.new(self)
|
13
17
|
@application = options[:application]
|
14
18
|
|
@@ -20,7 +24,7 @@ module Clearwater
|
|
20
24
|
end
|
21
25
|
|
22
26
|
def routes_for_path path
|
23
|
-
parts = path
|
27
|
+
parts = get_path_parts(path)
|
24
28
|
@routes[parts]
|
25
29
|
end
|
26
30
|
|
@@ -36,8 +40,8 @@ module Clearwater
|
|
36
40
|
end
|
37
41
|
|
38
42
|
def params path=current_path
|
39
|
-
path_parts = path
|
40
|
-
canonical_parts = canonical_path_for_path(path)
|
43
|
+
path_parts = get_path_parts(path)
|
44
|
+
canonical_parts = get_path_parts(canonical_path_for_path(path))
|
41
45
|
|
42
46
|
canonical_parts.each_with_index.reduce({}) { |params, (part, index)|
|
43
47
|
if part.start_with? ":"
|
@@ -109,6 +113,10 @@ module Clearwater
|
|
109
113
|
|
110
114
|
private
|
111
115
|
|
116
|
+
def get_path_parts path
|
117
|
+
path.split("/").reject(&:empty?)
|
118
|
+
end
|
119
|
+
|
112
120
|
def render_application
|
113
121
|
if application && application.component
|
114
122
|
application.component.call
|
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'clearwater/black_box_node'
|
3
|
+
require 'clearwater/component'
|
2
4
|
|
3
5
|
module Clearwater
|
4
6
|
describe BlackBoxNode do
|
@@ -6,59 +8,14 @@ module Clearwater
|
|
6
8
|
Class.new do
|
7
9
|
include Clearwater::BlackBoxNode
|
8
10
|
|
9
|
-
attr_reader :last_update
|
10
|
-
|
11
11
|
def node
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def mount node
|
16
|
-
@mounted = true
|
17
|
-
end
|
18
|
-
|
19
|
-
def update
|
20
|
-
@last_update = Time.now
|
21
|
-
end
|
22
|
-
|
23
|
-
def unmount
|
24
|
-
@mounted = false
|
25
|
-
end
|
26
|
-
|
27
|
-
def mounted?
|
28
|
-
!!@mounted
|
12
|
+
Clearwater::Component.div({ id: 'foo' }, 'hi')
|
29
13
|
end
|
30
14
|
end.new
|
31
15
|
}
|
32
|
-
let(:renderable) { BlackBoxNode::Renderable.new(object) }
|
33
|
-
|
34
|
-
it 'has the special type of "Widget"' do
|
35
|
-
r = renderable
|
36
|
-
expect(`r.type`).to eq 'Widget'
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'uses the delegate node to render into the DOM' do
|
40
|
-
r = renderable
|
41
|
-
expect(`#{renderable.create_element}.native.outerHTML`).to eq '<span id="foo">hi</span>'
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'calls mount when inserted into the DOM' do
|
45
|
-
r = renderable
|
46
|
-
`r.init()`
|
47
|
-
expect(object).to be_mounted
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'calls unmount when removed from the DOM' do
|
51
|
-
r = renderable
|
52
|
-
`r.init()`
|
53
|
-
`r.destroy()`
|
54
|
-
expect(object).not_to be_mounted
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'calls update when updated in the DOM' do
|
58
|
-
r = renderable
|
59
|
-
`r.update({})`
|
60
16
|
|
61
|
-
|
17
|
+
it 'just renders the specified node' do
|
18
|
+
expect(object.to_s).to eq '<div id="foo">hi</div>'
|
62
19
|
end
|
63
20
|
end
|
64
21
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/clearwater/component'
|
3
|
+
|
4
|
+
module Clearwater
|
5
|
+
RSpec.describe Component do
|
6
|
+
let(:component) { Class.new { include Clearwater::Component }.new }
|
7
|
+
|
8
|
+
it 'generates html' do
|
9
|
+
html = component.div({ id: 'foo', class_name: 'bar' }, [
|
10
|
+
component.p("baz"),
|
11
|
+
]).to_s
|
12
|
+
|
13
|
+
expect(html).to eq('<div id="foo" class="bar"><p>baz</p></div>')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'converts styles into strings' do
|
17
|
+
html = component.div({
|
18
|
+
style: {
|
19
|
+
font_size: '24px',
|
20
|
+
padding: '3px',
|
21
|
+
}
|
22
|
+
}, "Hello world!").to_s
|
23
|
+
|
24
|
+
expect(html).to eq('<div style="font-size:24px;padding:3px">Hello world!</div>')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'removes DOMReference attributes' do
|
28
|
+
html = component.div({
|
29
|
+
ref: DOMReference.new,
|
30
|
+
}, 'Hello World!').to_s
|
31
|
+
|
32
|
+
expect(html).to eq('<div>Hello World!</div>')
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'content sanitization' do
|
36
|
+
it 'sanitizes content strings, but not elements' do
|
37
|
+
html = component.div(component.p('<em>hi</em>')).to_s
|
38
|
+
|
39
|
+
expect(html).to eq '<div><p><em>hi</em></p></div>'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -4,12 +4,8 @@ module Clearwater
|
|
4
4
|
describe DOMReference do
|
5
5
|
let(:ref) { DOMReference.new }
|
6
6
|
|
7
|
-
it '
|
8
|
-
|
9
|
-
|
10
|
-
`r.hook({ value: 'hi' })`
|
11
|
-
|
12
|
-
expect(r.value).to eq 'hi'
|
7
|
+
it 'sanitizes to an empty string' do
|
8
|
+
expect(ref.to_s).to eq ''
|
13
9
|
end
|
14
10
|
end
|
15
11
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$:.unshift 'shared'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clearwater
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Gaskins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.1.
|
33
|
+
version: 0.1.5
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.1.
|
40
|
+
version: 0.1.5
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,28 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.5.0
|
61
|
+
version: 0.5.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.5.0
|
68
|
+
version: 0.5.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.3'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rake
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,6 +116,12 @@ extensions: []
|
|
102
116
|
extra_rdoc_files: []
|
103
117
|
files:
|
104
118
|
- lib/clearwater.rb
|
119
|
+
- lib/clearwater/application.rb
|
120
|
+
- lib/clearwater/black_box_node.rb
|
121
|
+
- lib/clearwater/cached_render.rb
|
122
|
+
- lib/clearwater/component.rb
|
123
|
+
- lib/clearwater/dom_reference.rb
|
124
|
+
- lib/clearwater/link.rb
|
105
125
|
- lib/clearwater/version.rb
|
106
126
|
- opal/clearwater.rb
|
107
127
|
- opal/clearwater/application.rb
|
@@ -111,18 +131,17 @@ files:
|
|
111
131
|
- opal/clearwater/component.rb
|
112
132
|
- opal/clearwater/dom_reference.rb
|
113
133
|
- opal/clearwater/link.rb
|
114
|
-
- opal/clearwater/router.rb
|
115
|
-
- opal/clearwater/router/route.rb
|
116
|
-
- opal/clearwater/router/route_collection.rb
|
117
134
|
- opal/clearwater/svg_component.rb
|
118
135
|
- opal/clearwater/virtual_dom.rb
|
119
136
|
- opal/clearwater/virtual_dom/js/virtual_dom.js
|
120
|
-
-
|
137
|
+
- shared/clearwater/component/html_tags.rb
|
138
|
+
- shared/clearwater/router.rb
|
139
|
+
- shared/clearwater/router/route.rb
|
140
|
+
- shared/clearwater/router/route_collection.rb
|
121
141
|
- spec/clearwater/black_box_node_spec.rb
|
122
|
-
- spec/clearwater/
|
142
|
+
- spec/clearwater/component_spec.rb
|
123
143
|
- spec/clearwater/dom_reference_spec.rb
|
124
|
-
- spec/
|
125
|
-
- spec/component_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
126
145
|
homepage: https://clearwater-rb.github.io/
|
127
146
|
licenses:
|
128
147
|
- MIT
|
@@ -131,6 +150,7 @@ post_install_message:
|
|
131
150
|
rdoc_options: []
|
132
151
|
require_paths:
|
133
152
|
- lib
|
153
|
+
- shared
|
134
154
|
required_ruby_version: !ruby/object:Gem::Requirement
|
135
155
|
requirements:
|
136
156
|
- - ">="
|
@@ -143,15 +163,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
163
|
version: 1.3.1
|
144
164
|
requirements: []
|
145
165
|
rubyforge_project:
|
146
|
-
rubygems_version: 2.
|
166
|
+
rubygems_version: 2.4.8
|
147
167
|
signing_key:
|
148
168
|
specification_version: 4
|
149
169
|
summary: Front-end Ruby web framework for fast, reasonable, and composable applications
|
150
170
|
test_files:
|
151
|
-
- spec/clearwater/application_spec.rb
|
152
171
|
- spec/clearwater/black_box_node_spec.rb
|
153
|
-
- spec/clearwater/
|
172
|
+
- spec/clearwater/component_spec.rb
|
154
173
|
- spec/clearwater/dom_reference_spec.rb
|
155
|
-
- spec/
|
156
|
-
- spec/component_spec.rb
|
157
|
-
has_rdoc:
|
174
|
+
- spec/spec_helper.rb
|
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'clearwater'
|
2
|
-
require 'clearwater/svg_component'
|
3
|
-
require 'bowser'
|
4
|
-
|
5
|
-
module Clearwater
|
6
|
-
RSpec.describe Application do
|
7
|
-
let(:app) {
|
8
|
-
Application.new(
|
9
|
-
component: component,
|
10
|
-
element: element,
|
11
|
-
)
|
12
|
-
}
|
13
|
-
let(:component) {
|
14
|
-
$svg_component = self.svg_component
|
15
|
-
Class.new do
|
16
|
-
include Clearwater::Component
|
17
|
-
|
18
|
-
def render
|
19
|
-
div([
|
20
|
-
p({ class_name: 'foo' }, 'Hello world'),
|
21
|
-
$svg_component,
|
22
|
-
])
|
23
|
-
end
|
24
|
-
end.new
|
25
|
-
}
|
26
|
-
let(:svg_component) {
|
27
|
-
Class.new do
|
28
|
-
include Clearwater::SVGComponent
|
29
|
-
|
30
|
-
def render
|
31
|
-
svg({ class: 'mysvg', marker_height: 10, marker_end: "url(#arrow)" }, [
|
32
|
-
circle(cx: 50, cy: 50, r: 30),
|
33
|
-
])
|
34
|
-
end
|
35
|
-
end.new
|
36
|
-
}
|
37
|
-
let(:element) { Bowser.document.create_element('div') }
|
38
|
-
|
39
|
-
it 'renders to the specified element' do
|
40
|
-
app.perform_render
|
41
|
-
|
42
|
-
expect(element.inner_html).to eq '<div><p class="foo">Hello world</p><svg class="mysvg" markerHeight="10" marker-end="url(#arrow)"><circle cx="50" cy="50" r="30"></circle></svg></div>'
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'calls queued blocks after rendering' do
|
46
|
-
i = 1
|
47
|
-
app.on_render << proc { i += 1 }
|
48
|
-
app.on_render << proc { i += 1 }
|
49
|
-
|
50
|
-
app.perform_render
|
51
|
-
expect(i).to eq 3
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'empties the block queue after rendering' do
|
55
|
-
app.on_render << proc { }
|
56
|
-
|
57
|
-
app.perform_render
|
58
|
-
|
59
|
-
expect(app.on_render).to be_empty
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'clearwater/component'
|
2
|
-
require 'clearwater/cached_render'
|
3
|
-
|
4
|
-
module Clearwater
|
5
|
-
describe CachedRender do
|
6
|
-
let(:component_class) {
|
7
|
-
Class.new do
|
8
|
-
include Clearwater::Component
|
9
|
-
include Clearwater::CachedRender
|
10
|
-
|
11
|
-
def initialize value
|
12
|
-
@value = value
|
13
|
-
end
|
14
|
-
|
15
|
-
def render
|
16
|
-
@value.to_s
|
17
|
-
end
|
18
|
-
end
|
19
|
-
}
|
20
|
-
let(:value) { double }
|
21
|
-
let(:component) { component_class.new(value) }
|
22
|
-
|
23
|
-
it 'memoizes the return value of render' do
|
24
|
-
component = component()
|
25
|
-
|
26
|
-
expect(value).to receive(:to_s)
|
27
|
-
%x{ component.render(component) }
|
28
|
-
|
29
|
-
component.instance_exec { @vnode = VirtualDOM.node('div', 'howdy') }
|
30
|
-
expect(value).not_to receive(:to_s)
|
31
|
-
|
32
|
-
2.times { `component.render(component)` }
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'uses should_render? to determine whether to call render again' do
|
36
|
-
component = component()
|
37
|
-
def component.should_render?
|
38
|
-
true
|
39
|
-
end
|
40
|
-
|
41
|
-
expect(value).to receive(:to_s).twice
|
42
|
-
|
43
|
-
2.times { `component.render(component)` }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'clearwater/router'
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
module Clearwater
|
5
|
-
RSpec.describe Router do
|
6
|
-
let(:component_class) {
|
7
|
-
Class.new do
|
8
|
-
include Clearwater::Component
|
9
|
-
end
|
10
|
-
}
|
11
|
-
let(:routed_component) { component_class.new }
|
12
|
-
let!(:router) {
|
13
|
-
component = routed_component
|
14
|
-
Router.new do
|
15
|
-
route 'articles' => component do
|
16
|
-
route ':article_id' => component
|
17
|
-
end
|
18
|
-
end
|
19
|
-
}
|
20
|
-
|
21
|
-
it 'sets the router for a routed component' do
|
22
|
-
expect(routed_component.router).to be router
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'gets the params for a given path' do
|
26
|
-
expect(router.params('/articles/foo')).to eq article_id: 'foo'
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'gets the components for a given path' do
|
30
|
-
expect(router.targets_for_path('/articles/1')).to eq [
|
31
|
-
routed_component, routed_component
|
32
|
-
]
|
33
|
-
|
34
|
-
expect(router.targets_for_path('/articles')).to eq [routed_component]
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'gets the current path' do
|
38
|
-
location = OpenStruct.new(path: '/foo')
|
39
|
-
router = Router.new(location: location)
|
40
|
-
|
41
|
-
expect(router.current_path).to eq '/foo'
|
42
|
-
|
43
|
-
location.path = '/bar'
|
44
|
-
|
45
|
-
expect(router.current_path).to eq '/bar'
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'gets the params from the path' do
|
49
|
-
expect(router.params('/articles/123')).to eq({ article_id: '123' })
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'gets params with a namespace' do
|
53
|
-
component = routed_component
|
54
|
-
router = Router.new do
|
55
|
-
namespace 'clearwater'
|
56
|
-
|
57
|
-
route 'articles' => component do
|
58
|
-
route ':article_id' => component
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
expect(router.params('/clearwater/articles/123')).to eq({ article_id: '123' })
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
data/spec/component_spec.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'clearwater/component'
|
2
|
-
|
3
|
-
module Clearwater
|
4
|
-
RSpec.describe Component do
|
5
|
-
let(:component_class) {
|
6
|
-
Class.new do
|
7
|
-
include Clearwater::Component
|
8
|
-
end
|
9
|
-
}
|
10
|
-
let(:component) { component_class.new }
|
11
|
-
|
12
|
-
it 'provides a default render method' do
|
13
|
-
expect(component.render).to be_nil
|
14
|
-
end
|
15
|
-
|
16
|
-
Component::HTML_TAGS.each do |tag|
|
17
|
-
it "provides helpers for `#{tag}` elements" do
|
18
|
-
expect(`#{component.send(tag)}.tagName`).to eq tag.upcase
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'sanitizes element attributes' do
|
23
|
-
attributes = Component.sanitize_attributes({
|
24
|
-
class: 'foo',
|
25
|
-
onclick: proc { |event| expect(event).to be_a Bowser::Event },
|
26
|
-
})
|
27
|
-
|
28
|
-
# Renames :class to :class_name
|
29
|
-
expect(attributes[:class_name]).to eq 'foo'
|
30
|
-
|
31
|
-
# Wraps yielded events in a Bowser::Event
|
32
|
-
attributes[:onclick].call(`document.createEvent('MouseEvent')`)
|
33
|
-
end
|
34
|
-
|
35
|
-
describe 'sanitizing content' do
|
36
|
-
it 'sanitizes components by calling `render`' do
|
37
|
-
allow(component).to receive(:render) { 'foo' }
|
38
|
-
expect(Component.sanitize_content(component)).to eq 'foo'
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sanitizes arrays by sanitizing each element' do
|
42
|
-
allow(component).to receive(:render) { 'foo' }
|
43
|
-
expect(Component.sanitize_content([component, nil, 1])).to eq ['foo', nil, 1]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'retrieves params from the router' do
|
48
|
-
router = double('Router')
|
49
|
-
params = { article_id: 123 }
|
50
|
-
allow(router).to receive(:params) { params }
|
51
|
-
allow(router).to receive(:current_path) { '/articles/123' }
|
52
|
-
component.router = router
|
53
|
-
|
54
|
-
expect(component.params).to eq params
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|