Articles tagged with Rails

  1. Rails
    03 Jul 2016
    1. 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.

  2. Rails
    15 Feb 2016
    1. 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.

    2. 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.

    3. 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 through current_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
  3. Rails
    15 May 2012
    1. 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.

    2. 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.

    3. Prerequisities

      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

    4. 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"
    5. 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.