clearwater 1.0.0.beta5 → 1.0.0.rc1
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 +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
|