Reputation: 21791
I have a ruby method
def get_status(creds)
client = create_client(creds)
status = client.account_status
client.close_session
status
end
Usually, I optimize this kind of code by tap
or yield_self
, but here I can't find a nice way to optimize it.
The only solution I have come up:
def get_status(creds)
create_client(creds).yeild_self do |client|
[client, client.account_status]
end.yield_self do |client, status|
client.close_session
status
end
end
But it doesn't better than the original solution, is it?
Upvotes: 1
Views: 189
Reputation: 110685
One could write the following.
class Client
def account_status
"Overdrawn!"
end
def close_session
puts "It's closed"
end
end
def create_client(creds)
Client.new
end
def get_status(creds)
begin
client = create_client(creds)
client.account_status
ensure
client.close_session if client
end
end
get_status("Anything")
It's closed
#=> "Overdrawn!"
Do I prefer this to #1 in the question? No.
Do I prefer this to #2 in the question? Yes!
Do I prefer this to @max's answer? No.
I understand a finalizer could be created using the class method ObjectSpace::define_finalizer.
class Client
def initialize
ObjectSpace.define_finalizer(self, proc { puts "It's finalized!" })
end
def account_status
"Overdrawn!"
end
end
def create_client(creds)
Client.new
end
def get_status(creds)
create_client(creds).account_status
end
get_status("Anything")
#=> "Overdrawn!"
exit
It's finalized!
One must be careful when creating finalizers, as explained Here. I understand a technique sometimes used is to have finalizer
's proc reference class-level objects. See, for example, this article, @Amadan's comments below and @Matt's comments on the question. I am not advocating the use of a finalizer. I merely thought readers unfamiliar with finalizers (as I was before writing this) would find this useful.
Upvotes: 3
Reputation: 106882
I like your first version because it is short, easy to read, and easy to understand. I would not change it.
Nevertheless, an alternative version using tap
might look like this:
def get_status(creds)
client = create_client(creds)
client.account_status.tap { client.close_session }
end
Upvotes: 1
Reputation: 26768
Let's list the goal of the function:
I would consider this a "temporary connection", and that leads me to think it could be refactored to a separate method.
Reasoning: The get_status
method is concerned with getting the status from a connection - it doesn't have to handle the details of actually closing/opening the connection itself.
def open_temporary_connection(creds, &block)
client = create_client(creds)
result = block.call(client)
client.close_session
result
end
def get_status(creds)
open_temporary_connection(creds, &:account_status)
end
Also, I should mention, I think yield_self
is a bit of a trap. Unless you're dead set on making all of your code into a single expression, it makes the code look awkward without offering a lot of benefit.
Upvotes: 2