Rails 4 - method to check matching attributes - ruby-on-rails

I am trying to make an app in Rails 4.
I am using devise and want to write an after sign up redirect path based on the following logic.
I have a user model that includes an :email attribute. I have an organisation model that includes an :email_format attribute (email attribute holds the part of the email address that comes after '#'. I have a profile model (which contains the user's information that can be altered by the user itself).
The associations are:
User - has_one: profile
Profile - belongs_to :user, belongs_to: organisation
Organisation - has_many :profiles
If the user (registering) inputs an email address that includes an email format that is saved in the db, then I want to associate that new user's profile with the organisation that has the matching email format.
Example:
New user's :email = bob#cat.com
I then check the organisation table to see if any :email_attributes stored are 'cat.com'.
If they are, then the user profile for the new user, is associated with the organisation that has the matching email_format (so the organisation_id in the relevant profile table is set to match that organisation id).
Here's my best attempt at writing this:
registrations controller:
def after_sign_up_path_for(resource)
# check if the email address the user used to register includes the email format of an organisation
if #user.email.includes? #organisation(:email_format)
# if it does match, then update the user profile so that the org id equals the org id for the org that has the matching email format
#user.profile.organisation_id.update_attribute(organisation.id == (#organisation.email includes? :email_format))
else
# set up a mandrill email sender to send this message
set_flash_message('We are just getting started and will get in touch with
you as soon as we have onboarded your organisation')
end
end
I'm new to this and don't understand how to query things well. Any pointers would be very much appreciated.
NEXT ATTEMPT
Taking the suggestion below, I've tried to add an after_create callback to my profile model as follows:
after_create :update_organisation
def update_organisation
Profile.update_attribute(organisation_id: :matching_org_id) # Associations must be defined correctly for this syntax, avoids using ID's directly.
# Profile.save
end
def matching_org_id
if #self.user.email.includes? #organisation(:email_format)
# how do you ask for the organisation with the matching email format id
profile.organisation_id: organisation.id
else
TBC
end
end
This isn't working. I'm not sure how to express this method correctly. I'm currently getting an error that says:
syntax error, unexpected '(', expecting keyword_then or ';' or '\n' if #self.user.email.includes? #organisation(:email_format)

First problem:
This part is fairly dangerous: you could have bobcat.com#gmail.com return a false positive.
if #user.email.includes? #organisation(:email_format)
Perhaps you can use store a regex format and check it via
/#cat.com$/ =~ #user.email
Try out the formats at rubular.com
Second problem:
The update method should be something similar to this.
#user.profile.update(organisation_id: #matching_organisation.id)
You will need to loop through all organisations to find the matching organisation.
Third Problem
I think this is a bad place to do this sort of manipulation. You should perhaps add an after_create hook to the user model and put these logic in that method.

Related

Associate a Readable User Dropdown in ActiveAdmin

I guess not much help here. When you are trying to associate a user from a dropdown list, and imagine if you have 1000000 users, you would see the user instance. I need it to show the actual user's email address.
app/models/user.rb:
has_one :company
app/models/company.rb:
belongs_to :user
The link I was given has nothing to do with the dropdown's value.
Have I setup my rails association incorrectly? Funny thing was, using rails admin I had no issue in this department as I could associate a company when creating a user but not so with active admin.
All I want is when I select the User dropdown, as in picture, I'd see a list of user email addresses.
Tim was correct all this time. I needed to create a function in the User model.
app/models/user.rb:
def display_name
email
end

Programmatically creating User with devise and first_or_create

I've got to programmatically create users via an api -- I'm able to create users passing in the proper params ie:
x={"email"=>"kid#kid.com", "username"=>"levi", "password"=>"password","password_confirmation" => "password", "firstname"=>"Bob", "lastname"=>"Smith"}
u=User.new(x)
u.valid?
And able to check if this is a valid user before saving, etc. But the problem is that if params such as username or email already already exist for another user I'll get an error on save.
Now I can check if a user exists already (ie u=User.where(username: x['username']) or u=User.where(email: x['email']) -- and that might be enough but I'm wondering if there's a way of doing u=User.where(x).first_or_create with devise? Works generally with other models but not devise.
The problem with find_or_create_by (http://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by) is that it will try to find a user matching all of the attributes you pass in as criteria. It is rarely a useful method. Instead, wrap it in your model:
class User < ActiveRecord::Base
def self.username_find_or_create(attributes = {})
User.create(attributes) unless User.exists?(attributes.slice(:username, :email))
end
end
(Exists is faster than where)

Validate uniqueness of email against another model's email address's

I have a User model that has a "has_many" :emails association to an Email model. The user's primary email is just a column "email" for the user. However, I have this association that allows for multiple email addresses for a user.
The problem is that when a new user is created and "validates_uniqueness_of :email" is checked, it only checks other user's email column instead of possible secondary emails stored in the Email model.
I have tried to write a custom validate method in my User model that tries to find a user by the email address given and it works, but then it screws up other things in my code, like being able to add secondary email addresses or assign primary ones.
Is there a way to say something like: "validates_uniqueness_of :email through Email.all.each do {|e| errors.add :email, "this is invalid" if e.email == :email}"
This looks really sloppy, but all I'm trying to do is validate the uniqueness of the email address of a user upon creation against another model's instance's email address.
P.S. In case you are confused about the e.email, Email is the model and it has a column email that stores the email address.
You'll want to write a custom validator. Something along these lines:
class User
validates_uniqueness_of :email
validate :email_in_use
private
def email_in_use
if Email.where("lower(email) = ?", self.email.downcase).first
errors.add(:email, "There is already a user with this email")
end
end
end
I agree with #CWitty, though – you may want to consider moving the primary email address into the 'emails' table, rather than storing it straight on the User model.
It sounds like you need to think about your schema a bit. I would remove the email column from your User model and instead have all of the emails stored using the association. You could add a default flag to the Email model to signify the users default email. This would allow you to move the validates_uniqueness_of :email to the Email model and not try to check it in multiple places. Plus it cleans up your data so that it isn't in multiple places.

How to obtain BEFORE and AFTER state of my object's related attributes?

In my application I have the following relationship:
Document has_and_belongs_to_many Users
User has_and_belongs_to_many Documents
What I am trying to figure out is how to perform the following:
Let's say a document has 3 users which belong to it. If after an update they become for ex. 4, I would like to send an email
message (document_updated) to the first 3 and a different email message (document_assigned) to the 4th.
So I have to know the users belonging to my Document BEFORE and AFTER the Document update occurs.
My approach so far has been to create an Observer like this:
class DocumentObserver < ActiveRecord::Observer
def after_update(document)
# this works because of ActiveModel::Dirty
# #old_subject=document.subject_was #subject is a Document attribute (string)
# this is not working - I get an 'undefined method' error
#old_users=document.users_was
#new_users=document.users.all.dup
# perform calculations to find out who the new users are and send emails....
end
end
I knew that my chances of #old_users taking a valid value were slim because I guess it is populated dynamically by rails via the has_and_belongs_to_many relation.
So my question is:
How do I get all my related users before an update occurs?
(some other things I've tried so far:)
A. Obtaining document.users.all inside DocumentController::edit. This returns a valid array, however I do not know how to pass this array to
DocumentObserver.after_update in order to perform the calculations (just setting an instance variable inside DocumentController is of course not working)
B. Trying to save document.users inside DocumentObserver::before_update. This is not working either. I still get the new user values
Thanks in advance
George
Ruby 1.9.2p320
Rails 3.1.0
You could use a before_add callback
class Document
has_and_belongs_to_many :users, :before_add => :do_stuff
def do_stuff(user)
end
end
When you add a user to a document that callback will be called and at that point self.users will still it yet contain the user you are adding.
If you need something more complicated it might be simpler to have a set_users method on document
def set_users(new_user_set)
existing = users
new_users = users - new_user_set
# send your emails
self.users = new_user_set
end

Rails 3 Admin Section Design Question About Inheritance and Namespacing

The way my site operates, some records are created by the user but are only partially filled out. An administrator has to complete some of the record fields. I was thinking about putting all the validations related to the administrative fields in a subclass.
For example, in /app/models/document.rb:
class Document < ActiveRecord::Base
# minimal validations needed, etc
end
In /app/models/admin/document.rb (I'm not even sure if the syntax below is valid Ruby)
class Admin::Document < Document
# Extra validations for the fields the admin
end
Would my approach be a bad idea? I also plan on having role based authentications using something like CanCan.
Perhaps an alternative route would be to use CanCan to allow admins to complete a users document but instead of subclassing the record or splitting it over two. When the form is submitted add an administrated ( or approved ) field then if its one admin or several you can search by the unadministrated( or unapproved )
Surely at the end of the day the admin does the final submission so they can then set the administrated ( or approved ) to true. Thereby finalizing the document?
I think it might be better to keep all of the validations in one class, and have a boolean database column that stores if the record has been completed by the administrator.
class Document < ActiveRecord::Base
attr_protected :completed_by_administrator # do not allow this to be set via mass assignment
validate ..., :if => :completed_by_administrator # only do these validates if completed_by_administrator is true
end
When the record is first created, completed_by_administrator is false and the admin-only validations do not run. When the record is completed by an administrator, set completed_by_administrator = true in your controller and the admin-only validations automatically run before the record is saved.

Resources