I have a setup command that I want executed every time I start the rails console -
MyClass.some_method()
I get tired of retyping it each time I fire up rails c - is there a way to have it automatically get run every time a new console is started?
Thanks!
We do this in order to ask for the tenant every time the console starts. It took a bit of investigation, but we got it working fairly elegantly. Note that this works with Rails 5.2 but it has worked mostly the same way since Rails 4.
Another thing to note is that this is written specifically because we wanted to be able to run the method once on start and then be able to run it again while using the console, say if we wanted to switch the tenant during a session.
The first step is to create a set of modules and classes in a lib file. Here is an example extracted from ours:
# lib/console_extension.rb
module ConsoleExtension
# This module provides methods that are only available in the console
module ConsoleHelpers
def do_someting
puts "doing something"
end
end
# This is a simple class that allows us access to the ConsoleHelpers before
# we get into the console
class ConsoleRunner
include ConsoleExtension::ConsoleHelpers
end
# This is specifically to patch into the startup behavior for the console.
#
# In the console_command.rb file, it does this right before start:
#
# if defined?(console::ExtendCommandBundle)
# console::ExtendCommandBundle.include(Rails::ConsoleMethods)
# end
#
# This is a little tricky. We're defining an included method on this module
# so that the Rails::ConsoleMethods module gets a self.included method.
#
# This causes the Rails::ConsoleMethods to run this code when it's included
# in the console::ExtendCommandBundle at the last step before the console
# starts, instead of during the earlier load_console stage.
module ConsoleMethods
def included(_klass)
ConsoleExtension::ConsoleRunner.new.do_someting
end
end
end
The next step is to add the following into your application.rb file:
module MyApp
class Application < Rails::Application
...
console do
require 'console_extension' # lib/console_extension.rb
Rails::ConsoleMethods.send :include, ConsoleExtension::ConsoleHelpers
Rails::ConsoleMethods.send :extend, ConsoleExtension::ConsoleMethods
end
end
end
Now, every time you run rails console, it will do something:
If you're just looking to run something once every time the console starts, this is more complicated than it needs to be. Instead, you can just use the console() method in MyApp::Application and it will run whatever code you want as part of the load_console step.
module MyApp
class Application < Rails::Application
...
console do
puts "do something"
end
end
end
One issue we had with this was that it runs the code before it prints out the environment, so if you're doing any printing or interaction it feels a bit weird:
You may not be as picky as we are though. Do whatever makes you and your team the happiest.
I dont know if its a good practice, but you can check if server is running on Console, like Aditya awnsered
if defined?(Rails::Console)
MyClass.some_method()
end
Note that this won't work during Rails initialization when running Spring like Swartz said.
I would try creating a Rake task for it and invoke it with after_initialize:
config.after_initialize do
IndividualProject::Application.load_tasks
Rake::Task[ 'foo:bar' ].invoke
end
Related
I'm working on a product that calls of perform_later jobs. This works for our product in production because we have a series of workers who will run all the jobs.
But, when I'm using the app locally, I don't have access to these workers, and I'd like to change all the perform_laters into perform_nows only when I use the app locally.
What's the best way to do this? One idea I had was to add something in my env file that would add a variable to make all perform_laters into perform_nows -- but I'm not sure what a flag or variable like that would look like.
Ideas?
The clean solution is to change the adapter in development environment.
In your /config/environments/development.rb you need to add:
Rails.application.configure do
config.active_job.queue_adapter = :inline
end
"When enqueueing jobs with the Inline adapter the job will be executed immediately."
In your app you can have:
/my_app/config/initializers/jobs_initializer.rb
module JobsExt
extend ActiveSupport::Concern
class_methods do
def perform_later(*args)
puts "I'm on #{Rails.env} envirnoment. So, I'll run right now"
perform_now(*args)
end
end
end
if Rails.env != "production"
puts "including mixin"
ActiveJob::Base.send(:include, JobsExt)
end
This mixin will be included on test and development environments only.
Then, if you have the job in:
/my_app/app/jobs/my_job.rb
class MyJob < ActiveJob::Base
def perform(param)
"I'm a #{param}!"
end
end
You can execute:
MyJob.perform_later("job")
And get:
#=> "I'm a job!"
Instead of the job instance:
#<MyJob:0x007ff197cd1938 #arguments=["job"], #job_id="aab4dbfb-3d57-4f6d-8994-065a178dc09a", #queue_name="default">
Remember: Doing this, ALL your Jobs will be executed right now on test and dev environments. If you want to enable this functionality for a single job, you will need to include the JobsExt mixin in that job only.
We solved this by calling an intermediate method which then called perform_later or perform_now depending on Rails config:
def self.perform(*args)
if Rails.application.config.perform_later
perform_later(*args)
else
perform_now(*args)
end
end
And simply updated environments configs accordingly
I open up a rails console and require rspec_rails. I want to write a test like the following and be able to call it.
RSpec.describe("foo") do
it "bar" do
expect(false).to be false
end
end
When I enter this into my console I see that a RSpec::ExampleGroup::Foo is created. However, I don't know how to call it.
I'm basically trying to reuse some helper methods I wrote for my tests. For example, I wrote a helper method called create_user_and_login which sends HTTP requests to a running server and creates a new user. I could reuse this method when migrating data our old database, but the method calls expect because up to now, it was only called from RSpec::ExampleGroup using the standard RSpec CLI runner.
Here's how you can write and call that test in your console
# in your terminal
rails c test
# this worked for me to get rspec working in my console
require 'rspec/rails'
my_test = RSpec.describe("foo") do
it "bar" do
expect(false).to be false
end
end
# now run it
my_test.run
# => true
I need to add a job to the Sidekiq queue when my Rails app starts, to update some data, but I don't know where is the best place to do it.
Right now, I've wrote this on my application.rb:
class Application < Rails::Application
config.after_initialize do
MyWorker.perform_async
end
end
But the problem is that when I run the sidekiq command it will also load the Rails stack, so I'll end up with 2 jobs on the queue.
Is there any other way of doing that? This is my first big Rails app and my first time with Sidekiq, so I don't know if I'm not understanding things correctly. That might not be the right way of doing that.
Thanks!
A better solution would be to create an initializer config/initializers/sidekiq.rb
Sidekiq.configure_client do |config|
Rails.application.config.after_initialize do
# You code goes here
end
end
We were having issues w/ Redis connections and multiple jobs being launched.
I ended up using this and it seems to be working well:
if defined?(Sidekiq)
Sidekiq.configure_server do |config|
config.on(:startup) do
already_scheduled = Sidekiq::ScheduledSet.new.any? {|job| job.klass == "MyWorker" }
MyWorker.perform_async unless already_scheduled
end
end
end
Probably foreman suits for your purposes.
I know this is old, but none of this worked for me - would still start the job several times. I came up with the following solution:
I have a Class to do the actual Job:
class InitScheduling
include Sidekiq::Worker
def perform
# your code here
end
end
And I have an inizializer, which would normally start 3 Times, every time, something loads the Rails environment. So I use the Job as a state variable that this job is already scheduled:
# code in inizilizer/your_inizilizer.rb
Rails.application.config.after_initialize do
all_jobs = Sidekiq::ScheduledSet.new
# If this is True InitScheduling is already scheduled - don't start it again
unless all_jobs.map(&:klass).include?("InitScheduling")
puts "########### InitScheduling ##############"
# give rails time to build before running this job & keeps this initialization from re-running
InitScheduling.perform_in(5.minutes)
# your code here
end
end
I want to authenticate Users when they try to fire up a TCP connection in my Rails app. Here's the current code I have, it's very simplistic but should give you an idea of what I want to do.
TcpServer.rb
module TcpServer
def receive_data(data)
(#buf ||= '') << data
if line = #buf.slice!(/(.+)\r?\n/)
commands = data.split(";")
case commands[0]
when /start/i
if !User.authenticate(commands[1],commands[2])
close_connection
puts "Subscription invalid."
else
put "Subscription validated."
end
end
end
end
EventMachine::run do
host = "localhost"
port = "5587"
EventMachine::start_server host, port, TcpServer
puts "TcpServer started # #{host}:#{port}"
end
end
What do I need to require or include in order to access my User model from that module? Or is this just a completely incorrect way to do it? If so, what do you suggest?
The issue is I wasn't running it with Rails.
I was running it with:
ruby lib/TcpServer.rb
rather than:
script/runner lib/TcpServer.rb
No includes or requires needed, Rails did it automagically.
Dir.glob(Rails.root.join('app/models/*.rb')).each { |file| require file }
The above will get all models loaded if you need them (you can just add 'user.rb' to the statement if needed, in the comment above you may need to specify the path and not include the ".rb" part -> "require 'user'").
You should make a decision as to whether you think this type of integration server should be part of the running Rails app or potentially another "application" that is part of the same code base. You could keep the core internals here and start your EM server with a custom rake task and load the Rails env through that rake task.
namespace :tcp do
task :start, :needs => :environment do
# server load and start here
end
end
If I am going to open a new means of execution then I prefer to keep these running in separate processes to keep any errors from causing both to go down together. (I would look at Resque jobs/workers as a good example of how to keep code in the same Rails app without forcing them to run in the same process)
I am running a delayed job worker. When ever I invoke the foo method, worker prints hello.
class User
def foo
puts "Hello"
end
handle_asynchronously :foo
end
If I make some changes to the foo method, I have to restart the worker for the changes to reflect. In the development mode this can become quite tiresome.
I am trying to find a way to reload the payload class(in this case User class) for every request. I tried monkey patching the DelayedJob library to invoke require_dependency before the payload method invocation.
module Delayed::Backend::Base
def payload_object_with_reload
if Rails.env.development? and #payload_object_with_reload.nil?
require_dependency(File.join(Rails.root, "app", "models", "user.rb"))
end
#payload_object_with_reload ||= payload_object_without_reload
end
alias_method_chain :payload_object, :reload
end
This approach doesn't work as the classes registered using require_dependency needs to be reloaded before the invocation and I haven't figured out how to do it. I spent some time reading the dispatcher code to figure out how Rails reloads the classes for every request. I wasn't able to locate the reload code.
Has anybody tried this before? How would you advise me to proceed? Or do you have any pointers for locating the Rails class reload code?
I managed to find a solution. I used ActiveSupport::Dependencies.clear method to clear the loaded classes.
Add a file called config/initializers/delayed_job.rb
Delayed::Worker.backend = :active_record
if Rails.env.development?
module Delayed::Backend::Base
def payload_object_with_reload
if #payload_object_with_reload.nil?
ActiveSupport::Dependencies.clear
end
#payload_object_with_reload ||= payload_object_without_reload
end
alias_method_chain :payload_object, :reload
end
end
As of version 4.0.6, DelayedJob reloads automatically if Rails.application.config.cache_classes is set to false:
In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes. You no longer need to restart Delayed Job every time you update your code in development.
This looks like it solves your problem without the alias_method hackery:
https://github.com/Viximo/delayed_job-rails_reloader