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,220 @@
|
|
|
1
|
+
# rubocop:disable all
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
# = Active Support \Concern
|
|
5
|
+
#
|
|
6
|
+
# A typical module looks like this:
|
|
7
|
+
#
|
|
8
|
+
# module M
|
|
9
|
+
# def self.included(base)
|
|
10
|
+
# base.extend ClassMethods
|
|
11
|
+
# base.class_eval do
|
|
12
|
+
# scope :disabled, -> { where(disabled: true) }
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# module ClassMethods
|
|
17
|
+
# ...
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# By using +ActiveSupport::Concern+ the above module could instead be
|
|
22
|
+
# written as:
|
|
23
|
+
#
|
|
24
|
+
# # require "active_support/concern"
|
|
25
|
+
#
|
|
26
|
+
# module M
|
|
27
|
+
# extend ActiveSupport::Concern
|
|
28
|
+
#
|
|
29
|
+
# included do
|
|
30
|
+
# scope :disabled, -> { where(disabled: true) }
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# class_methods do
|
|
34
|
+
# ...
|
|
35
|
+
# end
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
|
|
39
|
+
# and a +Bar+ module which depends on the former, we would typically write the
|
|
40
|
+
# following:
|
|
41
|
+
#
|
|
42
|
+
# module Foo
|
|
43
|
+
# def self.included(base)
|
|
44
|
+
# base.class_eval do
|
|
45
|
+
# def self.method_injected_by_foo
|
|
46
|
+
# ...
|
|
47
|
+
# end
|
|
48
|
+
# end
|
|
49
|
+
# end
|
|
50
|
+
# end
|
|
51
|
+
#
|
|
52
|
+
# module Bar
|
|
53
|
+
# def self.included(base)
|
|
54
|
+
# base.method_injected_by_foo
|
|
55
|
+
# end
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# class Host
|
|
59
|
+
# include Foo # We need to include this dependency for Bar
|
|
60
|
+
# include Bar # Bar is the module that Host really needs
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
|
|
64
|
+
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
|
|
65
|
+
#
|
|
66
|
+
# module Bar
|
|
67
|
+
# include Foo
|
|
68
|
+
# def self.included(base)
|
|
69
|
+
# base.method_injected_by_foo
|
|
70
|
+
# end
|
|
71
|
+
# end
|
|
72
|
+
#
|
|
73
|
+
# class Host
|
|
74
|
+
# include Bar
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
|
|
78
|
+
# is the +Bar+ module, not the +Host+ class. With +ActiveSupport::Concern+,
|
|
79
|
+
# module dependencies are properly resolved:
|
|
80
|
+
#
|
|
81
|
+
# # require "active_support/concern"
|
|
82
|
+
#
|
|
83
|
+
# module Foo
|
|
84
|
+
# extend ActiveSupport::Concern
|
|
85
|
+
# included do
|
|
86
|
+
# def self.method_injected_by_foo
|
|
87
|
+
# ...
|
|
88
|
+
# end
|
|
89
|
+
# end
|
|
90
|
+
# end
|
|
91
|
+
#
|
|
92
|
+
# module Bar
|
|
93
|
+
# extend ActiveSupport::Concern
|
|
94
|
+
# include Foo
|
|
95
|
+
#
|
|
96
|
+
# included do
|
|
97
|
+
# self.method_injected_by_foo
|
|
98
|
+
# end
|
|
99
|
+
# end
|
|
100
|
+
#
|
|
101
|
+
# class Host
|
|
102
|
+
# include Bar # It works, now Bar takes care of its dependencies
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# === Prepending concerns
|
|
106
|
+
#
|
|
107
|
+
# Just like <tt>include</tt>, concerns also support <tt>prepend</tt> with a corresponding
|
|
108
|
+
# <tt>prepended do</tt> callback. <tt>module ClassMethods</tt> or <tt>class_methods do</tt> are
|
|
109
|
+
# prepended as well.
|
|
110
|
+
#
|
|
111
|
+
# <tt>prepend</tt> is also used for any dependencies.
|
|
112
|
+
module Concern
|
|
113
|
+
class MultipleIncludedBlocks < StandardError # :nodoc:
|
|
114
|
+
def initialize
|
|
115
|
+
super "Cannot define multiple 'included' blocks for a Concern"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class MultiplePrependBlocks < StandardError # :nodoc:
|
|
120
|
+
def initialize
|
|
121
|
+
super "Cannot define multiple 'prepended' blocks for a Concern"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.extended(base) # :nodoc:
|
|
126
|
+
base.instance_variable_set(:@_dependencies, [])
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def append_features(base) # :nodoc:
|
|
130
|
+
if base.instance_variable_defined?(:@_dependencies)
|
|
131
|
+
base.instance_variable_get(:@_dependencies) << self
|
|
132
|
+
false
|
|
133
|
+
else
|
|
134
|
+
return false if base < self
|
|
135
|
+
@_dependencies.each { |dep| base.include(dep) }
|
|
136
|
+
Interfacets::Client::Assets.loader.load(self)
|
|
137
|
+
super
|
|
138
|
+
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
|
139
|
+
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def prepend_features(base) # :nodoc:
|
|
144
|
+
if base.instance_variable_defined?(:@_dependencies)
|
|
145
|
+
base.instance_variable_get(:@_dependencies).unshift self
|
|
146
|
+
false
|
|
147
|
+
else
|
|
148
|
+
return false if base < self
|
|
149
|
+
@_dependencies.each { |dep| base.prepend(dep) }
|
|
150
|
+
super
|
|
151
|
+
base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
|
152
|
+
base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Evaluate given block in context of base class,
|
|
157
|
+
# so that you can write class macros here.
|
|
158
|
+
# When you define more than one +included+ block, it raises an exception.
|
|
159
|
+
def included(base = nil, &block)
|
|
160
|
+
if base.nil?
|
|
161
|
+
if instance_variable_defined?(:@_included_block)
|
|
162
|
+
if @_included_block.source_location != block.source_location
|
|
163
|
+
raise MultipleIncludedBlocks
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
@_included_block = block
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
super
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Evaluate given block in context of base class,
|
|
174
|
+
# so that you can write class macros here.
|
|
175
|
+
# When you define more than one +prepended+ block, it raises an exception.
|
|
176
|
+
def prepended(base = nil, &block)
|
|
177
|
+
if base.nil?
|
|
178
|
+
if instance_variable_defined?(:@_prepended_block)
|
|
179
|
+
if @_prepended_block.source_location != block.source_location
|
|
180
|
+
raise MultiplePrependBlocks
|
|
181
|
+
end
|
|
182
|
+
else
|
|
183
|
+
@_prepended_block = block
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
super
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Define class methods from given block.
|
|
191
|
+
# You can define private class methods as well.
|
|
192
|
+
#
|
|
193
|
+
# module Example
|
|
194
|
+
# extend ActiveSupport::Concern
|
|
195
|
+
#
|
|
196
|
+
# class_methods do
|
|
197
|
+
# def foo; puts 'foo'; end
|
|
198
|
+
#
|
|
199
|
+
# private
|
|
200
|
+
# def bar; puts 'bar'; end
|
|
201
|
+
# end
|
|
202
|
+
# end
|
|
203
|
+
#
|
|
204
|
+
# class Buzz
|
|
205
|
+
# include Example
|
|
206
|
+
# end
|
|
207
|
+
#
|
|
208
|
+
# Buzz.foo # => "foo"
|
|
209
|
+
# Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
|
|
210
|
+
def class_methods(&class_methods_module_definition)
|
|
211
|
+
mod = const_defined?(:ClassMethods, false) ?
|
|
212
|
+
const_get(:ClassMethods) :
|
|
213
|
+
const_set(:ClassMethods, Module.new)
|
|
214
|
+
|
|
215
|
+
mod.module_eval(&class_methods_module_definition)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# rubocop:enable all
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
module Utils
|
|
6
|
+
module MrubyPatches
|
|
7
|
+
module HashExt
|
|
8
|
+
NO_DEFAULT = Object.new
|
|
9
|
+
|
|
10
|
+
def fetch(k, default = NO_DEFAULT)
|
|
11
|
+
if key?(k)
|
|
12
|
+
self[k]
|
|
13
|
+
elsif block_given?
|
|
14
|
+
yield
|
|
15
|
+
elsif default == NO_DEFAULT
|
|
16
|
+
raise("#{k} not found in #{inspect}")
|
|
17
|
+
else
|
|
18
|
+
default
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module InspectPatch
|
|
24
|
+
def inspect(...)
|
|
25
|
+
"<#{self.class}:#{self.object_id}>"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module ArrayPatch
|
|
30
|
+
def index_by
|
|
31
|
+
raise(ArgumentError) unless block_given?
|
|
32
|
+
|
|
33
|
+
result = {}
|
|
34
|
+
each { |elem| result[yield(elem)] = elem }
|
|
35
|
+
result
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module ObjectPresence
|
|
40
|
+
def blank?
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def present?
|
|
45
|
+
!blank?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module EnumerablePresence
|
|
50
|
+
def blank?
|
|
51
|
+
empty?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module FalsyPresence
|
|
56
|
+
def blank?
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module StringPresence
|
|
62
|
+
def blank?
|
|
63
|
+
match?(/^\s*$/)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if RUBY_ENGINE == "mruby"
|
|
68
|
+
Object.prepend(InspectPatch)
|
|
69
|
+
Hash.prepend(HashExt)
|
|
70
|
+
Array.prepend(ArrayPatch)
|
|
71
|
+
|
|
72
|
+
Object.include(ObjectPresence)
|
|
73
|
+
Enumerable.include(EnumerablePresence)
|
|
74
|
+
NilClass.include(FalsyPresence)
|
|
75
|
+
FalseClass.include(FalsyPresence)
|
|
76
|
+
String.include(StringPresence)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# rubocop:disable all
|
|
2
|
+
|
|
3
|
+
# copied from github
|
|
4
|
+
|
|
5
|
+
class OpenStruct
|
|
6
|
+
def initialize hash=nil
|
|
7
|
+
@table = {}
|
|
8
|
+
if hash
|
|
9
|
+
hash.each do |k,v|
|
|
10
|
+
k = k.to_sym
|
|
11
|
+
@table[k] = v
|
|
12
|
+
new_ostruct_member(k)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize_copy orig
|
|
18
|
+
super
|
|
19
|
+
@table = @table.dup
|
|
20
|
+
@table.each_key{|key| new_ostruct_member(key)}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_h
|
|
24
|
+
@table.dup
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def each_pair
|
|
28
|
+
return to_enum __method__ unless block_given?
|
|
29
|
+
@table.each{|p| yield p}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def new_ostruct_member name
|
|
33
|
+
name = name.to_sym
|
|
34
|
+
unless respond_to?(name)
|
|
35
|
+
define_singleton_method(name){ @table[name] }
|
|
36
|
+
define_singleton_method("#{name}=".to_sym){ |x| @table[name] = x }
|
|
37
|
+
end
|
|
38
|
+
name
|
|
39
|
+
end
|
|
40
|
+
protected :new_ostruct_member
|
|
41
|
+
|
|
42
|
+
def inspect
|
|
43
|
+
str = "#<#{self.class}"
|
|
44
|
+
ary = []
|
|
45
|
+
@table.each do |k,v|
|
|
46
|
+
ary << "#{k}=#{v}"
|
|
47
|
+
end
|
|
48
|
+
if 0 < ary.length
|
|
49
|
+
str << ' '
|
|
50
|
+
str << ary.join(', ')
|
|
51
|
+
end
|
|
52
|
+
str << '>'
|
|
53
|
+
end
|
|
54
|
+
alias :to_s :inspect
|
|
55
|
+
|
|
56
|
+
def delete_field name
|
|
57
|
+
sym = name.to_sym
|
|
58
|
+
singleton_class.__send__ :remove_method, sym, "#{sym}=".to_sym
|
|
59
|
+
@table.delete sym
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def == other
|
|
63
|
+
return false unless other.kind_of?(OpenStruct)
|
|
64
|
+
@table == other.table
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def [] key
|
|
68
|
+
@table[key.to_sym]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def []= key, value
|
|
72
|
+
@table[new_ostruct_member(key)] = value
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def eql? other
|
|
76
|
+
return false unless other.kind_of?(OpenStruct)
|
|
77
|
+
@table.eql?(other.table)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def hash
|
|
81
|
+
@table.hash
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
attr_reader :table
|
|
85
|
+
protected :table
|
|
86
|
+
|
|
87
|
+
def method_missing mid, *args
|
|
88
|
+
mname = mid.to_s
|
|
89
|
+
len = args.length
|
|
90
|
+
if mname.chomp! '='
|
|
91
|
+
if len != 1
|
|
92
|
+
raise ArgumentError, "wrong number of arguments (#{len} for 1)"
|
|
93
|
+
end
|
|
94
|
+
@table[new_ostruct_member(mname)] = args[0]
|
|
95
|
+
elsif len == 0
|
|
96
|
+
@table[mid]
|
|
97
|
+
else
|
|
98
|
+
raise NoMethodError, "undefined method `#{mid}' for #{self}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
# rubocop:enable all
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# rubocop:disable all
|
|
2
|
+
|
|
3
|
+
if RUBY_ENGINE == "mruby"
|
|
4
|
+
|
|
5
|
+
class SecureRandom
|
|
6
|
+
class << self
|
|
7
|
+
def random_bytes(n=nil)
|
|
8
|
+
n = n ? n.to_int : 16
|
|
9
|
+
|
|
10
|
+
# Need to read more than the number of bytes because sometimes it comes
|
|
11
|
+
# back as less. Not sure why, but whatever.
|
|
12
|
+
begin
|
|
13
|
+
File.open("/dev/urandom", 'r') {|f|
|
|
14
|
+
ret = f.read(n*2)
|
|
15
|
+
unless ret.length > n
|
|
16
|
+
raise NotImplementedError, "Unexpected partial read from random device: only #{ret.length} for #{n} bytes"
|
|
17
|
+
end
|
|
18
|
+
return ret[0..n]
|
|
19
|
+
}
|
|
20
|
+
rescue
|
|
21
|
+
raise File::NoFileError, "No random device"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
raise NotImplementedError, "No random device"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def hex(n=nil)
|
|
28
|
+
random_bytes(n).unpack("H*")[0]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def base64(n=nil)
|
|
32
|
+
[random_bytes(n)].pack("m*").gsub("\n", "")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def urlsafe_base64(n=nil, padding=false)
|
|
36
|
+
s = base64.gsub("+", "-").gsub("/", "_")
|
|
37
|
+
s.gsub!("=", "") unless padding
|
|
38
|
+
s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def random_number(n=0)
|
|
42
|
+
if 0 < n
|
|
43
|
+
hex = n.to_s(16)
|
|
44
|
+
hex = '0' + hex if (hex.length & 1) == 1
|
|
45
|
+
bin = [hex].pack("H*")
|
|
46
|
+
mask = bin[0].ord
|
|
47
|
+
mask |= mask >> 1
|
|
48
|
+
mask |= mask >> 2
|
|
49
|
+
mask |= mask >> 4
|
|
50
|
+
|
|
51
|
+
loop do
|
|
52
|
+
rnd = random_bytes(bin.length)
|
|
53
|
+
rnd[0] = (rnd[0].ord & mask).chr
|
|
54
|
+
return rnd.unpack("H*")[0].hex if rnd < bin
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def uuid
|
|
62
|
+
ary = random_bytes(16).unpack("nnnnnnnn")
|
|
63
|
+
ary[3] = (ary[3] & 0x0fff) | 0x4000
|
|
64
|
+
ary[4] = (ary[4] & 0x3fff) | 0x8000
|
|
65
|
+
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % ary
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Client
|
|
5
|
+
class View
|
|
6
|
+
class Evaluator
|
|
7
|
+
def self.call(...)
|
|
8
|
+
new(...).call
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :entity, :channels, :block
|
|
12
|
+
def initialize(entity:, channels:, block:)
|
|
13
|
+
@entity = entity
|
|
14
|
+
@channels = channels
|
|
15
|
+
@block = block
|
|
16
|
+
@data = Hash.new { |h, k| h[k] = { streams: {} } }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
instance_exec(entity, &@block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render(channel_id, stream: "default", &block)
|
|
24
|
+
channels
|
|
25
|
+
.fetch(channel_id.to_s)
|
|
26
|
+
.builder(stream.to_s)
|
|
27
|
+
.then { instance_exec(_1, &block) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def channel(name)
|
|
31
|
+
@channels.fetch(name)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
def view(&block)
|
|
37
|
+
@view = block if block_given?
|
|
38
|
+
@view
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render(entity:, channels:)
|
|
43
|
+
Evaluator.call(entity: entity, channels:, block: self.class.view)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
FROM emscripten/emsdk:3.1.62
|
|
2
|
+
|
|
3
|
+
RUN apt update
|
|
4
|
+
RUN apt install -y vim git
|
|
5
|
+
RUN apt install -y ruby
|
|
6
|
+
RUN git clone https://github.com/mruby/mruby.git /home/root/interfacets/mruby
|
|
7
|
+
WORKDIR /home/root/interfacets/mruby
|
|
8
|
+
# RUN git fetch origin master && git reset --hard origin/master
|
|
9
|
+
RUN git fetch origin --tags && git reset --hard 3.4.0
|
|
10
|
+
RUN rake all test
|
|
11
|
+
ENV PATH="/home/root/interfacets/mruby/bin:${PATH}"
|
|
12
|
+
COPY ./lib/interfacets/mruby/build_config.rb \
|
|
13
|
+
/home/root/interfacets/
|
|
14
|
+
WORKDIR /home/root/interfacets
|
|
15
|
+
RUN cd mruby && \
|
|
16
|
+
sed -i '/MRB_STR_LENGTH_MAX 1048576/c#define MRB_STR_LENGTH_MAX 0' src/string.c && \
|
|
17
|
+
MRUBY_CONFIG=../build_config.rb rake
|
|
18
|
+
RUN mkdir -p assets/mruby build
|
|
19
|
+
RUN cp -r mruby/include assets/mruby \
|
|
20
|
+
&& cp mruby/build/emscripten/lib/libmruby.a assets/mruby
|
|
21
|
+
COPY ./lib/interfacets/mruby/init.c \
|
|
22
|
+
./lib/interfacets/mruby/entrypoint.rb \
|
|
23
|
+
/home/root/interfacets/
|
|
24
|
+
# RUN mrbc --help
|
|
25
|
+
RUN mrbc -B ruby_app -o build/app.c entrypoint.rb
|
|
26
|
+
RUN echo "\n" >> build/app.c && cat init.c >> build/app.c
|
|
27
|
+
RUN emcc \
|
|
28
|
+
-s EXPORT_NAME=MRuby \
|
|
29
|
+
-s EXPORTED_FUNCTIONS='_ruby_eval,_main,_ruby_call,stringToNewUTF8' \
|
|
30
|
+
-s ENVIRONMENT=web \
|
|
31
|
+
-s MODULARIZE=1 \
|
|
32
|
+
-s EXPORT_ES6=1 \
|
|
33
|
+
-s ALLOW_MEMORY_GROWTH=1 \
|
|
34
|
+
-s SINGLE_FILE=1 \
|
|
35
|
+
-I ./assets/mruby/include \
|
|
36
|
+
build/app.c \
|
|
37
|
+
./assets/mruby/libmruby.a \
|
|
38
|
+
-o build/ruby.js
|
|
39
|
+
|
|
40
|
+
RUN emcc \
|
|
41
|
+
-s EXPORT_NAME=MRuby \
|
|
42
|
+
# -s EXPORTED_FUNCTIONS='_ruby_eval,_main,_ruby_call,stringToNewUTF8' \
|
|
43
|
+
-s ENVIRONMENT=node \
|
|
44
|
+
-s MODULARIZE=1 \
|
|
45
|
+
-s EXPORT_ES6=1 \
|
|
46
|
+
-s SIDE_MODULE=1 \
|
|
47
|
+
-s ALLOW_MEMORY_GROWTH=1 \
|
|
48
|
+
-I ./assets/mruby/include \
|
|
49
|
+
build/app.c \
|
|
50
|
+
./assets/mruby/libmruby.a \
|
|
51
|
+
-o build/test.wasm
|
|
52
|
+
|
|
53
|
+
RUN emcc \
|
|
54
|
+
-s EXPORT_NAME=MRuby \
|
|
55
|
+
-s EXPORTED_FUNCTIONS='_ruby_eval,_main,_ruby_call,stringToNewUTF8' \
|
|
56
|
+
-s ENVIRONMENT=node \
|
|
57
|
+
-s MODULARIZE=1 \
|
|
58
|
+
-s EXPORT_ES6=1 \
|
|
59
|
+
-s MAIN_MODULE=1 \
|
|
60
|
+
-s ALLOW_MEMORY_GROWTH=1 \
|
|
61
|
+
-I ./assets/mruby/include \
|
|
62
|
+
build/app.c \
|
|
63
|
+
./assets/mruby/libmruby.a \
|
|
64
|
+
-o build/test.js
|
|
65
|
+
|
|
66
|
+
RUN mv build /build
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
MRuby::Build.new do |conf|
|
|
4
|
+
toolchain :gcc
|
|
5
|
+
conf.gembox("full-core")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
MRuby::CrossBuild.new("emscripten") do |conf|
|
|
9
|
+
toolchain :clang
|
|
10
|
+
conf.gembox("full-core")
|
|
11
|
+
conf.cc.command = "emcc"
|
|
12
|
+
conf.cc.flags = ["-Os", "-fPIC"]
|
|
13
|
+
conf.linker.command = "emcc"
|
|
14
|
+
conf.archiver.command = "emar"
|
|
15
|
+
|
|
16
|
+
conf.gem(github: "mattn/mruby-json")
|
|
17
|
+
conf.gem(github: "mattn/mruby-base64")
|
|
18
|
+
conf.gem(github: "monochromegane/mruby-secure-random")
|
|
19
|
+
conf.gem(github: "iij/mruby-regexp-pcre")
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kernel
|
|
4
|
+
def wrapped_eval(str, path)
|
|
5
|
+
eval(str, binding, path)
|
|
6
|
+
rescue StandardError, SyntaxError => e
|
|
7
|
+
$stderr.puts(e.class)
|
|
8
|
+
$stderr.puts(e.message)
|
|
9
|
+
$stderr.puts(e.backtrace.join("\n"))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def wrapped_call(json_string)
|
|
13
|
+
spec = JSON.parse(json_string)
|
|
14
|
+
eval(spec.fetch("receiver")).send(
|
|
15
|
+
spec.fetch("method"),
|
|
16
|
+
*spec.fetch("args", []),
|
|
17
|
+
)
|
|
18
|
+
rescue StandardError, SyntaxError => e
|
|
19
|
+
$stderr.puts(e.class)
|
|
20
|
+
$stderr.puts(e.message)
|
|
21
|
+
$stderr.puts(e.backtrace.join("\n"))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#include <mruby.h>
|
|
2
|
+
#include <mruby/irep.h>
|
|
3
|
+
#include <emscripten.h>
|
|
4
|
+
|
|
5
|
+
mrb_value js_eval(mrb_state* mrb, mrb_value self)
|
|
6
|
+
{
|
|
7
|
+
char *js_code;
|
|
8
|
+
mrb_get_args(mrb, "z", &js_code);
|
|
9
|
+
emscripten_run_script(js_code);
|
|
10
|
+
return self;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
mrb_state *mrb;
|
|
14
|
+
void EMSCRIPTEN_KEEPALIVE ruby_eval(char* str, char* path) {
|
|
15
|
+
int ai = mrb_gc_arena_save(mrb);
|
|
16
|
+
mrb_funcall(
|
|
17
|
+
mrb,
|
|
18
|
+
mrb_top_self(mrb),
|
|
19
|
+
"wrapped_eval",
|
|
20
|
+
2,
|
|
21
|
+
mrb_str_new_cstr(mrb, str),
|
|
22
|
+
mrb_str_new_cstr(mrb, path)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (mrb->exc) mrb_print_error(mrb);
|
|
26
|
+
mrb_gc_arena_restore(mrb, ai);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// json of the form:
|
|
30
|
+
// {receiver: , method:, args: [ ...] ]
|
|
31
|
+
void EMSCRIPTEN_KEEPALIVE ruby_call(char* json) {
|
|
32
|
+
int ai = mrb_gc_arena_save(mrb);
|
|
33
|
+
|
|
34
|
+
mrb_funcall(
|
|
35
|
+
mrb,
|
|
36
|
+
mrb_top_self(mrb),
|
|
37
|
+
"wrapped_call",
|
|
38
|
+
1,
|
|
39
|
+
mrb_str_new_cstr(mrb, json)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (mrb->exc) mrb_print_error(mrb);
|
|
43
|
+
mrb_gc_arena_restore(mrb, ai);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
int main() {
|
|
47
|
+
// Leave this open for all eternity
|
|
48
|
+
mrb = mrb_open();
|
|
49
|
+
|
|
50
|
+
if (!mrb) { /* handle error */ }
|
|
51
|
+
|
|
52
|
+
mrb_define_method(
|
|
53
|
+
mrb,
|
|
54
|
+
mrb->kernel_module,
|
|
55
|
+
"js_eval",
|
|
56
|
+
js_eval,
|
|
57
|
+
MRB_ARGS_REQ(1)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
mrb_load_irep(mrb, ruby_app);
|
|
61
|
+
|
|
62
|
+
// If an exception, print error
|
|
63
|
+
if (mrb->exc) mrb_print_error(mrb);
|
|
64
|
+
|
|
65
|
+
return 0;
|
|
66
|
+
}
|