cubism 0.1.0.pre9 → 0.1.0.pre12
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/README.md +15 -4
- data/app/channels/cubism/presence_channel.rb +14 -5
- data/app/channels/cubism/presence_channel.rb~ +14 -5
- data/app/helpers/cubism_helper.rb +22 -7
- data/app/helpers/cubism_helper.rb~ +36 -0
- data/app/models/concerns/cubism/presence.rb +11 -3
- data/app/models/concerns/cubism/presence.rb~ +7 -0
- data/lib/cubism/broadcaster.rb +15 -6
- data/lib/cubism/broadcaster.rb~ +14 -14
- data/lib/cubism/cubicle_block_store.rb~ +34 -5
- data/lib/cubism/cubicle_source_store.rb~ +0 -0
- data/lib/cubism/cubicle_store.rb +141 -0
- data/lib/cubism/cubicle_store.rb~ +141 -0
- data/lib/cubism/engine.rb +4 -0
- data/lib/cubism/engine.rb~ +7 -0
- data/lib/cubism/parser.rb~ +0 -0
- data/lib/cubism/preprocessor.rb +33 -0
- data/lib/cubism/preprocessor.rb~ +30 -0
- data/lib/cubism/version.rb +1 -1
- data/lib/cubism/version.rb~ +1 -1
- data/lib/cubism.rb +5 -2
- data/lib/cubism.rb~ +3 -1
- metadata +10 -3
- data/lib/cubism/cubicle_block_store.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cd7c7ee681cabc05fd5329e7e6c17fd030d037beb100440faf1ef190a614ddf
|
4
|
+
data.tar.gz: a772708b9dc6a32c5a37766d203ea6a4b119eac57368378121e6b4da9f2b50f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b47525bcc5545b289624615b66215b68467c2a2ba35a7b5335c944bdfacc68d0c6d60c430c93ad1b18470db7e7d3a58313f232b35bc32a34b21847f4a28f086b
|
7
|
+
data.tar.gz: 4595ebf829643d556613ebc5ca03e19f35cc6601a8c435f0dfea3c4e9cc7c08966c94ae69996e87d0e8c572d1d0decc8e6bd1cfa9ee7840a7abad29909f3ab7f
|
data/README.md
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
5
5
|
[](https://twitter.com/julian_rubisch)
|
6
6
|
|
7
|
-
Lightweight Resource-Based Presence Solution with CableReady
|
7
|
+
Lightweight Resource-Based Presence Solution with CableReady.
|
8
|
+
|
9
|
+
`Cubism` provides real-time updates of who is viewing or interacting with whatever resources you need. Whether you want Slack's "X is typing..." indicator or an e-commerce "5 other customers are viewing this item" notice, `Cubism` gives you everything you need "under the hood" so that you can focus on what really matters—end-user functionality.
|
8
10
|
|
9
11
|
## Table of Contents
|
10
12
|
|
@@ -12,6 +14,7 @@ Lightweight Resource-Based Presence Solution with CableReady
|
|
12
14
|
- [Usage](#usage)
|
13
15
|
- [Installation](#installation)
|
14
16
|
- [API](#api)
|
17
|
+
- [Limitations](#limitations)
|
15
18
|
- [Gotchas](#gotchas)
|
16
19
|
- [Contributing](#contributing)
|
17
20
|
- [License](#license)
|
@@ -54,6 +57,8 @@ Using the `cubicle_for` helper, you can set up a presence indicator. It will
|
|
54
57
|
<% end %>
|
55
58
|
```
|
56
59
|
|
60
|
+
**Important!** due to technical limitations the cubism block does _not_ act as a closure, i.e. it has _only_ access to the `users` variable passed to it - think of it more as a self-contained component.
|
61
|
+
|
57
62
|
## Installation
|
58
63
|
Add this line to your application's Gemfile:
|
59
64
|
|
@@ -91,12 +96,18 @@ CableReady.initialize({ consumer });
|
|
91
96
|
|
92
97
|
The `cubicle_for` helper accepts the following options as keyword arguments:
|
93
98
|
|
94
|
-
- `
|
95
|
-
- `
|
96
|
-
- `
|
99
|
+
- `scope`: declare a scope in which presence indicators should appear. For example, if you want to divide between index and show views, do `scope: :index` and `scope: :show` respectively (default: `""`).
|
100
|
+
- `exclude_current_user (true|false)`: Whether or not to exclude the current user from the list of present users broadcasted to the view. Useful e.g. for "typing..." indicators (default: `true`).
|
101
|
+
- `appear_trigger`: JavaScript event names (e.g. `["focus", "debounced:input]`) to use. (Can also be a singular string, which will be converted to an array). The default is `:connect`, i.e. register a user as "appeared"/"present" when the element connects to the DOM. Another special value is `:intersect`, which fires when the `trigger_root` comes into the viewport.
|
102
|
+
- `disappear_trigger`: a JavaScript event name (e.g. `:blur`) to use. (Can also be a singular string, which will be converted to an array). The default is `:disconnect`, i.e. remove a user form the present users list when the element disconnects from the DOM. Analoguous to above, `:intersect` means that `disappear` will fire when the `trigger_root` is scrolled out of the viewport.
|
97
103
|
- `trigger_root`: a CSS selector to attach the appear/disappear events to. Defaults to the `cubicle-element` itself.
|
98
104
|
- `html_options` are passed to the TagBuilder.
|
99
105
|
|
106
|
+
## Limitations
|
107
|
+
|
108
|
+
### Supported Template Handlers
|
109
|
+
- ERB
|
110
|
+
|
100
111
|
## Gotchas
|
101
112
|
|
102
113
|
### Usage with ViewComponent
|
@@ -3,7 +3,7 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
3
3
|
|
4
4
|
def subscribed
|
5
5
|
if resource.present?
|
6
|
-
stream_from
|
6
|
+
stream_from element_id
|
7
7
|
resource.cubicle_element_ids << element_id
|
8
8
|
resource.excluded_user_id_for_element_id[element_id] = user.id if exclude_current_user?
|
9
9
|
else
|
@@ -20,11 +20,11 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def appear
|
23
|
-
resource.
|
23
|
+
resource.set_present_users_for_scope(resource.present_users_for_scope(scope).add(user.id), scope) if scope
|
24
24
|
end
|
25
25
|
|
26
26
|
def disappear
|
27
|
-
resource.
|
27
|
+
resource.set_present_users_for_scope(resource.present_users_for_scope(scope).delete(user.id), scope) if scope
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -35,7 +35,15 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def user
|
38
|
-
|
38
|
+
block_container&.user
|
39
|
+
end
|
40
|
+
|
41
|
+
def scope
|
42
|
+
block_container&.scope
|
43
|
+
end
|
44
|
+
|
45
|
+
def block_container
|
46
|
+
Cubism.block_store[element_id]
|
39
47
|
end
|
40
48
|
|
41
49
|
def exclude_current_user?
|
@@ -43,7 +51,8 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
43
51
|
end
|
44
52
|
|
45
53
|
def element_id
|
46
|
-
params[:element_id]
|
54
|
+
/cubicle-(?<element_id>.+)/ =~ params[:element_id]
|
55
|
+
element_id
|
47
56
|
end
|
48
57
|
|
49
58
|
def url
|
@@ -3,7 +3,7 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
3
3
|
|
4
4
|
def subscribed
|
5
5
|
if resource.present?
|
6
|
-
stream_from
|
6
|
+
stream_from element_id
|
7
7
|
resource.cubicle_element_ids << element_id
|
8
8
|
resource.excluded_user_id_for_element_id[element_id] = user.id if exclude_current_user?
|
9
9
|
else
|
@@ -20,11 +20,11 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def appear
|
23
|
-
resource.
|
23
|
+
resource.set_present_users_for_scope(resource.present_users_for_scope(scope).add(user.id), scope)
|
24
24
|
end
|
25
25
|
|
26
26
|
def disappear
|
27
|
-
resource.
|
27
|
+
resource.set_present_users_for_scope(resource.present_users_for_scope(scope).delete(user.id), scope)
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -35,7 +35,15 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def user
|
38
|
-
|
38
|
+
block_container.user
|
39
|
+
end
|
40
|
+
|
41
|
+
def scope
|
42
|
+
block_container.scope
|
43
|
+
end
|
44
|
+
|
45
|
+
def block_container
|
46
|
+
Cubism.block_store[element_id]
|
39
47
|
end
|
40
48
|
|
41
49
|
def exclude_current_user?
|
@@ -43,7 +51,8 @@ class Cubism::PresenceChannel < ActionCable::Channel::Base
|
|
43
51
|
end
|
44
52
|
|
45
53
|
def element_id
|
46
|
-
params[:element_id]
|
54
|
+
/cubicle-(?<element_id>.+)/ =~ params[:element_id]
|
55
|
+
element_id
|
47
56
|
end
|
48
57
|
|
49
58
|
def url
|
@@ -1,18 +1,33 @@
|
|
1
1
|
module CubismHelper
|
2
2
|
include CableReady::StreamIdentifier
|
3
3
|
|
4
|
-
def cubicle_for(resource, user, html_options: {}, appear_trigger: :connect, disappear_trigger: nil, trigger_root: nil, exclude_current_user: true, &block)
|
5
|
-
|
6
|
-
|
4
|
+
def cubicle_for(resource, user, scope: "", html_options: {}, appear_trigger: :connect, disappear_trigger: nil, trigger_root: nil, exclude_current_user: true, &block)
|
5
|
+
block_location = block.source_location.join(":")
|
6
|
+
block_source = Cubism::BlockSource.find_or_create(
|
7
|
+
location: block_location,
|
8
|
+
view_context: self
|
9
|
+
)
|
10
|
+
|
11
|
+
resource_gid = resource.to_gid.to_s
|
12
|
+
|
13
|
+
block_container = Cubism::BlockContainer.new(
|
14
|
+
block_location: block_location,
|
15
|
+
block_source: block_source,
|
16
|
+
resource_gid: resource_gid,
|
17
|
+
user_gid: user.to_gid.to_s,
|
18
|
+
scope: scope
|
19
|
+
)
|
20
|
+
|
21
|
+
digested_block_key = block_container.digest
|
22
|
+
|
23
|
+
Cubism.block_store.fetch(digested_block_key, block_container)
|
7
24
|
|
8
|
-
Cubism.store[digested_id] = Cubism::BlockStoreItem.new(context: self, block: block.dup)
|
9
25
|
tag.cubicle_element(
|
10
|
-
identifier: signed_stream_identifier(
|
11
|
-
user: user.to_sgid.to_s,
|
26
|
+
identifier: signed_stream_identifier(resource_gid),
|
12
27
|
"appear-trigger": Array(appear_trigger).join(","),
|
13
28
|
"disappear-trigger": disappear_trigger,
|
14
29
|
"trigger-root": trigger_root,
|
15
|
-
id: "cubicle-#{
|
30
|
+
id: "cubicle-#{digested_block_key}",
|
16
31
|
"exclude-current-user": exclude_current_user,
|
17
32
|
**html_options
|
18
33
|
)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CubismHelper
|
2
|
+
include CableReady::StreamIdentifier
|
3
|
+
|
4
|
+
def cubicle_for(resource, user, scope: "", html_options: {}, appear_trigger: :connect, disappear_trigger: nil, trigger_root: nil, exclude_current_user: true, &block)
|
5
|
+
block_location = block.source_location.join(":")
|
6
|
+
block_source = Cubism::BlockSource.find_or_create(
|
7
|
+
location: block_location,
|
8
|
+
view_context: self
|
9
|
+
)
|
10
|
+
|
11
|
+
resource_gid = resource.to_gid.to_s
|
12
|
+
|
13
|
+
block_container = Cubism::BlockContainer.new(
|
14
|
+
block_location: block_location,
|
15
|
+
block_source: block_source,
|
16
|
+
resource_gid: resource_gid,
|
17
|
+
user_gid: user.to_gid.to_s,
|
18
|
+
scope: scope
|
19
|
+
)
|
20
|
+
|
21
|
+
digested_block_key = block_container.digest
|
22
|
+
|
23
|
+
Cubism.block_store.fetch(digested_block_key, block_container)
|
24
|
+
|
25
|
+
tag.cubicle_element(
|
26
|
+
identifier: signed_stream_identifier(resource_gid),
|
27
|
+
"appear-trigger": Array(appear_trigger).join(","),
|
28
|
+
"disappear-trigger": disappear_trigger,
|
29
|
+
"trigger-root": trigger_root,
|
30
|
+
scope: scope,
|
31
|
+
id: "cubicle-#{digested_block_key}",
|
32
|
+
"exclude-current-user": exclude_current_user,
|
33
|
+
**html_options
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
@@ -2,7 +2,7 @@ module Cubism::Presence
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
|
5
|
+
kredis_hash :present_users, after_change: :stream_presence
|
6
6
|
kredis_set :cubicle_element_ids
|
7
7
|
kredis_hash :excluded_user_id_for_element_id
|
8
8
|
end
|
@@ -11,8 +11,16 @@ module Cubism::Presence
|
|
11
11
|
Cubism::Broadcaster.new(resource: self).broadcast
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
14
|
+
def present_users_for_scope(scope = "")
|
15
|
+
present_users[scope].present? ? Marshal.load(present_users[scope]) : Set.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_present_users_for_scope(user_ids, scope = "")
|
19
|
+
present_users[scope] = Marshal.dump(Set.new(user_ids))
|
20
|
+
end
|
21
|
+
|
22
|
+
def present_users_for_element_id_and_scope(element_id, scope = "")
|
23
|
+
users = Cubism.user_class.find(present_users_for_scope(scope).to_a)
|
16
24
|
users.reject! { |user| user.id == excluded_user_id_for_element_id[element_id].to_i }
|
17
25
|
|
18
26
|
users
|
@@ -10,4 +10,11 @@ module Cubism::Presence
|
|
10
10
|
def stream_presence
|
11
11
|
Cubism::Broadcaster.new(resource: self).broadcast
|
12
12
|
end
|
13
|
+
|
14
|
+
def present_users_for_element_id(element_id)
|
15
|
+
users = Cubism.user_class.find(present_users.members)
|
16
|
+
users.reject! { |user| user.id == excluded_user_id_for_element_id[element_id].to_i }
|
17
|
+
|
18
|
+
users
|
19
|
+
end
|
13
20
|
end
|
data/lib/cubism/broadcaster.rb
CHANGED
@@ -13,16 +13,25 @@ module Cubism
|
|
13
13
|
|
14
14
|
def broadcast
|
15
15
|
resource.cubicle_element_ids.to_a.each do |element_id|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
block_container = Cubism.block_store[element_id]
|
17
|
+
|
18
|
+
next if block_container.blank?
|
19
|
+
|
20
|
+
present_users = resource.present_users_for_element_id_and_scope(element_id, block_container.scope)
|
21
|
+
|
22
|
+
block_source = block_container.block_source
|
23
|
+
|
24
|
+
html = ApplicationController.render(inline: block_source.source, locals: {"#{block_source.variable_name}": present_users})
|
25
|
+
|
26
|
+
selector = "cubicle-element#cubicle-#{element_id}[identifier='#{signed_stream_identifier(resource.to_global_id.to_s)}']"
|
20
27
|
|
21
28
|
cable_ready[element_id].inner_html(
|
22
|
-
selector:
|
29
|
+
selector: selector,
|
23
30
|
html: html
|
24
|
-
)
|
31
|
+
)
|
25
32
|
end
|
33
|
+
|
34
|
+
cable_ready.broadcast
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
data/lib/cubism/broadcaster.rb~
CHANGED
@@ -13,25 +13,25 @@ module Cubism
|
|
13
13
|
|
14
14
|
def broadcast
|
15
15
|
resource.cubicle_element_ids.to_a.each do |element_id|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
block_container = Cubism.block_store[element_id]
|
17
|
+
|
18
|
+
next if block_container.blank?
|
19
|
+
|
20
|
+
present_users = resource.present_users_for_element_id_and_scope(element_id, block_container.scope)
|
21
|
+
|
22
|
+
block_source = block_container.block_source
|
23
|
+
|
24
|
+
html = ApplicationController.render(inline: block_source.source, locals: {"#{block_source.variable_name}": present_users})
|
25
|
+
|
26
|
+
selector = "cubicle-element#cubicle-#{element_id}[identifier='#{signed_stream_identifier(resource.to_global_id.to_s)}'][scope='#{block_container.scope}']"
|
20
27
|
|
21
28
|
cable_ready[element_id].inner_html(
|
22
|
-
selector:
|
29
|
+
selector: selector,
|
23
30
|
html: html
|
24
|
-
)
|
31
|
+
)
|
25
32
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def users_for(resource, element_id)
|
31
|
-
users = Cubism.user_class.find(resource.present_users.members)
|
32
|
-
users.reject! { |user| user.id == resource.excluded_user_id_for_element_id[element_id].to_i }
|
33
33
|
|
34
|
-
|
34
|
+
cable_ready.broadcast
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -1,21 +1,32 @@
|
|
1
1
|
module Cubism
|
2
2
|
class CubicleBlockStore
|
3
|
-
|
3
|
+
delegate_missing_to :@blocks
|
4
4
|
|
5
5
|
def initialize
|
6
|
-
@blocks =
|
6
|
+
@blocks = Kredis.hash "cubism-blocks"
|
7
7
|
end
|
8
8
|
|
9
9
|
def [](key)
|
10
|
-
@blocks[key]
|
10
|
+
Marshal.load(@blocks[key]) if @blocks[key]
|
11
11
|
end
|
12
12
|
|
13
13
|
def []=(key, value)
|
14
14
|
mutex.synchronize do
|
15
|
-
@blocks[key] = value
|
15
|
+
@blocks[key] = Marshal.dump value
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def clear
|
20
|
+
mutex.synchronize do
|
21
|
+
# kredis #remove
|
22
|
+
@blocks.remove
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def size
|
27
|
+
@blocks.to_h.size
|
28
|
+
end
|
29
|
+
|
19
30
|
private
|
20
31
|
|
21
32
|
def mutex
|
@@ -23,5 +34,23 @@ module Cubism
|
|
23
34
|
end
|
24
35
|
end
|
25
36
|
|
26
|
-
BlockStoreItem = Struct.new(:
|
37
|
+
BlockStoreItem = Struct.new(:block_location, :block_source, :user_gid, :resource_gid, keyword_init: true) do
|
38
|
+
def user
|
39
|
+
GlobalID::Locator.locate self[:user_gid]
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource
|
43
|
+
GlobalID::Locator.locate self[:resource_gid]
|
44
|
+
end
|
45
|
+
|
46
|
+
def marshal_dump
|
47
|
+
to_h
|
48
|
+
end
|
49
|
+
|
50
|
+
def marshal_load(serialized_item)
|
51
|
+
%i[block_location block_source user_gid resource_gid].each do |arg|
|
52
|
+
send("#{arg}=", serialized_item[arg])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
27
56
|
end
|
File without changes
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Cubism
|
2
|
+
class CubicleStore
|
3
|
+
delegate_missing_to :@blocks
|
4
|
+
|
5
|
+
def initialize(key)
|
6
|
+
@blocks = Kredis.hash key
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
Marshal.load(@blocks[key]) if @blocks[key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
mutex.synchronize do
|
15
|
+
@blocks[key] = Marshal.dump value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(key, value = nil, &block)
|
20
|
+
if self[key].nil?
|
21
|
+
yield value if block
|
22
|
+
self[key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
self[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear
|
29
|
+
mutex.synchronize do
|
30
|
+
# kredis #remove
|
31
|
+
@blocks.remove
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
@blocks.to_h.size
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def mutex
|
42
|
+
@mutex ||= Mutex.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Container for cubicle blocks
|
47
|
+
BlockContainer = Struct.new(
|
48
|
+
:block_location,
|
49
|
+
:block_source,
|
50
|
+
:user_gid,
|
51
|
+
:resource_gid,
|
52
|
+
:scope,
|
53
|
+
keyword_init: true
|
54
|
+
) do
|
55
|
+
def initialize(*args)
|
56
|
+
super
|
57
|
+
|
58
|
+
@filename, _lineno = block_location.split(":")
|
59
|
+
end
|
60
|
+
|
61
|
+
def user
|
62
|
+
GlobalID::Locator.locate self[:user_gid]
|
63
|
+
end
|
64
|
+
|
65
|
+
def resource
|
66
|
+
GlobalID::Locator.locate self[:resource_gid]
|
67
|
+
end
|
68
|
+
|
69
|
+
def scope
|
70
|
+
self[:scope] || ""
|
71
|
+
end
|
72
|
+
|
73
|
+
def digest
|
74
|
+
resource_user_key = [resource_gid, user_gid, scope].join(":")
|
75
|
+
|
76
|
+
ActiveSupport::Digest.hexdigest("#{block_location}:#{File.read(@filename)}:#{resource_user_key}")
|
77
|
+
end
|
78
|
+
|
79
|
+
def marshal_dump
|
80
|
+
to_h.merge(block_source: block_source.digest)
|
81
|
+
end
|
82
|
+
|
83
|
+
def marshal_load(serialized_item)
|
84
|
+
members.excluding(:block_source).each do |arg|
|
85
|
+
send("#{arg}=", serialized_item[arg])
|
86
|
+
end
|
87
|
+
|
88
|
+
self.block_source = Cubism.source_store[serialized_item[:block_source]]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Container for cubicle block sources
|
93
|
+
BlockSource = Struct.new(
|
94
|
+
:location,
|
95
|
+
:source,
|
96
|
+
:variable_name,
|
97
|
+
:view_context,
|
98
|
+
keyword_init: true
|
99
|
+
) do
|
100
|
+
def self.find_or_create(location:, view_context:)
|
101
|
+
instance = new(location: location, view_context: view_context)
|
102
|
+
|
103
|
+
Cubism.source_store.fetch(instance.digest, instance) do |instance|
|
104
|
+
instance.parse!
|
105
|
+
end
|
106
|
+
|
107
|
+
instance
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(*args)
|
111
|
+
super
|
112
|
+
|
113
|
+
@filename, @lineno = location.split(":")
|
114
|
+
@lineno = @lineno.to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse!
|
118
|
+
return if location.start_with?("inline template")
|
119
|
+
|
120
|
+
lines = File.readlines(@filename)[@lineno - 1..]
|
121
|
+
|
122
|
+
preprocessor = Cubism::Preprocessor.new(source: lines.join.squish, view_context: view_context)
|
123
|
+
self.variable_name = preprocessor.block_variable_name
|
124
|
+
self.source = preprocessor.process
|
125
|
+
end
|
126
|
+
|
127
|
+
def digest
|
128
|
+
ActiveSupport::Digest.hexdigest("#{location}:#{File.read(@filename)}")
|
129
|
+
end
|
130
|
+
|
131
|
+
def marshal_dump
|
132
|
+
to_h.except(:view_context)
|
133
|
+
end
|
134
|
+
|
135
|
+
def marshal_load(serialized_item)
|
136
|
+
members.excluding(:view_context).each do |arg|
|
137
|
+
send("#{arg}=", serialized_item[arg])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Cubism
|
2
|
+
class CubicleStore
|
3
|
+
delegate_missing_to :@blocks
|
4
|
+
|
5
|
+
def initialize(key)
|
6
|
+
@blocks = Kredis.hash key
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
Marshal.load(@blocks[key]) if @blocks[key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
mutex.synchronize do
|
15
|
+
@blocks[key] = Marshal.dump value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(key, value = nil, &block)
|
20
|
+
if self[key].nil?
|
21
|
+
yield value if block
|
22
|
+
self[key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
self[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear
|
29
|
+
mutex.synchronize do
|
30
|
+
# kredis #remove
|
31
|
+
@blocks.remove
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
@blocks.to_h.size
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def mutex
|
42
|
+
@mutex ||= Mutex.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Container for cubicle blocks
|
47
|
+
BlockContainer = Struct.new(
|
48
|
+
:block_location,
|
49
|
+
:block_source,
|
50
|
+
:user_gid,
|
51
|
+
:resource_gid,
|
52
|
+
:scope,
|
53
|
+
keyword_init: true
|
54
|
+
) do
|
55
|
+
def initialize(*args)
|
56
|
+
super
|
57
|
+
|
58
|
+
@filename, _lineno = block_location.split(":")
|
59
|
+
end
|
60
|
+
|
61
|
+
def user
|
62
|
+
GlobalID::Locator.locate self[:user_gid]
|
63
|
+
end
|
64
|
+
|
65
|
+
def resource
|
66
|
+
GlobalID::Locator.locate self[:resource_gid]
|
67
|
+
end
|
68
|
+
|
69
|
+
def scope
|
70
|
+
self[:scope] || ""
|
71
|
+
end
|
72
|
+
|
73
|
+
def digest
|
74
|
+
resource_user_key = [resource_gid, user_gid].join(":")
|
75
|
+
|
76
|
+
ActiveSupport::Digest.hexdigest("#{block_location}:#{File.read(@filename)}:#{resource_user_key}")
|
77
|
+
end
|
78
|
+
|
79
|
+
def marshal_dump
|
80
|
+
to_h.merge(block_source: block_source.digest)
|
81
|
+
end
|
82
|
+
|
83
|
+
def marshal_load(serialized_item)
|
84
|
+
members.excluding(:block_source).each do |arg|
|
85
|
+
send("#{arg}=", serialized_item[arg])
|
86
|
+
end
|
87
|
+
|
88
|
+
self.block_source = Cubism.source_store[serialized_item[:block_source]]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Container for cubicle block sources
|
93
|
+
BlockSource = Struct.new(
|
94
|
+
:location,
|
95
|
+
:source,
|
96
|
+
:variable_name,
|
97
|
+
:view_context,
|
98
|
+
keyword_init: true
|
99
|
+
) do
|
100
|
+
def self.find_or_create(location:, view_context:)
|
101
|
+
instance = new(location: location, view_context: view_context)
|
102
|
+
|
103
|
+
Cubism.source_store.fetch(instance.digest, instance) do |instance|
|
104
|
+
instance.parse!
|
105
|
+
end
|
106
|
+
|
107
|
+
instance
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(*args)
|
111
|
+
super
|
112
|
+
|
113
|
+
@filename, @lineno = location.split(":")
|
114
|
+
@lineno = @lineno.to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse!
|
118
|
+
return if location.start_with?("inline template")
|
119
|
+
|
120
|
+
lines = File.readlines(@filename)[@lineno - 1..]
|
121
|
+
|
122
|
+
preprocessor = Cubism::Preprocessor.new(source: lines.join.squish, view_context: view_context)
|
123
|
+
self.variable_name = preprocessor.block_variable_name
|
124
|
+
self.source = preprocessor.process
|
125
|
+
end
|
126
|
+
|
127
|
+
def digest
|
128
|
+
ActiveSupport::Digest.hexdigest("#{location}:#{File.read(@filename)}")
|
129
|
+
end
|
130
|
+
|
131
|
+
def marshal_dump
|
132
|
+
to_h.except(:view_context)
|
133
|
+
end
|
134
|
+
|
135
|
+
def marshal_load(serialized_item)
|
136
|
+
members.excluding(:view_context).each do |arg|
|
137
|
+
send("#{arg}=", serialized_item[arg])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/cubism/engine.rb
CHANGED
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Cubism
|
2
|
+
class Preprocessor
|
3
|
+
attr_reader :block_variable_name
|
4
|
+
|
5
|
+
def initialize(source:, view_context:)
|
6
|
+
match_data = /<%=\s+cubicle_for.+?\|(\w+)\|\s+%>/.match(source)
|
7
|
+
start_pos = match_data&.end(0) || 0
|
8
|
+
@block_variable_name = match_data[1] if match_data
|
9
|
+
@source = source[start_pos..]
|
10
|
+
@view_context = view_context
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
begin
|
15
|
+
do_parse
|
16
|
+
rescue NameError
|
17
|
+
# we ignore any name errors from unset instance variables or local assigns here
|
18
|
+
end
|
19
|
+
|
20
|
+
@source
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def do_parse
|
26
|
+
ActionView::Template::Handlers::ERB::Erubi.new(@source).evaluate(@view_context)
|
27
|
+
rescue SyntaxError
|
28
|
+
end_at_end = /(<%\s+end\s+%>)\z/.match(@source)
|
29
|
+
@source = end_at_end ? @source[..-(end_at_end[0].length + 1)] : @source[..-2]
|
30
|
+
do_parse
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cubism
|
2
|
+
class Preprocessor
|
3
|
+
def initialize(source:, view_context:)
|
4
|
+
match_data = /<%=\s+cubicle_for.+\|.+\|\s+%>/.match(source)
|
5
|
+
start_pos = match_data&.end(0) || 0
|
6
|
+
@source = source[start_pos..]
|
7
|
+
@view_context = view_context
|
8
|
+
end
|
9
|
+
|
10
|
+
def process
|
11
|
+
begin
|
12
|
+
do_parse
|
13
|
+
rescue NameError
|
14
|
+
# we ignore any name errors from unset instance variables or local assigns here
|
15
|
+
end
|
16
|
+
|
17
|
+
@source
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def do_parse
|
23
|
+
ActionView::Template::Handlers::ERB::Erubi.new(@source).evaluate(@view_context)
|
24
|
+
rescue SyntaxError
|
25
|
+
end_at_end = /(<%\s+end\s+%>)\z/.match(@source)
|
26
|
+
@source = end_at_end ? @source[..-(end_at_end[0].length + 1)] : @source[..-2]
|
27
|
+
do_parse
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/cubism/version.rb
CHANGED
data/lib/cubism/version.rb~
CHANGED
data/lib/cubism.rb
CHANGED
@@ -3,14 +3,17 @@ require "kredis"
|
|
3
3
|
require "cubism/version"
|
4
4
|
require "cubism/engine"
|
5
5
|
require "cubism/broadcaster"
|
6
|
-
require "cubism/
|
6
|
+
require "cubism/cubicle_store"
|
7
|
+
require "cubism/preprocessor"
|
7
8
|
|
8
9
|
module Cubism
|
9
10
|
extend ActiveSupport::Autoload
|
10
11
|
|
11
12
|
autoload :Broadcaster, "cubism/broadcaster"
|
13
|
+
autoload :Preprocessor, "cubism/preprocessor"
|
12
14
|
|
13
15
|
mattr_accessor :user_class, instance_writer: false, instance_reader: false
|
14
16
|
|
15
|
-
|
17
|
+
mattr_accessor :block_store, instance_reader: false
|
18
|
+
mattr_accessor :source_store, instance_reader: false
|
16
19
|
end
|
data/lib/cubism.rb~
CHANGED
@@ -4,13 +4,15 @@ require "cubism/version"
|
|
4
4
|
require "cubism/engine"
|
5
5
|
require "cubism/broadcaster"
|
6
6
|
require "cubism/cubicle_block_store"
|
7
|
+
require "cubism/preprocessor"
|
7
8
|
|
8
9
|
module Cubism
|
9
10
|
extend ActiveSupport::Autoload
|
10
11
|
|
11
12
|
autoload :Broadcaster, "cubism/broadcaster"
|
13
|
+
autoload :Preprocessor, "cubism/preprocessor"
|
12
14
|
|
13
15
|
mattr_accessor :user_class, instance_writer: false, instance_reader: false
|
14
16
|
|
15
|
-
|
17
|
+
mattr_accessor :block_store, instance_reader: false
|
16
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cubism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Rubisch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01
|
11
|
+
date: 2022-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -135,6 +135,7 @@ files:
|
|
135
135
|
- app/channels/cubism/presence_channel.rb
|
136
136
|
- app/channels/cubism/presence_channel.rb~
|
137
137
|
- app/helpers/cubism_helper.rb
|
138
|
+
- app/helpers/cubism_helper.rb~
|
138
139
|
- app/models/concerns/cubism/presence.rb
|
139
140
|
- app/models/concerns/cubism/presence.rb~
|
140
141
|
- app/models/concerns/cubism/user.rb
|
@@ -144,9 +145,15 @@ files:
|
|
144
145
|
- lib/cubism.rb~
|
145
146
|
- lib/cubism/broadcaster.rb
|
146
147
|
- lib/cubism/broadcaster.rb~
|
147
|
-
- lib/cubism/cubicle_block_store.rb
|
148
148
|
- lib/cubism/cubicle_block_store.rb~
|
149
|
+
- lib/cubism/cubicle_source_store.rb~
|
150
|
+
- lib/cubism/cubicle_store.rb
|
151
|
+
- lib/cubism/cubicle_store.rb~
|
149
152
|
- lib/cubism/engine.rb
|
153
|
+
- lib/cubism/engine.rb~
|
154
|
+
- lib/cubism/parser.rb~
|
155
|
+
- lib/cubism/preprocessor.rb
|
156
|
+
- lib/cubism/preprocessor.rb~
|
150
157
|
- lib/cubism/version.rb
|
151
158
|
- lib/cubism/version.rb~
|
152
159
|
- lib/tasks/cubism_tasks.rake
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Cubism
|
2
|
-
class CubicleBlockStore
|
3
|
-
include Singleton
|
4
|
-
|
5
|
-
delegate_missing_to :@blocks
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@blocks = {}
|
9
|
-
end
|
10
|
-
|
11
|
-
def [](key)
|
12
|
-
@blocks[key]
|
13
|
-
end
|
14
|
-
|
15
|
-
def []=(key, value)
|
16
|
-
mutex.synchronize do
|
17
|
-
@blocks[key] = value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def clear
|
22
|
-
mutex.synchronize do
|
23
|
-
@blocks.clear
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def mutex
|
30
|
-
@mutex ||= Mutex.new
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
BlockStoreItem = Struct.new(:context, :block, keyword_init: true)
|
35
|
-
end
|