I have a class whose initialize method gets data from a remote source, which it then uses to set its object attributes.
I'm expecting this class to be heavily used, possibly hundreds of times within a program. I'd like to reduce the overhead network calls by caching objects that have already been instantiated, which I'd then return to the user when they ask to instantiate that object again.
For that, I've been considering overriding the new method for this class. It would check to see if the cached object is available, and if so, return that reference. Otherwise, it would call the regular new method for the object, which would allocate memory and call initialize like usual.
If I override the new() method in a class, is it possible to call the original new method via something like super()?
Yes, super without parameters will call the parent method with the same parameters passed to the new method.
Or, you can cherry-pick parameters by adding them to super(p1, p2, ...).
Regarding what you want to do by remembering previous invocations, that's called "memoizing" and there is at least one memoize gem for it, or, you can write your own, depending on your needs.
It's pretty easy to do using a hash. Use the parameters used when invoking the new method as the key, and the value is the instance you want to return. Without examples of your code it's hard to come up with an example that's custom-fit, but this is a simple, untested, version:
def initialize(*args)
#memoizer ||= {}
return #memoizer[args] if #memoizer[args]
# do what you will with the args in this initializer,
# then create a new instance for the future.
#memoizer[args] = super(args)
end
The idea is that #memoizer remembers the "arity" of the call and automatically returns the result of similar calls. If that set of parameters haven't been seen before it'll compute and create the new instance and then return it.
This breaks down when the result could change with the same set of input parameters. You don't want to memoize database calls or anything using random or a date/time value, or that returns something outside your control. Trying to use it in those cases will return stale or wrong values unless you design in a method to sweep through and revalidate the #memoizer values periodically.
Also, there is no flushing mechanism, so #memoizer will only grow in size, and could possibly consume all available space given enough different input values. To deal with that you could also have a timestamp for when the value was added to #memoizer, and periodically purge entries that exceed a given lifetime. Only "live" values would remain in the hash then.
Useful information is at: "Memoize Techniques in Ruby and Rails".
either via super or via an alias_method chain
super is better if you sub-class
Check this: When monkey patching a method, can you call the overridden method from the new implementation?
Related
I am trying to store an instance method as a variable so I can pass it into a way to store logic on a menu I am building.
For example, I want my our_startup.prompt method to be stored in my start_game_ui.logic array. I am trying to do this using a start_game_ui.set_logic function which shovels arguments into the logic array. I would like to shovel 6 methods into the logic array so that when I run my final function to puts and receive input 1 - 6. If the user chooses 1 it should run the function in the first element of the array.
our_startup = Startup.new("No Name")
## START GAME UI ##
start_game_ui = UI.new("start_game_ui")
start_game_ui.menu_items = ["[1] - Yes", "[2] - Generate Another"]
##set up logic##
method1_test = our_startup.set_name("test")
rerun = start_game_ui.prompt
start_game_ui.set_logic(method1_test, rerun)
When I run this, my start_game_ui.prompt method will run. I want to store the start_game_ui.prompt method in that variable rerun without having the method run.
Once I run my final method and choose 1 it should return "test". However when I run this it runs start_game_ui.prompt and I don't want it to.
I hope you can understand what I mean. I have 2 classes UI and Startup if you couldn't already tell.
PLEASE DO NOT TELL ME I CAN DO method(:something) this does not help as it is an instance method being called by another instance. Unless you can tell me how to get that symbol to correspond with the correct method inside the instance. I've tried method(our_startup.prompt) and it does not work.
PLEASE DO NOT TELL ME I CAN DO method(:something) this does not help as it is an instance method being called by another instance.
I would really like to not tell you that, but unfortunately, that is the correct answer:
rerun = start_game_ui.method(:prompt)
# Then, later when you need to call it:
result = rerun.()
Not using Object#method, as you require in your question, leads to significant added complexity, e.g. by passing around the receiver and the name of the method separately:
rerun_receiver_and_message = [start_game_ui, :prompt]
# Then, later when you need to call it:
result = rerun_receiver_and_message.first.public_send(rerun_receiver_and_message.last)
The only thing you can store in variables and pass as arguments are objects.
Procs and Lambdas are objects, so you should be able to do something like
rerun = -> {start_game_ui.prompt}
start_game_ui.set_logic(method1_test, rerun)
rerun is storing the call to the method, not the results of the method
At the point where you need to execute the method, you would do
rerun.call
Note that Procs and Lambdas can also specify arguments, that you can supply at the time of the call.
I'm not sure if this helps with your problem, but hope it does.
If you'd like to get the instance method from inside the instance of your object, then you can use this: our_startup.method(:prompt)
I don't really understand what your end goal is, so I'm going to suggest you read up a little further on Ruby's object model and methods and provide you some guidance instead.
The method method returns an instance of Method (an object). If this is confusing, read it more slowly and check out the Method documentation. Whether the method being referenced by the argument is an instance method or not is irrelevant to the behavior of the method method.
Directly addressing something you said in the question: using method(:foo) does not call the referenced method (e.g. foo).
You can unbind a method from the source receiver (creating an UnboundMethod that can't be called) and rebind it to another receiver if you need to:
my_method_instance = some_string.method(:to_s)
# Now I can call `some_string.to_s` like so:
my_method_instance.to_s
# This isn't very useful for `to_s`, but it could be in other situations
method_instance = SomeModule::SomeHelper.method(:parse_html)
array_of_html_docs = array_of_strings.map(&method_instance)
# And you can unbind the method from the original receiver:
my_unbound_method_instance = my_method_instance.unbind
# And rebind it elsewhere
my_unbound_method_instance.bind(some_other_receiver)
my_unbound_method_instance.call(args) # receiver is `some_other_receiver` here
In order to debug the methods or whatever that consume a lot of memory or create many objects, I need to measure the amount of variables by the memory consumed, etc., of every module, class or method, and then list them.
I want to know which method/class/module created a given object. I can write a parser, but I want to know if there is anything easier. Is that possible? I'm doing something like this:
ObjectSpace.each_object String do |obj|
method = get_parent_method_of(obj)
#....
end
This question may seem like a duplicate of this one but the accepted answer does not help with my problem.
Context
Since Rails 5 no longer supports directly manipulating sessions in controller tests (which now inherit from ActionDispatch::IntegrationTest), I am going down the dark path of mocking and stubbing.
I know that this is bad practice and there are better ways to test a controller (and I do understand their move to integration tests) but I don't want to run a full integration test and call multiple actions in a single test just to set a specific session variable.
Scenario
Mocking/stubbing a session variable is actually quite easy with Mocha:
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(:some_variable).returns("some value")
Problem is, Rails stores a lot of things inside the session (just do a session.inspect anywhere in one of your views) and stubbing the :[] method obviously prevents access to any of them (so session[:some_other_variable] in a test will no longer work).
The question
Is there a way to stub/mock the :[] method only when called with a specific parameter and leave all other calls unstubbed?
I would have hoped for something like
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(:some_variable).returns("some value")
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(anything).returns(original_value)
but I could not find a way to get it done.
By what I see, this is a feature not available in mocha
https://github.com/freerange/mocha/issues/334
I know this does exist in rspec-mock
https://github.com/rspec/rspec-mocks/blob/97c972be57f2c060a4a7fb8a3c5700a5ede693f0/spec/rspec/mocks/stub_implementation_spec.rb#L29
One hacky way that you an do it though, is to store the original session in an object, then mock that whenever a controller receives session, it returns another mock object, and in this you may either return a mocked velue, or delegate the call to the original session
class MySession
def initialize(original)
#original = original
end
def [](key)
if key == :mocked_key
2
else
original[key]
end
end
end
let!(original_session) { controller.send(:session) }
let(:my_session) { MySession.new(original_session) }
before do
controller.stubs(:session) { my_session }
end
Guess that mocha also allows you to do block mocking, so you don't need the class, but you need that original_session to be called
But I don't see a clean way
Is it better to use case/when things or the send method when dynamically calling methods based on user input? "better" based primarily on good coding practices.
input = gets.chomp
case input
when foo
foo
when bar
bar
end
versus
input = gets.chomp #Where hopefully the input would be 'foo' or 'bar'
send(input)
Your wording makes the question incredibly hard to read.
If I understood you correctly, you want to call methods based on user input. One alternative would be to check every possible value and call a method, the other - to use send directly.
First of all, notice that in your first example, you were calling method1 when the user entered foo. If you used send(input) you would have called foo instead. So they are not exactly the same.
You can achieve the same behavior by putting the input->method mapping in a hash like so:
dispatch = {foo: :method1, bar: :method2}
input = gets.chomp.to_sym
send(dispatch[input])
Another thing to note is that send in the original situation would call any method passed. You can instead whitelist the possible methods with the hash above and checking if such value exists:
send(dispatch[input]) if dispatch.key? input
Now to the question of when to use one or the other:
If you have 2, 3, 5 or so possibilities, prefer explicitly listing them. It will be faster, easier to read, easier to do static code analysis and so on.
If you have hundreds and thousands of different methods, prefer send. The costs outweigh the benefits of being DRY.
If the list of allowed methods is generated dynamically, you don't have a choice - use send. Examples:
You want to call methods to a given object and that object is different each time
You want to allow different methods depending on the user's permissions
You want to implement a REPL or some other awesome tool that has extremely dynamic needs
In general, don't use meta programming, unless there is significant gain or you don't have any other choice.
Unless you'd like your user to be able to call any method in the method lookup chain, including private methods which send can invoke, it probably makes sense for you to lock it down and only allow your users some methods.
If you don't specify an object to send to (like in your code above), Ruby will look at self for a method by that name and then use a normal method lookup. In other words self will be the first link in the method lookup chain. If you do specify an object, maybe an object that you create for that purpose for example, another option might be to use the methods like try or respond_to?.
input = gets.chomp
if defined?(input.to_sym)
send(input)
else
puts "No such thing!"
Do I need to explicitly initialize an object if an initialize method is included in class definition?
No, Ruby does not call initialize automatically.
The default implementation of Class#new looks a bit like this:
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
obj
end
end
[Actually, initialize is private by default so you need to use obj.send(:initialize, *args, &block).]
So, the default implementation of Class#new does call initialize, but it would be perfectly possible (albeit extremely stupid) to override or overwrite it with an implementation that does not.
So, it's not Ruby that calls initialize, it's Class#new. You may think that's splitting hairs, because Class#new is an integral part of Ruby, but the important thing here is: it's not some kind of language magic. It's a method like any other, and like any other method it can be overridden or overwritten to do something completely different.
And, of course, if you don't use new to create an object but instead do it manually with allocate, then initialize wouldn't be called either.
There are some cases where objects are created without calling initialize. E.g. when duping or cloneing, initialize_dup and initialize_clone are called instead of initialize (both of which, in turn, call initialize_copy). And when deserializing an object via, say, Marshal, its internal state is reconstructed directly (i.e. the instance variables are set reflectively) instead of through initialize.
Yes, it's called from new method, which you use to create objects.
It depends on your definition of "explicit". Usually you need to, even if there are no arguments:
object = MyClass.new(...)
In some cases there are factory methods that produce instances you can use, creating a form of implicit initialization:
object = MyClass.factory_method(...)
This would have the effect of calling MyObject.new internally.
There are some libraries which have rather unusual method signatures, like:
object = MyClass(...)
object = MyClass[...]
The effect is the same, as these might look odd but are just method calls.