How to customize action when matching item exists - ruby-on-rails

I am checking whether a book exists, and if so, render some action. I'd like this action to be a popup message or redirecting to another page.
Here's the code in my model:
def
existing_book = Book.find_by('author LIKE ? AND name LIKE ?', "#{self.author}", "#{self.name}")
if existing_book != nil
errors.add(:name, message: "This book may already exist.")
end
end
The problem is that besides errors.add to show the error text on the same screen, I can't get it to do popup message or redirect. redirect_to or such code that work in controller doesn't work in this model file. How can I do a popup message or redirect in this case?
If that is impossible, how can I at least make the error message show more than one model? Above, there's only :name, but I'd like to show both name and author. Right now the error message shows as: "Name This book may already exist."
I'd really appreciate any help.

Related

Rails: How do I "validates_presence_of" conditionally? For example when reaching a certain page?

For example I have a user registering, during registration all thats needed is a password and username. As soon as they click "shop", they must input address and city to continue.
At the moment every user must input username, password, city, and address to register. How do I split this and only require it if the user clicks "shop"?
I'm using devise.
I'd like to send the user to a page saying, "if you would like to continue, please let us know your address and city"
Just add a method to your model called can_shop? or similar checking if all required fields are given:
def can_shop?
[city, address].all?(&:present?)
end
Then you can use this either to disable the button or to create a before_action in your controller:
before_action :check_if_can_shop
private
def check_if_can_shop
return if current_user.can_shop?
redirect_to edit_user_path(current_user), notice: "if you would like to continue, please let us know your address and city"
end
Naturally we are left with an UX element - you will need to mark those fields in the form as "optional, but required to shop". Later on you could pass original_url as an extra param, which you can then use to redirect user back to when he completed all the required fields.
Another thing is you could use validation contexts and use the right context in your user_update action - that way once you redirect user back to form, he will see proper validation errors when he miss normally-optional-but-now-required fields.
You can do that by nested forms for that and do custom validations in rails.
Ex:
class User < ActiveRecord::Base
validates :my_method_name, if: :page_reached?
def page_reached?
-- do stuff for page number --
end
def my_method_name
errors.messages :base, "Cities should not be empty."if cities.empty?
end
def my_method_name
errors.messages :base, "Address should not be empty."if address.empty?
end
end

Validation message helper or retrieving validation error

P.S: I've been working with Ruby for less than 48 hours, so expect me to be dumb!
I have set up validation on a model which all works fine. However I want to modify the message retuned by the uniqueness constraint. The message needs to be returned by a method which does some additional processing.
So for example if i try to create a new user model which the "name" attribute set to "bob", and the bob named account already exists, i want to display a message like "Bob isn't available, click here to view the current bob's account".
So the helper method will lookup the current bob account, get the localised string, and do the usual string placement of the localised string, placing the name, link to account and any other information i want into the message.
Essentially i want something like:
validates_uniqueness_of :text, :message => self.uniquefound()
def uniquefound()
return "some string i can make up myself with #{self.name}'s info in it"
end
No doubt that's completely wrong...
If this isn't possible, i've found i can use users.errors.added? to detect if the name attribute has a unique error added, from there i can probably do some hash manipulation to remove the unique error, and place my own in there either in an "after_validation" callback or in the controller... haven't worked out exactly how to do that, but that's my fallback plan.
So, is there a way of providing a class method callback for the message, AND either passing the current model to that method (if its a static method) or calling it so self inside that method is the model being validated.
Update
Trawling through the rails sourcecode, i have found this private method that is called during adding an error to the error class
private
def normalize_message(attribute, message, options)
case message
when Symbol
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
when Proc
message.call
else
message
end
end
end
So if i pass a method as the message, i presume that this method is used to call my function/method and get teh return data. However it appears it's not called in the scope of the current object, nor does it pass in the object for which the error pertains...
So, if my digging is on the right path, it appears that calling a method on the current object to the message, or calling a static method passing the object, is not possible.
You could do something like this if you really need this functionality.
Class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
....
validate :unique_name
def unique_name
original_user = User.where("LOWER(name) = ?", name.downcase)
if original_user
message = "#{name} is not available "
message << ActionController::Base.helpers.link_to("Click Here",user_path(original_user))
message << "to view the current #{name}'s account."
errors.add(:name,message)
end
end
end
EDIT
with a Proc Object instead
Class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
....
validates_uniqueness_of :name, message: Proc.new{|error,attributes| User.non_unique(attributes[:value]) }
def self.non_unique(name)
original_user = User.where("LOWER(name) = ? ", name.downcase)
message = "#{name} is not available "
message << ActionController::Base.helpers.link_to("Click Here",Rails.application.routes.url_helpers.user_path(original_user))
message << "to view the current #{name}'s account."
end
end
These will both add the following error message to :name
"Name Bob is not available Click Hereto view the current Bob's account."

Devise: Show error when the user is already confirmed

I would like to show a error message when a confirmed user tries to resend confirmation. Is this something that is already provided by devise or should i have to override some of their methods? If so which methods?
I got this working by overriding create action of confimations controller
def create
self.resource = resource_class.send_confirmation_instructions(resource_params)
if successfully_sent?(resource)
flash[:notice] = "Confirmed already, Please try signing in" if resource.confirmed?
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
I am just overriding the flash notice in the case of confirmed user
The first thing you want to decide is if you really want to put a message for confirmed users. This might allow user enumeration (i.e have a robot try to find the users' email on your site... that's why there's the paranoid mode.
If you truly want to display a confirmation message, you do not need to override the controller. Your user will already have an error in the resource something like: "was already confirmed, please try signing in". You therefore don't need to modify the flash for this, you might simply want to use devise_error_messages! (or your own custom code to display error content).
Hope this helps.

Soft db error handling for duplicate entries? -- Rails 3.1 newbie

If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end

url validation in ruby on rails

1)Url field should also accept url as “www.abc.com”. If user enters url like this, it should be automatically appended with “http://” resulting in value saved in database as “http://www.abc.com”. If user enters url as “http://www.xyz.com” system should not append “http://”. User should be able to save url with “https://”. what is the code for it in ruby on rails?
This code worked:
def validate
if !self.external_url.blank? && self.external_url != "external url"
if self.external_url.match(/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix).nil?
if self.external_url.match(/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix).nil?
self.errors.add :base, 'External url is invalid' #message is changed by Hemant
else
self.external_url = "http://"+self.external_url
end
end
end
end
Not a great way to ask for assistance which is why I suspect you've been down voted twice!
I would suggest you start by looking at before_validation, validates_format_of and regular expressions. See how you get on with those and perhaps post any updates if you get stuck.
Ps:
prepend = "To attach to the beginning of data"
append = "To attach to the end of the data"

Resources