-
Notifications
You must be signed in to change notification settings - Fork 284
Using the DataStore
The DataStore provides a convenient way to temporarily persist information between execution of events. Since every event is executed within a new instance of the controller class, instance variables set while processing an action will be lost after the action finishes executing.
There are two different DataStore classes that you can use:
The DataStore::Controller
class is unique for every controller. You can use it similar to how you would use instance variables within a plain ruby class. The values set within the controller store will be persisted between events. The controller store can be accessed within your controller using the #controller_store
method.
The DataStore::Connection
class is unique for every active connection. You can use it similar to the Rails session store. The connection data store can be accessed within your controller using the #connection_store
method.
Unlike the connection data store which behaves very much like the Rails
session store, the Controller DataStore is slightly unique. The basic
premise is to act as a stand-in for instance variables in your
controller. At it's core, it is a Hash which is accessible inside your
controller through the #controller_store
instance method. Any values
set in the controller store will be visible by all connected users which
trigger events that use that controller. However, values set in one
controller will not be visible by other controllers.
Lets take a look at a very contrived use case. Here we are going to keep a counter to track the number of events that have been triggered within a particular controller.
class AccountController < WebsocketRails::BaseController
# Set the initial event count value to 0
def initialize_session
controller_store[:event_count] = 0
end
# Mapped as `accounts.important_event` in the Event Router
def important_event
# This will be private for each controller
controller_store[:event_count] += 1
trigger_success controller_store[:event_count]
end
end
class ProductController < WebsocketRails::BaseController
# Set the initial event count value to 0
def initialize_session
controller_store[:event_count] = 0
end
# Mapped as `products.boring_event` in the Event Router
def boring_event
controller_store[:event_count] += 1
trigger_success controller_store[:event_count]
end
end
Lets take a look at the responses received when a user begins to trigger these events.
In the interest of brevity, I am going to leave out the JavaScript to trigger the events and handle the response.
# trigger `accounts.important_event`
=> 1
# trigger `accounts.important_event`
=> 2
# trigger `products.boring_event`
=> 1
As you can see, incrementing the :event_count
key in the
controller store for AccountController
had no effect on the
ProductController
event count.
Now if another user decides to trigger the accounts.important_event
immediately after the first user triggered the events above, they would
increment the count from 2 to 3 since the controller store is shared
amongst all connected users.
If you wish to store private data for a user, you can use the connection data store which is explained further below.
Sometimes it is useful to be able to collect information from all of
your data stores. For this we have the #collect_all
method. Like it's
name suggests, this method will collect all of the values for a given
key from every currently initialized data store and return them as an
array or yield them to a block if one is supplied.
Continuing the example from above, we could use #collect_all
to
retrieve a total event count across both controllers.
event_counts = controller_store.collect_all(:event_count)
=> [3, 1]
event_total = controller_store.collect_all(:event_count).reduce(:+)
=> 4
Assuming User B had actually triggered the important_event
a third
time, we now have the counts from AccountsController and
ProductController wrapped up and returned to us inside of an array.
If we pass #collect_all
a block, each value will be yielded.
controller_store.collect_all(:event_count) { |count| puts count }
=> 3
=> 1
One small caveat to the
#collect_all
method arises when load balancing between multiple instances of the standalone server. Currently,#collect_all
will only return the values from the data stores that have been initialized within the instance of the standalone server that the current user is connected to. If your production environment is currently configured this way I apologize. I will have a Redis backed data store implemented soon which will solve this issue.
The connection data store operates much like the controller store. The biggest difference is that the data placed inside is private for individual users and accessible from any controller. Anything placed inside the connection data store will be deleted when a user disconnects.
The connection data store is accessed through the #connection_store
instance method inside your controller.
If we were writing a basic chat system, we could use the connection data store to hold onto a user's current screen name.
class UserController < WebsocketRails::BaseController
def set_screen_name
connection_store[:screen_name] = message[:screen_name]
end
end
class ChatController < WebsocketRails::BaseController
def say_hello
screen_name = connection_store[:screen_name]
send_message :new_message, "#{screen_name} says hello"
end
end
Like the controller data store, you can use the #collect_all
method
explained above to retrieve values from the connection stores of all
connected users.
users = connection_store.collect_all(:users)
=> [:othello, :snoop_dogg]
Of course, the same caveats to the collect all method regarding load balanced standalone servers currently apply to the connection data store as well.