snabberb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +37 -0
- data/.travis.yml +7 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +167 -0
- data/Rakefile +10 -0
- data/examples/rack/Gemfile +8 -0
- data/examples/rack/Gemfile.lock +31 -0
- data/examples/rack/README.md +13 -0
- data/examples/rack/app/application.rb +68 -0
- data/examples/rack/config.ru +18 -0
- data/examples/rack/index.html.erb +11 -0
- data/lib/snabberb.rb +6 -0
- data/lib/snabberb/version.rb +5 -0
- data/opal/snabberb.rb +17 -0
- data/opal/snabberb/component.rb +148 -0
- data/opal/vendor/snabbdom-attributes.js +71 -0
- data/opal/vendor/snabbdom-class.js +29 -0
- data/opal/vendor/snabbdom-eventlisteners.js +99 -0
- data/opal/vendor/snabbdom-props.js +30 -0
- data/opal/vendor/snabbdom-style.js +90 -0
- data/opal/vendor/snabbdom-to-html.js +4960 -0
- data/opal/vendor/snabbdom.js +506 -0
- data/opal/vendor/tovnode.js +126 -0
- data/snabberb.gemspec +37 -0
- metadata +173 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: adebae58664968503236700933371f7d76003468056be676156f39c268edb88c
|
4
|
+
data.tar.gz: 41124f485907d7a84e537191ac8fad844f21beb009ddf7a1979a3d44e1b33522
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5038c8f60b03ea4bf7e2d3f636007ae4f393ced975fef43aa75bcc0abd6e97e530320dea6787e952a97d7bea238057b55927fd191044461f20533b23c06a93cc
|
7
|
+
data.tar.gz: 0c4f20f341244f14e64c88828d194f4ae582394910e65a10f63da781d229e721f7fbbf49e8d3f23d0d5ec4df52fae474c27f35d1d9f994b2cb874007afff700f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Layout/IndentFirstArrayElement:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Metrics/AbcSize:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Enabled: False
|
9
|
+
Metrics/LineLength:
|
10
|
+
Max: 120
|
11
|
+
|
12
|
+
Metrics/MethodLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Style/ClassVars:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/CommandLiteral:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/Documentation:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/EmptyCaseCondition:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/TrailingCommaInArguments:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/TrailingCommaInArrayLiteral:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/TrailingCommaInHashLiteral:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
AllCops:
|
37
|
+
TargetRubyVersion: 2.6
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Tobias Mao
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# Snabberb
|
2
|
+
|
3
|
+
Snabberb is a simple Ruby view framework built on [Opal](https://github.com/opal/opal) and [Snabbdom](https://github.com/snabbdom/snabbdom).
|
4
|
+
|
5
|
+
You can write reactive views in plain Ruby that compile to efficient Javascript.
|
6
|
+
|
7
|
+
## Inline Example
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'opal'
|
11
|
+
require 'snabberb'
|
12
|
+
|
13
|
+
class TextBox < Snabberb::Component
|
14
|
+
needs :text
|
15
|
+
needs :selected, default: false, store: true
|
16
|
+
|
17
|
+
def render
|
18
|
+
onclick = lambda do
|
19
|
+
store(:selected, !@selected)
|
20
|
+
end
|
21
|
+
|
22
|
+
style = {
|
23
|
+
cursor: 'pointer',
|
24
|
+
border: 'solid 1px rgba(0,0,0,0.2)',
|
25
|
+
}
|
26
|
+
|
27
|
+
style['background-color'] = 'lightblue' if @selected
|
28
|
+
|
29
|
+
h(:div, { style: style, on: { click: onclick } }, [
|
30
|
+
h(:div, @text)
|
31
|
+
])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Assuming you have a DOM element with ID=app
|
37
|
+
TextBox.attach('app', text: 'hello world')
|
38
|
+
|
39
|
+
# Or you can get the HTML string for isomorphic applications
|
40
|
+
TextBox.html(text: 'hello world')
|
41
|
+
```
|
42
|
+
|
43
|
+
## Examples
|
44
|
+
[Rack App](examples/rack)
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Creating DOM Elements With h
|
49
|
+
|
50
|
+
Subclass Snabberb::Component and override #render to build divs using \#h.
|
51
|
+
|
52
|
+
Render should only return one root element.
|
53
|
+
|
54
|
+
\#h takes either a DOM symbol (:div, :span, :a, ...) or another Snabberb::Component class.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
...
|
58
|
+
class DomExample < Snabberb::Component
|
59
|
+
def render
|
60
|
+
h(:div)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ComponentExample < Snabberb::Component
|
65
|
+
def render
|
66
|
+
h(OtherComponent)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Like Snabbdom, \#h with DOM elements can take props which take the form of a dict.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
...
|
75
|
+
class PropsExample < Snabberb::Component
|
76
|
+
def render
|
77
|
+
h(:div, { style: { display: 'inline-block' }, class: { selected: true } })
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
Components do not take props, instead they take [needs](#Needs) which are dependent arguments.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
...
|
86
|
+
class PassingNeedsExample < Snabberb::Component
|
87
|
+
def render
|
88
|
+
h(ChildComponent, need1: 1, need2: 2)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
\#h can also be nested with a child or multiple children.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
...
|
97
|
+
class NestedExample < Snabberb::Component
|
98
|
+
def render
|
99
|
+
h(:div, [
|
100
|
+
h(ChildComponent, need1: 1, need2: 2),
|
101
|
+
h(:div, { style: { width: '100px' } }, [
|
102
|
+
h(:div, 'hello'),
|
103
|
+
])
|
104
|
+
](
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
### Needs
|
110
|
+
|
111
|
+
Components can define needs which allow parent components to pass down arguments. They can also be stateful which allows changes to propogate easily throughout the application.
|
112
|
+
|
113
|
+
Needs are by default required. They can be set with default values. Needs are accesible with instance variables that are automatically set.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
...
|
117
|
+
class NeedsExample < Snabberb::Component
|
118
|
+
needs :name
|
119
|
+
needs :value, default: 0, store: true
|
120
|
+
|
121
|
+
def render
|
122
|
+
onclick = lambda do
|
123
|
+
store(:value, @value + 1)
|
124
|
+
end
|
125
|
+
|
126
|
+
h(:div, [
|
127
|
+
h(:div, @name),
|
128
|
+
h(:div, { on: { click: onclick} }, @value),
|
129
|
+
])
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
When simple state changes must be tracked, a need can define store: true. This will use the stored value of this key which is set on the root node.
|
135
|
+
The precedence of need values is stored > passed needs > default value.
|
136
|
+
|
137
|
+
Needs can be set with #store which will trigger a view update. Snabberb uses Snabbdom to update the DOM, so only the differences in the DOM are changed.
|
138
|
+
|
139
|
+
## Installation
|
140
|
+
|
141
|
+
Add this line to your application's Gemfile:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
gem 'snabberb'
|
145
|
+
```
|
146
|
+
|
147
|
+
And then execute:
|
148
|
+
|
149
|
+
$ bundle
|
150
|
+
|
151
|
+
Or install it yourself as:
|
152
|
+
|
153
|
+
$ gem install snabberb
|
154
|
+
|
155
|
+
## Development
|
156
|
+
|
157
|
+
```
|
158
|
+
bundle install
|
159
|
+
bundle exec rake
|
160
|
+
```
|
161
|
+
## Contributing
|
162
|
+
|
163
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/tobymao/snabberb.
|
164
|
+
|
165
|
+
## License
|
166
|
+
|
167
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../..
|
3
|
+
specs:
|
4
|
+
snabberb (0.1.0)
|
5
|
+
opal (~> 1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.0)
|
11
|
+
c_lexer (2.5.3.0.0)
|
12
|
+
ast (~> 2.4.0)
|
13
|
+
parser (= 2.5.3.0)
|
14
|
+
opal (1.0.0)
|
15
|
+
ast (>= 2.3.0)
|
16
|
+
parser (= 2.5.3.0)
|
17
|
+
parser (2.5.3.0)
|
18
|
+
ast (~> 2.4.0)
|
19
|
+
rack (2.0.7)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
c_lexer
|
26
|
+
opal
|
27
|
+
rack
|
28
|
+
snabberb!
|
29
|
+
|
30
|
+
BUNDLED WITH
|
31
|
+
2.0.2
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opal'
|
4
|
+
require 'snabberb'
|
5
|
+
|
6
|
+
class Row < Snabberb::Component
|
7
|
+
needs :index
|
8
|
+
needs :value
|
9
|
+
needs :selected_id, default: nil, store: true
|
10
|
+
|
11
|
+
def selected?
|
12
|
+
@index == @selected_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
onclick = lambda do
|
17
|
+
store(:selected_id, selected? ? nil : @index)
|
18
|
+
end
|
19
|
+
|
20
|
+
style = {
|
21
|
+
cursor: 'pointer',
|
22
|
+
border: 'solid 1px rgba(0,0,0,0.2)',
|
23
|
+
}
|
24
|
+
|
25
|
+
style['background-color'] = 'lightblue' if selected?
|
26
|
+
|
27
|
+
h(:div, { style: style, on: { click: onclick } }, [
|
28
|
+
h(:div, @value)
|
29
|
+
])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Form < Snabberb::Component
|
34
|
+
needs :values, store: true
|
35
|
+
|
36
|
+
def render
|
37
|
+
input = h(:input, props: { type: 'text', value: @values.last })
|
38
|
+
|
39
|
+
onclick = lambda do |event|
|
40
|
+
value = input.JS['elm'].JS['value']
|
41
|
+
event.JS.preventDefault
|
42
|
+
store(:values, @values + [value])
|
43
|
+
end
|
44
|
+
|
45
|
+
h(:form, { style: { width: '300px' } }, [
|
46
|
+
input,
|
47
|
+
h(:button, { on: { click: onclick } }, 'Add row'),
|
48
|
+
])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Application < Snabberb::Component
|
53
|
+
needs :values, store: true
|
54
|
+
|
55
|
+
def render
|
56
|
+
rows = @values.map.with_index do |value, index|
|
57
|
+
h(Row, index: index, value: value)
|
58
|
+
end
|
59
|
+
|
60
|
+
h(:div, { style: { width: '100px' } }, [
|
61
|
+
h(:div, 'List of Fruits'),
|
62
|
+
*rows,
|
63
|
+
h(Form),
|
64
|
+
])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Application.attach('app', values: %w[apple banana cantaloupe])
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.require
|
5
|
+
|
6
|
+
# Instructions: bundle in this directory
|
7
|
+
# then run `bundle exec rackup` to start the server
|
8
|
+
# and browse to http://localhost:9292
|
9
|
+
|
10
|
+
# the directory where the code is (add to opal load path )
|
11
|
+
Opal.append_path('app')
|
12
|
+
|
13
|
+
run(Opal::SimpleServer.new do |s|
|
14
|
+
# the name of the ruby file to load. To use more files they must be required from here (see app)
|
15
|
+
s.main = 'application'
|
16
|
+
# need to set the index explicitly for opal server to pick it up
|
17
|
+
s.index_path = 'index.html.erb'
|
18
|
+
end)
|