Ensuring at least one item in an array exists in another - ruby-on-rails

I have the following models:
class Post < ActiveRecord::Base
has_and_belongs_to_many :countries
end
class User < ActiveRecord::Base
has_many :entitlements
has_many :countries, :through => :entitlements
end
Posts on the Post index page must have at least one country that is the same as one of the Users' countries.
I have tried various scopes in my models and lengthy controller code but I can't figure out how to check what should be a simple relationship: whether at least one item in Post.countries exists in User.countries.
Any help greatly received.
UPDATED:
Ok, so I've got the following in my controller:
def index
#user = current_user
#user.countries.each do |user_country|
#user_country_posts += Country.find(user_country.id).posts
end
#posts = #user_country_posts
end
Which is iterating through the user.countries and finding each post for those countries. But when I run it I get:
NoMethodError: undefined method `+' for nil:NilClass
Any ideas what I'm doing wrong?

The problem is that you're trying to use the #user_country_posts instance variable which was not defined before, so its value is nil.
At the line:
#user_country_posts += Country.find(user_country.id).posts
You're actually calling the + method on the #user_country_posts variable, which is equivalent therefore with calling + on a nil.
Try to initialize the variable in the beginning of the method, like:
#user_country_posts = []

I would also consider using ruby's union approach:
ie:
[1,2,4] & [1,4,5]
=> [1,4]
So if you have list of user countries and a list of post countries then maybe the below would work:
ie:
#shared_country_ids = #user.countries.map(&:id) & #post.countries(&:id)
From your update above what it seems like you want to do is show all posts that have one of the user's country codes. If that's the case I would do the below:
ie:
#posts = Post.where(:countries => #user.countries)
The above should work assuming you configured the relationships correctly.

Related

Calling Child's scope through Parent

I have a scope method defined in my Child's model profile.rb as follows
scope :fees_to, -> (fees_to) { where("fees_to <= ?", "#{fees_to}") }
And in the parent which is tutor.rb there is
has_one :profile, dependent: :destroy
Now in rails console i can do Profile.all.fees_to(10) for example and its valid. But how can i do call the fees_to method through the parent Tutor?
Basically now i am able to filter through my profiles in the index view of it. But what i would like to do is to filter through the tutor index view based on values from the child.
All help and advice would be greatly appreciated! Thank you
This makes no sense with a has_one. You cannot "have one" and still apply a scope to it. Scopes work on many records, to apply additional conditions and narrow down the result set, not specific records.
You're effectively trying to add a where(fees_to < ?) condition to a specific record, which obviously makes no sense.
If you want to use a scope via an association, it needs to be a has_many.
In profile.rb:
You can create a function like:
def fees_to n
Profile.fees_to(n) # your scope is called here
end
Then in rails console :
tutor = Tutor.first
tutor.profile.fees_to(10)
it works like a filter. If profile's fees_to is > n, you will get [] (array empty)
UPDATED
bad => #tutor = Tutor.all
good => #tutor = Tutor.includes(:profile).all # with eager loading, avoid n+1 query.
may be you want make loop like that:
#tutor.each do |tutor|
tutor.profile.fees_to(10)
end
I defined the following in my tutor.rb
def self.fees_search(n)
#profile = Profile.fees_to(n)
if #profile.empty?
return Tutor.none
else
#profile.each do |y|
y.tutor
end
end
end
Not sure if its clunky or if there are redundancies, but it works perfectly fine in rails console. I can do Tutor.fees_search(10) and it renders all the respective Tutors accordingly.
Cheers

rails view has_many relation

I have such two models:
class Article < ActiveRecord::Base
belongs_to :articles_type
end
class ArticlesType < ActiveRecord::Base
has_many :articles
end
and in controller i write:
#articles = Article.where(article_type_id: params[:id])
and in view(haml) i try:
= #articles.articles_type.id
= #articles.articles_types.id
= #articles.first.articles_type.id
= #articles.first.articles_types.id
how could i display this articles_type.id but for only first row?
now i get
undefined method `articles_type'
but why? what i do wrong? how to display nested model id?
#articles will be a collection of items, not just a single one (because you used the where method). You will have to do:
#articles.first.articles_type_id
(Also note that you don't have to do .articles_type.id, because the #articles.first already has the ID of the type)
The undefined method message is because #articles does not have an articles_type method. You have to get to a single instance of an Article in order to use that method. You could do that with a call to #articles.first or by iterating on the collection.
= #articles.first.articles_type.id
is the line you want to use.
Looks like you've got your logic backwards.
Based on your models, an article belongs to an article_type.
#articles.first.article_type.id
# OR #articles.first.article_type_id
Just looks like you're incorrectly pluralizing .article_types when it should be .article_type.

Scoping a class method to current_user

I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations

Getting undefined method for ActiveRecord::Relation

I have the following models
class Book < ActiveRecord::Base
has_many :chapters
end
and
class Chapter < ActiveRecord::Base
belongs_to :book
end
in /chapters/edit/id I get
undefined method `book' for #<ActiveRecord::Relation:0x0000010378d5d0>
when i try to access book like this
#chapter.book
Looks like #chapter is not a single Chapter object. If #chapter is initialized something like this:
#chapter = Chapter.where(:id => params[:id])
then you get a Relation object (that can be treated as a collection, but not a single object). So to fix this you need to retrieve a record using find_by_id, or take a first one from the collection
#chapter = Chapter.where(:id => params[:id]).first
or
#chapter = Chapter.find_by_id(params[:id])
As the others have said - adding the .first method will resolve this. I have experienced this issue when calling a #chapter by it's unique ID. Adding .first (or .take in Rails 4) will ensure only one object is returned.
Try: Chapter.find(params[:id]).first
I got a similar error
Books.chapters
NoMethodError: undefined method `chapters' for #<Book::ActiveRecord_Relation:0x00007f8f9ce94610>
what I needed was:
Books.includes(:chapters)

Active Record Association with Memcached

Hi for rails model association, i know i can do this:
For example a model class Page.
class Page < ActiveRecord::Base
has_many :parts
end
I can do this:
Page.first.parts.find_by_name('body')
Page.first.parts.class actually returns Array. How can it activate methods for Part model? I found the similar post on How do rails association methods work?
My question is that when i try to use memcache to cache the response for parts methods. Then when i call Page.first.parts.find_by_name('body'), it tells me that the Array doesn't have method find_by_name. How do i solve this problem? I need to have the cache as this is one heavily used methods.
class Page
def parts_with_cache
Rails.cache.fetch("parts_for_page_#{id}", {:expires_in => 1.minutes}) do
parts_without_cache
end
end
alias_method_chain :parts, :cache
end
Since you are getting back an array of Parts objects associated to the Page object unfiltered by part name, just do an Array select method on the result set.
body_parts = Page.first.parts.select{ |part| part.name == 'body' }

Resources