Using a singleton class in a custom logger - ruby

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?

Related

why instantiate a class method in ruby?

What is the idea behind creating a new instance of a method inside the class << self construct?
I understand methods are put under the class << self block to make them class methods but what does it mean to create a new instance of the method itself?
class foo
class << self
def bar(param)
new.bar(some_param)
end
end
end
I think what your trying to describe is a convenience method:
class FooService
def initialize
#bar= Bar.new
end
# this does the actual work
def call
results = #bar.do_some_work
results.each do
# ...
end
end
# this is just a convenient wrapper
def self.call
new.call
end
end
This lets you call the FooService.call class method for instead of manually instantiating the class with FooService.new.call. It does not really look like that much from this simple example but its really useful for abstracting away object initialization in things like service objects or to combine initializer arguments with method arguments.
class ApiClient
def initialize(api_key)
#api_key = api_key
end
def get(path)
# ...
end
def self.get(path, api_key: ENV['API_KEY'])
new(api_key).call(path)
end
end
ApiClient.get('foo')

How to best abstract class variables in Ruby

I've got a class such as
# this has been simplified for the example
class MyClass
##private_attributes = []
def self.private_attributes(*args)
##private_attributes = args
end
def private_attributes
#private_attributes ||= ##private_attributes
end
end
It works great. I've this ##private_attributes set at class level which's then used at instance level in multiple ways.
I want to abstract this logic somewhere else to simplify my class, something like that
class MyClass
include PrivateAttributes
end
When I create the module PrivateAttributes, however I shape it, the ##private_attributes isn't understood at MyClass level.
I tried many things but here's the latest code attempt
module PrivateAttributes
include ProcessAttributes
def self.included(base)
base.extend(ClassMethods)
base.include(InstanceMethods)
end
module ClassMethods
##private_attributes = []
def private_attributes(*args)
##private_attributes = args
end
end
module InstanceMethods
def private_attributes
#private_attributes ||= process_attributes_from(##private_attributes)
end
def private_attributes?
instance_options[:scope] == :private
end
end
end
It crashes with this error
NameError:
uninitialized class variable ##private_attributes in PrivateAttributes::InstanceMethods
Did you mean? private_constant
In short, the ##private_attributes isn't transferred throughout the code, but looks like it stays at the module level.
What's the best way to abstract this logic from my original class ?
Working solution
An easy workaround is to use mattr_accessor on the class level or anything similar to communicate our data around. I preferred to write down my own methods in this case:
module PrivateAttributes
include ProcessAttributes
def self.included(base)
base.extend(ClassMethods)
base.include(InstanceMethods)
end
module ClassMethods
##private_attributes_memory = []
def private_attributes(*args)
##private_attributes_memory = args
end
def private_attributes_memory
##private_attributes_memory
end
end
module InstanceMethods
def private_attributes
#private_attributes ||= process_attributes_from private_attributes_memory
end
# you can add diverse methods here
# which will be used in MyClass once included
private
def private_attributes_memory
self.class.private_attributes_memory
end
end
end

How could I implement something like Rails' before_initialize/before_new in plain Ruby?

In Rails we can define a class like:
class Test < ActiveRecord::Base
before_initialize :method
end
and when calling Test.new, method() will be called on the instance. I'm trying to learn more about Ruby and class methods like this, but I'm having trouble trying to implement this in plain Ruby.
Here's what I have so far:
class LameAR
def self.before_initialize(*args, &block)
# somehow store the symbols or block to be called on init
end
def new(*args)
## Call methods/blocks here
super(*args)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end
I'm trying to figure out where to store the blocks in self.before_initialize. I originally tried an instance variable like #before_init_methods, but that instance variable wouldn't exist in memory at that point, so I couldn't store or retrieve from it. I'm not sure how/where could I store these blocks/procs/symbols during the class definition, to later be called inside of new.
How could I implement this? (Either having before_initialize take a block/proc/list of symbols, I don't mind at this point, just trying to understand the concept)
For a comprehensive description, you can always check the Rails source; it is itself implemented in 'plain Ruby', after all. (But it handles lots of edge cases, so it's not great for getting a quick overview.)
The quick version is:
module MyCallbacks
def self.included(klass)
klass.extend(ClassMethods) # we don't have ActiveSupport::Concern either
end
module ClassMethods
def initialize_callbacks
#callbacks ||= []
end
def before_initialize(&block)
initialize_callbacks << block
end
end
def initialize(*)
self.class.initialize_callbacks.each do |callback|
instance_eval(&callback)
end
super
end
end
class Tester
include MyCallbacks
before_initialize { puts "hello world" }
end
Tester.new
Left to the reader:
arguments
calling methods by name
inheritance
callbacks aborting a call and supplying the return value
"around" callbacks that wrap the original invocation
conditional callbacks (:if / :unless)
subclasses selectively overriding/skipping callbacks
inserting new callbacks elsewhere in the sequence
... but eliding all of those is what [hopefully] makes this implementation more approachable.
One way would be by overriding Class#new:
class LameAR
def self.before_initialize(*symbols_or_callables, &block)
#before_init_methods ||= []
#before_init_methods.concat(symbols_or_callables)
#before_init_methods << block if block
nil
end
def self.new(*args, &block)
obj = allocate
#before_init_methods.each do |symbol_or_callable|
if symbol_or_callable.is_a?(Symbol)
obj.public_send(symbol_or_callable)
else
symbol_or_callable.(obj)
end
end
obj.__send__(:initialize, *args, &block)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end

How to drop dependency on class method

I have the 3 following classes:
Base
module TitleSource
class Base
include Comparable
attr_accessor :company
attr_accessor :priority
attr_accessor :target_title
def initialize(args={})
self.company = args[:company]
self.priority = args[:priority]
self.target_title = args[:target_title]
end
def create_contact_source
raise NotImplementedError
end
def <=>(other)
fetch_value <=> other.fetch_value
end
protected def fetch_value
value
end
private def value
raise NotImplementedError
end
end
end
UnmatchedTitle
module TitleSource
class UnmatchedTitle < Base
def create_contact_source
::ContractorUi::ContactSource.generate_from_unmatched_title(self)
end
private def value
100
end
end
end
IncompleteContact
module TitleSource
class IncompleteContact < Base
attr_accessor :target_title_name
attr_accessor :contact
def initialize(args={})
super
self.target_title_name = args[:target_title_name]
self.contact = args[:contact]
end
def create_contact_source
::ContractorUi::ContactSource.generate_from_incomplete_contact(self)
end
private def value
10
end
end
end
I'm currently reading POODR and came up with this design, which so far served me well.
However, for didactical reasons, I would like to know how I can remove the dependency on ::ContractorUi::ContactSource if it's worth it and if it should be done.
I don't like the idea of passing it in with the constructor because the entire purpose of TitleSource module is to actually generate a ContactSource, but would like to hear from more experienced people. Reading the book (and some on-the-field experience) allowed me to understand how much decoupling is important
I can confirm that having a single dependency is not a big issue in this case. Based on POODR, this code can react to changes well enough, so keeping the class dependency in this case is fine, expecially considering it's the main purpose of the entire class, to generate an instance of ContactSource

Can i extend a Ruby class to behave like OpenStruct dynamically?

I have a Ruby class which includes a module. I want the including class to behave like OpenStruct. How do i achieve this without explicitly inheriting from OpenStruct?
class Book
include MyModule
end
module MyModule
def self.included(klass)
# Make including class behave like OpenStruct
end
end
instead of
class Book < OpenStruct
include MyModule
end
You could delegate all methods your class does not handle to an OpenStruct:
require 'ostruct'
class Test_OS
def initialize
#source = OpenStruct.new
end
def method_missing(method, *args, &block)
#source.send(method, *args, &block)
end
def own_method
puts "Hi."
end
end
t = Test_OS.new
t.foo = 1
p t.foo #=> 1
t.own_method #=> Hi.
Since OpenStruct is not a module, and you cannot cause modules to inherit from classes, you will need to write your own module that uses method_missing to implement the OpenStruct capabilities. Thankfully, this is very little work. The entire OpenStruct class is only about 80 lines of code, and most of that you probably don't even fully need.
Is there a strong reason you wanted to rely on OpenStruct itself?

Resources