Lucas Barreto
Lucas Barreto

Reputation: 150

Ruby - Passing instance variables to a class from another

I have 3 classes: Invoice, Address and Customer (but for this problem, only the Invoice and Address class are relevant)

This is my Invoice class:

class Invoice
  attr_reader :billing_address, :shipping_address, :order

  def initialize(attributes = {})
    @billing_address = attributes.values_at(:billing_address)
    @shipping_address = attributes.values_at(:shipping_address)
    @order = attributes.values_at(:order)
  end
end

and this is my Address class:

class Address
  attr_reader :zipcode, :full_address

  def initialize(zipcode:)
    @zipcode = zipcode  

    url = 'https://viacep.com.br/ws/' + zipcode.to_s + '/json/'
    uri = URI(url)
    status = Net::HTTP.get_response(uri)
    if (status.code == "200")
      response = Net::HTTP.get(uri)
      full_address = JSON.parse(response)
      @full_address = full_address
    else
      p "Houve um erro. API indisponível. Favor tentar novamente mais tarde."
      @full_adress = nil
    end  
  end
end

And this is my Customer class (not much relevant, but i'm showing for better explanation of the problem)

class Customer
  attr_reader :name, :age, :email, :gender

  def initialize(attributes = {})
    @name = attributes.values_at(:name)
    @age = attributes.values_at(:age)
    @email = attributes.values_at(:email)
    @gender = attributes.values_at(:gender)
  end

end

As you can see, my Invoice class has 3 instance variables and my Address class has 2 instance variables.

So, if i test something like that:

cliente = Customer.new(name: "Lucas", age: 28, email: "[email protected]", gender: "masculino")
endereco = Address.new(zipcode: 41701035)
entrega = Invoice.new(billing_address: endereco, shipping_address: endereco)

p endereco.instance_variables

[:@zipcode, :@full_address]

p entrega.shipping_address.instance_variables

[]

My instance variables can be acessed through the variable "endereco", that is an Address object, but can't be acessed through entrega.shipping_address that is also an Address object.

To be more precise, if a try this:

p entrega.shipping_address 

I get this return:

[#<Address:0x00000001323d58 @zipcode=41701035, @full_address={"cep"=>"41701-035", "logradouro"=>"Rua Parati", "complemento"=>"", "bairro"=>"Alphaville I", "localidade"=>"Salvador", "uf"=>"BA", "unidade"=>"", "ibge"=>"2927408", "gia"=>""}>]

My full object are being returned, but i can't access the content of my @full_address instance variable.

If a do this:

p entrega.shipping_address.full_address

I get a NoMethodError:

solucao.rb:8:in `<main>': undefined method `full_address' for #<Array:0x000000012d25e8> (NoMethodError)

I'm trying to understand why i can't access the content inside my object if i have the full object. Maybe i'm trying to access in the wrong way, i don't know.

Can someone help ?

Upvotes: 0

Views: 1236

Answers (2)

SteveTurczyn
SteveTurczyn

Reputation: 36860

values_at returns an array of values (see https://apidock.com/ruby/Hash/values_at for explanation)

Change

@shipping_address = attributes.values_at(:shipping_address)

into

@shipping_address = attributes[:shipping_address]

And that way @shipping_address will contain an Address object, not an array that contains an Address object

Upvotes: 3

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230286

If you take a look at the error, it says

undefined method `full_address' for #<Array:0x000000012d25e8>

You're trying to call full_address on an array. So this means that entrega.shipping_address returns you an array (which it does, of course, take a closer look at the output).

If I were you, I'd look into how shipping_address is implemented. It's a simple attr_reader, so it's backed by an instance variable. Must be you initialize that instance variable to a wrong value (it gets an array instead of an address). Look closely at that initialization code and try to run it in IRB session. You should see the problem.

Upvotes: 3

Related Questions