I have set up my logger using a module with the following (simplified) version:
module Logging
class << self
def logger
end
def logger=(logger)
#logger = logger
end
end
end
I would like to do something like the following (it didn't work):
Get the name of the currently executing method
def test_method
return __callee__
end
This way without the above, add logger.progname = self.test_method
So it should look like this:
module Logging
class << self
def logger
return #logger if #logger
#logger.progname = self.test_method
end
def logger=(logger)
#logger = logger
end
end
end
The end goal should be such that, with method "MyMethod":
def my_method
logger.debug "hi"
end
I get => "my_method: "hi"
Related
I have created logger object in "spec/logger.rb" and returned it.
I have created logger object in "spec/test1.rb" in describe method and can use it. it is working in same directory. If i need to use logger by creating logger object or by including file in different directory with and without class, then is there any way to do it ?
spec/logger.rb
class Logging
def initialize_logger()
log = Logger.new("test.log")
return log
end
end
spec/test1.rb
require 'java'
require 'rspec'
require 'logger'
require "logger.rb"
class SingleTest
describe 'Verify Single Test' do
# create logger object
logger_object = Logging.new
logger = logger_object.initialize_logger()
logger.info 'Checking Logger'
it 'Verify Single Test for logger is present' do
logger.info 'Creating logger'
end
end
end
lib/test_lib1.rb
require 'httparty'
require '../utility/logger.rb'
#logger_object = Logging.new
#logger = #logger_object.initialize_logger()
#logger.info 'Checking Logger'
def first()
#logger.info "first logger in lib"
end
def perform_url_request()
#logger.info "URL"
#logger.info "Request Method"
end
lib/test_lib2.rb
require '../utility/logger.rb'
class TestFile
def initialize()
#logger_object = Logging.new
#logger = #logger_object.initialize_logger()
#logger.info "I am intialize"
end
def test_method()
#logger.info "******"
end
end
I currently have a logging setup that looks like this:
require 'active_support'
require 'singleton'
class Worker
def initialize(logger)
#logger = logger
end
def perform
#logger.with_context(["tag1"], "Started work")
Provider.say_hello
end
end
class Vlogger
include Singleton
attr_accessor :logger
def logger
#logger ||= ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
end
def with_context(tags, msg)
#logger.tagged(*tags) { #logger.info msg }
end
end
class Provider
def self.logger
Vlogger.instance
end
def self.say_hello
logger.with_context(["tag2"], "hello")
end
end
v = Vlogger.instance
v.logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
Worker.new(v).perform
My objective with this is to avoid having to pass around the #logger instance variable found in my Worker class. Ideally, I want the Vlogger class to be available to all classes. I'm finding that I'm having to use this pattern quite a bit in my code:
def self.logger
Vlogger.instance
end
Is there a better way to do this? Is my OO design pattern here inherently flawed? Ideally I'm trying to avoid passing around objects when really the object has no state and it should be available to various classes without introducing class or instance methods. Thoughts?
There has to be a better way to do this:
require 'log4r'
class PaddedLogger
attr_accessor :logger, :padding
def initialize(args)
#logger = Log4r::Logger.new args[:name]
#padding = args[:padding]
end
def debug(system, message)
system = "[#{system}]"
#logger.debug "#{system.ljust(#padding)}#{message}"
end
def info(system, message)
system = "[#{system}]"
#logger.info "#{system.ljust(#padding)}#{message}"
end
def warn(system, message)
system = "[#{system}]"
#logger.warn "#{system.ljust(#padding)}#{message}"
end
def error(system, message)
system = "[#{system}]"
#logger.error "#{system.ljust(#padding + 3)}#{message}"
end
def fatal(system, message)
system = "[#{system}]"
#logger.fatal "#{system.ljust(#padding + 3)}#{message}"
end
end
It's just a wrapping class to help me get my log4r logs appearing in a specific format, and it works exactly as I need it too, but surely Ruby's meta-programming magic can simplify this so I don't have to constantly repeat myself.
Something like this maybe?
require 'log4r'
class PaddedLogger
attr_accessor :logger, :padding
def initialize(args)
#logger = Log4r::Logger.new args[:name]
#padding = args[:padding]
end
[:debug, :info, :warn, :error, :fatal].each do |reason|
define_method reason do |system, message|
system = "[#{system}]"
#logger.send reason, "#{system.ljust(#padding)}#{message}"
end
end
end
And while you're at it, why not collapse the two lines in the define_method block to one?
#logger.send reason, "[#{system}]".ljust(#padding) + message
Here is and alternative solution using method_missing:
class TestClass
##methods = [:upcase, :other_methods_to_catch]
def initialize
#message = 'hello'
end
def method_missing name, *args
super unless ##methods.include? name
#message.send(name, *args)
end
end
x = TestClass.new
p x.upcase #=> HELLO
My expectations are in the comments.
require 'logger'
module Logging
attr_accessor :logger
def logger
return #logger if #logger # allow items to have own loggers
##logger ||= Logger.new(STDERR)
puts "Instance Class REF ID#{##logger.__id__}"
puts "Class ID #{self.class.logger.__id__}"
##logger
end
module ClassMethods
def logger= logger
#logger = logger
end
def logger
#logger ||= Logger.new(STDERR)
puts "Class Instance REF ID #{#logger.__id__}"
#logger
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
class Test
include Logging
def wow
logger.info 'wow'
end
end
t = Test.new
# should be the same
puts "Loggers are #{t.logger.__id__ == Test.logger.__id__ ? '' : 'not '}the same"
Test.logger = Logger.new('/dev/null')
# should still be the same
puts "Loggers are #{t.logger.__id__ == Test.logger.__id__ ? '' : 'not '}the same"
lg = Test.logger.__id__
t.logger = Logger.new(STDERR)
# class logger should be same
puts "Class Logger is #{Test.logger.__id__ == lg ? 'still the' : 'not'} same"
# class and instance logger should be different
puts "Loggers are #{t.logger.__id__ == Test.logger.__id__ ? '' : 'not '}the same"
When executed:
➜ sandbox irb
1.9.3-p392 :001 > load 'test_l.rb'
Instance Class REF ID70203753590760
Class Instance REF ID 70203753590500
Class ID 70203753590500
Class Instance REF ID 70203753590500
Loggers are not the same # I expected to be same... :(
Instance Class REF ID70203753590760
Class Instance REF ID 70203753590000
Class ID 70203753590000
Class Instance REF ID 70203753590000
Loggers are not the same # I expected to be same... :(
Class Instance REF ID 70203753590000
Class Instance REF ID 70203753590000
Class Logger is still the same
Class Instance REF ID 70203753590000
Loggers are not the same
I've deliberately forgotten how to use ## variables because they are so confusing and rarely needed.
Instead, consider only using instance variables, but delegate up to the class level if needed:
module Logging
attr_writer :logger
def logger
defined?(#logger) ? #logger : self.class.logger
end
module ClassMethods
def logger=(logger)
#logger = logger
end
def logger
#logger ||= Logger.new(STDERR)
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
class Test
include Logging
# ...
end
I need to access the config variables from inside another class of a module.
In test.rb, how can I get the config values from client.rb? #config gives me an uninitialized var. It's in the same module but a different class.
Is the best bet to create a new instance of config? If so how do I get the argument passed in through run.rb?
Or, am I just structuring this all wrong or should I be using attr_accessor?
client.rb
module Cli
class Client
def initialize(config_file)
#config_file = config_file
#debug = false
end
def config
#config ||= Config.new(#config_file)
end
def startitup
Cli::Easy.test
end
end
end
config.rb
module Cli
class Config
def initialize(config_path)
#config_path = config_path
#config = {}
load
end
def load
begin
#config = YAML.load_file(#config_path)
rescue
nil
end
end
end
end
test.rb
module Cli
class Easy
def self.test
puts #config
end
end
end
run.rb
client = Cli::Client.new("path/to/my/config.yaml")
client.startitup
#config is a instance variable, if you want get it from outside you need to provide accessor, and give to Easy class self object.
client.rb:
attr_reader :config
#...
def startitup
Cli::Easy.test(self)
end
test.rb
def self.test(klass)
puts klass.config
end
If you use ##config, then you can acces to this variable without giving a self object, with class_variable_get.
class Lol
##lold = 0
def initialize(a)
##lold = a
end
end
x = Lol.new(4)
puts Lol.class_variable_get("##lold")
I recommend to you read metaprogramming ruby book.