Time.zone works on API but not on workers - ruby

I'm building an API using Grape framework and I want to set default timezone for the whole app as UTC so that when I call Time.zone.now I get the proper time
My config/application.rb looks like:
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'api'))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'app'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'bundler/setup'
Bundler.require :default, ENV['RACK_ENV']
Time.zone = 'UTC'
require_rel '../app'
require_rel '../api'
require_rel '../lib'
require_rel 'initializers'
If I start it up through bundle exec rackup -p 3000 or bin/console and call Time.zone.now I get the proper time: Fri, 05 Apr 2019 00:47:23 UTC +00:00
Although, when I'm using a Sidekiq worker or something outside Rack itself (bundle exec sidekiq -r ./config/application.rb) and try to call p Time.zone, nil is returned and Time.current returns time with my timezone: 2019-04-04 21:48:57 -0300
, even though I'm requiring application.rb which contains Time.zone = 'UTC' statement
How do I set UTC timezone globally for my workers as well?
EDIT #1 - Worker Code
It's a simple worker: it just prints Time.now or Time.zone.now
module Cron
class Date
include Sidekiq::Worker
JOB_NAME = 'date_job'
def perform(start_date = 1.day.ago, end_date = Time.current)
p [Time.now, Time.current]
end
end
end

You can just introduce an ApplicationWorker and have all other workers inherit from that class. In this new parent class, you can do everything that all workers have in common.
When you have two workers like this
class FooWorker
include Sidekiq::Worker
def perform
# do something
end
end
class BarWorker
include Sidekiq::Worker
def perform
# do something else
end
end
then you can use inheritance like this:
class ApplicationWorker
include Sidekiq::Worker
Time.zone = 'UTC'
end
class FooWorker < ApplicationWorker
def perform
# do something
end
end
class BarWorker < ApplicationWorker
def perform
# do something else
end
end

Related

How can I profile `require` statements in Ruby?

I'm interested in seeing how long (in seconds) each require statement in my Ruby code takes to load. How can I temporarily override that method and print out some logging information for slow loads?
You can override Kernel.require like this:
module Kernel
# make an alias of the original require
alias_method :original_require, :require
# rewrite require
def require name
start = Time.now
# code to time
ret = original_require name
finish = Time.now
diff = finish - start
# TODO: This should really add up all of
# the require calls for a certain module
if diff > 0.1
puts "#{diff} #{name}"
end
ret
end
end

How to test if some specific rack middleware is being used?

To be more particular, I'm talking about sentry-raven and sinatra here. I saw examples testing sinatra applications, or middlewares. But I didn't see ones testing if some particular middleware is present. Or should I be testing behavior, not configuration (or how should I call it)?
The important thing (I'd say) is the behaviour, but if you wish to check for middleware there are 2 ways I'd suggest after taking a delve into the Sinatra source (there are possibly much easier/better ways):
The env
In the Sinatra source there's a method that uses the env to check if a middleware is already present:
# Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
# if another CommonLogger is already in the middleware chain.
class CommonLogger < Rack::CommonLogger
def call(env)
env['sinatra.commonlogger'] ? #app.call(env) : super
end
You could do the same thing in a route, e.g.
get "/env-keys" do
env.keys.inspect
end
It'll only show you the middleware if it's inserted something in env hash, e.g.
class MyBad
def initialize app, options={}
#app = app
#options = options
end
def call env
#app.call env.merge("mybad" => "I'm sorry!")
end
end
output:
["SERVER_SOFTWARE", "SERVER_NAME", "rack.input", "rack.version", "rack.errors", "rack.multithread", "rack.multiprocess", "rack.run_once", "REQUEST_METHOD", "REQUEST_PATH", "PATH_INFO", "REQUEST_URI", "HTTP_VERSION", "HTTP_HOST", "HTTP_CONNECTION", "HTTP_CACHE_CONTROL", "HTTP_ACCEPT", "HTTP_USER_AGENT", "HTTP_DNT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "GATEWAY_INTERFACE", "SERVER_PORT", "QUERY_STRING", "SERVER_PROTOCOL", "rack.url_scheme", "SCRIPT_NAME", "REMOTE_ADDR", "async.callback", "async.close", "rack.logger", "mybad", "rack.request.query_string", "rack.request.query_hash", "sinatra.route"]
It's near the end of that list.
The middleware method
There's also a method called middleware in Sinatra::Base:
# Middleware used in this class and all superclasses.
def middleware
if superclass.respond_to?(:middleware)
superclass.middleware + #middleware
else
#middleware
end
end
Call it in the class definition of a modular app and you can get the middlewares in an array:
require 'sinatra/base'
class AnExample < Sinatra::Base
use MyBad
warn "self.middleware = #{self.middleware}"
output:
self.middleware = [[MyBad, [], nil]]
There may be a way to get it from Sinatra::Application, but I haven't looked.
With help from ruby-raven guys, we've got this:
ENV['RACK_ENV'] = 'test'
# the app: start
require 'sinatra'
require 'sentry-raven'
Raven.configure(true) do |config|
config.dsn = '...'
end
use Raven::Rack
get '/' do
'Hello, world!'
end
# the app: end
require 'rspec'
require 'rack/test'
Raven.configure do |config|
logger = ::Logger.new(STDOUT)
logger.level = ::Logger::WARN
config.logger = logger
end
describe 'app' do
include Rack::Test::Methods
def app
#app || Sinatra::Application
end
class TestRavenError < StandardError; end
it 'sends errors to sentry' do
#app = Class.new Sinatra::Application do
get '/' do
raise TestRavenError
end
end
allow(Raven.client).to receive(:send).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven.client).to have_received(:send)
end
end
Or if raven sending requests is in the way (when tests fail because of raven sending requests, and not because of the underlying error), one can disable them:
Raven.configure(true) do |config|
config.should_send = Proc.new { false }
end
And mock Raven.send_or_skip instead:
...
allow(Raven).to receive(:send_or_skip).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven).to have_received(:send_or_skip)
...

Setting up Time.zone in Padrino

I am having a trouble setting the default ActiveSupport::TimeZone in my padrino project.
In my boot.rb I have
Padrino.after_load do
Time.zone = 'UTC'
ActiveRecord::Base.default_timezone = :utc
end
My controller file has:
MyApp::App.controllers :post do
get :index do
puts Time.zone # this returns nil
render 'index'
end
end
When I hit the index action I get nil for Time.zone. It seems as though something might be overwriting Time.zone or it isn't loaded properly.
I am able to print out the Timezone after setting it in boot.rb. So I know it was set.
You can set it like this:
Time.zone_default = Time.find_zone!("UTC")
That's all you need, but see below for details.
The above worked for me with activesupport 5.0.2. I looked at how Time.zone is implemented:
class Time
include DateAndTime::Zones
class << self
attr_accessor :zone_default
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
def zone
Thread.current[:time_zone] || zone_default
end
I then guessed that it might be missing in the current thread with Padrino.
Presumably Time.zone would need to be set once for each thread. For whatever reason, that's not always the case when assigning the zone in Padrino.before_load. I did not dig into this, but I'm sure there's a nicer solution to be found that assigns it in each thread.
If you want per-user time zones, not just a global one for the entire app, you will need to dig further.
In my boot.rb I've got:
Padrino.before_load do
Time.zone = 'UTC'
end
and in my database.rb:
ActiveRecord::Base.default_timezone = :utc
Which having tested in the console seems to work:
ruby-2.1.4$ padrino c
=> Loading development console (Padrino v.0.12.4)
2.1.4 :001 > Time.zone
=> #<ActiveSupport::TimeZone:0x007fbff62ed5c0 #name="UTC", #utc_offset=nil, #tzinfo=#<TZInfo::TimezoneProxy: Etc/UTC>, #current_period=#<TZInfo::TimezonePeriod: nil,nil,#<TZInfo::TimezoneOffset: 0,0,UTC>>>>
2.1.4 :002 > Time.zone.now
=> Tue, 30 Dec 2014 13:14:57 UTC +00:00
2.1.4 :003 > Time.current
=> Tue, 30 Dec 2014 13:15:01 UTC +00:00
2.1.4 :004 > ActiveRecord::Base.default_timezone
=> :utc
N.B. Tested with ruby v2.1.4, padrino v0.12.4, activesupport/activerecord v4.2.0.

How mix in routes in Sinatra for a better structure

I found nothing about how I can mix-in routes from another module, like this:
module otherRoutes
get "/route1" do
end
end
class Server < Sinatra::Base
include otherRoutes
get "/" do
#do something
end
end
Is that possible?
You don't do include with Sinatra. You use extensions together with register.
I.e. build your module in a separate file:
require 'sinatra/base'
module Sinatra
module OtherRoutes
def self.registered(app)
app.get "/route1" do
...
end
end
end
register OtherRoutes # for non modular apps, just include this file and it will register
end
And then register:
class Server < Sinatra::Base
register Sinatra::OtherRoutes
...
end
It's not really clear from the docs that this is the way to go for non-basic Sinatra apps. Hope it helps others.
You could do this:
module OtherRoutes
def self.included( app )
app.get "/route1" do
...
end
end
end
class Server < Sinatra::Base
include OtherRoutes
...
end
Unlike Ramaze, Sinatra's routes are not methods, and so cannot use Ruby's method lookup chaining directly. Note that with this you can't later monkey-patch OtherRoutes and have the changes reflected in Server; this is just a one-time convenience for defining the routes.
I prefer the use of sinatra-contrib gem to extend sinatra for cleaner syntax and shared namespace
# Gemfile
gem 'sinatra', '~> 1.4.7'
gem 'sinatra-contrib', '~> 1.4.6', require: 'sinatra/extension'
# other_routes.rb
module Foo
module OtherRoutes
extend Sinatra::Extension
get '/some-other-route' do
'some other route'
end
end
end
# app.rb
module Foo
class BaseRoutes < Sinatra::Base
get '/' do
'base route'
end
register OtherRoutes
end
end
sinata-contrib is maintained alongside the sinatra project
Well you can also use the map method to map routes to your sinatra apps
map "/" do
run Rack::Directory.new("./public")
end
map '/posts' do
run PostsApp.new
end
map '/comments' do
run CommentsApp.new
end
map '/users' do
run UserssApp.new
end
Just my two cents:
my_app.rb:
require 'sinatra/base'
class MyApp < Sinatra::Base
set :root, File.expand_path('../', __FILE__)
set :app_file, __FILE__
disable :run
files_to_require = [
"#{root}/app/helpers/**/*.{rb}",
"#{root}/app/routes/**/*.{rb}"
]
files_to_require.each {|path| Dir.glob(path, &method(:require))}
helpers App::Helpers
end
app/routes/health.rb:
MyApp.configure do |c|
c.before do
content_type "application/json"
end
c.get "/health" do
{ Ruby: "#{RUBY_VERSION}",
Rack: "#{Rack::VERSION}",
Sinatra: "#{Sinatra::VERSION}"
}.to_json
end
end
app/helpers/application.rb:
module App
module Helpers
def t(*args)
::I18n::t(*args)
end
def h(text)
Rack::Utils.escape_html(text)
end
end
end
config.ru:
require './my_app.rb'

In Sinatra(Ruby), how should I create global variables which are assigned values only once in the application lifetime?

In Sinatra, I'm unable to create global variables which are assigned values only once in the application lifetime. Am I missing something? My simplified code looks like this:
require 'rubygems' if RUBY_VERSION < "1.9"
require 'sinatra/base'
class WebApp < Sinatra::Base
#a = 1
before do
#b = 2
end
get '/' do
puts #a, #b
"#{#a}, #{#b}"
end
end
WebApp.run!
This results in
nil
2
in the terminal and ,2 in the browser.
If I try to put #a = 1 in the initialize method, I'm getting an error in the WebApp.run! line.
I feel I'm missing something because if I can't have global variables, then how can I load large data during application instantiation?
before do seems to get called every time there is a request from the client side.
class WebApp < Sinatra::Base
configure do
set :my_config_property, 'hello world'
end
get '/' do
"#{settings.my_config_property}"
end
end
Beware that if you use Shotgun, or some other Rack runner tool that reloads the code on each request the value will be recreated each time and it will look as if it's not assigned only once. Run in production mode to disable reloading and you will see that it's only assigned on the first request (you can do this with for example rackup --env production config.ru).
I ran into a similar issue, I was trying to initialize an instance variable #a using the initialize method but kept receiving an exception every time:
class MyApp < Sinatra::Application
def initialize
#a = 1
end
get '/' do
puts #a
'inside get'
end
end
I finally decided to look into the Sinatra code for initialize:
# File 'lib/sinatra/base.rb', line 877
def initialize(app = nil)
super()
#app = app
#template_cache = Tilt::Cache.new
yield self if block_given?
end
Looks like it does some necessary bootstrapping and I needed to call super().
def initialize
super()
#a = 1
end
This seemed to fix my issue and everything worked as expected.
Another option:
helpers do
def a
a ||= 1
end
end
Building on Theo's accepted solution, it is also possible to do:
class App < Sinatra::Application
set :blabla, ''
namespace '/b' do
get '/baby' do
# do something where bouh is assigned a value
settings.blabla = 'bouh'
end
end
namespace '/z'
get '/human' do
# settings.blabla is available here with newly assigned value
end
end
end
You could use OpenStruct.
require 'rubygems'
require 'sinatra'
require 'ostruct'
configure do
Struct = OpenStruct.new(
:foo => 'bar'
)
end
get '/' do
"#{Struct.foo}" # => bar
end
You can even use the Struct class in views and other loaded files.

Resources