Ruby on rails, run a method on server start 2.3 - ruby-on-rails

I want to run a method, on the startup of the rails server. It's a model method.
I tried using config/initializers/myfile.rb, but the method was invoked during migrations, so it SELECTed from a nonexistant table.
Tried environment.rb also, but the class does not exist yet (and will probably have the same problem with migrations)
I don't know where to put that method, so it'll run only on server startup and not during migrations.

There are some things you could do to actually improve this a bit. The issue is that you are running this code when rake loads your environment, but you really only want to run this when the environment is loaded by an instance of your web server. One way to get around this is to set a value when rake loads your environment, and when that value is set, to not execute your initializer code. You can do this as follows:
task :environment => :disable_initializer
task :disable_initializer do
ENV['DISABLE_INITIALIZER_FROM_RAKE'] = 'true'
end
#In your initializer:
ENV['DISABLE_INITIALIZER_FROM_RAKE'] || MyModel.method_call

There is no way to avoid this from my understanding. You can put the initializer code that relies on the new table in a rescue block to quiet things down so others can run migrations.

Try putting your method call in boot.rb, in the run method after the Rails::initializer call. I don't have rails in front of me right now because I'm at work but I think that the whole environment should be loaded by that point and you can run methods on the framework.

I found this to work quite well:
if File.basename($0) == "rails" && ARGV == []
It also detects "rails generate .."

Related

Can I detect if a rake task was triggered on the command line (as opposed to with cron, etc.)?

Of course it is unusual for rake tasks to be triggered by a controller (and kind of kludgey) but very common for them to be triggered by cron. I would like to detect from within a rake task whether it was started manually on the command line, or not.
How can I do that? This is a pretty standard thing to do in a shell script, but I'm unable to find any documentation about how to do it with a rake task.
Why the hate? People are downgrading this simply because they don't know the answer? 🤦🏼‍♂️
Here's a stab I took.
I tested this in both CL and Rails Console. I also tacked an invocation at the end of Application.rb to double check. But I haven't tested it in all the many other ways one might, so people should use this only with caution.
Likewise, I'm not certain that index 7 will be universal.
But I'm pretty sure it's accomplishable if you really want it.
task who_called: :environment do
puts case caller_locations[7].label
when "<main>" then :rails
when "invoke_task" then :cli
else
raise "unknown caller: #{location}"
end
end
Another suggestion is to always invoke the task with an ENV variable or an argument. You can assume that nil defaults to the command line, so people don't have to type unnecessary arguments.
Try this:
if defined?(Rails::Console)
....
end
Or you can check what caller[0] returns when you call from the cmd and use that in the if instead.

Where to put model “utility” functions in Ruby on Rails, if it is also required in a rake task?

This is a 2nd part to the following question:
Where to put model "utility" functions in Ruby on Rails
Problem is, I need access to these utility functions from a rake task as well. Using the accepted technique in in the other thread, I get an "undefined method" error when accessing my model from a rake task.
What is the best way to fix this?
Thanks
You probably need to define your rake task as dependent on the Rails environment:
task :my_task => :environment do
# Will load Rails stack before executing this block
MyModel.foo
end
The default behavior is to load almost nothing, so you won't have access to your models unless you ask for it.

Ruby scripts with access to Rails Models

Where and how do I run a simple script that uses my rails environment. Specifically I have one column that holds multiple pieces of information, I've added columns now for each piece of information and need to run a ruby script that can run to call a method on each row of the database to extrapolate data and save it to the new column.
Using a migration sounds like the right way to go if I am understanding your use case.
However, if you really do want to write a standalone script that needs access to your Rails application's models, you can require the environment.rb file from inside your standalone script.
Example:
#!/bin/env ruby
ENV['RAILS_ENV'] = "production" # Set to your desired Rails environment name
require '/path/to/railsapp/config/environment.rb'
# After this point you have access to your models and other classes from your Rails application
model_instance = MyModel.find(7)
model_instance.some_attribute = "new value"
model_instance.save
I have to agree with David here. Use a migration for this. I'm not sure what you want to do, but running it from inside your environment is much, much more efficient then loading up the app environment manually. And since your initial post suggests you're only doing this once, a migration is the way to go:
rails g migration MigrateData
.. generates:
class MigrateData < ActiveRecord::Migration
def self.up
# Your migration code here
end
def self.down
# Rollback scenario
end
end
Of course, you will always want to perform this locally first, using some test data.
Agree with everyone, for this specific case it sounds like migration will be way to go, however, to do this regularly, or write some other task/script that interacts rails app environment make rails generate a rake task for you! This gets saved with your rails app, and can be run again and again :)
Easiest way to generate a rake task that interact with rails app/models is to make Rails generate Rake tasks for you!! :)
Here's an example
run rails g task my_namespace my_task
This will generate a file called lib/tasks/my_namespace.rake which looks like:
namespace :my_namespace do
desc "TODO: Describe your task here"
task :my_task1 => :environment do
#write any ruby code here and also work with your models
puts User.find(1).name
end
end
Run this task with rake my_namespace:my_task
Watch your ruby code task that interacts with rails modal run!
Seeding data:
http://railscasts.com/episodes/179-seed-data
Adding data with migrations
http://railscasts.com/episodes/23-counter-cache-column
Working with Rake Tasks
http://railscasts.com/episodes/66-custom-rake-tasks
I prefer to use migrations for adding some data in your case.
If it's a one-time thing, use a migration.
If this is something that needs to be done multiple times, use a rake task for it.

In Ruby on Rails, "if defined? Product" doesn't work in script/runner mode?

If there is a simple script and to distinguish whether it is running by itself or being run inside the Rails app environment, I tried using
if defined? Product
# something
end
but it failed to be recognized even though Product is defined and can be used otherwise. Since then I tried using
if defined? RAILS_ENV
instead and it works well, but wonder why the defined? Product doesn't work?
This should work
if Product
# something
end
defined? ModelName returns nil for all my models.
Loading development environment (Rails 2.3.8)
>> defined? Post
=> nil
But then if I do this
>> Post; defined? Post
=> "constant"
Probably because nothing is loaded until you touch it. Hope this helps.
Edit: Ah ok well then, script/runner is a non-interactive form of script/console, I would think it loads the whole Rails app and runs from that context. If you need to identify wether the call was made from script/runner I can only think of passing a parameter to the function Model.long_running_method(:runner => true) and do your conditional check on that or if that is not convenient enough set a ENV constant ENV['something_runner']. And do the condition check on that instead.

Determine if script/server is being started

In Rails, in an initializer/environment.rb Whats the pefered way to detemrine if the webapp itself is being loaded (script/server).
All the initializers are loaded for migrations script/console and other rails task as well, but in my case some stuff only has to be loaded when the server itself is being initialized.
My ideas: checking $0
Thanks!
Reto
Because there are multiple application servers, each with their own initialization strategy, I would recommend the only way to reliably hook into the server boot process: ActionController::Dispatcher.
The dispatcher has some callbacks; namely:
prepare_dispatch (added with to_prepare)
before_dispatch
after_dispatch
The "prepare" callbacks are run before every request in development mode, and before the first request in production mode. The Rails configuration object allows you to add such callbacks via its own to_prepare method:
Rails::Initializer.run do |config|
config.to_prepare do
# do your special initialization stuff
end
end
Unfortunately, to my knowledge this callback will always be run since Rails initializer calls Dispatcher.run_prepare_callbacks regardless of if we're booting up with a server or to a script/console or even a rake task. You want to avoid this, so you might try this in your environment.rb:
Rails::Initializer.run do |config|
# your normal stuff
end
if defined? ActionController::Dispatcher
ActionController::Dispatcher.to_prepare do
# your special stuff
end
end
Now, your "special stuff" will only execute before first request in production mode, but before every request in development. If you're loading extra libraries, you might want to avoid loading something twice by putting an if statement around load or require. The require method will not load a single file twice, but I still recommend that you put a guard around it.
There is probably a better way to do this, but since I am not aware of one, I would probably alter script/server to set an environment variable of some kind.
Then I would have my initializer check for that environment variable.

Resources