My class is calling a non-existing class? - ruby

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

Related

Ruby inheritance, method not being passed to a child class

I've got a ruby exercise, and cannot quite get past one point.
When I run the tests, it throws undefined method "attribute" for CommentSerializer:Class.
Though, there is such a method defined in serializer.rb, from which it's being inherited.
Am I missing something about inheritance in ruby here?
Note: I am neither allowed to add any gems other than the two listed below, nor to modify any file other than serializer.rb.
Here are the files:
Gemfile:
gem 'rspec'
gem 'pry'
app/comment.rb:
Comment = Struct.new(:id, :body)
app/comment_serializer.rb:
require_relative "serializer"
class CommentSerializer < Serializer
attribute :id
attribute :body
end
app/serializer.rb:
class Serializer
def initialize(object)
#obj = object
end
def serialize
obj.members.inject({}) do |hash, member|
hash[member] = obj[member]
hash
end
end
def attribute(key)
end
private
def obj
#obj
end
end
spec/comment_serializer_spec.rb:
require "date"
require_relative "spec_helper"
require_relative "../app/comment"
require_relative "../app/comment_serializer"
RSpec.describe CommentSerializer do
subject { described_class.new(comment) }
let(:comment) do
Comment.new(1, "Foo bar")
end
it "serializes object" do
expect(subject.serialize).to eq({
id: 1,
body: "Foo bar",
})
end
end
If you call something like attribute in the body of the class definition then it happens in the class context at that exact moment, as in:
class Example < Serializer
# This is evaluated immediately, as in self.attribute(:a) or Example.attribute(:a)
attribute :a
end
There must be a corresponding class method to receive that call, as in:
class Serializer
def self.attribute(name)
# ...
end
end
Since you're inheriting that method it will be defined prior to calling it, but that's not the case if you have something like:
class Example
attribute :a # undefined method `attribute' for Example:Class (NoMethodError)
def self.attribute(name)
end
end
The method is defined after it's called, so you get this error. You must either reverse the order, define first, call second, or put it into a parent class.

How to Initialize Class Arrays in Ruby

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

Ruby class instance variables in subclasses

Relying on this answer, I wrote the following class. When using it, I get an error:
in 'serialize': undefined method '[]=' for nil:NilClass (NoMethodError).
How can I access the variable #serializable_attrs in a base class?
Base class:
# Provides an attribute serialization interface to subclasses.
class Serializable
#serializable_attrs = {}
def self.serialize(name, target=nil)
attr_accessor(name)
#serializable_attrs[name] = target
end
def initialize(opts)
opts.each do |attr, val|
instance_variable_set("##{attr}", val)
end
end
def to_hash
result = {}
self.class.serializable_attrs.each do |attr, target|
if target != nil then
result[target] = instance_variable_get("##{attr}")
end
end
return result
end
end
Usage example:
class AuthRequest < Serializable
serialize :company_id, 'companyId'
serialize :private_key, 'privateKey'
end
Class instance variables are not inherited, so the line
#serializable_attrs = {}
Only sets this in Serializable not its subclasses. While you could use the inherited hook to set this on subclassing or change the serialize method to initialize #serializable_attrs I would probably add
def self.serializable_attrs
#serializable_attrs ||= {}
end
And then use that rather than referring directly to the instance variable.

Unable to Create a Ruby Hash inside a Class

I am trying to create a simple Ruby class but I am getting stuck. This is my code:
#!/usr/bin/ruby
class Dock
#ships = Hash.new(false)
def initialize()
end
def store(ship, pier)
#ships[pier] = ship
end
end
yathi = Dock.new
yathi.store("test", 12)
But when I try to run this by running this in Terminal:
ruby test.rb
This is the error message I am getting:
test.rb:8:in `'store': undefined method `'[]=' for nil:NilClass (NoMethodError)
from test.rb:13
It does work when if I rewrite it like this:
#ships = {pier => ship}
But this creates a new hash everytime with just one value which I don't want. Can someone please tell me what I am doing wrong?
Put the #ships = Hash.new(false) inside the initialize method. When you put it outside the initialize method you are defining a class level instance variable instead. Check out this writeup if you want to know more.
Try initializing the hash inside the "initialize" method so that it is a member of instances of the Dock class instead of a member of the Dock class itself:
class Dock
def initialize()
#ships = Hash.new(false) # <-- Define #ships as an instance variable.
end
def store(ship, pier)
#ships[pier] = ship
end
end

a problem about singleton in ruby

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.

Resources