Pabi
Pabi

Reputation: 994

Ruby + Sinatra: Run ruby script in background and access Variables in Sinatra

How can I run a ruby script in the background of the server while still being able to get the variables that the script uses to display the user when they load the page.

I need the server to continuously run processes in the background in an infinite while loop.

Example of background proccess:

while 0==0
  if @variable!=nil
    @variable+=1
  else
    @variable=0
  end
end

How can I have this run in the background on my server and have sinatra get the value of @variable when it receives a get request: ex:

get 'variable' do
    "This is the current value of the variable: #{@variable}"
end

Upvotes: 0

Views: 1439

Answers (2)

mwp
mwp

Reputation: 8467

You're probably best off putting the output of your server-side process in a database, file, shared memory segment, or key-value store (e.g. Redis), and accessing the current value from Sinatra when the request is made. Although theoretically possible (with a forked process or background thread that kicks off before Sinatra racks up), you probably shouldn't attempt to commingle the two things into a single process.

Responding to your comment, it seems like you are just getting started with Ruby and possibly programming in general. You should take some time to learn about databases, SQL, and migrations (e.g. ActiveRecord migrations). It would be good knowledge for a lot of practical use cases.

Since it is probably beyond your scope today to install and configure MySQL, Redis, or something equivalent, we can use a simple SQLite database (and the sqlite3 gem) to solve your problem. SQLite is similar in some ways to a relational database in that it understands SQL statements, but instead of a full-fledged service it simply uses a file that lives on disk. Our server-side process (i.e. the loop) will be responsible for creating the database, the table to hold our value(s), and for updating the value(s). Our Sinatra process will open the database and retrieve the current value(s) as needed.

Before we get into the code, I want to stress that this is a very simplistic example, and it's merely one way to solve the problem. There are a lot of other techniques at your disposal. Maybe some other SO posters will be kind enough to post other suggestions. :-)

background.rb:

require 'sqlite3'

db = SQLite3::Database.new('/tmp/test.db')

# Initialize the table and zero count if not yet present.
db.execute <<-SQL
  CREATE TABLE IF NOT EXISTS my_values (
    id INTEGER PRIMARY KEY,
    my_count INTEGER
  );

  INSERT OR IGNORE INTO my_values (id, my_count) VALUES (1, 0);
SQL

# Prepare a query to run every interval.
stmt = db.prepare <<-SQL
  UPDATE my_values
  SET my_count = my_count + 1
  WHERE id = 1;
SQL

# Main processing loop.
loop do
  stmt.execute
  sleep 10
end

sinatra.rb:

require 'sinatra'
require 'sqlite3'

configure do
  set :database, SQLite3::Database.new('/tmp/test.db', { :readonly => true })
end

get '/variable' do
  count = settings.database.get_first_value <<-SQL
    SELECT my_count FROM my_values WHERE id = 1;
  SQL

  "This is the current value of the variable: #{count}"
end

There are a lot of ways to improve the above as you continue to learn and grow as a Ruby programmer:

  1. Use an ORM such as Sequel, DataMapper, or ActiveRecord (instead of INSERT, UPDATE, and SELECT SQL statements).
  2. Use your ORM's migrations to make changes to the database schema (instead of CREATE TABLE SQL statements).
  3. Use a "real" database such as MySQL, PostgreSQL, or Firebird (instead of SQLite), or switch to using a key-value store such as Redis, Memcached, or Rocks (instead of a traditional SQL-based database).
  4. Add some "sanity checking" to background.rb to ensure that e.g. only one copy is running at a time.
  5. Improve background.rb so that it backgrounds (daemonizes) itself and writes out a log of its activities.
  6. Use a threading application server such as Puma and a connection pool for the database connections in sinatra.rb (some ORMs provide this out of the box). You'll need to learn more about Rack and create a separate "rackup" script (e.g. config.ru).

Hope this helps.

Upvotes: 3

thesecretmaster
thesecretmaster

Reputation: 1984

You could just do it in a different thread, and make the variable global to access it, like:

require 'sinatra'

Thread.new do
    loop do
        if $variable!=nil
            $variable+=1
        else
            $variable=0
        end
    end
end

get '\variable' do
    "This is the current value of the variable: #{$variable}"
end

Source: http://blog.markwatson.com/2011/11/ruby-sinatra-web-apps-with-background.html?m=1

Upvotes: 1

Related Questions