Set a Ruby variable and never be able to change it again? [duplicate] - ruby

This question already has answers here:
Closest Ruby representation of a 'private static final' and 'public static final' class variable in Java?
(4 answers)
Closed 8 years ago.
Giving a Java example:
class MyClass {
private final int variable;
MyClass(int variable) {
this.variable = variable;
}
}
Is there something like final I can do in Ruby? I looked up freeze but am not sure if it's the right approach. I don't need the variable to be assigned while creating a new instance of my class, I need to set it in one of the methods and once it's set I want to freeze it.

They are called constants. A constant in Ruby is defined by a UPPER_CASE name.
VARIABLE = "foo"
It is worth to mention that, technically, in Ruby there is no way to prevent a variable to be changed. In fact, if you try to re-assign a value to a constant you will get a warning, not an error.
➜ ~ irb
2.1.5 :001 > VARIABLE = "foo"
=> "foo"
2.1.5 :002 > VARIABLE = "bar"
(irb):2: warning: already initialized constant VARIABLE
(irb):1: warning: previous definition of VARIABLE was here
=> "bar"
It's also worth to note that using constants will warn you if you try to replace the value of the constant, but not if you change the constant value in place.
2.1.5 :001 > VARIABLE = "foo"
=> "foo"
2.1.5 :002 > VARIABLE.upcase!
=> "FOO"
2.1.5 :003 > VARIABLE
=> "FOO"
In order to prevent changes to the value referenced by the constant, you can freeze the value once assigned.
2.1.5 :001 > VARIABLE = "foo".freeze
=> "foo"
2.1.5 :002 > VARIABLE.upcase!
RuntimeError: can't modify frozen String
from (irb):2:in `upcase!'
from (irb):2
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :003 > VARIABLE
=> "foo"
Here's an example inside a class.
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT
# => "foo"

The Ruby equivalent of your code looks like this:
class MyClass
def initialize(variable)
#variable = variable.freeze
end
end
Generally this is frowned upon as this method doesn't have ownership of the object variable represents, so the caller might be in for a rude surprise when their object is suddenly frozen.
Note that this prevents manipulating the #variable object, but doesn't prevent repeated assignment to that property. There's nothing that can block that behaviour.
You can alway make a copy if applicable and freeze that.
In general practice you don't normally do this, but instead take a disciplined approach about not manipulating objects your class doesn't own, or making copies of them if you do need to make changes.
So, in summary, there's no tradition of locking things down like this in Ruby and very little support in the language for that sort of behaviour.

The closest thing might be a constant. A Ruby constant is like a variable, except that its value is supposed to remain constant for the duration of the program. The Ruby interpreter does not actually enforce the constancy of constants, but it does issue a warning if a program changes the value of a constant.

Related

Ruby: how to determine an object type in irb and binding.pry?

I have just begun adding binding.pry after my objects in order to start determining what they evaluate to. However, now I want to know what type of object the output is.
How can I do this in irb? How can I do this in binding.pry?
EDIT:
Here is what I have tried to determine the type of object H. I know it is a hash, but sometimes it is less obvious in the console:
irb(main):001:0> H = Hash["a" => 100, "b" => 200]
=> {"a"=>100, "b"=>200}
irb(main):002:0> H
=> {"a"=>100, "b"=>200}
irb(main):003:0> type(H)
NoMethodError: undefined method `type' for main:Object
from (irb):3
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
irb(main):004:0> object.class(H)
NameError: undefined local variable or method `object' for main:Object
Did you mean? object_id
from (irb):4
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
irb(main):005:0> object.is_a?(H)
NameError: undefined local variable or method `object' for main:Object
Did you mean? object_id
from (irb):5
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
You can get the class with
h = {a: 100, b: 200}
h.class
# Hash
You can also check if something is of a particular class
h.is_a? Hash
# true
h.is_a? String
# false
The Ruby Language has no concept of "type". Or, to be more precise: in Ruby, types only exist latently in the mind of the programmer, they are not manifest in the program. Ergo, there is no way to get an object's type from the program, you can only get it from the programmer.
Sometimes, types are written down in the documentation or in comments. Some types are basically community folklore, not written down anywhere but passed down from generation to generation.
You can ask an object about its class, you can ask it about its methods, you can ask it whether it responds to a specific message, but you can not ask it about its type.
Note that older versions of Ruby had a type but that method was removed because it was misleading: it didn't actually return the type (I explained above why that is impossible), it returned the class, which is something completely different.

What does IRB use to determine how to represent a returned instance?

Given a Ruby class:
class Foo
def initialize(options={})
#sensitive = options.delete :sensitive
end
end
If I create an instance of that class in IRB, I get to see instance vars and memory address.
irb(main):002:0> Foo.new(sensitive: 'foo')
=> #<Foo:0x007fe766134a98 #sensitive="foo">
If I create an instance of AWS::S3, I don't:
irb(main):003:0> require 'aws-sdk'
=> true
irb(main):004:0> AWS::S3.new(access_key_id: 'aki', secret_access_key: 'sak')
=> <AWS::S3>
Note that AWS::S3 is not a singleton (at least not in the sense of explicitly including the Singleton module).
Is there anything I can do to tell IRB not to output instance vars and/or memory address?
(I've already tried .to_s but I get a string containing the memory address for instances of both classes without any instance vars.)
If you start IRB irb --noecho, it will suppress all IRB inspections. But I think this is not your question.
IRB use #inpect method. Read the line from the Documentation :
Returns a string containing a human-readable representation of obj. By default, show the class name and the list of the instance variables and their values (by calling inspect on each of them). User defined classes should override this method to make better representation of obj. When overriding this method, it should return a string whose encoding is compatible with the default external encoding.
Example :
class Foo
def initialize
#x = 10
end
# customized inspection
def inspect
"0x%7x" % self.object_id.to_s
end
end
foo = Foo.new
foo # => 0x118e27c
Note : I used String#% method inside my customized #inspect method.
The standard method being used to render human-readable debugging output (not just in IRb but in general, e.g. on Rails error pages etc.) is #inspect. Depending on which extensions you loaded, your command line options or whether you are using Pry instead of IRb, it may also look for a #pretty_inspect first.

Why do undeclared Ruby local, instance, class, & global variables have different behavior?

Some undeclared variables are nil, some throw an error. How come?
$ irb
1.9.3p0 :001 > asdf # local
NameError: undefined local variable or method `asdf' for main:Object
from (irb):1
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :002 >#asdf # instance
=> nil
1.9.3p0 :003 >##asdf # class
NameError: uninitialized class variable ##asdf in Object
from (irb):3
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :004 > $asdf # global
=> nil
Class variables must always be assigned or else they will return a NameError when you attempt to use them. I do not currently have the details as to why this is.
Instance and Global variables will return nil even if they are not assigned. However, they will raise a warning if you run the script with the -w flag.
I do, however, have the answer in regards to the local variables. The reason local variables act like this comes in the fact that they do not have any punctuation in front of them. This means the variable could be either a variable or a method call (since Ruby does not require () after a method call with no parameters).
something # could be a variable named 'something' or a method called 'something()'
If there is no value assigned to something variable then the Ruby interpreter assumes it is a method invocation. If there is no method by that name then it raises NameError. That is why you will get this message:
NameError: undefined local variable or method 'something' for main:Object
from (irb):1
from path/to/Ruby/bin/irb:12 in '<main>'
So, it is important for the Ruby interpreter to treat local variables in this manner just in case it is actually a method you are referring to.
As an interesting side note:
There is one quirk—a variable comes into existence when the Ruby
interpreter sees an assignment expression for that variable. This is
the case even if that assignment is not actually executed. A variable
that exists but has not been assigned a value is given the default
value nil.
Which means that:
if false
z = "Something"
end
z.nil? #=> true
never_assigned.nil? #=> NameError
The above quote is from The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto section 4.2

Ruby 1.9 const_defined?("Timeout") returns true when Timeout not in the list of constants

I'm trying to upgrade Puppet to use Ruby 1.9 and running into trouble with constants. const_defined?("Timeout") is returning true even though :Timeout isn't in the list of constants. This doesn't happen on Ruby 1.8.7. Any ideas why?
[128, 137] in /Users/matthewrobinson/work/puppet/lib/puppet/util/classgen.rb
128 def handleclassconst(klass, name, options)
129 const = genconst_string(name, options)
130
131 require 'ruby-debug';
132 debugger if const == "Timeout"=>
133 if const_defined?(const)
134 if options[:overwrite]
135 Puppet.info "Redefining #{name} in #{self}"
136 remove_const(const)
137 else
(rdb:1) const
=> "Timeout"
(rdb:1) const_defined?(const)
=> true
(rdb:1) constants.grep /Timeout/
=> []
(rdb:1) constants
=> [:Ensure, :ParameterName, :Auth_type, :Allow_root, :Authenticate_user, :Auth_class, :Comment, :Group, :K_of_n, :Mechanisms, :Rule, :Session_owner, :Shared, :MetaParamNoop, :MetaParamSchedule, :MetaParamAudit, :MetaParamCheck, :MetaParamLoglevel, :MetaParamAlias, :MetaParamTag, :RelationshipMetaparam, :MetaParamRequire, :MetaParamSubscribe, :MetaParamBefore, :MetaParamNotify, :MetaParamStage, :Component, :Macauthorization, :Expirer, :ClassMethods, :InstanceMethods, :ExecutionStub, :POSIX, :Errors, :MethodHelper, :ClassGen, :Docs, :Execution, :Tagging, :Log, :Logging, :Package, :Warnings, :Cacher, :Autoload, :LoadedFile, :Settings, :Feature, :SUIDManager, :RunMode, :CommandLine, :InstanceLoader, :Pson, :Metric, :LogPaths, :ProviderFeatures, :InlineDocs, :FileLocking, :Storage, :Checksums]
(rdb:1) constants.grep /Path/
=> [:LogPaths]
(rdb:1) self
=> Puppet::Type::Macauthorization
The behavior of const_defined? in Ruby 1.9 can be made to be the same as in Ruby 1.8 by setting the new inherit parameter to false.
mod.const_defined?(sym, inherit=true)
Here's a example to illustrate the different behavior.
module Foo
def self.bar
puts "The constant I got was #{const_get("Timeout")}"
if const_defined?("Timeout")
puts "I found #{Timeout}!"
remove_const("Timeout")
puts "Timeout is now #{Timeout}"
end
end
end
class Timeout
end
puts Foo.bar
Under Ruby 1.9.2 the output from running this is:
The constant I got was Timeout
I found Timeout!
19_test.rb:6:in `remove_const': constant Foo::Timeout not defined (NameError)
from 19_test.rb:6:in `bar'
from 19_test.rb:13:in `<main>'
So even though const_defined? recognizes that Timeout is defined at top scope as a class, remove_const is only allowed to remove constants in Foo's scope.
In Ruby 1.8.7 the output is:
The constant I got was Timeout
nil
So const_get looks at ancestor scopes just like in Ruby 1.9.2, but const_defined? does not, which prevents remove_const from getting called.
Ruby 1.9.2 can be made to behave like 1.8.7 like so:
module Foo
def self.bar
puts "The constant I got was #{const_get("Timeout")}"
if const_defined?("Timeout", false)
puts "I found #{Timeout}!"
remove_const("Timeout")
puts "Timeout is now #{Timeout}"
end
end
end
class Timeout
end
puts Foo.bar
However, this is now not backwards compatible with Ruby 1.8 since const_defined? doesn't have a second parameter in 1.8. To get around this I made the following method that can be called instead of const_defined? and used in either version of Ruby.
def is_constant_defined?(const)
if ::RUBY_VERSION =~ /1.9/
const_defined?(const, false)
else
const_defined?(const)
end
end
This solved this particular Ruby 1.9 upgrade issue. It may not be the best long term solution, and the real issue is that there's a class called Timeout at topscope AND sometimes a constant called Timeout in other classes that needs to be checked for, but this change gets the code much closer to running on Ruby 1.9.
I can't say for sure what's going on.
However, the RDoc for const_defined? and constants is different in 1.8.7, whereas it's fairly similar in 1.9.
In 1.8.7, const_defined? says:
Returns true if a constant with the given name is defined by mod.
and constants says
Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section).
However, in 1.9, const_defined? says
Returns true if a constant with the given name is defined by mod, or its ancestors if inherit is not false. [by default, inherit is true]
and constants says
Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section), unless the all parameter is set to false. [by default, all is true]
So it seems like the behaviour of the two methods is consistent in 1.9, but not consistent in 1.8.7. But I could be wrong.
That being said, I'd suggest the following:
Create a toy example of using const_defined? and constants, preferably not involving Timeout, and play around with it until you are confident you understand what the two methods do, under both 1.8 and 1.9.
Work out where the Timeout constant belongs to. Also check whether IRB or the debugger may cause Timeout to become defined when it previously was undefined, and whether it gets loaded by default by one version of Ruby but not the other.
I also came across http://redmine.ruby-lang.org/issues/1915 when googling for const_defined? 1.8 1.9. I'm not sure if it's relevant or not.
I hope this helps - I'm not sure though!

Ruby equivalent operators at "OrElse" and "AndAlso" of Vb.net

There are operators in Ruby similar to "OrElse"and "AndAlso" in VB.NET?
For example in Ruby NoMethodError exception is raised when active_record is nil:
if active_record.nil? || active_record.errors.count == 0
...
end
In VB.net i can do:
If active_record Is Nothing OrElse active_record.errors.count = 0
...
End
That does not generate an exception because it is only checked the first expression
In this case there will be no exception raised (because only the first term in || will be evaluated). However you might be interested in reading about Object#try from ActiveSupport, which can be helpful when dealing with objects that can be nil.
in ruby, there is a big difference between something that is nil and something that is undefined. Considering the following, from IRB:
ruby-1.9.2-p0 :002 > active_record
NameError: undefined local variable or method `active_record' for main:Object
from (irb):2
from /Users/jed/.rvm/rubies/ruby-1.9.2-p0/bin/irb:16:in `<main>'
ruby-1.9.2-p0 :003 > active_record = nil
=> nil
ruby-1.9.2-p0 :004 > active_record.class
=> NilClass
ruby-1.9.2-p0 :006 > active_record.nil?
=> true
So, an object that is nil is an instance of NilClass and therefore responds to the message nil? will return true, but without declaring the variable (as in your code) Ruby doesn't know what you are calling.
A couple of options here:
Ruby's || operator is a strict operator, whereas the or keyword is less strict, so I don't know where the vb operation compares to these two or flow options.
you could use a neat little gem callled 'andand'
require 'andand'
active_record.andand.errors.count == 0
but, generally when you are dealing with this situation in rails, you would use another means to determine the situation above, consider:
#post = Post.new(:my_key => "my value") #=> an ActiveRecord object
if #post.valid?
# do something meaningful
else
puts #post.errors.full_messages.to_sentence
end
and if you mean to assign something based on if it possibly undefined, you would want to use memoization:
#post ||= Post.new
which will declare the object if undefined or use the existing object
Ruby || is short circuit evaluation operator, so it should evaluate only first condition, therefore your if should not raise any exception.
I assume active_record.nil? returns boolean true.

Resources