I'm learning Ruby and I like playing with irb to discover new features and tricks. Today I was playing with variables and methods because I wanted to know which one took preference in front of the other one. Everything looked fine until I tried this:
def test
puts "hello"
end
test = "bye" if false
puts test
I was expecting this to return "hello" , but it doesn't. So, I suppose the parser is treating 'test' as a variable instead of as a method. I have two questions:
Is my assumption correct?
Is there any way to know if something is a variable or a method? Some method like test.is_variable?
test = "hello" if false
p test #=> nil
The local variable test is created anyway (with default value nil), and given that local variables overshadow methods with the same name, that's the value you get. Just an hour ago someone got bitten by a subtle variation of the theme. And don't you think this only happens with one-liner conditionals:
if false
test = "hello"
end
p test #=> nil
That's because Ruby defines variables when they are parsed (and not when they are executed).
There are at least two methods that help: methods and local_variables. I wouldn't recommend using them in real world programs, but they might be useful when learning Ruby.
Related
I have a function in my .irbrc which basically builds together a string form certain input parameters, and then is supposed to check, whether another function with name of this string exists. I'm doing it like this
methodname = ... # Calculate a string
if respond_to?(methodname)
....
end
This does not work in that respond_to? returns false even in those cases where in my opinion it should return true. I have boiled down the problem to the following simple case:
I have in my .irbrc
def foo
end
def respond_to_foo?
respond_to?(:foo)
end
puts "Respond: #{respond_to_foo?}"
Running irb, this outputs false. I would expect it to print true instead. Still, I am able to run foo from within irb.
I guess that this has to do with the scope in which irb defines my methods. For instance, self.foo does not work (private method 'foo' called for main:Object), while send.foo does work (since it bypasses privacy). This looks like a clue to my problem, but I still can't come up with an explanation, nor find the proper way for doing my task.
Could someone enlighten me?
To answer my own question: It just occured to me, that I could use
private_methods.include?(:foo)
This does work, but it looks a bit like a hack to me. If someone knows a better way, please let me know.
I'm looking for the shortest, most simple Ruby one-liner to execute a statement exactly once. Idea is to use this while debugging to quickly add a debug statement to a loop that gets executed exactly once.
Best I've come up with so far:
puts "do something interesting exactly once!" if (once ||= "0").next! == "1"
Can you come up with something even shorter?
Added for clarification:
The idea for the questions was to focus on the "do it once" part and not so much on the "do something interesting" part. It should be assumed that the code do be executed once could be anything, not just a puts statement.
The ideal solution would also work in different kinds of loop constructs. E.g. as was pointed out my initial solution only works if the once variable is already defined outside the loop context or if the loop context used doesn't create a new lexical scope.
The original use case that triggered this question was slightly different - it looked more like below. But I though the above, simpler example would more easily explain the kind of solution I was looking for.
def process
do_some_preprocessing()
raise SomeError if <once> # added temp. for debugging purposes - the <once> part is what this question is about!
dangerous_operation() # this can raise SomeError under certain conditions
rescue SomeError
attempt_to_rescue() and retry
end
Well, you could abuse lambdas and closures.
->{puts "do something interesting exactly once!";x=->{x}}[][]
#=> do something interesting exactly once!
#=> #<Proc:0x5465282c#(irb):10 (lambda)>
The original contents of the lambda are only run once; any subsequent invocations will simply return an empty proc.
You could alternately abuse globals for a more true "one-liner", but it's awful.
$x ||= puts("do something interesting exactly once!") || 1
debug = ["do something interesting exactly once!"]
puts debug.pop # "do something interesting exactly once!"
puts debug.pop # nil
(answer edited to reflect the discussion in comments)
Your code won't do what you want it to do, it will depend of the looping construct you use.
This will work:
puts "do something interesting exactly once!" if once = once.nil?
But with this one, you'll have to define once before: once = nil (same thing for your own code). This is because otherwise, the scope of the once variable will be restrained to the block within an each loop, causing it to fail. This would work just fine within a for loop (the way you must have tested it):
(1..3).each do # 3.times would behave just the same
puts "once has not been defined before! Won't work!" if once = once.nil?
end
# >once has not been defined before! Won't work!
# once has not been defined before! Won't work!
# once has not been defined before! Won't work!
for i in 1..3 do
puts "Works because of the way for loops treat vars within loop" if once = once.nil?
end
# >Works because of the way for loops treat vars within loop
To avoid that problem without having to initialize the variable first, you can make once global:
(1..3).each do
puts "$once's scope is not restrained to the 'each' loop! Works!" if $once = $once.nil?
end
# >$once's scope is not restrained to the 'each' loop! Works!
The original idea generates code-smell. It results in code that will leave someone else scratching their head, which isn't a good thing. Generating code that is obvious and easy to understand will make your, and other programmer's, job easier.
Writing code that takes a while to figure out will take you a while to figure out in the future if you're debugging so be kind to your future self.
I'd stick with the standard way, using a simple flag:
once = false
2.times do
puts "do something interesting exactly once!" unless once
once ||= true
end
Which results in this output:
# >> do something interesting exactly once!
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
I want to programmatically insert a new variable into the local Ruby namespace. For example, I want to be able to write
label = 'some_name'
# some code equivalent to
# some_name = 3
# but using only 'label' to get the name.
puts some_name # returns 3
What do I put in the middle here to get this done?
I've answered another SO question similar to this. The short answer is this, if you specifically want to create a local variable with the name of it based on the value of another variable, then there is no way to do it. It you just want to make seem as though you've created a local but it is really ruby magic, then something like #mikong's answer is one way to go.
Note that if you relax your contraint and are happy to create an instance variable instead, then you can do it.
label = 'some_name'
self.instance_variable_set("#{label}", 3)
puts #some_name
You can even dynamically define an accessor and then you can get rid of the unsightly #, but once again you will simply have a method masquerading as a local rather than a real local variable.
The following is not exactly code between the 2 lines that you mentioned above:
class Example
attr_accessor :label
def method_missing(name, *args, &block)
return some_processing if name == label.to_sym
end
def some_processing
3 # of course, this can be something more complicated
end
def test
#label = 'some_name'
puts some_name
end
end
Nonetheless it seems to work with what you need. The mechanism has changed from what you gave (label is now an attribute). Also, technically, it's not a variable but a method with a dynamic name that returns what you need.
Personally, I think your requirements seem a little bit dangerous in that the "variable" name changes. I would probably not use the code in my example. I guess depending on the project requirements, I'll think of a different approach.
label = 'some_name'
eval "#{label} = 3"
puts eval "#{label}"
puts local_variables
Note that you would presumably never have an opportunity to execute...
puts some_name
...because if you knew what local variables you were going to create there would be no need to name them with run-time code. And that's good, because the interpreter will not be able to puts some_name directly because it never parsed an assignment for some_name. But it is there and it is a local, as puts local_variables is able to show.
I was writing a small Heap implementation and upon creating my Node class I noticed some weird behaviour. I wanted to call defined?(x) to ensure x was defined, then check if x was an Integer, before storing it in the Node's value class variable. In IRB I can call
defined?(x) and the result is nil.
However, in the class, I try this:
def change_value value
#value = value if defined?(value)
end
and the result when I call the change_value with a random letter, let's say 'e', is the standard undefined local variable or method error. Again, in IRB it seems to work fine and I am wondering if I have some kind of environment issue or if this is not the 'best' way to check if value is really there.
Thanks.
(Edit: DigitalRoss has since cleaned up the question's formatting, so the comment on formatting and the initial re-write may no longer apply. I'll leave them in until I get some feedback from the OP.)
That's a nearly unreadable way to write a method and it doesn't even parse on my machine (Ruby 1.8.7).
I'm assuming you mean the following:
def change_value(value)
#value = value if defined?(value)
end
This works fine when I call it, but it's incorrect. nil and undefined are two different beasts in Ruby; value will always be defined in that context because it's a declared method parameter. I suspect what you are really after is:
def change_value(value)
#value = value unless value.nil?
end
Note that some people would simply write this as:
def change_value(value)
#value = value if value
end
because nil is "falsy". However, this form conflates nil and false, so it's not a good habit to get into.
This isn't the best way to check if it's really there. If change_value is called without providing any parameters, you would get:
ArgumentError: wrong number of arguments (0 for 1)
Instead, you might want to check to make sure value is not nil. Of course you can do this a variety of ways:
if !value.nil?
#...
end
if value
# this will be exeuted if value is either not `nil` or not `false`
end
Best of luck!
Would it have to do with calling the method from the class instance and not the object instance?