interfacets 0.1.0 → 0.9.99
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/.tmp +5 -0
- data/LICENSE +21 -0
- data/Rakefile +9 -7
- data/lib/interfacets/client/actor.rb +20 -0
- data/lib/interfacets/client/assets.rb +210 -0
- data/lib/interfacets/client/bus.rb +73 -0
- data/lib/interfacets/client/channels/api.rb +102 -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/registry.rb +23 -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 +115 -0
- data/lib/interfacets/client/utils/securerandom.rb +69 -0
- data/lib/interfacets/client.rb +13 -0
- data/lib/interfacets/component_registry.rb +115 -0
- data/lib/interfacets/component_schema_parser.rb +84 -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 +44 -0
- data/lib/interfacets/server/assets/facet.rb +63 -0
- data/lib/interfacets/server/assets.rb +216 -0
- data/lib/interfacets/server/basic_router.rb +79 -0
- data/lib/interfacets/server/bus.rb +34 -0
- data/lib/interfacets/server/config.rb +87 -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 +51 -0
- data/lib/interfacets/shared/basic_routable.rb +45 -0
- data/lib/interfacets/shared/entities/bus.rb +230 -0
- data/lib/interfacets/shared/entities/collection_proxy.rb +190 -0
- data/lib/interfacets/shared/entities/specs/handlers.rb +133 -0
- data/lib/interfacets/shared/entities/specs.rb +161 -0
- data/lib/interfacets/shared/entity.rb +102 -0
- data/lib/interfacets/shared/entity_dsl.rb +154 -0
- data/lib/interfacets/shared/facet.rb +200 -0
- data/lib/interfacets/shared/generated_store.rb +149 -0
- data/lib/interfacets/shared/utils.rb +54 -0
- data/lib/interfacets/shared/validations.rb +75 -0
- data/lib/interfacets/shared/view.rb +74 -0
- data/lib/interfacets/test/component_registry.rb +63 -0
- data/lib/interfacets/test/js/inline_bus.rb +100 -0
- data/lib/interfacets/test/js/nodo_bus.rb +98 -0
- data/lib/interfacets/test/js/receivers/api.rb +48 -0
- data/lib/interfacets/test/js/receivers/react/node/xml_parser.rb +75 -0
- data/lib/interfacets/test/js/receivers/react/node.rb +133 -0
- data/lib/interfacets/test/js/receivers/react.rb +32 -0
- data/lib/interfacets/test/js/receivers/timer.rb +77 -0
- data/lib/interfacets/test/js/receivers/url.rb +60 -0
- data/lib/interfacets/test/standard_elements.yml +173 -0
- data/lib/interfacets/test/ui_simulator.rb +75 -0
- data/lib/interfacets/test/validation_engine.rb +151 -0
- data/lib/interfacets/test.rb +13 -0
- data/lib/interfacets/version.rb +1 -1
- data/lib/interfacets.rb +29 -2
- metadata +114 -6
- data/README.md +0 -35
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'json'
|
|
6
|
+
require "interfacets/component_schema_parser"
|
|
7
|
+
|
|
8
|
+
module Interfacets
|
|
9
|
+
class ComponentRegistry
|
|
10
|
+
def initialize(config_path:)
|
|
11
|
+
@config_path = config_path
|
|
12
|
+
@schema_cache = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def write_client_registry(path:)
|
|
16
|
+
components = load_components
|
|
17
|
+
content = generate_registry_content(components)
|
|
18
|
+
write_atomic(path, content)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def load_components
|
|
24
|
+
raise Errno::ENOENT, "Configuration file not found at #{@config_path}" unless File.exist?(@config_path)
|
|
25
|
+
|
|
26
|
+
# 1. Load the raw YAML
|
|
27
|
+
components = YAML.load_file(@config_path, aliases: true) || {}
|
|
28
|
+
|
|
29
|
+
# 2. Parse with the V2 parser
|
|
30
|
+
parser = Interfacets::ComponentSchemaParser.new
|
|
31
|
+
parsed_components = {}
|
|
32
|
+
components.each do |name, config|
|
|
33
|
+
next unless config.is_a?(Hash)
|
|
34
|
+
component_data = { name => config }
|
|
35
|
+
# The result of parse is the component data, which we merge.
|
|
36
|
+
parsed_components.merge!(parser.parse(component_data))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
parsed_components
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def generate_registry_content(components)
|
|
43
|
+
registry_components = components.select { |_, config| config.key?("js") || has_transforms?(config) }
|
|
44
|
+
|
|
45
|
+
imports = []
|
|
46
|
+
mappings = []
|
|
47
|
+
any_transforms = false
|
|
48
|
+
|
|
49
|
+
registry_components.each do |name, config|
|
|
50
|
+
js_config = config["js"]
|
|
51
|
+
path = js_config&.dig("path")
|
|
52
|
+
|
|
53
|
+
transforms = extract_transforms(config)
|
|
54
|
+
|
|
55
|
+
if path
|
|
56
|
+
imports << if js_config["default"]
|
|
57
|
+
"import #{name} from \"#{path}\";"
|
|
58
|
+
elsif (export = js_config["export"])
|
|
59
|
+
"import { #{export} as #{name} } from \"#{path}\";"
|
|
60
|
+
else
|
|
61
|
+
"import { #{name} } from \"#{path}\";"
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
# Native element
|
|
65
|
+
imports << "const #{name} = \"#{name}\";"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if transforms.any?
|
|
69
|
+
any_transforms = true
|
|
70
|
+
mappings << " #{name}: withTransform(#{name}, #{transforms.to_json}),"
|
|
71
|
+
else
|
|
72
|
+
mappings << " #{name},"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if any_transforms
|
|
77
|
+
imports.unshift('import { withTransform } from "interfacets/withTransform";')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
template = []
|
|
81
|
+
template << imports.join("\n") if imports.any?
|
|
82
|
+
template << <<~JS.strip
|
|
83
|
+
export const registry = {
|
|
84
|
+
#{mappings.join("\n")}
|
|
85
|
+
};
|
|
86
|
+
JS
|
|
87
|
+
|
|
88
|
+
"#{template.join("\n\n")}\n"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def has_transforms?(config)
|
|
92
|
+
extract_transforms(config).any?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def extract_transforms(config)
|
|
96
|
+
transforms = {}
|
|
97
|
+
if props = config["props"]
|
|
98
|
+
props.each do |prop_name, prop_config|
|
|
99
|
+
if prop_config["is_event"] && prop_config.key?("transform")
|
|
100
|
+
transforms[prop_name] = prop_config["transform"]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
transforms
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def write_atomic(path, content)
|
|
110
|
+
temp_file = "#{path}.tmp"
|
|
111
|
+
File.write(temp_file, content)
|
|
112
|
+
FileUtils.mv(temp_file, path)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json_schemer"
|
|
4
|
+
|
|
5
|
+
module Interfacets
|
|
6
|
+
class ComponentSchemaParser
|
|
7
|
+
SCHEMA = {
|
|
8
|
+
"$schema" => "http://json-schema.org/draft-07/schema#",
|
|
9
|
+
"type" => "object",
|
|
10
|
+
"patternProperties" => {
|
|
11
|
+
"^.*$" => {
|
|
12
|
+
"type" => "object",
|
|
13
|
+
"properties" => {
|
|
14
|
+
"schema" => {
|
|
15
|
+
"if" => { "type" => "object" },
|
|
16
|
+
"then" => {
|
|
17
|
+
"not" => { "required" => ["props"] }
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"props" => {
|
|
21
|
+
"type" => "object",
|
|
22
|
+
"patternProperties" => {
|
|
23
|
+
"^.*$" => {
|
|
24
|
+
"type" => "object",
|
|
25
|
+
"properties" => {
|
|
26
|
+
"is_event" => { "type" => "boolean" }
|
|
27
|
+
},
|
|
28
|
+
"if" => {
|
|
29
|
+
"required" => ["is_event"],
|
|
30
|
+
"properties" => { "is_event" => { "const" => true } }
|
|
31
|
+
},
|
|
32
|
+
"then" => {
|
|
33
|
+
"properties" => {
|
|
34
|
+
"transform" => { "$ref" => "#/definitions/Transform" },
|
|
35
|
+
"payload" => { "type" => "object" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"definitions" => {
|
|
45
|
+
"Transform" => {
|
|
46
|
+
"oneOf" => [
|
|
47
|
+
{ "type" => "null" },
|
|
48
|
+
{ "type" => "string" },
|
|
49
|
+
{
|
|
50
|
+
"type" => "object",
|
|
51
|
+
"additionalProperties" => {
|
|
52
|
+
"type" => "array",
|
|
53
|
+
"items" => [
|
|
54
|
+
{ "type" => "integer" }
|
|
55
|
+
],
|
|
56
|
+
"additionalItems" => { "type" => "string" },
|
|
57
|
+
"minItems" => 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}.freeze
|
|
64
|
+
|
|
65
|
+
SCHEMER = JSONSchemer.schema(SCHEMA)
|
|
66
|
+
|
|
67
|
+
def parse(component_config)
|
|
68
|
+
errors = SCHEMER.validate(component_config).to_a
|
|
69
|
+
if errors.any?
|
|
70
|
+
if errors.any? { |e| e["data_pointer"].include?("/schema") && e["type"] == "not" }
|
|
71
|
+
raise StandardError, "Old schema format is not supported."
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
message = "Validation failed:\n"
|
|
75
|
+
errors.each do |error|
|
|
76
|
+
message += " - #{error["data_pointer"]}: #{error["type"]} #{error["details"]}\n"
|
|
77
|
+
end
|
|
78
|
+
raise StandardError, message
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
component_config
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
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
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Server
|
|
5
|
+
class Api
|
|
6
|
+
attr_reader :entity, :name, :registry
|
|
7
|
+
def initialize(entity:, name:, registry:)
|
|
8
|
+
@entity = entity
|
|
9
|
+
@name = name
|
|
10
|
+
@registry = registry
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def handle(event)
|
|
14
|
+
Shared::Entities::Bus
|
|
15
|
+
.new(entity:)
|
|
16
|
+
.handle(event:)
|
|
17
|
+
|
|
18
|
+
if entity.channel.rendered?
|
|
19
|
+
entity.channel.render_facet
|
|
20
|
+
else
|
|
21
|
+
emit("after_#{event.fetch("action")}", nesting: event.fetch("nesting"))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render
|
|
26
|
+
emit("after_load", nesting: ["root"])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def emit(action, nesting: )
|
|
32
|
+
{
|
|
33
|
+
facet: name,
|
|
34
|
+
payload: (
|
|
35
|
+
Shared::Entities::Bus
|
|
36
|
+
.new(entity:)
|
|
37
|
+
.serialize(to: "client", action:, nesting:)
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Server
|
|
5
|
+
module Assets
|
|
6
|
+
class Facet
|
|
7
|
+
def self.register(klass:, assets:)
|
|
8
|
+
assets[klass.name] = new(klass).code
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :klass
|
|
12
|
+
def initialize(klass)
|
|
13
|
+
@klass = klass
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def code
|
|
17
|
+
# nest it all so names resolve
|
|
18
|
+
*mods, klass_name = klass.name.split("::")
|
|
19
|
+
|
|
20
|
+
headers = []
|
|
21
|
+
footers = []
|
|
22
|
+
|
|
23
|
+
current_mod = ""
|
|
24
|
+
mods.each do |mod_name|
|
|
25
|
+
current_mod += "::#{mod_name}"
|
|
26
|
+
type = current_mod.constantize.is_a?(Class) ? :class : :module
|
|
27
|
+
headers << "#{type} #{mod_name}"
|
|
28
|
+
footers << "end"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
headers << "class #{klass_name}"
|
|
32
|
+
headers << " include Interfacets::Shared::Facet"
|
|
33
|
+
footers << "end"
|
|
34
|
+
|
|
35
|
+
<<~TXT
|
|
36
|
+
#{headers.join("\n")}
|
|
37
|
+
|
|
38
|
+
include Interfacets::Shared::Facets::Schema
|
|
39
|
+
|
|
40
|
+
view_spec #{write_source(klass.client_config.view.block)}
|
|
41
|
+
|
|
42
|
+
entity_spec #{write_source(klass.client_config.api.block)}
|
|
43
|
+
|
|
44
|
+
client_spec #{write_source(klass.client_config.entity.block)}
|
|
45
|
+
#{footers.join("\n")}
|
|
46
|
+
TXT
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def write_source(block)
|
|
52
|
+
return unless block
|
|
53
|
+
|
|
54
|
+
RubyVM::AbstractSyntaxTree
|
|
55
|
+
.of(block, keep_script_lines: true)
|
|
56
|
+
.source
|
|
57
|
+
.strip
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|