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
@@ -1,30 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
class ViewContext
|
4
|
-
MSG_NONCOMPONENT = 'Cannot subscribe a non-component view'
|
5
|
-
|
6
|
-
# Mutates a view with a registered mutator.
|
7
|
-
#
|
8
|
-
# @api public
|
9
|
-
def mutate(mutator, data: nil, with: nil)
|
10
|
-
Pakyow::UI::Mutator.instance.mutate(mutator, self, data || with || [])
|
11
|
-
end
|
12
|
-
|
13
|
-
# Subscribes a view and sets the `data-channel` attribute.
|
14
|
-
#
|
15
|
-
# @api public
|
16
|
-
def subscribe(qualifications = {})
|
17
|
-
fail ArgumentError, MSG_NONCOMPONENT unless component?
|
18
|
-
|
19
|
-
channel = Pakyow::UI::ChannelBuilder.build(
|
20
|
-
component: component_name,
|
21
|
-
qualifications: qualifications
|
22
|
-
)
|
23
|
-
|
24
|
-
context.socket.subscribe(channel)
|
25
|
-
attrs.send(:'data-channel=', channel)
|
26
|
-
self
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
require_relative 'no_op_view'
|
2
|
-
|
3
|
-
# Makes it possible to fetch a particular part of a view for a path. Calls a
|
4
|
-
# route with all view actions becoming no-ops. Then a query is run against the
|
5
|
-
# final view, pulling out the part that was requested.
|
6
|
-
#
|
7
|
-
# Expects the following in the message:
|
8
|
-
#
|
9
|
-
# - uri: the route to call
|
10
|
-
# - lookup: the view query
|
11
|
-
#
|
12
|
-
# Lookup currently supports the following keys:
|
13
|
-
#
|
14
|
-
# - channel
|
15
|
-
# - version
|
16
|
-
# - container
|
17
|
-
# - partial
|
18
|
-
# - scope
|
19
|
-
# - prop
|
20
|
-
#
|
21
|
-
Pakyow::Realtime.handler :'fetch-view' do |message, session, response|
|
22
|
-
env = Rack::MockRequest.env_for(message['uri'])
|
23
|
-
env['pakyow.socket'] = true
|
24
|
-
env['rack.session'] = session
|
25
|
-
|
26
|
-
context = Pakyow::CallContext.new(env)
|
27
|
-
|
28
|
-
def context.view
|
29
|
-
Pakyow::Presenter::NoOpView.new(
|
30
|
-
Pakyow::Presenter::ViewContext.new(@presenter.view, self),
|
31
|
-
self
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
app_response = context.process.finish
|
36
|
-
|
37
|
-
body = ''
|
38
|
-
lookup = message['lookup']
|
39
|
-
view = context.presenter.view
|
40
|
-
|
41
|
-
channel = lookup['channel']
|
42
|
-
|
43
|
-
if channel
|
44
|
-
unqualified_channel = channel.split('::')[0]
|
45
|
-
view_for_channel = view.composed.doc.channel(unqualified_channel)
|
46
|
-
|
47
|
-
if view_for_channel
|
48
|
-
view_for_channel.set_attribute(:'data-channel', channel)
|
49
|
-
body = view_for_channel.to_html
|
50
|
-
end
|
51
|
-
else
|
52
|
-
lookup.each_pair do |key, value|
|
53
|
-
next if key == 'version'
|
54
|
-
view = view.send(key.to_sym, value.to_sym)
|
55
|
-
end
|
56
|
-
|
57
|
-
if view.is_a?(Pakyow::Presenter::ViewVersion)
|
58
|
-
body = view.use((lookup['version'] || :default).to_sym).to_html
|
59
|
-
else
|
60
|
-
body = view.to_html
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
response[:status] = app_response[0]
|
65
|
-
response[:headers] = app_response[1]
|
66
|
-
response[:body] = body
|
67
|
-
response
|
68
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
# Used by NoOpView to perform mutations in a no-op manner.
|
4
|
-
#
|
5
|
-
# @api private
|
6
|
-
class MockMutationEval
|
7
|
-
def initialize(mutation_name, relation_name, view)
|
8
|
-
@mutation_name = mutation_name
|
9
|
-
@relation_name = relation_name
|
10
|
-
@view = view
|
11
|
-
end
|
12
|
-
|
13
|
-
# NOTE we don't care about qualifiers here since we're just getting
|
14
|
-
# the proper view template; not actually setting it up with data
|
15
|
-
def subscribe(*_args)
|
16
|
-
channel = Pakyow::UI::ChannelBuilder.build(
|
17
|
-
scope: @view.scoped_as,
|
18
|
-
mutation: @mutation_name
|
19
|
-
)
|
20
|
-
|
21
|
-
@view.attrs.send(:'data-channel=', channel)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
require_relative 'mutable_data'
|
2
|
-
|
3
|
-
# TODO: make it possible to register this as data instead of mutables
|
4
|
-
|
5
|
-
module Pakyow
|
6
|
-
module UI
|
7
|
-
# Mutables enable PakyowUI to automatically handle changes in application
|
8
|
-
# state by interacting with the data layer in a declarative manner.
|
9
|
-
#
|
10
|
-
# Wraps a data source (such as a model object) and provides a convenient
|
11
|
-
# interface for defining and executing queries and actions. Queries accept
|
12
|
-
# parameters and return data sets. Actions cause a state change in
|
13
|
-
# application state.
|
14
|
-
#
|
15
|
-
# Once defined, all interactions with the data layer should occur through
|
16
|
-
# Mutables via the `data` helper method. When an action is performed that
|
17
|
-
# changes the state of the application, Pakyow will propogate the change
|
18
|
-
# through to all other connected clients automatically.
|
19
|
-
#
|
20
|
-
# Mutables should be registered with the `Pakyow::App.mutable` helper. The
|
21
|
-
# defined block will be executed in context of a `Mutable` instance.
|
22
|
-
#
|
23
|
-
# @api public
|
24
|
-
class Mutable
|
25
|
-
include Helpers
|
26
|
-
|
27
|
-
attr_reader :context
|
28
|
-
|
29
|
-
# @api private
|
30
|
-
def initialize(context, scope, &block)
|
31
|
-
@context = context
|
32
|
-
@scope = scope
|
33
|
-
@actions = {}
|
34
|
-
@queries = {}
|
35
|
-
|
36
|
-
instance_exec(&block)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Sets the model object.
|
40
|
-
#
|
41
|
-
# @api public
|
42
|
-
def model(model_class, type: nil)
|
43
|
-
@model_class = model_class
|
44
|
-
|
45
|
-
return if type.nil?
|
46
|
-
@model_type = type
|
47
|
-
|
48
|
-
# TODO: load default actions / queries based on type
|
49
|
-
end
|
50
|
-
|
51
|
-
# Defines an action.
|
52
|
-
#
|
53
|
-
# @api public
|
54
|
-
def action(name, mutation: true, &block)
|
55
|
-
@actions[name] = {
|
56
|
-
block: block,
|
57
|
-
mutation: mutation
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
# Defines a query.
|
62
|
-
#
|
63
|
-
# @api public
|
64
|
-
def query(name, &block)
|
65
|
-
@queries[name] = block
|
66
|
-
end
|
67
|
-
|
68
|
-
# Handles calling queries or actions. Enables convenience like:
|
69
|
-
#
|
70
|
-
# data(:some_data).{action or query}
|
71
|
-
#
|
72
|
-
# @api public
|
73
|
-
def method_missing(method, *args)
|
74
|
-
action = @actions[method]
|
75
|
-
query = @queries[method]
|
76
|
-
|
77
|
-
if action
|
78
|
-
call_action(action, *args)
|
79
|
-
elsif query
|
80
|
-
call_query(query, method, *args)
|
81
|
-
else
|
82
|
-
fail ArgumentError, "Could not find query or action named #{method}"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def call_action(action, *args)
|
89
|
-
result = action[:block].call(*args)
|
90
|
-
@context.ui.mutated(@scope, result, @context) if action[:mutation]
|
91
|
-
result
|
92
|
-
end
|
93
|
-
|
94
|
-
def call_query(query, method, *args)
|
95
|
-
MutableData.new(query, method, args, @scope)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module UI
|
3
|
-
# Adds metadata to a dataset returned by a Mutable query.
|
4
|
-
#
|
5
|
-
# @api private
|
6
|
-
class MutableData
|
7
|
-
attr_reader :query_name, :query_args, :scope
|
8
|
-
|
9
|
-
def initialize(query, query_name, query_args, scope)
|
10
|
-
@query = query
|
11
|
-
@query_name = query_name
|
12
|
-
@query_args = query_args
|
13
|
-
@scope = scope
|
14
|
-
end
|
15
|
-
|
16
|
-
def data
|
17
|
-
@data ||= @query.call(*@query_args)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
require_relative 'channel_builder'
|
2
|
-
|
3
|
-
module Pakyow
|
4
|
-
module UI
|
5
|
-
# Provides helper methods to perform in context of a mutation. For example:
|
6
|
-
#
|
7
|
-
# view.scope(:foo).mutate(:bar).subscribe
|
8
|
-
#
|
9
|
-
# In the above example `mutate` returns a MutateContext object on which
|
10
|
-
# `subscribe` is called.
|
11
|
-
#
|
12
|
-
# @api public
|
13
|
-
class MutateContext
|
14
|
-
attr_reader :mutation, :view, :data
|
15
|
-
|
16
|
-
# Creates a new context. Intended to be created by a Mutator.
|
17
|
-
#
|
18
|
-
# @api private
|
19
|
-
def initialize(mutation, view, data)
|
20
|
-
@mutation = mutation
|
21
|
-
@view = view
|
22
|
-
@data = data
|
23
|
-
end
|
24
|
-
|
25
|
-
# Subscribes a mutation with optional qualifications. Qualifications are
|
26
|
-
# used to control the scope of future mutations. For example:
|
27
|
-
#
|
28
|
-
# view.scope(:foo).mutate(:bar).subscribe(user_id: 1)
|
29
|
-
#
|
30
|
-
# In the above example, a subscription is created qualified by `user_id`.
|
31
|
-
# Only mutations occuring with the same qualifications will cause the
|
32
|
-
# mutation to be performed again, triggering a view refresh.
|
33
|
-
#
|
34
|
-
# ui.mutated(:foo, user_id: 1)
|
35
|
-
#
|
36
|
-
# @api public
|
37
|
-
def subscribe(qualifications = {})
|
38
|
-
if data.is_a?(MutableData) && !view.context.request.env['pakyow.socket']
|
39
|
-
MutationStore.instance.register(self, view, data, qualifications, view.context.request.session)
|
40
|
-
end
|
41
|
-
|
42
|
-
channel = ChannelBuilder.build(
|
43
|
-
scope: view.scoped_as,
|
44
|
-
mutation: mutation[:name],
|
45
|
-
qualifiers: mutation[:qualifiers],
|
46
|
-
data: data,
|
47
|
-
qualifications: qualifications
|
48
|
-
)
|
49
|
-
|
50
|
-
# subscribe to the channel
|
51
|
-
view.context.socket.subscribe(channel)
|
52
|
-
|
53
|
-
# handle setting the channel on the view
|
54
|
-
if view.is_a?(Presenter::ViewContext)
|
55
|
-
working_view = view.instance_variable_get(:@view)
|
56
|
-
else
|
57
|
-
working_view = view
|
58
|
-
end
|
59
|
-
|
60
|
-
working_view.attrs.send(:'data-channel=', channel)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module UI
|
3
|
-
# Stores mutations.
|
4
|
-
#
|
5
|
-
# @api private
|
6
|
-
class MutationSet
|
7
|
-
attr_reader :mutations
|
8
|
-
|
9
|
-
def initialize(&block)
|
10
|
-
@mutations = {}
|
11
|
-
instance_exec(&block)
|
12
|
-
end
|
13
|
-
|
14
|
-
# NOTE I do have some concerns about defining qualifiers in this way;
|
15
|
-
# mainly because it will lead to having lots of versions of the same
|
16
|
-
# mutator just so the proper channels will be created.
|
17
|
-
#
|
18
|
-
# It's could end up being better to pass qualifiers to `subscribe`;
|
19
|
-
# however it feels premature to make this decision since it'll lead
|
20
|
-
# to a large increase in complexity to add at this point.
|
21
|
-
def mutator(name, qualify: [], &block)
|
22
|
-
@mutations[name] = {
|
23
|
-
fn: block,
|
24
|
-
qualifiers: Array.ensure(qualify),
|
25
|
-
name: name
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
def mutation(name)
|
30
|
-
@mutations.fetch(name)
|
31
|
-
end
|
32
|
-
|
33
|
-
def each(&block)
|
34
|
-
@mutations.each(&block)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module UI
|
3
|
-
# Stores mutations that have occurred in the configured registry.
|
4
|
-
#
|
5
|
-
# @api private
|
6
|
-
class MutationStore
|
7
|
-
include Singleton
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@registry = Config.ui.registry.instance
|
11
|
-
end
|
12
|
-
|
13
|
-
def register(mutate_context, view, mutable_data, qualifications, session)
|
14
|
-
@registry.register(
|
15
|
-
mutable_data.scope,
|
16
|
-
|
17
|
-
view_scope: view.scoped_as,
|
18
|
-
mutation: mutate_context.mutation[:name],
|
19
|
-
qualifiers: mutate_context.mutation[:qualifiers],
|
20
|
-
qualifications: qualifications,
|
21
|
-
query_name: mutable_data.query_name,
|
22
|
-
query_args: mutable_data.query_args,
|
23
|
-
session: session.to_hash,
|
24
|
-
socket_key: mutate_context.view.context.socket_digest(mutate_context.view.context.socket_connection_id)
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
def unregister(socket_key)
|
29
|
-
@registry.unregister(socket_key)
|
30
|
-
end
|
31
|
-
|
32
|
-
def mutations(scope)
|
33
|
-
@registry.mutations(scope) || []
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
Pakyow::Realtime::Websocket.on :leave do
|
40
|
-
Pakyow::UI::MutationStore.instance.unregister(socket_digest(socket_connection_id))
|
41
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require_relative 'mutation_set'
|
2
|
-
require_relative 'mutate_context'
|
3
|
-
|
4
|
-
module Pakyow
|
5
|
-
module UI
|
6
|
-
# Performs mutations on views.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class Mutator
|
10
|
-
include Singleton
|
11
|
-
|
12
|
-
attr_reader :sets
|
13
|
-
|
14
|
-
# @api private
|
15
|
-
def initialize
|
16
|
-
reset
|
17
|
-
end
|
18
|
-
|
19
|
-
def reset
|
20
|
-
@sets = {}
|
21
|
-
@mutables = {}
|
22
|
-
self
|
23
|
-
end
|
24
|
-
|
25
|
-
def set(scope, &block)
|
26
|
-
@sets[scope] = MutationSet.new(&block)
|
27
|
-
end
|
28
|
-
|
29
|
-
def mutable(scope, context = nil, &block)
|
30
|
-
if block_given?
|
31
|
-
@mutables[scope] = block
|
32
|
-
else
|
33
|
-
# TODO: inefficient to have to execute the block each time
|
34
|
-
Mutable.new(context, scope, &@mutables[scope])
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def mutation(scope, name)
|
39
|
-
if mutations = mutations_by_scope(scope)
|
40
|
-
mutations.mutation(name)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# TODO: rename to mutation_set_for_scope
|
45
|
-
def mutations_by_scope(scope)
|
46
|
-
@sets[scope]
|
47
|
-
end
|
48
|
-
|
49
|
-
def mutate(mutation_name, view, data)
|
50
|
-
if mutation = mutation(view.scoped_as, mutation_name)
|
51
|
-
if data.is_a?(MutableData)
|
52
|
-
working_data = data.data
|
53
|
-
else
|
54
|
-
working_data = data
|
55
|
-
end
|
56
|
-
|
57
|
-
view.instance_exec(view, working_data, &mutation[:fn])
|
58
|
-
MutateContext.new(mutation, view, data)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
require_relative 'mock_mutation_eval'
|
2
|
-
|
3
|
-
module Pakyow
|
4
|
-
module Presenter
|
5
|
-
# Stands in for a real View object and makes any attempted transformation
|
6
|
-
# a no-op.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class NoOpView
|
10
|
-
include Helpers
|
11
|
-
VIEW_CLASSES = [ViewContext]
|
12
|
-
|
13
|
-
# The arities of misc view methods that switch the behavior from
|
14
|
-
# instance_exec to yield.
|
15
|
-
#
|
16
|
-
EXEC_ARITIES = { with: 0, for: 1, for_with_index: 2, repeat: 1,
|
17
|
-
repeat_with_index: 2, bind: 1, bind_with_index: 2,
|
18
|
-
apply: 1 }
|
19
|
-
|
20
|
-
def initialize(view, context)
|
21
|
-
@view = view
|
22
|
-
@context = context
|
23
|
-
end
|
24
|
-
|
25
|
-
def is_a?(klass)
|
26
|
-
@view.is_a?(klass)
|
27
|
-
end
|
28
|
-
|
29
|
-
# View methods that should be a no-op
|
30
|
-
#
|
31
|
-
%i(bind bind_with_index apply).each do |method|
|
32
|
-
define_method(method) do |_data, **_kargs, &_block|
|
33
|
-
self
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def mutate(mutator, with: nil, data: nil)
|
38
|
-
MockMutationEval.new(mutator, with || data, self)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Pass these through, handling the return value.
|
42
|
-
#
|
43
|
-
def method_missing(method, *args, &block)
|
44
|
-
ret = @view.send(method, *args, &wrap(method, &block))
|
45
|
-
handle_return_value(ret)
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def view?(obj)
|
51
|
-
VIEW_CLASSES.include?(obj.class)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Returns a new context for returned views, or the return value.
|
55
|
-
#
|
56
|
-
def handle_return_value(value)
|
57
|
-
return NoOpView.new(value, @context) if view?(value)
|
58
|
-
|
59
|
-
value
|
60
|
-
end
|
61
|
-
|
62
|
-
# Wrap the block, substituting the view with the current view context.
|
63
|
-
#
|
64
|
-
def wrap(method, &block)
|
65
|
-
return if block.nil?
|
66
|
-
|
67
|
-
proc do |*args|
|
68
|
-
ctx = args.map! { |arg|
|
69
|
-
view?(arg) ? NoOpView.new(arg, @context) : arg
|
70
|
-
}.find { |arg| arg.is_a?(ViewContext) }
|
71
|
-
|
72
|
-
case block.arity
|
73
|
-
when EXEC_ARITIES[method]
|
74
|
-
# Rejecting ViewContext handles the edge cases around the order of
|
75
|
-
# arguments from view methods (since view is not present in some
|
76
|
-
# situations and when it is present, is always the first arg).
|
77
|
-
ctx.instance_exec(*args.reject { |arg|
|
78
|
-
arg.is_a?(ViewContext)
|
79
|
-
}, &block)
|
80
|
-
else
|
81
|
-
block.call(*args)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module Pakyow
|
4
|
-
module UI
|
5
|
-
# Manages mutations.
|
6
|
-
#
|
7
|
-
# This is the default registry in production systems and is required in
|
8
|
-
# deployments with more than one app instance.
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
class RedisMutationRegistry
|
12
|
-
include Singleton
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
end
|
16
|
-
|
17
|
-
def register(scope, mutation)
|
18
|
-
Pakyow::Realtime.redis.sadd(key(scope: scope, socket_key: mutation[:socket_key]), mutation.to_json)
|
19
|
-
end
|
20
|
-
|
21
|
-
def mutations(scope)
|
22
|
-
mutations = []
|
23
|
-
|
24
|
-
keys(key(scope: scope)) do |key|
|
25
|
-
Pakyow::Realtime.redis.smembers(key).each do |m|
|
26
|
-
mutations << Hash.strhash(JSON.parse(m))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
mutations
|
31
|
-
end
|
32
|
-
|
33
|
-
def unregister(socket_key)
|
34
|
-
keys(key(socket_key: socket_key)) do |key|
|
35
|
-
Pakyow::Realtime.redis.del(key)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def key(scope: nil, socket_key: nil)
|
42
|
-
if socket_key.nil?
|
43
|
-
base = "*:"
|
44
|
-
else
|
45
|
-
base = "#{socket_key}:"
|
46
|
-
end
|
47
|
-
|
48
|
-
if scope.nil?
|
49
|
-
"#{base}*"
|
50
|
-
else
|
51
|
-
"#{base}pui-mutation-#{scope}"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def keys(match)
|
56
|
-
cursor = 0
|
57
|
-
|
58
|
-
loop do
|
59
|
-
cursor, keys = Pakyow::Realtime.redis.scan(cursor, match: match)
|
60
|
-
|
61
|
-
keys.each do |key|
|
62
|
-
yield key
|
63
|
-
end
|
64
|
-
|
65
|
-
break if cursor == '0'
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module UI
|
3
|
-
# Manages mutations.
|
4
|
-
#
|
5
|
-
# Intended only for use in development or single app-instance deployments.
|
6
|
-
#
|
7
|
-
# @api private
|
8
|
-
class SimpleMutationRegistry
|
9
|
-
include Singleton
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
reset
|
13
|
-
end
|
14
|
-
|
15
|
-
def reset
|
16
|
-
@mutations = {}
|
17
|
-
end
|
18
|
-
|
19
|
-
def register(scope, mutation)
|
20
|
-
@mutations[scope] ||= []
|
21
|
-
@mutations[scope] << mutation
|
22
|
-
end
|
23
|
-
|
24
|
-
def unregister(socket_key)
|
25
|
-
@mutations.each do |_, mutations|
|
26
|
-
mutations.delete_if { |mutation|
|
27
|
-
mutation[:socket_key] == socket_key
|
28
|
-
}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def mutations(scope)
|
33
|
-
@mutations[scope]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|