TCPServer inside Rails app

Hi there!

I have a rails app (5.2.2.1) and I want to add a feature to support a external legacy system which it’s need to connect to socket with his own protocol.

I did a reverse engineering to this protocol, and I want to use it with rails app benefits like models.

In my case I added this code to config/initializers/entrance_lathes.rb:

require 'socket'

Thread.new do
  TCPServer.open(9999) do |srv|
    loop do
      Thread.start(srv.accept) do |cli|
        line = cli.gets(chomp: true)
        id, action, args = line.split

        # ... Query with ActiveRecord to logic

        # Finishing connection
        cli.puts "See you soon #{id}!"
        cli.flush
        cli.close
      end
    end
  end
end.join

Of course, before any deployment, I wrap this code into clases.

Questions;

  • It’s a good practice add a TCPServer inside rails app?
  • There are a better way or solution to do this?
  • How to in development environment if I code change, how is to do reloaded, close, and open again tcpserver?

Best regards, JV

1 Like

I can’t help you but it does sound interesting. I assume you would need to hook either into the web server (puma or similar) and maybe make some workarounds around rack. Will the protocol be using a classic request-response system or will the TCP stream stay open? If it stays open, you might be able to hook into Action Cable.

Sometimes I wondered if rails in combination with action cable could be used as a ssh command line tui backend.

Edit: Maybe you can write an intermediate software in a separate process which allows the legacy system to connect. This intermediate layer will then talk via HTTP to the rails server. This would provide you with an easier separation of concern and might avoid you some performance troubles within the rails web process.

It depends on your deployment setup, but I would lean toward creating a separate ruby project that starts the TCP server and manages the protocol, and loads and uses Rails as necessary.

I have a ruby project that is run as a command-line program, either externally or from a request to the Rails app. Here’s some sample code to load the rails app into that external ruby program, after which the rails app models can be accessed same as in the rails app itself.

    # load rails environment so I can use ActiveRecord models for data access
    if defined?(Rails)
      puts("Rails is already loaded")
    else
      puts("Rails not previously loaded...attempting to load from #{@config[:rails_dir]}...")
      ENV['RAILS_ENV'] = @args[:environment]
      Dir.chdir(@config[:rails_dir]) do
        require "#{@config[:rails_dir]}/config/environment.rb"
      end
    end
    exit 5 unless defined?(Rails)

Since this program is a “run and done” I don’t need to worry about reloading the Rails app in development mode. However, you could potentially check your environment and just call Rails.application.reloader.reload! in same way that makes sense for your custom protocol.

After reading several tutorials it seems that the best way is to make a task and execute it from the command line or with systemd.