I have the following code:
class User < ActiveRecord::Base
named_scope :regular_users, :conditions => { :is_developer => false }
end
How can I change this code to return if a specific user is a regular user (has :is_developer => false ) instead of a list of all regular users?
Thanks
You can just check User.find(1).is_developer? (actually it will work even without the ?)
To check the opposite, use ! User.find(1).is_developer? or not User.find(1).is_developer
or put this in a model method like
def is_regular?
! is_developer?
end
I doubt that you can get boolean value with scope.
btw, with Rails3 you can use scope instead of named_scope
Related
I have a user table in my rails application and the application uses many where conditions for this model throughout the application in many controller methods.
Now i have to add an extra attribute for the where condition.
is there a way to do the following and how? instead of adding the extra attribute to all the where condition used in the entire application can i write a custom where to the user model so the condition will be pre-added to the where in entire application for the user model.
i found out the source for the where
def where(opts = :chain, *rest)
if :chain == opts
WhereChain.new(spawn)
elsif opts.blank?
self
else
spawn.where!(opts, *rest)
end
end
my where condition in the controller methods now:
User.where(:status => true, :country => "IN")
this condition and similar conditions are used in many methods in application and i want to get the user who has not :deactivated.
i can make changes to all where condition like
User.where(:status => true, :country => "IN", :deactivated => false)
instead i thought of writing a custom where that precheck :deactivated => false
Default Scope:
class User < ActiveRecord::Base
default_scope -> { where(deactivated: false) }
end
You can use default_scope.
Now, whenever you query User, automatically the default scope query will get appended.
For more details on default_scope, please refer:
https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-default_scope
If there are usecases that prevent you from using default_scope, then you can use custom scopes or unscope the default scope.
Unscoping:
You can unscope in Project model if you want to remove the default scope.
belongs_to :user, ->{ unscope(where: :deactivated) }
Or you can fetch all user and then unscope
project.users.unscoped
Custom Scope:
class User < ActiveRecord::Base
scope :deactivated, ->(deactivated = false) { where(deactivated: deactivated) }
end
Now, to make use of that scope, you can query like this:
User.deactivated.where(:status => true, :country => "IN")
For reference:
https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope
I'm trying to set a default scope so that Users where notified: true are soft-deleted. notified is a boolean data column.
This is what I've tried:
class User < ActiveRecord::Base
default_scope { where('notified != ?', true) }
#...
end
But this way, no users appear in any scope. ie - all the users appear to be soft-deleted, even the ones with notified: false or notified: nil. What's wrong with my scope?
I recommend using boolean values the database understands. In this case you want to see users that have a notified that is not true, so I'd user:
default_scope { where('notified IS NOT TRUE') }
That way users will only appear in other scopes if their boolean database values is FALSE or NULL.
Note: default scopes are actually considered a code smell... because they're a bit magic and hide away what you really mean when you fetch out users. You might want to instead create an active and inactive scope and specify them explicitly in your code eg:
scope :active ->{ where('notified IS NOT TRUE') }
scope :inactive ->{ where('notified IS TRUE') }
# in your controller
def index
#users = User.active.all
end
According to your objective of 'set a default scope so that Users where notified != true are soft-deleted.', you should use default_scope { where(manual_down: true) }, which will only retrieve the records with that column is TRUE, and ignore the others (FALSE or NIL)
I totally agree with Taryn East. Changing/removing default_scope might require lots of modification to logic that depends on that model, so use it only if you're sure you don't change the default_scope condition later (which is usually not the case).
I am using Ruby on Rails 3.0.7 and I would like to find some records at run time for validation purposes but passing\setting a value for that finder method. That is, in a my class I have the following:
class Group < < ActiveRecord::Base
validates :relation_id,
:presence => true,
:inclusion => {
:in => ... # Read below for more information about
}
end
If I set :in to be
:in => User.find(1).group_ids
it works, but I would like to set "some-dynamic-things" for the finder method instead of the 1 value stated below in the example. That is, I would like to do something like the following in order to pass to the model a <test_value> in someway:
class Group < < ActiveRecord::Base
validates :relation_id,
:presence => true,
:inclusion => {
:in => User.find(<test_value>).group_ids
}
end
Is it possible? If so, how can I pass the value to the constant?
P.S.: Just to know, I am trying to make that in order to move some logic from the controller to the model.
I'm inferring that what you're trying to do is enforce something like "Only users who are members of a group can save it." If that's the case, you have behavior that should stay in the controller.
Your model doesn't have access to the current session, and adding this logic will prevent you from using your model for other things in the future. For example, you'd never be able to save a group from a batch or maintenance job that wasn't associated with a user.
If you really want to do this you could put a current_user class level variable in the User object and set it in a before_filter...
class ApplicationController
before_fitler :set_current_user
def set_current_user
User.current_user = #however you get your user in your controllers
end
end
class User
##current_user
end
class Group
validates :user_in_group
def user_in_group
return true unless User.current_user #if we don't have a user set, skip validation
User.current_user.group_ids.include? self.id
end
end
It looks like you want something like a proc to be run for the validator for the :in attribute. I think you may be threading in dangerous territory when you rely on load order of models and playing with "dynamic constants".
Instead how about just building your own custom validator for this case?
It's not that hard, and you will have full control of what you need:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#creating-custom-validation-methods
I need to validate a value's presence, but only AFTER the value is populated. When a User is created, it is not required to set a shortcut_url. However, once the user decides to pick a shorcut_url, they cannot remove it, it must be unique, it must exist.
If I use validates_presence_of, since the shortcut_url is not defined, the User isn't created. If I use :allowblank => true, Users can then have "" as a shortcut_url, which doesn't follow the logic of the site.
Any help would be greatly appreciated.
Here we are always making sure the shortcut_url is unique, but we only make sure it is present if the attribute shortcut_selected is set (or if it was set and now was changed)
class Account
validates_uniqueness_of :shortcut_url
with_options :if => lambda { |o| !o.new_record? or o.shortcut_changed? } do |on_required|
on_required.validates_presence_of :shortcut_url
end
end
You'll need to test to make sure this works well with new records.
Try the :allow_nil option instead of :allow_blank. That'll prevent empty strings from validating.
Edit: Is an empty string being assigned to the shortcut_url when the user is being created, then? Maybe try:
class User < ActiveRecord::Base
validates_presence_of :shortcut_url, :allow_nil => true
def shortcut_url=(value)
super(value.presence)
end
end
try conditional validations, something like:
validates_presence_of :shortcut_url, :if => :shortcut_url_already_exists?
validates_uniqueness_of :shortcut_url, :if => :shortcut_url_already_exists?
def shortcut_url_already_exists?
#shortcut_url_already_exists ||= User.find(self.id).shortcut_url.present?
end
I have a MailingList model that has_may :people
For most of my application, I only want to get people that are active
So #mailing_list.people should only return people that are active
In my model, I can't do
def people
self.people.find_all{ |p| !p.activated_at.nil? }
end
because that keeps calling itself. What is the ruby/rails way to automatically filter the people. Another possible issue is that I think self.people returns an array of active record objects where self.people.find_all... will return an array. This will cause some of my code to break. It's easy fixes but is there a way to return active record objects? It would be nice to have the option.
Thanks!
This is a perfect example for a named scope:
class Person < ActiveRecord::Base
named_scope :active, :conditions => 'activated_at is not null'
end
Then just call it:
# equivalent to Person.find(:all, :conditions => 'activated_at is not null')
#active_people = Person.active
You can also filter at the association level.
has_many :people, :conditions => {:activated => true}
You can used the standard find method or a dynamic finder. Your find might read as follows:
people.find(:all, :conditions => "activated_at = nil")
OR
people.find_all(:conditions => "activated_at = nil")
A dynamic version of this might read as:
people.find_by_activated_at(nil)