activerecord validation - validates_associated - validation

I'm unclear on what this method actually does or when to use it.
Lets say I have these models:
Person < ...
# id, name
has_many :phone_numbers
end
PhoneNumber < ...
# id, number
belongs_to :person
validates_length_of :number, :in => 9..12
end
When I create phone numbers for a person like this:
#person = Person.find(1)
#person.phone_numbers.build(:number => "123456")
#person.phone_numbers.build(:number => "12346789012")
#person.save
The save fails because the first number wasn't valid. This is a good thing, to me. But what I don't understand is if its already validating the associated records what is the function validates_associated?

You can do has_many :phone_numbers, validate: false and the validation you're seeing wouldn't happen.
Why use validates_associated then? You might want to do validates_associated :phone_numbers, on: :create and skip validation on update (e.g. if there was already bad data in your db and you didn't want to hassle existing users about it).
There are other scenarios. has_one according to docs is validate: false by default. So you need validates_associated to change that.

Related

How to query with has_may/belongs_to associations in controller?

I am really confused on how the has_many and belongs_to works within the controller, more specifically how to query the data.
I have a Users modal, and Tasks model, and a User can have many Tasks and Tasks belong to one particular user.
Here are my Model's:
class Task < ActiveRecord::Base
belongs_to :user
validates :title,
presence: true,
length: {minimum: 5, maximum: 50}
validates :description,
presence: true,
length: {minimum: 1, maximum: 140}
end
class User < ActiveRecord::Base
has_many :tasks, dependent: :destroy
has_secure_password
validates :email,
presence: true,
uniqueness: true
end
So in my Tasks controller for example how would I implement the this same action:
def index
# Get all tasks from database
#tasks = Task.all
# how would you achieve the same thing, but only show tasks that belong to a specific user? something like this:
#tasks.users.find(:all)?
end
I have been doing research but I can't seem get a grasp on this. anyways any explanation would help out a lot. thanks guys.
http://guides.rubyonrails.org/active_record_querying.html
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
First find the User record using find or find_by_id.
user = User.find_by_id(id)
Then call tasks on the user object,which will list all the tasks of that particular user.
list_of_tasks = user.tasks
For proper ways of querying a has_many association, refer to Rails' documentation on eager loading associations (specifically the includes method):
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
I'm not sure exactly what kind of information you'd like to pull from your database tables, but here are a couple examples of how you might query all tasks and their associated users.
#tasks_and_users = Task.all.includes(:user)
#tasks_and_users.each do |task|
puts "The user with email '#{task.user.email}' has this task: #{task.title}"
end
Or, if you'd like a list of all users (regardless of whether or not they have any tasks) and their associated tasks, here is an alternative:
#users_and_tasks = User.all.includes(:tasks)
#users_and_tasks.each do |user|
puts "The user with with email '#{user.email}' has the following tasks:"
user.tasks.each do |task|
puts "\t Task: #{task.title}"
end
end

Attempting to create a database item using the has_one relationship, no exceptions, but still no item

Models:
A User has_one Ucellar
A Ucellar belongs_to User
I have confirmed from multiple sources that these are set up correctly. For posterity, here is the top portion of those two models.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
This is actually the entire Ucellar model.
class Ucellar < ActiveRecord::Base
belongs_to :user
end
Ucellar has a column called user_id, which I know is necessary. The part of my application that creates a user uses the method create_with_oath. Below is the entire User class. Note the second line of the create method.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
def create
#user = User.new(user_params)
#ucellar = #user.create_ucellar
end
def add_provider(auth_hash)
# Check if the provider already exists, so we don't add it twice unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"]
end
end
def self.create_with_omniauth(auth)
user = User.create({:name => auth["info"]["name"], :email => auth["info"]["email"]})
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
EDIT:
Forgot to summarize the symptoms. On create, the user is in the db, with no exceptions thrown, and nothing to signify that anything went wrong. However, the related ucellar is never created. Per the documentation Here, the create method should create AND save the related ucellar.
It should create ucellar too.
Try to get the error messages after the creation by calling:
raise #user.errors.full_messages.to_sentence.inspect
I'm not sure why this wasn't working, but I ended up just moving this code out of the create action of the user controller, and putting it directly after an action that was creating a user. It solved my issue though. Thanks everyone for your help!

How to define method names and object references before actually having to use them (Ruby)

Say I am keeping track of email correspondances. An enquiry (from a customer) or a reply (from a supporter) is embedded in the order the two parties are corresponding about. They share the exact same logic when put into the database.
My problem is that even though I use the same logic, the object classes are different, the model fields I need to call are different, and the method names are different as well.
How do I put methods and objects references in before I actually have to use them? Does a "string_to_method" method exists or something like that?
Sample code with commentaries:
class Email
include Mongoid::Document
field :from, type: String
field :to, type: String
field :subject, type: String
belongs_to :order, :inverse_of => :emails
def start
email = Email.create!(:from => "sender#example.com", :to => "recipient#example.com", :subject => "Hello")
from_or_to = from # This represents the database field from where I later on will fetch the customers email address. It is either from or to.
enquiries_or_replies = enquiries # This represents a method that should later be called. It is either enquiries or replies.
self.test_if_enquiry_or_reply(from_or_to, enquiries_or_replies)
end
def test_if_enquiry_or_reply(from_or_to, enquiries_or_replies)
order = Order.add_enquiry_or_reply(self, from_or_to, enquiries_or_replies)
self.order = order
self.save
end
end
class Order
include Mongoid::Document
field :email_address, type: String
has_many :emails, :inverse_of => :order
embeds_many :enquiries, :inverse_of => :order
embeds_many :replies, :inverse_of => :order
def self.add_enquiry_or_reply(email, from_or_to, enquiries_or_replies)
order = Order.where(:email_address => email.from_or_to).first # from_or_to could either be from or to.
order.enquiries_or_replies.create!(subject: email.subject) # enquiries_or_replies could either be enquiries or replies.
order
end
end
Judging by the question and the code sample, it sounds like you are mixing concerns too much. My first suggestion would be to re-evaluate your method names and object structure. Ambiguous names like test_if_thing1_or_thing2 and from_or_to (it should just be one thing) will make it very hard for others, and your future self, to understand the code laster.
However, without diverging into a debate on separation of concerns, you can change the methods you call by using public_send (or the private aware send). So you can do
order.public_send(:replies).create!
order.public_send(:enquiries).create!
string to method does exist, it's called eval
so, you could do
method_name = "name"
eval(method_name) #calls the name method

Many-to-Many Uniqueness Constraint Test Not Working

I have a many-to-many relationship with a join table in my Rails application. I'm using the has_many :through idiom in my models. To keep things simple, lets call my first class Student, my second class Course, and the join table class Enrollment (which contains fields student_id and course_id). I want to make sure that a given Student is associated with a given Course at most once (i.e. the {student_id, course_id} tuple should be unique in the enrollment table).
So I have a migration a that enforces this uniqueness.
def change
add_index :enrollments, [:student_id, :course_id], :unique => true
end
In addition my model classes are defined as such:
class Student < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollment
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :students, :through => :enrollment
end
class Enrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
validates :student, :presence => true
validates :course, :presence => true
validates :student_id, :uniqueness => {:scope => :course_id}
end
In a rails console, I can do the following:
student = Student.first
course = Course.first
student.courses << course
#... succeeds
student.courses << course
#... appropriately fails and raises an ActiveRecord::RecordInvalid exception
In my RSpec test, I do the exact same thing and I get no exception with the following code:
#student.courses << #course
expect { #student.courses << #course }.to raise_error(ActiveRecord::RecordInvalid)
And so my test fails and reports:
expected ActiveRecord::RecordInvalid but nothing was raised
What's going on here? What could I be doing wrong? How do I fix it?
Rails uses model level validation, if you want strict checking for uniquiness you need to use database level - foreign keys for example. But in this case you need to catch exceptions from database connector.
This is strange because in my code (very similar to your) validation for unique raises exception.
There's a couple of things here that could be happening:
#courses has changed between uses.
#student has changed between uses.
By using let you'll protect these values from changing between expectations.
let(:course) { Course.first }
let(:student) { Student.first }
subject{ student.courses << course << course }
it { should raise_error(ActiveRecord::RecordInvalid) }
Or, there could just be something wrong with your code :)

MongoMapper: how do I create a model like this

Suppose we have two models, Task and User.
So a user can have many tasks and tasks should be able to have many users too. But, a task should also have a unique creator who is also a user.
Exemple:
A task in this context is like this:
Task ID, Task Creator, Users who should do the task
User_1 creates a task and he is then the creator.
User_1 specifies User_2 and User_3 as users who should do the task. So these two last users are not creators of task.
How do I create this models so that if I have a task object, I can find it's creator and users who should complete it. And how do I do, if I have a user, to find all tasks he created and all tasks he should complete.
You'll need a many-to-many relationship between the Tasks and Users, and you need an additional one-to-many relationship between Users and Tasks, pointing to the creator (User).
Something along these lines: (I usually use Mongoid, so double-check the syntax for the relations in the MongoMapper API - link below.. you might to manually specify :foreign_key and :class)
The idea is that you have two relationships between the models, one which models the many-to-many relationship
with which you get either to the assigned_users or assigned_tasks, and a one-to-many relationship with which you get to either the creator of a task, or the created_tasks for a given user. If you chose these names for the relationships, it will be clear which is which.
class Task
include MongoMapper::Document
key :title, String , :required => true
key :user_ids , Array
has_many :users, :in => user_ids # , :as => :assigned_users
key :creator_id , ObjectId
belongs_to: user, :as => :creator
end
class User
include MongoMapper::Document
key: name, String, :required => true
has_many :tasks # , :as => :assigned_tasks
has_many :tasks, :as => :created_tasks
end
See:
http://mongomapper.com/documentation/plugins/associations.html
The answer suggested by Tilo is correct about how to model the data, but the example code is incorrect and will not work. The :as option is for polymorphic associations, you want to use the :foreign_key option. Also, you can't have two associations named the same. See below for revised code.
class Task
include MongoMapper::Document
key :title, String , :required => true
key :assigned_user_ids, Array
has_many :assigned_users, :in => :assigned_user_ids
key :creator_id , ObjectId
belongs_to :creator, :class => User
# userstamps! also accomplishes the above
end
class User
include MongoMapper::Document
key: name, String, :required => true
has_many :created_tasks, :foreign_key => :creator_id, :class => Task
# inverse of many :in is still in the works
# see https://github.com/jnunemaker/mongomapper/pull/259
# this is a decent workaround for now
def assigned_tasks
Task.where(:assigned_user_ids => self.id)
end
end
See also:
MongoMapper userstamps! documentation

Resources