i am a newbie about ruby.
should not the singleton class`s initialize function have any arguments?
here is my code:
require 'singleton'
class AAA
attr :string , true
include Singleton
def initialize(stirng)
#string = "aaa";
end
end
a = AAA.instance("simpleton");
puts a.string
i think should be correct.but the ruby also tell me :
/usr/lib/ruby/1.8/singleton.rb:94:in `initialize': wrong number of arguments (0 for 1) (ArgumentError)
from /usr/lib/ruby/1.8/singleton.rb:94:in `new'
from /usr/lib/ruby/1.8/singleton.rb:94:in `instance'
T___T
Since it's a singleton there's no need for passing any parameters to initialize, because it will always return the same object.
If you want to change something on the class everytime you call it you could define a new method.
require 'singleton'
class AAA
attr :string , true
include Singleton
def initialize
#string = "aaa";
end
def self.change(string)
instance.string = string
instance
end
end
a = AAA.change("simpleton");
puts a.string
I'm not sure what you're trying to do but there are plenty of other ways to get a singleton instance without using the Singleton module.
I personally like this method:
class Foo
def self.instance
#__instance__ ||= new
end
end
If you could give a little more information about why you're trying to do what you're doing it might be helpful.
Related
Sorry that I have no clue how to title this, I'm having a hard time looking this up because I don't know how to say this. Anyway...
Let's say I have a class that looks like this for example:
class Run
def self.starting
print "starting..."
end
def self.finished
print "Finished!"
end
end
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting. Now let's say that I wanted to add some instance variables...
class Run
attr_accessor :starting, :finished
def self.starting
print "starting..."
#starting = true
#finished = false
end
def self.finished
print "finished!"
#starting = false
#finished = true
end
end
What if I wanted to access those instance variables from outside the class? I know that something like print "#{Run.finished}" or print "#{Run.starting}" won't do anything. Can I do that without run = Run.new? Or should I just remove self and then use run = Run.new? (Sorry if this question is a mess.)
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting
There's much more to it than this. In your case you're calling class methods. If you did runner = Runner.new - then you'd be calling instance methods (those are defined without self.
In general, if you need "the thing" to hold some kind of state (like #running = true) then you'd rather want to instantiate an object, and call those methods.
Now, #whatever are instance variables, and you don't have the access to them in class methods.
class Run
attr_reader :running
def start
#running = true
end
def stop
#running = false
end
end
runner = Run.new
runner.running # nil
runner.start
runner.running # true
runner.stop
runner.running # false
I'd recommend you doing some tutorial or basic level book on rails programming, find a chapter about objects and classes. Do some exercises.
In Ruby instance variables are just lexical variables scoped to an instance of a class. Since they are scoped to the instance they always act like a private variable.
If you want to provide access to an instance variable from the outside you create setter and getter methods. Thats what attr_accessor does.
class Person
attr_accessor :name
def initialize(name:)
#name = name
end
def hello
"Hello my name is #{#name}"
end
end
john = Person.new(name: 'John')
john.name = "John Smith"
puts john.hello # "Hello my name is John Smith"
puts john.name # "John Smith"
Methods defined with def self.foo are class methods which are also referred to as singleton methods. You can't access variables belonging to an instance from inside a class method since the recipient when calling the method is the class itself and not an instance of the class.
Ruby also has class variables which are shared by a class and its subclasses:
class Person
##count = 0
def initialize
self.class.count += 1
end
def self.count
##count
end
def self.count=(value)
##count = value
end
end
class Student < Person
end
Person.new
Student.new
puts Person.count # 2 - wtf!
And class instance variables that are not shared with subclasses:
class Person
#count = 0 # sets an instance variable in the eigenclass
def initialize
self.class.count += 1
end
def self.count
#count
end
def self.count=(value)
#count = value
end
end
class Student < Person
#count = 0 # sets its own class instance variable
end
Person.new
Student.new
puts Person.count # 1
Class variables are not used as often and usually hold references to things like database connections or configuration which is shared by all instances of a class.
You can't access instance variables from outside the instance. That is the whole point of instance variables.
The only thing you can access from outside the instance are (public) methods.
However, you can create a public method that returns the instance variable. Such a method is called an attribute reader in Ruby, other languages may call it a getter. In Ruby, an attribute reader is typically named the same as the instance variable, but in your case that is not possible since there are already methods with the names starting and finished. Therefore, we have to find some other names for the attribute readers:
class Run
def self.starting?
#starting
end
def self.finished?
#finished
end
end
Since this is a common operation, there are helper methods which generate those methods for you, for example Module#attr_reader. However, they also assume that the name of the attribute reader method is the same as the name of the instance variable, so if you were to use this helper method, it would overwrite the methods you have already written!
class << Run
attr_reader :starting, :finished
end
When you do this, you will get warnings (you always have warning turned on when developing, do you?) telling you that you have overwritten your existing methods:
run.rb:19: warning: method redefined; discarding old starting
run.rb:2: warning: previous definition of starting was here
run.rb:19: warning: method redefined; discarding old finished
run.rb:5: warning: previous definition of finished was here
I have searched around for the answer to this and I can see a lot of similar problems but I still do not understand what I am doing wrong here. I have declared a Ruby class and attempted to new it and then call some instance methods on the instance, so why do I get the NoMethodError on my start method?
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def self.start(port)
...
end
end
test = MyClass.new
test.start '8082' <- here <- undefined method `start' for #<MyClass:0x2f494b0> (NoMethodError)
As you can see I am a Ruby noob. Any help would be appreciated. I can change my class structure but I would really like to understand what I am doing wrong here.
here start is a class method.
By your current approach, you can use it in the following way
MyClass.start '8080'
But if you want to use it on instance of class then use the following code
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def start(port)
...
end
end
test = MyClass.new
test.start '8080'
You are using start as a Class variable, the method names preceded with self-keyword make those methods as Class methods. So if you really want to not change your class then you should call it like this:
MyClass.start '8080'
Else you can remove the self from your reset and start methods and make them as Instance methods and use them as:
test = MyClass.new
test.start '8082'
I want to create an empty array as a class instance variable in Ruby. However, my current method does not seem to work.
Here is my code:
class Something
#something = []
def dosomething
s = 5
#something << s
end
end
When I call the function, it gives me an undefined method traceback.
However, if I do something similar with class variables, i.e.:
class Something
##something = []
def dosomething
s = 5
##something << s
end
end
This works perfectly.
I know I can use the initialize method to actually create an empty list for #something, but is there another way of doing this without using the initialize method? And why does this work for class variables?
EDIT: Fixed typo
You need to use initialize as a constructor as below code and is there any reason why not to use initialize/constructor. And please fix a typo error in class definition Class Something to class Something no camel case or first letter capitalize while in class
class Something
def initialize
#something = Array.new
end
def dosomething
s = 5
#something << s
end
end
class variable ## are available to the whole class scope. so they are working in the code and if you want to use instance variable # you need to initialize it as above. The instance variable is share with instance/objects of a class
for more details visit the link Ruby initialize method
At first you have a typo. Change Classto class. Next I suggest to use the initialize method. While creating a new object this is the perfect place to initialize instance variables.
class Something
##my_class_variable = [1]
def initialize
#something = []
end
def dosomething
s = 5
#something << s
end
def self.get_my_class_variable
##my_class_variable
end
end
Your script will be read and executed from top to bottom and after this,
you can access the class Something. While the parser reads your script/class/module you can define class variables (##), execute mixins and extend the class with other modules. This is why you can define a class variable, but you can not define an instance variable. Because actually you have no instance object from your class. You only have a class object. In ruby everything is an object. And your class object has a defined class variable now:
Something.get_my_class_variable
# => [1]
Now you can create an instance from your class. With Something.new the initialize method will be invoked and your instance variable will be defined.
something = Something.new
something.dosomething
# => [5]
Later, if you are familar with this you can define getter and setter methods with attr_reader, attr_writer and attr_accessor for instance objects or cattr_reader, cattr_writer and cattr_accessor for class objects. For example:
class Something
attr_reader :my_something
def initialize
#my_something = []
end
def dosomething
s = 5
#my_something << s
end
end
something = Something.new
something.my_something
# => []
something.dosomething
# => [5]
something.my_something
# => [5]
Your problem in trying to access #something in your instance method is that, in the scope of instance methods, # variables refer to instance variables, and your #something is a class instance variable.
# variables are instance variables of the instance that is self when they are created. When #something was created, self was the class Something, not an instance of Something, which would be the case inside an instance method.
How then to access a class instance variable in an instance method? Like regular instance variables, this must be done via a method, as in attr_accessor. One way to do this is to use class << self to tell the Ruby interpreter that the enclosed code should be evaluated with the class (and not the instance) as self:
class C
#foo = 'hello'
class << self
attr_accessor :foo # this will be a class method
end
def test_foo # this is, of course, an instance method
puts self.class.foo # or puts C.foo
end
end
We can show that this works in irb:
2.3.0 :005 > C.foo
=> "hello"
2.3.0 :006 > C.new.test_foo
hello
You have correctly created a class instance variable, #something, and initialized it to an empty array. There are two ways for instances to obtain or change the value of that variable. One is to use the methods Object#instance_variable_get and Object#instance_variable_set (invoked on the class):
class Something
#something = []
def dosomething
s = 5
self.class.instance_variable_get(:#something) << s
end
end
sthg = Something.new
sthg.dosomething
Something.instance_variable_get(:#something)
#=> 5
The other way is to create an accessor for the variable. There are several ways to do that. My preference is the following:
Something.singleton_class.send(:attr_accessor, :something)
Something.something #=> [5]
In your dosomething method you would write:
self.class.something << s
Here is my code:
class Klass
["thing", nil].each do |i|
instance_variable_set("##{i}reqs", {})
end
def initialize(var)
#reqs[var] = self
end
end
Klass.new("hello")
Which gives me the error:
in initialize': undefined method[]=' for nil:NilClass (NoMethodError)
I shouldn't be getting this error because the loop at the top should have initialized #reqs in it's second iteration. What is going on?
Instance variables belong to particular instances. That's why they are called instance variables.
In line 3, you set the instance variable called #reqs of the object Klass. In line 6, you access the instance variable called #reqs of an instance of the class Klass. Those are two completely different, distinct objects each with its own set of instance variables. Heck, those two objects don't even have the same class! (Klass's class is Class, whereas Klass.new's class is Klass.)
In line 6, #reqs is uninitialized, and uninitialized instance variables evaluate to nil.
There are many different ways to fix this, depending on your exact circumstances and requirements, the easiest way would be to initialize the instance variables in the initialize method, after all, that's what that method is there for:
class Klass
def initialize(var)
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
#reqs[var] = self
end
end
Klass.new('hello')
Remember, the problem was that the instance variables were initialized in one object, and accessed in another. This solution moves the initialization to the same object that was doing the reading.
However, the dual is also possible: move the reading to where the initialized variables are:
class Klass
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
def initialize(var)
self.class.instance_variable_get(:#reqs)[var] = self
end
end
Klass.new('hello')
This is kind of ugly, so let's add an attr_reader:
class Klass
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
class << self; attr_reader :reqs end
def initialize(var)
self.class.reqs[var] = self
end
end
Klass.new('hello')
Obviously, these two do very different things. It is unclear from your question which of the two you actually want.
A third possibility would be using class variables:
class Klass
['thing', nil].each do |i|
class_variable_set(:"###{i}reqs", {})
end
def initialize(var)
##reqs[var] = self
end
end
Klass.new('hello')
Note that this does yet another different thing. Again, whether you want that or not is not clear from your question.
The loop at the top is defining an instance variable for the Class, not for any object of the class.
So for the object, it doesn't exist.
From the looks of it, you want a hash common to the whole class where you store each created object in the hash. Assuming you don't have issues with class inheritance, you'd be better of with a class variable.
so...
class Klass
["thing",nil].each do |i|
class_variable_set("###{i}reqs", {})
end
def initialize(var)
##reqs[var] = self
end
end
Klass.new("hello")
If you define class like this:
class Klass
instance_variable_set(:#name, 'dog')
def self.name
#name
end
def name
#name
end
end
then
Klass.name # => 'dog'
instance = Klass.new
instance.name # => nil
Now you can see the difference. Your variable is defined on class level, not instance level.
I'm using:
Ruby 1.9.2
Rails 3.1.10
This is my code:
class Report::ExpectedHour
def initialize(user, options = {})
#user = user
#date_start = options[:start]
#date_end = options[:end]
end
def expected_hours_range
previous = ExpectedHour.previous_dates(#user, #date_start).first
hours_range = ExpectedHour.between_dates(#user, #date_start, #date_end)
unless hours_range.include?(previous)
hours_range << previous
end
hours_range
end
end
Every time I call expected_hours_range from my instance I get this error:
NameError: uninitialized constant Report::ExpectedHour::ExpectedHour
from /home/edelpero/.rvm/gems/ruby-1.9.2-p180#titi/gems/aws-s3-0.6.2/lib/aws/s3/extensions.rb:206:in `const_missing_from_s3_library'
from /opt/lampp/htdocs/titi/app/models/report/expected_hour.rb:10:in `expected_hours_range'
I'm not sure why Report::ExpectedHour::ExpectedHour is called because I'm calling ExpectedHour which is an actual existing ActiveRecord class. Also Report::ExpectedHour::ExpectedHour doesn't exist.
When calling classes inside your class methods, ruby expects it to be either a class nested inside you class itself or a constant. Try this:
class MyClass
def some_method
use_external_class = ::ExternalClass::CONSTANTB.bla
# Use the '::'
end
end