How to create plans without the "rails c" command ? - ruby-on-rails

I took a shared server to deploy my app, to begin.
Everything works but I have some troubles to create my plans with stripe.
On localhost, I can use 'rails c' to create them but my server don't allow me to do it.
Here's the command to create plans from the console :
CreatePlan.call(stripe_id: 'test_plan', name: 'Test Plan', amount: 500, interval: 'month', description: 'Test Plan', published: false)
The create plan method is a service object : app/services/create_plan.rb
Here's my create_plan.rb :
class CreatePlan
def self.call(options={})
plan = Plan.new(options)
if !plan.valid?
return plan
end
begin
Stripe::Plan.create(
id: options[:stripe_id],
amount: options[:amount],
currency: 'usd',
interval: options[:interval],
name: options[:name],
)
rescue Stripe::StripeError => e
plan.errors[:base] << e.message
return plan
end
plan.save
return plan
end
end
How could I create my plans with no console ?
I tried with seeds.rb but it don't work.

To do stuff like this, make a rake task.
Like this:
namespace :stripe do
desc "Create stripe plans"
task :create_plans => :environment do
# Do the business
end
end
And then run rake stripe:create_plans on your server.

Related

ActiveRecord::QueryMethods not working within rspec test but in console it working fine

I have a test where I want to test that a book_group cannot delete when is associated with a book, this test failed but in the application, this feature works fine
describe 'callbacks' do
context 'before_validation' do
include_examples 'examples for strippable attributes', :book_group, :name, :description, :spawn_button_label
end
it 'is not destroyed and returns a base error if there is an book associated to the it' do
error = 'Cannot delete record because dependent books exist'
book_group.books << create(:book)
expect(book_group.destroy).to be false
end
end
I debugged into the test and found that the error is because this query not working as expected
First, I valid that these two models have an association
pry(#<RSpec::ExampleGroups::bookGroup::Callbacks>)> book_group.books
=> [#<book:0x0000563cb3f6eaf0
id: 1,
review_id: 1,
name: "Vernie Becker",
level: "site",
book_group_id: 831812,
author_book_id: nil]
I do the next query but its result is wrong
book_group.books.where(author_book_id: nil).order(id: :desc).first
=> nil
but this query within console working as expected
[4] pry(main)> #book_group.books.where(author_book_id: nil).order(id: :desc).first DEBUG
[2022-04-02T16:16:49.295Z] book Load (0.7ms) SELECT `books`.* FROM `books` WHERE `books`.`book_group_id` = 6 AND `books`.`author_book_id` IS NULL ORDER BY `books`.`id` DESC LIMIT 1
=> #<book:0x000055d2217339c8 id: 261, review_id: 1, name: "Base book site", level: "site", book_group_id: 6, book_template_id: 2, author_book_id: nil]
the book_group is created in this way
def create
ActiveRecord::Base.transaction do
#book_group = bookGroup.new(book_group_params)
#book_group.author_id = params[:author_id]
#book_group.save!
AllowedActorship.create_from_level_scoped_params(
book_group_params,
#book_group
)
render(
json: { message: #book_group },
status: :ok
)
end
end
I already have reset and prepared the bd, I'm not sure why it working so weird, I will say thank you for whatever helped with it.

Rails 5 - Sidekiq worker shows job done but nothing happens

I'm using Sidekiq for delayed jobs with sidekiq-status and sidekiq-ent gems. I've created a worker which is reponsible to update minor status to false when user is adult and has minor: true. This worker should be fired every day at midnight ET. Like below:
#initializers/sidekiq.rb
config.periodic do |mgr|
# every day between midnight 0 5 * * *
mgr.register("0 5 * * *", MinorWorker)
end
#app/workers/minor_worker.rb
class MinorWorker
include Sidekiq::Worker
def perform
User.adults.where(minor: true).remove_minor_status
rescue => e
Rails.logger.error("Unable to update minor field. Exception: #{e.message} : #{e.backtrace.join('\n')}")
end
end
#models/user.rb
class User < ApplicationRecord
scope :adults, -> { where('date_of_birth <= ?', 18.years.ago) }
def self.remove_minor_status
update(minor: false)
end
end
No I want to check this on my local machine - to do so I'm using gem 'timecop' to timetravel:
#application.rb
config.time_zone = 'Eastern Time (US & Canada)'
#config/environments/development.rb
config.after_initialize do
t = Time.local(2021, 12, 21, 23, 59, 0)
Timecop.travel(t)
end
After firing up sidekiq by bundle exec sidekiq and bundle exec rails s I'm waiting a minute and I see that worker shows up:
2021-12-21T22:59:00.130Z 25711 TID-ovvzr9828 INFO: Managing 3 periodic jobs
2021-12-21T23:00:00.009Z 25711 TID-ovw69k4ao INFO: Enqueued periodic job SettlementWorker with JID ddab15264f81e0b417e7dd83 for 2021-12-22 00:00:00 +0100
2021-12-21T23:00:00.011Z 25711 TID-ovw69k4ao INFO: Enqueued periodic job MinorWorker with JID 0bcd6b76d6ee4ff9e7850b35 for 2021-12-22 00:00:00 +0100
But it didn't do anything, the user's minor status is still set to minor: true:
2.4.5 :002 > User.last.date_of_birth
=> Mon, 22 Dec 2003
2.4.5 :001 > User.last.minor
=> true
Did I miss something?
EDIT
I have to add that when I'm trying to call this worker on rails c everything works well. I've got even a RSpec test which also passes:
RSpec.describe MinorWorker, type: :worker do
subject(:perform) { described_class.new.perform }
context 'when User has minor status' do
let(:user1) { create(:user, minor: true) }
it 'removes minor status' do
expect { perform }.to change { user1.reload.minor }.from(true).to(false)
end
context 'when user is adult' do
let(:registrant2) { create(:registrant) }
it 'not change minor status' do
expect(registrant2.reload.minor).to eq(false)
end
end
end
end
Since this is the class method update won't work
def self.remove_minor_status
update(minor: false)
end
Make use of #update_all
def self.remove_minor_status
update_all(minor: false)
end
Also, I think it's best practice to have some test cases to ensure the working of the methods.
As of now you can try this method from rails console and verify if they actually work
test "update minor status" do
user = User.create(date_of_birth: 19.years.ago, minor: true)
User.adults.where(minor: true).remove_minor_status
assert_equal user.reload.minor, false
end
I think you need to either do update_all or update each record by itself, like this:
User.adults.where(minor: true).update_all(minor: false)
or
class MinorWorker
include Sidekiq::Worker
def perform
users = User.adults.where(minor: true)
users.each { |user| user.remove_minor_status }
rescue => e
Rails.logger.error("Unable to update minor field. Exception: #{e.message} : #{e.backtrace.join('\n')}")
end
end
You may also want to consider changing update to update! so it throws an error if failing to be caught by your rescue in the job:
def self.remove_minor_status
update!(minor: false)
end

Rails Rspec allow multiple method call in one line

desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
task failed_user_cleaner: :environment do
puts "Daily UserRecord Cleaning CronJob started - #{Time.now}"
#user = User.with_state("credentials").with_last_otp_at(Time.now - 10.minutes)
Users::Delete.new(#user).destroy_all
puts "Daily UserRecord Cleaning CronJob ended - #{Time.now}"
end
Above is crop job rake file code.
then I've tried in many times and found in many times.
But I couldn't find the way to write unit test case for above job.
Help me to write test case correctly.
here is my spec code
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let (:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
it 'calls right service method' do
#users = Users::Delete.new([user])
expect(#users).to receive(:destroy_all)
run_users_rake_task
end
end
end
here is the error log
Failures:
1) users rake tasks when remove credential state users who no longer request for confirm otp within 10 minutes calls right service method
Failure/Error: expect(#users).to receive(:destroy_all)
(#<Users::Delete:0x0000556dfcca3a40 #user=[#<User id: 181, uuid: nil, phone: "+66969597538", otp_secret: nil, last_otp_at: "2021-09-30 09:32:24.961548000 +0700", created_at: "2021-09-30 09:43:24.973818000 +0700", updated_at: "2021-09-30 09:43:24.973818000 +0700", email: nil, avatar: "https://dummyimage.com/300x300/f04720/153572.png?t...", refresh_token: "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzI5Njk4MDQsImV4c...", first_name_en: "Jenise", first_name_th: "Damion", last_name_en: "McCullough", last_name_th: "Beatty", nationality: "TH", thai_national_id: nil, thai_laser_code: nil, company_id: 200, role: nil, state: "credentials", date_of_birth: "2020-10-30 00:00:00.000000000 +0700", deleted_at: nil, password_digest: "$2a$04$jfR9X9ci06602tlAyLOoRewTK1lZ12vJ2cZ9Dc2ov4F...", username: "zreejme238", shopname: nil, access_token: nil, locked_at: nil, login_attempts: 0, locale: "th", scorm_completed: false>]>).destroy_all(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/tasks/users_spec.rb:19:in `block (3 levels) in <top (required)>'
You are creating two instances of Users::Delete when running this test, one within the test and one within the task. Since the instance within the test is not used, it is incorrect to expect it to receive a message.
Rspec has an expectation, expect_any_instance_of, that will fix this however consider reading the full page since it can create fragile or flaky tests. If you wanted to use this method, your test would look something like:
it 'calls right service method' do
expect_any_instance_of(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
Personally I'd instead check that the expected users were deleted with something like:
it 'removes the user' do
expect { run_users_rake_task }.to change { User.exists?(id: #user.id) }.to(false)
end
Unless you want to use any_instance_of (which is a code smell) you need to stub the Users::Delete method so that it returns a double and put the expectation on the double:
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let(:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
let(:double) do
instance_double('Users::Delete')
end
before do
allow(Users::Delete).to receive(:new).and_return(double)
end
it 'calls right service method' do
expect(double).to receive(:destroy_all)
run_users_rake_task
end
end
end
However this really just tells us that the API of the service object is clunky and that you should write a class method which both instanciates and performs:
module Users
class Delete
# ...
def self.destroy_all(users)
new(users).destroy_all
end
end
end
desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
#...
Users::Delete.destroy_all(#user)
# ...
end
require 'rails_helper'
describe 'users rake tasks' do
# ...
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
# ...
it 'calls right service method' do
expect(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
end
end

SQL execution time in Rake tasks

I've various rake tasks inside my rails app. One simple example is shown below.
desc "Simple rake task"
task :test_rake do |task|
first_sql_query = FirstModel.find(10)
SecondModel.create(:name => 'Test 101', :email => 'abc#def.co')
final_query = SecondModel.find(900)
end
Now in the above rake task, we're making three database calls with each of them taking x, y, z seconds supposedly.
Is there any way to find out the total time spent on db operations(x+y+z secs) for a given rake task..??
Use benchmark
task :test_rake do |task|
time = Benchmark.realtime {
first_sql_query = FirstModel.find(10)
SecondModel.create(:name => 'Test 101', :email => 'abc#def.co')
final_query = SecondModel.find(900)
}
puts time
end
getting separate benchmarks:
puts Benchmark.measure { FirstModel.find(10) }
puts Benchmark.measure { SecondModel.create(:name => 'Test 101', :email => 'abc#def.co') }
puts Benchmark.measure { final_query = SecondModel.find(900) }
Query timing is included in the log file:
❯❯❯ rake db:migrate:status
database: ml_development
Status Migration ID Migration Name
--------------------------------------------------
up 20180612055823 ********** NO FILE **********
then:
❯❯❯ cat log/development.log
[DEBUG] (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
```
#pattu Rails internally use Benchmark for execution time check say_with_time
You can add the same function in a module and include in the rack task to find the execution time of SQL queries.
These Two functions are needed:
def say(message, subitem = false)
puts "#{subitem ? " ->" : "--"} #{message}"
end
def say_with_time(message = "")
say(message)
result = nil
time = Benchmark.measure { result = yield }
say "%.4fs" % time.real, :subitem
say("#{result} rows", :subitem) if result.is_a?(Integer)
result
end
Use this in Your rake task as
desc "Simple rake task"
task :test_rake do |task|
say_with_time do
first_sql_query = FirstModel.find(10)
SecondModel.create(:name => 'Test 101', :email => 'abc#def.co')
final_query = SecondModel.find(900)
end
end

Delayed Job DeserializationError, failed to load: allocator undefined for Proc

With version 4 of delayed_job_active_record gem, I want to delay requests to an external API. My job is added to database, and rake jobs:work runs it and removes it from the database. However, the actual delayed code from my messages_controller.rb is never executed.
If I try to Delayed::Job.last.invoke_job in the rails console, I receive the following error:
Delayed::DeserializationError: Job failed to load: allocator undefined for Proc. Handler: "--- !ruby/object:Delayed::PerformableMethod\nobject: !ruby/object:MyApp::Zendesk\n client: !ruby/object:ZendeskAPI::Client\n config: !ruby/object:ZendeskAPI::Configuration\n client_options: {}\n cache: !ruby/object:ZendeskAPI::LRUCache\n size: 1000\n store: {}\n lru: []\n url: https://redacted.zendesk.com/api/v2\n username: redacted\n password: redacted\n retry: true\n logger: !ruby/object:Logger\n progname: \n level: 0\n default_formatter: !ruby/object:Logger::Formatter\n datetime_format: \n formatter: \n logdev: !ruby/object:Logger::LogDevice\n shift_size: \n shift_age: \n filename: \n dev: !ruby/object:IO {}\n mutex: !ruby/object:Logger::LogDevice::LogDeviceMutex\n mon_owner: \n mon_count: 0\n mon_mutex: !ruby/object:Mutex {}\n callbacks:\n - !ruby/object:Proc {}\n resource_cache: {}\nmethod_name: :create_support_ticket\nargs:\n- !ruby/hash:ActionController::Parameters\n name: redacted\n email: redacted\n reason: General\n message: test\n"
The code trying to be run is the create_support_ticket method:
# messages_controller.rb
require 'zendesk'
class MessagesController < ApplicationController
layout "application"
# /suport/contact-us
def contact_us
zendesk = MyApp::Zendesk.new
zendesk.delay.create_support_ticket(params[:message])
# render page
respond_to do |format|
flash[:notice] = "Email sent successfully!" if #sent
format.html { render "pages/support/contact-us" }
end
end
end
# zendesk.rb
require 'zendesk_api'
module MyApp
class Zendesk
attr_accessor :client
def initialize(*args)
#client = create_client
end
# contact-us ticket methods
def create_support_ticket(params={})
unless params.blank? || #client.blank?
# get or create user_id for submitter
params[:requester_id] = check_user_exists(params)
begin
ticket = #client.tickets.create(
subject: "Support Ticket",
comment: { value: params[:message] },
submitter_id: params[:requester_id],
requester_id: params[:requester_id],
assignee_id: 201578811,
status: "new",
fields: [
{id: 20887016, value: "Support"},
{id: 20966436, value: "New"}])
return ticket
rescue => e
Airbrake.notify e
end
else
return false
end
end
end
end
DelayedJob tries to serialize the object you are calling the method on.
In your case, that object has an IO and also a Proc object. Both of them are not compatible with serialization. Best thing you can do is writing a simple wrapper that has no dependencies and initializes all the stuff inside the method-call.
I happened to come across the same issue as above. I had an object of Oauth::Client which failed to deserialize by delayed_job Gem.
To resolve, I have changed my approach, so that the Ouath::Client object isn't serialized/deserialized when I add a method to the delayed job.

Resources