cubism 0.1.0.pre7 → 0.1.0.pre11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Twitter follow](https://img.shields.io/twitter/follow/julian_rubisch?style=social)](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