Reputation: 16811
I have this method, #upload, that basically takes in, amongst other arguments, a file option or some text. If a file (File.open("test.txt",'r+')) is passed,upload(:file => ...) , then this method reads from the file for the text, or if someone decides to pass that very same text just not in a txt file, he can do upload(:content => ...)
But I need either a file with the text or the text itself passed, how would you approach this?
Here's what I have so far.
def upload(args)
if args[:content].present?
self.content = args[:content]
elsif args[:file].present?
self.content = args[:file].read
end
end
thanks!
Upvotes: 2
Views: 557
Reputation: 168101
Solution 1
If the class of the argument is different (String
vs. File
), then you can use that in case
construction. You do not need further information to distinguish that.
def upload(arg)
self.content =
case arg
when String; arg
when File; arg.read
end
end
Solution 2
Added after inspired by a comment by Nemo157
Using polymorphism of object oriented programming, you can do this:
def upload(arg); self.content = arg.upload end
class String
def upload; self end
end
class File
def upload; read end
end
Some notes on polymorphism
Often, we refer to similar but different actions under the same word. For example, consider the word add
in the context of ordinary life: we use it in different meanings: add water to the jar, add 3 to 1, add a comment, add shade of blue to the green paint, and so on. They have different meanings but we have the intuition that they are somehow related. One way to distinguish these meanings will be to use different words, like liquid-add, number-add, context-add, or may be you can number them like add1, add2, add3, but this is a mess. However, notice that their meaning depend largely on the type of the object it is predicated of: depending on whether it's liquid, number, discourse, etc., the appropriate sense of "add" while be determined. The idea of polymorphism uses this fact, and applies it to programming. In the case here, "upload" will have different meaning depending on whether its about a string or a file. But as long as they are defined within their respective class, you don't have to care about the difference when you use them. Hence, you are freed from case statement, and makes the code simpler.
Upvotes: 2
Reputation: 3589
Just make it a test in the method, something like
def upload(args)
if args[:content].present?
self.content = args[:content]
elsif args[:file].present?
self.content = args[:file].read
else
raise ArgumentError, 'one of :content or :file must be given'
end
end
EDIT: alternative that uses just a single argument and duck-typing
def upload(arg)
if arg.respond_to? :to_str
self.content = arg.to_str
elsif arg.respond_to? :read
self.content = arg.read
else
raise ArgumentError, 'a String-like or IO-like object must be given'
end
end
Similar to the other two but with what I feel are a couple of improvements
Duck-typing instead of basing behaviour on the class, this means other classes could be used as long as they implement the right methods. Mainly this allows other forms of IO other than just normal files. Could be done with sub-typing, but that's not as much of a requirement in Ruby.
Using to_str
instead of to_s
, every class has a to_s
method and I'm assuming that you're not wanting to upload content like #<MyClass:0x523e>
. to_str
on the other hand is only implemented by classes that specifically should be convertible to a string.
Upvotes: 0
Reputation: 3253
Do you specifically want to pass the argument as a hash? If not I would go for something like:
def upload(args)
if args.respond_to? :read
self.content = args.read
else
self.content = args.to_s # This allows non-string arguments.
end
end
Upvotes: 2