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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/{pakyow-ui/CHANGELOG.md → CHANGELOG.md} +0 -0
  3. data/LICENSE +4 -0
  4. data/{pakyow-ui/README.md → README.md} +1 -2
  5. data/lib/pakyow/ui/behavior/recording.rb +51 -0
  6. data/lib/pakyow/ui/behavior/rendering/install_transforms.rb +47 -0
  7. data/lib/pakyow/ui/behavior/rendering.rb +105 -0
  8. data/lib/pakyow/ui/behavior/timeouts.rb +31 -0
  9. data/lib/pakyow/ui/framework.rb +75 -0
  10. data/lib/pakyow/ui/handler.rb +42 -0
  11. data/lib/pakyow/ui/helpers.rb +19 -0
  12. data/lib/pakyow/ui/recordable/attribute.rb +39 -0
  13. data/lib/pakyow/ui/recordable/attributes.rb +50 -0
  14. data/lib/pakyow/ui/recordable/helpers/client_remapping.rb +30 -0
  15. data/lib/pakyow/ui/recordable.rb +303 -0
  16. data/lib/pakyow/ui.rb +9 -0
  17. metadata +46 -60
  18. data/pakyow-ui/LICENSE +0 -20
  19. data/pakyow-ui/lib/pakyow/ui/base.rb +0 -26
  20. data/pakyow-ui/lib/pakyow/ui/channel_builder.rb +0 -55
  21. data/pakyow-ui/lib/pakyow/ui/config.rb +0 -11
  22. data/pakyow-ui/lib/pakyow/ui/ext/app.rb +0 -52
  23. data/pakyow-ui/lib/pakyow/ui/ext/view_context.rb +0 -30
  24. data/pakyow-ui/lib/pakyow/ui/fetch_view_handler.rb +0 -68
  25. data/pakyow-ui/lib/pakyow/ui/helpers.rb +0 -15
  26. data/pakyow-ui/lib/pakyow/ui/mock_mutation_eval.rb +0 -25
  27. data/pakyow-ui/lib/pakyow/ui/mutable.rb +0 -99
  28. data/pakyow-ui/lib/pakyow/ui/mutable_data.rb +0 -21
  29. data/pakyow-ui/lib/pakyow/ui/mutate_context.rb +0 -64
  30. data/pakyow-ui/lib/pakyow/ui/mutation_set.rb +0 -38
  31. data/pakyow-ui/lib/pakyow/ui/mutation_store.rb +0 -41
  32. data/pakyow-ui/lib/pakyow/ui/mutator.rb +0 -63
  33. data/pakyow-ui/lib/pakyow/ui/no_op_view.rb +0 -87
  34. data/pakyow-ui/lib/pakyow/ui/registries/redis_mutation_registry.rb +0 -70
  35. data/pakyow-ui/lib/pakyow/ui/registries/simple_mutation_registry.rb +0 -37
  36. data/pakyow-ui/lib/pakyow/ui/ui.rb +0 -81
  37. data/pakyow-ui/lib/pakyow/ui/ui_attrs.rb +0 -40
  38. data/pakyow-ui/lib/pakyow/ui/ui_component.rb +0 -68
  39. data/pakyow-ui/lib/pakyow/ui/ui_context.rb +0 -16
  40. data/pakyow-ui/lib/pakyow/ui/ui_instructable.rb +0 -117
  41. data/pakyow-ui/lib/pakyow/ui/ui_request.rb +0 -14
  42. data/pakyow-ui/lib/pakyow/ui/ui_view.rb +0 -200
  43. data/pakyow-ui/lib/pakyow/ui.rb +0 -1
  44. data/pakyow-ui/lib/pakyow-ui.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b844430b966145355c6fbe487e29146929844acc
4
- data.tar.gz: 11749702224aaa8734d3f3a833ecb964ecbca8fb
2
+ SHA256:
3
+ metadata.gz: e73cc5b15a5dc52180b3e004a21835916a5c94b91e50716e4d9decc0d8b47129
4
+ data.tar.gz: '095d1cf65f4d828ed90260b501ea7e6a7759dbfe02d1d3ecfd30e9ad4f234b0f'
5
5
  SHA512:
6
- metadata.gz: 24877fe45b5e018abd93bdee153398beca25fd06ac74e135e1220716618ceeee54c024ac8387ac52441c7b7ab160f0df18dc76ec4824af9b4e3f88fec1ce45b7
7
- data.tar.gz: 056c6222bec4874a0c4a07768fb8c8aa8d71f7a05389762c34f59f6fe99d22f12ce6e35acaab62e30fa9f81b448b557b9d924afe0f64720bf0fa23cc26309bad
6
+ metadata.gz: 78cee63f23c55096e8fd1fc2fcc13526766172ba9346cf54ffb00c25679945f641921a39f6cec6c5c5aeedabacc89ef4434ea302787babfa52d1b701947b610c
7
+ data.tar.gz: 937625badf07777bc7ebd71413d86aa27cc1e9af28f9d25e5c595a376d0528d8984168e6db12ebe8730515d161b7951f314a2b1cb6bc81d92de7be7dfa3fb770
File without changes
data/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) Metabahn, LLC
2
+
3
+ Pakyow UI is an open-source project licensed under the terms of the LGPLv3 license.
4
+ See <https://choosealicense.com/licenses/lgpl-3.0/> for license text.
@@ -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 released free and open-source under the [MIT
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