I would like:
module MyLog
def log
unless #log
#log = Logger.new(log_path)
#log.formatter = proc do |severity, datetime, progname, msg|
"#{datetime} #{msg}\n"
end
end
#log
end
end
To be reused between other classes like this:
Class A
def self.log_path; 'log/a.log' end
def log_path; 'log/a.log' end
include MyLog
extend MyLog
def some_method
log.debug 'some thing'
end
def self.some_class_method
log.debug 'in a class method'
end
end
Is there a shorter way than those four lines at start of class A?
Another thing
I would like to log by batches:
def expire
expired_ids = []
failed_ids = []
all.each do |event|
if event.expire # saves record
expired_ids << event.id
else
failed_ids << event.id
end
end
log.debug "These ids were expired: #{ expired_ids }"
log.debug "These ids failed to expire: #{ failed_ids }"
end
Is there a way I can do this cleanly? Separating logging from method logic?
This is what I've been doing recentrly when faced with problems like yours:
class A
class << self
include MyLog
def log_path
'log/a.log'
end
end
delegate :log_path, :log, to: "self.class"
end
Otherwise, choosing the best programing approach depends on your situation, on how often you are going to reuse the script, how many times will you have to refactor it during its lifetime, and so on. But in any case, please, don't try to save the lines of code, try to save the trouble to yourself when you are reading the code next time after yourself.
As for your second question, the main dilemma you are facing is, whether it is worth bother for you to introduce Events class:
class Events < Array
def foobar
each_with_object [[], []] do |event, (expired_ids, failed_ids)|
( event.expire ? expired_ids : failed_ids ) << event.id
end
end
end
And then when the time comes:
def expire
expired_ids, failed_ids = Events[ all ].foobar
log.debug "These ids were expired: #{ expired_ids }"
log.debug "These ids failed to expire: #{ failed_ids }"
end
You can use the included hook to automatically define class methods and instance methods:
module MyLog
def self.included(base)
base.extend(Methods)
base.send(:include, Methods)
end
module Methods
def log_path; 'log/a.log' end
def log
unless #log
#log = Logger.new(log_path)
#log.formatter = proc do |severity, datetime, progname, msg|
"#{datetime} #{msg}\n"
end
end
#log
end
end
end
Like this, both class and instance methods will be automatically defined once the module gets included.
For your second problem, use partition and return the values, then log them:
def expire
all.partition(&:expire)
end
Then, where you call expire, you can log the return values:
def call_something
expired, failed = expire
log.debug "These ids were expired: #{expired.map(&:id)}"
log.debug "These ids failed to expire: #{failed.map(&:id)}"
end
If your log path is always supposed to be log/<lower_case_class>.log, you can implement that in your module and you should be fine. When the module's methods are executed, self will still be the object the method was called on, so you can say self.class and get A not your module name.
Check out the accepted answer on this question for how to add methods to both your class as well as your objects of that class
Why 'self' method of module cannot become a singleton method of class?
You can use Enumerable#partition to break up your "all" (you didn't actually say where that came from in your code you posted)
expired, failed = all.partition(&:expire)
Related
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
I am building a DSL and have this module
module EDAApiBuilder
module Client
attr_accessor :api_client, :endpoint, :url
def api_client(api_name)
#apis ||= {}
raise ArgumentError.new('API name already exists.') if #apis.has_key?(api_name)
#api_client = api_name
#apis[#api_client] = {}
yield(self) if block_given?
end
def fetch_client(api_name)
#apis[api_name]
end
def endpoint(endpoint_name)
raise ArgumentError.new("Endpoint #{endpoint_name} already exists for #{#api_client} API client.") if fetch_client(#api_client).has_key?(endpoint_name)
#endpoint = endpoint_name
#apis[#api_client][#endpoint] = {}
yield(self) if block_given?
end
def url=(endpoint_url)
fetch_client(#api_client)[#endpoint]['url'] = endpoint_url
end
end
end
so that I have tests like
context 'errors' do
it 'raises an ArgumentError when trying to create an already existent API client' do
expect {
obj = MixinTester.new
obj.api_client('google')
obj.api_client('google')
}.to raise_error(ArgumentError,'API name already exists.')
end
it 'raises an ArgumentError when trying to create a repeated endpoint for the same API client' do
expect {
obj = MixinTester.new
obj.api_client('google') do |apic|
apic.endpoint('test1')
apic.endpoint('test1')
end
}.to raise_error(ArgumentError,"Endpoint test1 already exists for google API client.")
end
end
I would rather have #api_clientwritten as an assignment block
def api_client=(api_name)
so that I could write
obj = MixinTester.new
obj.api_client = 'google' do |apic| # <=== Notice the difference here
apic.endpoint('test1')
apic.endpoint('test1')
end
because I think this notation (with assignment) is more meaningful. But then, when I run my tests this way I just get an error saying that the keyworkd_do is unexpected in this case.
It seems to me that the definition of an assignment block is syntactic sugar which won't contemplate blocks.
Is this correct? Does anyone have some information about this?
By the way: MixinTester is just a class for testing, defined in my spec/spec_helper.rb as
class MixinTester
include EDAApiBuilder::Client
end
SyntaxError
It seems to me that the definition of an assignment [method] is syntactic
sugar which won't contemplate blocks.
It seems you're right. It looks like no method with = can accept a block, even with the normal method call and no syntactic sugar :
class MixinTester
def name=(name,&block)
end
def set_name(name, &block)
end
end
obj = MixinTester.new
obj.set_name('test') do |x|
puts x
end
obj.name=('test') do |x| # <- syntax error, unexpected keyword_do, expecting end-of-input
puts x
end
Alternative
Hash parameter
An alternative could be written with a Hash :
class MixinTester
def api(params, &block)
block.call(params)
end
end
obj = MixinTester.new
obj.api client: 'google' do |apic|
puts apic
end
#=> {:client=>"google"}
You could adjust the method name and hash parameters to taste.
Parameter with block
If the block belongs to the method parameter, and not the setter method, the syntax is accepted :
def google(&block)
puts "Instantiate Google API"
block.call("custom apic object")
end
class MixinTester
attr_writer :api_client
end
obj = MixinTester.new
obj.api_client = google do |apic|
puts apic
end
# =>
# Instantiate Google API
# custom apic object
It looks weird, but it's pretty close to what you wanted to achieve.
Heres what I have/want:
module Observable
def observers; #observers; end
def trigger(event, *args)
good = true
return good unless (#observers ||= {})[event]
#obersvers[event].each { |e| good = false and break unless e.call(self, args) }
good
end
def on(event, &block)
#obersvers ||= {}
#obersvers[event] ||= []
#observers[event] << block
end
end
class Item < Thing
include Observable
def pickup(pickuper)
return unless trigger(:before_pick_up, pickuper)
pickuper.add_to_pocket self
trigger(:after_pick_up, pickuper)
end
def drop(droper)
return unless trigger(:before_drop, droper)
droper.remove_from_pocket self
trigger(:after_drop, droper)
end
# Lots of other methods
end
# How it all should work
Item.new.on(:before_pickup) do |item, pickuper|
puts "Hey #{pickuper} thats my #{item}"
return false # The pickuper never picks up the object
end
While starting on trying to create a game in Ruby, I thought it would be great if it could be based all around Observers and Events. The problem is have to write all of these triggers seems to be a waste, as it seems like a lot of duplicated code. I feel there must be some meta programming method out there to wrap methods with functionality.
Ideal Sceanrio:
class CustomBaseObject
class << self
### Replace with correct meta magic
def public_method_called(name, *args, &block)
return unless trigger(:before_+name.to_sym, args)
yield block
trigger(:after_+name.to_sym, args)
end
###
end
end
And then I have all of my object inherit from this Class.
I'm still new to Ruby's more advanced meta programming subjects, so any knowledge about this type of thing would be awesome.
There are a several ways to do it with the help of metaprogramming magic. For example, you can define a method like this:
def override_public_methods(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
end
override_public_methods(CustomBaseObject)
foo = CustomBaseObject.new
foo.test(2) { puts 'Block!' }
# => Foo
Test: 2
Block!
Bar
In this case, you figure out all the required methods defined in the class by using instance_methods and then override them.
Another way is to use so-called 'hook' methods:
module Overrideable
def self.included(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
include Overrideable
end
The included hook, defined in this module, is called when you include that module. This requires that you include the module at the end of the class definition, because included should know about all the already defined methods. I think it's rather ugly :)
I'm not sure that's the right title for this question, but I don't know how else to ask it. I have classes that need to be registered globally so they can be called later. I have most of it working except for a very important part. When the child inherits from the parent class, it registers a new instance, but when the on_message class method is called, I can't figure out how to set the instance variables that I need.
class MyExtension < ExtensionBase
on_message '/join (.+)' do |username|
# this will be a callback function used later
end
end
class ExtensionBase
def self.inherited(child)
MainAppModule.registered_extensions << child.new
end
def self.on_message(string, &block)
# these need to be set on child instance
#regex = Regexp.new(string)
#on_message_callback = block
end
def exec(message)
args = #regex.match(message).captures
#on_message_callback.call(args)
end
end
# somewhere else in the code, I find the class that I need...
MainAppModule.registered_extensions.each do |child|
puts child.regex.inspect # this is nil and I dont want it to be
if message =~ child.regex
return child.exec(message)
end
end
How can I design this so that the #regex will be set so I can access it within the loop?
I finally found a solution that works, and I have added now the whole code that is executable. Just store the code e.g. in file callexample.rb and call it by ruby callexample.rb
The main difference of my solution to the question is that the call to on_message now creates the instance with the relevant arguments and registers the created instance. Therefore I have deleted the method inherited because I don't need it any more.
I have added some puts statements to demonstrate in which order the code works.
class MainAppModule ## Added class
##registered_extensions = []
def self.registered_extensions; ##registered_extensions; end
end
class ExtensionBase
attr_reader :regex
def self.on_message(string, &block)
MainAppModule.registered_extensions << self.new(string, block)
end
def initialize(string, block)
#regex = Regexp.new(string)
#on_message_callback = block
end
def exec(message)
args = #regex.match(message).captures
#on_message_callback.call(args)
end
end
class MyExtension < ExtensionBase
on_message '/join (.+)' do |username|
# this will be a callback function used later
puts "Callback of #{self} called."
"returnvalue"
end
end
# somewhere else in the code, I find the class that I need...
MainAppModule.registered_extensions.each do |child|
puts "Value of regex: #{child.regex}" # this is no more nil
message = '/join something'
if message =~ child.regex
puts "On match evalue 'child.exec(message)' to: #{child.exec(message)}"
end
end
I found one source which successfully overrode Time.strftime like this:
class Time
alias :old_strftime :strftime
def strftime
#do something
old_strftime
end
end
The trouble is, strftime is an instance method. I need to override Time.now - a class method - in such away that any caller gets my new method, while the new method still calls the original .now method. I've looked at alias_method and have met with no success.
This is kinda hard to get your head around sometimes, but you need to open the "eigenclass" which is the singleton associated with a specific class object. the syntax for this is class << self do...end.
class Time
alias :old_strftime :strftime
def strftime
puts "got here"
old_strftime
end
end
class Time
class << self
alias :old_now :now
def now
puts "got here too"
old_now
end
end
end
t = Time.now
puts t.strftime
Class methods are just methods. I highly recommend against this, but you have two equivalent choices:
class Time
class << self
alias_method :old_time_now, :now
def now
my_now = old_time_now
# new code
my_now
end
end
end
class << Time
alias_method :old_time_now, :now
def now
my_now = old_time_now
# new code
my_now
end
end
If the you need to override it for testing purposes (the reason I normally want to override Time.now), Ruby mocking/stubbing frameworks will do this for you easily. For instance, with RSpec (which uses flexmock):
Time.stub!(:now).and_return(Time.mktime(1970,1,1))
By the way, I highly recommend avoiding the need to stub out Time.now by giving your classes an overrideable clock:
class Foo
def initialize(clock=Time)
#clock = clock
end
def do_something
time = #clock.now
# ...
end
end
I've been trying to figure out how to override an instance method using modules.
module Mo
def self.included(base)
base.instance_eval do
alias :old_time_now :now
def now
my_now = old_time_now
puts 'overrided now'
# new code
my_now
end
end
end
end
Time.send(:include, Mo) unless Time.include?(Mo)
> Time.now
overrided now
=> Mon Aug 02 23:12:31 -0500 2010