0

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 ?

2 Answers 2

3

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

Sign up to request clarification or add additional context in comments.

3 Comments

This worked too !! thank you very much for your help
Another thing that worked was that: p entrega.shipping_address[0].full_address["logradouro"] If I use an indexer, i can acess the object Address inside the array. I didn't noticed that i had an structure of an object inside an array.
Yes you could access the first element of the array with [0] or even first but unless your design calls for multiple shipping addresses in the entrega you shouldn't make it an array.
3

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.

2 Comments

oh man... thanks for the advice... i was blind. I just changed my implementation of the Invoice Class to this: class Invoice attr_reader :billing_address, :shipping_address def initialize(billing_address:, shipping_address:) @billing_address = billing_address @shipping_address = shipping_address end end And it worked. But now i have another problem... i had to remove the "order" parameter from the constructor, because i was getting an error of missing argument. Is there someway to make the constructor accept this argument if it's null ?
@LucasBarreto: not sure what you mean, but you can set default values to parameters: def initialize(billing_address:, order: nil) Here billing_address is a required parameter and order is optional

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.