funicular 0.1.0 → 0.2.1
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/CHANGELOG.md +24 -0
- data/README.md +12 -4
- data/Rakefile +29 -0
- data/docs/architecture.md +113 -404
- data/lib/funicular/assets/funicular.css +23 -0
- data/lib/funicular/compiler.rb +25 -18
- data/lib/funicular/helpers/picoruby_helper.rb +65 -3
- data/lib/funicular/middleware.rb +34 -9
- data/lib/funicular/plugin.rb +147 -0
- data/lib/funicular/schema.rb +167 -0
- data/lib/funicular/ssr/runtime.rb +101 -0
- data/lib/funicular/ssr.rb +51 -0
- data/lib/funicular/testing/node_runner.mjs +293 -0
- data/lib/funicular/testing/node_runner.rb +190 -0
- data/lib/funicular/testing.rb +22 -0
- data/lib/funicular/vendor/picorbc/picorbc.wasm +0 -0
- data/lib/funicular/vendor/picoruby/debug/picoruby.js +115 -87
- data/lib/funicular/vendor/picoruby/debug/picoruby.wasm +0 -0
- data/lib/funicular/vendor/picoruby/dist/picoruby.js +1 -1
- data/lib/funicular/vendor/picoruby/dist/picoruby.wasm +0 -0
- data/lib/funicular/vendor/picoruby-test-node/VERSION +1 -0
- data/lib/funicular/vendor/picoruby-test-node/picoruby.js +6908 -0
- data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm +0 -0
- data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm.map +1 -0
- data/lib/funicular/version.rb +1 -1
- data/lib/funicular.rb +3 -0
- data/lib/generators/funicular/chat/chat_generator.rb +104 -0
- data/lib/generators/funicular/chat/templates/application_cable_channel.rb.tt +4 -0
- data/lib/generators/funicular/chat/templates/application_cable_connection.rb.tt +4 -0
- data/lib/generators/funicular/chat/templates/create_funicular_chat_messages.rb.tt +10 -0
- data/lib/generators/funicular/chat/templates/funicular_chat.css.tt +141 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_channel.rb.tt +5 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_component.rb.tt +135 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_component_picotest.rb.tt +64 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_controller.rb.tt +4 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_message.rb.tt +13 -0
- data/lib/generators/funicular/chat/templates/funicular_chat_messages_controller.rb.tt +23 -0
- data/lib/generators/funicular/chat/templates/initializer.rb.tt +4 -0
- data/lib/generators/funicular/chat/templates/show.html.erb.tt +6 -0
- data/lib/tasks/funicular.rake +87 -4
- data/minitest/fixtures/funicular_app/components/greeting_component.rb +16 -0
- data/minitest/fixtures/funicular_app/initializer.rb +5 -0
- data/minitest/form_for_test.rb +106 -0
- data/minitest/hydration_test.rb +87 -0
- data/minitest/plugin_test.rb +51 -0
- data/minitest/schema_test.rb +106 -0
- data/minitest/ssr_test.rb +94 -0
- data/minitest/validations_test.rb +183 -0
- data/mrbgem.rake +1 -0
- data/mrblib/0_validations.rb +206 -0
- data/mrblib/1_validators.rb +180 -0
- data/mrblib/cable.rb +24 -9
- data/mrblib/component.rb +246 -47
- data/mrblib/debug.rb +3 -0
- data/mrblib/differ.rb +47 -37
- data/mrblib/file_upload.rb +9 -1
- data/mrblib/form_builder.rb +28 -6
- data/mrblib/funicular.rb +97 -8
- data/mrblib/html_serializer.rb +121 -0
- data/mrblib/http.rb +123 -29
- data/mrblib/model.rb +50 -0
- data/mrblib/patcher.rb +78 -8
- data/mrblib/router.rb +40 -3
- data/mrblib/store.rb +304 -0
- data/mrblib/store_collection.rb +171 -0
- data/mrblib/store_singleton.rb +79 -0
- data/mrblib/vdom.rb +2 -0
- data/sig/cable.rbs +1 -0
- data/sig/component.rbs +13 -5
- data/sig/funicular.rbs +14 -1
- data/sig/html_serializer.rbs +20 -0
- data/sig/http.rbs +21 -6
- data/sig/model.rbs +6 -1
- data/sig/patcher.rbs +4 -1
- data/sig/router.rbs +3 -2
- data/sig/store.rbs +89 -0
- data/sig/store_collection.rbs +43 -0
- data/sig/store_singleton.rbs +19 -0
- data/sig/validations.rbs +103 -0
- data/sig/vdom.rbs +6 -6
- metadata +48 -12
- data/docs/README.md +0 -419
- data/docs/advanced-features.md +0 -632
- data/docs/components-and-state.md +0 -539
- data/docs/data-fetching.md +0 -528
- data/docs/forms.md +0 -446
- data/docs/rails-integration.md +0 -426
- data/docs/realtime.md +0 -543
- data/docs/routing-and-navigation.md +0 -427
- data/docs/styling.md +0 -285
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
class Store
|
|
3
|
+
# Singleton store: one value per scope. Suitable for things like a
|
|
4
|
+
# per-channel draft text or per-user preferences blob.
|
|
5
|
+
#
|
|
6
|
+
# class DraftStore < Funicular::Store::Singleton
|
|
7
|
+
# database "funicular_drafts"
|
|
8
|
+
# scope :channel_id
|
|
9
|
+
# cleared_on :logout
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# draft = DraftStore.where(channel_id: 1)
|
|
13
|
+
# draft.value = "hello"
|
|
14
|
+
# draft.value # => "hello"
|
|
15
|
+
# draft.delete
|
|
16
|
+
class Singleton < Store
|
|
17
|
+
def self.scope_class
|
|
18
|
+
Funicular::Store::Singleton::Scope
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Scope < Funicular::Store::Scope
|
|
22
|
+
def value
|
|
23
|
+
rec = read
|
|
24
|
+
return nil unless rec.is_a?(Hash)
|
|
25
|
+
if expired_record?(rec)
|
|
26
|
+
erase
|
|
27
|
+
return nil
|
|
28
|
+
end
|
|
29
|
+
rec["v"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Setting "" on a String-typed value deletes the entry, matching
|
|
33
|
+
# the semantics of the original DraftStore.
|
|
34
|
+
def value=(v)
|
|
35
|
+
if v.is_a?(String) && v.empty?
|
|
36
|
+
delete
|
|
37
|
+
return v
|
|
38
|
+
end
|
|
39
|
+
write(v)
|
|
40
|
+
fire_change(v)
|
|
41
|
+
v
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete
|
|
45
|
+
erase
|
|
46
|
+
fire_change(nil)
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def present?
|
|
51
|
+
!value.nil?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def expired?
|
|
55
|
+
rec = read
|
|
56
|
+
expired_record?(rec)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def read
|
|
62
|
+
kvs[storage_key]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def write(v)
|
|
66
|
+
kvs[storage_key] = {
|
|
67
|
+
"v" => v,
|
|
68
|
+
"wrote_at" => now_seconds,
|
|
69
|
+
"expires_in" => @store_class.__expires_in
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def erase
|
|
74
|
+
kvs.delete(storage_key)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/mrblib/vdom.rb
CHANGED
|
@@ -132,8 +132,10 @@ module Funicular
|
|
|
132
132
|
# Handle boolean attributes
|
|
133
133
|
if value.nil? || value.to_s == "false"
|
|
134
134
|
# Do not set attribute (leave it absent)
|
|
135
|
+
dom_node[key_str] = false
|
|
135
136
|
else
|
|
136
137
|
dom_node.setAttribute(key_str, key_str)
|
|
138
|
+
dom_node[key_str] = true
|
|
137
139
|
end
|
|
138
140
|
else
|
|
139
141
|
# Attribute
|
data/sig/cable.rbs
CHANGED
|
@@ -41,6 +41,7 @@ module Funicular
|
|
|
41
41
|
def notify_subscription_confirmed: (String identifier) -> void
|
|
42
42
|
def notify_subscription_rejected: (String identifier) -> void
|
|
43
43
|
def notify_message: (String identifier, untyped message) -> void
|
|
44
|
+
def resubscribe_all: () -> void
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
class Subscription
|
data/sig/component.rbs
CHANGED
|
@@ -12,9 +12,9 @@ module Funicular
|
|
|
12
12
|
|
|
13
13
|
attr_accessor props: Hash[Symbol, untyped]
|
|
14
14
|
attr_accessor vdom: VDOM::VNode | VDOM::Text | nil
|
|
15
|
-
attr_accessor dom_element: JS::
|
|
15
|
+
attr_accessor dom_element: JS::Element
|
|
16
16
|
attr_accessor mounted: bool
|
|
17
|
-
attr_reader refs: Hash[Symbol, JS::
|
|
17
|
+
attr_reader refs: Hash[Symbol, JS::Element]
|
|
18
18
|
@event_listeners: Array[Integer]
|
|
19
19
|
@child_components: Array[Component]
|
|
20
20
|
@suspense_data: Hash[Symbol, untyped]
|
|
@@ -53,10 +53,12 @@ module Funicular
|
|
|
53
53
|
def suspense: (fallback: ^() -> void, ?error: ^(untyped) -> void) { () -> void } -> void
|
|
54
54
|
|
|
55
55
|
def patch: (Hash[Symbol, untyped] new_state) -> void
|
|
56
|
-
def mount: (JS::
|
|
56
|
+
def mount: (JS::Element container) -> void
|
|
57
|
+
def hydrate: (JS::Element dom_element) -> void
|
|
58
|
+
def seed_state: (Hash[untyped, untyped]? state_hash) -> self
|
|
57
59
|
def unmount: () -> void
|
|
58
60
|
def render: () -> (VDOM::VNode | String | Integer | Float | Array[untyped] | nil)
|
|
59
|
-
def bind_events: (JS::
|
|
61
|
+
def bind_events: (JS::Element dom_element, VDOM::VNode | VDOM::Text | nil vnode) -> void
|
|
60
62
|
def build_vdom: () -> (VDOM::VNode | VDOM::Text | nil)
|
|
61
63
|
|
|
62
64
|
# Lifecycle hooks (public, can be overridden in subclasses)
|
|
@@ -91,13 +93,19 @@ module Funicular
|
|
|
91
93
|
private def re_render: () -> void
|
|
92
94
|
private def normalize_vnode: (untyped value) -> (VDOM::Element | VDOM::Text | VDOM::Component | nil)
|
|
93
95
|
private def add_data_component_attribute: (VDOM::VNode vnode) -> void
|
|
94
|
-
private def collect_refs: (JS::
|
|
96
|
+
private def collect_refs: (JS::Element dom_element, VDOM::VNode | VDOM::Text | nil vnode, ?Hash[Symbol, JS::Element] refs_map) -> Hash[Symbol, JS::Element]
|
|
95
97
|
private def cleanup_events: () -> void
|
|
96
98
|
private def cleanup_suspense_timers: () -> void
|
|
97
99
|
private def add_child: (untyped child) -> void
|
|
98
100
|
private def collect_child_components: (VDOM::VNode | VDOM::Text | nil vnode) -> void
|
|
99
101
|
private def collect_child_components_recursive: (VDOM::VNode | VDOM::Text | nil vnode, Array[Component] components) -> void
|
|
100
102
|
|
|
103
|
+
# Hydration helpers
|
|
104
|
+
private def hydration_match?: (untyped vnode, untyped dom_element) -> bool
|
|
105
|
+
private def warn_hydration_mismatch: (untyped vnode, untyped dom_element) -> void
|
|
106
|
+
private def full_render_fallback: (VDOM::VNode | VDOM::Text | nil new_vdom, untyped server_dom) -> JS::Element
|
|
107
|
+
private def hydrate_child_components: (untyped vnode, untyped dom_element) -> void
|
|
108
|
+
|
|
101
109
|
# HTML element methods (DSL)
|
|
102
110
|
def div: (?Hash[Symbol, untyped] props) ?{ -> untyped } -> VDOM::Element
|
|
103
111
|
def span: (?Hash[Symbol, untyped] props) ?{ -> untyped } -> VDOM::Element
|
data/sig/funicular.rbs
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
module Funicular
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
|
+
self.@server: bool
|
|
5
|
+
self.@form_builder_config: Hash[Symbol, String]?
|
|
6
|
+
|
|
4
7
|
def self.version: () -> String
|
|
5
8
|
def self.env: () -> EnvironmentInquirer
|
|
6
9
|
def self.env=: (EnvironmentInquirer | String environment) -> EnvironmentInquirer?
|
|
7
10
|
def self.router: () -> Router?
|
|
11
|
+
|
|
12
|
+
# SSR / hydration support
|
|
13
|
+
def self.server?: () -> bool
|
|
14
|
+
def self.server=: (untyped value) -> bool
|
|
15
|
+
def self.window_state: () -> Hash[String, untyped]
|
|
16
|
+
def self.has_ssr_state?: () -> bool
|
|
17
|
+
def self.first_element_child: (JS::Element container_element) -> JS::Element?
|
|
18
|
+
|
|
8
19
|
def self.load_schemas: (Hash[singleton(Model), String] models) ?{ () -> void } -> void
|
|
9
|
-
def self.start: (?singleton(Component)? component_class, ?container: String | JS::
|
|
20
|
+
def self.start: (?singleton(Component)? component_class, ?container: String | JS::Element, ?props: Hash[Symbol, untyped], ?hydrate: bool) ?{ (Router router) -> void } -> (Component | Router | nil)
|
|
10
21
|
def self.configure_forms: () ?{ (Hash[Symbol, String]) -> void } -> void
|
|
22
|
+
def self.form_builder_config: () -> Hash[Symbol, String]?
|
|
23
|
+
def self.form_builder_config=: (Hash[Symbol, String] config) -> Hash[Symbol, String]
|
|
11
24
|
def self.configure_debug: () ?{ (self) -> void } -> void
|
|
12
25
|
def self.export_debug_config: () -> void
|
|
13
26
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
module VDOM
|
|
3
|
+
class HTMLSerializer
|
|
4
|
+
VOID_ELEMENTS: Array[String]
|
|
5
|
+
SKIP_PROPS: Array[Symbol]
|
|
6
|
+
|
|
7
|
+
def self.serialize: (VNode? vnode) -> String
|
|
8
|
+
def render: (VNode? vnode) -> String
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
def render_element: (Element element) -> String
|
|
12
|
+
def render_children: (Array[child_t] children) -> String
|
|
13
|
+
def render_text: (Text text) -> String
|
|
14
|
+
def render_component: (Component component_vnode) -> String
|
|
15
|
+
def serialize_props: (Hash[Symbol, untyped] props) -> String
|
|
16
|
+
def escape_html: (untyped str) -> String
|
|
17
|
+
def escape_attr: (untyped str) -> String
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/sig/http.rbs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
module Funicular
|
|
2
2
|
module HTTP
|
|
3
|
+
CACHE_DB_NAME: String
|
|
4
|
+
CACHE_STORE: String
|
|
5
|
+
|
|
6
|
+
self.@cache: IndexedDB::KVS?
|
|
7
|
+
|
|
3
8
|
class Response
|
|
4
9
|
attr_reader data: untyped
|
|
5
10
|
attr_reader status: Integer
|
|
@@ -10,13 +15,23 @@ module Funicular
|
|
|
10
15
|
def error_message: () -> String?
|
|
11
16
|
end
|
|
12
17
|
|
|
13
|
-
def self.
|
|
14
|
-
def self.
|
|
15
|
-
def self.
|
|
16
|
-
def self.
|
|
17
|
-
def self.
|
|
18
|
+
def self.cache_init!: () -> IndexedDB::KVS
|
|
19
|
+
def self.cache_purge: (String url) -> nil
|
|
20
|
+
def self.cache_clear: () -> nil
|
|
21
|
+
def self.cache_lookup: (String url) -> untyped
|
|
22
|
+
def self.cache_write: (String url, Hash[String, untyped] entry) -> nil
|
|
23
|
+
|
|
24
|
+
def self.get: (String url, ?cache: Integer?) { (Response) -> void } -> void
|
|
25
|
+
def self.post: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
|
|
26
|
+
def self.patch: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
|
|
27
|
+
def self.delete: (String url, ?cache: Integer?) { (Response) -> void } -> void
|
|
28
|
+
def self.put: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
|
|
18
29
|
def self.csrf_token: () -> String?
|
|
19
30
|
|
|
20
|
-
private def self.
|
|
31
|
+
private def self.warn_unsupported_cache: (String verb) -> void
|
|
32
|
+
private def self.now_seconds: () -> Integer
|
|
33
|
+
private def self.cache_hit?: (untyped entry, Integer ttl) -> bool
|
|
34
|
+
private def self.serve_from_cache: (Hash[String, untyped] entry) { (Response) -> void } -> void
|
|
35
|
+
private def self.request: (String method, String url, Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
|
|
21
36
|
end
|
|
22
37
|
end
|
data/sig/model.rbs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module Funicular
|
|
2
2
|
class Model
|
|
3
|
+
include Funicular::Model::Validations
|
|
4
|
+
extend Funicular::Model::Validations::ClassMethods
|
|
5
|
+
|
|
3
6
|
attr_reader id: untyped
|
|
4
7
|
@changed_attributes: Hash[String, untyped]
|
|
5
8
|
|
|
@@ -10,10 +13,12 @@ module Funicular
|
|
|
10
13
|
|
|
11
14
|
def initialize: (?Hash[untyped, untyped] attributes) -> void
|
|
12
15
|
def self.load_schema: (Hash[String, untyped] schema_data) -> void
|
|
16
|
+
def self.register_schema_validations: (untyped validations) -> void
|
|
17
|
+
def self.normalize_validation_options: (Symbol | String kind, untyped opts) -> untyped
|
|
13
18
|
|
|
14
19
|
def self.all: (?Hash[untyped, untyped] params) ?{ (Array[Model]? instances, String? error) -> void } -> void
|
|
15
20
|
def self.find: (?untyped id, ?endpoint_name: String, ?model_class: singleton(Model)) ?{ (Model? instance, String? error) -> void } -> void
|
|
16
|
-
def self.create: (Hash[untyped, untyped] attrs, ?model_class: singleton(Model)) ?{ (Model? instance,
|
|
21
|
+
def self.create: (Hash[untyped, untyped] attrs, ?model_class: singleton(Model)) ?{ (Model? instance, untyped error) -> void } -> void
|
|
17
22
|
def self.destroy: (?untyped id) ?{ (bool success, untyped result) -> void } -> void
|
|
18
23
|
|
|
19
24
|
def update: (?Hash[untyped, untyped]? attrs) ?{ (bool success, untyped result) -> void } -> void
|
data/sig/patcher.rbs
CHANGED
|
@@ -3,9 +3,12 @@ module Funicular
|
|
|
3
3
|
BOOLEAN_ATTRIBUTES: Array[String]
|
|
4
4
|
|
|
5
5
|
class Patcher
|
|
6
|
-
def initialize: (?JS::
|
|
6
|
+
def initialize: (?JS::Element? doc) -> void
|
|
7
|
+
# `apply` accepts any DOM node so text-node patches (e.g. :replace)
|
|
8
|
+
# can recurse into it; Element-only operations are narrowed inside.
|
|
7
9
|
def apply: (JS::Object element, Array[patch_t] patches) -> JS::Object
|
|
8
10
|
|
|
11
|
+
# Accepts JS::Object since callers narrow via is_a?(JS::Element) at runtime
|
|
9
12
|
private def update_props: (JS::Object element, Hash[Symbol, String?] props_patch) -> void
|
|
10
13
|
private def create_element: (untyped vnode) -> JS::Object
|
|
11
14
|
private def unmount_component: (VDOM::VNode vnode) -> void
|
data/sig/router.rbs
CHANGED
|
@@ -19,7 +19,7 @@ module Funicular
|
|
|
19
19
|
attr_reader current_component: Component?
|
|
20
20
|
attr_reader current_path: String?
|
|
21
21
|
|
|
22
|
-
def initialize: (JS::
|
|
22
|
+
def initialize: (JS::Element? container) -> void
|
|
23
23
|
def get: (String path, to: singleton(Component), ?as: String?, ?constraints: route_constraints_t?) -> void
|
|
24
24
|
def post: (String path, to: singleton(Component), ?as: String?, ?constraints: route_constraints_t?) -> void
|
|
25
25
|
def put: (String path, to: singleton(Component), ?as: String?, ?constraints: route_constraints_t?) -> void
|
|
@@ -27,7 +27,8 @@ module Funicular
|
|
|
27
27
|
def delete: (String path, to: singleton(Component), ?as: String?, ?constraints: route_constraints_t?) -> void
|
|
28
28
|
def add_route: (String path, singleton(Component) component_class, ?as: String?, ?constraints: route_constraints_t?) -> void
|
|
29
29
|
def set_default: (String path) -> void
|
|
30
|
-
def
|
|
30
|
+
def match: (String path) -> [singleton(Component)?, Hash[Symbol, untyped]]
|
|
31
|
+
def start: (?hydrate: bool) -> void
|
|
31
32
|
def stop: () -> void
|
|
32
33
|
def navigate: (String path) -> void
|
|
33
34
|
def current_hash_path: () -> String
|
data/sig/store.rbs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
class Store
|
|
3
|
+
EVENT_REGISTRY: Hash[Symbol, Array[singleton(Funicular::Store)]]
|
|
4
|
+
KVS_POOL: Hash[Array[String], IndexedDB::KVS]
|
|
5
|
+
|
|
6
|
+
class SubscribesTo < Data
|
|
7
|
+
attr_reader channel_name: String
|
|
8
|
+
attr_reader params_proc: ^(Funicular::Store::Scope) -> Hash[Symbol, untyped]
|
|
9
|
+
attr_reader handler_block: Proc
|
|
10
|
+
|
|
11
|
+
def self.new: (String channel_name, ^(Funicular::Store::Scope) -> Hash[Symbol, untyped] params_proc, Proc handler_block) -> instance
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Subscription
|
|
15
|
+
attr_reader cable_sub: Funicular::Cable::Subscription
|
|
16
|
+
|
|
17
|
+
def initialize: (Funicular::Cable::Subscription cable_sub) -> void
|
|
18
|
+
def unsubscribe: () -> nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Scope
|
|
22
|
+
attr_reader store_class: singleton(Funicular::Store)
|
|
23
|
+
attr_reader scope_kwargs: Hash[Symbol, untyped]
|
|
24
|
+
|
|
25
|
+
@on_change: Hash[Integer, Proc]
|
|
26
|
+
@next_cb_id: Integer
|
|
27
|
+
@subscription: Funicular::Store::Subscription?
|
|
28
|
+
|
|
29
|
+
def initialize: (singleton(Funicular::Store) store_class, Hash[Symbol, untyped] scope_kwargs) -> void
|
|
30
|
+
def method_missing: (Symbol name, *untyped args) -> untyped
|
|
31
|
+
def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
|
|
32
|
+
def on_change: () { (untyped) -> void } -> Integer
|
|
33
|
+
def off_change: (Integer id) -> nil
|
|
34
|
+
def subscribed?: () -> bool
|
|
35
|
+
def subscription: () -> Funicular::Store::Subscription?
|
|
36
|
+
def subscribe!: () -> Funicular::Store::Subscription
|
|
37
|
+
def unsubscribe!: () -> nil
|
|
38
|
+
|
|
39
|
+
private def storage_key: () -> String
|
|
40
|
+
private def kvs: () -> IndexedDB::KVS
|
|
41
|
+
private def now_seconds: () -> Integer
|
|
42
|
+
private def expired_record?: (untyped rec) -> bool
|
|
43
|
+
private def fire_change: (untyped snapshot) -> void
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
self.@__database: String?
|
|
47
|
+
self.@__kvs_store_name: String?
|
|
48
|
+
self.@__scope_keys: Array[Symbol]?
|
|
49
|
+
self.@__expires_in: Integer?
|
|
50
|
+
self.@__source: untyped
|
|
51
|
+
self.@__belongs_to: Symbol?
|
|
52
|
+
self.@__cable_url: String?
|
|
53
|
+
self.@__cable_binding: SubscribesTo?
|
|
54
|
+
self.@__cleared_handlers: Hash[Symbol, Proc?]?
|
|
55
|
+
self.@__scope_pool: Hash[Hash[Symbol, untyped], Funicular::Store::Scope]?
|
|
56
|
+
self.@__consumer: Funicular::Cable::Consumer?
|
|
57
|
+
|
|
58
|
+
def self.database: (String name) -> String
|
|
59
|
+
def self.kvs_store: (String name) -> String
|
|
60
|
+
def self.scope: (*Symbol keys) -> Array[Symbol]
|
|
61
|
+
def self.expires_in: (Integer seconds) -> Integer
|
|
62
|
+
def self.source: (untyped model_class) -> untyped
|
|
63
|
+
def self.belongs_to: (Symbol name) -> Symbol
|
|
64
|
+
def self.cable_url: (String url) -> String
|
|
65
|
+
def self.subscribes_to: (String channel_name, params: ^(Funicular::Store::Scope) -> Hash[Symbol, untyped]) { (untyped, Funicular::Store::Scope) -> void } -> SubscribesTo
|
|
66
|
+
def self.cleared_on: (*Symbol event_names) ?{ (untyped) -> void } -> void
|
|
67
|
+
|
|
68
|
+
def self.where: (**untyped scope_kwargs) -> Funicular::Store::Scope
|
|
69
|
+
def self.scope_class: () -> singleton(Funicular::Store::Scope)
|
|
70
|
+
|
|
71
|
+
def self.__database: () -> String?
|
|
72
|
+
def self.__kvs_store_name: () -> String?
|
|
73
|
+
def self.__scope_keys: () -> Array[Symbol]?
|
|
74
|
+
def self.__expires_in: () -> Integer?
|
|
75
|
+
def self.__source: () -> untyped
|
|
76
|
+
def self.__belongs_to: () -> Symbol?
|
|
77
|
+
def self.__cable_url: () -> String?
|
|
78
|
+
def self.__cable_binding: () -> SubscribesTo?
|
|
79
|
+
def self.__cleared_handlers: () -> Hash[Symbol, Proc?]?
|
|
80
|
+
def self.__kvs: () -> IndexedDB::KVS
|
|
81
|
+
def self.__consumer: () -> Funicular::Cable::Consumer
|
|
82
|
+
def self.__handle_dispatch: (Symbol event, untyped payload) -> void
|
|
83
|
+
def self.__clear_all!: () -> nil
|
|
84
|
+
|
|
85
|
+
private def self.validate_scope_kwargs!: (Hash[Symbol, untyped] scope_kwargs) -> void
|
|
86
|
+
|
|
87
|
+
def self.dispatch: (Symbol | String event, ?untyped payload) -> nil
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
class Store
|
|
3
|
+
class Collection < Funicular::Store
|
|
4
|
+
DEFAULT_KEY_PROC: ^(untyped) -> untyped
|
|
5
|
+
|
|
6
|
+
self.@__limit: Integer?
|
|
7
|
+
self.@__order: Symbol?
|
|
8
|
+
self.@__key_proc: ^(untyped) -> untyped | nil
|
|
9
|
+
|
|
10
|
+
def self.limit: (Integer n) -> Integer
|
|
11
|
+
def self.order: (Symbol direction) -> Symbol
|
|
12
|
+
def self.key: (^(untyped) -> untyped proc) -> (^(untyped) -> untyped)
|
|
13
|
+
def self.scope_class: () -> singleton(Funicular::Store::Collection::Scope)
|
|
14
|
+
|
|
15
|
+
def self.__limit: () -> Integer?
|
|
16
|
+
def self.__order: () -> Symbol?
|
|
17
|
+
def self.__key_proc: () -> (^(untyped) -> untyped | nil)
|
|
18
|
+
|
|
19
|
+
class Scope < Funicular::Store::Scope
|
|
20
|
+
attr_reader store_class: singleton(Funicular::Store::Collection)
|
|
21
|
+
@store_class: singleton(Funicular::Store::Collection)
|
|
22
|
+
|
|
23
|
+
def all: () -> Array[untyped]
|
|
24
|
+
def replace: (untyped arr) -> Array[untyped]
|
|
25
|
+
def append: (untyped item) -> Array[untyped]
|
|
26
|
+
def remove: (untyped id) -> Array[untyped]
|
|
27
|
+
def last: () -> untyped
|
|
28
|
+
def last_id: () -> untyped
|
|
29
|
+
def size: () -> Integer
|
|
30
|
+
def clear: () -> nil
|
|
31
|
+
def expired?: () -> bool
|
|
32
|
+
def same_tail?: (untyped other) -> bool
|
|
33
|
+
|
|
34
|
+
private def key_proc: () -> ^(untyped) -> untyped
|
|
35
|
+
private def cap: (Array[untyped] arr) -> Array[untyped]
|
|
36
|
+
private def append_to: (Array[untyped] arr, untyped item) -> Array[untyped]
|
|
37
|
+
private def read: () -> untyped
|
|
38
|
+
private def write: (Array[untyped] items) -> Hash[String, untyped]
|
|
39
|
+
private def erase: () -> nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
class Store
|
|
3
|
+
class Singleton < Funicular::Store
|
|
4
|
+
def self.scope_class: () -> singleton(Funicular::Store::Singleton::Scope)
|
|
5
|
+
|
|
6
|
+
class Scope < Funicular::Store::Scope
|
|
7
|
+
def value: () -> untyped
|
|
8
|
+
def value=: (untyped v) -> untyped
|
|
9
|
+
def delete: () -> nil
|
|
10
|
+
def present?: () -> bool
|
|
11
|
+
def expired?: () -> bool
|
|
12
|
+
|
|
13
|
+
private def read: () -> untyped
|
|
14
|
+
private def write: (untyped v) -> Hash[String, untyped]
|
|
15
|
+
private def erase: () -> nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/sig/validations.rbs
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module Funicular
|
|
2
|
+
class Model
|
|
3
|
+
# Validation error collection (a small slice of ActiveModel::Errors).
|
|
4
|
+
class Errors
|
|
5
|
+
@messages: Hash[Symbol, Array[String]]
|
|
6
|
+
|
|
7
|
+
def initialize: () -> void
|
|
8
|
+
def add: (Symbol | String attribute, String message) -> String
|
|
9
|
+
def []: (Symbol | String attribute) -> Array[String]
|
|
10
|
+
def added?: (Symbol | String attribute) -> bool
|
|
11
|
+
def messages: () -> Hash[Symbol, Array[String]]
|
|
12
|
+
def full_messages: () -> Array[String]
|
|
13
|
+
def clear: () -> void
|
|
14
|
+
def empty?: () -> bool
|
|
15
|
+
def any?: () -> bool
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def humanize: (Symbol | String attribute) -> String
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# ActiveModel-style validation, mixed into Model.
|
|
22
|
+
module Validations
|
|
23
|
+
def self.included: (untyped base) -> void
|
|
24
|
+
|
|
25
|
+
# Base class for attribute validators.
|
|
26
|
+
class EachValidator
|
|
27
|
+
@attributes: Array[Symbol]
|
|
28
|
+
@options: Hash[Symbol, untyped]
|
|
29
|
+
|
|
30
|
+
attr_reader attributes: Array[Symbol]
|
|
31
|
+
attr_reader options: Hash[Symbol, untyped]
|
|
32
|
+
|
|
33
|
+
def initialize: (attributes: Array[Symbol], **untyped options) -> void
|
|
34
|
+
def kind: () -> Symbol
|
|
35
|
+
def validate: (untyped record) -> void
|
|
36
|
+
def validate_each: (untyped record, Symbol attribute, untyped value) -> void
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def blank?: (untyped value) -> bool
|
|
40
|
+
def present?: (untyped value) -> bool
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module ClassMethods
|
|
44
|
+
@validators: Array[EachValidator]
|
|
45
|
+
|
|
46
|
+
SHARED_OPTIONS: Array[Symbol]
|
|
47
|
+
|
|
48
|
+
def validators: () -> Array[EachValidator]
|
|
49
|
+
def validators_on: (Symbol | String attribute) -> Array[EachValidator]
|
|
50
|
+
def validates: (*Symbol | String attributes, **untyped options) -> void
|
|
51
|
+
def add_schema_validator: (Symbol | String attribute, Symbol | String kind, untyped opts) -> void
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def validator_class_for: (Symbol | String key) -> singleton(EachValidator)?
|
|
55
|
+
def symbolize_keys: (untyped hash) -> Hash[Symbol, untyped]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Instance API (mixed into Model).
|
|
59
|
+
@errors: Errors
|
|
60
|
+
def errors: () -> Errors
|
|
61
|
+
def read_attribute_for_validation: (Symbol attribute) -> untyped
|
|
62
|
+
def valid?: () -> bool
|
|
63
|
+
def invalid?: () -> bool
|
|
64
|
+
|
|
65
|
+
# --- concrete validators ---
|
|
66
|
+
|
|
67
|
+
class PresenceValidator < EachValidator
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class AbsenceValidator < EachValidator
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class LengthValidator < EachValidator
|
|
74
|
+
private
|
|
75
|
+
def range_include?: (untyped range, Integer length) -> bool
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class FormatValidator < EachValidator
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class NumericalityValidator < EachValidator
|
|
82
|
+
CHECKS: Hash[Symbol, String]
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
def compare: (Numeric number, Symbol option, untyped target) -> bool
|
|
86
|
+
def message_for: (Symbol option, untyped target) -> String
|
|
87
|
+
def to_number: (untyped value) -> (Numeric | nil)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class InclusionValidator < EachValidator
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class ExclusionValidator < EachValidator
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class AcceptanceValidator < EachValidator
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class ConfirmationValidator < EachValidator
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/sig/vdom.rbs
CHANGED
|
@@ -32,18 +32,18 @@ module Funicular
|
|
|
32
32
|
class Renderer
|
|
33
33
|
@error_boundary_stack: Array[Funicular::ErrorBoundary]
|
|
34
34
|
|
|
35
|
-
def initialize: (?JS::
|
|
36
|
-
def render: (VDOM::VNode | VDOM::Text | nil vnode, ?JS::
|
|
35
|
+
def initialize: (?JS::Element? doc) -> void
|
|
36
|
+
def render: (VDOM::VNode | VDOM::Text | nil vnode, ?JS::Element? parent) -> JS::Element
|
|
37
37
|
|
|
38
38
|
private def current_error_boundary: () -> ErrorBoundary?
|
|
39
|
-
private def render_element: (Element element, JS::
|
|
40
|
-
private def render_text: (VDOM::Text text, JS::
|
|
41
|
-
private def render_component: (VDOM::Component component_vnode, JS::
|
|
39
|
+
private def render_element: (Element element, JS::Element? parent) -> JS::Element
|
|
40
|
+
private def render_text: (VDOM::Text text, JS::Element? parent) -> JS::Element
|
|
41
|
+
private def render_component: (VDOM::Component component_vnode, JS::Element? parent) -> JS::Element
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def self.create_element: (String tag, ?Hash[Symbol, untyped] props, *child_t children) -> Element
|
|
45
45
|
def self.create_text: (String content) -> Text
|
|
46
|
-
def self.render: (VNode vnode, JS::
|
|
46
|
+
def self.render: (VNode vnode, JS::Element container) -> void
|
|
47
47
|
def self.diff: (VNode? old_vnode, VNode? new_vnode) -> Array[patch_t]
|
|
48
48
|
def self.patch: (JS::Object element, Array[patch_t] patches) -> JS::Object
|
|
49
49
|
|