What does question mark mean in the definition of function in ruby? - ruby

I have got a function like this--
Function's name is seld.is_dl and it is accepting path parameter. My question is that what does this ? sign in the function definition indicate.
def self.is_dl?(path)
path = File.basename(path)
if path =~ /setup.exe/i
return false
else
return true
end
end
I am java developer and I have seen "?" in case of If-ELSE block mainly, that is why I am not able to figure what does this mean?

? is a valid character in a method name.
It is typically used to denote a method that returns true or false
For example:
File.exists?
File.readable?
String#ascii_only?
etc
Note: ! is also a valid character. It is typically used to denote a "destructive" method
String#capitalize!
String#downcase!
String#rstrip!
etc
If you're feeling like going the extra mile, Ruby technically allows any string to be a method name. Odd ones need define_method() and send() calls, but formally there’s no restriction.
module Hello
class << self
define_method "this is my method :)" do |foo|
puts "you gave my method #{foo}"
end
define_method "this isn't your method :(, sorry" do
puts "sorry, not your method, bro"
end
end
end
Hello.send("this is my method :)", "candy")
#=> you gave my method candy
Hello.send("this isn't your method :(, sorry")
#=> sorry, not your method, bro

Related

Examples of methods with single string arguments

Could someone give me an example of a method that accepts a single string argument. Whilst i appreciate this might seem trivial I am trying to understand how this works in ruby. Would the syntax below be correct.
Def method("argument")
true
end
You need to check if it is a string by yourself. You can make use of is_a? to see if it is a String as Ruby is dynamically typed.
def add_watson(f_name)
f_name << " Watson" if f_name.is_a? String
end
Now, calling it
puts add_watson("Emma") #=> Emma Watson
No, it wouldn't. First of all, def keyword is lowercased. Also, method argument(s) is written like this def my_method(argument) and at this point it doesn't check if it's string, in fact, it can accept any object, because Ruby is typed dynamically. If you want to force String instance as an argument, you can do it inside of method body:
def my_method(argument)
raise ArgumentError, "argument must be a String" unless argument.is_a? String
true
end
Your syntax is wrong.
def method(arg)
puts "arg"
end
You can review some tutorial too
http://www.tutorialspoint.com/ruby/ruby_methods.htm
http://www.skorks.com/2009/08/method-arguments-in-ruby/

Naive aspect implementation in ruby

I am trying to make a simplistic implementation of AOP in ruby. I was able to implement before and after advices, I got stuck with around advice.
This is the target class that is going to be advised:
class MyClass
def method
puts "running method"
end
end
This is the Aspect class to instantiate objects capable of making advices:
class Aspect
def advise(class_name, method, type, &block)
class_name.send(:alias_method, :proceed, :method)
class_name.send(:define_method, :method) do
case type
when :before
yield
proceed
when :after
proceed
yield
when :around
yield(proceed) # * proceed is the old version of the method
end
end
end
end
(*) Yield should execute the block around MyClass#proceed on the current object when method is invoked.
Creating the target and the aspect:
mc = MyClass.new
a = Aspect.new()
Invoking the method without advising it:
puts mc.method
Advising MyClass#method with around:
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
puts mc.method
(*) I am not being able to pass something to identify the call of proceed, that is the invocation of the old method without the advice.
The output should be:
First
running method
Last
In Ruby, a method call looks like this:
receiver.method(arguments)
Or, you can leave off the receiver if the receiver is self.
So, to call a method named proceed on some receiver, you would write
receiver.proceed
However, in your implementation, you don't keep track of what the receiver should be, so since you don't know the receiver, you simply cannot call the method.
Note that there are lots of other problems with your approach as well. For example, if you advise multiple methods, you will alias them all to the same method, overwriting each other.
I believe there are two things going wrong here.
This section of code
when :around
yield(proceed) # * proceed is the old version of the method
end
Calls the block given to advise providing the output of the proceed method as an argument.
So your output probably looks something like:
running method
First
Last
This block
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
Just evaluates the argument given as proceed. If a method is given it does not call it. So taking problem 1 into consideration in your case the original definition of method (aliased to proceed) returns nil (output of return) which will be passed as the value to the proceed argument in the block when yielded. the block ends up evaluating to something like
puts "First"
nil
puts "Last"
mc.method is called.
To address the second part, you may want to consider using send. Because the inner workings of your aspect may not be known to your code that calls it. It may change over time, so what ever calls Aspect.advise shouldn't make assumptions that the original method will still be accessible. Instead, it should take an argument (the new method name) and send it to the object. Making the block passed to advise:
a.advise(MyClass, :method, :around) do |aliased_method_name|
puts "First"
send(aliased_method_name)
puts "Last"
end
And adjusting the around item added to your class when advise is called to the following:
when :around
yield(:proceed) # * proceed is the old version of the method
end
If you do both of these things, your around section will calls the provided block, using the symbol for the new alias for the overridden method.
N.B.: This approach won't work for methods that require any arguments.
This is what I did. In the definition of Aspect#advise now I use a Proc, like this:
when :around
yield Proc.new { proceed }
end
And when calling the method to advise MyClass#method with :around parameter I use this:
a.advise(MyClass, :method, :around) do |original|
puts "First"
original.call
puts "Last"
end
I got:
First
running method
Last
Here's the fixed version that will work for arguments, and avoid clobbering.
class Aspect
##count = 0
def self.advise(class_name, method, type=nil, &block)
old_method = :"__aspect_#{method}_#{##count += 1}"
class_name.send(:alias_method, old_method, method)
class_name.send(:define_method, method) do |*args, &callblock|
case type
when :before
yield
send(old_method, *args, &callblock)
when :after
send(old_method, *args, &callblock)
yield
when :around, nil
yield lambda {
send(old_method, *args, &callblock)
}
end
end
end
end
class Foo
def foo(what)
puts "Hello, #{what}!"
end
end
Aspect.advise(Foo, :foo) do |y|
puts "before around"
y.yield
puts "after around"
end
Aspect.advise(Foo, :foo, :before) do
puts "before"
end
Aspect.advise(Foo, :foo, :after) do
puts "after"
end
Foo.new.foo("world")
# before
# before around
# Hello, world!
# after around
# after

Handing regex string to method

Within a method, I want to dynamically evaluate the following code chunk with a regex:
if (/^[A-F][A-Z]*[^\.\*]$/).match(some_value)
The method I attempted is this:
def check(val)
if (/^[val][A-Z]*[^\.\*]$/).match(some_value)
puts "foo"
else
puts "waa"
end
end
check("A-F")
The value I am passing in is not making it there correctly. It appears that passing a value in this fashion needs something more. Is this not something you can do with a method?
You expected string interpolation. To do that, you need to use the interpolation syntax #{}:
def check(val)
if (/^[#{val}][A-Z]*[^\.\*]$/).match(some_value)
puts "foo"
else
puts "waa"
end
end

method_missing with unquoted string arguments in Ruby - possible?

I'm learning Ruby and want to be able to do this:
Printer.hi there
and have Ruby output
"hi there"
So far I have the following implementation
class Printer
def method_missing(name, *args)
puts "#{name} #{args.join(',')}"
end
end
But this only lets me do
Printer.hi "there"
If I attempt
Printer.hi there
I get a
NameError: undefined local variable or method `there' for main:Object
which makes sense as I haven't ever defined 'there'. Is there a way to make this work though?
No, this is not possible in the form given (as far as I know).
You aren't looking for method missing, you are looking for the equivalent in the Ruby interpreter to capture when it cannot find a given symbol. So while you cannot intercept it there, you can do it inside of a block:
def hi(&block)
begin
yield
rescue NameError => e
e.message =~ /variable or method `(.+)'/
puts "hi #{$1}"
end
end
hi { there } # => hi there
Please note that I feel like a terrible world citizen for showing you this. Please don't use it anywhere, ever.
Yes, there is a way. When you write there without an explicit receiver, the receiver is the self object of that scope. In this case, it is main. Define methods_missing in the main context.
def method_missing(name, *args)
puts "#{name} was called with arguments: #{args.join(',')}"
end
But if you do so, that would mess up the rest of your code, perhaps. I see not point in doing this.
Since the return value of puts is nil, if you do Printer.hi there, it will evaluate to Printer.hi(nil). So in order for it to output "hi there", you need to define:
class Printer
def self.hi _; puts "hi there" end
end
No because strings need to be quoted, so they are not seen as variables.
Otherwise variables such as there would need some special sort of character to indicate that it is a string. However this still wouldn't work well as spaces would then need to be dealt with.
Use single or double quotes.
It's how the language works. accept this and move on to the next challenge :)
Interestingly you can do this in ruby 1.8.7 with just this code:
def method_missing(*args)
puts args.join ' '
end
I learned about this from Gary Bernhardt's talk, Wat. In 1.9 this gives you a stack level too deep error unless you do it inside a class. Google lead me to this post on Aurthur's tech blog thing, which claims you can do something similar in JRuby 1.9 mode:
def method_missing(*args)
puts [method.to_s, args].flatten.join ' '
end
However when I tried this on MRI 1.9.3 it did not work either. So in 1.9 you can't quite do what you want. Here is the closest I could come:
class Printer
def self.hi(message)
puts "hi #{message}"
end
def self.method_missing(m, *args)
[m.to_s, args].flatten.join ' '
end
def self.bare
hi there
end
end
Printer.bare

Can Ruby operators be aliased?

I'm interested in how one would go in getting this to work :
me = "this is a string"
class << me
alias :old<< :<<
def <<(text)
old<<(text)
puts "appended #{text}"
end
end
I'd like that when something gets appended to the me variable, the object will use the redefined method.
If I try to run this, I get syntax error, unexpected ':', expecting kEND at :<<.
Only certain characters are allowed in symbol literals. You are looking for:
alias :"old<<" :"<<"
:old<< looks like ":old <<". Try just :old, or if you really want, :"old<<" (but have fun calling it through that name).
As others have already explained, the problem is simply that old<< is not a legal Ruby identifier. You can, with tricks, create a method with that name, but you can't call it in the normal ways, and it certainly won't be recognized as an operator.
However, all the answers so far, while they have certainly answered your question, have completely ignored the underlying problem: that method shouldn't even have a name in the first place! And if it doesn't have a name, then the problem of the name being illegal simply doesn't even arise.
#!/usr/bin/env ruby
require 'test/unit'
require 'stringio'
class TestOperatorDecorator < Test::Unit::TestCase
def setup; #old_stdout, $> = $>, (#fake_stdout = StringIO.new) end
def teardown; $> = #old_stdout end
def test_that_me_dot_append_writes_to_stdio
me = 'this is a string'
class << me
old_method = instance_method :<<
define_method :<< do |text|
old_method.bind(self).(text)
puts "appended #{text}"
end
end
me << 'Test'
assert_equal "appended Test\n", #fake_stdout.string
end
end
In this case, the method never gets named, which not only means that we don't have to invent a name for it, it also means that it doesn't pollute the namespace.
The problem is with :old<<. It gets interpreted as :old <<, i.e. a symbol :old followed by the << operator, so it is a syntax error. Maybe you can try :"old<<"?
While I agree with thenduks and ephemient, You can alias the operator that way then use send to call it, you can also still use class inheritance. e.g.:
me = "is a string"
class << me
def <<(text)
super
puts "appended #{text}"
end
end
me << " bob"
puts me #=> is a string appended bob

Resources