pakyow-ui 0.11.3 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/{pakyow-ui/CHANGELOG.md → CHANGELOG.md} +0 -0
- data/LICENSE +4 -0
- data/{pakyow-ui/README.md → README.md} +1 -2
- data/lib/pakyow/ui/behavior/recording.rb +51 -0
- data/lib/pakyow/ui/behavior/rendering/install_transforms.rb +47 -0
- data/lib/pakyow/ui/behavior/rendering.rb +105 -0
- data/lib/pakyow/ui/behavior/timeouts.rb +31 -0
- data/lib/pakyow/ui/framework.rb +75 -0
- data/lib/pakyow/ui/handler.rb +42 -0
- data/lib/pakyow/ui/helpers.rb +19 -0
- data/lib/pakyow/ui/recordable/attribute.rb +39 -0
- data/lib/pakyow/ui/recordable/attributes.rb +50 -0
- data/lib/pakyow/ui/recordable/helpers/client_remapping.rb +30 -0
- data/lib/pakyow/ui/recordable.rb +303 -0
- data/lib/pakyow/ui.rb +9 -0
- metadata +46 -60
- data/pakyow-ui/LICENSE +0 -20
- data/pakyow-ui/lib/pakyow/ui/base.rb +0 -26
- data/pakyow-ui/lib/pakyow/ui/channel_builder.rb +0 -55
- data/pakyow-ui/lib/pakyow/ui/config.rb +0 -11
- data/pakyow-ui/lib/pakyow/ui/ext/app.rb +0 -52
- data/pakyow-ui/lib/pakyow/ui/ext/view_context.rb +0 -30
- data/pakyow-ui/lib/pakyow/ui/fetch_view_handler.rb +0 -68
- data/pakyow-ui/lib/pakyow/ui/helpers.rb +0 -15
- data/pakyow-ui/lib/pakyow/ui/mock_mutation_eval.rb +0 -25
- data/pakyow-ui/lib/pakyow/ui/mutable.rb +0 -99
- data/pakyow-ui/lib/pakyow/ui/mutable_data.rb +0 -21
- data/pakyow-ui/lib/pakyow/ui/mutate_context.rb +0 -64
- data/pakyow-ui/lib/pakyow/ui/mutation_set.rb +0 -38
- data/pakyow-ui/lib/pakyow/ui/mutation_store.rb +0 -41
- data/pakyow-ui/lib/pakyow/ui/mutator.rb +0 -63
- data/pakyow-ui/lib/pakyow/ui/no_op_view.rb +0 -87
- data/pakyow-ui/lib/pakyow/ui/registries/redis_mutation_registry.rb +0 -70
- data/pakyow-ui/lib/pakyow/ui/registries/simple_mutation_registry.rb +0 -37
- data/pakyow-ui/lib/pakyow/ui/ui.rb +0 -81
- data/pakyow-ui/lib/pakyow/ui/ui_attrs.rb +0 -40
- data/pakyow-ui/lib/pakyow/ui/ui_component.rb +0 -68
- data/pakyow-ui/lib/pakyow/ui/ui_context.rb +0 -16
- data/pakyow-ui/lib/pakyow/ui/ui_instructable.rb +0 -117
- data/pakyow-ui/lib/pakyow/ui/ui_request.rb +0 -14
- data/pakyow-ui/lib/pakyow/ui/ui_view.rb +0 -200
- data/pakyow-ui/lib/pakyow/ui.rb +0 -1
- data/pakyow-ui/lib/pakyow-ui.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e73cc5b15a5dc52180b3e004a21835916a5c94b91e50716e4d9decc0d8b47129
|
4
|
+
data.tar.gz: '095d1cf65f4d828ed90260b501ea7e6a7759dbfe02d1d3ecfd30e9ad4f234b0f'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78cee63f23c55096e8fd1fc2fcc13526766172ba9346cf54ffb00c25679945f641921a39f6cec6c5c5aeedabacc89ef4434ea302787babfa52d1b701947b610c
|
7
|
+
data.tar.gz: 937625badf07777bc7ebd71413d86aa27cc1e9af28f9d25e5c595a376d0528d8984168e6db12ebe8730515d161b7951f314a2b1cb6bc81d92de7be7dfa3fb770
|
File without changes
|
data/LICENSE
ADDED
@@ -307,8 +307,7 @@ Source code can be downloaded as part of the Pakyow project on Github:
|
|
307
307
|
|
308
308
|
# License
|
309
309
|
|
310
|
-
Pakyow UI is
|
311
|
-
License](http://opensource.org/licenses/MIT).
|
310
|
+
Pakyow UI is free and open-source under the [LGPLv3 license](https://choosealicense.com/licenses/lgpl-3.0/).
|
312
311
|
|
313
312
|
# Support
|
314
313
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
require "pakyow/ui/recordable"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
module UI
|
9
|
+
module Behavior
|
10
|
+
module Recording
|
11
|
+
extend Support::Extension
|
12
|
+
|
13
|
+
def find_ui_presenter_for(presenter_class)
|
14
|
+
if is_a?(Plugin)
|
15
|
+
# Look for the presenter in the plugin first, falling back to the app.
|
16
|
+
#
|
17
|
+
ui_presenter_class = parent.ui_presenters.find { |klass|
|
18
|
+
klass.ancestors.include?(presenter_class)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
ui_presenter_class ||= @ui_presenters.find { |klass|
|
23
|
+
klass.ancestors.include?(presenter_class)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
apply_extension do
|
28
|
+
# Create subclasses of each presenter, then make the subclasses recordable.
|
29
|
+
# These subclasses will be used when performing a ui presentation instead
|
30
|
+
# of the original presenter, but they'll behave identically!
|
31
|
+
#
|
32
|
+
after "initialize" do
|
33
|
+
@ui_presenters = [isolated(:Presenter)].concat(
|
34
|
+
state(:presenter)
|
35
|
+
).concat(
|
36
|
+
state(:component).map(&:__presenter_class)
|
37
|
+
).map { |presenter_class|
|
38
|
+
Class.new(presenter_class) do
|
39
|
+
include Recordable
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
class_eval do
|
45
|
+
attr_reader :ui_presenters
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
require "pakyow/presenter/view"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
module UI
|
9
|
+
module Behavior
|
10
|
+
module Rendering
|
11
|
+
module InstallTransforms
|
12
|
+
extend Support::Extension
|
13
|
+
|
14
|
+
apply_extension do
|
15
|
+
attach do |presenter|
|
16
|
+
presenter.render node: -> {
|
17
|
+
nodes = []
|
18
|
+
|
19
|
+
if html_node = object.find_first_significant_node(:html)
|
20
|
+
nodes << Pakyow::Presenter::View.from_object(html_node)
|
21
|
+
end
|
22
|
+
|
23
|
+
if !object.is_a?(StringDoc) && object.significant?(:component)
|
24
|
+
nodes << Pakyow::Presenter::View.from_object(object)
|
25
|
+
end
|
26
|
+
|
27
|
+
object.each_significant_node_without_descending_into_type(:component, descend: true) do |node|
|
28
|
+
if node.label(:components).any? { |c| c[:renderable ] }
|
29
|
+
nodes << Pakyow::Presenter::View.from_object(node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
nodes
|
34
|
+
} do
|
35
|
+
if transformation_id = presentables[:__transformation_id]
|
36
|
+
# Set the transformation_id on the target node so that transformations can be applied to the right place.
|
37
|
+
#
|
38
|
+
attributes[:"data-t"] = transformation_id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
require "pakyow/support/deep_dup"
|
6
|
+
require "pakyow/support/extension"
|
7
|
+
require "pakyow/support/inflector"
|
8
|
+
|
9
|
+
require "pakyow/realtime/helpers/subscriptions"
|
10
|
+
|
11
|
+
require "pakyow/ui/handler"
|
12
|
+
|
13
|
+
module Pakyow
|
14
|
+
module UI
|
15
|
+
module Behavior
|
16
|
+
module Rendering
|
17
|
+
extend Support::Extension
|
18
|
+
|
19
|
+
apply_extension do
|
20
|
+
isolated :Renderer do
|
21
|
+
include Realtime::Helpers::Subscriptions
|
22
|
+
|
23
|
+
def socket_client_id
|
24
|
+
presentables[:__socket_client_id]
|
25
|
+
end
|
26
|
+
|
27
|
+
on "render" do
|
28
|
+
unless presentables.include?(:__ui_transform) || subscribables.empty?
|
29
|
+
# To keep up with the node(s) that matter for the transformation, a `data-t` attribute
|
30
|
+
# is added to the node that contains the transformation_id. When the transformation is
|
31
|
+
# triggered in the future, the client knows what node to apply tranformations to.
|
32
|
+
#
|
33
|
+
# Note that when we're presenting an entire view, `data-t` is set on the `html` node.
|
34
|
+
#
|
35
|
+
transformation_target = case @presenter.view.object
|
36
|
+
when StringDoc
|
37
|
+
@presenter.view.object.find_first_significant_node(:html)
|
38
|
+
when StringDoc::Node
|
39
|
+
@presenter.view.object
|
40
|
+
end
|
41
|
+
|
42
|
+
if transformation_target
|
43
|
+
ui_renderer_instance = @app.isolated(:UIRenderer).allocate
|
44
|
+
|
45
|
+
instance_variables.each do |ivar|
|
46
|
+
ui_renderer_instance.instance_variable_set(ivar, instance_variable_get(ivar))
|
47
|
+
end
|
48
|
+
|
49
|
+
metadata = {
|
50
|
+
renderer: ui_renderer_instance
|
51
|
+
}
|
52
|
+
|
53
|
+
payload = {
|
54
|
+
metadata: Marshal.dump(metadata)
|
55
|
+
}
|
56
|
+
|
57
|
+
# Generate a unique id based on the value of the metadata. This guarantees that the
|
58
|
+
# transformation id will be consistent across subscriptions.
|
59
|
+
#
|
60
|
+
transformation_id = Digest::SHA1.hexdigest(payload[:metadata])
|
61
|
+
presentables[:__transformation_id] = transformation_id
|
62
|
+
payload[:transformation_id] = transformation_id
|
63
|
+
|
64
|
+
# Find every subscribable presentable, creating a data subscription for each.
|
65
|
+
#
|
66
|
+
subscribables.each do |subscribable|
|
67
|
+
subscribable.subscribe(socket_client_id, handler: Handler, payload: payload) do |ids|
|
68
|
+
# Subscribe the subscriptions to the "transformation" channel.
|
69
|
+
#
|
70
|
+
subscribe(:transformation, *ids.uniq)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
using Support::DeepDup
|
78
|
+
|
79
|
+
private def subscribables
|
80
|
+
@subscribables ||= presentables.reject { |presentable_name, _|
|
81
|
+
presentable_name.to_s.start_with?("__")
|
82
|
+
}.map { |_, value|
|
83
|
+
proxy = if value.is_a?(Data::Proxy)
|
84
|
+
value
|
85
|
+
elsif value.instance_variable_defined?(:@__proxy)
|
86
|
+
value.instance_variable_get(:@__proxy)
|
87
|
+
else
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
if proxy && proxy.subscribable?
|
92
|
+
proxy.deep_dup
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
}.compact.uniq { |subscribable|
|
97
|
+
subscribable.source.__getobj__
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module UI
|
7
|
+
module Behavior
|
8
|
+
module Timeouts
|
9
|
+
extend Support::Extension
|
10
|
+
|
11
|
+
apply_extension do
|
12
|
+
on :join do
|
13
|
+
@connection.app.data.persist(@id)
|
14
|
+
end
|
15
|
+
|
16
|
+
on :leave do
|
17
|
+
@connection.app.data.expire(@id, config.realtime.timeouts.disconnect)
|
18
|
+
end
|
19
|
+
|
20
|
+
isolated :Renderer do
|
21
|
+
after "render" do
|
22
|
+
# Expire subscriptions if the connection is never established.
|
23
|
+
#
|
24
|
+
@app.data.expire(presentables[:__socket_client_id], @app.config.realtime.timeouts.initial)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/framework"
|
4
|
+
|
5
|
+
require "pakyow/ui/helpers"
|
6
|
+
|
7
|
+
require "pakyow/ui/behavior/recording"
|
8
|
+
require "pakyow/ui/behavior/rendering"
|
9
|
+
require "pakyow/ui/behavior/timeouts"
|
10
|
+
|
11
|
+
require "pakyow/ui/behavior/rendering/install_transforms"
|
12
|
+
|
13
|
+
module Pakyow
|
14
|
+
module UI
|
15
|
+
class Framework < Pakyow::Framework(:ui)
|
16
|
+
# @api private
|
17
|
+
module PresenterForContext
|
18
|
+
def presenter_for_context(presenter_class, context)
|
19
|
+
if context.presentables.include?(:__ui_transform)
|
20
|
+
instance = find_ui_presenter_for(presenter_class).new(
|
21
|
+
context.view, app: context.app, presentables: context.presentables
|
22
|
+
)
|
23
|
+
|
24
|
+
instance.instance_variable_set(:@calls, context.calls)
|
25
|
+
instance
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def boot
|
33
|
+
object.class_eval do
|
34
|
+
register_helper :passive, Helpers
|
35
|
+
|
36
|
+
include Behavior::Recording
|
37
|
+
include Behavior::Rendering
|
38
|
+
include Behavior::Timeouts
|
39
|
+
|
40
|
+
isolated :Renderer do
|
41
|
+
include Behavior::Rendering::InstallTransforms
|
42
|
+
end
|
43
|
+
|
44
|
+
prepend PresenterForContext
|
45
|
+
|
46
|
+
ui_renderer = Class.new(isolated(:Renderer)) do
|
47
|
+
def marshal_load(state)
|
48
|
+
deserialize(state)
|
49
|
+
@presenter_class = @app.find_ui_presenter_for(@presenter_class)
|
50
|
+
initialize_presenter
|
51
|
+
end
|
52
|
+
|
53
|
+
def perform(*)
|
54
|
+
@presenter.to_html
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Delete the render_components build step since ui will not be invoking the component.
|
59
|
+
#
|
60
|
+
ui_renderer.__build_fns.delete_if { |fn|
|
61
|
+
fn.source_location[0].end_with?("render_components.rb")
|
62
|
+
}
|
63
|
+
|
64
|
+
unless const_defined?(:UIRenderer, false)
|
65
|
+
const_set(:UIRenderer, ui_renderer)
|
66
|
+
end
|
67
|
+
|
68
|
+
after :initialize do
|
69
|
+
config.data.subscriptions.version = config.version
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/connection"
|
4
|
+
|
5
|
+
require "pakyow/data/proxy"
|
6
|
+
require "pakyow/data/sources/ephemeral"
|
7
|
+
|
8
|
+
require "pakyow/realtime/channel"
|
9
|
+
|
10
|
+
require "pakyow/ui/recordable"
|
11
|
+
|
12
|
+
module Pakyow
|
13
|
+
module UI
|
14
|
+
class Handler
|
15
|
+
def initialize(app)
|
16
|
+
@app = app
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(args, subscription: nil, result: nil)
|
20
|
+
renderer = Marshal.restore(args[:metadata])[:renderer]
|
21
|
+
renderer.presentables[:__ui_transform] = true
|
22
|
+
|
23
|
+
# If an ephemeral caused the update, replace the value for any matching presentables.
|
24
|
+
#
|
25
|
+
if result.is_a?(Data::Sources::Ephemeral)
|
26
|
+
renderer.presentables.each do |key, value|
|
27
|
+
if value.is_a?(Data::Proxy) && value.source.is_a?(Data::Sources::Ephemeral) && value.source.type == result.type && value.source.qualifications == result.qualifications
|
28
|
+
value.instance_variable_set(:@source, result)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
renderer.perform
|
34
|
+
|
35
|
+
@app.websocket_server.subscription_broadcast(
|
36
|
+
Realtime::Channel.new(:transformation, subscription[:id]),
|
37
|
+
{ id: args[:transformation_id], calls: renderer.presenter }
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
module UI
|
5
|
+
module Helpers
|
6
|
+
def ui?
|
7
|
+
@connection.request_header?("pw-ui")
|
8
|
+
end
|
9
|
+
|
10
|
+
def ui
|
11
|
+
@connection.request_header("pw-ui")
|
12
|
+
end
|
13
|
+
|
14
|
+
def ui_transform?
|
15
|
+
@connection.set?(:__ui_transform)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
require "pakyow/ui/recordable/helpers/client_remapping"
|
7
|
+
|
8
|
+
module Pakyow
|
9
|
+
module UI
|
10
|
+
module Recordable
|
11
|
+
class Attribute < SimpleDelegator
|
12
|
+
include Helpers::ClientRemapping
|
13
|
+
|
14
|
+
def initialize(attribute)
|
15
|
+
__setobj__(attribute)
|
16
|
+
@calls = []
|
17
|
+
end
|
18
|
+
|
19
|
+
%i([] []= << delete clear add).each do |method_name|
|
20
|
+
define_method method_name do |*args|
|
21
|
+
super(*args).tap do
|
22
|
+
@calls << [remap_for_client(method_name), args, [], []]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_json(*)
|
28
|
+
@calls.to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
# Fixes an issue using pp inside a delegator.
|
32
|
+
#
|
33
|
+
def pp(*args)
|
34
|
+
Kernel.pp(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require "pakyow/ui/recordable/attribute"
|
6
|
+
require "pakyow/ui/recordable/helpers/client_remapping"
|
7
|
+
|
8
|
+
module Pakyow
|
9
|
+
module UI
|
10
|
+
module Recordable
|
11
|
+
class Attributes < Pakyow::Presenter::Attributes
|
12
|
+
include Helpers::ClientRemapping
|
13
|
+
|
14
|
+
%i([] []=).each do |method_name|
|
15
|
+
define_method method_name do |*args|
|
16
|
+
result = super(*args)
|
17
|
+
result = case method_name
|
18
|
+
when :[]
|
19
|
+
Attribute.new(result)
|
20
|
+
else
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
result.tap do
|
25
|
+
subsequent = if result.is_a?(Attribute)
|
26
|
+
result
|
27
|
+
else
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
@calls << [remap_for_client(method_name), args, [], subsequent]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_json(*)
|
37
|
+
@calls.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def from_attributes(attributes)
|
42
|
+
new(attributes.instance_variable_get(:@attributes)).tap { |instance|
|
43
|
+
instance.instance_variable_set(:@calls, [])
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
module UI
|
5
|
+
module Recordable
|
6
|
+
module Helpers
|
7
|
+
module ClientRemapping
|
8
|
+
def remap_for_client(method_name)
|
9
|
+
case method_name
|
10
|
+
when :[]
|
11
|
+
:get
|
12
|
+
when :[]=
|
13
|
+
:set
|
14
|
+
when :<<
|
15
|
+
:add
|
16
|
+
when :title=
|
17
|
+
:setTitle
|
18
|
+
when :html=
|
19
|
+
:setHtml
|
20
|
+
when :endpoint_action
|
21
|
+
:endpointAction
|
22
|
+
else
|
23
|
+
method_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|