I am trying to implement a Job in my Ruby on Rails application, but I keep getting this error:
NotImplementedError
Error from the server:
NotImplementedError (NotImplementedError):
app/controllers/cron_controller.rb:6:in `message_10_minutes'
Here's the ActiveJob:
class TestSmsJob < ActiveJob::Base
queue_as :default
#include Plivio
def perform(*args)
# do my stuff
end
end
This is the call to the function for executing the job:
class CronController < ApplicationController
def index
end
def message_10_minutes
TestSmsJob.set(wait: 10.minutes).perform_later()
render :layout => false
end
end
Do you guys know what am I missing?
Rails by default comes with own implementation of asynchronous queuing however you still needs to specify which adapter do you want to use. If you want to use built in adapter you simply don't have to bundle any other gem.
Here is the list of adapters - http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html
Since sidekiq may be too hard for the start you can use sucker_punch - https://github.com/brandonhilkert/sucker_punch
After you choose your adapter you have to update config
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :adapter_name
end
end
Related
As a personal project, I decided to write a minified-version of Ruby on Rails and upload it as a gem using Bundle called railz_lite.
Inside of my project, I was hoping to implement a Generator similar to rails new, which would create the necessary folders for a web app i.e. controllers/, views/, models/, etc.
To do so, I included Thor as a dependency, then created the following files:
require 'thor/group'
module RailzLite
module Generators
class Project < Thor::Group
include Thor::Actions
def self.source_root
File.dirname(__FILE__) + "/templates"
end
def add_controllers
empty_directory("controllers")
end
def add_models
empty_directory("models")
end
def add_server
template("server.rb", "config/server.rb")
end
def add_views
empty_directory("views")
end
def add_public
empty_directory("public")
end
end
end
end
Inside the gem project's root folder, when I run bundle exec railz_lite new, the generator works just fine and the necessary files are created.
However, if I create a new project, puts my gem (railz_lite) in the Gemfile, run bundle install, then execute bundle exec rails_lite new, I am greeted with the following error:
.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:232:in `mkdir':
: Read-only file system # dir_s_mkdir - /controllers (Errno::EROFS)
I suspect the error is because the empty_directory command is not referring to the root directory of the project I just created. I am hoping that there is a simple way to fix this.
For further reference, the CLI script and class look as follows:
railz_lite
#!/usr/bin/env ruby
require 'railz_lite/cli'
RailzLite::CLI.start
cli.rb
require 'thor'
require 'railz_lite'
require 'railz_lite/generators/project'
module RailzLite
class CLI < Thor
desc 'new', 'Generates a new RailzLite project'
def new
RailzLite::Generators::Project.start([])
end
end
end
Any solutions would be greatly appreciated!
Note: I am running this on macOS Catalina.
So I found a solution after searching extensively through gem forums and looking at the Rails source code.
Inside of the generation I have to manually set the destination_root to the working directory of the project. The working directory can be found with Dir.pwd
require 'thor/group'
module RailzLite
module Generators
class Project < Thor::Group
include Thor::Actions
def self.source_root
File.dirname(__FILE__) + "/templates"
end
def self.destination_root # this method fixes the problem!
Dir.pwd # get the current project directory
end
def add_controllers
empty_directory("controllers")
end
def add_models
empty_directory("models")
end
def add_server
template("server.rb", "config/server.rb")
end
def add_views
empty_directory("views")
end
def add_public
empty_directory("public")
end
end
end
end
My cop:
# lib/rubocop/cop/myproject/my_cop.rb
require 'rubocop'
module RuboCop
module Cop
module MyProject
class MyCop < RuboCop::Cop::Cop
# ...
end
end
end
end
This cop needs to know some global settings Rails. For example, Rails.logger.log_level
But I get errors:
1) undefined method 'logger' for RuboCop::Cop::Rails:Module - when I call Rails.logger.log_level
2) uninitialized constant Rails - when I call ::Rails.logger.log_level
Can this be done or is it a stupid idea?
As an option you can do:
# lib/rubocop/cop/myproject/my_cop.rb
require 'rubocop'
require_relative '../../../../../config/environment'
module RuboCop
module Cop
module MyProject
class MyCop < RuboCop::Cop::Cop
# ...
end
end
end
end
And call ::Rails.logger.level
Rubocop is a static code analyzer. Which means when you run rubocop command, it does not load any ruby environments, including Rails. It just reads ruby files and analyses those as text files.
So the short answer is: no, it can not be achieved with Rubocop.
We are writing a gem that includes multiple common gems for a couple of our shared apps. We want to be able to have a config in application.rb or enviroment.rb/*rb something like config.fruit_chain.enable_transport = true from the consuming app to conditionally require a gem and it's initializer dynamically. But the initializer from common gem does not run after require in a railtie. I wondered if there is a better way to do this
fruit_store/config/application.rb . (consuming app)
module FruitStore
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
config.fruit_chain.enable_transport = true
end
end
fruit_chain/lib/fruit_chain.rb (Our gem)
require analytic
- require transport <----- removed this so it dose not autoload
require marketing
...
module FruitChain
end
fruit_chain/lib/fruit_chain/rails/railtie.rb
module FruitChain
module Rails
class Railtie < ::Rails::Railtie
config.fruit_chain = ActiveSupport::OrderedOptions.new
config.fruit_chain.enable_transport = false
config.before_initialize do |app|
if app.config.fruit_chain.enable_transport
Kernel.require 'transport' <--- this require the gem correct and load it up
app.initializers.find{
|a| a.name === 'transport.configure'
}.run <--- transport.configure initializer doesn't kick off
end
end
end
end
end
transport/lib/transport.rb . (Dependent common gem)
require transport/rails/railtie
...
module Transport
end
transport/lib/transport/rails/railtie.rb
module Transport
module Rails
class Railtie < ::Rails::Railtie
initializer 'transport.configure' do |app|
...
end
end
end
end
Under Rails 4.2.3, I have setup ActiveJob to use delayed_job for its backend in all environments:
environment.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module MyApp
class Application < Rails::Application
# ... snip
# Use delayed_job for Active Job queueing
config.active_job.queue_adapter = :delayed_job
end
end
(and queue_adapter is not otherwise set under config).
I have configured my DelayedJob instance as follows - including code from this question to back-port the Rails 5 feature of getting the database row for a job:
config/delayed_job.rb
Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.sleep_delay = 60
Delayed::Worker.max_attempts = 3
Delayed::Worker.max_run_time = 5.minutes
Delayed::Worker.read_ahead = 10
# Delayed::Worker.delay_jobs = true #!Rails.env.test?
Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log'))
class Delayed::Job < ActiveRecord::Base
belongs_to :owner, polymorphic: true
end
# Backport Rails 5 code to make the Delayed::Job database id avilable on ActiveJobs
module ActiveJob
module Core
# ID optionally provided by adapter
attr_accessor :provider_job_id
end
module QueueAdapters
class DelayedJobAdapter
class << self
send(:prepend, Module.new do
def enqueue(job)
provider_job = super
job.provider_job_id = provider_job.id
provider_job
end
def enqueue_at(job, timestamp)
provider_job = super
job.provider_job_id = provider_job.id
provider_job
end
end)
end
end
end
end
I have model code to perform a job later, and hook the Delayed::Job instance onto the model instance:
app/models/my_model.rb
def schedule
job = MyJob.set(wait_until: scheduled_time).perform_later(self)
# Associate the delayed_job object with the item that has been scheduled
delayed_job = Delayed::Job.find(job.provider_job_id)
delayed_job.owner = self
delayed_job.save
end
(and I have set up appropriate associations for Delayed::Job).
This appears to work fine in development; but in test, the Delayed::Job.find(job.provider_job_id) fails and raises an exception, because provider_job_id is nil. Examining the logs shows no SQL INSERT for the delayed_jobs table.
Why is there no delayed job row being created in test?
I was having this same problem. Turns out that ActiveJob::TestCase hijacks the perform_later logic and doesn't actually queue them, but instead stores data about queued jobs in an array (see here).
My solution was to write a test that inherited from ActiveSupport::TestCase instead, which seems to queue delayed jobs like normal. I believe there are some test settings that will force jobs to be performed inline, but as far as I know, the default behavior is to queue them, so it should work as expected without much tinkering.
I have a problem with sidekiq/activejob integration. I have a controller that calls a perform_later method from a MyJob class. This works with the perform method, but when I change to perfom_later, the job is scheduled in my development log. However, when I see the sidekiq dashboard, at the retries section, I can see NameError: uninitialized constant (look below image)
These are my files:
# app/jobs/crime_job.rb
class CrimeJob < ActiveJob::Base
queue_as :default
def perform(crime)
puts "Perform #{crime}"
end
def self.job_name(crime)
"RadarCrime:#{crime.id}"
end
end
Crime Controller
# app/controllers/crime_controller.rb
def show
# [...]
CrimeJob.perform_later(#crime)
end
Sidekiq initializer
# config/initializers/active_job.rb
Rails.application.config.active_job.queue_adapter = :sidekiq
Well, I also open an issue in Sidekiq repository, and the solution is easier than I've think.
Just restart the sidekiq process and it's works fine.
Issue link: https://github.com/mperham/sidekiq/issues/2207