Articles tagged with Rails
-
03 Jul 2016
-
I just updated our Video Streaming App to Rails 5 and am very pleased with it.
The upgrade path had just a minor bump with some failing test cases due to an incompatibility with devise 4.1.1 and therefore switched to 4.2.0 which was released 4 hours ago.Action Cable now finally works like a charm, no need to manage the AR Connections manually anymore and also the bug with deadlocks when using multiple channels is gone.
Thinking back to the early days of Action Cable, it has come a long way and gives you an easy option to harness the power of websockets from within your Rails App.
Some years ago I was quite intrigued by the websocket support of Torquebox 3 and on paper it all seemed awesome. You get a nice abstraction layer using the STOMP protocol with Stilts and perfect integration with a Bridge for JMS with your backend messaging system HornetQ all out of the box on a JBoss AS. You would have everything nicely bundled in your rails monolith and just deloy it in Torquebox and you are done, compared to the alternatives at the time where you would deploy your rails app to Phusion Passenger and have to setup something seperate for websockets. In practice it was kind of a letdown, for example one issue I encountered was that it wasn’t really reliable, websockets would just close down and stuff like that.
Now with Action Cable it just works™. In the mentioned application we have a chat, updates of the current viewer count of a stream, the list of logged in users that watch it, and status changes all funneled through Action Cable backed by Redis with no hickups whatsoever.
-
-
15 Feb 2016
-
After upgrading an app to Rails 5.0.0.beta2 I started playing with Action Cable.
In this post I want to show how to do authorization in Cable Channels.
Whether you use CanCanCan, Pundit or whatever, first off you will have to authenticate the user, after that you can do your permission checks.How to do authentication is shown in the Action Cable Examples. Basically you are supposed to fetch the
user_id
from the cookie. The example shows how to check if the user is signed in and if not reject the websocket connection.
If you need more granular checks, keep reading. -
To understand the following code you should first familiarize yourself with the basics of Action Cable, the Readme is a good start.
The goal here is to identify logged in users and do permission checks per message. One could also check permissions during initiation of the connection or the subscription of a channel, the most granular option is to verify permissions for each message. This can be beneficial if multiple types of messages or messages regarding different resources which require distinct permissions are delivered from the same queue.
Also imagine permissions change while a channel is subscribed, you would propably want to stop sending messages immediately if a user gets the permission to receive them revoked. -
In the ApplicationCable we define methods to get the user from the session and Cancancan’s
Ability
through which we can check permissions.module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end def session cookies.encrypted[Rails.application.config.session_options[:key]] end def ability @ability ||= Ability.new(current_user) end protected def find_verified_user User.find_by(id: session["user_id"]) end end end
We give
Channel
access to the session and the ability object. The current user is already accessable throughcurrent_user
.module ApplicationCable class Channel < ActionCable::Channel::Base delegate :session, :ability, to: :connection # dont allow the clients to call those methods protected :session, :ability end end
So far we setup everything we need to verify permissions in our own channels.
So now we can use the ability object to deny subscription in general, or in this case to filter which messages are sent.Notice: Currently using ActiveRecord from inside a stream callback depletes the connection pool. I reported this issue under #23778: ActionCable can deplete AR’s connection pool. Therefore we have to ensure the connection is checked back into the pool ourselfs.
class StreamUpdatesChannel < ApplicationCable::Channel def subscribed queue = "stream_updates:#{params[:stream_id]}" stream_from queue, -> (message) do ActiveRecord::Base.connection_pool.with_connection do if ability.can? :show, Stream.find(params[:stream_id]) transmit ActiveSupport::JSON.decode(message), via: queue end end end end end
-
-
15 May 2012
-
TorqueBox allows running distributed transactions over multiple databases and messaging queues. But what to do, if you also want to operate on the filesystem?
This can be done with XADisk.This post outlines the necessary steps to setup XADisk.
It will enable you to work on your filesystem and database in distributed transactions from within your Rails application. -
Notice
At the time of writing, this does not work out of the box.
There is an issue in IronJacamar which prevents deployment of XADisk as a resource adapter.Also the XADisk 1.2 Adapter does not comply with the JCA Spec.
Both issues are resolved and should be fixed in the next releases.To work around this, I had to modify the adapter. You can get it here.
-
Prerequisities
- TorqueBox 2.x installation. See TorqueBox Installation
- “TorqueBox ready” Rails application. See Preparing your Rails application
- XA capable database.
Verify that TorqueBox and your application work by executing the following tasks from your rails app.
rake torquebox:check rake torquebox:deploy rake torquebox:run
Your application should now be available at localhost:8080.
To enable full distributed transaction support in PostgreSQL, you’ll need to set max_prepared_transactions to something greater than zero in postgresql.conf, which is the usual default in most installations.
http://torquebox.org/documentation/2.0.1/transactions.html#d0e5250 -
Deploying the XADisk Resource Adapter
To deploy it, put XADisk.rar in torquebox/jboss/standalone/deployments.
Next the resource adapter needs to be configured.
Therefore open torquebox/jboss/configuration/standalone.xml.
Modify the resource adapters subsystem:<subsystem xmlns="urn:jboss:domain:resource-adapters:1.0"> <resource-adapters> <resource-adapter> <archive> XADisk.rar </archive> <transaction-support>XATransaction</transaction-support> <config-property name="xaDiskHome"> /opt/xadisk/xadisk1 </config-property> <config-property name="instanceId"> xadisk1 </config-property> <connection-definitions> <connection-definition class-name="org.xadisk.connector.outbound.XADiskManagedConnectionFactory" jndi-name="java:global/xadisk1" pool-name="XADiskConnectionFactoryPool"> <config-property name="instanceId"> xadisk1 </config-property> <xa-pool> <min-pool-size>1</min-pool-size> <max-pool-size>5</max-pool-size> </xa-pool> </connection-definition> </connection-definitions> </resource-adapter> </resource-adapters> </subsystem>
Make sure to modify the property “xaDiskHome” according to your system.
This is the working directory of the XADisk instance where it stores its transaction logs among other stuff. The instanceId is the name for this instance.
Also the JNDI name of the connection factory must be configured.Start TorqueBox. XADisk should now be successfully deployed.
The logs should state something similar to this:
01:30:55,552 INFO [org.jboss.as.server.deployment] (MSC service thread 1-5) JBAS015876: Starting deployment of "XADisk.rar" 01:30:56,090 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-4) JBAS010406: Registered connection factory java:global/xadisk1 01:30:56,252 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-1) JBAS010401: Bound JCA ConnectionFactory [java:global/xadisk1] 01:30:58,772 INFO [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018559: Deployed "XADisk.rar"
-
Using the adapter in a Rails app
To use the resource adapter the connection factory has to be looked up via JNDI.
The JavaDoc of XADisk lists the supported operations.# wrap database and filesystem in one transaction TorqueBox.transaction do # lookup the xadisk connection factory factory = TorqueBox::Naming.context.lookup("java:global/xadisk1") # acquire a connection conn = factory.connection begin a = Article.create! :title => "test" file = java.io.File.new "/opt/filestore/#{a.id}" conn.create_file file ensure conn.close end end
More about distributed transactions in TorqueBox can be found here.
-