snabberb 0.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d6d783a17ba02a615ad7a82b4f6f120b2fe805b4c3f19d0364aae38fc98a24a
4
- data.tar.gz: 528c609d9ca398ae2288e951d98673f3d69cd3d866000fc24d665024948cd458
3
+ metadata.gz: 9905574c98d778f67508b52d3195f97d3d95b00b6d8a729a086f74c48e03b532
4
+ data.tar.gz: 770cb2d53f5aa816c0e156ba231ad5bacc26b77fc118ce8c2963930b56faaf25
5
5
  SHA512:
6
- metadata.gz: 52793e19ab8991b24aea629724cb60e6f56dfdaae33318ce1098e12f44be4ca23c6acb0caec5f82e0ac0a2be4f0047f3507e67a7793fe4887d271a24638a128d
7
- data.tar.gz: 4e8e093484a519198de36a86d22885076263a3de437c2b253c8e8f203edd5ae63d2186e57bf9b0b22e856bc9acc290ae3e16a82c018d368448013a13b61a4608
6
+ metadata.gz: 1590dacc07bd3dab3da4c7cc5b799744242f29d08e2bd87bff32f56aedfdb50c70225910f4dd8d0ed95372c5f6a45f7fe359db42c7221e78133273b35c8c4cb2
7
+ data.tar.gz: 3d13da1d38258a606df13c4bdccb8afc2a38d15860f42a900718b903492cfdbe7a84d0342187cdde9bb3239075463cd5651785122bf7f00bcf008c745e1fccd6
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /examples/roda/public
9
10
 
10
11
  .DS_STORE
11
12
  *.swp
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- Layout/IndentFirstArrayElement:
1
+ Layout/FirstArrayElementIndentation:
2
2
  Enabled: false
3
3
 
4
4
  Metrics/AbcSize:
@@ -34,4 +34,4 @@ Style/TrailingCommaInHashLiteral:
34
34
  Enabled: false
35
35
 
36
36
  AllCops:
37
- TargetRubyVersion: 2.6
37
+ TargetRubyVersion: 2.3
data/README.md CHANGED
@@ -43,6 +43,8 @@ TextBox.html(text: 'hello world')
43
43
  ## Examples
44
44
  [Rack App](examples/rack)
45
45
 
46
+ [Roda App with HTML Prerendering](examples/roda)
47
+
46
48
  ## Usage
47
49
 
48
50
  ### Creating DOM Elements With h
@@ -136,6 +138,16 @@ The precedence of need values is stored > passed needs > default value.
136
138
 
137
139
  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
140
 
141
+ ### Prerendering
142
+
143
+ You can prerender your HTML by calling
144
+
145
+ ```ruby
146
+ Snabberb.prerender_script('LayoutClass', 'ApplicationClass', 'application_id', javascript_include_tags: '', **needs)
147
+ ```
148
+
149
+ A detailed example can be found [in the Roda example](examples/roda).
150
+
139
151
  ## Installation
140
152
 
141
153
  Add this line to your application's Gemfile:
@@ -1,20 +1,20 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- snabberb (0.1.0)
4
+ snabberb (0.2.0)
5
5
  opal (~> 1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.0)
11
- c_lexer (2.5.3.0.0)
11
+ c_lexer (2.6.4.1.1)
12
12
  ast (~> 2.4.0)
13
- parser (= 2.5.3.0)
14
- opal (1.0.0)
13
+ parser (= 2.6.4.1)
14
+ opal (1.0.1)
15
15
  ast (>= 2.3.0)
16
- parser (= 2.5.3.0)
17
- parser (2.5.3.0)
16
+ parser (~> 2.6)
17
+ parser (2.6.4.1)
18
18
  ast (~> 2.4.0)
19
19
  rack (2.0.7)
20
20
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'c_lexer'
6
+ gem 'execjs'
7
+ gem 'mini_racer'
8
+ gem 'opal'
9
+ gem 'opal-sprockets'
10
+ gem 'roda'
11
+ gem 'snabberb', path: '../..'
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: ../..
3
+ specs:
4
+ snabberb (0.2.0)
5
+ opal (~> 1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ c_lexer (2.6.4.1.1)
12
+ ast (~> 2.4.0)
13
+ parser (= 2.6.4.1)
14
+ concurrent-ruby (1.1.5)
15
+ execjs (2.7.0)
16
+ libv8 (7.3.492.27.1)
17
+ mini_racer (0.2.8)
18
+ libv8 (>= 6.9.411)
19
+ opal (1.0.1)
20
+ ast (>= 2.3.0)
21
+ parser (~> 2.6)
22
+ opal-sprockets (0.4.8.1.0.3.7)
23
+ opal (~> 1.0.0)
24
+ sprockets (~> 3.7)
25
+ tilt (>= 1.4)
26
+ parser (2.6.4.1)
27
+ ast (~> 2.4.0)
28
+ rack (2.0.7)
29
+ roda (3.26.0)
30
+ rack
31
+ sprockets (3.7.2)
32
+ concurrent-ruby (~> 1.0)
33
+ rack (> 1, < 3)
34
+ tilt (2.0.10)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ c_lexer
41
+ execjs
42
+ mini_racer
43
+ opal
44
+ opal-sprockets
45
+ roda
46
+ snabberb!
47
+
48
+ BUNDLED WITH
49
+ 2.0.2
@@ -0,0 +1,14 @@
1
+ # Roda Example
2
+
3
+ This is an example using Roda. It features prerendering HTML and SVG rendering.
4
+
5
+ ![SVG Hexes](roda_example.png)
6
+
7
+ ## Install and Run
8
+
9
+ ```
10
+ bundle install
11
+ bundle exec rackup
12
+ ```
13
+
14
+ Navigate to http://localhost:9292.
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ class Tile < Snabberb::Component
6
+ def render
7
+ h(:g, { attrs: { transform: 'rotate(60)' } }, [
8
+ h(:path, attrs: { d: 'm 0 87 L 0 -87', stroke: 'black', 'stroke-width' => 8 }),
9
+ h(:path, attrs: { d: 'm -4 86 L -4 -86', stroke: 'white', 'stroke-width' => 2 }),
10
+ h(:path, attrs: { d: 'm 4 86 L 4 -86', stroke: 'white', 'stroke-width' => 2 }),
11
+ ])
12
+ end
13
+ end
14
+
15
+ class Hex < Snabberb::Component
16
+ SIZE = 100
17
+ POINTS = '100,0 50,-87 -50,-87 -100,-0 -50,87 50,87'
18
+
19
+ needs :x
20
+ needs :y
21
+ needs :selected, default: Set.new, store: true
22
+
23
+ def translation
24
+ offset = self.class::SIZE
25
+ x = self.class::SIZE * Math.sqrt(3) / 2 * @x + offset
26
+ y = self.class::SIZE * 3 / 2 * @y + offset
27
+ "translate(#{x}, #{y})"
28
+ end
29
+
30
+ def transform
31
+ "#{translation} rotate(30)"
32
+ end
33
+
34
+ def render
35
+ coordinates = [@x, @y]
36
+ selected = @selected.include?(coordinates)
37
+ children = [h(:polygon, attrs: { points: self.class::POINTS })]
38
+ children << h(Tile) if selected
39
+
40
+ onclick = lambda do
41
+ if selected
42
+ store(:selected, @selected - [coordinates])
43
+ else
44
+ store(:selected, @selected | [coordinates])
45
+ end
46
+ end
47
+
48
+ props = {
49
+ attrs: {
50
+ transform: transform,
51
+ fill: selected ? 'yellow' : 'white',
52
+ stroke: 'black',
53
+ },
54
+ style: { cursor: 'pointer' },
55
+ on: { click: onclick }
56
+ }
57
+
58
+ h(:g, props, children)
59
+ end
60
+ end
61
+
62
+ class Map < Snabberb::Component
63
+ needs :size_x
64
+ needs :size_y
65
+
66
+ def render
67
+ hexes = @size_x.times.flat_map do |x|
68
+ @size_y.times.map do |y|
69
+ h(Hex, x: y.even? ? x + 1 : x, y: x.even? ? y + 1 : y)
70
+ end
71
+ end
72
+
73
+ container_style = {
74
+ width: '100%',
75
+ height: '100%',
76
+ overflow: 'auto',
77
+ }
78
+
79
+ svg_style = {
80
+ width: "#{@size_x * 50}px",
81
+ height: "#{@size_y * 80}px",
82
+ }
83
+
84
+ h(:div, { props: { id: 'map_id' }, style: container_style }, [
85
+ h(:svg, { style: svg_style }, [
86
+ h(:g, { attrs: { transform: 'scale(0.5)' } }, hexes)
87
+ ])
88
+ ])
89
+ end
90
+ end
91
+
92
+ class Index < Snabberb::Layout
93
+ def render
94
+ h(:html, [
95
+ h(:head, [
96
+ h(:meta, props: { charset: 'utf-8' }),
97
+ h(:title, 'Roda Demo'),
98
+ ]),
99
+ h(:body, [
100
+ @application,
101
+ h(:div, props: { innerHTML: @javascript_include_tags }),
102
+ h(:script, props: { innerHTML: @attach_func }),
103
+ ]),
104
+ ])
105
+ end
106
+ end
@@ -0,0 +1,2 @@
1
+ if (typeof setTimeout === 'undefined') { function setTimeout(func) { func() } }
2
+ if (typeof OpalLoaded === 'undefined') { OpalLoaded = [] }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opal'
4
+ require 'polyfill'
5
+ require 'snabberb'
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'execjs'
4
+ require 'json'
5
+ require 'opal'
6
+ require 'opal/sprockets'
7
+ require 'roda'
8
+ require 'sprockets'
9
+ require 'snabberb'
10
+
11
+ LIB_NAME = 'lib.js'
12
+ LIB_PATH = "./public/#{LIB_NAME}"
13
+ Opal.append_path('app')
14
+ File.write(LIB_PATH, Opal::Builder.build('requires')) unless File.file?(LIB_PATH)
15
+
16
+ class App < Roda
17
+ plugin :public
18
+
19
+ context = ExecJS.compile(File.read(LIB_PATH) + Opal::Builder.build('application').to_s)
20
+
21
+ environment = Sprockets::Environment.new
22
+ Opal.paths.each { |p| environment.append_path(p) }
23
+
24
+ javascript_include_tags = "<script src=#{LIB_NAME}></script>" + Opal::Sprockets.javascript_include_tag(
25
+ 'application',
26
+ sprockets: environment,
27
+ prefix: '/assets',
28
+ debug: true,
29
+ )
30
+
31
+ route do |r|
32
+ r.public
33
+
34
+ r.on 'assets' do
35
+ r.run environment
36
+ end
37
+
38
+ r.root do
39
+ context.eval(
40
+ Snabberb.prerender_script(
41
+ 'Index',
42
+ 'Map',
43
+ 'map_id',
44
+ javascript_include_tags: javascript_include_tags,
45
+ size_x: 30,
46
+ size_y: 30,
47
+ )
48
+ )
49
+ end
50
+ end
51
+ end
52
+
53
+ run App.freeze.app
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Snabberb
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/snabberb.rb CHANGED
@@ -1,6 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'opal'
4
5
  require 'snabberb/version'
5
6
 
6
- Opal.append_path File.expand_path('../opal', __dir__)
7
+ Opal.append_path(File.expand_path('../opal', __dir__))
8
+
9
+ module Snabberb
10
+ def self.prerender_script(layout, application, application_id, javascript_include_tags: '', **needs)
11
+ needs = JSON.generate(needs)
12
+
13
+ <<~JS
14
+ Opal.$$.#{layout}.$html(Opal.hash({
15
+ application: Opal.$$.#{application}.$new(null, Opal.hash(#{needs})).$render(),
16
+ javascript_include_tags: '#{javascript_include_tags.gsub("\n", '')}',
17
+ attach_func: 'Opal.$$.#{application}.$attach("#{application_id}", Opal.hash(#{needs}))'
18
+ }))
19
+ JS
20
+ end
21
+ end
@@ -16,12 +16,11 @@ module Snabberb
16
16
  # :default - Sets the default value, if not passed the need is considered required.
17
17
  # :store - Whether or not to store the need as state. The default is false.
18
18
  def self.needs(key, **opts)
19
- @class_needs ||= {}
20
- @class_needs[key] = opts
19
+ class_needs[key] = opts
21
20
  end
22
21
 
23
22
  def self.class_needs
24
- @class_needs || {}
23
+ @class_needs ||= superclass.respond_to?(:class_needs) ? superclass.class_needs.clone : {}
25
24
  end
26
25
 
27
26
  # Attach the root component to a dom element by container id.
@@ -145,4 +144,25 @@ module Snabberb
145
144
  end
146
145
  end
147
146
  end
147
+
148
+ # Sublcass this to prerender applications.
149
+ class Layout < Snabberb::Component
150
+ needs :application # the rendered application
151
+ needs :attach_func # function to reattach the application after dom load
152
+ needs :javascript_include_tags # all necessary javascript tags
153
+
154
+ # Example render function.
155
+ # def render
156
+ # h(:html, [
157
+ # h(:head, [
158
+ # h(:meta, { props: { charset: 'utf-8'} }),
159
+ # ]),
160
+ # h(:body, [
161
+ # @application,
162
+ # h(:div, { props: { innerHTML: @javascript_include_tags } }),
163
+ # h(:script, { props: { innerHTML: @attach_func} }),
164
+ # ]),
165
+ # ])
166
+ # end
167
+ end
148
168
  end
data/snabberb.gemspec CHANGED
@@ -25,13 +25,14 @@ Gem::Specification.new do |spec|
25
25
  spec.bindir = 'exe'
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = %w[lib opal]
28
+ spec.required_ruby_version = '>= 2.3'
28
29
 
29
30
  spec.add_dependency 'opal', '~> 1.0'
30
31
 
31
- spec.add_development_dependency 'bundler', '~> 2.0'
32
+ spec.add_development_dependency 'bundler'
32
33
  spec.add_development_dependency 'c_lexer'
33
34
  spec.add_development_dependency 'execjs'
34
- spec.add_development_dependency 'rake', '~> 12.0'
35
- spec.add_development_dependency 'rspec', '~> 3.0'
36
- spec.add_development_dependency 'rubocop', '~> 0.7'
35
+ spec.add_development_dependency 'rake'
36
+ spec.add_development_dependency 'rspec'
37
+ spec.add_development_dependency 'rubocop'
37
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snabberb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toby Mao
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-08 00:00:00.000000000 Z
11
+ date: 2019-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opal
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '0'
34
34
  type: :development
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: '2.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: c_lexer
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -70,44 +70,44 @@ dependencies:
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '12.0'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '12.0'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '3.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '3.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '0.7'
103
+ version: '0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '0.7'
110
+ version: '0'
111
111
  description: 'Snabberb is a minimalistic Opal view framework based on Snabbdom. You
112
112
  can write efficient reactive Javascript in pure Ruby. Snabberb also provides a simple
113
113
  way to track state.
@@ -133,6 +133,14 @@ files:
133
133
  - examples/rack/app/application.rb
134
134
  - examples/rack/config.ru
135
135
  - examples/rack/index.html.erb
136
+ - examples/roda/Gemfile
137
+ - examples/roda/Gemfile.lock
138
+ - examples/roda/README.md
139
+ - examples/roda/app/application.rb
140
+ - examples/roda/app/polyfill.js
141
+ - examples/roda/app/requires.rb
142
+ - examples/roda/config.ru
143
+ - examples/roda/roda_example.png
136
144
  - lib/snabberb.rb
137
145
  - lib/snabberb/version.rb
138
146
  - opal/snabberb.rb
@@ -159,7 +167,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
167
  requirements:
160
168
  - - ">="
161
169
  - !ruby/object:Gem::Version
162
- version: '0'
170
+ version: '2.3'
163
171
  required_rubygems_version: !ruby/object:Gem::Requirement
164
172
  requirements:
165
173
  - - ">="