Static variables in ruby, like in C functions
Scope your variable in a method and return lambda
def counter
count = 0
lambda{count = count+1}
end
test = counter
test[]
#=>1
test[]
#=>2
Are Ruby class variables similar to the Java static variables?
There's a lot of similarity between Ruby and Java by virtue of them being object-oriented, but their family tree is different. Ruby leans very heavily on Smalltalk while Java inherits from the C++ school of thinking.
The difference here is that Ruby's concept of public/private/protected is a lot weaker, they're more suggestions than rules, and things like static methods or constants are more of a pattern than a construct in the language.
Global variables are frowned on quite heavily, they can cause chaos if used liberally. The Ruby way is to namespace things:
$ugly_global = 0 # Not recommended, could conflict with other code
# Ownership of this variable isn't made clear.
$ugly_global += 1 # Works, but again, it's without context.
module UglyCounter # Defines a module/namespace to live in
def self.current # Defines a clear interface to this value
@counter ||= 0 # Initializes a local instance variable
end
def self.current=(v) # Allow modification of this value
@counter = v.to_i # A chance to perform any casting/cleaning
end
end
UglyCounter.current += 1 # Modifies the state of a variable, but
# the context is made clear.
Even a thin layer like this module gives you the ability to intercept read/write operations from this variable and alter the behaviour. Maybe you want to default to a particular value or convert values into a normalized form. With a bare global you have to repeat this code everywhere. Here you can consolidate it.
Class variables are a whole different thing. They're also best avoided because sharing data between the class and instances of this class can be messy. They're two different contexts and that separation should be respected.
class MessyClass
@@shared = 0
def counter
@@shared
end
def counter=(v)
@@shared = v
end
end
This is a pretty rough take on how to use a shared class-level instance variable. The problem here is each instance is directly modifying it, bypassing the class context, which means the class is helpless. This is fundamentally rude, the instance is over-extending its authority. A better approach is this:
class CleanerClass
def self.counter
@counter ||= 0
end
def self.counter=(v)
@counter = v.to_i
end
# These are reduced to simple bridge methods, nothing more. Because
# they simply forward calls there's no breach of authority.
def counter
self.class.counter
end
def counter=(v)
self.class.counter = v
end
end
In many languages a static class method becomes available in the scope of an instance automatically, but this is not the case in Ruby. You must write bridge/proxy/delegate methods, the terminology here varying depending on what you're used to.
ruby access static variable
You can't do what you want to do :)
@harald is right. attr_reader
will define GETTER only for instance variable, for "static" (aka "class variables") you need to define setter and getter by yourself:
class A
@@ololo = 1
# instance level
# getter
def ololo
@@ololo
end
# setter
def ololo=trololo
@@ololo = trololo
end
# and class level
# if you need it
# getter
def self.ololo
@@ololo
end
# setter
def self.ololo=trololo
@@ololo = trololo
end
end
So:
a = A.new
b = A.new
A.ololo
#=> 1
a.ololo
#=> 1
A.ololo = 100
A.ololo
#=> 100
a.ololo
#=> 100
b.ololo
#=> 100
a.ololo = 4
A.ololo
#=> 4
...
Shorter one:
class A
@ololo = 1
class << self
attr_accessor :ololo
end
end
Dynamically create static variables in a class
Constants are defined like this (they have to start with an uppercase letter):
class A
A = 1
B = 2
C = 3
end
A::A #=> 1
A::B #=> 2
A::C #=> 3
To define them dynamically, use const_set
:
class A
%w(A B C).each.with_index(1) { |c, i| const_set(c, i) }
end
In ruby, how do I declare the C++ equivalent of static function variables?
What you're doing is pretty common in Ruby, but also so common you don't need to make a big fuss about it. All @
-type instance variables are local to that instance only. Keep in mind "instance" generally refers to an instance of a class, but it can refer to the instance of the class as well.
You can use @@
to refer to a class instance variable from the context of an instance, but this tends to get messy in practice.
What you want to do is one of the following.
A variable that persists between method calls, but only within the context of a single object instance:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
@hash ||= { }
if @hash[x]
puts 'spaghetti'
else
@hash[x] = true
puts x.to_s
end
end
A variable that persists between method calls, but only within the context of all object instances:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
@@hash ||= { }
if @@hash[x]
puts 'spaghetti'
else
@@hash[x] = true
puts x.to_s
end
end
This is usually made cleaner by wrapping the @@hash
into a class method. This has the secondary effect of making testing easier:
def self.func_hash
@func_hash ||= { }
end
def func(x)
if self.class.func_hash[x]
puts 'spaghetti'
else
self.class.func_hash[x] = true
puts x.to_s
end
end
Accessing a static variable and method in a module
I assume you're trying this out in Rails development mode. In this mode all constants (classes, modules and constants) are loaded by demand.
When ruby meets undefined constant, it throws an error, which autoloader intercepts and tries to load the constant by converting its name to a path.
In your case, autoloader tries to findMyNamespace::MY_SET
in app/presenters/my_namespace.rb
(which fails) and has no idea that you actually defined it in app/presenters/my_namespace/base_presenter.rb
. But after you have loaded your MyNamespace::BasePresenter
(which, btw, lies on the correct path), MyNamespace
, MyNamespace::MY_SET
, and MyNamespace.my_method
got initialized and become available.
What you need to do is to
a) define MyNamespace
correctly and move methods and constants to its definition:
app/presenters/my_namespace.rb
module MyNamespace
MY_SET = Set.new(['a', 'b', 'c'])
def self.my_method
true
end
end
app/presenters/my_namespace/base_presenter.rb
# note that I don't open module here,
# but use a constant to enable autoloading of MyNamespace module
class MyNamespace::BasePresenter
end
or
b) just move all methods/constants to your BasePresenter
class. Since it lies on a correct path, constant MyNamespace::BasePresenter::MY_SET
will just work.
module MyNamespace
class BasePresenter
MY_SET = Set.new(['a', 'b', 'c'])
def self.my_method
true
end
end
end
Bonus part
The difference between
module MyNamespace
class BasePresenter
end
end
and
class MyNamespace::BasePresenter
end
is when MyNamespace
is undefined in first case it will be defined (module MyNamespace
either opens existing module or defines new one), but in second case the mechanism described above will try to load MyNamespace
somewhere, and if it fails - you'll get uninitialized constant error MyNamespace
.
What it also means to you is if you define all your namespaced classes as a first case (inside module MyNamespace
)
app/presenters/my_namespace/base_presenter.rb
module MyNamespace
class BasePresenter
end
end
and also have MyNamespace
in its proper place with some code inside
app/presenters/my_namespace.rb
module MyNamespace
MY_SET = Set.new(['a', 'b', 'c'])
end
And if your MyNamespace::BasePresenter
will gets loaded first, it'll actually define MyNamespace
and your app/presenters/my_namespace.rb
will not be loaded (since autoloading loads only missing constants), and you'll have to require
it yourself.
presenter = MyNamespace::BasePresenter.new
MyNamespace::MY_SET # boom, uninitialized constant error
The solution here is to define modules in 1 proper place (that autoloading knows how to find) and use class MyNamespace::BasePresenter
format for defining namespaced classes in their proper locations.
Static local variables for methods in Ruby?
This answer is a little larger in scope than your question, but I think it gets at the root of what you're trying to do, and will be the easiest and most maintainable.
I think what you're really looking for here is factories. Try using something like factory_girl, which will make a lot of testing much easier.
First, you'd set up a factory to create whatever type of object it is you're testing, and use a sequence for the email attribute:
FactoryGirl.define do
factory :model do
sequence(:email) {|n| "person#{n}@example.com" }
# include whatever else is required to make your model valid
end
end
Then, when you need valid attributes, you can use
Factory.attributes_for(:model)
You can also use Factory.create
and Factory.build
to create saved and unsaved instances of the model.
There's explanation of a lot more of the features in the getting started document, as well as instructions on how to add factories to your project.
Ruby, variables and their C# equivalent
C# does not use sigils for variables.
The "equivalent" C# variable depends entirely on how the variable/member is defined. Note that there are differences even between the "equivalent" forms.
However, there are a number of naming conventions that are encouraged to be followed. The exact conventions used vary by project and may differ from the names I chose below, which reflect my conventions - do not use "class" or "instance" or "local" in real variable names.
Examples:
class MyClass: IMyInterface {
// "const" makes it constant, not the name
public const int CONSTANT = 42;
// static member variable - somewhat like Ruby's @@variable
private static int classVariable;
public static int ExposedClassVariable; // but use properties
// @variable - unlike Ruby, can be accessed outside "self" scope
int instanceVariable;
public int ExposedInstanceVariable; // but use properties
void method (int parameter) {
int localVariable;
}
}
C# does not have "global variables in a shared namespace", but static member variables can be accessed by a stable path which means they can be effectively abused as global variables.
Related Topics
Has_Many and No Method Error Issue
Ruby on Rails - Add Condition on ':Include =>' to Load Limited Number of Objects
Is /Etc/Irbrc Installed by Os X? Does Irb Read It
Ruby Code for Modifying Outer Quotes on Strings
Pg_Dump: [Archiver (Db)] Query Failed: Error: Permission Denied for Relation Abouts
No Such File or Directory @ Rb_Sysopen for External Url/Rails 6.11/Ruby 3
Uri::Invalidurierror: Bad Uri(Is Not Uri) Testing Rails Controllers
How to Stop Bundler from Adding Ruby Version to Gemfile.Lock
There Is a Way to Handle 'After_Save' and 'After_Destroy' "Equally"
Why Do We Need Nginx with Thin on Production Setup
Why Does Single '=' Work in 'If' Statement
Rails 3: Belongs_To, Has_One and Migrations
Initializing a Hash with Empty Array Unexpected Behaviour