How to disable warning for redefining a constant when loading a file - ruby

Is there a way to disable warning: already initialized constant when loading particular files?

The solution to your problem depends on what is causing it.
1 - You are changing the value of a constant that was set before somewhere in your code, or are trying to define a constant with the same name as an existant class or module. Solution: don't use constants if you know in advance that the value of the constant will change; don't define constants with the same name as class/modules.
2 - You are in a situation where you want to redefine a constant for good reasons, without getting warnings. There are two options.
First, you could undefine the constant before redefining it (this requires a helper method, because remove_const is a private function):
Object.module_eval do
# Unset a constant without private access.
def self.const_unset(const)
self.instance_eval { remove_const(const) }
end
end
Or, you could just tell the Ruby interpreter to shut up (this suppresses all warnings):
# Runs a block of code without warnings.
def silence_warnings(&block)
warn_level = $VERBOSE
$VERBOSE = nil
result = block.call
$VERBOSE = warn_level
result
end
3 - You are requiring an external library that defines a class/module whose name clashes with a new constant or class/module you are creating. Solution: wrap your code inside a top-level module-namespace to prevent the name clash.
class SomeClass; end
module SomeModule
SomeClass = '...'
end
4 - Same as above, but you absolutely need to define a class with the same name as the gem/library's class. Solution: you can assign the library's class name to a variable, and then clear it for your later use:
require 'clashing_library'
some_class_alias = SomeClass
SomeClass = nil
# You can now define your own class:
class SomeClass; end
# Or your own constant:
SomeClass = 'foo'

Try this :
Kernel::silence_warnings { MY_CONSTANT = 'my value '}

To suppress warnings, use the following code at the top of the script:
$VERBOSE = nil

The accepted answer to this question was helpful. I looked at the Rails source to get the following. Before and after loading the file, I can insert these lines:
# Supress warning messages.
original_verbose, $VERBOSE = $VERBOSE, nil
load(file_in_question)
# Activate warning messages again.
$VERBOSE = original_verbose

Using user2398029's reply the simplest way for me to remove warnings was to add this line:
before { described_class.instance_eval { remove_const(:CONSTANT_NAME) } }

Related

Why raising NameError when constant removed even if I have set autoload for it?

The following code:
module P; end
P.autoload(:Foo, "#{__dir__}/foo.rb")
P::Foo.new.foo # works well
P.send(:remove_const, :Foo)
P::Foo.new.foo # raising error
I have set the autoload for P::Foo, but it does not work for second time. Why and How?
Even if I reset the autoload it still not works:
P.send(:remove_const, :Foo)
P.autoload(:Foo, "#{__dir__}/foo.rb")
P::Foo.new.foo # raising error
Per the Docs for Module#autoload:
Registers filename to be loaded (using Kernel::require) the first time that module (which may be a String or a symbol) is accessed in the namespace of mod.
Per the Docs for Kernel#require:
Loads the given name, returning true if successful and false if the feature is already loaded.
You first called the P::Foo which "autoloaded" the "#{__dir__}/foo.rb" file via Kernel#require. Then you removed the constant P::Foo (defined in that file) but the feature was already loaded so the subsequent call does not try and require it again due to the fact that Kernel.require("#{__dir__}/foo.rb") #=> false.
This would be identical to an explicit require e.g.
p/foo.rb
module P
class Foo
def foo; end
end
end
p.rb
module P; end
require './p/foo' #=> true
P::Foo.new.foo # works well
P.send(:remove_const, :Foo)
require './p/foo' #=> false
P::Foo.new.foo # raising error
If you want to "force" the reloading of that file you can call Kernel#load instead.

Check if a namedspaced class exists

I'm writing a helper to check if a given class exists.
def safe_constant(constant_sym)
return Object.const_get(constant_sym) if Object.const_defined? constant_sym
end
This works just fine for something like safe_constant(:User)
However, we have a number of objects that are namespaced in modules
safe_constant(:Admin::User) is obviously not a valid symbol. I know I could determine if this exists by doing:
Object.const_get(:Admin).const_get(:User) but before I go writing some kind of split loop drill-down method to get the final constant, is there any standard way to handling this already?
According to Ruby API document, it's possible to get const_defined? and const_get working with namespace string to check and return the class constant. What needs to be done is passing the second argument (inherit) as false, for example:
class User; end
module Admin
class User; end
end
Object.const_defined?('Admin::User', false) # => true
Object.const_get('Admin::User', false) # => Admin::User
PS: const_defined? and const_get do not accept namespaced Symbol and will raise NameError exception, for example:
# namespaced String
Object.const_defined?('Admin::User', false) # => true
# namespaced Symbol
Object.const_defined?(:'Admin::User', false)
# => NameError (wrong constant name Admin::User)
How about something like this?
def safe_constant(constant_sym)
return constant_sym.to_s.constantize if valid_constant? constant_sym.to_s
end
def valid_constant?(const)
const.constantize
return true
rescue NameError
return false
end

How can I mimic Node.js's require function in Ruby?

In node.js you can write:
var lib = require('lib');
but in Ruby the require function simply runs the code in the file and true is returned.
Currently, I'm using a very dirty solution:
main.rb:
$stuff = []
require './file1.rb'
require './file2.rb'
# and so on
file1.rb:
$stuff << something
and so on.
How can I eliminate the use of a global variable?
eg:
main.rb:
$stuff = []
$stuff << cool_require './file1.rb'
# etc
file1.rb:
exports.what = something
One of the biggest errors when working with a language, is trying to make the language working like a different one.
Ruby is not NodeJs, there are features built-in into each language that are unique to the language and cannot be reproduced easily.
In other words, there is no way to implement the NodeJS require behavior in Ruby because in Ruby there is no notion of export. When you require a file, every method/class included in the required file are made available to the scope.
In Ruby there are objects and method visibility. The way you have to make a method visible or not is to declare it as public or private/protected.
Well, first consider that Ruby is not Node.js. As Simone Carletti said, there are some features that are unique to each language. Sometimes it's good to take from other language but sometimes it's bad.
There are few things that you must keep in mind:
meth is method invocation, to pass method you use method method: method(:meth) or package it into module/class
you can use class/module by assigning it to some 2nd variable:
class A;
def self.aa; puts 'aa'; end;
end;
New_a = A;
New_a.aa # aa;
eval is dangerous method(you can evaluate unknown code)
Method:
Here is one way you can do. It is not idiot-proof tough. It is not 100% safe(eval). :
file1.rb:
Module.new do
def self.meth1
42
end
def self.meth2
'meth2'
end
end
This file contain module with 2 methods.
I am using Module.new because it returns object that you want. You can assign it later into variable/constant.
I am using self.meth* so you don't have to include but run instantly it like this: module_name.meth1()
req.rb:
def cool_require name
eval(File.read name)
end
Some_variable = cool_require('req.rb')
puts Some_variable.meth1 # 42
puts Some_variable.meth2 # meth2
cool_require reads filename(argument name) and evaluate it(it is just like you would type it in irb/pry)
Some_variable is constant. It won't disappear that easily.
2 last line is how it works. As fair I remember, that's how node.js' require works.

rails rspec - how to check for a model constant?

How can I do something like:
it { should have_constant(:FIXED_LIST) }
In my model (active record) I have FIXED_LIST = 'A String'
It's not a db attribute or a method and I haven't been able to use responds_to or has_attribute to test for it (they fail). What can I use the to check for it. - btw I have the shoulda-matchers installed.
Based on David Chelimsky's answer I've got this to work by slightly modifying his code.
In a file spec/support/utilities.rb (or some other in spec/support) you can put:
RSpec::Matchers.define :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
Note the use of "RSpec::Matchers.define" in stead of "matchers"
This allows to test for constants in your specs, like:
it "should have a fixed list constant" do
YourModel.should have_constant(:FIXED_LIST)
end
Note the use of "have_constant" in stead of "have_const"
It reads a little silly, but:
describe MyClass do
it { should be_const_defined(:VERSION) }
end
The reason is that Rspec has "magic" matchers for methods starting with be_ and have_. For example, it { should have_green_pants } would assert that the has_green_pants? method on the subject returns true.
In the same fashion, an example such as it { should be_happy } would assert that the happy? method on the subject returns true.
So, the example it { should be_const_defined(:VERSION) } asserts that const_defined?(:VERSION) returns true.
If you want to say have_constant you can define a custom matcher for it:
matcher :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
MyClass.should have_const(:CONST)
If you're trying to use the one-liner syntax, you'll need to make sure the subject is a class (not an instance) or check for it in the matcher:
matcher :have_constant do |const|
match do |owner|
(owner.is_a?(Class) ? owner : owner.class).const_defined?(const)
end
end
See http://rubydoc.info/gems/rspec-expectations/RSpec/Matchers for more info on custom matchers.
HTH,
David
Another option to simply make sure the constant is defined – not worrying about what it's defined with:
it 'has a WHATEVER constant' do
expect(SomeClass::WHATEVER).not_to be_nil
end
A warning to anyone trying to test that constants are defined: If your code references an undefined constant while defining a class, then your specs will crash before they get to your test.
This can lead you to believe that
expect { FOO }.to_not raise_error
is failing to catch the NameError, because you'll get a big stack trace, instead of a nice "expected not to raise error, but raised NameError."
Amidst the huge stack trace, it can be difficult to notice that your test is actually crashing on line 1: requre "spec/spec_helper" because your entire application is failing to load before it gets to your actual test.
This can happen if you have dynamically defined constants, such as is done by ActiveHash::Enum, and you then use them in the definition of another constant. Don't bother testing that they exist, every spec in your app will crash if one of them fails to be defined.
You could use
defined? YOUR_MODEL::FIXED_LIST
In RSpec 2, I was able to get this to work in one line as follows:
it { subject.class.should be_const_defined(:MY_CONST) }
That is, check against the class, instead of the instance.
In My model
class Role < ActiveRecord::Base
ROLE_ADMIN = "Administrador"
end
In My rspec
RSpec.describe Role, type: :model do
let(:fake_class) { Class.new }
describe "set constants" do
before { stub_const("#{described_class}", fake_class) }
it { expect(described_class::ROLE_ADMIN).to eq("Administrador") }
end
end
For ruby 2.1.5 and rspec 3.5.0 I am able to test that constant SEARCH_CHARS_TO_IGNORE is defined in the class DiffAlertsDatatable as follows:
expect(DiffAlertsDatatable.const_defined?(:SEARCH_CHARS_TO_IGNORE)).to eq(true)

ruby: how to load .rb file in the local context

How this simple task can be done in Ruby?
I have some simple config file
=== config.rb
config = { 'var' => 'val' }
I want to load config file from some method, defined in main.rb file so that the local variables from config.rb became local vars of that method.
Something like this:
=== main.rb
Class App
def loader
load('config.rb') # or smth like that
p config['var'] # => "val"
end
end
I know that i can use global vars in config.rb and then undefine them when done, but i hope there's a ruby way )
The config file.
{ 'var' => 'val' }
Loading the config file
class App
def loader
config = eval(File.open(File.expand_path('~/config.rb')).read)
p config['var']
end
end
As others said, for configuration it's better to use YAML or JSON. To eval a file
binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb")
binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")
This syntax would allow you to see filename in backtraces which is important. See api docs [1].
Updated eval command to avoid FD (file descriptor) leaks. I must have been sleeping or maybe should have been sleeping at that time of the night instead of writing on stackoverflow..
[1] http://www.ruby-doc.org/core-1.9.3/Binding.html
You certainly could hack out a solution using eval and File.read, but the fact this is hard should give you a signal that this is not a ruby-like way to solve the problem you have. Two alternative designs would be using yaml for your config api, or defining a simple dsl.
The YAML case is the easiest, you'd simply have something like this in main.rb:
Class App
def loader
config = YAML.load('config.yml')
p config['var'] # => "val"
end
end
and your config file would look like:
---
var: val
I do NOT recommend doing this except in a controlled environment.
Save a module to a file with a predetermined name that defines an initialize and run_it methods. For this example I used test.rb as the filename:
module Test
##classvar = 'Hello'
def initialize
#who = 'me'
end
def get_who
#who
end
def run_it
print "#{##classvar} #{get_who()}"
end
end
Then write a simple app to load and execute it:
require 'test'
class Foo
include Test
end
END {
Foo.new.run_it
}
# >> Hello me
Just because you can do something doesn't mean you should. I cannot think of a reason I'd do it in production and only show it here as a curiosity and proof-of-concept. Making this available to unknown people would be a good way to get your machine hacked because the code could do anything the owning account could do.
I just had to do a similar thing as I wanted to be able to load a "Ruby DLL" where it returns an anonymous class ( a factory for instances of things ) I created this which keeps track of items already loaded and allows the loaded file to return a value which can be anything - a totally anonymous Class, Module, data etc. It could be a module which you could then "include" in an object after it is loaded and it could could supply a host of "attributes" or methods. you could also add an "unload" item to clear it from the loaded hash and dereference any object it loaded.
module LoadableModule
##loadedByFile_ = {};
def self.load(fileName)
fileName = File.expand_path(fileName);
mod = ##loadedByFile_[fileName];
return mod if mod;
begin
Thread.current[:loadReturn] = nil;
Kernel.load(fileName);
mod = Thread.current[:loadReturn];
##loadedByFile_[fileName] = mod if(mod);
rescue => e
puts(e);
puts(e.backtrace);
mod = nil;
end
Thread.current[:loadReturn] = nil;
mod
end
def self.onLoaded(retVal)
Thread.current[:loadReturn] = retVal;
end
end
inside the loaded file:
LoadableModule.onLoaded("a value to return from the loaded file");

Resources