ovto 0.2.0

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.
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
data/book/api/state.md ADDED
@@ -0,0 +1,97 @@
1
+ # Ovto::State
2
+
3
+ `Ovto::State` is like a hash, but members are accessible with name rather than `[]`.
4
+
5
+ ## Example
6
+
7
+ ```rb
8
+ class State < Ovto::State
9
+ item :foo
10
+ item :bar
11
+ end
12
+
13
+ state = State.new(foo: 1, bar: 2)
14
+ state.foo #=> 1
15
+ state.bar #=> 2
16
+ ```
17
+
18
+ ## Default value
19
+
20
+
21
+ ```rb
22
+ class State < Ovto::State
23
+ item :foo, default: 1
24
+ item :bar, default: 2
25
+ end
26
+
27
+ state = State.new
28
+ state.foo #=> 1
29
+ state.bar #=> 2
30
+ ```
31
+
32
+ ## Immutable
33
+
34
+ State objects are immutable. i.e. you cannot update value of a key. Instead, use `State#merge`.
35
+
36
+ ```rb
37
+ state = State.new(foo: 1, bar: 2)
38
+ new_state = state.merge(bar: 3)
39
+ new_state.foo #=> 1
40
+ new_state.bar #=> 3
41
+ ```
42
+
43
+ ## Nesting state
44
+
45
+ For practical apps, you can nest State like this.
46
+
47
+ ```rb
48
+ class Book < Ovto::State
49
+ item :title
50
+ item :author
51
+ end
52
+
53
+ class State < Ovto::State
54
+ item :books, []
55
+ end
56
+
57
+ book = Book.new('Hello world', 'taro')
58
+ state = State.new(books: [book])
59
+ ```
60
+
61
+ ## Defining instance methods of state
62
+
63
+ You can define instance methods of state.
64
+
65
+ ```rb
66
+ class Book < Ovto::State
67
+ item :title
68
+ item :author
69
+
70
+ def to_text
71
+ "#{self.title} (#{self.author})"
72
+ end
73
+ end
74
+
75
+ book = Book.new('Hello world', 'taro')
76
+ book.to_text #=> "Hello world (taro)"
77
+ ```
78
+
79
+ ## Defining class methods of state
80
+
81
+ Ovto does not have a class like `StateList`. Just use Array to represent a list of state.
82
+
83
+ You can define class methods to manipulate a list of state.
84
+
85
+ ```rb
86
+ class Book < Ovto::State
87
+ item :title
88
+ item :author
89
+
90
+ def self.of_author(books, author)
91
+ books.select{|x| x.author == author}
92
+ end
93
+ end
94
+
95
+ # Example
96
+ taro_books = Book.of_author(books, "taro")
97
+ ```
@@ -0,0 +1,23 @@
1
+ # Debugging Ovto app
2
+
3
+ ## console.log
4
+
5
+ In an Ovto app, you can print any object to developer console by `console.log`
6
+ like in JavaScript.
7
+
8
+ ```rb
9
+ console.log(state: State.new)
10
+ ```
11
+
12
+ This is mostly equal to `p state: State.new` but `console.log` supports
13
+ JavaScript objects too.
14
+
15
+ (Note: this is not an official feature of Opal. You can do this setup by this:)
16
+
17
+ ```rb
18
+ require 'console'; def console; $console; end
19
+ ```
20
+
21
+ ## ovto-debug
22
+
23
+ If the page has a tag with `id='ovto-debug'`, exception is shown in the tag.
@@ -0,0 +1,11 @@
1
+ # Development notes
2
+
3
+ ## How to run unit test
4
+
5
+ 1. git clone
6
+ 2. bundle install
7
+ 3. bundle exec rake
8
+
9
+ ## How to rebuild docs
10
+
11
+ `bundle exec doc:build`
@@ -0,0 +1,288 @@
1
+ # Getting Started
2
+
3
+ This is a tutorial of making an Ovto app. We create a static app (.html + .js) here,
4
+ but you can embed Ovto apps into a Rails or Sinatra app (See `./example/*`).
5
+
6
+ This is the final Ruby code.
7
+
8
+ ```
9
+ require 'ovto'
10
+
11
+ class MyApp < Ovto::App
12
+ class State < Ovto::State
13
+ item :celsius, default: 0
14
+
15
+ def fahrenheit
16
+ (celsius * 9 / 5.0) + 32
17
+ end
18
+ end
19
+
20
+ class Actions < Ovto::Actions
21
+ def set_celsius(state:, value:)
22
+ return {celsius: value}
23
+ end
24
+
25
+ def set_fahrenheit(state:, value:)
26
+ new_celsius = (value - 32) * 5 / 9.0
27
+ return {celsius: new_celsius}
28
+ end
29
+ end
30
+
31
+ class View < Ovto::Component
32
+ def render(state:)
33
+ o 'div' do
34
+ o 'span', 'Celcius:'
35
+ o 'input', {
36
+ type: 'text',
37
+ onchange: ->(e){ actions.set_celsius(value: e.target.value.to_i) },
38
+ value: state.celsius
39
+ }
40
+ o 'span', 'Fahrenheit:'
41
+ o 'input', {
42
+ type: 'text',
43
+ onchange: ->(e){ actions.set_fahrenheit(value: e.target.value.to_i) },
44
+ value: state.fahrenheit
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ MyApp.run(id: 'ovto-view')
52
+ ```
53
+
54
+ Let's take a look step-by-step.
55
+
56
+ ## Prerequisites
57
+
58
+ - Ruby
59
+ - Bundler (`gem install bundler`)
60
+
61
+ ## Setup
62
+
63
+ Make a Gemfile:
64
+
65
+ ```
66
+ source "https://rubygems.org"
67
+ gem "ovto", github: 'yhara/ovto' # Use git master because ovto gem is not released yet
68
+ gem 'rake'
69
+ ```
70
+
71
+ Run `bundle install`.
72
+
73
+ ## HTML
74
+
75
+ Make an index.html:
76
+
77
+ ```html
78
+ <!doctype html>
79
+ <html>
80
+ <head>
81
+ <meta charset="utf-8">
82
+ <script type='text/javascript' src='app.js'></script>
83
+ </head>
84
+ <body>
85
+ <div id='ovto-view'></div>
86
+ <div id='ovto-debug'></div>
87
+ </body>
88
+ </html>
89
+ ```
90
+
91
+ ## Write code
92
+
93
+ app.rb:
94
+
95
+ ```rb
96
+ require 'ovto'
97
+
98
+ class MyApp < Ovto::App
99
+ class State < Ovto::State
100
+ end
101
+
102
+ class Actions < Ovto::Actions
103
+ end
104
+
105
+ class View < Ovto::Component
106
+ def render(state:) # Don't miss the `:`. This is not a typo but
107
+ o 'div' do # a "mandatory keyword argument".
108
+ o 'h1', "HELLO" # All of the Ovto methods take keyword arguments.
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ MyApp.run(id: 'ovto-view')
115
+ ```
116
+
117
+ - The name `MyApp` is arbitrary.
118
+ - The id `ovto-view` corresponds to the `div` tag in `index.html`.
119
+
120
+ ## Compile
121
+
122
+ Generate app.js from app.rb.
123
+
124
+ ```
125
+ $ bundle exec opal -c -g ovto app.rb > app.js
126
+ ```
127
+
128
+ (Compile will fail if there is a syntax error in your `app.rb`.)
129
+
130
+ Now you can run your app by opening `index.html` in your browser.
131
+
132
+ ## Trouble shooting
133
+
134
+ If you see HELLO, the setup is done. Otherwise, check the developer console
135
+ and you should see some error messages there.
136
+
137
+ For example if you misspelled `class State` to `class Stat`, you will see:
138
+
139
+ ```
140
+ app.js:5022 Uncaught $NameError {name: "State", message: "uninitialized constant MyApp::State", stack: "State: uninitialized constant MyApp::State"}
141
+ ```
142
+
143
+ because an Ovto app must have a `State` class in its namespace.
144
+
145
+ ## (Tips: auto-compile)
146
+
147
+ If you get tired to run `bundle exec opal` manually, try `ifchanged` gem:
148
+
149
+ 1. Add `gem "ifchanged"` to Gemfile
150
+ 1. `bundle install`
151
+ 1. `bundle exec ifchanged ./app.rb --do 'bundle exec opal -c -g ovto app.rb > app.js'`
152
+
153
+ Now you just edit and save `app.rb` and it runs `opal -c` for you.
154
+
155
+ ## Add some state
156
+
157
+ In this tutorial, we make an app that convers Celsius and Fahrenheit degrees to
158
+ each other. First, add an item to `MyApp::State`.
159
+
160
+ ```rb
161
+ class State < Ovto::State
162
+ item :celsius, default: 0
163
+ end
164
+ ```
165
+
166
+ Now an item `celsius` is added to the global app state. Its value is `0` when
167
+ the app starts. You can read this value by `state.celsius`. Let's display the
168
+ value with `MyApp::View`.
169
+
170
+ ```rb
171
+ class View < Ovto::Component
172
+ def render(state:)
173
+ o 'div' do
174
+ o 'span', 'Celcius:'
175
+ o 'input', type: 'text', value: state.celsius
176
+ end
177
+ end
178
+ end
179
+ ```
180
+
181
+ Now you should see `Celsius: [0 ]` in the browser.
182
+
183
+ ## Add a method to State
184
+
185
+ Next, we want to know what degree is it in Fahrenheit. Let's add a method to
186
+ convert.
187
+
188
+ ```rb
189
+ class State < Ovto::State
190
+ item :celsius, default: 0
191
+
192
+ def fahrenheit
193
+ (celsius * 9 / 5.0) + 32
194
+ end
195
+ end
196
+ ```
197
+
198
+ Now you can know the value by `state.fahrenheit`. Update the `View` to show the value too.
199
+
200
+ ```
201
+ class View < Ovto::Component
202
+ def render(state:)
203
+ o 'div' do
204
+ o 'span', 'Celcius:'
205
+ o 'input', type: 'text', value: state.celsius
206
+ o 'span', 'Fahrenheit:'
207
+ o 'input', type: 'text', value: state.fahrenheit
208
+ end
209
+ end
210
+ end
211
+ ```
212
+
213
+ ## Add an action
214
+
215
+ Now we know 0 degrees Celsius is 32 degrees Fahrenheit. But how about 10 degrees or
216
+ 100 degrees Celsius? Let's update the app to we can specify a Celsius value.
217
+
218
+ You may think that you can change the value with `state.celsius = 100`, but this is
219
+ prohibited. In Ovto, you can only modify app state with Actions.
220
+
221
+ Our first action looks like this. An action is a method defined in `MyApp::Actions`.
222
+ It takes an old state (and its own parameters) and returns a Hash that describes
223
+ the updates to the state. This return value is `merge`d into the global app state.
224
+
225
+ ```rb
226
+ class Actions < Ovto::Actions
227
+ def set_celsius(state:, value:)
228
+ return {celsius: value}
229
+ end
230
+ end
231
+ ```
232
+
233
+ This action can be called by `actions.set_celsius` from the View. Replace the
234
+ first input tag with this:
235
+
236
+ ```rb
237
+ o 'input', {
238
+ type: 'text',
239
+ onchange: ->(e){ actions.set_celsius(value: e.target.value.to_i) },
240
+ value: state.celsius
241
+ }
242
+ ```
243
+
244
+ `onchange:` is a special attribute that takes an event handler as its value.
245
+ The argument `e` is an instance of `Opal::Native` and wraps the event object of
246
+ JavaScript. In this case you can get the input string by `e.target.value`.
247
+
248
+ Now reload your browser and input `100` to the left input box. Next, press Tab key
249
+ (or click somewhere in the page) to commit the value. Then you should see `212`
250
+ in the right input box. 100 degrees Celsius is 212 degrees Fahrenheit!
251
+
252
+ ## What has happend
253
+
254
+ In case you are curious, here is what happens when you give 100 to the input box.
255
+
256
+ 1. JavaScript's `onchange` event is executed.
257
+ 1. Ovto calls the event handler.
258
+ 1. It calls `actions.set_celsius`. `actions` is an instance of `Ovto::WiredActions`.
259
+ It is a proxy to the `MyApp::Actions`. It has the same methods as those in
260
+ `MyApp::Actions` but does some more:
261
+ - It passes `state` to the user-defined action.
262
+ - It merges the result to the global app state.
263
+ - It schedules re-rendering the view to represent the new state.
264
+
265
+ ## Reverse conversion
266
+
267
+ It is easy to update the app to support Fahrenheit-to-Celsius conversion.
268
+ The second input should be updated to:
269
+
270
+ ```rb
271
+ o 'input', {
272
+ type: 'text',
273
+ onchange: ->(e){ actions.set_fahrenheit(value: e.target.value.to_i) },
274
+ value: state.fahrenheit
275
+ }
276
+ ```
277
+
278
+ Then add an action `set_fahrenheit` to `MyApp::Actions`. This action convers the
279
+ Fahrenheit degree into Celsius and set it to the global state.
280
+
281
+ ```rb
282
+ def set_fahrenheit(state:, value:)
283
+ new_celsius = (value - 32) * 5 / 9.0
284
+ return {celsius: new_celsius}
285
+ end
286
+ ```
287
+
288
+ Now your app should react to the change of the Fahrenheit value too.
Binary file
@@ -0,0 +1,135 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Class: Ovto::Actions
8
+
9
+ &mdash; Documentation by YARD 0.9.12
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="../css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="../css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "Ovto::Actions";
19
+ relpath = '../';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="../js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="../js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="../class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="../_index.html">Index (A)</a> &raquo;
40
+ <span class='title'><span class='object_link'><a href="../Ovto.html" title="Ovto (module)">Ovto</a></span></span>
41
+ &raquo;
42
+ <span class="title">Actions</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="../class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Class: Ovto::Actions
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+ <dl>
70
+ <dt>Inherits:</dt>
71
+ <dd>
72
+ <span class="inheritName">Object</span>
73
+
74
+ <ul class="fullTree">
75
+ <li>Object</li>
76
+
77
+ <li class="next">Ovto::Actions</li>
78
+
79
+ </ul>
80
+ <a href="#" class="inheritanceTree">show all</a>
81
+
82
+ </dd>
83
+ </dl>
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+ <dl>
96
+ <dt>Defined in:</dt>
97
+ <dd>lib/ovto/actions.rb</dd>
98
+ </dl>
99
+
100
+ </div>
101
+
102
+ <h2>Overview</h2><div class="docstring">
103
+ <div class="discussion">
104
+
105
+ <p>Base class for ovto actions.</p>
106
+
107
+ <p>Currently this class is empty; exists for consistency and future
108
+ extension.</p>
109
+
110
+
111
+ </div>
112
+ </div>
113
+ <div class="tags">
114
+
115
+
116
+ </div>
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+ </div>
126
+
127
+ <div id="footer">
128
+ Generated on Fri Jun 1 21:07:20 2018 by
129
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
130
+ 0.9.12 (ruby-2.4.2).
131
+ </div>
132
+
133
+ </div>
134
+ </body>
135
+ </html>