Accessing Sinatra application scope from Rake task - ruby

I have a global variable in a Sinatra application that I want to update with a scheduled task from my Rakefile. Note that the application is hosted on Heroku. I have set up helpers in order to access this variable.
get '/' do
##var
end
helpers do
def get_var
return ##var
end
def set_var(value)
##var = value
end
end
Here is the task in my Rakefile:
task :do_something do
Sinatra::Application.set_var(get_data)
end
def get_data
# Retrieve value from external source
...
return val
end
The problem I run into is that the task executes properly, but the variable in the Sinatra application is never updated. I assume this is because calling Sinatra::Application from within the Rakefile actually creates a separate instance of the application from the primary instance that I am trying to update.
I want to know if their is a way to access the scope of the running Sinatra web app from within a Rakefile task.
*Note: I could just write the value retrieved in the scheduled task to a database and then access it from the Sinatra app, but that would be overkill because this variable is updated so infrequently but retrieved so regularly that I would prefer to keep it in memory for easier access. I have looked into Memcache and Redis in order to avoid turning to a database, but again I feel that this would be excessive for a single value. Feel free to disagree with this.
EDIT: In regards to Alexey Sukhoviy's comment, Heroku does not allow writing to files outside of the tmp directories, and these are not kept alive long enough to satisfy the needs of the application.

I ended up storing the variable using Memcache. This plays well with Heroku since they provide a free add-on for it. The Dalli gem provides a simple interface with Ruby for Memcache. In my Sinatra app file, I set the following options:
require 'dalli'
set :cache, Dalli::Client.new
I then am able to recover the stored variable from the Rakefile:
task :do_something do
Sinatra::Application.settings.cache.set('var', get_data)
end
I can then access this variable again in my Sinatra controller:
settings.cache.get('var')

Related

How to set up redis for an API using Ruby and Rack?

I'm building a simple API using Grape, Ruby, and Rack (no framework). I want to use redis to store the data the API is working with. So far, I've defined a Directory class and a DirectoryAPI class using Grape. The Directory class has methods that return JSON data in the exact same way as the API.
I've never had to set up redis before, so I'm not sure how to do it. What I'm looking to accomplish is:
Do a mass insert to redis when I run rackup, so that there is pre-populated data. The pre-populated data would come from using my Directory class.
Have my GET routes return a hash stored in redis
Have the POST route for my API add an entry to redis
How do I configure redis to run and populate when I run rackup? Should I do it in my config.ru file somehow and define a new class for redis? How would I get the API class to interact with redis?
When I use redis, I do the fallowing:
In my initializers folder I create a file redis.rb and create the connection:
`Redis.new(:host => "10.0.1.1", :port => 6380, :db => 15)`
Then I assign this conn to a global variable $redis so I can have access to it everywhere.
Then it's up to you, you can organise your code however you want.
An example:
Controller A
def index
MemoryModel.getAll()
end
class MemoryModel
def getAll()
$redis.get("key")
end
end
Have a look at redis gem you can find some examples in there, also connection_pool gem

What is the correct way to setup database with DataMapper and Sinatra on production server?

From the DataMapper document, I think there are at least four functions need to be called to have database setup:
DataMapper.setup(:default, 'sqlite:///path/to/project.db')
DataMapper.finalize
DataMapper.auto_migrate!
DataMapper.auto_upgrade!
In many DataMapper+Sinatra tutorials I learned that auto_migrate! and auto_upgrade! are not supposed to be called every time the app is loaded on production server. But in the meanwhile many examples call these functions in the main ruby file of the sinatra app, say app.rb, without additional check. And some examples do not call finalize at all. So far I am confused and I am not sure what to do on the production server.
Take this following simple app.rb for example, I have some questions:
Where and when should finalize be called?
When deploying the app first time there is no db file on the production server, how do I have it automatically created? Or do I have to create the project.db file manually?
Since the auto_upgrade! is wrapped in :development block, it won't be called on production server. How am I supposed to upgrade database when I add or remove columns in it?
require 'sinatra'
require 'data_mapper'
configure do
DataMapper.setup :default, "sqlite3://#{Dir.pwd}/project.db"
end
class Book
include DataMapper::Resource
property :id, Serial
property :title, Text
belongs_to :author
end
class Author
include DataMapper::Resource
property :id, Serial
property :name, Text
has n, :books
end
configure :development do
DataMapper.auto_upgrade!
end
get '/:id' do
#author = Author.get params[:id]
erb :list_author_and_his_books # The template has nothing to do with this question, ignore it
end
get '/new' do
# Some code for user to input book or author details
end
get '/create' do
# Some code to create book or author in db
end
Thanks for reading this long post :D
Where and when should finalize be called?
From http://rdoc.info/github/datamapper/dm-core/DataMapper#finalize-class_method
This method should be called after loading all models and plugins.
When deploying the app first time there is no db file on the production server, how do I have it automatically created? Or do I have to create the project.db file manually?
It depends on your hosting arrangement, but the main thing to do is put the running of migrations in a Rake task and run them when the app is deployed. If you're using Sqlite, this would create the database (although on some hosts you are not allowed to update the file system). I don't think it's a good idea to use Sqlite for a production database, but that's your decision.
Since the auto_upgrade! is wrapped in :development block, it won't be called on production server. How am I supposed to upgrade database when I add or remove columns in it?
Use a Rake task. After each deployment you'd run the "db:migrate:up" (or whatever you'd called it) task and it would run the latest migrations. You might get a few ideas from Padrino's Rake tasks for DataMapper

Espresso Enginery

i use framwork Espresso with Enginery generator (Ruby gems). I create new project, and not understand how work this application. I will work with Espresso. Explain me struct Expresso Application, please.
I can run this apllication: rackup config.ru
I can edit controllers, but i not understand depending between ruby scripts in this project.
i run projects, but why this work it?
config.ru
require File.expand_path('../base/boot', __FILE__)
puts App.urlmap
run App
in project not /base/boot directory.
requiring '../base/boot' will actually load dependencies, controllers, models etc. and build the application.
The application are stored under App constant, so you can access it from different files:
https://github.com/espresso/enginery/blob/master/app/base/base/boot.rb#L9
puts App.urlmap will display all the routes to be served by app.
And run App will start your app.
You can also start app by ruby app.rb, then you do not need to pass server/port at startup.
Instead you'll set them in config/config.yml, like this:
development:
server: Thin
port: 5252
The config.ru file looks quite normal for a rack start-up file. You would start the application from the project folder, with a command like:
rackup -p 8080
The following line:
require File.expand_path('../base/boot', __FILE__)
will load in the ../base/boot file (similar to require_relative, but also works with older Ruby e.g. 1.8.7), which I would guess requires the dependencies where App is defined. The class or module App will implement a call method. To start the server, the rack host calls App.new (which is called due to run App) and then on each request it will call .call( env ) on the resulting object (the object doesn't have to be an App object, but in simpler frameworks it will be).
The variable env contains all the details of the request and the rack environment that can be inspected to fetch details of the current path, cookies, query params etc. Typically access to this data is abstracted through Sinatra and Espresso helper methods that you will use.
The Sinatra and Espresso helper methods look like they are doing magic declarations, but they are just normal methods. Usually they do some calculation and then stash a code block/lambda for rack to call later. Sinatra's get is like this . . . it isn't true declarative code. Instead, when the controller is parsed, it just takes the code block and tells the application object to call it (later) when the path matches.

Is there a Rack or Sinatra based environment configuration utility?

Is there anything in the Sinatra / Rack world similar to Rails configuration loading scheme that loads one of the config\enviroments\*.rb files depending on Rails.env
I know I could develop one pretty easily, i was just wondering if there was something already in place.
If you're following the Rails convention of putting a file for each environment in config/environments/environment_name.rb, you can put something like this in your Sinatra app, or for Rack in your config.ru file:
Dir.glob(File.dirname(__FILE__) + "/config/environments/#{settings.environment}.rb", &method(:require))
With some minor modifications you could make it load other file locations/combinations. Sinatra's configure blocks work just as well, too.
It turns out that there is something from Sinatra, that provides a similar, though limited, functionality.
See the code:
https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1120
So that you can do this:
class MyApp < Sinatra::Base
configure :development, :test do
#only executes this code when environment is equal to one of the passed arguments
# I'm pretty sure Sinatra sets this based on ENV['RACK_ENV']
end
end
There is one called Sinatra::ConfigFile, which now lives in Sinatra::Contrib http://www.sinatrarb.com/contrib/config_file.html
There's lots of useful stuff in there.
I adapted mine from monkrb.com (it's also yaml in RoR anyways)
YAML.load_file(path_of "config/settings.yml")[RACK_ENV]
e.g.
http://github.com/codepants/yasumi/blob/master/config/settings.yml

Stubbing Sinatra helper in Cucumber

I am currently struggling with stubbing out a helper method of my Sinatra app from within Cucumber.
I have a Sinatra app with simple session authentication (by cookies) and I want to turn of authentication by stubbing out the logged_in? helper method for my Cucumber scenarios.
There seems to be a problem with Sinatra and Cucumber concerning sessions so I thought about just using Mocha to work around the problem.
However I don't know how I can access the Sinatra::Application instance from within a Given-Block to stub out the method.
It seems like I need to directly override my Authentication mechanism within a Before do ... end-block
So I ended up with a hooks.rb placed in features/support/ file overwriting my logged_in? and the current_user method.
Before do
MySinatraApplicationClass.class_eval do
helpers do
def logged_in?
return true
end
def current_user
# This returns a certain Username usually stored
# in the session, returning it like
# that prohibits different user logins, but for
# now this is enough for me
"Walter"
end
end
end
end
The only thing I had to take care of, is that the no other actions within the application directly read from session but rather use those helpers.
Sadly I think this way of handling session based Sinatra applications through Cucumber is already described somewhere else and I just thought my problem was different.
You can get the right context by using Sinatra::Application.class_eval
Edit: See original poster's answer for full explanation.

Resources