I am iterating through a set of ids and want to return the first object which returns true when a predicate method is called on it. A few lines of code is worth a thousand words:
def applicable_question(question_ids)
ordered_ids(question_ids, order).detect do |question_id|
question = Question.find_by(id: question_id)
return question if question.applicable_for?(self)
end
end
Stripping away the domain terms:
def desired_thing(ids)
ids.detect do |id|
thing = Thing.new(id)
return thing if thing.true?
end
end
Is there a more idiomatic approach here? Specifically, I feel like I am abusing detect. I immediately reached for each and break, but didn't get very far with that approach.
A requirement for this code is for it to not need to instantiate a large array of objects (ActiveRecord subtypes for example) to find the desired thing.
You have:
desired_thing = ids.detect do |id|
thing = Thing.new(id)
return thing if thing.true?
end
If thing is found for which thing.true? is true, detect never returns an element of ids (to be assigned to desired_thing) because it's preempted by return. On the other hand, if the block completes without return being invoked, detect returns nil and assigns that value to desired_thing, but that nil value is of no use in the code that follows. It therefore would be better to just write:
ids.each do |id|
thing = Thing.new(id)
return thing if thing.true?
end
I think you are almost there.
From the find_by_id method, I assume that Question is a class with some kind of ActiveRecord functionality, so I would expect a where method to exist as well.
In that case:
applicable_question = Question.where(id: ids).order(...).detect do |question|
question.applicable_for?(self)
end
will do just fine.
It doesn't feel like abusing detect, you just need to have the correct enumerable set of objects.
Update
If instantiating too many objects is not an acceptable solution, then https://stackoverflow.com/a/29176622/687142 is the way to go. Keep in mind that both approaches have their drawbacks.
Question.where(id: ids).order(...).detect {|q| q.condition? } will do a single query to the database but instantiate too many objects.
ids.each {|id| q = Question.find(id); return q if q.condition? } will perform too many queries to the database but only instantiate one object at a time
The first approach is constantly heavy (memory wise) while the second one's performance depends on the order that you retrieve the records and can become quite expensive as well.
The best choice depends on your data set as well. If you have hundreds of thousands of questions the first approach is out of the question.
Maybe the best choice is to try to order the records in a way that condition? is more likely to be true
What you have isn't far off from what you're trying to do. I would do something like:
desired_thing = ids.detect{ |id| Thing.new(id) }
I'm not sure if you're trying to stay away from SQL methods like find_by_id or where, but I'm sure you could use those too unless you NEED to use detect (or find).
Related
I have a collection of 'data endpoints'. Each endpoint has a name and can be available or unavailable. In Ruby I want to present the available endpoints as a Hash to make it easy to work with them. The difficulty is that getting information about the endpoints is costly and should be done lazily.
Some examples of how I want my object to behave:
endpoints = get_endpoints.call # No endpoint information is accessed yet
result = endpoints['name1'] # This should only query endpoint "name1"
is_available = endpoints.key? 'name2' # This should only query endpoint "name2"
all_available = endpoints.keys # This has to query all endpoints
The comments describe how the object internally makes requests to the 'data endpoints'.
It is straightforward to make a Hash that can do the first 2 lines. However I don't know how to support the last 2 lines. To do this I need a way to make the keys lazy, not just the values.
Thank you for taking a look!
You'd have to override the key? method, and do your own checking in there.
class LazyHash < Hash
def key?(key)
# Do your checking here. However that looks for your application
end
end
In my opinion, you're asking for trouble though. One of the most powerful virtues in computer science is expectability. If you're changing the behavior of something, modifying it far beyond it's intent, it doesn't serve you to continue calling it by the original name. You don't need to shoe-horn your solution into existing classes/interfaces.
Programming offers you plenty of flexibility, so you can do stuff like this (dependent on the language of course), but in that same argument, you have no reason not to simply build a new object/service with it's own API.
I recommend starting fresh with a new class and building out your desired interface and functionality.
class LazyEndpoints
def on?(name)
end
def set(name, value)
end
end
(Or something like that, the world is yours for the taking!)
I'm looking for a function that would do the following:
def self.my_find
object = self.first #Whatever
return object.my_check? ? object : nil
end
Something like check:
object.check(&:my_check?)
It should exist, shouldn't it?
Here's my situation:
In a method (controller), I have some nil-able objects and I need to check something on the object to further use it.
In the event of trying to write pseudo-functional code, the logic is first to retrieve the objects, then making actions on the objects and then return those.
If I have a collection, the code would be:
result = get_my_collection_method.select(&:my_check?).map(&:my_action)
There is no issue if the select methods filters all objects, then result will equal [] but the code is still valid.
I find natural to wanting to do the same even if it is not a collection but a single object:
result = get_my_object.check(&:my_check?).try(:my_action)
But the fact that this method doesn't exist tells me there is no monadic transformation between object and array in ruby.
I guess the simplest way to achieve this is that I transform my singleton into a single value array:
result = Array(get_my_object).select(&:my_check?).map(&:my_action).first
I hope this clarifies why I was looking for such method.
But the question remains: does it exist natively? If I write it myself (even at the Object level), this is not standard, I lose the benefits of writing clean code.
While a method like that might seem useful in your particular circumstance, this sort of thing is generally expressed like:
object = self.first
object.my_check? and object
Where it's understood that this method may return false but that's not a logically true value so it's fine.
If you want to write your own method for Object that does this, you're welcome to, but keep in mind the more esoteric and quirky your code is the harder it will be for other people to engage with. Those "other people" might be you in the future when you've forgotten about your unusual core extensions.
Here's a few ways:
def self.my_find
object = self.first # Whatever
# Use ONE of the following examples:
# 1.
object if object.my_check?
# 2.
object.my_check? && object
# 3.
object.my_check? ? object : nil
end
Note that an explicit return is not needed at the end of a method in ruby.
Depending on the context though (I can only speculate, as none was given!), there might be a cleaner way to do this. For example, if this is actually something you're building in a rails application, then one of these examples would be better:
def self.my_find
where(...).find_by(my_check: true)
# Or, where `my_check` is a *SCOPE*!!
my_check.find_by(...)
end
...With the key difference being that you're performing the check in SQL rather than in ruby. This gives better performance.
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!"
I have a view which aggregates the result of running a complex computation several times with different inputs. This computation relies on a couple of base methods which are sometimes, but not always, very expensive (on a cache miss). The following is a working example of roughly this situation:
def view
10.times.map do
complicated_process_with_many_methods
end
end
def complicated_process_with_many_methods
[sometimes_expensive_method, sometimes_expensive_method]
end
def sometimes_expensive_method
if rand < 0.5
"expensive result"
else
"inexpensive result"
end
end
puts view
However, sometimes the user just wants to see whatever data we can fetch quickly, and doesn't want to wait a long time for the data that would have to be computed. That is, we should render as much of view as we can without depending on an expensive invocation of sometimes_expensive_method. sometimes_expensive_method can easily determine if this will be an expensive invocation or not, but the information required to do so should not be available to view or to complicated_process_with_many_methods.
I would like to be able to call complicated_process_with_many_methods in such a way that, if ever sometimes_expensive_method turns out to be expensive, we give up and use a default value for that invocation of complicated_process_with_many_methods. However, if nothing turns out to be expensive, then the real result of complicated_process_with_many_methods should be used.
I could add a flag which causes sometimes_expensive_method to behave differently when the result would be expensive:
def sometimes_expensive_method(throw_on_miss)
if rand < 0.5
if throw_on_miss
raise ExpensiveResultException
else
"expensive result"
end
else
"inexpensive result"
end
end
then catch that exception in view and use a default value instead. However, this would require passing that flag through all of the different methods involved in complicated_process_with_many_methods, and I feel like those methods should not have to know about this behavior. I could instead have sometimes_expensive_method call a continuation when the result would be expensive, but that continuation would also have to be passed through all the calls. I don't think a global would help, because different requests may invoke the different behaviors at the same time.
What I'm looking for is some way for sometimes_expensive_method to change its behavior (perhaps by throwing an exception) in response to some sort of signal that is defined in view, without modifying the body of complicated_process_with_many_methods to pass the signal along. Does such a signal exist in ruby?
Try Timeout. See here.
I don't know if it will release the external resources (files or db connections), so use it at your own risk.
I have a text log from a game with (for example) two types of entries viz. Chat and Event. For the most part they are very similar so I have a LogEntry class defined as so;
class LogEntry < Array
def initialize(str)
super str.split
end
def parse
LogEntry.parse self
end
def LogEntry.parse(entry)
# Processes the elements that are in any Entry
# Figure out whether it's a Chat entry or an Event entry
# Returns an object of type LogChat or LogEvent
end
end
LogChat and LogEvent both extend LogEntry and do further processing relevant to their domain. Everything works as expected;
chat = LogEntry.new("some chat")
event = LogEntry.new("some event")
chat.parse.class # => LogChat
event.parse.class # => LogEvent
Question:
The class method LogEntry.parse essentially returns a parsed entry of the appropriate class. In this context, the parsed entry is the important bit. But we could rename the instance method 'parse' to 'what_type_should_i_be?'. I want the object to act on that information and 'self.become LogEntry.parse(self)'
Right now, to parse an entry, i have to do this;
entry = entry.parse
I want to push this further so that i get the same result with;
entry.parse
I've tried the obvious;
class LogEntry
def parse
self = LogEntry.parse(self)
end
end
Yet I get the error Can't change the value of self. Does anyone know how I should go about achieving this?
Edit:
I have changed my examples because many answers were focusing on the iteration over many entries. Chuck's answer elegantly shows that this situation isn't a problem.
In case this arouses anyone's interest, i've stumbled across Evil Ruby which let's you meddle with `self.class'. There's a nice Orielly article about it called Ruby code that will swallow your soul! I'm looking into it to see if it offers any answers. (Edit: evil.rb is well named! Something that low level doesn't 'seem' suitable for stable/long term distribution.)
I think the fundamental problem is that each is the wrong method here. Either have parse change the object's internal state rather than the object itself, or use map! to replace the objects in the collection with the new versions.
entries.map! {|entry| entry.parse}
will update the objects in the array with the result of calling parse on them, so there's no reason to do weird stuff to self in the parse method.
If you can break out the functionality into different modules, you can mutateextend() self as you like:
class LogEntry
...
def parse! # This mutates self!
case LogEntry.parse!
when :chat
self.extend MyApp::LogChat
when :event
self.extend MyApp::LogEvent
else
raise MyApp::Exception, "waaah"
end
end
end
You don't have to do a clunky case statement with repeated calls to self.extend(), of course, but you get the idea.
for starters, your comments say that LogEntry.parse returns an LogChat or LogEvent object. So you are asking the object to change itself to a different type of object.
It also looks like class methods and instance methods are being confused a little
I am guessing a little but why couldn't you do :
entries.each do |entry|
some_type_log = entry.parse
some_type_of_log.save!
end
EDIT:
sorry, wanted to clarify something. Since you are parsing data that is part of LogEntry, and you want an entry to parse itself, there is no need to pass in any parameters. just keep the parse method parameter-less.
If you know what type of log something is, you can skip a step and parse it on the way in.
chat_entry = LogChat.new(:log => LogEntry)
then make a method called log which is your parser that explicityly handles chat related items.
You've got some string/array/LogEntry confusion here, but assuming you get that worked out, and at the end you still want to have an Array subclass replacing its own contents, you need to use replace:
self.replace(LogEntry.parse(self))