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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c83b9a2ad7fe21e4f47f356a63f64e787f8f7ac
4
- data.tar.gz: f35bd0d6f10bc01809b33b9eadaf53f7276edbce
3
+ metadata.gz: 7a977102139bfa209ce1a27d10bb2a54c62f8fde
4
+ data.tar.gz: 16d3f137ac9d4d7c613f9b3d5e82ea761d72bc62
5
5
  SHA512:
6
- metadata.gz: 4c72ec18e0557aa59bebba66aaca56ad70d4543f7065193e4519be52532453f29937037d01b6495308dad4746988c63b9db94bc1b056b2ee5fa4532b9fce34bd
7
- data.tar.gz: d58b23dbe9b28e15197902a4e25e3f4e33e678ea0681756fe440b192a660f4f8f67b30b56b6b052b78831c15e6e4620a3a40839c243fd7a9d839b7505daff284
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,7 @@
1
+ module Clearwater
2
+ module BlackBoxNode
3
+ def to_s
4
+ node.to_s
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Clearwater
2
+ module CachedRender
3
+ end
4
+ 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('<', '&lt;')
98
+ else
99
+ content.to_s
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,7 @@
1
+ module Clearwater
2
+ class DOMReference
3
+ def to_s
4
+ ''.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ require 'clearwater/component'
2
+
3
+ class Link
4
+ include Clearwater::Component
5
+
6
+ def initialize attributes, content
7
+ @attributes = attributes
8
+ @content = content
9
+ end
10
+
11
+ def render
12
+ a(@attributes, @content)
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Clearwater
2
- VERSION = "1.0.0.beta5"
2
+ VERSION = "1.0.0.rc1"
3
3
  end
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
- Opal.append_path(File.expand_path(File.join("..", "..", "opal"), __FILE__).untaint)
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? :class
131
- if attributes.key? :class_name
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(:class)
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.$$class === Opal.Array) {
154
- return #{content.map { |c| `self.$sanitize_content(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
- content = sanitize_content(content)
6
- attributes = HashUtils.camelize_keys(attributes).to_n
7
- `virtualDom.h(tag_name, attributes, content)`
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.camelize_keys(attributes).to_n},
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
- %x{
71
- return string.replace(/_(\w)/g, function(full_match, character_match) {
72
- return character_match.toUpperCase();
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.camelize_keys(hash)
80
- return hash unless hash.is_a? Hash
83
+ def self.camelized_native hash
84
+ return hash.to_n unless `!!hash.$$is_hash`
81
85
 
82
- camelized = {}
83
- hash.each do |k, v|
84
- key = StringUtils.camelize(k)
85
- value = if v.class == Hash
86
- camelize_keys(v)
87
- else
88
- v
89
- end
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
@@ -6,9 +6,13 @@ module Clearwater
6
6
  attr_accessor :application
7
7
 
8
8
  def initialize options={}, &block
9
- @window = options.fetch(:window) { Bowser.window }
10
- @location = options.fetch(:location) { window.location }
11
- @history = options.fetch(:history) { window.history }
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.split("/").reject(&:empty?)
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.split("/").reject(&:empty?)
40
- canonical_parts = canonical_path_for_path(path).split("/").reject(&:empty?)
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
- VirtualDOM.node :span, { id: 'foo' }, ['hi']
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
- expect(object.last_update).not_to be_nil
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>&lt;em>hi&lt;/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 'delegates to the DOM node passed in on mount' do
8
- r = ref
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
@@ -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.beta5
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-03-06 00:00:00.000000000 Z
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.2
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.2
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.beta2
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.beta2
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
- - spec/clearwater/application_spec.rb
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/cached_render_spec.rb
142
+ - spec/clearwater/component_spec.rb
123
143
  - spec/clearwater/dom_reference_spec.rb
124
- - spec/clearwater/router_spec.rb
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.5.1
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/cached_render_spec.rb
172
+ - spec/clearwater/component_spec.rb
154
173
  - spec/clearwater/dom_reference_spec.rb
155
- - spec/clearwater/router_spec.rb
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
@@ -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