inesita 0.2.3 → 0.3.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 +4 -4
- data/.travis.yml +9 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +7 -1
- data/LICENSE.md +21 -0
- data/README.md +5 -3
- data/Rakefile +17 -0
- data/inesita.gemspec +7 -5
- data/lib/inesita.rb +28 -1
- data/lib/inesita/app_files_listener.rb +42 -0
- data/lib/inesita/cli.rb +1 -0
- data/lib/inesita/cli/build.rb +33 -10
- data/lib/inesita/cli/new.rb +2 -5
- data/lib/inesita/cli/server.rb +1 -2
- data/lib/inesita/cli/template/Gemfile.tt +9 -6
- data/lib/inesita/cli/template/app/application.js.rb.tt +2 -4
- data/lib/inesita/cli/template/app/components/home.rb.tt +1 -1
- data/lib/inesita/cli/template/app/index.html.slim.tt +1 -9
- data/lib/inesita/cli/template/app/{components/layout.rb.tt → layout.rb.tt} +1 -1
- data/lib/inesita/cli/template/config.ru.tt +6 -1
- data/lib/inesita/cli/template/static/inesita-rb.png +0 -0
- data/lib/inesita/config.rb +8 -0
- data/lib/inesita/live_reload.rb +47 -0
- data/lib/inesita/minify.rb +34 -0
- data/lib/inesita/server.rb +56 -49
- data/lib/rubame.rb +156 -0
- data/opal/inesita.rb +12 -2
- data/opal/inesita/application.rb +37 -15
- data/opal/inesita/component.rb +37 -36
- data/opal/inesita/{component_withs.rb → component_properties.rb} +1 -1
- data/opal/inesita/component_virtual_dom_extension.rb +22 -0
- data/opal/inesita/error.rb +4 -0
- data/opal/inesita/layout.rb +1 -3
- data/opal/inesita/live_reload.rb +79 -0
- data/opal/inesita/router.rb +24 -20
- data/opal/inesita/routes.rb +16 -13
- data/opal/inesita/store.rb +6 -1
- data/spec/lib/nil_spec.rb +7 -0
- data/spec/lib/spec_helper.rb +1 -0
- data/spec/opal/application_spec.rb +48 -0
- data/spec/opal/component_spec.rb +56 -0
- data/spec/opal/layout_spec.rb +9 -0
- data/spec/opal/router_spec.rb +60 -0
- data/spec/opal/spec_helper.rb +5 -0
- data/spec/opal/store_spec.rb +17 -0
- metadata +69 -16
- data/TODO.md +0 -8
- data/opal/inesita/js_helpers.rb +0 -27
- data/test.rb +0 -27
@@ -0,0 +1,34 @@
|
|
1
|
+
module Inesita
|
2
|
+
module Minify
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def html(source)
|
6
|
+
if defined?(HtmlCompressor) && defined?(HtmlCompressor::Compressor)
|
7
|
+
HtmlCompressor::Compressor.new.compress(source)
|
8
|
+
else
|
9
|
+
source
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def js(source)
|
14
|
+
if defined?(Uglifier)
|
15
|
+
Uglifier.compile(source)
|
16
|
+
else
|
17
|
+
source
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def css(source)
|
22
|
+
if defined?(Sass) && defined?(Sass::Engine)
|
23
|
+
Sass::Engine.new(source,
|
24
|
+
syntax: :scss,
|
25
|
+
cache: false,
|
26
|
+
read_cache: false,
|
27
|
+
style: :compressed
|
28
|
+
).render
|
29
|
+
else
|
30
|
+
source
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/inesita/server.rb
CHANGED
@@ -1,74 +1,81 @@
|
|
1
1
|
require 'rack/rewrite'
|
2
2
|
|
3
3
|
module Inesita
|
4
|
-
module
|
5
|
-
|
6
|
-
|
4
|
+
module SprocketsContext
|
5
|
+
def asset_path(path, _options = {})
|
6
|
+
path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Server
|
11
|
+
attr_reader :assets_app
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@assets_app = create_assets_app
|
15
|
+
@source_maps_app = create_source_maps_app
|
16
|
+
@app = create_app
|
17
|
+
Inesita.assets_code = assets_code
|
18
|
+
end
|
7
19
|
|
8
|
-
|
20
|
+
def assets_code
|
21
|
+
assets_prefix = Inesita.env == :development ? Config::ASSETS_PREFIX : nil
|
22
|
+
%(
|
23
|
+
<link rel="stylesheet" type="text/css" href="#{assets_prefix}/stylesheet.css">
|
24
|
+
#{Opal::Sprockets.javascript_include_tag('application', sprockets: @assets_app, prefix: assets_prefix, debug: Inesita.env == :development)}
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_app
|
29
|
+
assets_app = @assets_app
|
30
|
+
source_maps_app = @source_maps_app
|
31
|
+
|
32
|
+
Rack::Builder.new do
|
33
|
+
use Rack::Static, :urls => [Config::STATIC_DIR]
|
9
34
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
35
|
+
use Rack::Rewrite do
|
36
|
+
rewrite(/^(?!#{Config::ASSETS_PREFIX}|#{Config::SOURCE_MAP_PREFIX}).*/, Config::ASSETS_PREFIX)
|
37
|
+
end
|
38
|
+
|
39
|
+
map Config::ASSETS_PREFIX do
|
40
|
+
run assets_app
|
41
|
+
end
|
42
|
+
|
43
|
+
map Config::SOURCE_MAP_PREFIX do
|
44
|
+
run source_maps_app
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
15
48
|
|
16
|
-
|
17
|
-
|
49
|
+
def create_assets_app
|
50
|
+
Opal::Server.new do |s|
|
51
|
+
s.append_path Config::APP_DIR
|
18
52
|
|
19
|
-
# add paths from opal
|
20
53
|
Opal.paths.each do |p|
|
21
54
|
s.append_path p
|
22
55
|
end
|
23
56
|
|
24
|
-
# add paths from rails-assets
|
25
57
|
RailsAssets.load_paths.each do |p|
|
26
58
|
s.append_path p
|
27
59
|
end if defined?(RailsAssets)
|
28
60
|
|
29
|
-
s.
|
30
|
-
|
31
|
-
$DEVELOPMENT_MODE ? "#{ASSETS_PREFIX}/#{path}" : path
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
61
|
+
configure_sprockets(s.sprockets)
|
62
|
+
end.sprockets
|
35
63
|
end
|
36
64
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
65
|
+
def configure_sprockets(sprockets)
|
66
|
+
sprockets.register_engine '.slim', Slim::Template
|
67
|
+
sprockets.context_class.class_eval do
|
68
|
+
include SprocketsContext
|
41
69
|
end
|
42
70
|
end
|
43
71
|
|
44
|
-
def
|
45
|
-
::Opal::Sprockets::SourceMapHeaderPatch.inject!(SOURCE_MAP_PREFIX)
|
46
|
-
Opal::SourceMapServer.new(
|
72
|
+
def create_source_maps_app
|
73
|
+
::Opal::Sprockets::SourceMapHeaderPatch.inject!(Config::SOURCE_MAP_PREFIX)
|
74
|
+
Opal::SourceMapServer.new(@assets_app, Config::SOURCE_MAP_PREFIX)
|
47
75
|
end
|
48
76
|
|
49
|
-
def
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
def create
|
54
|
-
development_mode
|
55
|
-
assets_app = assets
|
56
|
-
source_maps_app = source_maps(assets_app)
|
57
|
-
set_global_vars(assets_app, true)
|
58
|
-
|
59
|
-
Rack::Builder.new do
|
60
|
-
use Rack::Rewrite do
|
61
|
-
rewrite %r[^(?!#{ASSETS_PREFIX}|#{SOURCE_MAP_PREFIX}).*], ASSETS_PREFIX
|
62
|
-
end
|
63
|
-
|
64
|
-
map ASSETS_PREFIX do
|
65
|
-
run assets_app
|
66
|
-
end
|
67
|
-
|
68
|
-
map SOURCE_MAP_PREFIX do
|
69
|
-
run source_maps_app
|
70
|
-
end
|
71
|
-
end
|
77
|
+
def call(env)
|
78
|
+
@app.call(env)
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
data/lib/rubame.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'websocket'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module Rubame
|
5
|
+
class Server
|
6
|
+
def initialize(host, port)
|
7
|
+
Socket.do_not_reverse_lookup
|
8
|
+
@hostname = host
|
9
|
+
@port = port
|
10
|
+
|
11
|
+
@reading = []
|
12
|
+
@writing = []
|
13
|
+
|
14
|
+
@clients = {} # Socket as key, and Client as value
|
15
|
+
|
16
|
+
@socket = TCPServer.new(@hostname, @port)
|
17
|
+
@reading.push @socket
|
18
|
+
end
|
19
|
+
|
20
|
+
def accept
|
21
|
+
socket = @socket.accept_nonblock
|
22
|
+
@reading.push socket
|
23
|
+
handshake = WebSocket::Handshake::Server.new
|
24
|
+
client = Rubame::Client.new(socket, handshake, self)
|
25
|
+
|
26
|
+
while line = socket.gets
|
27
|
+
client.handshake << line
|
28
|
+
break if client.handshake.finished?
|
29
|
+
end
|
30
|
+
if client.handshake.valid?
|
31
|
+
@clients[socket] = client
|
32
|
+
client.write handshake.to_s
|
33
|
+
client.opened = true
|
34
|
+
return client
|
35
|
+
else
|
36
|
+
close(client)
|
37
|
+
end
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def read(client)
|
42
|
+
|
43
|
+
pairs = client.socket.recvfrom(2000)
|
44
|
+
messages = []
|
45
|
+
|
46
|
+
if pairs[0].length == 0
|
47
|
+
close(client)
|
48
|
+
else
|
49
|
+
client.frame << pairs[0]
|
50
|
+
|
51
|
+
while f = client.frame.next
|
52
|
+
if (f.type == :close)
|
53
|
+
close(client)
|
54
|
+
return messages
|
55
|
+
else
|
56
|
+
messages.push f
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
return messages
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def close(client)
|
67
|
+
@reading.delete client.socket
|
68
|
+
@clients.delete client.socket
|
69
|
+
begin
|
70
|
+
client.socket.close
|
71
|
+
ensure
|
72
|
+
client.closed = true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def run(&blk)
|
77
|
+
readable, _writable = IO.select(@reading, @writing)
|
78
|
+
|
79
|
+
if readable
|
80
|
+
readable.each do |socket|
|
81
|
+
client = @clients[socket]
|
82
|
+
if socket == @socket
|
83
|
+
client = accept
|
84
|
+
else
|
85
|
+
msg = read(client)
|
86
|
+
client.messaged = msg
|
87
|
+
end
|
88
|
+
|
89
|
+
blk.call(client) if client and blk
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop
|
95
|
+
@socket.close
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Client
|
100
|
+
attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed
|
101
|
+
|
102
|
+
def initialize(socket, handshake, server)
|
103
|
+
@socket = socket
|
104
|
+
@handshake = handshake
|
105
|
+
@frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
|
106
|
+
@opened = false
|
107
|
+
@messaged = []
|
108
|
+
@closed = false
|
109
|
+
@server = server
|
110
|
+
end
|
111
|
+
|
112
|
+
def write(data)
|
113
|
+
@socket.write data
|
114
|
+
end
|
115
|
+
|
116
|
+
def send(data)
|
117
|
+
frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :text)
|
118
|
+
begin
|
119
|
+
@socket.write frame
|
120
|
+
@socket.flush
|
121
|
+
rescue
|
122
|
+
@server.close(self) unless @closed
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def onopen(&blk)
|
127
|
+
if @opened
|
128
|
+
begin
|
129
|
+
blk.call
|
130
|
+
ensure
|
131
|
+
@opened = false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def onmessage(&blk)
|
137
|
+
if @messaged.size > 0
|
138
|
+
begin
|
139
|
+
@messaged.each do |x|
|
140
|
+
blk.call(x.to_s)
|
141
|
+
end
|
142
|
+
ensure
|
143
|
+
@messaged = []
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def onclose(&blk)
|
149
|
+
if @closed
|
150
|
+
begin
|
151
|
+
blk.call
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
data/opal/inesita.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
require 'opal'
|
2
|
+
require 'js'
|
3
|
+
require 'console'
|
4
|
+
|
5
|
+
require 'browser'
|
6
|
+
require 'browser/history'
|
7
|
+
require 'browser/animation_frame'
|
8
|
+
|
1
9
|
require 'virtual_dom'
|
2
|
-
|
3
|
-
require 'inesita/
|
10
|
+
|
11
|
+
require 'inesita/error'
|
12
|
+
require 'inesita/component_virtual_dom_extension'
|
13
|
+
require 'inesita/component_properties'
|
4
14
|
require 'inesita/component'
|
5
15
|
require 'inesita/routes'
|
6
16
|
require 'inesita/router'
|
data/opal/inesita/application.rb
CHANGED
@@ -2,27 +2,49 @@ module Inesita
|
|
2
2
|
class Application
|
3
3
|
include Inesita::Component
|
4
4
|
|
5
|
-
|
5
|
+
def initialize(options = {})
|
6
|
+
setup_router(options[:router])
|
7
|
+
setup_layout(options[:layout])
|
8
|
+
setup_root
|
9
|
+
setup_store(options[:store])
|
10
|
+
end
|
6
11
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
store = options[:store]
|
12
|
+
def render
|
13
|
+
component @root
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
raise "Invalid #{router} class, should mixin Inesita::Router" unless router.include?(Inesita::Router)
|
14
|
-
raise "Invalid #{layout} class, should mixin Inesita::Layout" unless layout.include?(Inesita::Layout)
|
15
|
-
raise "Invalid #{store} class, should mixin Inesita::Store" unless store.include?(Inesita::Store)
|
16
|
+
private
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
def setup_router(router)
|
19
|
+
if router
|
20
|
+
fail Error, "Invalid #{router} class, should mixin Inesita::Router" unless router.include?(Inesita::Router)
|
21
|
+
@router = router.new
|
22
|
+
else
|
23
|
+
@router = Class.new { define_method(:method_missing) { fail 'Router missing' } }.new
|
24
|
+
end
|
25
|
+
end
|
19
26
|
|
20
|
-
|
21
|
-
|
27
|
+
def setup_layout(layout)
|
28
|
+
if layout
|
29
|
+
fail Error, "Invalid #{layout} class, should mixin Inesita::Layout" unless layout.include?(Inesita::Layout)
|
30
|
+
@layout = layout.new
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
|
-
def
|
25
|
-
|
34
|
+
def setup_root
|
35
|
+
if @layout
|
36
|
+
@root = @layout
|
37
|
+
elsif @router
|
38
|
+
@root = @router
|
39
|
+
else
|
40
|
+
fail Error, 'Router or Layout not found!'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_store(store)
|
45
|
+
return unless store
|
46
|
+
fail Error, "Invalid #{store} class, should mixin Inesita::Store" unless store.include?(Inesita::Store)
|
47
|
+
@store = store.new.with_root_component(@root).store
|
26
48
|
end
|
27
49
|
end
|
28
50
|
end
|
data/opal/inesita/component.rb
CHANGED
@@ -1,36 +1,64 @@
|
|
1
1
|
module Inesita
|
2
2
|
module Component
|
3
3
|
include VirtualDOM::DOM
|
4
|
-
include
|
4
|
+
include ComponentProperties
|
5
|
+
include ComponentVirtualDomExtension
|
6
|
+
|
7
|
+
def render
|
8
|
+
fail Error, "Implement #render in #{self.class} component"
|
9
|
+
end
|
5
10
|
|
6
11
|
def mount_to(element)
|
12
|
+
fail Error, "Can't mount #{self.class}, target element not found!" unless element
|
7
13
|
@root_component = self
|
8
14
|
@virtual_dom = render_virtual_dom
|
9
15
|
@root_node = VirtualDOM.create(@virtual_dom)
|
10
16
|
element.inner_dom = @root_node
|
17
|
+
@root_component.call_after_render
|
18
|
+
self
|
11
19
|
end
|
12
20
|
|
13
21
|
def render_if_root
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
22
|
+
return unless @virtual_dom && @root_node
|
23
|
+
new_virtual_dom = render_virtual_dom
|
24
|
+
diff = VirtualDOM.diff(@virtual_dom, new_virtual_dom)
|
25
|
+
VirtualDOM.patch(@root_node, diff)
|
26
|
+
@virtual_dom = new_virtual_dom
|
20
27
|
end
|
21
28
|
|
22
29
|
def render_virtual_dom
|
30
|
+
@after_render_callbacks = []
|
31
|
+
@root_component.add_after_render(method(:after_render)) if respond_to?(:after_render)
|
32
|
+
@cache_component_counter = 0
|
23
33
|
@__virtual_nodes__ = []
|
24
34
|
render
|
25
35
|
if @__virtual_nodes__.length == 1
|
26
36
|
@__virtual_nodes__.first
|
27
37
|
else
|
28
|
-
VirtualDOM::VirtualNode.new('div', {}, @__virtual_nodes__).
|
38
|
+
VirtualDOM::VirtualNode.new('div', {}, @__virtual_nodes__).to_n
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
42
|
+
def add_after_render(block)
|
43
|
+
@after_render_callbacks << block
|
44
|
+
end
|
45
|
+
|
46
|
+
def call_after_render
|
47
|
+
@after_render_callbacks.reverse_each(&:call)
|
48
|
+
end
|
49
|
+
|
32
50
|
def update_dom
|
33
|
-
|
51
|
+
$console.warn "Use 'render!' instead of 'update_dom'"
|
52
|
+
render!
|
53
|
+
end
|
54
|
+
|
55
|
+
def render!
|
56
|
+
animation_frame do
|
57
|
+
if @root_component
|
58
|
+
@root_component.render_if_root
|
59
|
+
@root_component.call_after_render
|
60
|
+
end
|
61
|
+
end
|
34
62
|
end
|
35
63
|
|
36
64
|
def cache_component(component, &block)
|
@@ -39,32 +67,5 @@ module Inesita
|
|
39
67
|
@cache_component_counter += 1
|
40
68
|
@cache_component["#{component}-#{@cache_component_counter}"] || @cache_component["#{component}-#{@cache_component_counter}"] = block.call
|
41
69
|
end
|
42
|
-
|
43
|
-
def a(params, &block)
|
44
|
-
params = { onclick: -> { @router.handle_link(params[:href]) } }.merge(params) if params[:href] && @router
|
45
|
-
@__virtual_nodes__ ||= []
|
46
|
-
if block
|
47
|
-
current = @__virtual_nodes__
|
48
|
-
@__virtual_nodes__ = []
|
49
|
-
result = block.call
|
50
|
-
vnode = VirtualDOM::VirtualNode.new('a', process_params(params), @__virtual_nodes__.count == 0 ? result : @__virtual_nodes__).vnode
|
51
|
-
@__virtual_nodes__ = current
|
52
|
-
else
|
53
|
-
vnode = VirtualDOM::VirtualNode.new('a', process_params(params), []).vnode
|
54
|
-
end
|
55
|
-
@__virtual_nodes__ << vnode
|
56
|
-
vnode
|
57
|
-
end
|
58
|
-
|
59
|
-
def component(comp, opts = {})
|
60
|
-
fail "Component is nil in #{self.class} class" if comp.nil?
|
61
|
-
@__virtual_nodes__ ||= []
|
62
|
-
@__virtual_nodes__ << cache_component(comp) do
|
63
|
-
(comp.is_a?(Class) ? comp.new : comp)
|
64
|
-
.with_root_component(@root_component)
|
65
|
-
.with_router(@router)
|
66
|
-
.with_store(@store)
|
67
|
-
end.with_props(opts[:props] || {}).render_virtual_dom
|
68
|
-
end
|
69
70
|
end
|
70
71
|
end
|