Calling a method in a separate class from the whenever schedule.rb - ruby

I am trying to call a method from a whenever schedule like this:
schedule.rb:
require '../app/bots/task_bot'
every 1.minute do
runner 'TaskBot.test_robot'
end
And I have a class called TaskBot placed inside the directory app/bots/task_bot.rb. It looks like this:
task_bot.rb:
class TaskBot
def test_robot()
logger = Logger.new('log/robot.log')
logger.debug("TEST")
end
end
Then I ran the command whenever --update-crontab and verified that the crontab is updated by using crontab -l. Crontab was updated but I don't see the Logger being executed every minute. I'm new to this, am I missing something here?

test_robot is an instance method but you are trying to execute it as a class method; in order for it to work you have two options:
Create the TaskBot object first and then call test_robot:
every 1.minutes do
runner 'TaskBot.new.test_robot'
end
Create a class method, and do not modify schedule code (i prefer this way):
class TaskBot
def self.test_robot
logger = Logger.new('log/robot.log')
logger.debug("TEST")
end
end

Related

Not able to find global log file in Ruby

I have a script called main.rb in which I call modules. In the script, I am creating a log file (something like \abc\xyz\script\tool.log) and storing it in a global variable $LOG: $LOG = Logger.new('tool.log', 'daily')
From main.rb I call another module's method (say HostM.changeDir1) which changes directory to \abc\xyz. Now when I am trying to write into $LOG it errors out by printing \abc\xyz\tool.log does not exist. I was wondering how can I make it look for the log in proper location i.e., \abc\xyz\script\tool.log. Need guidance in fixing this issue.
Add the full path to the log file is one option
$LOG = Logger.new('\abc\xyz\script\tool.log', 'daily')
However, using a global variable is usually poor practice, and I expect using an absolute path is also going to cause problems if you move the app to another location. Therefore, I'd suggest you wrap the Logger in a new class that defines a standard behaviour and a path to the log file that is relative to the file where the new class is defined.
require 'logger'
class AppLogger < Logger
def initialize(period = 'daily')
path_to_log = File.expand_path('relative/path/to/tool.log', File.dirname(__FILE__))
super path_to_log, period
end
end
Then when you need a logger in your app, you can do this:
logger = AppLogger.new
logger.debug 'Whoops'
If you want to ensure you only work with one logger instance, I'd load the logger instance in your Main class, and share it from there:
class Main
require_relative 'app_logger'
def self.logger
#logger ||= AppLogger.new
end
....
end
Then in the rest of your app, you'll be able to call the logger like this:
Main.logger.debug 'Whoops!'

Including commands from separate Ruby file

I have a chunk of code that has commands. I want to keep more commands in a separate file and pull them into the main. Thus my main file can be updated without losing any custom commands. How would I go about that?
{
class myClass()
#commands
listen_for /what is your name/i do
say "my name is mud"
end
## Insert Custom.rb here ##
# Everything in the custom rb file is just ike the "listen_for" command above
end
}
The answe above will not work in this case because there is no listen_for methods defined in the custom.rb file
wrap whatever you have in custom.rb in a module, like
module Foo
# commands
end
require your file custom.rb on the top of your script and include it in your class:
require_relative './custom.rb'
class myClass()
include Foo
# code here
end
This is the new try
Remove the module wrapper in the listen_for commands, and instead simply list them in the custom.rb as you would do inside your main class definition. And in your main class, Read and eval it, like this:
class myClass()
eval(File.read('./custom.rb'))
# code here
end

How to provide config file for Ruby command line utility written in Ruby?

I have a command line utility written in Ruby using GLI framework. I would like to have configuration for my command line utility in my home directory, using Ruby itself as DSL to handle it (similar to Gemfile or Rakefile).
I have in class ConfigData in folder lib/myapp. The class looks like following way:
class ConfigData
##data = {}
class ConfigItem
def initialize
#data = {}
end
def missing_method(name, *args)
#data[name] = args[0]
end
end
def self.add(section)
item = ConfigItem.new()
yield item
##data[section]=item
end
end
Now, what I would like to have, is the config file, preferrably with name Myappfile, in current working folder, with the following content
add('section1') do |i|
i.param1 'Some data'
i.param2 'More data'
end
When this code was included between class and end of ConfigData, it worked fine. But now I would like to have it placed in the working folder, where I start the application.
I tried require('./Myappfile') between class and end of ConfigData, but it doesn't work for me. I tried to read the source codes of rake, but it is not very much clear to me.
Any hint how this can be solved?
To evaluate code within the context of an instance, which is what you want to do, you need the instance_eval() method. Never, ever, use normal eval. Ever. Anyway, here's how you'd load your fingi file and get the data:
config = ConfigData.new
config.instance_eval(File.read("Myconfig"))
#Access configuration data here from the config object
That simple. After you've loaded the object in that way, you can access values of the object.
WARNING: This is not very secure. This is actually a gaping security hole. Here's the secure version:
f = Fiber.new {str = File.read("Myconfig"); $SAFE = 4; config = ConfigData.new; config.instance_eval(str); Fiber.yield config}
confdata = f.resume
#Access configuration data here from confdata.
This executes the external code in a (sort of) sandbox, so that it can't do anything dastardly.
Also, why don't you just use a YAML config? Unless configuration needs to run code like pwd or access RUBY_VERSION, YAML is much simpler and more secure, in addition to being more failproof.

How do you set the WEBRick options parameter in the run method with Padrino

I don't want to monkey patch Padrino.
I still want to be able to use the command padrino start -d from the command line.
I want to get SSL up and running within padrino. Within Sinatra I just do:
Rack::Handler::WEBrick.run MyServer, MyServerOptionsWithAppropriateSSLStuffEtc
I found the file deep inside the Padrino core that handles setting these options, but I really don't want to monkey patch the application.
Ideally I'd like there to be be some way I could set the options within my Padrino::Application subclass.
So far I haven't found any documentation on how to do this, or if this is even possible.
mmm, you should be able to do the same.
In your project folder you should see config.ru
Try to edit it removing last line with:
Rack::Handler::WEBrick.run Padrino.application, MyServerOptionsWithAppropriateSSLStuff
Then from command line:
$ rackup
I know this is old, but in case anybody is trying to do this cleanly, here is what I use:
class MyApplication < ::Sinatra::Base
# ...
def self.server_settings
{ key: value, ... }
end
# ...
end
You can also inject settings prior to runtime:
MyApplication.class_exec(server_settings) do |server_params|
def self.server_settings
server_params
end
end
I frequently use the second example for injecting a custom logger into my application for specs.
For example:
module CustomLogger
def logger
settings.try(:server_settings)[:Logger] || request.logger
end
end
MyApplication.class_exec(CustomLogger) do |logger_module|
helpers logger_module
def self.server_settings
# global specified in guard/spec helper
{ Logger: $LOGGER }
end
end
class MyApplication < ::Sinatra::Base
enable :logging
get '/' do
logger.info "FOO"
end
end
MyApplication.run!
See this source link for more info about server_settings usage in Application::self.run!.

How to really reset a unit test?

I'd like to test class and gem loading. Have a look at the following stupid test case:
require 'rubygems'
require 'shoulda'
class SimpleTest < Test::Unit::TestCase
context 'This test' do
should 'first load something' do
require 'bundler'
assert Object.const_defined? :Bundler
end
should 'second have it reset again' do
assert !Object.const_defined?(:Bundler)
end
teardown do
# This works, but is tedious and unclean
#Object.send :remove_const, :Bundler rescue nil
# Instead I want something like this ;)
magic_reset
end
end
end
How about creating a subclass of Test::Unit::TestCase which runs the test method in a forked process?
class ForkingTestCase < Test::Unit::TestCase
def run(...)
fork do
super.run(...)
# somehow communicate the result back to the parent process
# this is the hard part
end
end
end
If this can be implemented, it should then just be a matter of changing the base class of your test case.
AFAIK, you cannot unload a file that you have loaded. You need to start a separate Ruby process for every test. (Or a separate Ruby instance if you are running on a Ruby implementation which supports multiple instances in the same process.)
Try using Kernel#load with wrap set to true:
load(filename, wrap=false) → true
Loads and executes the Ruby program in the file filename. If the
filename does not resolve to an absolute path, the file is searched
for in the library directories listed in $:. If the optional wrap
parameter is true, the loaded script will be executed under an
anonymous module, protecting the calling program’s global namespace.
In no circumstance will any local variables in the loaded file be
propagated to the loading environment.
each time you want to do a test of bundler, load it into a new anonymous module, do your tests on the bundler class within that module, and then go onto your next test.
If your code refers to the Bundler constant, then you'd have to set and unset that constant, though.
I haven't tried it myself, but can't see why it wouldn't work.
Try keeping track of what constants were defined before your tests started, and what constants are defined after your test finished, and remove the constants that were defined during the test.
I guess this isn't so much telling you how to call magic_reset as how to implement magic_reset.

Resources