Is it possible to identify aliased methods in Ruby? - ruby

Often within the console, I'll interrogate an object
pp obj.methods.sort #or...
pp (obj.methods - Object.methods).sort
In Ruby it's pretty common for a developer to provide aliases for methods. I am wondering if there is a reflective way of identifying aliases so that I might be able to display aliased methods, something like...
array.aliased_methods #=> {:collect => :map, ...}
This would be helpful for being able to identify exactly how many things an object can do.

In Ruby 1.9, aliased instance methods will be eql?, so you can define:
class Module
def aliased_methods
instance_methods.group_by{|m| instance_method(m)}.
map(&:last).keep_if{|symbols| symbols.length > 1}
end
end
Now if you try it, you will get:
class Foo
def bar; 42 end
alias baz bar
def hello; 42 end
end
Foo.aliased_methods # => [[:bar, :baz]]
Array.aliased_methods # => [[:inspect, :to_s], [:length, :size]]
Note that some pairs are missing, e.g. [:map, :collect]. This is due to a bug that is now fixed and will be in the next version (2.0.0) If it is important to you, you can roll your own group_by without using hashes or eql? and only using ==.

Not really. Alias isn't just a pointer or something like that, after an alias you can undef the first method and the aliased method won't change (think hard link vs sym link). Typically, aliases are reflected in the rdoc, so I would go there for a definitive list.

Related

How to avoid instance variable initializing ugliness

I see this popping up all the time in my code
class Foo
def initialize(foo)
#foo = foo
end
#...
end
This isn't too bad, but it gets worse:
class Foo
def initialize(foo,baz,bar,a,b,c,d)
#foo = foo
#baz = baz
#bar = bar
#etc...
You can sortof get around this by doing something like
#foo, #baz, #bar = foo, baz, bar
But even that feels wrong and is annoying to type. Is there a better way to define instance variables according to arguments?
Edit:
There seem to be 2 distinct solutions to this problem. See:
spickermann's answer
Antarr Byrd's answer
You might want to consider using a Struct:
class Foo < Struct.new(foo,baz,bar,a,b,c,d)
end
foo = Foo.new(1,2,3,4,5,6,7)
foo.bar #=> 2
No need to define an extra initialize method at all...
Yes, that's the preferred way to initialize instance variables in Ruby. It can be annoying to type, but it's a well understood pattern. As always in Ruby, using metaprogramming to automate it away is possible, but will make your code harder to follow.
I'd also argue that it's probably a good thing for a class to look ugly when it's taking more than two or three arguments. If your class depends on six different things to function, it's a strong candidate for refactoring.
def initialize args
#foo, #baz, #bar = *args
end
I think there are 3 ways to make initialization of instance variables shorter:
Use Struct or OpenStruct.
Use ruby's parallel assignment.
Use metaprogramming to make a macro like this.
The fattr gem was recently endorsed on Ruby Tapas to help solve this problem. Another consideration though, is whether there are too many things being passed into the initializer. It could be that this class is doing too much and needs to be broken into smaller pieces.

In Ruby, $? is a 'read-only' variable that can't be directly assigned to. Any idea how I can do this for my own custom variable/class?

The issue I am faced with is that I need to prevent a Ruby class from being manipulated after it is defined. I can freeze it, but that doesn't stop people from just overwriting it all together.
I realize that some will want to respond with some sort of "Ruby isn't meant to be used like this" mantra. I get it, but my case is very special. This is for codewars.com where user submitted solutions are combined with a custom test framework, so I need to stop the user-submitted code from tinkering with the Test class.
I had thought that it wasn't possible at all to make constants true constants, but I noticed that the $? global variable is like this. Its likely that its because its built-in to the language to be like this and not something that can be done with custom variables.
That's because it is built into the language.
In Ruby there is no way to truly define a constant. The closest you can come is writing custom getters/setters and throwing an error if a variable has already been set.
Throw exception when re-assigning a constant in Ruby?
This is defined as "The status of the last executed child process." so, if you assign something to that variable, it will be immediately overwritten by the language with the result of the last (your) assignation.
I would think about a custom implementation - perhaps extracted into some helper gem?
def foo
#foo
end
def foo=(foo)
if defined?(#foo)
warn "warning: already initialized foo"
else
#foo = foo
end
end
self.foo = :bar
puts foo # => bar
self.foo = :baz # => warning: already initialized foo
puts foo # => bar

How do I have Ruby YAML dump a Hash subclass as a simple Hash?

I have a class Foo that is a subclass of Hash.
class Foo < Hash
# whatever Foo-specific methods/etc
end
When I dump it to a file with YAML, it is written with a tag indicating the class.
> f = Foo.new
> f[:bar] = "baz"
> puts YAML.dump(f)
--- !ruby/hash:Foo
:bar: baz
I would like it to just be written as a plain old hash (not !ruby/hash:Foo)
> puts YAML.dump({bar:"baz"})
---
:bar: baz
...so that consumers of my data don't need to know about Foo. Is there a magic method to add to my class to convert itself for serialization, or a magic option to pass to YAML.dump?
Of course it is easy to convert one Foo object to a hash, but they may show up nested at any level inside the actual hash I'd like to dump, and I'd rather not have to do a search and replace.
You can achieve this with the (poorly documented) encode_with and represent_map methods. To customise the YAML serialisation of an object you provide it with an encode_with method which accepts a coder object, one of the methods on which is represent_map.
class Foo < Hash
# other methods ...
def encode_with coder
coder.represent_map nil, self
end
end
Now YAML.dump will just output your object as a normal hash.
However
There is a bit of a problem because there is a bug that will cause this to fail and is only fixed in the latest Gem version of Psych. It is not fixed in the current latest Ruby version (ruby 2.0.0p247). It is fixed in Ruby trunk so later patch releases should be okay.
In order to use this you will have to make sure you are using the latest Psych Gem, rather than the version bundled with Ruby. This should be as easy as
gem 'psych', '2.0.0'
before you require Yaml, but it seems that in Ruby 2.0 this doesn’t work for some reason that I can’t figure out. Using Bundler to specify the Gem versions does work though, so you might need to create a Gemfile and specify Psych in there if you’re not already using it.
Search and replace was actually not too bad:
# Convert Hash/Array subclasses into plain hashes/arrays for YAML dump.
# Assumptions:
# Hash keys will be simple objects - don't need to clear them
# No custom objects containing Hash/Array subclass instances
def deep_clear_subclasses(obj, dedup = {})
case obj
when Hash
return dedup[obj] if dedup.has_key? obj
dedup[obj] = copy = {}
obj.each {|k,v| copy[k] = deep_clear_subclasses(v, dedup)}
copy
when Array
return dedup[obj] if dedup.has_key? obj
obj.inject(dedup[obj] = []) {|a,v| a << deep_clear_subclasses(v,dedup)}
else
obj # not going to operate on other kinds of objects
end
end

How is the syntax of Ruby's alias allowed?

This one's been keeping me up at night for a while.
class Foo
def bar
'bar'
end
# What the hell is going on here?!?
alias :baz :bar
end
Foo.new.baz #=> 'bar'
Why does alias take 2 symbol as arguments, but without a comma separating them? That doesn't seem to be any form of valid syntax in any other context. And in fact, if you do use a comma, it actually throws a syntax error.
alias :bar, :baz
# syntax error, unexpected ','
However, if I try to pass 2 symbol in the same way to my own method, it also explodes:
def somemethod(*args)
:whatever
end
somemethod :a :b
# syntax error, unexpected ':', expecting $end
So why is the alias method get to use a syntax nothing else gets to use?
Is it possible to use this syntax in any other context?
What is the benefit of using this odd syntax quirk, when nothing else in the language works this way? I see no discernable benefit to this language inconsistency.
The reason alias works is because it's a Ruby keyword, similar to class, def, etc. It's not a method.
The alias keyword doesn't need a comma because the Ruby designers decided it didn't. Keywords are essentially hardcoded in the interpreter.
There is a good reason for alias when you need to be certain the alias happens at parse time, not runtime.
The alias keyword may be confusing or surprising. For typical development, I believe it's better to use the Ruby method Module#alias_method, which does use a comma and works at runtime.
Here's a good blog post about alias and alias_method:
This is because alias is a keyword and it is lexically scoped. It
means it treats self as the value of self at the time the source code
was read. In contrast alias_method treats self as the value
determined at the run time.
Overall my recommendation would be to use alias_method. Since
alias_method is a method defined in class Module it can be overridden
later and it offers more flexibility.
It's not a method, and those aren't arguments. alias is one of the few keywords in Ruby (see them all here!). It bothers me as well, so I switched to alias_method (and there are other arguments for this).

How to get argument names using reflection

I would like to do some fairly heavy-duty reflection in Ruby. I want to create a function that returns the names of the arguments of various calling functions higher up the call stack (just one higher would be enough but why stop there?). I could use Kernel.caller, go to the file and parse the argument list but that would be ugly and unreliable.
The function that I would like would work in the following way:
module A
def method1( tuti, fruity)
foo
end
def method2(bim, bam, boom)
foo
end
def foo
print caller_args[1].join(",") #the "1" mean one step up the call stack
end
end
A.method1
#prints "tuti,fruity"
A.method2
#prints "bim, bam, boom"
I would not mind using ParseTree or some similar tool for this task but looking at Parsetree, it is not obvious how to use it for this purpose. Creating a C extension like this is another possibility but it would be nice if someone had already done it for me.
I can see that I'll probably need some kind of C extension. I suppose that means my question is what combination of C extension would work most easily. I don't think caller+ParseTree would be enough by themselves.
As far as why I would like to do this goes, rather than saying "automatic debugging", perhaps I should say that I would like to use this functionality to do automatic checking of the calling and return conditions of functions:
def add x, y
check_positive
return x + y
end
Where check_positive would throw an exception if x and y weren't positive. Obviously, there would be more to it than that but hopefully this gives enough motivation.
In Ruby 1.9.2, you can trivially get the parameter list of any Proc (and thus of course also of any Method or UnboundMethod) with Proc#parameters:
A.instance_method(:method1).parameters # => [[:req, :tuti], [:req, :fruity]]
The format is an array of pairs of symbols: type (required, optional, rest, block) and name.
For the format you want, try
A.instance_method(:method1).parameters.map(&:last).map(&:to_s)
# => ['tuti', 'fruity']
Of course, that still doesn't give you access to the caller, though.
I suggest you take a look at Merb's action-args library.
require 'rubygems'
require 'merb'
include GetArgs
def foo(bar, zed=42)
end
method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]]
If you don't want to depend on Merb, you can choose and pick the best parts from the source code in github.
I have a method that is quite expensive and only almost works.
$shadow_stack = []
set_trace_func( lambda {
|event, file, line, id, binding, classname|
if event == "call"
$shadow_stack.push( eval("local_variables", binding) )
elsif event == "return"
$shadow_stack.pop
end
} )
def method1( tuti, fruity )
foo
end
def method2(bim, bam, boom)
foo
x = 10
y = 3
end
def foo
puts $shadow_stack[-2].join(", ")
end
method1(1,2)
method2(3,4,4)
Outputs
tuti, fruity
bim, bam, boom, x, y
I'm curious as to why you'd want such functionality in such a generalized manner.
I'm curious how you think this functionality would allow for automatic debugging? You'd still need to inject calls to your "foo" function. In fact, something based on set_trace_func is more able to be automatic, as you don't need to touch existing code. Indeed this is how debug.rb is implemented, in terms of set_trace_func.
The solutions to your precise question are indeed basically, as you outlined. use caller + parsetree, or open the file and grab the data that way. There is no reflection capability that I am aware of that will let you get the names of arguments. You can approve upon my solution by grabbing the associated method object and calling #arity to then infer what of local_variables are arguments, but though it appears the result of that function is ordered, I'm not sure it is safe to rely on that. If you don't mind me asking, once you have the data and the interface you describe, what are you going to do with it? Automatic debugging was not what initially came to mind when I imagined uses for this functionality, although perhaps it is failing of imagination on my part.
Aha!
I would approach this differently then. There are several ruby libraries for doing design by contract already, including ruby-contract, rdbc, etc.
Another option is to write something like:
def positive
lambda { |x| x >= 0 }
end
def any
lambda { |x| true }
end
class Module
def define_checked_method(name, *checkers, &body)
define_method(name) do |*args|
unless checkers.zip(args).all? { |check, arg| check[arg] }
raise "bad argument"
end
body.call(*args)
end
end
end
class A
define_checked_method(:add, positive, any) do |x, y|
x + y
end
end
a = A.new
p a.add(3, 2)
p a.add(3, -1)
p a.add(-4, 2)
Outputs
5
2
checked_rb.rb:13:in `add': bad argument (RuntimeError)
from checked_rb.rb:29
Of course this can be made much more sophisticated, and indeed that's some of what the libraries I mentioned provided, but perhaps this is a way to get you where you want to go without necessarily taking the path you planned to use to get there?
if you want the value for the default values, too, there's the "arguments" gem
$ gem install rdp-arguments
$ irb
>> require 'arguments'
>> require 'test.rb' # class A is defined here
>> Arguments.names(A, :go)
In fact, the method you describe clearly fails to distinguish arguments from local variables while also failing to work automatically
That's because what you're trying to do is not something which is supported. It's possible (everything is possible in ruby), but there's no documented or known way to do it.
Either you can eval the backtrace like what logan suggested, or you can bust out your C compiler and hack sourcecode for ruby. I'm reasonably confident there aren't any other ways to do this.

Resources