Difference between &param and :&param in ruby methods - ruby-on-rails

I have functions with the definition def test(&param) and def test(:&param). What is the difference between both?

The difference is that def test(:&param) causes a syntax error and def test(&param) does not.

def test(&block) ...
means that our method accepts a block:
def test(number, &block)
yield number
# same as
# block.call number
end
test(10) {|a| a+a}
#=> 20
# or
block = proc{|a| a*a}
test 10, &block
#=> 100
While def test(:&param) will throw an error.
Also you can call something like method(&:operator):
[1,2,3].inject(&:+)
#=> 6
That is the same as
[1,2,3].inject{|sum, i| sum+i }

Related

yield to an anonymous block two functions up

there is probably a simple way to do this.
I'm trying to refactor something like the following
def foo(baz)
baz.update_first
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
called like
foo(baz) {|b| b.modify}
to something like
def foo(baz)
baz.update_first
bar(baz) {|i| yield i if block_given? }
end
def bar(baz)
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
Will that work? How?
I think it will, but I'd appreciate a clear explanation of how yielding inside a block works... reading through proc.c and vm.c and a relevant git commit in the ruby source code , I think when bar is called in foo it executes until it yields, and then you walk up the frame stack to the local environment pointer for block defined in foo, which is called, where the yield walks up to the block foo is called with, executes it, and then you are back in bar. Is that correct? Is there a better way to do this?
This feels a little weird to me, like inverting control, and it requires foo to know about baz more then I'd like, but I unfortunately can't simply pass a proc or lambda in this code.
I think maybe the concept of yield will be more clear if you look at an alternative syntax, which is converting the bloc to a proc argument.
For example, the following examples are the same
def my_each(arr)
arr.each { |x| yield x }
end
def my_each(arr, &blk)
arr.each { |x| blk.call(x) }
end
# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123
When using yield, the variable is available in the method without declaring it in the parameters list. Prepending an & sign to a parameter converts it to a proc, so in the method it can be run with .call.
Here's an example of providing a block to one method then executing it two scopes in:
def method_a(number, &blk)
method_b do
method_c do
blk.call(number)
end
end
end
def method_b(&blk)
blk.call
end
def method_c(&blk)
blk.call
end
method_a(1) { |num| puts num + 1 }
# => 2
Note that blk is not a magic word - you can name the variable whatever you want.
Here's the same thing with yield:
def method_a(number)
method_b do
method_c do
yield number
end
end
end
def method_b
yield
end
def method_c
yield
end
method_a(1) { |num| puts num + 1 }
# => 2
I think using the &blk syntax is clearer because it assigns a variable to the proc. Just because a proc is used in the method doesn't mean you have to ever run Proc.new. The block is automatically converted to a proc.

Ruby - collection of methods

I have inherited a bunch of methods that are not wrapped by any classes or modules, and are just listed in an .rb file. This was made possible due to the file being used inside a Cucumber test suite. I want to take a collection of all these methods and iterate over each method call, doing some work on each one as they are called.
EX:
def call_all_methods
method1
method2
method3(true)
method3(false)
method4('Y', true)
method4('N', true)
method4('Y', false)
method4('N', false)
end
What i want to be able to do is wrap these all in an array and call them individually with a begin/rescue block around them
$all_methods.each do |method|
begin
method.call
rescue Exception1
handle_exception1
rescue Exception2
handle_exception2
end
end
I've tried putting them all in an array using %w
call_all_methods = %w(...)
and that works but it makes the methods ugly to look at in the IDE
I've tried doing a readlines on the file, but the methods get executed while the file is being read.
I could create methods to wrap each call, but then I have a method to call another method (one line) which isn't right either.
I have looked at Ruby: methods as array elements - how do they work? but neither of those solutions seemed like good solutions for what I'm trying to do, as it would dirty the code
If I understand what you're asking correctly, you could just wrap those methods in a class.
class MyMethods
# all those methods that you have in that file
end
You could then list them all by doing
all_methods = MyMethods.instance_methods(false)
To execute them, you can do all_methods.each {|m| MyMethods.new.send(m)}
You could do something like this:
def execute_several(arr)
arr.each do |method, *args|
begin
v = send(method, *args)
puts "for method '#{method}': #{v}"
rescue ArgumentError => e
puts "for method '#{method}': #{e.message}"
end
end
end
arr = [
[:class],
[:rand, 20],
[:Integer, "20"],
[:Integer, 'cat']
]
execute_several(arr)
# for method 'class': Object
# for method 'rand': 17
# for method 'Integer': 20
# for method 'Integer': invalid value for Integer(): "cat"
Here's an example of how that would be done within a class:
class Array
def execute_several(arr)
arr.each do |method, args|
begin
v = args ? send(method, args) : send(method)
puts "for method '#{method}': #{v}"
rescue TypeError => e
puts "for method '#{method}': #{e.message}"
end
end
end
end
arr = [
[:reverse],
['first'],
[:&, [2,3,4]],
[:|, 'cat']
]
[1,2,3].execute_several(arr)
# for method 'reverse': [3, 2, 1]
# for method 'first': 1
# for method '&': [2, 3]
# for method '|': no implicit conversion of String into Array
I ended up making an array of procs

ruby pass method as a parameter

Im trying to pass a method as a parameter to method_2, execute this one and return the result:
def method_2( method_p, param )
res = method(method_p).call(param)
return res
end
def method_1
klass = MyKlass.instance
return method_2( klass.foo, "test" )
end
this's MyKlass file:
class MyKlass
def foo(param)
param+param
end
end
All I got is an error
wrong number of arguments (0 for 1)
You can use symbols to refer to methods:
def method_2(method_symbol, *args)
send method_symbol, *args
end
However, since you're calling the method on a specific object, you would either have to pass that in as an additional argument, or use a proc or a lambda, which is like a block wrapped in an object:
def method_2(proc, *args)
proc.call(*args)
end
method_2(->(param){ klass.foo(param) }, "test")
It's more common to just use blocks to do this:
def method_2(receiver, *args, &block)
yield receiver, *args
end
method_2(klass, "test") do |receiver, param|
receiver.foo(param)
end
All of these are fairly contrived examples; is there a specific problem you're trying to solve?
When you:
return method_2( klass.foo, "test" )
klass.foo requires one arg, that might be what's causing your error.

How can I cleanly define "antonym" or "opposite" methods in Ruby / Rails?

I'm pretty often defining methods and their antonyms in the code I'm writing, as in:
def happy?
#happiness > 3
end
def sad?
!happy?
end
Which is fine, but I'm a little surprised that Ruby or ActiveSupport doesn't give me something like:
def happy?
#happiness > 3
end
alias_opposite :sad? :happy?
Or am I just looking in the wrong place?
There is no such method in popular libraries, but there is how this could be implemented
class Module
def alias_opposite(a, b)
define_method(a) { !self.send(b) }
end
end
Usage
class A < Struct.new(:happiness)
def happy?
happiness > 3
end
alias_opposite :sad?, :happy?
end
p A.new(1).sad? # => true
p A.new(5).sad? # => false
I suspect this pattern is not as common in ruby because the unless keyword often does the trick:
# ...
clap_your_hands if happy?
stomp_your_feet unless happy?
# ...
Of course, its simple to roll your own:
module Antonymator
def define_antonym(as, of)
define_method(as.to_sym) do |*args|
return !(send(of.to_sym, *args))
end
end
end
# Usage Example
class AreThey
extend Antonymator
define_antonym :uneql?, :eql?
define_antonym :nonconsecutive?, :consecutive?
def eql?(a, b)
a == b
end
def consecutive?(a, b)
a.next == b
end
end
are_they = AreThey.new
puts are_they.uneql? 1, 2 # true
puts are_they.nonconsecutive? 1, 2 # false
If your methods return a Boolean, you can always include the positive method in the negative method.
def drinking_age?(age)
age > #restricted_age
end
def not_drinking_age?(age)
!drinking_age?(age)
end
#restricted_age = 20
Hope that helps.
I guess it depends on what 'opposite' means in the context.

Ruby/Rails: Prepend, append code to all methods

I wrote a small benchmarking Class for testing my code doing development. At the moment I have to add the Class to the beginning and end of every method. Is it posible to prepend, append on the fly, so that I don't have to clutter my code?
class ApplicationController
before_filter :init_perf
after_filter :write_perf_results_to_log!
def init_perf
#perf ||= Perf.new
end
def write_perf_results_to_log!
#perf.results
end
end
class Products < ApplicationsController
def foo
#perf.log(__methond__.to_s)
caculation = 5 *4
#perf.write!
end
def bar
#perf.log(__methond__.to_s)
caculation = 1 / 5
#perf.write!
end
end
This is the Perf class. It is located in the services folder.
class Perf
def initialize
#results = []
end
def log(note)
#start = Time.now
#note = note
end
def write!
if #results.find {|h| h[:note] == #note } # Update :sec method exists in results
#results.select { |h| h["note"] == #note; h[":sec"] = (Time.now - #start).round(3) }
else # Add new Hash to results
#results << { :note => #note, :sec => (Time.now - #start).round(3) }
end
end
def results
content = "
PERFORMANCE STATISTICS!
"
#results.each do |r|
content += r[:note] + " " + r[:sec].to_s + "
"
end
content += "
"
Rails.logger.info content
end
end
In general computing terms what you want to do is called code instrumentation. There are several ways to accomplish this, however here's one (crude) example using some metaprogramming:
First define a new method that we will use for injecting our instrumentation code:
class ApplicationController
def self.instrument_methods(*methods)
methods.each { |m|
# Rename original method
self.send(:alias_method, "#{m}_orig", m)
# Redefine old method with instrumentation code added
define_method m do
puts "Perf log #{m}"
self.send "#{m}_orig"
puts "Perf write"
end
}
end
end
How to use it:
class Product < ApplicationController
def foo
puts "Foo"
end
def bar
puts "Bar"
end
# This has to be called last, once the original methods are defined
instrument_methods :foo, :bar
end
Then:
p = Product.new
p.foo
p.bar
Will output:
Perf log foo
Foo
Perf write
Perf log bar
Bar
Perf write
Here are some other ways to instrument ruby code and measure performance:
http://ruby-prof.rubyforge.org/
http://www.igvita.com/2009/06/13/profiling-ruby-with-googles-perftools/
There is better solution.
class ApplicationController
def self.inherited(klass)
def klass.method_added(name)
return if #_not_new
#_not_new = true
original = "original #{name}"
alias_method original, name
define_method(name) do |*args, &block|
puts "==> called #{name} with args: #{args.inspect}"
result = send original, *args, &block
puts "<== result is #{result}"
result
end
#_not_new = false
end
end
end
class Product < ApplicationController
def meth(a1, a2)
a1 + a2
end
end
product = Product.new
puts product.meth(2,3)
And the result:
==> called meth with args: [2, 3]
<== result is 5
5
The source & explanation are here: http://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming. I recommend to spend not a big money to get this course.
I'm the author of aspector gem. Thanks to dimuch for mentioning it.
I've come up with a solution using aspector. Here are the high level steps:
Create an aspect as a subclass of Aspector::Base
Inside the aspect, define advices (before/after/around are the primary types of advices)
Apply the aspect on target class (or module/object)
The full code can be found in this gist. Please feel free to let me know if you have questions or the solution doesn't do what you intend to.
class PerfAspect < Aspector::Base
around options[:action_methods] do |proxy|
#perf ||= Perf.new
proxy.call
#perf.results
end
around options[:other_methods], :method_arg => true do |method, proxy, *args, &block|
#perf.log(method)
result = proxy.call *args, &block
#perf.write!
result
end
end
action_methods = [:action]
other_methods = Products.instance_methods(false) - action_methods
PerfAspect.apply(Products, :action_methods => action_methods, :other_methods => other_methods)
Guess aspector gem can help. It's not well documented but has useful examples.

Resources