Reputation:
I'm new to Sinatra and programming for the web, so some of the terminology used may not be quite right. Anyway...
I have an app that reads a .txt line by line into an array and then when you load the index.html.erb it randomly displays one of the lines. I put the contents in a text file instead of directly into an array so that if I need to add more data it's easier to update then adding to the array directly and redeploying the app. My concern is if it's recreating the array and rereading the file every time you load the page. I don't know exactly how that kind of thing works server side, or how to check it. The code that creates the array reads:
before do
@ways ||= ['']
if @ways[1].nil?
File.open('ways.txt', 'r').each_line { |line| @ways << line }
end
end
And then my route:
get '/' do
@way = @ways.sample
erb :index
end
Is there a way to make sure this is as efficient as possible? Or should it be done some other way entirely? According to the Chrome Dev tools, it transfers ~ 800b per page load.
Upvotes: 1
Views: 1035
Reputation: 303271
What you have written here will indeed read the file into an array on each request.
Tip: you can make it shorter and faster with @ways = File.readlines('ways.txt')
If you wanted to cache this array, you could do so as a constant at app startup, e.g.:
WAYS = File.readlines('ways.txt').map(&:chomp)
get "/" do
@way = WAYS.sample
erb :index
end
However, with this you would need to quit and restart your server if you edited the text file. If you wanted to avoid that, you could check the modification time of the file in your before
action and only update the array (replace
it) if the contents had changed.
If you need help with this last suggestion, let me know and I can edit to suit.
Edit: Here's one way to use a constant to save the data and reload only when the file changes:
WAYS = { file:'ways.txt', all:[] }
before do
if WAYS[:updated] != (mtime=File.mtime(WAYS[:file]))
WAYS[:all].replace File.readlines(WAYS[:file]).map(&:chomp)
WAYS[:updated] = mtime
end
end
get "/" do
@way = WAYS[:all].sample
erb :index
end
This is a little bit aggressive—checking the file modification time on every request—but the performance should be fine for all but the heaviest load or slowest disk.
An alternative solution would be to kick off a thread to force-check/update the array every few minutes, e.g.
require 'sinatra'
WAYS = { file:'ways.txt', all:[] }
Thread.new do
loop do
if WAYS[:updated] != (mtime= File.mtime(WAYS[:file]))
WAYS[:all].replace File.readlines(WAYS[:file]).map(&:chomp)
WAYS[:updated] = mtime
end
sleep 5 # seconds
end
end
get '/' do
WAYS[:all].sample
end
Upvotes: 3