-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathorbital_ring.rb
119 lines (98 loc) · 3.1 KB
/
orbital_ring.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
require 'js'
require 'js/require_remote'
require 'erb'
class OrbitalRing
include Singleton
def dir = 'app_root'
class Loader
def self.setup
new.setup OrbitalRing.instance.dir
end
def setup(dir)
Loader.define_const_missing dir, Object
end
private
# Define const_missing to read missing constants remotely
def self.define_const_missing(dir, mod)
mod.define_singleton_method(:const_missing) do |id|
# Convert constant name to snake case and read remotely
feature_name = Util.to_snake_case(id)
JS::RequireRemote.instance.load("#{dir}/#{feature_name}")
# Define const_missing in the loaded module
loaded_module = const_get(id)
Loader.define_const_missing dir, loaded_module
loaded_module
end
end
end
module Renderer
def self.included(base)
# Define variables to cache templates
base.define_method(:tempaltes_cache) { @tempaltes_cache ||= {} }
end
def render(template_name, locals)
tempaltes_cache[template_name] = load_template(template_name) unless tempaltes_cache[template_name]
if(locals[:collection])
# When a collection is passed and a template is called,
# the template can access individual members of the collection via a variable
# with the same name as the template.
locals[:collection].map do
render_one template_name,
{ template_name.to_s => _1 }
end.join
else
render_one(template_name, locals)
end
end
private
def load_template(template_name)
# Determine the file name from the template name.
feature_name = "#{Util.to_snake_case(template_name)}.html.erb"
path = "#{OrbitalRing.instance.dir}/templates"
url = "#{path}/#{feature_name}"
response = JS.global.fetch(url).await
raise "Failed to fetch template: #{url}" unless response[:status].to_i == 200
ERB.new(response.text().await.to_s)
end
def render_one(template_name, locals)
# Specify the binding for this method
# to allow the render method to be used in the template.
b = binding
locals.each { |key, value| b.local_variable_set key, value }
tempaltes_cache[template_name].result b
end
end
class Routes
Events = ['click', 'change', 'input']
def self.draw(&block)
instance = self.new
instance.instance_eval(&block)
end
def initialize
Events.each do |event_name|
define_event event_name
end
end
private
def define_event(event_name)
self.class.define_method(event_name) do |selectors, options|
root.addEventListener event_name do |event|
if event[:target].closest(selectors) != JS::Null
options[:to].call event, options[:params]
end
end
end
end
def root
document = JS.global[:document]
document.getElementById OrbitalRing.instance.dir
end
end
module Util
def self.to_snake_case(symbol)
symbol.to_s
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.downcase
end
end
end