Rails setting OR conditions in validate_presence_of in a model? - ruby-on-rails

In a rails model, is it possible to do something like
class Example < ActiveRecord::Base
#associations
validates_presence_of :item_id, (:user_id OR :user_email)
#functions
end
Where the model has 3 columns of :item_id, :user_id, and :user_email?
I want the model to be valid as long as I have a :user_id or a :user_email.
Idea being that if the item is recommended to a person who isn't currently signed up, it can be associated via email address for when the recommended person signs up.
Or is there a different method that I can use instead?

One approach is to wrap those fields as a virtual attribute, say:
class Example < ActiveRecord::Base
validates_presence_of :referral
def referral
user_id || user_email
end
end
or you can just throw a custom validate validation method. See custom validations on the Rails API
If both user_id and user_email come from another model, perhaps it's better to add the association instead
class Example
belongs_to :user
validates_associated :user
before_validate :build_user_from_id_or_email
def build_user_from_id_or_email
# ... Find something with the parameters
end
end

validates_presence_of :item_id
validates_presence_of :user_id, :if => Proc.new{ |x| x.user_email.blank? }
validates_presence_of :user_email, :if => Proc.new{ |x| x.user_id.blank? }

Related

Editing a has_one association in ActiveAdmin - avoid saving when nothing is entered

I've got a model in which a very small percentage of the objects will have a rather large descriptive text. Trying to keep my database somewhat normalized, I wanted to extract this descriptive text to a separate model, but I'm having trouble creating a sensible workflow in ActiveAdmin.
My models look like this:
class Person < ActiveRecord::Base
has_one :long_description
end
class LongDescription < ActiveRecord::Base
attr_accessible :text, :person_id
belongs_to :person
validates :text, presence: true
end
Currently I've created a form for editing the Person model, looking somewhat like this:
form do |f|
...
f.inputs :for => [
:long_description,
f.object.long_description || LongDescription.new
] do |ld_f|
ld_f.input :text
end
f.actions
end
This works for adding/editing the LongDescription object, but I still have an issue: I'd like to avoid validating/creating the LongDescription object if no text is entered.
Anyone with better ActiveAdmin skills than me know how to achieve this?
Are you using accepts_nested_attributes_for :long_description? If so, you can add a :reject_if option:
class Person < ActiveRecord::Base
has_one :long_description
accepts_nested_attributes_for :long_description, reject_if: proc { |attrs| attrs['text'].blank? }
end
Note that this is a Rails thing, not an ActiveAdmin thing, and so it will simply skip assignment and update/create of the nested object if that attribute is missing.
More here: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Editing a has_one association in ActiveAdmin - destroying when attribute is blanked

I've got a model in which a very small percentage of the objects will have a rather large descriptive text. Trying to keep my database somewhat normalized, I wanted to extract this descriptive text to a separate model, but I'm having trouble creating a sensible workflow in ActiveAdmin.
My models look like this:
class Person < ActiveRecord::Base
has_one :long_description
accepts_nested_attributes_for :long_description, reject_if: proc { |attrs| attrs['text'].blank? }
end
class LongDescription < ActiveRecord::Base
attr_accessible :text, :person_id
belongs_to :person
validates :text, presence: true
end
Currently I've created a form for editing the Person model, looking somewhat like this:
form do |f|
...
f.inputs :for => [
:long_description,
f.object.long_description || LongDescription.new
] do |ld_f|
ld_f.input :text
end
f.actions
end
This works for adding/editing the LongDescription object, and it avdois validating/creating the LongDescription object if no text is entered.
What I'd like to achieve is to also be able to remove the LongDescription object, for example if the text attribute is ever set to an empty string/nil again.
Anyone with better Rails or ActiveAdmin skills than me know how to achieve this?
That seems like an awfully unusual architecture decision, but the implementation is pretty simple:
class LongDescription < ActiveRecord::Base
validates_presence_of :text, on: :create
after_save do
destroy if text.blank?
end
end

Validate before Posting a text or an image

I am working on a self-learning Rails application (the source code can be found here. I want to validate the presence of the content before posting a text or an image:
.
Those are my models or look below:
class Post < ActiveRecord::Base
belongs_to :user
default_scope { order ("created_at DESC")}
belongs_to :content, polymorphic: true
has_reputation :votes, source: :user, aggregated_by: :sum
end
class PhotoPost < ActiveRecord::Base
has_attached_file :image, styles: {
post: "200x200>"
}
end
class TextPost < ActiveRecord::Base
attr_accessible :body
end
Here are my controllers in case they have a relation with this. Any other files can be found in my Github account. I am sure it will be messy to copy the whole project (that is why I am giving links for the controllers and for my project).
So what I have tried so far. (I tried those on the Posts Model)
=> Using validates_associated
validates_associated :content, :text_post
and getting an error "undefined method `text_post' for #Post:0x517c848>"
=> Used validates
validates :content, :presence => true
and getting no error however a post is created with no text.
validates :body, :presence => true
and getting an error "undefined method `body' for #Post:0x513e4a8>"
If you need any other information please let me know and I will provide it asap.
Thank you.
It would seem you have quite a confusing model setup with some key missing relation rules. E.g. Polymorphic rule which is not being utilised and a has_many relation between User and Post with no sign a of a user_id value in the Post model. Here is how I would set it up:
User.rb
def User << ActiveRecord::Base
has_many :text_posts
has_many :photo_posts
end
TextPost.rb
def TextPost << ActiveRecord::Base
attr_accessible :body, :user_id
belongs_to :user
validates :body, :presence => true
end
PhotoPost.rb
def PhotoPost << ActiveRecord::Base
attr_accessible :image, :user_id
belongs_to :user
validates :file, :presence => true, :format => {
:with => %r{\.(gif|png|jpg)$}i,
:message => "must be a URL for GIF, JPG or PNG image."
}
end
Then in your view you would need to do:
<%= form_for #text_post do |f| %>
# ...
<% end %>
And in your controller you can modify the create method to include the current_user from devise and assign it to the new text post record (user_id attribute):
text_posts_controller.rb
def create
#text_post = current_user.text_posts.new(params[:text_post])
end
This adheres more to the DRY principle which Ruby on Rails excels at - you shouldn't be writing alot of code to just create a new record.
I would advise on reading up on some Ruby on Rails standard and best practises. You shouldn't need to create a method in the Dashboard Model in order to create a new TextPost or PhotoPost record. This is a very confusing way of going about it; instead you should be utilising the power of ActiveRecord relation.
I would advise checking out Railscasts. They have alot of fulfilling content.

Rails: How to receive errors from nested models

I am trying to validate emails given from a CSV list of emails. So I have created the invite_list virtual attribute where when given a list of emails, it will loop and create a new record in the invited_only_emails model.
Now the thing is, this works fine, but how can I catch the validation error thrown by InvitedOnlyEmail while looping in Users model so I'll be able to use that error in the controller?
This is my main model:
class Users < ActiveRecord::Base
attr_accessor :invite_list
attr_accessible :invite_list
has_many :invited_only_emails
def invite_list=(list)
list.split(",").each do |address|
self.invited_only_emails.create! :email => address
end
end
def invite_list
self.invited_only_emails.map {|email| email.email}.join(',')
end
end
And this is the invited_only_emails model:
class InvitedOnlyEmail < ActiveRecord::Base
attr_accessible :email
belongs_to :users
validates_format_of :email, :with => /^([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})$/i
end
Thanks!
I think you could use validates_associated method:
class User < ActiveRecord::Base
validates_associated :invited_only_emails
# ...
end
http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_associated
Have you looked at validates_associated?
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_associated

Rails form structure for accepting Primary and Secondary email

I want to have a 'contact person' form that allows a user to enter their Personal and Work email at the same time.
I can imagine two ways of doing this but am not sure they're optimal and may be missing a Rails way of doing so:
Have the nested form create the email model twice, but add a flag for :position to identify them. (Hidden field to do so?)
Set up a delegate that maps :personal_email and :work_email to the Email model such that the model handles them separately.
Something else?
Currently I have emails set up like this:
class Individual < ActiveRecord::Base
attr_accessible :first_name, ...
has_many :emails
#delegate :personal_email, :to => :email, :allow_nil => true
end
class Email < ActiveRecord::Base
attr_accessible :email_address, :owner_id, :owner_klass, :position, :verified, :email_type
belongs_to :individual
# WIP Returns 'primary' email for a user
def personal_email
end
end

Resources