How can I create an opbjet that's totally lazy by itself? I have a block, and I want to pass around (as a dependency) the "current value" (at call time) of the block instead of the value at dependency injection time.
I can't actually pass around a lambda because all the services expect an actual object, so they won't send :call to them, just access them.
This (oversimplified) example might clarify the situation:
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
print #current_time
end
end
class Injector
def current_time
# a lazy object that when accessed actually calls the lambda below
# every single time.
end
def current_time_lazy
-> { Time.now }
end
def instantiate(class_name)
# search for the class, look at the constructor and
# create an instance with the dependencies injected by
# name
# but to be simple
if class_name == "Timer"
Timer.new(current_time)
end
end
end
timer = Injector.new.instantiate("Timer")
timer.print_current_time # => some time
sleep 2
timer.print_current_time # => some *different* time
The actual situation implies passing around the current_user but depending on the situation the current user might change after those values are injected.
I would really appreciate any suggestion (even if for now I will carefully sort the dependency injection code so this doesn't happen, but I think it's pretty fragile)
This should help :
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
puts #current_time
end
end
class LazyMaker < BasicObject
def self.instantiate(class_name, lambada)
if class_name == 'Timer'
::Timer.new(new(class_name, lambada))
end
end
def initialize(class_name, lambada)
#lambada = lambada
end
def method_missing(method, *args)
#lambada.call.send(method, *args)
end
end
timer = LazyMaker.instantiate('Timer', -> { Time.now })
timer.print_current_time # some time
sleep 2
timer.print_current_time # some other time
I'm trying to use delegation to implement it, so that I can call the block first, get a new object and redirect the method call to it. Why this way ? Because basically, accessing an object to do something means to call a method on it. For instance, in print #current_time, it sends #current_time.to_s.
But since almost all objects will have a few methods inherited from standard base classes in Ruby like Object, LazyMaker also has methods like to_s. So I thought of making just the LazyMaker inherit from BasicObject, which is a blank class. So almost all of the methods get delegated.
But yeah, there might be another way to do this.
Related
EDIT: For those criticizing my intentions with replacing self, you are free to click the back button, continue developing with your own opinions, and leave me to develop with mine :)
I was wondering if there is a way to completely remove the object that self references and replace it with a new instance.
Example:
def refresh_from_server!
self = newly_fetched_object_from_server
end
I don't want to return the new object.
It seems like I would have to build my own copying interface and call self.copy_from(other_object) but maybe someone has a cool ruby bit to share that works better!
--EDIT
Since some people seem unclear on the question, I want instance.my_method! to completely replace instance with a new instance of that class
For example lets imagine we have a class
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
No, you can't replace self. You can only change some/all of its state, but the object reference will remain the same.
Why would you want to do this, anyway? If you just want to piggyback on your initialization logic (as it seems to me to be the case), some refactoring will help: just call a shared method from both places.
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
As already noted by others, self can't be replaced from enclosed instance. If replacement of instance with a new instance is required, it need to be done from outside, like in a class factory which registers its class instances.
Bellow is a simplest example using a delegator, demonstrating what I mean. SimpleDelegator represents a simple wrapper around Counter instance:
require 'delegate'
class Counter
attr_accessor :count
def initialize
#count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
Though the question is old, it is still visited. I will attempt to elaborate more on the "why" in "Why can't self be replaced in Ruby?".
usage of self in which context
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
There are various contexts in which self can be used. You question uses it in the context of an instance method, so I will focus on that.
E.g. this context:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
Note that there are other usages of self: e.g. where it reference the Class object in scope of a class definition. E.g.
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
the intended effect of replacing self
Below code a demonstration of what you expected / wished (as I understand it):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
But what about other references? E.g. assuming you wanted:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
If stated as such it gives a hint on the why not.
why we can't replace self
The short answer is that it is simply not something that the language / interpreter offers. As to the reasoning: in a way #matthewd answers that in this answer:
All ruby variable references are essentially pointers (but not
pointers-to-pointers), in C parlance.
You can mutate an object (assuming it's not immutable), and all
variables that reference it will thus be pointing at the same (now
mutated) object. But the only way to change which object a variable is
referring to is with direct assignment to that variable -- and each
variable is a separate reference; you can't alias a single reference
with two names.
In short: there may be other references to that object in variables that are not in the scope of the instance method. These cannot be manipulated by that instance method.
a way to achieve the intended effect
If you want this effect and only want to touch the code of Counter you might move all methods and state to an inner class Counter::Inner and make Counter behave like a decoupled reference. The only 'state' of Counter would be the reference to the Counter::Inner object and Counter can delegate all calls it receives to that reference in a method_missing method. In case of your refresh! you can replace the reference in Counter same as you now intent to replace self. All outside code will now use indirectly the new Counter:Inner instance.
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
#reference = Inner.new(*args)
end
def method_missing(method_id, *args)
#reference.send(method_id, *args)
end
def refresh!
#reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
Just one more answer for the archives :-) I hope this gives useful insights to future visitors.
I have a class Klass, and its constructor accepts an argument. We should be able to call methods on this object that are not defined in Klass.
We can chain multiple methods, but in the end, we have to use Klass#result to get the result like:
Klass.new(5).pred.pred.result
and the output here should be 3. I tried using method_missing in Klass and using send on the object's class, but that would have worked without the result method that I have to use. Can someone explain how this can be done with delegation?
You could do something like this:
class Klass
def initialize(number)
#number = number
end
def result
#number
end
def method_missing(method_name, *arguments, &block)
if #number.respond_to?(method_name)
#number = #number.method(method_name).call(*arguments, &block)
return self
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
# be sure to implement this...
end
end
puts Klass.new(5).pred.pred.result # => 3
But it's problematic. In this particular example, since #pred returns a new object (it doesn't modify the object it was called on), we have to reassign the instance variable to the result. It works for pred and other methods that return new Integers, but some methods on Integer don't return an Integer (e.g. Integer#even). In this case you'd get this sort of behavior:
puts Klass.new(4).even?.result # => true
Depending on your particular situation, that might be what you're after. Or, it might be that in your situation all methods the object being delegated to mutate that object, rather than return new instances of the object, in which case the reassignment isn't needed.
I don't think you can use Ruby's existing Delegator and SimpleDelegator constructs, because the only way you can chain the final #result call onto the end is if every delegated call returns the instance of Klass. Using those existing constructs would cause delegated calls to return their normal return values, and the chaining would then be on whatever objects those return values return. For example, using the above code, you'd see this behavior:
puts Klass.new(5).pred.pred.class # => "Klass"
Using SimpleDelegator, you'd see this behavior
require 'delegate'
class Klass2 < SimpleDelegator
# Klass2 methods...
end
puts Klass2.new(5).pred.pred.class # => "Fixnum"
Hope that helps.
I am trying to create a macro "has_accessor_for", that accepts a symbol which is used as a parameter for an internal object that it uses (the Accessorizer object). The problem I am having is, when multiple modules do the has_accessors_for, the parameter (scope) ends up being stuck on the last value it was assigned to.
I added a puts prior to the define_method, which shows that it's scope1, and then scope2... But inside the define_method, it's scope2 always. I am looking for a way to basically encapsulate that variable, so that when it the first module calls has_accessor_for, anytime my_wut is called, it will be bound to scope1... and anytime my_bleah is called, it will be bound to scope2. But as I said, right now, both my_bleah and my_wut are bound to scope2-- If I change the order of the includes in MyModel, then they will both be bound to scope1.
class Accessorizer
def initialize(record, scope)
#record = record
#scope = scope
end
def value_for(key)
#record.send key
end
end
module Magic
def has_accessors_for(scope)
accessors = {}
puts "initial: #{scope}"
define_method :get_value_for do |key|
puts "inside method #{scope}"
accessor.value_for key
end
define_method :accessor do
accessors[:scope] ||= Accessorizer.new(self, scope)
end
end
end
module SomeAccessor
extend Magic
has_accessors_for :scope1
def my_wut
get_value_for :wut
end
end
module SomeOtherAccessor
extend Magic
has_accessors_for :scope2
def my_bleah
get_value_for :bleah
end
end
class MyModel
include SomeAccessor
include SomeOtherAccessor
attr_accessor :wut, :bleah
end
m = MyModel.new
m.wut = 'wut'
m.bleah = 'bleah'
m.my_bleah
m.my_wut
output:
initial: scope1
initial: scope2
inside method scope2
inside method scope2
Short answer: the problem is not with the closures.
Long answer:
define_method :get_value_for do |key|
puts "inside method #{scope}"
accessor.value_for key
end
On a given class there can only be one method called get_value_for - the second definition will overwrite the first.
It doesn't matter so much because you're call accessor in both cases, however that method suffers from the same problem - you define it twice and so the second definition overwrites the first and you end up with only one Accessorizer object.
I think you'll need to rethink your design here.
I have a situation for Ruby, where an object is possibly necessary to be created, but it is not sure. And as the creation of the object might be costly I am not too eager creating it. I think this is a clear case for lazy loading. How can I define an object which is not created only when someone sends a message to it? The object would be created in a block. Is there a way for simple lazy loading/initialisation in Ruby? Are these things supported by some gems, which provide different solutions for various cases of lazy initialisation of objects? Thanks for your suggestions!
There are two ways.
The first is to let the caller handle lazy object creation. This is the simplest solution, and it is a very common pattern in Ruby code.
class ExpensiveObject
def initialize
# Expensive stuff here.
end
end
class Caller
def some_method
my_object.do_something
end
def my_object
# Expensive object is created when my_object is called. Subsequent calls
# will return the same object.
#my_object ||= ExpensiveObject.new
end
end
The second option is to let the object initialise itself lazily. We create a delegate object around our actual object to achieve this. This approach is a little more tricky and not recommended unless you have existing calling code that you can't modify, for example.
class ExpensiveObject # Delegate
class RealExpensiveObject # Actual object
def initialize
# Expensive stuff here.
end
# More methods...
end
def initialize(*args)
#init_args = args
end
def method_missing(method, *args)
# Delegate to expensive object. __object method will create the expensive
# object if necessary.
__object__.send(method, *args)
end
def __object__
#object ||= RealExpensiveObject.new(*#init_args)
end
end
# This will only create the wrapper object (cheap).
obj = ExpensiveObject.new
# Only when the first message is sent will the internal object be initialised.
obj.do_something
You could also use the stdlib delegate to build this on top of.
If you want to lazily evaluate pieces of code, use a proxy:
class LazyProxy
# blank slate... (use BasicObject in Ruby 1.9)
instance_methods.each do |method|
undef_method(method) unless method =~ /^__/
end
def initialize(&lazy_proxy_block)
#lazy_proxy_block = lazy_proxy_block
end
def method_missing(method, *args, &block)
#lazy_proxy_obj ||= #lazy_proxy_block.call # evaluate the real receiver
#lazy_proxy_obj.send(method, *args, &block) # delegate unknown methods to the real receiver
end
end
You then use it like this:
expensive_object = LazyProxy.new { ExpensiveObject.new }
expensive_object.do_something
You can use this code to do arbitrarily complex initialization of expensive stuff:
expensive_object = LazyProxy.new do
expensive_helper = ExpensiveHelper.new
do_really_expensive_stuff_with(expensive_helper)
ExpensiveObject.new(:using => expensive_helper)
end
expensive_object.do_something
How does it work? You instantiate a LazyProxy object that holds instructions on how to build some expensive object in a Proc. If you then call some method on the proxy object, it first instantiates the expensive object and then delegates the method call to it.
EDIT: I slightly changed the spec, to better match what I imagined this to do.
Well, I don't really want to fake C# attributes, I want to one-up-them and support AOP as well.
Given the program:
class Object
def Object.profile
# magic code here
end
end
class Foo
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
Foo.new.bar("test")
Foo.new.barbar("test")
puts Foo.get_comment(:snafu)
Desired output:
Foo.bar was called with param: b = "test"
test
Foo.bar call finished, duration was 1ms
test
This really should be fixed
Is there any way to achieve this?
I have a somewhat different approach:
class Object
def self.profile(method_name)
return_value = nil
time = Benchmark.measure do
return_value = yield
end
puts "#{method_name} finished in #{time.real}"
return_value
end
end
require "benchmark"
module Profiler
def method_added(name)
profile_method(name) if #method_profiled
super
end
def profile_method(method_name)
#method_profiled = nil
alias_method "unprofiled_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
name = "\#{self.class}##{method_name}"
msg = "\#{name} was called with \#{args.inspect}"
msg << " and a block" if block_given?
puts msg
Object.profile(name) { unprofiled_#{method_name}(*args, &blk) }
end
ruby_eval
end
def profile
#method_profiled = true
end
end
module Comment
def method_added(name)
comment_method(name) if #method_commented
super
end
def comment_method(method_name)
comment = #method_commented
#method_commented = nil
alias_method "uncommented_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
puts #{comment.inspect}
uncommented_#{method_name}(*args, &blk)
end
ruby_eval
end
def comment(text)
#method_commented = text
end
end
class Foo
extend Profiler
extend Comment
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
A few points about this solution:
I provided the additional methods via modules which could be extended into new classes as needed. This avoids polluting the global namespace for all modules.
I avoided using alias_method, since module includes allow AOP-style extensions (in this case, for method_added) without the need for aliasing.
I chose to use class_eval rather than define_method to define the new method in order to be able to support methods that take blocks. This also necessitated the use of alias_method.
Because I chose to support blocks, I also added a bit of text to the output in case the method takes a block.
There are ways to get the actual parameter names, which would be closer to your original output, but they don't really fit in a response here. You can check out merb-action-args, where we wrote some code that required getting the actual parameter names. It works in JRuby, Ruby 1.8.x, Ruby 1.9.1 (with a gem), and Ruby 1.9 trunk (natively).
The basic technique here is to store a class instance variable when profile or comment is called, which is then applied when a method is added. As in the previous solution, the method_added hook is used to track when the new method is added, but instead of removing the hook each time, the hook checks for an instance variable. The instance variable is removed after the AOP is applied, so it only applies once. If this same technique was used multiple time, it could be further abstracted.
In general, I tried to stick as close to your "spec" as possible, which is why I included the Object.profile snippet instead of implementing it inline.
Great question. This is my quick attempt at an implementation (I did not try to optimise the code). I took the liberty of adding the profile method to the
Module class. In this way it will be available in every class and module definition. It would be even better
to extract it into a module and mix it into the class Module whenever you need it.
I also didn't know if the point was to make the profile method behave like Ruby's public/protected/private keywords,
but I implemented it like that anyway. All methods defined after calling profile are profiled, until noprofile is called.
class Module
def profile
require "benchmark"
#profiled_methods ||= []
class << self
# Save any original method_added callback.
alias_method :__unprofiling_method_added, :method_added
# Create new callback.
def method_added(method)
# Possible infinite loop if we do not check if we already replaced this method.
unless #profiled_methods.include?(method)
#profiled_methods << method
unbound_method = instance_method(method)
define_method(method) do |*args|
puts "#{self.class}##{method} was called with params #{args.join(", ")}"
bench = Benchmark.measure do
unbound_method.bind(self).call(*args)
end
puts "#{self.class}##{method} finished in %.5fs" % bench.real
end
# Call the original callback too.
__unprofiling_method_added(method)
end
end
end
end
def noprofile # What's the opposite of profile?
class << self
# Remove profiling callback and restore previous one.
alias_method :method_added, :__unprofiling_method_added
end
end
end
You can now use it as follows:
class Foo
def self.method_added(method) # This still works.
puts "Method '#{method}' has been added to '#{self}'."
end
profile
def foo(arg1, arg2, arg3 = nil)
puts "> body of foo"
sleep 1
end
def bar(arg)
puts "> body of bar"
end
noprofile
def baz(arg)
puts "> body of baz"
end
end
Call the methods as you would normally:
foo = Foo.new
foo.foo(1, 2, 3)
foo.bar(2)
foo.baz(3)
And get benchmarked output (and the result of the original method_added callback just to show that it still works):
Method 'foo' has been added to 'Foo'.
Method 'bar' has been added to 'Foo'.
Method 'baz' has been added to 'Foo'.
Foo#foo was called with params 1, 2, 3
> body of foo
Foo#foo finished in 1.00018s
Foo#bar was called with params 2
> body of bar
Foo#bar finished in 0.00016s
> body of baz
One thing to note is that it is impossible to dynamically get the name of the arguments with Ruby meta-programming.
You'd have to parse the original Ruby file, which is certainly possible but a little more complex. See the parse_tree and ruby_parser
gems for details.
A fun improvement would be to be able to define this kind of behaviour with a class method in the Module class. It would be cool to be able to do something like:
class Module
method_wrapper :profile do |*arguments|
# Do something before calling method.
yield *arguments # Call original method.
# Do something afterwards.
end
end
I'll leave this meta-meta-programming exercise for another time. :-)