ovto 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.gitmodules +3 -0
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile +7 -0
  6. data/Gemfile.lock +57 -0
  7. data/LICENSE.txt +44 -0
  8. data/README.md +84 -0
  9. data/Rakefile +41 -0
  10. data/book/README.md +1 -0
  11. data/book/SUMMARY.md +13 -0
  12. data/book/api/actions.md +77 -0
  13. data/book/api/app.md +86 -0
  14. data/book/api/component.md +175 -0
  15. data/book/api/fetch.md +42 -0
  16. data/book/api/state.md +97 -0
  17. data/book/guides/debugging.md +23 -0
  18. data/book/guides/development.md +11 -0
  19. data/book/guides/tutorial.md +288 -0
  20. data/book/screenshot.png +0 -0
  21. data/docs/api/Ovto/Actions.html +135 -0
  22. data/docs/api/Ovto/App.html +531 -0
  23. data/docs/api/Ovto/Component/MoreThanOneNode.html +135 -0
  24. data/docs/api/Ovto/Component.html +350 -0
  25. data/docs/api/Ovto/Runtime.html +315 -0
  26. data/docs/api/Ovto/State/MissingValue.html +135 -0
  27. data/docs/api/Ovto/State/UnknownKey.html +135 -0
  28. data/docs/api/Ovto/State.html +699 -0
  29. data/docs/api/Ovto/WiredActions.html +343 -0
  30. data/docs/api/Ovto.html +319 -0
  31. data/docs/api/_index.html +229 -0
  32. data/docs/api/actions.html +398 -0
  33. data/docs/api/app.html +411 -0
  34. data/docs/api/class_list.html +51 -0
  35. data/docs/api/component.html +469 -0
  36. data/docs/api/css/common.css +1 -0
  37. data/docs/api/css/full_list.css +58 -0
  38. data/docs/api/css/style.css +499 -0
  39. data/docs/api/file.README.html +162 -0
  40. data/docs/api/file_list.html +56 -0
  41. data/docs/api/frames.html +17 -0
  42. data/docs/api/index.html +162 -0
  43. data/docs/api/js/app.js +248 -0
  44. data/docs/api/js/full_list.js +216 -0
  45. data/docs/api/js/jquery.js +4 -0
  46. data/docs/api/method_list.html +243 -0
  47. data/docs/api/state.html +430 -0
  48. data/docs/api/top-level-namespace.html +110 -0
  49. data/docs/gitbook/fonts/fontawesome/FontAwesome.otf +0 -0
  50. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot +0 -0
  51. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg +685 -0
  52. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf +0 -0
  53. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff +0 -0
  54. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 +0 -0
  55. data/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js +240 -0
  56. data/docs/gitbook/gitbook-plugin-fontsettings/website.css +291 -0
  57. data/docs/gitbook/gitbook-plugin-highlight/ebook.css +135 -0
  58. data/docs/gitbook/gitbook-plugin-highlight/website.css +434 -0
  59. data/docs/gitbook/gitbook-plugin-lunr/lunr.min.js +7 -0
  60. data/docs/gitbook/gitbook-plugin-lunr/search-lunr.js +59 -0
  61. data/docs/gitbook/gitbook-plugin-search/lunr.min.js +7 -0
  62. data/docs/gitbook/gitbook-plugin-search/search-engine.js +50 -0
  63. data/docs/gitbook/gitbook-plugin-search/search.css +35 -0
  64. data/docs/gitbook/gitbook-plugin-search/search.js +213 -0
  65. data/docs/gitbook/gitbook-plugin-sharing/buttons.js +90 -0
  66. data/docs/gitbook/gitbook.js +4 -0
  67. data/docs/gitbook/images/apple-touch-icon-precomposed-152.png +0 -0
  68. data/docs/gitbook/images/favicon.ico +0 -0
  69. data/docs/gitbook/style.css +9 -0
  70. data/docs/gitbook/theme.js +4 -0
  71. data/docs/guides/debugging.html +355 -0
  72. data/docs/guides/development.html +361 -0
  73. data/docs/guides/tutorial.html +571 -0
  74. data/docs/index.html +422 -0
  75. data/docs/screenshot.png +0 -0
  76. data/docs/search_index.json +1 -0
  77. data/example/sinatra/Gemfile +6 -0
  78. data/example/sinatra/Gemfile.lock +59 -0
  79. data/example/sinatra/README.md +21 -0
  80. data/example/sinatra/app.rb +18 -0
  81. data/example/sinatra/config.ru +30 -0
  82. data/example/sinatra/ovto/app.rb +171 -0
  83. data/example/sinatra/public/style.css +4 -0
  84. data/example/sinatra/public/todomvc-app-css_index.css +376 -0
  85. data/example/sinatra/public/todomvc-common_base.css +141 -0
  86. data/example/sinatra/views/index.erb +21 -0
  87. data/example/static/Gemfile +3 -0
  88. data/example/static/Gemfile.lock +30 -0
  89. data/example/static/README.md +10 -0
  90. data/example/static/Rakefile +4 -0
  91. data/example/static/app.js +24808 -0
  92. data/example/static/app.rb +43 -0
  93. data/example/static/index.html +11 -0
  94. data/lib/ovto/actions.rb +10 -0
  95. data/lib/ovto/app.rb +58 -0
  96. data/lib/ovto/component.rb +191 -0
  97. data/lib/ovto/fetch.rb +53 -0
  98. data/lib/ovto/runtime.rb +388 -0
  99. data/lib/ovto/state.rb +69 -0
  100. data/lib/ovto/version.rb +3 -0
  101. data/lib/ovto/wired_actions.rb +33 -0
  102. data/lib/ovto.rb +50 -0
  103. data/ovto.gemspec +22 -0
  104. data/screenshot.png +0 -0
  105. metadata +161 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3b5c09cd1d638ce54f7aea489477c059ac6935704ae284c5fdae49c7b45b7cdd
4
+ data.tar.gz: 2c83538298b032f336e035dc7ad48ea71114bb8f6ece4054accd9d3a593db5e6
5
+ SHA512:
6
+ metadata.gz: 316292a3ef746099b7c1eb5054d34830771b428122325972c91a5c1924123a544f5016e0eab4904120df56d16be5c70c50cb4c4dedd43a5e821eecd53c5aafbb
7
+ data.tar.gz: 478d66e8ce0c82f40b258c9490baa64b617b4d66f772931f7c1c5cbd52f2d0a70073d62c98ec0863954de6d6436725c4adad988d85d3ef98754fc00aef70f584
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /.yardoc
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "opal-rspec"]
2
+ path = opal-rspec
3
+ url = git://github.com/opal/opal-rspec.git
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ ## v0.2.0 2018-11-01
2
+
3
+ New features
4
+
5
+ - First gem release!
6
+ - `Ovto.fetch`
7
+ - Calling another action from actions
8
+ - Allow `o "div#id.class"` or `o "div.class#id"`
9
+ - Sub component can access the app state by adding `state:` keyword to `render`
10
+ - `App#actions`, `App#setup`
11
+ - `State#==`
12
+ - `console.log`
13
+
14
+ Fixes
15
+
16
+ - Skip rendering if app state is not changed by an action
17
+ - `o "div.main", class: 'hovered'` should yield `<div class='main hovered'>`
18
+ - Cannot pass falsy value when rendering a child component
19
+
20
+ ## v0.1.0 2018-06-01
21
+
22
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+ gem "opal"
3
+ gem 'opal-rspec', path: './opal-rspec'
4
+ gem 'opal-sprockets', github: 'opal/opal-sprockets'
5
+ gem 'rake'
6
+ gem 'ovto', path: '.'
7
+ gem 'yard'
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ GIT
2
+ remote: git://github.com/opal/opal-sprockets.git
3
+ revision: 8a8faeb1cfbc51cfa2e71ddb6cf219bd4a14f333
4
+ specs:
5
+ opal-sprockets (0.4.2.0.11.0.3.1)
6
+ opal (~> 0.11.0)
7
+ sprockets (~> 3.1)
8
+ tilt (>= 1.4)
9
+
10
+ PATH
11
+ remote: .
12
+ specs:
13
+ ovto (0.2.0)
14
+ opal (~> 0.11)
15
+
16
+ PATH
17
+ remote: opal-rspec
18
+ specs:
19
+ opal-rspec (0.7.0.dev)
20
+ opal (>= 0.10.0, < 0.12)
21
+ opal-sprockets
22
+
23
+ GEM
24
+ remote: https://rubygems.org/
25
+ specs:
26
+ ast (2.4.0)
27
+ concurrent-ruby (1.0.5)
28
+ hike (1.2.3)
29
+ opal (0.11.3)
30
+ ast (>= 2.3.0)
31
+ hike (~> 1.2)
32
+ parser (= 2.3.3.1)
33
+ sourcemap (~> 0.1.0)
34
+ parser (2.3.3.1)
35
+ ast (~> 2.2)
36
+ rack (2.0.5)
37
+ rake (12.3.1)
38
+ sourcemap (0.1.1)
39
+ sprockets (3.7.2)
40
+ concurrent-ruby (~> 1.0)
41
+ rack (> 1, < 3)
42
+ tilt (2.0.8)
43
+ yard (0.9.16)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ opal
50
+ opal-rspec!
51
+ opal-sprockets!
52
+ ovto!
53
+ rake
54
+ yard
55
+
56
+ BUNDLED WITH
57
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,44 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018-present Yutaka HARA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ ----
24
+ License of Hyperapp (in lib/ovto/runtime.rb):
25
+
26
+ Copyright © 2017-present [Jorge Bucaran](https://github.com/jorgebucaran)
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining a copy
29
+ of this software and associated documentation files (the "Software"), to deal
30
+ in the Software without restriction, including without limitation the rights
31
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32
+ copies of the Software, and to permit persons to whom the Software is
33
+ furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in all
36
+ copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Ovto
2
+
3
+ Client-side web framework for Opal, inspired by hyperapp
4
+
5
+ ## Documents
6
+
7
+ - [Book](https://yhara.github.io/ovto/)
8
+ - [yardoc](https://yhara.github.io/ovto/api/)
9
+
10
+ ## Example
11
+
12
+ ![screenshot](screenshot.png)
13
+
14
+ ```rb
15
+ require 'ovto'
16
+
17
+ class MyApp < Ovto::App
18
+ class State < Ovto::State
19
+ item :celsius, default: 0
20
+
21
+ def fahrenheit
22
+ (celsius * 9 / 5.0) + 32
23
+ end
24
+ end
25
+
26
+ class Actions < Ovto::Actions
27
+ def set_celsius(state:, value:)
28
+ return {celsius: value}
29
+ end
30
+
31
+ def set_fahrenheit(state:, value:)
32
+ new_celsius = (value - 32) * 5 / 9.0
33
+ return {celsius: new_celsius}
34
+ end
35
+ end
36
+
37
+ class View < Ovto::Component
38
+ def render(state:)
39
+ o 'div' do
40
+ o 'span', 'Celcius:'
41
+ o 'input', {
42
+ type: 'text',
43
+ onchange: ->(e){ actions.set_celsius(value: e.target.value.to_i) },
44
+ value: state.celsius
45
+ }
46
+ o 'span', 'Fahrenheit:'
47
+ o 'input', {
48
+ type: 'text',
49
+ onchange: ->(e){ actions.set_fahrenheit(value: e.target.value.to_i) },
50
+ value: state.fahrenheit
51
+ }
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ MyApp.run(id: 'ovto-view')
58
+ ```
59
+
60
+ See the [book](https://yhara.github.io/ovto/guides/tutorial.html) for details.
61
+
62
+ ## Setup
63
+
64
+ ### Static
65
+
66
+ [./example/static](./example/static) demonstrates how to convert Ovto app
67
+ into a .js file.
68
+
69
+ ### Ovto + Sinatra
70
+
71
+ [./example/sinatra](./example/sinatra) demonstrates how to serve Ovto app
72
+ from Sinatra.
73
+
74
+ ### Ovto + Rails
75
+
76
+ [yhara/vision](https://github.com/yhara/vision) is a working example of using Ovto with Rails 5.
77
+
78
+ ## Acknowledgements
79
+
80
+ - [hyperapp](https://github.com/hyperapp/hyperapp)
81
+
82
+ ## Contact
83
+
84
+ https://github.com/yhara/ovto/issues
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'opal/rspec/rake_task'
2
+ Opal.append_path "./opal"
3
+ Opal.append_path "./spec"
4
+ Opal.use_gem "ovto"
5
+
6
+ Opal::RSpec::RakeTask.new(:default) do |server, task|
7
+ task.pattern = 'spec/**/*_spec.rb'
8
+ end
9
+
10
+ namespace :docs do
11
+ desc "build docs"
12
+ task :build do
13
+ cd "book" do
14
+ sh "gitbook build . ../docs"
15
+ end
16
+ sh "yardoc -o docs/api"
17
+ end
18
+
19
+ desc "start gitbook server"
20
+ task :serve do
21
+ cd "book" do
22
+ sh "gitbook serve"
23
+ end
24
+ end
25
+ end
26
+
27
+ namespace :release do
28
+ task :prepare_commit do
29
+ sh "git ci -am v#{Ovto::VERSION}"
30
+ sh "git tag v#{Ovto::VERSION}"
31
+ end
32
+
33
+ task :push_commit do
34
+ sh "git push origin master --tags"
35
+ end
36
+
37
+ task :push_gem do
38
+ sh "gem build ovto"
39
+ sh "gem push ovto-#{Ovto::VERSION}.gem"
40
+ end
41
+ end
data/book/README.md ADDED
@@ -0,0 +1 @@
1
+ book/../README.md
data/book/SUMMARY.md ADDED
@@ -0,0 +1,13 @@
1
+ # Summary
2
+
3
+ - [Introduction](README.md)
4
+ - [Getting Started](guides/tutorial.md)
5
+ - API
6
+ - [Ovto::App](api/app.md)
7
+ - [Ovto::State](api/state.md)
8
+ - [Ovto::Actions](api/actions.md)
9
+ - [Ovto::Component](api/component.md)
10
+ - [Ovto.fetch](api/fetch.md)
11
+ - Guides
12
+ - [Debugging](guides/debugging.md)
13
+ - [Development](guides/development.md)
@@ -0,0 +1,77 @@
1
+ # Ovto::Actions
2
+
3
+ Actions are the only way to change the state. Actions must be defined as methods of
4
+ the `Actions` class. Here is some more conventions:
5
+
6
+ - You must use keyword arguments
7
+ - You must provide `state:` keyword to receive the app state
8
+ - You must return state updates as a Hash. It will be merged into the app state.
9
+
10
+ Example:
11
+
12
+ ```rb
13
+ require 'opal'
14
+ require 'ovto'
15
+
16
+ class MyApp < Ovto::App
17
+ class State < Ovto::State
18
+ item :count, 0
19
+ end
20
+
21
+ class Actions < Ovto::Actions
22
+ def increment(state:, by:)
23
+ return {count: state.count + by}
24
+ end
25
+ end
26
+
27
+ class View < Ovto::Component
28
+ def render(state:)
29
+ o 'span', state.count
30
+ o 'button', onclick: ->{ actions.increment(by: 1) }
31
+ end
32
+ end
33
+ end
34
+
35
+ MyApp.run(id: 'ovto-view')
36
+ ```
37
+
38
+ ## Calling actions
39
+
40
+ Actions can be called from components via `actions` method. This is an instance of
41
+ `Ovto::WiredActions` and has methods of the same name as your `Actions` class.
42
+
43
+ o 'button', onclick: ->{ actions.increment(by: 1) }
44
+
45
+ Arguments are almost the same as the original but you don't need to provide `state`;
46
+ it is automatically passed by `Ovto::WiredActions` class. It also updates the app
47
+ state with the return value of the action, and schedules rendering the view.
48
+
49
+ ## Skipping state update
50
+
51
+ An action may return `nil` when no app state changes are needed.
52
+
53
+ Promises are also special values which does not cause state changes (see the next section).
54
+
55
+ ## Async actions
56
+
57
+ When calling server apis, you cannot tell how the app state will change until the server responds.
58
+ In such cases, you can call another action via `actions` to tell Ovto to reflect the api result to the app state.
59
+
60
+ Example:
61
+
62
+ ```rb
63
+ class Actions < Ovto::Actions
64
+ def fetch_tasks(state:)
65
+ Ovto.fetch('/tasks.json').then {|tasks_json|
66
+ actions.receive_tasks(tasks: tasks_json)
67
+ }.fail {|e|
68
+ console.log("failed:", e)
69
+ }
70
+ end
71
+
72
+ def receive_tasks(state:, tasks_json:)
73
+ tasks = tasks_json.map{|item| Task.new(**item)}
74
+ return {tasks: tasks}
75
+ end
76
+ end
77
+ ```
data/book/api/app.md ADDED
@@ -0,0 +1,86 @@
1
+ # Ovto::App
2
+
3
+ First of all, you need to define a subclass of `Ovto::App` and define `class State`,
4
+ `class Actions` and `class View` in it.
5
+
6
+ ## Example
7
+
8
+ This is a smallest Ovto app.
9
+
10
+ ```rb
11
+ require 'opal'
12
+ require 'ovto'
13
+
14
+ class MyApp < Ovto::App
15
+ class State < Ovto::State
16
+ end
17
+
18
+ class Actions < Ovto::Actions
19
+ end
20
+
21
+ class View < Ovto::Component
22
+ def render(state:)
23
+ o 'input', type: 'button', value: 'Hello'
24
+ end
25
+ end
26
+ end
27
+
28
+ MyApp.run(id: 'ovto-view')
29
+ ```
30
+
31
+ It renders a button and does nothing else. Let's have some fun:
32
+
33
+ ```rb
34
+ require 'opal'
35
+ require 'ovto'
36
+
37
+ class MyApp < Ovto::App
38
+ COLORS = ["red", "blue", "green"]
39
+
40
+ class State < Ovto::State
41
+ item :color_idx, default: 0
42
+ end
43
+
44
+ class Actions < Ovto::Actions
45
+ def update_color(state:)
46
+ new_idx = (state.color_idx + 1) % COLORS.length
47
+ return {color_idx: new_idx}
48
+ end
49
+ end
50
+
51
+ class View < Ovto::Component
52
+ def render(state:)
53
+ o 'input', {
54
+ type: 'button',
55
+ value: 'Hello',
56
+ style: {background: COLORS[state.color_idx]},
57
+ onclick: ->{ actions.update_color },
58
+ }
59
+ end
60
+ end
61
+ end
62
+
63
+ MyApp.run(id: 'ovto-view')
64
+ ```
65
+
66
+ Here we added `color_idx` to app state and `update_color` action to change it.
67
+ The button is updated to have the color indicated by `color_idx` and
68
+ now has `onclick` event handler which executes the action.
69
+
70
+ ## Calling actions on startup
71
+
72
+ To invoke certain actions on app startup, define `MyApp#setup` and use `MyApp#actions`.
73
+
74
+ Example:
75
+
76
+ ```rb
77
+ class MyApp < Ovto::App
78
+ def setup
79
+ actions.fetch_data()
80
+ end
81
+
82
+ ...
83
+ end
84
+
85
+ MyApp.run(id: 'ovto-view')
86
+ ```
@@ -0,0 +1,175 @@
1
+ # Ovto::Component
2
+
3
+ Your app must have a `View` class, a subclass of `Ovto::Component`.
4
+
5
+ ## 'render' method
6
+
7
+ `render` is the only method you need to define in the `View` class.
8
+ It must take the global app state as a keyword argument `state:`.
9
+
10
+ ```rb
11
+ class View < Ovto::Component
12
+ def render(state:)
13
+ o 'div' do
14
+ o 'h1', 'Your todos'
15
+ o 'ul' do
16
+ state.todos.each do |todo|
17
+ o 'li', todo.title
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ ### MoreThanOneNode error
26
+
27
+ If you missed the surrounding 'div' tag, Ovto raises an `MoreThanOneNode` error. `render` must create a single DOM node.
28
+
29
+ ```rb
30
+ def render(state:)
31
+ o 'h1', 'Your todos'
32
+ o 'ul' do
33
+ state.todos.each do |todo|
34
+ o 'li', todo.title
35
+ end
36
+ end
37
+ end
38
+
39
+ #=> $MoreThanOneNode {name: "MoreThanOneNode", message: "MyApp::View#render must generate a single DOM node. Please wrap the tags with a 'div' or something.", stack: "MoreThanOneNode: MyApp::View#render must generate …opbox/proj/ovto/example/tutorial/app.js:22887:18)"}
40
+ ```
41
+
42
+ ## The 'o' method
43
+
44
+ <a name='the-o-method' />
45
+
46
+ `Ovto::Component#o` describes your app's view. For example:
47
+
48
+ ```rb
49
+ o 'div'
50
+ #=> <div></div>
51
+
52
+ o 'div', 'Hello.'
53
+ #=> <div>Hello.</div>
54
+ ```
55
+
56
+ You can pass attributes with a Hash.
57
+
58
+ ```rb
59
+ o 'div', class: 'main', 'Hello.'
60
+ #=> <div class='main'>Hello.</div>
61
+ ```
62
+
63
+ There are shorthand notations for classes and ids.
64
+
65
+ ```rb
66
+ o 'div.main'
67
+ #=> <div class='main'>Hello.</div>
68
+
69
+ o 'div#main'
70
+ #=> <div id='main'>Hello.</div>
71
+ ```
72
+
73
+ You can also give a block to specify its content.
74
+
75
+ ```rb
76
+ o 'div' do
77
+ 'Hello.'
78
+ end
79
+ #=> <div>Hello.</div>
80
+
81
+ o 'div' do
82
+ o 'h1', 'Hello.'
83
+ end
84
+ #=> <div><h1>Hello.</h1></div>
85
+ ```
86
+
87
+ ### Special attribute: `style`
88
+
89
+ <a name='special-attributes' />
90
+
91
+ There are some special keys for the attributes Hash. `style:` key takes a hash as
92
+ its value and specifies styles of the tag.
93
+
94
+ ```rb
95
+ o 'div', style: {color: 'red'}, 'Hello.'
96
+ #=> <div style='color: red;'>Hello.</div>
97
+ ```
98
+
99
+ ### Special attribute: `onxx`
100
+
101
+ An attribute starts with `"on"` specifies an event handler.
102
+
103
+ For example:
104
+
105
+ ```rb
106
+ o 'input', {
107
+ type: 'button',
108
+ onclick: ->(e){ p e.target.value },
109
+ value: 'Hello.'
110
+ }
111
+ ```
112
+
113
+ The argument `e` is an instance of Opal::Native and wraps the JavaScript event object.
114
+ You can get the input value with `e.target.value` here.
115
+
116
+ #### Lifecycle events
117
+
118
+ There are special events `oncreate`, `onupdate`, `onremove`, `ondestroy`.
119
+
120
+ https://github.com/hyperapp/hyperapp#lifecycle-events
121
+
122
+ ### Special attribute: `key`
123
+
124
+ https://github.com/hyperapp/hyperapp#keys
125
+
126
+ ## Sub components
127
+
128
+ `o` can take another component class to render.
129
+
130
+ ```rb
131
+ # Sub component
132
+ class TodoList < Ovto::Component
133
+ def render(todos:)
134
+ o 'ul' do
135
+ todos.each do |todo|
136
+ o 'li', todo.title
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Main View class
143
+ class View < Ovto::Component
144
+ def render(state:)
145
+ o 'div' do
146
+ o 'h1', 'Your todos'
147
+ o TodoList, todos: state.todos
148
+ end
149
+ end
150
+ end
151
+ ```
152
+
153
+ For sub components, the `state:` keyword of `render` method is optional.
154
+
155
+ ## Text node
156
+
157
+ Sometimes you may want to create a text node.
158
+
159
+ ```rb
160
+ #=> <div>Age: <span class='age'>12</a></div>
161
+ # ~~~~~
162
+ # |
163
+ # +--Raw text (not enclosed by an inner tag)
164
+ ```
165
+
166
+ `o` generates a text node when `'text'` is specified as tag name. The above
167
+ HTML could be described like this.
168
+
169
+ ```rb
170
+ o 'div' do
171
+ o 'text', 'Age:'
172
+ o 'span', '12'
173
+ end
174
+ ```
175
+
data/book/api/fetch.md ADDED
@@ -0,0 +1,42 @@
1
+ # Ovto.fetch
2
+
3
+ Ovto provides wrapper of [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch), for convenience of calling typical server-side APIs (eg. those generated by `rails scaffold` command.)
4
+
5
+ `Ovto.fetch` returns Opal's Promise object that calls the API with the specified parameters.
6
+
7
+ ## Examples
8
+
9
+ GET
10
+
11
+ ```rb
12
+ Ovto.fetch('/api/tasks'){|json_data|
13
+ p json_data
14
+ }.fail{|e| # Network error, 404 Not Found, JSON parse error, etc.
15
+ p e
16
+ }
17
+ ```
18
+
19
+ POST
20
+
21
+ ```rb
22
+ Ovto.fetch('/api/new_task', 'POST', {title: "do something"}){|json_data|
23
+ p json_data
24
+ }.fail{|e| # Network error, 404 Not Found, JSON parse error, etc.
25
+ p e
26
+ }
27
+
28
+ ```
29
+
30
+ PUT
31
+
32
+ ```rb
33
+ Ovto.fetch('/api/tasks/1', 'PUT', {title: "do something"}){|json_data|
34
+ p json_data
35
+ }.fail{|e| # Network error, 404 Not Found, JSON parse error, etc.
36
+ p e
37
+ }
38
+ ```
39
+
40
+ ## CSRF tokens
41
+
42
+ You don't need to care about CSRF tokens if the server is a Rails app because `Ovto.fetch` automatically send `X-CSRF-Token` header if the page contains a meta tag like `<meta name='csrf-token' content='....' />`.