Rails 3 validate uniqueness ignores default scope on model - ruby-on-rails-3

I'm using the permanent_records gem in my rails 3.0.10 app, to prevent hard deletes and it seems rails is ignoring my default scope in checking uniqueness
# user.rb
class User < AR::Base
default_scope where(:deleted_at => nil)
validates_uniqueness_of :email # done by devise
end
in my rails console trying to find a user by email that has been deleted results in null, but when signing up for a new account with a deleted email address results in a validation error on the email field.
This is also the case for another model in my app
# group.rb
class Group < AR::Base
default_scope where(:deleted_at => nil)
validates_uniqueness_of :class_name
end
and that is the same case as before, deleting a group then trying to find it by class name results in nil, however when I try to create a group with a known deleted class name it fails validation.
Does anyone know if I am doing something wrong or should I just write custom validators for this behavior?

Try scoping the uniqueness check with deleted_at
validates_uniqueness_of : email, :scope => :deleted_at
This can allow two records with the same email value as long as deleted_at field is different for both. As long as deleted at is populated with the correct timestamp, which I guess permanent_records gem does, this should work.

Related

What is better way to create Client record after User is registered (corresponding client record)

Basically i have user registering himself to the app , by using devise gem.
Instead of having standard sign up form like (email, password) i have an extra 2 fields (name, contact_nr) in total used (name, contact_nr, email, password, password_confirm) fields, :name and :contact_nr attributes exists in 'clients' table only.
Table name: clients
id :integer not null, primary key,
name :string(255)
surname :string(255)
contact_nr :string(255)
user_id :integer
class Client < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
after_create :update_user_client
def name
return unless client
client.name
end
def contact_nr
return unless client
client.contact_nr
end
def update_user_client
Client.last.update_attributes(user: self)
end
end
In my RegistrationsController I have only one method
class RegistrationsController < Devise::RegistrationsController
before_action :create_client
private
def create_client
return if params[:user].blank?
Client
.new(name: params[:user][:name],
contact_nr: params[:user][:contact_nr])
.save(validate: false)
end
end
What bothers me is that kind of writing code, it feels like code smell.
How would you implement it?
Thanks guys looking forward to your answers..
First advice I can give is do not separate client and user into two tables if you don't have valid reasons and/or requirements for now. That would make things much easier.
If you have valid reasons, here are my advices on how to improve your existing state of this code piece:
Rails and all mature gems around it rely on 'convention over configuration', so you should check if there are conventional ways to achieve same results.
In your RegistrationsController instead of doing params[:user].blank? check, you should use Devise's way of doing this, provided with inherited methods as devise_parameter_sanitizer.permit within a before_action callback.
Instead of creating client in your controller, move that to model logic, and in your user model put accepts_nested_attributes_for :client.
Since both of your models(client and user) share same name, put a before_save callback, so that you can pass user's name attribute to client itself.
after_create callback is very risky, since it is not an atomic save (no guarantee that client will be updated after user record is updated.). So don't use it. accepts_nested_attributes_for will handle both create and update calls.
If name attribute for user would be fetched through client only, there is no need to keep name within user.
If you want to access client's contact_nr and name attributes directly from user model, then use delegate method inside it.
Putting all together, I would refactor that code piece as this:
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
accept_nested_attributes_for :client
delegate :name, to: :client
delegate :contact_nr, to: :client
# optional. if you want to keep name attr in both models.
before_save :sync_names
private
def sync_names
self.client.name = name if client.present?
end
end
class RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
added_attrs = [:name, :email, :password, :password_confirmation, client_attributes: [:contact_nr]]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
end
Don't forget to update your signup and account update forms to accept nested attributes for client resource.
As far as you are validating the data with JS and filtering with params.require(:client).permit, the code looks fine. Try to create many differente scenarios in your Rspec. The test usually reveals unexpected flaws.

Devise allow email uniqueness within scope

I'm in the process of upgrading a large project from rails 2 to rails 3, as a part of that upgrade I'm replacing a very old restful_athentication with devise.
The problem I'm having is that in the existing users table emails are validated like this.
validates_uniqueness_of :email, :scope => :account_id # No dupes within account
So if I add the index from the migration to add devise to users it WILL fail.
Is there a way that I can use
add_index :users, [:email,:account_id]
And have devise work properly?
I managed to get this working on my own, I added the following to config/initializer/devise.rb
config.authentication_keys = [ :email , :account_id]

how to make has_and_belongs_to_many relationship work in mongoid

i have the following code in the rails company model:
class Company
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
...
has_and_belongs_to_many :users
end
User model:
class User
include Mongoid::Document
include Mongoid::Timestamps
include ActiveModel::SecurePassword
field :email, type: String
...
has_and_belongs_to_many :companies
end
There is a company record in the database, and a user record and they are associated. For some reason, the following code does NOT work:
c = Company.first
c.users # returns empty array
similarly, the followign code does not work:
u = User.first
u.companies
But the following code DOES work:
c = Company.first
user = User.find c.user_ids.first
and the following code also works:
u = User.first
company = Company.find u.company_ids.first
so if i try to access users from the company.users, it does not work, but the user_ids array does have a list of user ids, and when i try to access the users from this list, it works. How can i fix this issue?
i am using rails 3.2.5 and mongoid 3.0.0.rc
I had exactly the same issue ;)
Make sure you're using mongodb version > 2.0.0, for more details see: http://mongoid.org/en/mongoid/docs/installation.html#installation

Testing route in controller

I have a Rails 3.0 web app that allow user to create own path to the application.
example : www.my_app.com/user_company_name
So I store a custom path in user DB field. User can changing path throught a input.
I have added this validation in model
validates_presence_of :custom_page
validates_format_of :custom_page, :with => /^([a-z]|[0-9]|\-|_)+$/, :message => "Only letter (small caps), number, underscore and - are authorized"
validates_length_of :custom_page, :minimum => 3
validates_uniqueness_of :custom_page, :case_sensitive => false
But I don't know how I can validate url to check it isn't in conflict with another route in my routing.
For example in my route.rb I have
resources :user
Validation need to don't allow using www.my_app.com/user, how I can do that?
Thanks, vincent
In your routes, you match the company name to a variable
match 'some_path/:company_name.format'
you can then do the lookup using company_name which rails will populate for you.
Validating the uniqueness of the custom_page variable should be enough to ensure there's no overlap. (note that validate uniqueness of doesn't scale -- if this will be big, you need a db constraint as well) as long as users can only specify one field.
If you're letting users specify
'some_path/:custom_path_1/:custom_path_2.format'
then you have to validate across both fields, and now it's getting messy. Hope you're not doing that.
You can try a custom validation to weed out "user"
validate :custom_page_cant_be_user
def custom_page_cant_be_user
errors.add(:custom_page, "can't be `user`") if self.custom_page =~ /^user$/i
end
assuming :custom_page comes in as a basic [a-z], if :custom_page has /user you need to update the regex a bit.

Strange behavior of references in MongoDB

I am using Rails 3 with Mongoid.
I have two documents:
class MyUser
include Mongoid::Document
field ......
references_many :statuses, :class_name => "MyStatus"
end
class MyStatus
include Mongoid::Document
field ......
referenced_in :user, :class_name => "MyUser"
end
The problem is, I can get the user of any given status, but I cannot get the list of statuses from a user!
ie.
status = MyStatus.first
status.user # the output is correct here
user = MyUser.first
user.statuses # this one outputs [] instead of the list of statuses...
Please tell me what have I done wrong? I am just a few days with mongo......
Your code looks correct to me.
Are you sure that MyStatus.first.user == MyUser.first ?
It's possible that you have multiple users in your db.. where the first user has no statuses, and the second user has status1 in his list.
To test this, try doing:
status = MyStatus.first
user = status.user
user.statuses # Should return at least one status