I tried to google and read about it, but I could not find an easy solution or a very good answer in regard to this topic. There are many similar questions, solving people's problems. I could not figure out how to solve mine and on top of that I still wonder why nobody did not solve that yet. It is such a common problem I think.
I am using Rails 4 and Mongoid 4 with Mongoid Paranoia.
I want to implement a soft deletion for User. That works.
However I have an Event model which has_and_belongs_to_many :users, inverse_of: nil.
When I soft delete a user, who is a user of the event, then I seem to fail to find a way to have this user being fetched by default. Soft deleted users are not fetched, because of the default scope on User with deleted_at: nil.
Anyone enlightening out there ?
How to get started to implement something similar to what ActiveRecord offers:
has_and_belongs_to_many :users, -> { *a defined scope* }
I'm not sure if I am understanding you completely, but shouldn't calling another scope that utilizes unscoped do what you need. Docs here.
default_scope { where(deleted_at: nil) }
scope :some_other_scope, -> { unscoped.where(something_else: true) }
Edit
Ah, I see. Not the most elegant way but if you're set on not changing the default_scope of the User model; perhaps:
# Event model
def all_users
User.unscoped.where(event: self)
end
Related
Sorry for the title I have some difficulties to explain my issue, and even more in english.
My use case is that:
I have one user who has_many teams, each team own one game.
I would like add a method (a scope I believe) in my model user who gets me all the games who are related with the user. I thought something like that:
scope :games, includes({:teams => [:game]})
This line don't work for some reason i don't know yet.
But if I succeed to do that I will get all the teams and all the games.
Whereas I would like only an array with the games.
I search the more elegant way to do that.
Thanks for reading :)
If you use Rails 4, then all scopes must be wrapped in lambdas.
scope :games, -> { includes({:teams => [:game]}) }
If model User has_one Profile, is there a simple way to find all users who have no profile without a custom query (eg NOT User.where('profile_id IS NULL')) or processing it in the app?
Since the association between User and Profile is already known to Rails I don't want to restate it. I want to keep this DRY. The actual connection between the models is more complex than this simple example (uses keys and class name) and may change in the future.
As #tadman & #antonk already said, you probably want to use scope:
scope :without_profile, where(profile_id: nil)
EDIT:
To answer #David Mauricio's question: you could use it by calling User.without_profile, to return the AR association of all users with a nil :profile_id.
#DriverDan : then I'm really unsure what you're asking for. Ask another question with more details, and we can try to answer it!
As I learned update_only doesn't work for has_many association.
I am updating nested attributes and it creates new children each time and don't delete old children. What would be the good way to delete them automatically on each update (to mimic :update_only behavior which works for has_one)?
I know about :allow_destroy. However, I don't have children id's to let rails know what should be destroyed.
P.S. I asked this question originally back in 2012 and back then I found a hacky solution to override assign_nested_attributes_for_collection_association. I wonder whether anything was introduced in Rails to solve this problem. It looks like very common straightforward case. I wonder why there is a built-in solution for it.
The best approach which I found is to override method assign_nested_attributes_for_collection_association on the object, check association type in there. if it's has_many then do destroy_all on this association.
Such code can be generalized and moved to a module for further reuse.
It looks like there is a better solution in town mentioned in these two questions:
Rails 4 has_many nested_attributes to replace all
Rails replace collection intead of adding to it from a has_many nested attributes form
The solution is
def foo_attributes=(*attrs)
self.foo.clear
super(*attrs)
end
I've recently started an internship. My employer uses ruby on rails, and I frequently encounter new syntax that I need to look up to understand. I've googled around for a good explanation of named_scope, but what I've found so far is mostly blog posts giving high praise for it, rather a straight definition or introduction.
What exactly is named_scope (now simply called scope) in ruby on rails?
A scope is a subset of a collection. Sounds complicated? It isn't. Imagine this:
You have Users. Now, some of those Users are subscribed to your newsletter. You marked those who receive a newsletter by adding a field to the Users Database (user.subscribed_to_newsletter = true). Naturally, you sometimes want to get those Users who are subscribed to your newsletter.
You could, of course, always do this:
User.where(subscribed_to_newsletter: true).each do #something
Instead of always writing this you could, however, do something like this.
#File: users.rb
class User < ActiveRecord::Base
scope :newsletter, where(subscribed_to_newsletter: true)
#yada yada
end
If you're using Rails 4 or newer, do this instead:
#File: users.rb
class User < ActiveRecord::Base
scope :newsletter, -> { where(subscribed_to_newsletter: true) }
#yada yada
end
This allows you to access your subscribers by simply doing this:
User.newsletter.each do #something
This is a very simple example but in general scopes can be very powerful tools to easy your work.
Check out this link: API Description
scope in active record is like class methods but they return Relation object which means you can call another scope or active record querying method on it.
For example, if you have a Zombie model (zombies table) with below mentioned scope methods,
class Zombie
scope :rotting, -> { where(rotting: true) }
scope :fresh, -> { where('age < ?', 25) }
scope :recent, -> { order(created_at: :desc) }
end
And you call
Zombie.rotting.fresh.recent.limit(3)
It translates to the below in SQL,
select "zombies.*" from "zombies" where "zombies"."rotting" = 't' and (age<20) order by create_at desc limit 3
Example above is based on rails 4 syntax
The best way to understand about the details is to go to API Documentation.
You'll get the complete details and the ways we can use Scopes.
API Documentation of Scope
Imagine you have a model: Person.
Now imagine you :
want all the people in the world who have red hair.
want all the people in the world who play cricket
You could get those particular classes of people by using a scope!
Person.red_hair.cricket ## finds all people with red hair who play cricket
Person.red_hair ## finds all people with red hair
Person.cricket ## finds all people who play cricket.
Now that wasn't so hard was it?
Some context:
I have some models that look like this
Actor has_many Acts
Act belongs_to Actor, belongs_to Decision
Decision has_many Acts, belongs_to Prompt
Prompt has_many Decisions
What I need to do is in the ActorsController, get a random Prompt that has not been used yet of all the available Prompts.
In my rails app, Actors are presented with prompts that give them a few choices to make. When they make a choice (Decision), that is saved in the db as an Act.
I've tried various iterations of named_scope and find_by_sql, but none worked, and I'm not even sure if my thinking was right to begin with on them, since there are so many models at work, and I don't seem to know where to start.
I hope this gives an idea of what I'm up against. I'd appreciate even a general pointer to form a plan of attack even.
Thanks!
edit
After chewing on this for a couple hours, I've got something working but it's very messy, and my logs are filled with SQL calls, so it could definitely stand a critical eye.
In the Prompt model:
named_scope :available, lambda { |used|
{ :conditions => ["id NOT IN (?)", used ] }
}
In the Actor model:
def used_prompts
prompts = Array.new
if self.acts && self.acts.length >= 1
self.acts self.acts.each { |act| prompts.insert(0, act.decision.prompt.id) }
return prompts.sort
else
return [0]
end
end
And in the ActorsController:
#prompt = Prompt.available(#actor.used_prompts).find(:first, :order => "RAND()")
Obviously the if block in used_prompts is one guilty party here, but I don't know a better way to deal with that since I can't do self.acts.decisions.each or some such thing. Maybe someone can school me :)
The simplest thing to do would be to add a after_create or similar callback on the Decision model that marks the associated prompt as used. You could also achieve this using some joins, but that would take a little more work, and will possibly lead to scalability issues (if you care).