How can we log everything done by Net::Sftp in a file - ruby-on-rails

how to log every action of Net::Sftp into a file in ruby on rails for debugging and how can I log verbose::debug to a logfile.
sftp = Net::SFTP.start(config[:host], config[:user], config[:options], verbose::debug)
files_arr = []
sftp.connect!
sftp.dir.foreach(src_dir) do |entry|
file_name = entry.name
begin
if entry.file? && (file_name.end_with?(".xml") || file_name.end_with?(".zip"))
sftp.download!(File.join(src_dir, file_name), File.join(dest_dir, file_name))
files_arr << file_name
end
rescue Exception => e
next
end
end
sftp.channel.eof!
sftp.close_channel unless sftp.closed?
my objective is to log all that happens in this code to a file, how can I do it.

Following on from the comments, it seems that what you want is to define a custom logger that you can then use to log your custom info to its own file. Something like this in the relevant environment.rb should do what you need.
my_logger = Logger.new('log/my_logger.log')
my_logger.level = Logger::DEBUG
Then, you can add my_logger 'MESSAGE' at the relevant points in your code to add entries to your custom logfile.

Related

Reading text from a PDF works in Rails console but not in Rails application

I have a simple one-page searchable PDF that is uploaded to a Rails 6 application model (Car) using Active Storage. I can extract the text from the PDF using the 'tempfile' and 'pdf-reader' gems in the Rails console:
> #car.creport.attached?
=> true
> f = Tempfile.new(['file', '.pdf'])
> f.binmode
> f.write(#car.creport.blob.download)
> r = PDF::Reader.new(f.path.to_s)
> r.pages[1].text
=> "Welcome to the ABC Car Report for January 16, 20...
But, if I try the same thing in the create method of my cars_controller.rb, it doesn't work:
# cars_controller.rb
...
def create
#car = Car.new(car_params)
#car.filetext = ""
f = Tempfile.new(['file', '.pdf'])
f.binmode
f.write(#car.creport.blob.download)
r = PDF::Reader.new(f.path.to_s)
#car.filetext = r.pages[1].text
...
end
When I run the Rails application I can create a new Car and select a PDF file to attach. But when I click 'Submit' I get a FileNotFoundError in cars_controller.rb at the f.write() line.
My gut instinct is that the controller is trying to read the blob in order to write it to the temp file too soon (i.e., before the blob has even been written). I tried inserting a sleep(2) to give it time, but I get the same FileNotFoundError.
Any ideas?
Thank you!
I don't get why you're jumping through so many hoops. And using .download without a block loads the entire file into memory (yikes). If #car.creport is an ActiveStorage attachment you can just use the open method instead:
#car.creport.blob.open do |file|
file.binmode
r = PDF::Reader.new(file) # just pass the IO object
#car.filetext = r.pages[1].text
end if #car.creport
This steams the file to disk instead (as a tempfile).
If you're just taking file input via a plain old file input you will get a ActionDispatch::Http::UploadedFile in the parameters that also is extemely easy to open:
params[:file].open do |file|
file.binmode
r = PDF::Reader.new(file) # just pass the IO object
#car.filetext = r.pages[1].text
end if params[:file].respond_to?(:open)
The difference looks like it's with your #car variable.
In the console you have a blob attached (#car.creport.attached? => true). In your controller, you're initializing a new instance of the Car class, so unless you have some initialization going on that attaches something in the background, that will be nil.
Why that would return a 'file not found' error I'm not sure, but from what I can see that's the only difference between code samples. You're trying to write #car.creport.blob.download, which is present on #car in console, but nil in your controller.

How can I force an ActionMailer error for testing?

I have this code that sends an email based on conditions. If there is an error its supposed to catch the account that wasn't able to be sent in an array like so:
def process_email(delivery_method_name)
begin
Account::Access.new(account).spam! if delivery_method_name == 'mark_as_spam'
AccountMailer.send("#{delivery_method_name}", account).deliver_now
rescue
#first_notification_error << account.id
#second_notification_failure << account.id
#third_notification_failure << account.id
#fourth_notification_error << account.id
#fourth_notification_failure << account.id
return
end
update_reverification_fields
end
So in my test.rb file I want to be able to test that the account.id was caught inside of the #first_notification_error and other containers. It's not exactly clear to me though how to do this though. I read in another post to place this code in development.rb and/or test.rb config.action_mailer.raise_delivery_errors = true but I don't think this is what I'm looking for. Is there a way I can raise the error in my test, perhaps with a stub or something similar?
You need to mock AccountMailer. Put this line before calling the code you're testing:
delivery_method = 'some method on mailer that you want to mock'
allow(AccountMailer).to receive(delivery_method).and_raise("boom")

Ruby, Tempfile, CSV

I have the below resque job that produces a csv file and sends it to a mailer. I want to validate that the csv file has data so I do not email blank files. For some reason, when I write a method outside of the perform method, it will not work. For example, the below code will print invalid when I know the csv file has data on the first line. If I uncomment the line below ensure it works properly, however I want to extract this checking of the file into a separate method. Is this correct?
class ReportJob
#queue = :report_job
def self.perform(application_id, current_user_id)
user = User.find(current_user_id)
client_application = Application.find(client_application_id)
transactions = application.transactions
file = Tempfile.open(["#{Rails.root}/tmp/", ".csv"]) do |csv|
begin
csv_file = CSV.new(csv)
csv_file << ["Application", "Price", "Tax"]
transactions.each do |transaction|
csv_file << [application.name, transaction.price, transaction.tax]
end
ensure
ReportJob.email_report(user.email, csv_file)
#ReportMailer.send_report(user.email, csv_file).deliver
csv_file.close(unlink=true)
end
end
end
def self.email_report(email, csv)
array = csv.to_a
if array[1].blank?
puts "invalid"
else
ReportMailer.send_report(email, csv).deliver
end
end
end
You should invoke your method as such:
ReportJob.email_report(email, csv)
Otherwise, get rid of the self in:
def self.email_report(email, csv)
# your implementation here.
end
and define your method as follows:
def email_report(email, csv)
# your implementation.
end
This is something that we call Class Methods and Instance Methods.

Add current time before log message

I created a custom logger for my application, called CRON_LOG, just by adding this piece of code to config/environment.rb
CRON_LOG = Logger.new("#{Rails.root}/log/cron.log")
CRON_LOG.level = Logger::INFO
And then, when I want to log something, just do that:
CRON_LOG.info "something"
It works fine, but I'd like to add the current timestamp before each log message. Of course I can just add Time.now to my log message, but I'd like to know if there is a way to add it as default to every log message. How can I do that ?
Thanks.
The easiest way to make a SysLog-formatted logger is to assign a formatter directly:
logger = Logger.new Rails.root.join('path', 'to', 'log')
logger.formatter = Logger::Formatter.new
logger.info 'Hello, world!'
# Writes:
#
# I, [2011-06-02T20:02:34.579501 #15444] INFO -- : Hello, world!
You can redefine the proper method (e.g. adding the following to environment.rb or in an initializer):
class Logger
def format_message(severity, timestamp, progname, msg)
"#{timestamp} (#{$$}) #{msg}\n"
end
end
[Caution: this could disrupt other loggers; see Stephen's answer for a safe solution - jph]

Rails, use the content of file in the controller

I've a file in the config directory, let's say my_policy.txt.
I want to use the content of that file in my controller like a simple string.
#policy = #content of /config/my_policy.txt
How to achieve that goal, does Rails provide its own way to do that?
Thanks
Rails doesn't provide a way, but Ruby does:
#policy = IO.read("#{Rails.root}\config\my_policy.txt")
#policy = File.read(RAILS_ROOT + '/config/my_policy.txt')
To also cache the content (if you don't want to read it every time the variable is used):
def policy
##policy ||= File.read(RAILS_ROOT + '/config/my_policy.txt')
end
If you need something more elegant for configuration, check configatronic.
Read file as string like that?!
def get_file_as_string(filename)
data = ''
f = File.open(filename, "r")
f.each_line do |line|
data += line
end
return data
end
##### MAIN #####
#policy = get_file_as_string 'path/to/my_policy.txt'
# print out the string
puts #policy

Resources