interfacets 0.1.0 → 0.9.9
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/.rubocop.yml +173 -1
- data/LICENSE +21 -0
- data/Rakefile +4 -6
- data/lib/interfacets/client/actor.rb +20 -0
- data/lib/interfacets/client/assets.rb +209 -0
- data/lib/interfacets/client/bus.rb +69 -0
- data/lib/interfacets/client/channels/api.rb +95 -0
- data/lib/interfacets/client/channels/audio.rb +101 -0
- data/lib/interfacets/client/channels/base.rb +28 -0
- data/lib/interfacets/client/channels/page_visibility.rb +21 -0
- data/lib/interfacets/client/channels/react/builder.rb +203 -0
- data/lib/interfacets/client/channels/react/channel.rb +61 -0
- data/lib/interfacets/client/channels/react/dom.rb +91 -0
- data/lib/interfacets/client/channels/react/evaluator.rb +33 -0
- data/lib/interfacets/client/channels/speech_to_text.rb +100 -0
- data/lib/interfacets/client/channels/timer.rb +51 -0
- data/lib/interfacets/client/channels/url.rb +52 -0
- data/lib/interfacets/client/config.rb +22 -0
- data/lib/interfacets/client/delegator.rb +37 -0
- data/lib/interfacets/client/facet.rb +26 -0
- data/lib/interfacets/client/facet2.rb +15 -0
- data/lib/interfacets/client/facets/attributes/accessor.rb +28 -0
- data/lib/interfacets/client/facets/attributes/association.rb +50 -0
- data/lib/interfacets/client/facets/attributes/bind.rb +25 -0
- data/lib/interfacets/client/facets/attributes/collection.rb +47 -0
- data/lib/interfacets/client/facets/attributes/readonly.rb +19 -0
- data/lib/interfacets/client/facets/deserializer.rb +30 -0
- data/lib/interfacets/client/facets/schema/deserializer.rb +63 -0
- data/lib/interfacets/client/facets/schema.rb +63 -0
- data/lib/interfacets/client/facets/serializer.rb +18 -0
- data/lib/interfacets/client/registry.rb +84 -0
- data/lib/interfacets/client/system.rb +88 -0
- data/lib/interfacets/client/utils/active_support_concern.rb +220 -0
- data/lib/interfacets/client/utils/mruby_patches.rb +81 -0
- data/lib/interfacets/client/utils/open_struct.rb +102 -0
- data/lib/interfacets/client/utils/securerandom.rb +69 -0
- data/lib/interfacets/client/view.rb +47 -0
- data/lib/interfacets/client.rb +13 -0
- data/lib/interfacets/mruby/build.dockerfile +66 -0
- data/lib/interfacets/mruby/build_config.rb +20 -0
- data/lib/interfacets/mruby/entrypoint.rb +23 -0
- data/lib/interfacets/mruby/init.c +66 -0
- data/lib/interfacets/server/api.rb +64 -0
- data/lib/interfacets/server/assets/facet.rb +61 -0
- data/lib/interfacets/server/assets.rb +210 -0
- data/lib/interfacets/server/basic_routable.rb +40 -0
- data/lib/interfacets/server/basic_router.rb +74 -0
- data/lib/interfacets/server/bus.rb +39 -0
- data/lib/interfacets/server/config.rb +87 -0
- data/lib/interfacets/server/facet.rb +51 -0
- data/lib/interfacets/server/facets/deserializer.rb +25 -0
- data/lib/interfacets/server/facets/schema/serializer.rb +54 -0
- data/lib/interfacets/server/facets/serializer.rb +50 -0
- data/lib/interfacets/server/registry.rb +212 -0
- data/lib/interfacets/shared/entities/bus.rb +218 -0
- data/lib/interfacets/shared/entities/collection_proxy.rb +190 -0
- data/lib/interfacets/shared/entities/specs/handlers.rb +117 -0
- data/lib/interfacets/shared/entities/specs.rb +124 -0
- data/lib/interfacets/shared/entity.rb +178 -0
- data/lib/interfacets/shared/entity_collection.rb +88 -0
- data/lib/interfacets/shared/generated_store.rb +145 -0
- data/lib/interfacets/shared/utils.rb +54 -0
- data/lib/interfacets/shared/validations.rb +71 -0
- data/lib/interfacets/test/browser.rb +63 -0
- data/lib/interfacets/test/js/inline_bus.rb +91 -0
- data/lib/interfacets/test/js/nodo_bus.rb +81 -0
- data/lib/interfacets/test/js/receivers/api.rb +37 -0
- data/lib/interfacets/test/js/receivers/react/node.rb +167 -0
- data/lib/interfacets/test/js/receivers/react.rb +31 -0
- data/lib/interfacets/test/js/receivers/url.rb +55 -0
- data/lib/interfacets/test.rb +17 -0
- data/lib/interfacets/version.rb +1 -1
- data/lib/interfacets.rb +26 -2
- metadata +103 -6
- data/README.md +0 -35
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
class Audio
|
|
7
|
+
include Channels::Base
|
|
8
|
+
|
|
9
|
+
type("interfacets:audio")
|
|
10
|
+
|
|
11
|
+
class Builder
|
|
12
|
+
attr_reader :event, :callbacks
|
|
13
|
+
attr_accessor :entity
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@event = {}
|
|
17
|
+
@callbacks = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def flush_event
|
|
21
|
+
@event.tap { @event = {} }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def play(sounds, reset_offset: false)
|
|
25
|
+
event[:type] = "play"
|
|
26
|
+
event[:payload] ||= { sounds:, resetOffset: reset_offset }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def load_audios(audios, &block)
|
|
30
|
+
id = SecureRandom.uuid
|
|
31
|
+
@event = {
|
|
32
|
+
id:,
|
|
33
|
+
type: "load",
|
|
34
|
+
payload: audios,
|
|
35
|
+
}
|
|
36
|
+
@callbacks[id] = block
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def request_mic(&block)
|
|
40
|
+
id = SecureRandom.uuid
|
|
41
|
+
@callbacks[id] = block
|
|
42
|
+
@event = {
|
|
43
|
+
id:,
|
|
44
|
+
type: "request-mic",
|
|
45
|
+
payload: {},
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def start_recording(&block)
|
|
50
|
+
id = SecureRandom.uuid
|
|
51
|
+
@callbacks[id] = block
|
|
52
|
+
@event = {
|
|
53
|
+
id:,
|
|
54
|
+
type: "start-record",
|
|
55
|
+
payload: {},
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def stop_recording(&block)
|
|
60
|
+
id = SecureRandom.uuid
|
|
61
|
+
@callbacks[id] = block
|
|
62
|
+
@event = {
|
|
63
|
+
id:,
|
|
64
|
+
type: "stop-record",
|
|
65
|
+
payload: {},
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def prepare(entity)
|
|
71
|
+
@builder ||= Builder.new
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def builder(stream = "default")
|
|
75
|
+
@builder
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def handle(event:, entity:, **)
|
|
79
|
+
return unless [
|
|
80
|
+
"loaded",
|
|
81
|
+
"mic-ready",
|
|
82
|
+
"recording-started",
|
|
83
|
+
"recording-stopped",
|
|
84
|
+
].include?(event.fetch("type"))
|
|
85
|
+
|
|
86
|
+
callback_id = event.dig("payload", "id")
|
|
87
|
+
callback = builder.callbacks.delete(callback_id)
|
|
88
|
+
if event["payload"].nil? || event["payload"].empty?
|
|
89
|
+
entity.instance_exec(&callback) if callback
|
|
90
|
+
elsif callback
|
|
91
|
+
entity.instance_exec(event.fetch("payload"), &callback)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def result
|
|
96
|
+
{ streams: { default: @builder.flush_event } }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
module Base
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
attr_reader :id
|
|
10
|
+
|
|
11
|
+
def initialize(id:)
|
|
12
|
+
@id = id
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def type
|
|
16
|
+
self.class.type
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class_methods do
|
|
20
|
+
def type(str = nil)
|
|
21
|
+
@type = str if str
|
|
22
|
+
@type
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
class PageVisibility
|
|
7
|
+
include Channels::Base
|
|
8
|
+
|
|
9
|
+
type("interfacets:page-visibility")
|
|
10
|
+
|
|
11
|
+
def configure(facet:); end
|
|
12
|
+
|
|
13
|
+
def handle(facet:, event:)
|
|
14
|
+
facet.visibility_change(event.dig("payload", "state"))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render(_facet); end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
module React
|
|
7
|
+
class Builder
|
|
8
|
+
attr_reader :nodes, :callbacks, :entity
|
|
9
|
+
def initialize(entity, cache)
|
|
10
|
+
@entity = entity
|
|
11
|
+
@nodes = []
|
|
12
|
+
@callbacks = {}
|
|
13
|
+
@stack = []
|
|
14
|
+
@cache = cache
|
|
15
|
+
@memos = (@cache["Interfacets.Memos"] ||= {})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def __reset__
|
|
19
|
+
@nodes = []
|
|
20
|
+
@stack = []
|
|
21
|
+
@memos = (@cache["Interfacets.Memos"] ||= {})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def apply(nodes)
|
|
25
|
+
nodes.each { @nodes << _1 }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def capture
|
|
29
|
+
raise ArgumentError unless block_given?
|
|
30
|
+
|
|
31
|
+
original_nodes = @nodes
|
|
32
|
+
@nodes = []
|
|
33
|
+
yield
|
|
34
|
+
captured_nodes = @nodes
|
|
35
|
+
@nodes = original_nodes
|
|
36
|
+
captured_nodes
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def string(val)
|
|
40
|
+
string_node(val)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def str(...)
|
|
44
|
+
string(...)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Need to override ruby's "p" methods (ie, puts)
|
|
48
|
+
def p(...)
|
|
49
|
+
add_node("p", ...)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def memo(*keys, on:, &)
|
|
53
|
+
System.logger.warn("Memo is disabled")
|
|
54
|
+
return add_node(
|
|
55
|
+
"React.Fragment",
|
|
56
|
+
&
|
|
57
|
+
)
|
|
58
|
+
memo_key = (
|
|
59
|
+
([entity.internal_id] + keys)
|
|
60
|
+
.map(&:to_json)
|
|
61
|
+
.join(".")
|
|
62
|
+
)
|
|
63
|
+
memo_val = Array(on).map(&:to_json).join(".")
|
|
64
|
+
|
|
65
|
+
if @memos.dig(memo_key, :memo_val) == memo_val
|
|
66
|
+
@nodes << @memos.dig(memo_key, :component)
|
|
67
|
+
else
|
|
68
|
+
component = add_node(
|
|
69
|
+
"Interfacets.Memo",
|
|
70
|
+
memoKey: memo_key,
|
|
71
|
+
memoVal: memo_val,
|
|
72
|
+
&
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@memos[memo_key] = { memo_val:, component: }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def cache(*keys, &)
|
|
80
|
+
cache_key = keys.map(&:to_json).join(".")
|
|
81
|
+
if @cache.key?(cache_key)
|
|
82
|
+
@nodes << @cache.fetch(cache_key)
|
|
83
|
+
else
|
|
84
|
+
@cache[cache_key] = (
|
|
85
|
+
add_node(
|
|
86
|
+
"Interfacets.Cache",
|
|
87
|
+
cacheKey: cache_key,
|
|
88
|
+
&
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def safe_html(str)
|
|
95
|
+
# add_node("React.Fragment", dangerouslySetInnerHTML: { __html: str })
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def f(id, callback = nil, &block)
|
|
99
|
+
register_handler(id: id.to_json, callback: callback || block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def prop(path)
|
|
103
|
+
paths = (
|
|
104
|
+
if path.is_a?(String)
|
|
105
|
+
path.split(".")
|
|
106
|
+
elsif path.is_a?(Symbol)
|
|
107
|
+
[path.to_s]
|
|
108
|
+
else
|
|
109
|
+
path
|
|
110
|
+
end
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
type: "interfacets:react-dom:prop",
|
|
115
|
+
payload: { path: paths },
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def function(...)
|
|
120
|
+
f(...)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def respond_to_missing?(...)
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def method_missing(meth, *, **, &)
|
|
128
|
+
raise if meth.to_s == "name"
|
|
129
|
+
add_node(meth, *, **, &)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def string_node(str)
|
|
135
|
+
@nodes << {
|
|
136
|
+
type: "interfacets:string-node",
|
|
137
|
+
attributes: { value: str },
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def register_handler(id:, callback:)
|
|
142
|
+
callbacks[id] = callback
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
type: "interfacets:react-dom:memoized-action",
|
|
146
|
+
payload: { id: },
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def add_node(element, str = nil, **attrs)
|
|
152
|
+
raise("cannot pass both a block and a string") if str && block_given?
|
|
153
|
+
raise if element == :entity
|
|
154
|
+
|
|
155
|
+
new_element = {
|
|
156
|
+
type: "interfacets:react-dom:element",
|
|
157
|
+
element:,
|
|
158
|
+
children: [],
|
|
159
|
+
attributes: (
|
|
160
|
+
attrs.transform_values { |value| transform_attr(value) }
|
|
161
|
+
),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if block_given? || str
|
|
165
|
+
# recurse
|
|
166
|
+
original_nodes = @nodes
|
|
167
|
+
@nodes = new_element[:children]
|
|
168
|
+
begin
|
|
169
|
+
yield if block_given?
|
|
170
|
+
string_node(str) if str
|
|
171
|
+
ensure
|
|
172
|
+
@nodes = original_nodes
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
@nodes << new_element
|
|
177
|
+
new_element
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def transform_attr(value)
|
|
181
|
+
if value.is_a?(Proc)
|
|
182
|
+
register_handler(
|
|
183
|
+
id: value.object_id.to_s,
|
|
184
|
+
callback: value,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
{
|
|
188
|
+
type: "interfacets:react-dom:action",
|
|
189
|
+
payload: { id: value.object_id.to_s },
|
|
190
|
+
}
|
|
191
|
+
elsif value.is_a?(Array)
|
|
192
|
+
value.map { transform_attr(_1) }
|
|
193
|
+
elsif value.is_a?(Hash)
|
|
194
|
+
value.transform_values { |k| transform_attr(k) }
|
|
195
|
+
else
|
|
196
|
+
value
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
module React
|
|
7
|
+
class Channel
|
|
8
|
+
include Channels::Base
|
|
9
|
+
|
|
10
|
+
type("interfacets:react-dom")
|
|
11
|
+
|
|
12
|
+
def prepare(entity)
|
|
13
|
+
@cache ||= {}
|
|
14
|
+
|
|
15
|
+
if entity.internal_entity_id == @builder&.entity&.internal_entity_id
|
|
16
|
+
# keep all the callbacks we've ever assigned, if its the same entity
|
|
17
|
+
# should probably prune these? Only need to remember last renders
|
|
18
|
+
# callbacks?
|
|
19
|
+
builder.__reset__
|
|
20
|
+
else
|
|
21
|
+
@builder = Builder.new(entity, @cache)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def builder(stream = "default")
|
|
26
|
+
@builder
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def result
|
|
30
|
+
@callbacks = builder.callbacks
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
streams: {
|
|
34
|
+
default: { dom: builder.nodes }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def handle(entity:, event:, build_entity:)
|
|
40
|
+
raise if event.nil?
|
|
41
|
+
|
|
42
|
+
actions = [
|
|
43
|
+
"interfacets:react-dom:action",
|
|
44
|
+
"interfacets:react-dom:memoized-action",
|
|
45
|
+
"interfacets:react-dom:online",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
raise("unknown event: #{event}") unless actions.include?(event.fetch("type"))
|
|
49
|
+
|
|
50
|
+
return if event.fetch("type") == "interfacets:react-dom:online"
|
|
51
|
+
|
|
52
|
+
payload = event.fetch("payload")
|
|
53
|
+
callback_id = payload.fetch("id")
|
|
54
|
+
callback = @callbacks.fetch(callback_id)
|
|
55
|
+
Evaluator.call(entity, callback, payload["event"])
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
module React
|
|
7
|
+
class Dom
|
|
8
|
+
attr_accessor :children, :handlers, :facet
|
|
9
|
+
|
|
10
|
+
def initialize(facet:)
|
|
11
|
+
@children = []
|
|
12
|
+
@portals = []
|
|
13
|
+
@handlers = {}
|
|
14
|
+
@facet = facet
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def reset
|
|
18
|
+
@children = []
|
|
19
|
+
@portals = []
|
|
20
|
+
@handlers = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def handle(facet:, payload:)
|
|
24
|
+
handler_id = payload.fetch("id")
|
|
25
|
+
handler = handlers.fetch(handler_id)
|
|
26
|
+
callback = handler.fetch(:callback)
|
|
27
|
+
og_facet = handler.fetch(:facet)
|
|
28
|
+
|
|
29
|
+
Evaluator.call(og_facet, callback, payload["event"])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def register_handler(id:, callback:, facet:)
|
|
33
|
+
@handlers[id] = { facet:, callback: }
|
|
34
|
+
|
|
35
|
+
{
|
|
36
|
+
type: "interfacets:react-dom:memoized-action",
|
|
37
|
+
payload: { id: },
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def string_node(str)
|
|
43
|
+
@children << {
|
|
44
|
+
type: "interfacets:string-node",
|
|
45
|
+
attributes: { value: str },
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def call(type, str = nil, **attrs)
|
|
50
|
+
raise("cannot pass both a block and a string") if str && block_given?
|
|
51
|
+
raise if type == :facet
|
|
52
|
+
|
|
53
|
+
new_element = {
|
|
54
|
+
type:,
|
|
55
|
+
children: [],
|
|
56
|
+
attributes: (
|
|
57
|
+
attrs.transform_values { |value|
|
|
58
|
+
if value.is_a?(Proc)
|
|
59
|
+
register_handler(
|
|
60
|
+
id: value.object_id.to_s,
|
|
61
|
+
callback: value,
|
|
62
|
+
facet:,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
type: "interfacets:react-dom:action",
|
|
67
|
+
payload: { id: value.object_id.to_s },
|
|
68
|
+
}
|
|
69
|
+
else
|
|
70
|
+
value
|
|
71
|
+
end
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if block_given? || str
|
|
77
|
+
# recurse
|
|
78
|
+
original_children = @children
|
|
79
|
+
@children = new_element[:children]
|
|
80
|
+
yield if block_given?
|
|
81
|
+
string_node(str) if str
|
|
82
|
+
@children = original_children
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
@children << new_element
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
module React
|
|
7
|
+
class Evaluator
|
|
8
|
+
def self.call(facet, handler, event)
|
|
9
|
+
new(facet).call(handler, event)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# This just isolates the evaluation so that we don't
|
|
13
|
+
# conflict with other methods
|
|
14
|
+
#
|
|
15
|
+
# for associations, this facet needs to be the relevant one...
|
|
16
|
+
attr_reader :facet
|
|
17
|
+
|
|
18
|
+
def initialize(facet)
|
|
19
|
+
@facet = facet
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(handler, event)
|
|
23
|
+
Shared::Utils.permissive_exec(self, event, &handler)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def channel(name)
|
|
27
|
+
System.current_bus.channel(name).builder
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
class SpeechToText
|
|
7
|
+
include Channels::Base
|
|
8
|
+
|
|
9
|
+
type("speechToText")
|
|
10
|
+
|
|
11
|
+
class Builder
|
|
12
|
+
attr_accessor :facet
|
|
13
|
+
attr_accessor :entity
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@event = {}
|
|
17
|
+
@callbacks = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def flush_event
|
|
21
|
+
@event.tap { @event = {} }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def record(
|
|
25
|
+
on_done: ->(*, **) {},
|
|
26
|
+
on_progress: ->(*,**) {},
|
|
27
|
+
on_submit: ->(*, **) {}
|
|
28
|
+
)
|
|
29
|
+
id = SecureRandom.uuid
|
|
30
|
+
@event = {
|
|
31
|
+
id:,
|
|
32
|
+
type: "record",
|
|
33
|
+
}
|
|
34
|
+
@callbacks[id] = {
|
|
35
|
+
on_progress:,
|
|
36
|
+
on_done:,
|
|
37
|
+
on_submit:,
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def callbacks
|
|
42
|
+
@callbacks ||= {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def event
|
|
46
|
+
@event ||= {}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_reader :helper
|
|
51
|
+
|
|
52
|
+
def prepare(entity)
|
|
53
|
+
@builder ||= Builder.new
|
|
54
|
+
@builder.entity = entity
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def builder(stream = "default")
|
|
58
|
+
@builder
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def handle(event:, entity:, **)
|
|
62
|
+
callback_id = event.dig("payload", "id")
|
|
63
|
+
|
|
64
|
+
case event.fetch("type")
|
|
65
|
+
when "text-result"
|
|
66
|
+
builder
|
|
67
|
+
.callbacks
|
|
68
|
+
.delete(callback_id)
|
|
69
|
+
.fetch(:on_done)
|
|
70
|
+
.then do
|
|
71
|
+
entity.instance_exec(event.dig("payload", "text"), &_1)
|
|
72
|
+
end
|
|
73
|
+
when "text-progress"
|
|
74
|
+
builder
|
|
75
|
+
.callbacks
|
|
76
|
+
.fetch(callback_id)
|
|
77
|
+
.fetch(:on_progress)
|
|
78
|
+
.then do
|
|
79
|
+
entity.instance_exec(event.dig("payload", "text"), &_1)
|
|
80
|
+
end
|
|
81
|
+
when "text-submit"
|
|
82
|
+
builder
|
|
83
|
+
.callbacks
|
|
84
|
+
.fetch(callback_id)
|
|
85
|
+
.fetch(:on_submit)
|
|
86
|
+
.then do
|
|
87
|
+
entity.instance_exec(event.dig("payload", "text"), &_1)
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
raise "unknown event type: #{event.inspect}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def result
|
|
95
|
+
{ streams: { default: @builder.flush_event } }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Channels
|
|
6
|
+
class Timer
|
|
7
|
+
include Channels::Base
|
|
8
|
+
|
|
9
|
+
type("interfacets:timer")
|
|
10
|
+
|
|
11
|
+
class Builder
|
|
12
|
+
attr_reader :event, :callbacks
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@event = {}
|
|
16
|
+
@callbacks = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def flush_event
|
|
20
|
+
@event.tap { @event = {} }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def callback(in_ms:, &block)
|
|
24
|
+
id = SecureRandom.uuid
|
|
25
|
+
event[:ms] = in_ms
|
|
26
|
+
event[:response] = { id: }
|
|
27
|
+
callbacks[id] = block
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def prepare(entity)
|
|
32
|
+
@builder ||= Builder.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def builder(stream = "defualt")
|
|
36
|
+
@builder
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def handle(event:, entity:, **)
|
|
40
|
+
callback_id = event.dig("payload", "id")
|
|
41
|
+
entity.instance_exec(&builder.callbacks.fetch(callback_id))
|
|
42
|
+
builder.callbacks.delete(callback_id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def result
|
|
46
|
+
{ streams: { default: @builder.flush_event } }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|