ovto 0.4.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.gitmodules +3 -0
- data/CHANGELOG.md +15 -2
- data/Gemfile +2 -2
- data/Gemfile.lock +23 -27
- data/README.md +2 -2
- data/book/SUMMARY.md +3 -1
- data/book/api/actions.md +1 -1
- data/book/api/fetch.md +3 -3
- data/book/api/middleware.md +99 -0
- data/book/api/pure_component.md +30 -0
- data/book/guides/install.md +76 -0
- data/book/guides/tutorial.md +3 -3
- data/book/ovtologo.png +0 -0
- data/docs/api/Array.html +190 -0
- data/docs/api/Hash.html +196 -0
- data/docs/api/Ovto/Actions.html +132 -40
- data/docs/api/Ovto/App.html +254 -40
- data/docs/api/Ovto/Component/MoreThanOneNode.html +6 -6
- data/docs/api/Ovto/Component.html +102 -27
- data/docs/api/Ovto/Middleware/Actions.html +428 -0
- data/docs/api/Ovto/Middleware/Base.html +606 -0
- data/docs/api/Ovto/Middleware/Component.html +355 -0
- data/docs/api/Ovto/Middleware.html +288 -0
- data/docs/api/Ovto/PureComponent/StateIsNotAvailable.html +124 -0
- data/docs/api/Ovto/PureComponent.html +368 -0
- data/docs/api/Ovto/Runtime.html +7 -7
- data/docs/api/Ovto/State/MissingValue.html +6 -6
- data/docs/api/Ovto/State/{UnknownKey.html → UnknownStateKey.html} +11 -11
- data/docs/api/Ovto/State.html +59 -43
- data/docs/api/Ovto/WiredActionSet.html +636 -0
- data/docs/api/Ovto/WiredActions.html +78 -35
- data/docs/api/Ovto.html +226 -29
- data/docs/api/_index.html +99 -11
- data/docs/api/actions.html +36 -10
- data/docs/api/app.html +35 -9
- data/docs/api/class_list.html +3 -3
- data/docs/api/component.html +37 -11
- data/docs/api/css/style.css +2 -2
- data/docs/api/fetch.html +40 -14
- data/docs/api/file.README.html +8 -8
- data/docs/api/file_list.html +2 -2
- data/docs/api/frames.html +2 -2
- data/docs/api/index.html +8 -8
- data/docs/api/js/app.js +14 -3
- data/docs/api/method_list.html +318 -22
- data/docs/api/middleware.html +498 -0
- data/docs/api/pure_component.html +428 -0
- data/docs/api/state.html +35 -9
- data/docs/api/top-level-namespace.html +9 -7
- data/docs/gitbook/gitbook.js +2 -2
- data/docs/gitbook/theme.js +1 -1
- data/docs/guides/debugging.html +35 -9
- data/docs/guides/development.html +35 -9
- data/docs/guides/install.html +452 -0
- data/docs/guides/tutorial.html +101 -66
- data/docs/index.html +37 -17
- data/docs/ovtologo.png +0 -0
- data/docs/search_index.json +1 -1
- data/examples/sinatra/Gemfile +2 -0
- data/examples/sinatra/Gemfile.lock +33 -31
- data/examples/sinatra/app.rb +1 -4
- data/examples/sinatra/config.ru +6 -6
- data/examples/static/Gemfile.lock +10 -14
- data/lib/ovto/actions.rb +11 -0
- data/lib/ovto/app.rb +37 -13
- data/lib/ovto/component.rb +149 -73
- data/lib/ovto/fetch.rb +1 -1
- data/lib/ovto/middleware.rb +121 -0
- data/lib/ovto/pure_component.rb +22 -0
- data/lib/ovto/state.rb +13 -5
- data/lib/ovto/version.rb +1 -1
- data/lib/ovto/wired_action_set.rb +40 -0
- data/lib/ovto/wired_actions.rb +49 -11
- data/lib/ovto.rb +27 -0
- data/ovto.gemspec +1 -1
- metadata +35 -9
data/examples/sinatra/Gemfile
CHANGED
@@ -1,52 +1,52 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
ovto (0.
|
5
|
-
opal (
|
4
|
+
ovto (0.6.0)
|
5
|
+
opal (>= 0.11, < 2)
|
6
6
|
rack (~> 2.0)
|
7
7
|
thor (~> 0.20)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
ast (2.4.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
opal (
|
12
|
+
ast (2.4.2)
|
13
|
+
concurrent-ruby (1.1.9)
|
14
|
+
multi_json (1.15.0)
|
15
|
+
mustermann (1.1.1)
|
16
|
+
ruby2_keywords (~> 0.0.1)
|
17
|
+
nio4r (2.5.8)
|
18
|
+
opal (1.2.0)
|
19
19
|
ast (>= 2.3.0)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
opal (~> 0.11.0)
|
25
|
-
sprockets (~> 3.7)
|
20
|
+
parser (~> 3.0)
|
21
|
+
opal-sprockets (1.0.2)
|
22
|
+
opal (>= 1.0, < 2.0)
|
23
|
+
sprockets (~> 4.0)
|
26
24
|
tilt (>= 1.4)
|
27
|
-
parser (
|
28
|
-
ast (~> 2.
|
29
|
-
|
30
|
-
|
25
|
+
parser (3.0.2.0)
|
26
|
+
ast (~> 2.4.1)
|
27
|
+
puma (5.5.2)
|
28
|
+
nio4r (~> 2.0)
|
29
|
+
rack (2.2.3)
|
30
|
+
rack-protection (2.1.0)
|
31
31
|
rack
|
32
|
-
|
32
|
+
rake (13.0.6)
|
33
|
+
ruby2_keywords (0.0.5)
|
34
|
+
sinatra (2.1.0)
|
33
35
|
mustermann (~> 1.0)
|
34
|
-
rack (~> 2.
|
35
|
-
rack-protection (= 2.0
|
36
|
+
rack (~> 2.2)
|
37
|
+
rack-protection (= 2.1.0)
|
36
38
|
tilt (~> 2.0)
|
37
|
-
sinatra-contrib (2.0
|
38
|
-
backports (>= 2.8.2)
|
39
|
+
sinatra-contrib (2.1.0)
|
39
40
|
multi_json
|
40
41
|
mustermann (~> 1.0)
|
41
|
-
rack-protection (= 2.0
|
42
|
-
sinatra (= 2.0
|
43
|
-
tilt (
|
44
|
-
|
45
|
-
sprockets (3.7.2)
|
42
|
+
rack-protection (= 2.1.0)
|
43
|
+
sinatra (= 2.1.0)
|
44
|
+
tilt (~> 2.0)
|
45
|
+
sprockets (4.0.2)
|
46
46
|
concurrent-ruby (~> 1.0)
|
47
47
|
rack (> 1, < 3)
|
48
48
|
thor (0.20.3)
|
49
|
-
tilt (2.0.
|
49
|
+
tilt (2.0.10)
|
50
50
|
|
51
51
|
PLATFORMS
|
52
52
|
ruby
|
@@ -54,9 +54,11 @@ PLATFORMS
|
|
54
54
|
DEPENDENCIES
|
55
55
|
opal-sprockets
|
56
56
|
ovto!
|
57
|
+
puma
|
57
58
|
rack
|
59
|
+
rake
|
58
60
|
sinatra
|
59
61
|
sinatra-contrib
|
60
62
|
|
61
63
|
BUNDLED WITH
|
62
|
-
2.
|
64
|
+
2.2.22
|
data/examples/sinatra/app.rb
CHANGED
@@ -3,16 +3,13 @@ require 'sinatra/reloader'
|
|
3
3
|
require 'opal'
|
4
4
|
|
5
5
|
class SinatraApp < Sinatra::Base
|
6
|
-
# Proc to generate javascript include tag (see config.ru)
|
7
|
-
set :generate_javascript_include_tag, nil
|
8
|
-
|
9
6
|
configure(:development) do
|
10
7
|
register Sinatra::Reloader
|
11
8
|
also_reload "#{__dir__}/**/*.rb"
|
12
9
|
end
|
13
10
|
|
14
11
|
get '/' do
|
15
|
-
@js_tag =
|
12
|
+
@js_tag = $GENERATE_JAVASCRIPT_INCLUDE_TAG.call # Defined in config.ru
|
16
13
|
erb :index # Render views/index.erb
|
17
14
|
end
|
18
15
|
end
|
data/examples/sinatra/config.ru
CHANGED
@@ -2,20 +2,20 @@ require 'opal/sprockets'
|
|
2
2
|
require_relative './app.rb'
|
3
3
|
|
4
4
|
Opal.use_gem 'ovto'
|
5
|
-
|
5
|
+
opal_server = Opal::Sprockets::Server.new {|s|
|
6
6
|
s.append_path './ovto/'
|
7
7
|
s.main = 'app'
|
8
8
|
}
|
9
9
|
|
10
|
-
sprockets =
|
10
|
+
sprockets = opal_server.sprockets
|
11
11
|
prefix = '/assets'
|
12
12
|
|
13
13
|
map prefix do
|
14
14
|
run sprockets
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
::Opal::Sprockets.javascript_include_tag('app', sprockets: sprockets, prefix: prefix, debug: true)
|
20
|
-
}
|
17
|
+
$GENERATE_JAVASCRIPT_INCLUDE_TAG = ->{
|
18
|
+
::Opal::Sprockets.javascript_include_tag('app', sprockets: sprockets, prefix: prefix, debug: true)
|
21
19
|
}
|
20
|
+
|
21
|
+
run SinatraApp
|
@@ -1,27 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
ovto (0.
|
5
|
-
opal (
|
4
|
+
ovto (0.6.0)
|
5
|
+
opal (>= 0.11, < 2)
|
6
6
|
rack (~> 2.0)
|
7
7
|
thor (~> 0.20)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
ast (2.4.
|
13
|
-
hike (1.2.3)
|
12
|
+
ast (2.4.2)
|
14
13
|
ifchanged (1.0.1)
|
15
|
-
opal (
|
14
|
+
opal (1.2.0)
|
16
15
|
ast (>= 2.3.0)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rack (2.0.7)
|
23
|
-
rake (12.3.2)
|
24
|
-
sourcemap (0.1.1)
|
16
|
+
parser (~> 3.0)
|
17
|
+
parser (3.0.2.0)
|
18
|
+
ast (~> 2.4.1)
|
19
|
+
rack (2.2.3)
|
20
|
+
rake (13.0.6)
|
25
21
|
thor (0.20.3)
|
26
22
|
|
27
23
|
PLATFORMS
|
@@ -33,4 +29,4 @@ DEPENDENCIES
|
|
33
29
|
rake
|
34
30
|
|
35
31
|
BUNDLED WITH
|
36
|
-
2.
|
32
|
+
2.2.22
|
data/lib/ovto/actions.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
module Ovto
|
2
2
|
# Base class for ovto actions.
|
3
3
|
class Actions
|
4
|
+
# WiredActions must be set after initialization
|
5
|
+
# (this cannot be an argument of #initialize because Actions and
|
6
|
+
# WiredActions have references to each other)
|
4
7
|
attr_writer :wired_actions
|
5
8
|
|
6
9
|
def actions
|
@@ -10,5 +13,13 @@ module Ovto
|
|
10
13
|
def state
|
11
14
|
@wired_actions._app.state
|
12
15
|
end
|
16
|
+
|
17
|
+
def middleware_name
|
18
|
+
WiredActionSet::I_AM_APP_NOT_A_MIDDLEWARE
|
19
|
+
end
|
20
|
+
|
21
|
+
def middleware_path
|
22
|
+
[]
|
23
|
+
end
|
13
24
|
end
|
14
25
|
end
|
data/lib/ovto/app.rb
CHANGED
@@ -1,18 +1,37 @@
|
|
1
1
|
module Ovto
|
2
2
|
class App
|
3
|
+
# List of installed middleware classes
|
4
|
+
def self.middlewares
|
5
|
+
@middlewares ||= []
|
6
|
+
end
|
7
|
+
|
3
8
|
# Create an App and start it
|
4
9
|
def self.run(*args)
|
5
10
|
new.run(*args)
|
6
11
|
end
|
7
12
|
|
13
|
+
# Install a middleware
|
14
|
+
def self.use(middleware_class)
|
15
|
+
self.middlewares.push(middleware_class)
|
16
|
+
end
|
17
|
+
|
8
18
|
def initialize
|
9
|
-
|
10
|
-
|
19
|
+
app_state_class = self.class.const_get('State')
|
20
|
+
# Inject middleware states
|
21
|
+
app_state_class.item :_middlewares, default_proc: ->{
|
22
|
+
Ovto::Middleware.create_middleware_states_class(self.class.middlewares).new
|
23
|
+
}
|
24
|
+
@state = app_state_class.new
|
25
|
+
@wired_action_set = nil
|
26
|
+
@main_component = nil
|
11
27
|
end
|
12
28
|
attr_reader :state
|
13
29
|
|
30
|
+
# An instance of YourApp::MainComponent (mainly for testing)
|
31
|
+
attr_reader :main_component
|
32
|
+
|
14
33
|
def actions
|
15
|
-
@
|
34
|
+
@wired_action_set.app_wired_actions
|
16
35
|
end
|
17
36
|
|
18
37
|
# Internal use only
|
@@ -36,9 +55,9 @@ module Ovto
|
|
36
55
|
def _run(id: nil)
|
37
56
|
runtime = Ovto::Runtime.new(self)
|
38
57
|
actions = self.class.const_get('Actions').new
|
39
|
-
@
|
40
|
-
actions.wired_actions = @
|
41
|
-
|
58
|
+
@wired_action_set = WiredActionSet.new(self, actions, [], self.class.middlewares, runtime)
|
59
|
+
actions.wired_actions = @wired_action_set.app_wired_actions
|
60
|
+
@main_component = create_view(@wired_action_set)
|
42
61
|
if id
|
43
62
|
%x{
|
44
63
|
document.addEventListener('DOMContentLoaded', function(){
|
@@ -46,16 +65,16 @@ module Ovto
|
|
46
65
|
if (!container) {
|
47
66
|
throw "Ovto::App#run: tag with id='" + id + "' was not found";
|
48
67
|
}
|
49
|
-
#{start_application(runtime,
|
68
|
+
#{start_application(runtime, `container`)}
|
50
69
|
});
|
51
70
|
}
|
52
71
|
else
|
53
|
-
start_application(runtime,
|
72
|
+
start_application(runtime, nil)
|
54
73
|
end
|
55
74
|
end
|
56
75
|
|
57
76
|
# Instantiate MyApp::MainComponent
|
58
|
-
def create_view
|
77
|
+
def create_view(wired_action_set)
|
59
78
|
begin
|
60
79
|
main_component_class = self.class.const_get('MainComponent')
|
61
80
|
rescue NameError => orig_ex
|
@@ -68,12 +87,17 @@ module Ovto
|
|
68
87
|
"#{self.class}::View to #{self.class}::MainComponent"
|
69
88
|
end
|
70
89
|
end
|
71
|
-
return main_component_class.new(
|
90
|
+
return main_component_class.new(wired_action_set)
|
72
91
|
end
|
73
92
|
|
74
|
-
def start_application(runtime,
|
75
|
-
|
76
|
-
|
93
|
+
def start_application(runtime, container)
|
94
|
+
Ovto.log_error {
|
95
|
+
runtime.run(@main_component, container)
|
96
|
+
setup
|
97
|
+
self.class.middlewares.each do |m|
|
98
|
+
m._run_setup(@wired_action_set[m.name])
|
99
|
+
end
|
100
|
+
}
|
77
101
|
end
|
78
102
|
end
|
79
103
|
end
|
data/lib/ovto/component.rb
CHANGED
@@ -13,10 +13,18 @@ module Ovto
|
|
13
13
|
ret
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
# (internal) Defined for convenience
|
17
|
+
def self.middleware_name
|
18
|
+
WiredActionSet::I_AM_APP_NOT_A_MIDDLEWARE
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(wired_action_set, middleware_path=[])
|
22
|
+
@wired_action_set = wired_action_set || WiredActionSet.dummy
|
23
|
+
@middleware_path = middleware_path
|
18
24
|
# Initialize here for the unit tests
|
19
|
-
@
|
25
|
+
@vdom_stack = [[]]
|
26
|
+
@components = []
|
27
|
+
@components_index = 0
|
20
28
|
end
|
21
29
|
|
22
30
|
def render
|
@@ -24,7 +32,7 @@ module Ovto
|
|
24
32
|
end
|
25
33
|
|
26
34
|
def state
|
27
|
-
@
|
35
|
+
@wired_action_set.app.state
|
28
36
|
end
|
29
37
|
|
30
38
|
private
|
@@ -32,38 +40,38 @@ module Ovto
|
|
32
40
|
# Render entire MyApp::MainComponent
|
33
41
|
# Called from runtime.rb
|
34
42
|
def render_view(state)
|
35
|
-
do_render(
|
43
|
+
do_render({}, state)
|
36
44
|
end
|
37
45
|
|
38
|
-
|
46
|
+
# Call #render to generate VDom
|
47
|
+
def do_render(args, state, &block)
|
39
48
|
Ovto.debug_trace_log("rendering #{self}")
|
40
|
-
@
|
49
|
+
@vdom_stack = [[]]
|
50
|
+
@components_index = 0
|
41
51
|
@done_render = false
|
42
|
-
@current_state =
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
52
|
+
@current_state = state
|
53
|
+
rendered = Ovto.log_error {
|
54
|
+
Ovto.send_args_with_state(self, :render, args, state, &block)
|
55
|
+
}
|
56
|
+
case rendered
|
57
|
+
when String
|
58
|
+
return rendered
|
59
|
+
when Array
|
60
|
+
if rendered.length > 1
|
61
|
+
raise MoreThanOneNode
|
62
|
+
end
|
63
|
+
raise "rendered is empty" if rendered.length == 0
|
64
|
+
return rendered[0]
|
47
65
|
else
|
48
|
-
#
|
49
|
-
|
50
|
-
# Check it is empty (see https://github.com/opal/opal/issues/1872)
|
51
|
-
return args_wo_state.empty? ? render() : render(**args_wo_state)
|
66
|
+
console.error("#render returned unknown value", rendered)
|
67
|
+
raise "#render returned unknown value"
|
52
68
|
end
|
53
69
|
end
|
54
70
|
|
55
|
-
# Return true if the method accepts `state:` keyword
|
56
|
-
def accepts_state?(parameters)
|
57
|
-
parameters.each do |item|
|
58
|
-
return true if item == [:key, :state] ||
|
59
|
-
item == [:keyreq, :state] ||
|
60
|
-
item[0] == :keyrest
|
61
|
-
end
|
62
|
-
return false
|
63
|
-
end
|
64
|
-
|
65
71
|
def actions
|
66
|
-
@
|
72
|
+
return @middleware_path.inject(@wired_action_set){|wa_set, middleware_name|
|
73
|
+
wa_set[middleware_name]
|
74
|
+
}[WiredActionSet::THE_MIDDLEWARE_ITSELF]
|
67
75
|
end
|
68
76
|
|
69
77
|
# o 'div', 'Hello.'
|
@@ -76,31 +84,39 @@ module Ovto
|
|
76
84
|
# o 'h1', 'Hello.'
|
77
85
|
# end
|
78
86
|
# o 'div', `{nodeName: ....}` # Inject VDom spec directly
|
87
|
+
# o SubComponentClass
|
88
|
+
# o SubComponentClass do ... end # Ovto passes the block to SubComponent#render
|
79
89
|
def o(_tag_name, arg1=nil, arg2=nil, &block)
|
80
|
-
if native?(arg1)
|
90
|
+
if native?(arg1) # Embed VDom directly
|
81
91
|
attributes = {}
|
82
92
|
content = arg1
|
83
|
-
elsif arg1.is_a?(Hash)
|
93
|
+
elsif arg1.is_a?(Hash) # Has attributes
|
84
94
|
attributes = arg1
|
85
95
|
content = arg2
|
86
|
-
elsif arg2 == nil
|
96
|
+
elsif arg2 == nil # Has content instead of attributes, or both are nil
|
87
97
|
attributes = {}
|
88
98
|
content = arg1
|
89
99
|
else
|
90
100
|
raise ArgumentError
|
91
101
|
end
|
92
102
|
|
93
|
-
children = render_children(content, block)
|
94
103
|
case _tag_name
|
95
104
|
when Class
|
96
|
-
|
105
|
+
if content
|
106
|
+
raise ArgumentError, "use a block to pass content to sub component"
|
107
|
+
end
|
108
|
+
result = render_component(_tag_name, attributes, &block)
|
97
109
|
when 'text'
|
98
110
|
unless attributes.empty?
|
99
111
|
raise ArgumentError, "text cannot take attributes"
|
100
112
|
end
|
101
113
|
result = content
|
102
114
|
when String
|
115
|
+
children = render_children(content, block)
|
103
116
|
tag_name, base_attributes = *extract_attrs(_tag_name)
|
117
|
+
if tag_name == "textarea" && (content || block)
|
118
|
+
raise ArgumentError, "Use `value:` to specify content of a textarea"
|
119
|
+
end
|
104
120
|
# Ignore nil/false
|
105
121
|
more_attributes = attributes.reject{|k, v| !v}
|
106
122
|
result = render_tag(tag_name, merge_attrs(base_attributes, more_attributes), children)
|
@@ -108,23 +124,15 @@ module Ovto
|
|
108
124
|
raise TypeError, "tag_name must be a String or Component but got "+
|
109
125
|
Ovto.inspect(tag_name)
|
110
126
|
end
|
111
|
-
|
112
|
-
|
113
|
-
raise MoreThanOneNode, "#{self.class}#render must generate a single DOM node. Please wrap the tags with a 'div' or something."
|
114
|
-
end
|
115
|
-
@done_render = true
|
116
|
-
return result
|
117
|
-
else
|
118
|
-
@vdom_tree.last.push(result)
|
119
|
-
return @vdom_tree.last
|
120
|
-
end
|
127
|
+
@vdom_stack.last.push(result)
|
128
|
+
return @vdom_stack.last
|
121
129
|
end
|
122
130
|
|
123
131
|
def extract_attrs(tag_name)
|
124
132
|
case tag_name
|
125
|
-
when /^([^.#]*)\.([
|
133
|
+
when /^([^.#]*)\.([-\w]+)(\#([-\w]+))?/ # a.b#c
|
126
134
|
tag_name, class_name, id = ($1.empty? ? 'div' : $1), $2, $4
|
127
|
-
when /^([^.#]*)\#([
|
135
|
+
when /^([^.#]*)\#([-\w]+)(\.([-\w]+))?/ # a#b.c
|
128
136
|
tag_name, class_name, id = ($1.empty? ? 'div' : $1), $4, $2
|
129
137
|
else
|
130
138
|
class_name = id = nil
|
@@ -162,43 +170,111 @@ module Ovto
|
|
162
170
|
[content.to_s]
|
163
171
|
end
|
164
172
|
when block
|
165
|
-
|
166
|
-
block_value = block.call
|
167
|
-
results = @vdom_tree.pop
|
168
|
-
if results.length > 0 # 'o' was called at least once
|
169
|
-
results
|
170
|
-
elsif native?(block_value)
|
171
|
-
# Inject VDom tree written in JS object
|
172
|
-
# eg. Embed markdown
|
173
|
-
[block_value]
|
174
|
-
elsif block_value.is_a?(String)
|
175
|
-
# When 'o' is never called in the child block, use the last value
|
176
|
-
# eg.
|
177
|
-
# o 'span' do
|
178
|
-
# 'Hello' #=> This will be the content of the span tag
|
179
|
-
# end
|
180
|
-
[block_value]
|
181
|
-
else
|
182
|
-
# o 'div' do
|
183
|
-
# # When items is `[]`, 'o' is never called and `block_value` will be `[]`
|
184
|
-
# items.each{ o 'div', '...' }
|
185
|
-
# end
|
186
|
-
[]
|
187
|
-
end
|
173
|
+
render_block(block)
|
188
174
|
else
|
189
175
|
[]
|
190
176
|
end
|
191
177
|
end
|
192
178
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
|
179
|
+
def render_block(block)
|
180
|
+
@vdom_stack.push []
|
181
|
+
orig_depth = @vdom_stack.length
|
182
|
+
block_value = block.call
|
183
|
+
@vdom_stack = @vdom_stack.first(orig_depth)
|
184
|
+
results = @vdom_stack.pop
|
185
|
+
|
186
|
+
if results.length > 0 # 'o' was called at least once
|
187
|
+
results
|
188
|
+
elsif native?(block_value)
|
189
|
+
# Inject VDom tree written in JS object
|
190
|
+
# eg. Embed markdown
|
191
|
+
[block_value]
|
192
|
+
elsif block_value.is_a?(String)
|
193
|
+
# When 'o' is never called in the child block, use the last value
|
194
|
+
# eg.
|
195
|
+
# o 'span' do
|
196
|
+
# 'Hello' #=> This will be the content of the span tag
|
197
|
+
# end
|
198
|
+
[block_value]
|
199
|
+
elsif block_value.is_a?(Array)
|
200
|
+
# Case 1
|
201
|
+
# o "div", &block
|
202
|
+
# Case 2
|
203
|
+
# items = []
|
204
|
+
# o 'div' do items.each{ o ... } end # == o 'div' do [] end
|
205
|
+
block_value
|
206
|
+
else
|
207
|
+
console.error("Invalid block_value:", Ovto.inspect(block_value))
|
208
|
+
raise "Invalid block value"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Instantiate component and call its #render to get VDom
|
213
|
+
def render_component(comp_class, args, &block)
|
214
|
+
comp = new_component(comp_class)
|
215
|
+
orig_stack, @vdom_stack = @vdom_stack, [[]]
|
216
|
+
ret = comp.do_render(args, @current_state, &block)
|
217
|
+
@vdom_stack = orig_stack
|
218
|
+
ret
|
219
|
+
end
|
220
|
+
|
221
|
+
def new_component(comp_class)
|
222
|
+
comp = @components[@components_index]
|
223
|
+
if comp.is_a?(comp_class)
|
224
|
+
@components_index += 1
|
225
|
+
return comp
|
226
|
+
end
|
227
|
+
|
228
|
+
middleware_path = new_middleware_path(comp_class)
|
229
|
+
comp = @components[@components_index] = comp_class.new(@wired_action_set, middleware_path)
|
230
|
+
@components_index += 1
|
231
|
+
comp
|
232
|
+
end
|
233
|
+
|
234
|
+
# Make new middleware_path by adding comp_class
|
235
|
+
def new_middleware_path(comp_class)
|
236
|
+
mw_name = comp_class.middleware_name
|
237
|
+
if (idx = @middleware_path.index(mw_name))
|
238
|
+
# eg. suppose OvtoIde uses OvtoWindow
|
239
|
+
# class CompI < OvtoIde::Component
|
240
|
+
# def render
|
241
|
+
# o Window do
|
242
|
+
# o AnotherComponentOfOvtoIde
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
# end
|
246
|
+
# class Window < OvtoWindow::Component
|
247
|
+
# def render(&block)
|
248
|
+
# o ".window", &block
|
249
|
+
# end
|
250
|
+
# end
|
251
|
+
# Rendering order:
|
252
|
+
# 1. CompI (ovto_ide)
|
253
|
+
# 2. Window (ovto_window)
|
254
|
+
# 3. AnotherComponentOfOvtoIde (ovto_ide again)
|
255
|
+
@middleware_path[0..idx]
|
256
|
+
else
|
257
|
+
if comp_class.middleware_name == WiredActionSet::I_AM_APP_NOT_A_MIDDLEWARE
|
258
|
+
@middleware_path
|
259
|
+
else
|
260
|
+
@middleware_path + [comp_class.middleware_name]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
# TODO: it would be nice if we could raise an error when comp_class
|
264
|
+
# is invalid middleware (i.e. not use'd)
|
197
265
|
end
|
198
266
|
|
199
267
|
def render_tag(tag_name, attributes, children)
|
200
|
-
|
201
|
-
|
268
|
+
attributes_ = attributes.map{|k, v|
|
269
|
+
if k.start_with?("on")
|
270
|
+
# Inject log_error to event handlers
|
271
|
+
[k, ->(e){ Ovto.log_error{ v.call(e) }}]
|
272
|
+
else
|
273
|
+
[k, v]
|
274
|
+
end
|
275
|
+
}.to_h
|
276
|
+
js_attributes = Component.hash_to_js_obj(attributes_ || {})
|
277
|
+
if (style = attributes_['style'])
|
202
278
|
`js_attributes.style = #{Component.hash_to_js_obj(style)}`
|
203
279
|
end
|
204
280
|
children ||= `null`
|
data/lib/ovto/fetch.rb
CHANGED
@@ -7,7 +7,7 @@ module Ovto
|
|
7
7
|
# The server must respond a json text.
|
8
8
|
#
|
9
9
|
# Example:
|
10
|
-
# Ovto.fetch('/api/new_task', 'POST', {title: "do something"}){|json_data|
|
10
|
+
# Ovto.fetch('/api/new_task', 'POST', {title: "do something"}).then{|json_data|
|
11
11
|
# p json_data
|
12
12
|
# }.fail{|e| # Network error, 404 Not Found, JSON parse error, etc.
|
13
13
|
# p e
|