How to redefine a Ruby constant without warning? - ruby

I'm running some Ruby code which evals a Ruby file every time its date changes. In the file, I have constant definitions, like
Tau = 2 * Pi
and, of course, they make the interpreter display the unwanted "already initialized constant" warning every time, so, I'd like to have the following functions:
def_if_not_defined(:Tau, 2 * Pi)
redef_without_warning(:Tau, 2 * Pi)
I could avoid the warning by writing all my constant definitions like this:
Tau = 2 * Pi unless defined?(Tau)
but it is inelegant and a bit wet (not DRY).
Is there a better way to def_if_not_defined? And how to redef_without_warning?
--
Solution thanks to Steve:
class Object
def def_if_not_defined(const, value)
mod = self.is_a?(Module) ? self : self.class
mod.const_set(const, value) unless mod.const_defined?(const)
end
def redef_without_warning(const, value)
mod = self.is_a?(Module) ? self : self.class
mod.send(:remove_const, const) if mod.const_defined?(const)
mod.const_set(const, value)
end
end
A = 1
redef_without_warning :A, 2
fail 'unit test' unless A == 2
module M
B = 10
redef_without_warning :B, 20
end
fail 'unit test' unless M::B == 20
--
This question is old. The above code is only necessary for Ruby 1.8. In Ruby 1.9, P3t3rU5's answer produces no warning and is simply better.

The following module may do what you want. If not it may provide some pointers to your solution
module RemovableConstants
def def_if_not_defined(const, value)
self.class.const_set(const, value) unless self.class.const_defined?(const)
end
def redef_without_warning(const, value)
self.class.send(:remove_const, const) if self.class.const_defined?(const)
self.class.const_set(const, value)
end
end
And as an example of using it
class A
include RemovableConstants
def initialize
def_if_not_defined("Foo", "ABC")
def_if_not_defined("Bar", "DEF")
end
def show_constants
puts "Foo is #{Foo}"
puts "Bar is #{Bar}"
end
def reload
redef_without_warning("Foo", "GHI")
redef_without_warning("Bar", "JKL")
end
end
a = A.new
a.show_constants
a.reload
a.show_constants
Gives the following output
Foo is ABC
Bar is DEF
Foo is GHI
Bar is JKL
Forgive me if i've broken any ruby taboos here as I am still getting my head around some of the Module:Class:Eigenclass structure within Ruby

Another approach, using $VERBOSE, to suppress warnings, is discussed here: http://mentalized.net/journal/2010/04/02/suppress_warnings_from_ruby/
Update 2020/5/6: In response to the comment that the link is now dead, I am pasting an example here from my old project, though I can't say whether and in what circumstances it is a good approach:
original_verbose = $VERBOSE
$VERBOSE = nil # suppress warnings
# do stuff that raises warnings you don't care about
$VERBOSE = original_verbose

If you want to redefine a value then don't use constants, use a global variable instead ($tau = 2 * Pi), but that's not a good practice too. You should make it an instance variable of a suitable class.
For the other case, Tau = 2 * Pi unless defined?(Tau) is perfectly alright and the most readable, therefore the most elegant solution.

Unless the values of the constants are pretty weird (i.e. you have constants set to nil or false), the best choice would be to use the conditional assignment operator: Tau ||= 2*Pi
This will set Tau to 2π if it is nil, false or undefined, and leave it alone otherwise.

Related

Short hand way to evaluate args in ruby

I am currently using the following code in a ruby program to evaluate variable length arguments that are passed to a method. The program is running however I'm wondering if there is a short hand way to write this.
Should have been more specific in my original description, trying to rewrite the Inject method for the Array class (hence the witty name...)
Therefore it needs to be able to accept a maximum of two args, and a minimum 0 if a block is given.
array.inject(:+)
array.inject{ |output, num| output + num }
array.inject(arg, :+)
array.inject(arg) { |output, num| output + num }
The most difficult case/s to handle are the first and forth where the 1 arg can be either a Fixnum or a Symbol. As mentioned, the code works, just looking for ways to tidy it up.
class Array
def enjict(*args)
if args.length == 2 && args[0].is_a?(Fixnum) && args[1].is_a?(Symbol)
start, symbol = args
elsif args.length == 1
raise ArgumentError unless args.first.is_a?(Symbol) || args.first.is_a?(Fixnum)
symbol = args.first if args.first.is_a?(Symbol)
start = args.first if args.first.is_a?(Fixnum)
else
raise ArgumentError unless block_given?
end
copiedArray = dup
start = copiedArray.shift unless start
if block_given?
copiedArray.each { |num| start = yield(start, num) }
else
copiedArray.each { |num| start = start.send(symbol, num) }
end
start
end
end
The sad truth is: it's messy, and there's nothing you can do about it. Almost all Ruby implementations implement Enumerable#inject with privileged access to the interpreter internals, including introspection of the arguments. MRI, YARV, MRuby implement it in C, MacRuby and RubyMotion in Objective-C, XRuby and JRuby in Java, Ruby.NET and IronRuby in C#, Topaz in RPython, Cardinal in PIR, and so on.
This is something that is simply not available to Ruby code.
Only Rubinius implements it in Ruby.
You can use a similar trick by (ab)using the fact that the default argument expression for an optional parameter can be any arbitrarily complex Ruby expression and that local variables of those expressions become local variables of the method. This is a common trick for figuring out whether an argument was passed or not:
def inject(initial=(no_initial = true; nil), sym=(no_sym = true; nil))
sym, initial = initial, nil if !block_given && no_sym
# and so on …
end
Judging from the conditions, how about refactoring your method arguments to:
def enjict(start, symbol, *options, &block)
e = proc{ raise ArgumentError if options.length > 0 && !block_given? }
e.call
if start.is_a?(Fixnum) && symbol.is_a?(Symbol)
# do something you want
else
e.call
end
end

Dynamic methods using define_method and eval

I've put together two sample classes implemented in a couple of different ways which pretty well mirrors what I want to do in my Rails model. My concern is that I don't know what, if any are the concerns of using either method. And I've only found posts which explain how to implement them or a general warning to avoid/ be careful when using them. What I have not found is a clear explanation of how to accomplish this safely, and what I'm being careful of or why I should avoid this pattern.
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
define_method("#{t}_add") do |val|
val = ActiveRecord::Base.send(:sanitize_sql_array, ["%s", val])
eval("##{t}_sc += #{val}")
end
end
end
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
# eval <<-EVAL also works
self.class_eval <<-EVAL
def #{t}_add(val)
##{t}_sc += val
end
EVAL
end
end
x = X.new
x.yn_add(1) #=> x.yn_sc == 1 for both
Well, your code looks realy safe. But imagine a code based on user input. It might be look something like
puts 'Give me an order, sir!'
order = gets.chomp
eval(order)
What will happen if our captain will go wild and order us to 'rm -rf ~/'? Sad things for sure!
So take a little lesson. eval is not safe because it evaluates every string it receives.
But there's another reason not to use eval. Sometimes it evaluates slower than alternatives. Look here if interested.

Ruby min max assignment operators

When programming ruby I always find myself doing this:
a = [a, b].min
This means compare a and b and store the smallest value in a. I don't like writing the code above as I have to write a twice.
I know that some non-standard dialects of C++ had an operator which did exactly this
a <?= b
Which I find very convenient. But I'm not really interested in the operator as much as I'm in the feature of avoiding repetition. I would also be happy if I could write
a.keep_max(b)
a can be a quite long variable, like my_array[indice1][indice2], and you don't want to write that twice.
I did alot of googling on this and found no result, hopefully this question will pop up and be useful for others aswell.
So, is there any non-repeitive way to express what I want in ruby?
What you would like to do is in fact not possible in ruby (see this question). I think the best you can do is
def max(*args)
args.max
end
a = max a, b
I don't understand your question. You can always do something like this ...
module Comparable
def keep_min(other)
(self <=> other) <= 0 ? self : other
end
def keep_max(other)
(self <=> other) >= 0 ? self : other
end
end
1.keep_min(2)
=> 1
1.keep_max(2)
=> 2
Well, that won't work for all objects with <=> because not all of them are implementing Comparable, so you could monkey-patch Object.
Personally I prefer clarity and tend to avoid monkey-patching. Plus, this clearly is a binary predicate, just like "+", therefore method-chaining doesn't necessarily make sense so I prefer something like this to get rid of that array syntax:
def min(*args)
args.min
end
def max(*args)
args.max
end
min(1, 2)
=> 1
max(1, 2)
=> 2
But hey, I'm also a Python developer :-)
You can define your own method for it:
class Object
def keep_max(other)
[self, other].max
end
end
a = 3
b = 7
puts a.keep_max(b)
But you should be careful defining methods on Object as it can have unpredictable behaviour (for example, if objects cannot be compared).
def keep_max(var, other, binding)
eval "#{var} = [#{var}, #{other}].max", binding
end
a = 5
b = 78
keep_max(:a, :b, binding)
puts a
#=> 78
This basically does what you want. Take a look at Change variable passed in a method

Is it possible to access block's scope in method?

I'd like to write the method (define_variables) which can get a block and use the variables defined in it. Is it possible? For example, I'd like to get 5 in output:
module A
def self.define_variables
yield
puts a # not 5 :(
end
end
A::define_variables do
a = 5
end
Maybe there is some tricks with eval, but haven't found anyone yet.
In short, no. After you've called yield those variables defined in the block are gone (sort of, as we shall see), except for what is returned—that's just how scope works. In your example, the 5 is still there in that it is returned by the block, and thus puts yield would print 5. Using this you could return a hash from the block {:a => 5}, and then access multiple "variables" that way. In Ruby 1.8 (in IRb only) you can do:
eval "a = 5"
a # => 5
Though I don't know of anyway to eval the contents of a block. Regardless, in Ruby 1.9 the scope of eval was isolated and this will give you a NameError. You can do an eval within the context of a Binding though:
def foo
b = yield
eval(a, b) + 2
end
foo do
a = 5
binding
end # => 7
It seems to me that what you're trying to do is emulate macros in Ruby, which is just not possible (at least not pure Ruby), and I discourage the use of any of the "workarounds" I've mentioned above.
Agreed that this is a bit backwards, and Andrew's explanation is correct. If your use case is defining variables, however, there are already class_variable_set and instance_variable_set methods that are great for this:
module A
def self.define_variables(vars = {})
vars.each { |n, v| class_variable_set n, v }
puts ##a
end
end
A::define_variables :##a => 5
The above is more of an example of how it would work within the code you've posted rather than a recommendation.

In Ruby, how can you invoke a method using its default argument or with a specified argument without repeating code?

Say I have a ruby method:
def blah(foo=17)
...
end
In code I want to invoke blah with a specific argument "blah(a)" or invoke blah using its default argument "blah()" Is there any way to do that without specifying the method name twice? I'm trying to avoid:
if a.nil?
blah()
else
blah(a)
end
Because it makes the code look more complicated than it is. Best I can come up with (didn't test) is:
args=[]
args << a unless a.nil?
a.send :blah, args
I just tried a few ways, and didn't find any, but if you find yourself doing this a lot, I wonder of the benefit of using a default parameter that way. Try this instead:
def blah(foo=nil)
foo ||= 17
puts foo
end
blah()
a = nil
blah(a)
a = 20
blah(a)
This will output:
17
17
20
I don't like this answer, but I guess it works:
blah( *[a].compact )
It's hard to say without knowing what the actual problem being solved is, but I have a feeling something like this would work best:
blah(a || 17)
This statement seems to more clearly express its intent, without leaving the reader to lookup the definition of the blah function in order to work out what the default is.
In ruby everything has a return value. So if also has a return value. The following method works with any number of arguments. The * expands an array so the array elemnts are individual arguments.
blah(*
if a.nil?
[]
else
[a]
end
)
You don't have to indent it this way, but this looked like the indentation to make it clearest.
I was looking for the same. (I think it is almost a bug in Ruby specs)
I think I will solve this in other way ussing the option hash.
def foo(options={})
bar = options.delete(:bar) || :default
puts bar
end
foo
#=> default
foo :bar => :something
#=> something
a = nil
foo :bar => a
#=> default
It is more extensible and is readable for any rubyst.
I vote for
def blah(foo = nil)
foo ||= 17
end
too.
But if you would like to distinguish between the nil-value and
no-argument situations, I suggest using a block like so:
def blah
foo = block_given? ? yield : 17
end
blah #=> 17
blah { nil } #=> nil
blah { 71 } #=> 71

Resources