active_record configuration from yaml file: debuglevel - ruby

I'm trying to configure the debuglevel for active-record logger from a YAML configuration file but get the following error, how could i do this other than using a number in the YAML ?
sample.rb:30 warning: toplevel constant LEVEL referenced by Logger::LEVEL
"DEBUG"
ArgumentError: comparison of Fixnum with String failed
here is the sample.rb
require 'java'
require 'active_record'
require 'activerecord-jdbc-adapter'
require 'yaml'
require 'logger'
def get_jar_path
if __FILE__[/.+\.jar!/] #in case run from JAR
scriptpath = __FILE__[/(.*)\/.+\.jar!/]
$1[6..-1]
else #in case run with jRuby
'..'
end
end
def load_config
path = "#{get_jar_path}/#{File.basename(__FILE__, ".*")}.configuration.yml"
p path
$conf = YAML::load_file(path)
end
load_config
LEVEL = $conf['debug_level'] #string 'DEBUG' from configuration file
$log = Logger.new( "#{get_jar_path}/log_#{Time.now.strftime("%Y%m%d")}.txt", 'monthly' )
ActiveRecord::Base.logger = $log
ActiveRecord::Base.logger.level = Logger::DEBUG #works
ActiveRecord::Base.logger.level = Logger::LEVEL #doesn't work
p ActiveRecord::Base.logger.level
$log.info "start #{__FILE__}"

The available log levels are: :debug, :info, :warn, :error, :fatal,
and :unknown, corresponding to the log level numbers from 0 up to 5
respectively.
http://guides.rubyonrails.org/debugging_rails_applications.html
require 'logger'
puts Logger::DEBUG
--output:--
0
str = "DEBUG"
puts Logger.const_get(str)
--output:--
0
So you should do something like:
level = $conf['debug_level'] #string 'DEBUG' from configuration file
$log = Logger.new( "#{get_jar_path}/log_#{Time.now.strftime("%Y%m%d")}.txt", 'monthly' )
ActiveRecord::Base.logger = $log
ActiveRecord::Base.logger.level = Logger.const_get(level)
I'm not sure why you thought defining a constant, LEVEL, in the current scope would make that constant appear in the Logger scope, so that you could write Logger::LEVEL. You essentially did this:
MYCONST = "hello"
module SomeModule
SOMECONST = "goodbye"
end
You can write:
puts MYCONST #=>hello
..and you can write:
puts SomeModule::SOMECONST #goodbye
..but you cannot write:
puts SomeModule::MYCONST
--output:--
1.rb:10:in `<main>': uninitialized constant SomeModule::MYCONST (NameError)

Related

Uninitialized constant NameError in Rspec

When I run rails c, I can call the following class and the method works:
test = SlackService::BoardGameNotifier
test.create_alert("test")
>>method works
I'm trying to set this up in rspec like this:
require 'spec_helper'
require 'slack-notifier'
RSpec.describe SlackService::BoardGameNotifier do
describe '#notify' do
#notifier = SlackService::BoardGameNotifier
it 'pings Slack' do
error = nil
message = "test"
expect(notifier).to receive(:ping).with(message)
notifier.send_message()
end
end
end
But I keep getting the error:
NameError:
uninitialized constant SlackService
Does this have to do with how I set up the module?
My current setup:
slack_service/board_game_notifier.rb
module SlackService
class BoardGameNotifier < BaseNotifier
WEBHOOK_URL = Rails.configuration.x.slack.url
DEFAULT_OPTIONS = {
channel: "board-games-channel",
text: "board games alert",
username: "bot",
}
def create_alert(message)
message #testing
end
end
end
slack_service/base_notifier.rb
module SlackService
class BaseNotifier
include Singleton
def initialize
webhook_url = self.class::WEBHOOK_URL
options = self.class::DEFAULT_OPTIONS
#notifier = Slack::Notifier.new(webhook_url, options)
end
def self.send_message
message = instance.create_alert("test")
instance.notify(message)
end
def notify(message)
#notifier.post blocks: message
end
end
end
Add this to your spec_helper.rb
# spec_helper.rb
ENV["RAILS_ENV"] ||= "test"
require File.expand_path("../config/environment", __dir__)
When running RSpec, Rails doesn't automatically boot up, and therefore doesn't automatically load all the libraries.
Also, I'd suggest creating a .rspec in your app's root folder with the following lines so that spec_helper is automatically loaded for all your RSpec tests:
# .rspec
--format documentation
--color
--require spec_helper
I would use the described_class from Rspec
require 'spec_helper'
require 'slack-notifier'
RSpec.describe ::SlackService::BoardGameNotifier do
describe '#notify' do
it 'pings Slack' do
error = nil
message = "test"
expect(described_class).to receive(:ping).with(message)
notifier.send_message()
end
end
end

How to define a common module logging shared among others module

I am developing a script with a big main function, which I have split in several modules.
What I need is to have access to the log functionality from all of them, this means that the log file has to be opened only once, and the access be shared.
This is what I have:
require 'module_1'
require 'module_2'
require 'module_3'
module Main
Module_1.Function_startup()
Module_2.Function_configuration()
Module_3.Function_self_test()
end
Here is the dirty module for the logger I need available in all the other modules.
Ideally I would like to call it as "logger.error", where "logger" returns the instance of the logger, and "error" is the function call on rlogger as rlogger.error.
require 'logger'
module Logging
#rlogger = nil
def init_logger
if #rlogger.nil?
puts "initializing logger"
file_path = File.join(File.dirname(__FILE__), 'checker.log')
open_mode = File::TRUNC # or File::APPEND
file = File.open(file_path, File::WRONLY | open_mode)
#rlogger = Logger.new(file)
#rlogger.datetime_format = "%Y-%m-%d %H:%M:%S"
#rlogger.formatter = proc do |severity, datetime, progname, msg|
con_msg = ""
if msg.match("ERROR:")
con_msg = msg.color(:red)
elsif msg.match("OK!")
con_msg = msg.color(:green)
else
con_msg = msg
end
puts ">>>#{con_msg}"
# notice that the colors introduce extra non-printable characters
# which are not nice in the log file.
"#{datetime}: #{msg}\n"
end
# Here is the first log entry
#rlogger.info('Initialize') {"#{Time.new.strftime("%H-%M-%S")}: Checker v#{#version}"}
end
end
# returns the logger
def logger
if #rlogger.nil?
puts "requesting nil rlogger"
end
#rlogger
end
end
end
Just after require you can add this piece of code
$FILE_LOG = Logging.create_log(File.expand_path('LoggingFile.log'), Logger::DEBUG)
Explanation of the above line : It is calling a function in Logging Module, to create File , Level of Logging is debug.
Below is the piece of code for Module
module Logging
def self.create_log(output_location level)
log = Logger.new(output_location, 'weekly').tap do |l|
next unless l
l.level = level
l.progname = File.basename($0)
l.datetime_format = DateTime.iso8601
l.formatter = proc { |severity, datetime, progname, msg| "#{severity}: #{datetime} - (#{progname}) : #{msg}\n" }
end
log.level = level if level < log.level
log
end
def self.log(msg, level)
# Here I am just logging only FATAL and DEBUG, similarly you can add in different level of logs
if level == :FATAL
$FILE_LOG.fatal(msg)
elsif level == :DEBUG
$FILE_LOG.debug(msg)
end
end
end
Then in Every method every Ruby file, we can use this logging as follows
Logging.log("Message",:FATAL)

How can I test logger-messages with MiniTest?

I have an application and I want to test if I get correct
messages from my logger.
A short example (you may switch between log4r and logger):
gem 'minitest'
require 'minitest/autorun'
require 'log4r'
#~ require 'logger'
class Testlog < MiniTest::Test
def setup
if defined? Log4r
#log = Log4r::Logger.new('log')
#log.outputters << Log4r::StdoutOutputter.new('stdout', :level => Log4r::INFO)
else
#log = Logger.new(STDOUT)
#log.level = Logger::INFO
end
end
def test_silent
assert_silent{ #log.debug("hello world") }
assert_output(nil,nil){ #log.debug("Hello World") }
end
def test_output
#~ refute_silent{ #log.INFO("Hello") }#-> NoMethodError: undefined method `refute_silent'
assert_output("INFO log: Hello World\n",''){ #log.info("Hello World") }
end
end
But I get:
1) Failure:
Testlog#test_output [minitest_log4r.rb:27]:
In stdout.
Expected: "INFO log: Hello World\n"
Actual: ""
On my output screen I see the message.
I have similar results with Log4r::StderrOutputter and Log4r::Outputter.stdout.
So it seems it is send to the output screen, but it is not catched by minitest in STDOUT or STDERR.
Before I start to write a minitest-log4r-Gem:
Is there a possibility to test logger-output in minitest?
If not:
Any recommendations how to implement a minitest-log4r-Gem?
Examples what I could imagine:
define new outputter for minitest (Log4r::MinitestOutputter)
Mock the logger.
new assertions (add the new outputter as parameter?):
assert_message('INFO log: Hello World'){ #log.info("Hello World") }
assert_messages(:info => 1, :debug => 2){ #log.info("Hello World") } to count messages.
assert_info_messages('Hello World'){ #log.info("Hello World") }
assert_debug_messages('Hello World'){ #log.info("Hello World") }
You can set up a pipe, pass the writer from the pipe to the logger, and then use the reader from the pipe to test your assertions.
http://ruby-doc.org/core-2.1.0/IO.html#method-c-pipe
Something like:
require 'logger'
r, w = IO.pipe
log = Logger.new(w)
log.info "testing info log message"
output = r.gets
puts "Test passed: #{!!(/testing/ =~ output)}"
In meantime I created a minitest-logger-Gem
A code example how to use it:
require 'log4r'
require 'minitest-logger'
class Test_log4r < MiniTest::Test
def setup
#log = Log4r::Logger.new('log')
#log.level = Log4r::INFO
end
def test_output_1
assert_log(" INFO log: Hello World\n"){ #log.info("Hello World") }
end
def test_output_regex
assert_log(/Hello World/){ #log.info("Hello World") }
end
def test_silent
assert_silent_log(){
#log.debug("Hello World")
#~ #log.info("Hello World") #uncomment this to see a failure
}
refute_silent_log(){
#log.warn("Hello World") #comment this to see a failure
}
end
end
During the test a temporary outputter is added to the logger #log. After the test the outputter is removed again.
The gem supports log4r and logger.
#Puhlze answer is good. Just for non-blocking, check before hand if there is input available:
if IO.select([r], [], [], 0.01).nil?
Suppose we have this code here on a file called logger.rb:
require 'logger'
class Framework
attr_reader :logger
def initialize
#logger = Logger.new("/tmp/minitest.log")
end
end
class Custom
def initialize(framework)
#framework = framework
end
def error!
raise StandardError, 'Forced error'
rescue StandardError => e
#framework.logger.error "Error: #{e}"
end
end
And we need to test the logger error messages. We can use a stub method and a StringIO object:
require 'minitest'
require 'minitest/autorun'
require_relative 'logger.rb'
class LoggerTest < MiniTest::Test
def test_logger
framework = Framework.new
custom = Custom.new(framework)
io = StringIO.new
framework.stub :logger, Logger.new(io) do
custom.error!
assert_match(/Forced error/, io.string)
end
end
end
This way we don't need to override the framework logger, just stub it.

NoMethodError undefined method `configure' for #<Sinatra::Application>

I tried to work sinatra application, but the error occurs which is very mystery.
#encoding: utf-8
require 'sinatra'
require 'rss'
require 'dalli'
require './url'
require './feed'
set :bind, '0.0.0.0'
configure :production do
require 'newrelic_rpm'
end
...
configure :development do
require 'sinatra/reloader'
end
...
get '/new_movie' do
if params['tag2']
#key = 'tag1=' + params['tag1'] + '&tag2=' + params['tag2']
else
#key = 'tag1=' + params['tag1']
end
configure :production do ####### ERROR OCCURS AT HERE! #######
# if cache exists
if output = settings.cache.get(#key)
#isCacheUsed = true
output
end
end
unless #isCacheUsed
# Thread One
t1 = Thread.new(params['tag1']) do |param_tag1|
#feed_nico = feed_nico(param_tag1)
puts 'nico' if DEBUG_APP
end
# Thread Two
if params['tag2']
t2 = Thread.new(params['tag2']) do |param_tag2|
#feed_vimeo = feed_vimeo(param_tag2)
puts 'vimeo' if DEBUG_APP
end
end
# Main Thread
feed_hatena1 = feed_hatena(params['tag1'])
puts 'hatena1' if DEBUG_APP
t1.join
t2.join if params['tag2']
if params['tag2']
feed = feed_hatena1.append(
#feed_nico, #feed_vimeo).
unique
puts 'append + unique' if DEBUG_APP
else
feed = feed_hatena1.append(#feed_nico).unique
end
content_type = 'text/xml; charset=utf-8'
#output = feed.to_s
end
end
...
Thank you for your help.
You can't call "configure" from within your route. Make sure that all your configuration parameters exist outside of your routes

Using a Ruby Module for Defaults

I'd like to use a Ruby Module to store a set of configuration defaults.
I'm having some problems using the values and hope someone could help.
This may not be the best way to do this but this is what I've come up with so far.
Here is a module to hold value for a persons Resume => resume.rb
module Resume
require 'ostruct'
attr_reader :personal, :education
#personal = OpenStruct.new
#education = Array.new
def self.included(base)
set_personal
set_education
end
def self.set_personal
#personal.name = "Joe Blogs"
#personal.email_address = 'joe.blogs#gmail.com'
#personal.phone_number = '5555 55 55 555'
end
def self.set_education
#education << %w{ School\ 1 Description\ 1 }
#education << %w{ School\ 2 Description\ 2 }
end
end
From irb it works fine:
% irb -I .
1.9.3-p194 :001 > require 'resume'
=> true
1.9.3-p194 :002 > include Resume
=> Object
1.9.3-p194 :003 > puts Resume.personal.name
Joe Blogs
=> nil
However when I include this into a class it throws and error => build.rb
require 'resume'
class Build
include Resume
def build
puts Resume.personal.name
end
end
From irb:
% irb -I .
1.9.3-p194 :001 > require 'build'
=> true
1.9.3-p194 :002 > b = Build.new
=> #<Build:0x00000001d0ebb0>
1.9.3-p194 :003 > b.build
NoMethodError: undefined method `personal' for Resume:Module
from /home/oolyme/projects/lh_resume_builder_ruby/build.rb:7:in `build'
from (irb):3
from /home/oolyme/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
I've tried a few variations to output the include module variables in the Build class instance but all error out.
attr_accessor creates a couple of instance methods, meaning that they will be available on an instance of Build. But you clearly want class instance methods. Change definition of your module to this:
module Resume
require 'ostruct'
def self.personal
#personal
end
def self.education
#education
end
def self.included(base)
#personal = OpenStruct.new
#education = Array.new
set_personal
set_education
end
def self.set_personal
#personal.name = "Joe Blogs"
#personal.email_address = 'joe.blogs#gmail.com'
#personal.phone_number = '5555 55 55 555'
end
def self.set_education
#education << %w{ School\ 1 Description\ 1 }
#education << %w{ School\ 2 Description\ 2 }
end
end
And it'll work
b = Build.new
b.build # >> Joe Blogs

Resources