cubism 0.1.0.pre7 → 0.1.0.pre11
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 +12 -2
- data/app/channels/cubism/presence_channel.rb~ +1 -1
- data/app/helpers/cubism_helper.rb +20 -5
- data/app/helpers/cubism_helper.rb~ +19 -6
- data/app/models/concerns/cubism/presence.rb +7 -0
- data/app/models/concerns/cubism/presence.rb~ +12 -3
- data/lib/cubism/broadcaster.rb +7 -12
- data/lib/cubism/broadcaster.rb~ +7 -14
- data/lib/cubism/cubicle_block_store.rb~ +56 -0
- data/{app/models/cubism/current.rb~ → lib/cubism/cubicle_source_store.rb~} +0 -0
- data/lib/cubism/cubicle_store.rb +136 -0
- data/lib/cubism/cubicle_store.rb~ +136 -0
- data/lib/cubism/engine.rb +4 -0
- data/lib/cubism/engine.rb~ +3 -1
- 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~ +10 -0
- metadata +60 -20
- data/app/channels/cubism/presence.rb~ +0 -18
- data/app/models/concerns/cubism/base.rb~ +0 -3
- data/app/models/concerns/cubism/user.rb~ +0 -11
- data/app/models/cubism/base.rb +0 -14
- data/app/models/cubism/base.rb~ +0 -13
- data/lib/cubism/cubicle_block_store.rb +0 -27
- data/lib/cubism/railtie.rb~ +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf5d9eed6d97039688a2f51dd5ddab248619879ad47d50abbf37bab73d7f7d56
|
4
|
+
data.tar.gz: 8120474f22abf5ee4b9bc2497e91ef2e68f41aea79f59fde4ae1c9e577a75dd7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe7a0bca6d22807e0eb6b81e612ee8d435f67380a5aec20df253a86a92606642f907a67341b845159b2ae498dfcc0d7c56c1f49fb3425e1470789140adf4939e
|
7
|
+
data.tar.gz: dd6925f36a82fcfab2de5a888b1d974b151fb9a14a7c4cf76cab9f3ffde28b865790c09dd4b97b19a8284192cef644847a3c944b06e53dd2d690ef8da2a7245e
|
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,17 @@ CableReady.initialize({ consumer });
|
|
91
96
|
|
92
97
|
The `cubicle_for` helper accepts the following options as keyword arguments:
|
93
98
|
|
94
|
-
- `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.
|
99
|
+
- `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`).
|
95
100
|
- `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.
|
96
101
|
- `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.
|
97
102
|
- `trigger_root`: a CSS selector to attach the appear/disappear events to. Defaults to the `cubicle-element` itself.
|
98
103
|
- `html_options` are passed to the TagBuilder.
|
99
104
|
|
105
|
+
## Limitations
|
106
|
+
|
107
|
+
### Supported Template Handlers
|
108
|
+
- ERB
|
109
|
+
|
100
110
|
## Gotchas
|
101
111
|
|
102
112
|
### 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
|
-
|
6
|
+
stream_from params[: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
|
@@ -2,17 +2,32 @@ module CubismHelper
|
|
2
2
|
include CableReady::StreamIdentifier
|
3
3
|
|
4
4
|
def cubicle_for(resource, user, html_options: {}, appear_trigger: :connect, disappear_trigger: nil, trigger_root: nil, exclude_current_user: true, &block)
|
5
|
-
|
6
|
-
|
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
|
+
)
|
19
|
+
|
20
|
+
digested_block_key = block_container.digest
|
21
|
+
|
22
|
+
Cubism.block_store.fetch(digested_block_key, block_container)
|
7
23
|
|
8
|
-
Cubism.store[digested_id] = Cubism::BlockStoreItem.new(context: self, block: block.dup)
|
9
24
|
tag.cubicle_element(
|
10
|
-
identifier: signed_stream_identifier(
|
25
|
+
identifier: signed_stream_identifier(resource_gid),
|
11
26
|
user: user.to_sgid.to_s,
|
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
|
)
|
@@ -2,17 +2,30 @@ module CubismHelper
|
|
2
2
|
include CableReady::StreamIdentifier
|
3
3
|
|
4
4
|
def cubicle_for(resource, user, html_options: {}, appear_trigger: :connect, disappear_trigger: nil, trigger_root: nil, exclude_current_user: true, &block)
|
5
|
-
|
6
|
-
|
5
|
+
block_location = block.source_location.join(":")
|
6
|
+
|
7
|
+
resource_gid = resource.to_gid.to_s
|
8
|
+
|
9
|
+
store_item = Cubism::BlockStoreItem.new(
|
10
|
+
block_location: block_location,
|
11
|
+
resource_gid: resource_gid,
|
12
|
+
user_gid: user.to_gid.to_s,
|
13
|
+
view_context: self
|
14
|
+
)
|
15
|
+
|
16
|
+
digested_block_key = store_item.digest
|
17
|
+
|
18
|
+
store_item.parse!
|
19
|
+
|
20
|
+
Cubism.block_store[digested_block_key] = store_item
|
7
21
|
|
8
|
-
Cubism.store[digested_id] = Cubism::BlockStoreItem.new(context: self, block: block.dup)
|
9
22
|
tag.cubicle_element(
|
10
|
-
identifier: signed_stream_identifier(
|
23
|
+
identifier: signed_stream_identifier(resource_gid),
|
11
24
|
user: user.to_sgid.to_s,
|
12
|
-
"appear-trigger": appear_trigger,
|
25
|
+
"appear-trigger": Array(appear_trigger).join(","),
|
13
26
|
"disappear-trigger": disappear_trigger,
|
14
27
|
"trigger-root": trigger_root,
|
15
|
-
id: "cubicle-#{
|
28
|
+
id: "cubicle-#{digested_block_key}",
|
16
29
|
"exclude-current-user": exclude_current_user,
|
17
30
|
**html_options
|
18
31
|
)
|
@@ -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
|
@@ -2,10 +2,19 @@ module Cubism::Presence
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
kredis_set :present_users, after_change: :
|
5
|
+
kredis_set :present_users, after_change: :stream_presence
|
6
|
+
kredis_set :cubicle_element_ids
|
7
|
+
kredis_hash :excluded_user_id_for_element_id
|
6
8
|
end
|
7
9
|
|
8
|
-
def
|
9
|
-
Cubism::
|
10
|
+
def stream_presence
|
11
|
+
Cubism::Broadcaster.new(resource: self).broadcast
|
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
|
10
19
|
end
|
11
20
|
end
|
data/lib/cubism/broadcaster.rb
CHANGED
@@ -14,9 +14,13 @@ module Cubism
|
|
14
14
|
def broadcast
|
15
15
|
resource.cubicle_element_ids.to_a.each do |element_id|
|
16
16
|
/cubicle-(?<block_key>.+)/ =~ element_id
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
block_container = Cubism.block_store[block_key]
|
18
|
+
|
19
|
+
next if block_container.blank?
|
20
|
+
|
21
|
+
block_source = block_container.block_source
|
22
|
+
|
23
|
+
html = ApplicationController.render(inline: block_source.source, locals: {"#{block_source.variable_name}": resource.present_users_for_element_id(element_id)})
|
20
24
|
|
21
25
|
cable_ready[element_id].inner_html(
|
22
26
|
selector: "cubicle-element##{element_id}[identifier='#{signed_stream_identifier(resource.to_global_id.to_s)}']",
|
@@ -24,14 +28,5 @@ module Cubism
|
|
24
28
|
).broadcast
|
25
29
|
end
|
26
30
|
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
|
-
|
34
|
-
users
|
35
|
-
end
|
36
31
|
end
|
37
32
|
end
|
data/lib/cubism/broadcaster.rb~
CHANGED
@@ -14,24 +14,17 @@ module Cubism
|
|
14
14
|
def broadcast
|
15
15
|
resource.cubicle_element_ids.to_a.each do |element_id|
|
16
16
|
/cubicle-(?<block_key>.+)/ =~ element_id
|
17
|
-
|
18
|
-
view_context = Cubism.store[block_key].context
|
19
|
-
html = view_context.capture(users_for(resource, element_id), &block)
|
17
|
+
store_item = Cubism.block_store[block_key]
|
20
18
|
|
21
|
-
|
19
|
+
next if store_item.blank?
|
20
|
+
|
21
|
+
html = ApplicationController.render(inline: store_item.block_source, locals: {"#{store_item.block_variable_name}": resource.present_users_for_element_id(element_id)})
|
22
|
+
|
23
|
+
cable_ready[element_id].inner_html(
|
22
24
|
selector: "cubicle-element##{element_id}[identifier='#{signed_stream_identifier(resource.to_global_id.to_s)}']",
|
23
25
|
html: html
|
24
|
-
).
|
26
|
+
).broadcast
|
25
27
|
end
|
26
28
|
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
|
-
|
34
|
-
users
|
35
|
-
end
|
36
29
|
end
|
37
30
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Cubism
|
2
|
+
class CubicleBlockStore
|
3
|
+
delegate_missing_to :@blocks
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@blocks = Kredis.hash "cubism-blocks"
|
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 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
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def mutex
|
33
|
+
@mutex ||= Mutex.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
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
|
56
|
+
end
|
File without changes
|
@@ -0,0 +1,136 @@
|
|
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
|
+
keyword_init: true
|
53
|
+
) do
|
54
|
+
def initialize(*args)
|
55
|
+
super
|
56
|
+
|
57
|
+
@filename, _lineno = block_location.split(":")
|
58
|
+
end
|
59
|
+
|
60
|
+
def user
|
61
|
+
GlobalID::Locator.locate self[:user_gid]
|
62
|
+
end
|
63
|
+
|
64
|
+
def resource
|
65
|
+
GlobalID::Locator.locate self[:resource_gid]
|
66
|
+
end
|
67
|
+
|
68
|
+
def digest
|
69
|
+
resource_user_key = [resource_gid, user_gid].join(":")
|
70
|
+
|
71
|
+
ActiveSupport::Digest.hexdigest("#{block_location}:#{File.read(@filename)}:#{resource_user_key}")
|
72
|
+
end
|
73
|
+
|
74
|
+
def marshal_dump
|
75
|
+
to_h.merge(block_source: block_source.digest)
|
76
|
+
end
|
77
|
+
|
78
|
+
def marshal_load(serialized_item)
|
79
|
+
members.excluding(:block_source).each do |arg|
|
80
|
+
send("#{arg}=", serialized_item[arg])
|
81
|
+
end
|
82
|
+
|
83
|
+
self.block_source = Cubism.source_store[serialized_item[:block_source]]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Container for cubicle block sources
|
88
|
+
BlockSource = Struct.new(
|
89
|
+
:location,
|
90
|
+
:source,
|
91
|
+
:variable_name,
|
92
|
+
:view_context,
|
93
|
+
keyword_init: true
|
94
|
+
) do
|
95
|
+
def self.find_or_create(location:, view_context:)
|
96
|
+
instance = new(location: location, view_context: view_context)
|
97
|
+
|
98
|
+
Cubism.source_store.fetch(instance.digest, instance) do |instance|
|
99
|
+
instance.parse!
|
100
|
+
end
|
101
|
+
|
102
|
+
instance
|
103
|
+
end
|
104
|
+
|
105
|
+
def initialize(*args)
|
106
|
+
super
|
107
|
+
|
108
|
+
@filename, @lineno = location.split(":")
|
109
|
+
@lineno = @lineno.to_i
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse!
|
113
|
+
return if location.start_with?("inline template")
|
114
|
+
|
115
|
+
lines = File.readlines(@filename)[@lineno - 1..]
|
116
|
+
|
117
|
+
preprocessor = Cubism::Preprocessor.new(source: lines.join.squish, view_context: view_context)
|
118
|
+
self.variable_name = preprocessor.block_variable_name
|
119
|
+
self.source = preprocessor.process
|
120
|
+
end
|
121
|
+
|
122
|
+
def digest
|
123
|
+
ActiveSupport::Digest.hexdigest("#{location}:#{File.read(@filename)}")
|
124
|
+
end
|
125
|
+
|
126
|
+
def marshal_dump
|
127
|
+
to_h.except(:view_context)
|
128
|
+
end
|
129
|
+
|
130
|
+
def marshal_load(serialized_item)
|
131
|
+
members.excluding(:view_context).each do |arg|
|
132
|
+
send("#{arg}=", serialized_item[arg])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,136 @@
|
|
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
|
+
BlockStoreItem = Struct.new(
|
48
|
+
:block_location,
|
49
|
+
:block_source,
|
50
|
+
:user_gid,
|
51
|
+
:resource_gid,
|
52
|
+
keyword_init: true
|
53
|
+
) do
|
54
|
+
def initialize(*args)
|
55
|
+
super
|
56
|
+
|
57
|
+
@filename, _lineno = block_location.split(":")
|
58
|
+
end
|
59
|
+
|
60
|
+
def user
|
61
|
+
GlobalID::Locator.locate self[:user_gid]
|
62
|
+
end
|
63
|
+
|
64
|
+
def resource
|
65
|
+
GlobalID::Locator.locate self[:resource_gid]
|
66
|
+
end
|
67
|
+
|
68
|
+
def digest
|
69
|
+
resource_user_key = [resource_gid, user_gid].join(":")
|
70
|
+
|
71
|
+
ActiveSupport::Digest.hexdigest("#{block_location}:#{File.read(@filename)}:#{resource_user_key}")
|
72
|
+
end
|
73
|
+
|
74
|
+
def marshal_dump
|
75
|
+
to_h.reverse_merge(block_source: block_source.digest)
|
76
|
+
end
|
77
|
+
|
78
|
+
def marshal_load(serialized_item)
|
79
|
+
members.excluding(:block_source).each do |arg|
|
80
|
+
send("#{arg}=", serialized_item[arg])
|
81
|
+
end
|
82
|
+
|
83
|
+
self.block_source = Cubism.source_store(serialized_item[:block_source])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Container for cubicle block sources
|
88
|
+
BlockSource = Struct.new(
|
89
|
+
:location,
|
90
|
+
:source,
|
91
|
+
:variable_name,
|
92
|
+
:view_context,
|
93
|
+
keyword_init: true
|
94
|
+
) do
|
95
|
+
def self.find_or_create(location:, view_context:)
|
96
|
+
instance = new(location: location, view_context: view_context)
|
97
|
+
|
98
|
+
Cubism.source_store.fetch(instance.digest, instance) do |instance|
|
99
|
+
instance.parse!
|
100
|
+
end
|
101
|
+
|
102
|
+
instance
|
103
|
+
end
|
104
|
+
|
105
|
+
def initialize(*args)
|
106
|
+
super
|
107
|
+
|
108
|
+
@filename, @lineno = location.split(":")
|
109
|
+
@lineno = @lineno.to_i
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse!
|
113
|
+
return if location.start_with?("inline template")
|
114
|
+
|
115
|
+
lines = File.readlines(@filename)[@lineno - 1..]
|
116
|
+
|
117
|
+
preprocessor = Cubism::Preprocessor.new(source: lines.join.squish, view_context: view_context)
|
118
|
+
self.variable_name = preprocessor.block_variable_name
|
119
|
+
self.source = preprocessor.process
|
120
|
+
end
|
121
|
+
|
122
|
+
def digest
|
123
|
+
ActiveSupport::Digest.hexdigest("#{location}:#{File.read(@filename)}")
|
124
|
+
end
|
125
|
+
|
126
|
+
def marshal_dump
|
127
|
+
to_h.except(:view_context)
|
128
|
+
end
|
129
|
+
|
130
|
+
def marshal_load(serialized_item)
|
131
|
+
members.excluding(:view_context).each do |arg|
|
132
|
+
send("#{arg}=", serialized_item[arg])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/cubism/engine.rb
CHANGED
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
@@ -2,7 +2,17 @@ require "kredis"
|
|
2
2
|
|
3
3
|
require "cubism/version"
|
4
4
|
require "cubism/engine"
|
5
|
+
require "cubism/broadcaster"
|
6
|
+
require "cubism/cubicle_block_store"
|
7
|
+
require "cubism/preprocessor"
|
5
8
|
|
6
9
|
module Cubism
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
|
12
|
+
autoload :Broadcaster, "cubism/broadcaster"
|
13
|
+
autoload :Preprocessor, "cubism/preprocessor"
|
14
|
+
|
7
15
|
mattr_accessor :user_class, instance_writer: false, instance_reader: false
|
16
|
+
|
17
|
+
mattr_accessor :block_store, instance_reader: false
|
8
18
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
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.pre11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Rubisch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: kredis
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 5.0.0.pre8
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: standard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,49 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mocha
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: appraisal
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
72
114
|
requirements:
|
73
115
|
- - ">="
|
@@ -90,30 +132,28 @@ files:
|
|
90
132
|
- MIT-LICENSE
|
91
133
|
- README.md
|
92
134
|
- Rakefile
|
93
|
-
- app/channels/cubism/presence.rb~
|
94
135
|
- app/channels/cubism/presence_channel.rb
|
95
136
|
- app/channels/cubism/presence_channel.rb~
|
96
137
|
- app/helpers/cubism_helper.rb
|
97
138
|
- app/helpers/cubism_helper.rb~
|
98
|
-
- app/models/concerns/cubism/base.rb~
|
99
139
|
- app/models/concerns/cubism/presence.rb
|
100
140
|
- app/models/concerns/cubism/presence.rb~
|
101
141
|
- app/models/concerns/cubism/user.rb
|
102
|
-
- app/models/concerns/cubism/user.rb~
|
103
|
-
- app/models/cubism/base.rb
|
104
|
-
- app/models/cubism/base.rb~
|
105
142
|
- app/models/cubism/current.rb
|
106
|
-
- app/models/cubism/current.rb~
|
107
143
|
- config/routes.rb
|
108
144
|
- lib/cubism.rb
|
109
145
|
- lib/cubism.rb~
|
110
146
|
- lib/cubism/broadcaster.rb
|
111
147
|
- lib/cubism/broadcaster.rb~
|
112
|
-
- lib/cubism/cubicle_block_store.rb
|
113
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~
|
114
152
|
- lib/cubism/engine.rb
|
115
153
|
- lib/cubism/engine.rb~
|
116
|
-
- lib/cubism/
|
154
|
+
- lib/cubism/parser.rb~
|
155
|
+
- lib/cubism/preprocessor.rb
|
156
|
+
- lib/cubism/preprocessor.rb~
|
117
157
|
- lib/cubism/version.rb
|
118
158
|
- lib/cubism/version.rb~
|
119
159
|
- lib/tasks/cubism_tasks.rake
|
@@ -123,7 +163,7 @@ licenses:
|
|
123
163
|
metadata:
|
124
164
|
homepage_uri: https://github.com/julianrubisch/cubism
|
125
165
|
source_code_uri: https://github.com/julianrubisch/cubism.git
|
126
|
-
post_install_message:
|
166
|
+
post_install_message:
|
127
167
|
rdoc_options: []
|
128
168
|
require_paths:
|
129
169
|
- lib
|
@@ -138,8 +178,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
178
|
- !ruby/object:Gem::Version
|
139
179
|
version: 1.3.1
|
140
180
|
requirements: []
|
141
|
-
rubygems_version: 3.
|
142
|
-
signing_key:
|
181
|
+
rubygems_version: 3.1.4
|
182
|
+
signing_key:
|
143
183
|
specification_version: 4
|
144
184
|
summary: Lightweight Resource-Based Presence Solution with CableReady
|
145
185
|
test_files: []
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Cubism::Presence < ActionCable::Channel::Base
|
2
|
-
def subscribed
|
3
|
-
resource = GlobalID::Locator.locate_signed params[:signed_resource]
|
4
|
-
if resource.present?
|
5
|
-
stream_for resource
|
6
|
-
resource.present_users.add(current_user.id)
|
7
|
-
else
|
8
|
-
reject
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def unsubscribed
|
13
|
-
resource = GlobalID::Locator.locate_signed params[:signed_resource]
|
14
|
-
return unless resource.present?
|
15
|
-
|
16
|
-
resource.present_users.remove(current_user.id)
|
17
|
-
end
|
18
|
-
end
|
data/app/models/cubism/base.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
class Cubism::Base
|
2
|
-
include ActiveModel::Model
|
3
|
-
include Kredis::Attributes
|
4
|
-
include Cubism::Presence
|
5
|
-
include GlobalID::Identification
|
6
|
-
|
7
|
-
def self.find(id)
|
8
|
-
new if id == "cubism-#{self.class.name.underscore}"
|
9
|
-
end
|
10
|
-
|
11
|
-
def id
|
12
|
-
"cubism-#{self.class.name.underscore}"
|
13
|
-
end
|
14
|
-
end
|
data/app/models/cubism/base.rb~
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
class Cubism::Base
|
2
|
-
include ActiveModel::Model
|
3
|
-
include Cubism::Presence
|
4
|
-
include GlobalID::Identifiable
|
5
|
-
|
6
|
-
def self.find(id)
|
7
|
-
new if id == "cubism-#{self.class.name.underscore}"
|
8
|
-
end
|
9
|
-
|
10
|
-
def id
|
11
|
-
"cubism-#{self.class.name.underscore}"
|
12
|
-
end
|
13
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Cubism
|
2
|
-
class CubicleBlockStore
|
3
|
-
include Singleton
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@blocks = {}
|
7
|
-
end
|
8
|
-
|
9
|
-
def [](key)
|
10
|
-
@blocks[key]
|
11
|
-
end
|
12
|
-
|
13
|
-
def []=(key, value)
|
14
|
-
mutex.synchronize do
|
15
|
-
@blocks[key] = value
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def mutex
|
22
|
-
@mutex ||= Mutex.new
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
BlockStoreItem = Struct.new(:context, :block, keyword_init: true)
|
27
|
-
end
|
data/lib/cubism/railtie.rb~
DELETED