botbot
botbot

Reputation: 7359

Rails Ruby Array Nil && []

Why do certain queries return records with [] and some Nil?

I am trying to figure out why if I run a statement like:

user.articles # yields '[]' if the user has no articles

but if I write a statement like this:

user.likes # yields Nil if empty

likes would be a method in the User model and would look something like this:

def likes
    Likes.find_by_user_id(id)
end

Likes would be a join table containing a user_id and article_id.

What I'd like to do in my view is make these sort of statements:

user.likes.count

Actually now that I think of it, it would be great to do this:

@user.articles.likes 

and get a array of the articles that a user likes.

Make sense?

I'm getting the feeling that this calls for a :through parameter in my models, but I haven't gotten the hang of that yet, and don't know if it's appropriate.

Upvotes: 0

Views: 700

Answers (3)

Virtual
Virtual

Reputation: 2191

Account.find_by_user_name('ajik') => nil

Account.find_all_by_user_name('ajik') => []

The methods that are meant to return one record return nil when not matching. The ones that are meant to return many records return an empty collection, instead.

Upvotes: 6

thorsten müller
thorsten müller

Reputation: 5651

While Virtuals answer would work, you should read about ActiveRecord relationships. Your scenario could be setup like this: (just a rough sketch, leaving out details)

class User
  has_many :likes
  has_many :articles, :through => :likes
end

class Article
  has_many :likes
  has_many :users, :through => :likes
end

class Likes
  belongs_to :user
  belongs_to :article
end

This would give you most of the functionality you want (and much more)

Update

I added the has_many through, which makes more sense and should work better for you. I don't exactly like the naming here, since you now can access something like @user.articles, which would use the defined associations. Normally @user.articles would mean, that the articles are directly related to the user (have a user_id field of their own). You could use something like

has_many :liked_articles, :through => :likes

and then use

@user.liked_articles

But this would require even more information, since you would have to tell Rails the names of the id fields explicitly (so this won't work exactly like I wrote the code, just a hint that there are more options)

There are even more options by using scopes or the new Rails 3.x query syntax like @user.where(...), which works easier if you need to chain several conditions.

In general I would recommend to set up as many information as possible with associations like has_many and belongs_to, since this way Rails gives you a lot of functionality for free, that you won't get otherwise. But there may be many reasons to do it this or that way, so as often in programming there is no 100% answer that fits every situation.

Upvotes: 3

Joshua Cheek
Joshua Cheek

Reputation: 31726

It's because an empty collection is the null object when there are no results. In other words, if I asked you for an array with all the states in them that begin with the letter "x", you would give me an empty array. This corresponds to the first example of articles, a method probably written by declaring a has_many(:articles) in your model. This is a collection of records, just as the states beginning with "x" is a collection of states. So it returns an empty array.

But if I asked for the one state that begins with the letter x, what would you give me? You can't give me an empty array, because that represents a collection. You need an object that represents "nothing". Such an object exists, it's called nil.

BTW, usually your models are singular (Like rather than Likes), and the find method should return all the likes for that id, not just the first one.

Upvotes: 3

Related Questions