I'm trying to run a method immediately on startup in my rails project. Previously I used the following for running locally
# application.rb
if defined?(Rails::Server)
config.after_initialize do
# Do stuff here
end
end
While this works locally, we are now running on prod with bundle exec rackup, which returns false for the above statement. Is there something I can call in my Rails project to execute a method immediately on start from Rackup?
Related
I have a Ruby script in a subdirectory of a Ruby on Rails application that runs in the background and performs some support tasks. In this script, would like have access to the Rails environment and the value of an application controller constant.
The best approach to retrieve these values I could find so far is based in Rails runner. If I run
cd .. && Rails runner "puts [Rails.env, ApplicationController::CONSTANT_NAME]"
from the subdirectory in shell, I get the desired values. But when I try to use the same command in my script, I get an undefined method error for active_storage:
/home/user/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.2/lib/rails/railtie/configuration.rb:96:in `method_missing': undefined method `active_storage' for #<Rails::Application::Configuration:0x0000563603fbdaa8> (NoMethodError)
The code in the script is
puts %x|cd .. && rails runner "puts [Rails.env, ApplicationController::CONSTANT_NAME]"|
The Rails application and the Ruby script run under the same user. I have Rails 6.0.3.2 and Ruby 2.6.5.
What you want to do is write a Rake task instead:
# lib/tasks/foo.rake
namespace :foo do
description "#TODO write a descripion"
task bar: :environment do
# your logic goes here
puts [Rails.env, ApplicationController::CONSTANT_NAME]
end
end
This task can be invoked via bin/rake foo:bar. bar: :environment loads the Rails environment for this task.
This is a lot less hacky/wonky then using the rails runner, and is the defacto way of writing tasks/scripts in Ruby that are meant to invoked from the command line.
So I have a rails 6 app that doesn't load any css or javascript assets when I run cap production deploy.
So what I do is after I run cap production deploy, I ssh into my server, navigate to myapp/current/ and run bin/webpack and then everything works. So I'd like my deployment process to do this for me so that I don't have to go into my server and run this everytime.
I've looked on how to run custom capistrano "tasks," but all the tutorials show you only how to run custom rake tasks, but this isn't a rake task.
I don't run rake bin/webpack, i just run bin/webpack.
So how I would go about implementing this in my capistrano setup? I assume I have to enter some sort of capistrano command in my deploy.rb.
Any ideas?
Capistrano at its core is just ssh. It's doing something similar to what you're doing when you ssh (although slightly different because it's not typically an interactive session).
You could write a custom task to run Webpack.
To do that you need to load them first if they're not already being loaded.
Check for this line in your Capfile or add it yourself. This will load your custom tasks which come in the form of rake tasks:
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
Then, create a task in that folder. Call it something like lib/capistrano/tasks/webpack.rake
namespace :webpack do
task :build do
on roles(:app) do
within release_path do
# https://github.com/capistrano/sshkit#the-command-map
with path: "#{release_path}/bin:$PATH" do
execute :webpack
end
end
end
end
end
Now, you need to tell Capistrano when to execute it. I usually do this with before/after hooks. For more info check out Capistrano flow
after the namespace block add:
# lib/capistrano/tasks/webpack.rake
# ...
after "deploy:updated", "webpack:build"
you can test out task execution with bundle exec cap production deploy --dry-run
I have the following code in config/application.rb
config.after_initialize do
IndividualProject::Application.load_tasks
#load File.join(Rails.root, 'lib', 'tasks', 'download_csv.rake')
Rake::Task[ 'download_csv:get_files' ].invoke
Rake::Task[ 'download_csv:place_in_database' ].invoke
end
My problem is that if I try to execute migrations, I get a database error which says that one of tables I'm referencing in the rake task does not exist.
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "currencies" does not exist
I can solve the issue by commenting out the code and then running the migrations. After this, the server runs fine.
However, I want to deploy to Heroku, where I can't comment out the code before running the migrations.
How should I solve this issue?
Do I need to place the code somewhere else in the project?
Remove your code from config/application.rb and change the web process in Procfile like following:
web: rake download_csv:get_files && rake download_csv:place_in_database && bundle exec rails server -p $PORT
Change bundle exec rails server -p $PORT with whatever code you use to start your server.
If you don't have Procfile in your project yet, create one and add it to git.
Now your rake tasks will be executed only before starting the server.
Ruby on rails + Capistrano + Whenever gem
I executed whenever --update-crontab but still cron job is not getting executed at production server. There are no logs in the log file. Though everything works well at development where capistrano is not required.
schedule.rb
set :output, "../dev/log/cron.log"
every 1.minute do
runner "SOME_TASK"
end
deploy.rb
set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
capfile
require "whenever/capistrano"
What's the issue? How to debug?
I've had similar issues when deploying our apps.
If you check the log files of crontab you'll see that it does execute but its being executed in the wrong context.
For example:
You think this code should execute but it doesn't:
every 1.minute do
runner "bundle exec rake db:seed"
end
Instead you should supply the absolute path tot the executable. Cron doesn't know in what kind of context it should be run, it just executes.
We use rbenv in our deployment and we use shims of gems. So I just supplied cron with the absolute path to the executable.
This code does run:
every 1.minute do
runner "/usr/bin/shims/bundle exec rake db:seed"
end
I tried:
after_initialize do
#code
end
But: (documentation)
Some parts of your application, notably observers and routing, are not
yet set up at the point where the after_initialize block is called.
I need routing and logger in my code
Any ideas?
See section 3.1 from http://guides.rubyonrails.org/configuring.html
I believe you would put this code in config/application.rb
config.after_initialize do
# ....
end
# config.after_initialize takes a block which will be run after Rails has finished initializing the application.
# That includes the initialization of the framework itself
Also http://guides.rubyonrails.org/initialization.html
#house9's answer is correct, as pointed out by the comments, this will also execute when running rake tasks, console, etc. I used the following to recognize when a server was actually being executed:
# application.rb
if defined?(Rails::Server)
config.after_initialize do
# Do stuff here
end
end
Another option is to create a custom initializer. It's just a ruby file that lives under config/initializers/ and is executed exactly "on_server_start" event :)
Lines added to config.ru will be run by the Rails server, but not by Rails console or Rake tasks that load the environment.
# config.ru
# This file is used by Rack-based servers to start the application.
require ::File.expand_path("../config/environment", __FILE__)
# your code here (after environment is loaded)
run Rails.application
Since Rails 5 the default server is Puma, so code in config/puma.rb will be run just once, and only if the server is started.