1

I'm pretty new to Ruby and the Rails framework. My background is primarily Java. Anyhow, I'm facing a weird situation. I have a method in one of my models that returns associated models. The association is as follows. A has_many Bs, and B belongs to A (i.e. one-to-many)

class ModelA < ActiveRecord::Base
  has_many :model_bs

  def get_bs
    ModelB.where(:a_id => id)
  end
end 

class ModelB < ActiveRecord::Base
  belongs_to :model_a
end

In my view, if I try to access the records (models) in the result set, I'm able to call its properties without any issue (Figure A). Life is good.

Figure A:

<% bs = a.get_bs %>
<% bs.each do |b| %> 
  <%= b.some_prop %> 
<% end %>

But if I try to access the models by index, I get an error saying that I can't call a method on a nil object (Figure B & C).

Figure B:

<% bs = a.get_bs %>
<%= bs[0].some_prop) %>

Or even..

Figure C:

<% bs = a.get_bs %>
<%= bs[0].first %>

Does not work. I know it's user error (me). I've looked at the documentation for accessing objects from a collection (in this case, I believe it's a Ruby array). I've also searched here on StackOverflow. I'm still left scratching my head. I haven't quite found a similar thread.

2
  • What about bs.first.some_prop or (not nice) bs.to_a[0].some_prop. Check what you get from outs a.get_bs.class.name - it is likely not Array. Commented Jul 7, 2012 at 18:36
  • Hey, thanks for the ".class.name" suggestion. I am now getting somewhere. bs.class.name returns ActiveRecord::Relation. bs.to_a.class.name returns Array. bs.to_a[0].class.name returns B (my model). But, when I call a method from model B, I still get a null pointer. Hmmm....getting closer. Thanks guys, for your help so far. Commented Jul 7, 2012 at 23:33

3 Answers 3

1

You are wrong, it is not Array, it is an ActiveRecord::Relation class. You can transform it to an array with .to_a, if you really need it. I've checked, you can use [] operator to access item by index: ModelA.where("created_at = created_at")[0].name, so I think the problem is somewhere else, maybe in your condition.

Check the documentation. But anyway, you shouldn't use the relationship like this. Use has_many and belongs_to to indicate relationship between models. Like this:

class ModelA < ActiveRecord::Base
  has_many :ModelB
end 

class ModelB < ActiveRecord::Base
  belongs_to :ModelA
end 
Sign up to request clarification or add additional context in comments.

7 Comments

Yes, my model A and B are defined like said. So what you're saying is, when you call the 'where' method of an ActiveRecord, it returns an ActiveRecord::Relation, not an array?
Yes, because the Relation class can be used to further stuff before actually bring data from the database (like: .joins().where().order() etc..). The each operator executes the SQL query, thus it can provide you data.
Oh ok. So how would I retrieve the records/models by index, rather than iterating? I tried transforming it to an array (i.e .to_a) as you suggested but still didn't work. I'm still getting, "undefined method `some_prop' for nil:NilClass"
This means something in your expression is evaluated to nil. Try this: a.get_bs.to_a[0].some_prop
Are you sure that your condition is good? Your first working example does really output anything?
|
1

I found a solution. It's not pretty but it'll due for now. With a little help from someone on LinkedIn, I discovered that using the .try method on my model while attempting to access an attribute, I'm able to retrieve the value without the null pointer exception.

Example:

<% bs = a.get_bs.to_a%>
<%= bs[0].try(:some_attr) %>

It's not clear to me as why I need to use the .try method. I mean, I know what the method is for. It's a convenience method for checking nil values and allowing the page to render without throwing an exception. But it's obvious that my model is not null and it has data. So why am I only able to access its attributes with the .try method? Honestly, I think this could be a bug in Rails.

I think what I'll end up doing is create a helper method that utilizes the .try method. That way, I'm not calling .try all over my views.

1 Comment

Well, if it says 'No method ... for nil object', then it is definitely nil :) I bet the value of your attribute is nil too. Nevertheless it is easy to debug and see what is the actual value of your array, or and its first value.
0

Why not using this?

def get_bs
  ModelB.find_all_by_a_id(id)
end

Comments

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.