how to set flash error in rails app - ruby-on-rails

I'm doin some simple app,i't gonna be some kind of translation app,so i have a search option and adding translations option.I have a problem with my search.When I click on search without any word there,it's shows me a list of translations.i would like to have a flash error there,like a i have it,when i look for a word which is not in database.
my model
class Translation < ActiveRecord::Base
attr_accessible :text_english, :text_polish
validates_presence_of :text_english, :text_polish
validates :text_polish, :text_english, :uniqueness => true
def self.search(search)
if search
where("text_english LIKE ? OR text_polish LIKE ?", "%#{search.strip}%", "%#{search.strip}%")
else
scooped
end
end
end
in my controller
# GET /translations/search
def search
#translations = Translation.search(params[:search])
if !#translations.empty?
respond_to do |format|
format.html
end
else
flash[:error] = "NO TRANSLATIONS"
redirect_to new_translation_url
end
end
end
and my search form in index
<h1>Words</h1>
<%= form_tag search_translations_path, method: :get do %>
<div class="field">
<%= text_field_tag :search, params[:search] %>
<button type="submit" class="btn">Search</button>
<% end %>
thanx for any help.

You have several options, one that is fairly simple would be to add a couple of lines to your controller to account for no params[:search]
# GET /translations/search
def search
if params[:search].blank?
flash[:error] = "A WORD TO TRANSLATE IS REQUIRED"
redirect_to new_translation_url
else
#translations = Translation.search(params[:search])
if !#translations.empty?
respond_to do |format|
format.html
end
else
flash[:error] = "NO TRANSLATIONS"
redirect_to new_translation_url
end
end
end
The important thing to remember is to not allow any path in your code that could redirect or render twice. Hence the if params[:search].blank? .. else

Related

Rails - Send associated form only if condition is met

 I have two models: one for contacts ("Contatos") and one for users ("Usuarios"). Contatos has_one Usuario , as follows:
class Contato < ApplicationRecord
has_one :usuario, dependent: :destroy
accepts_nested_attributes_for :usuario,
allow_destroy: true
And
class Usuario < ApplicationRecord
has_secure_password
belongs_to :contato
validates_presence_of :login, :password
validates_uniqueness_of :login
end
 I want to use one form for creating and editing both models. The _form partial that I currently have is this:
<%= form_with(model: contato, local: true) do |contato_form| %>
<%= if contato.errors.any?
showferr contato
end %>
#Here are the inputs for contato, I cut them out so it wouldn't be too long to read.
 Bellow (same file as above) there is a check box for the Contato model that I left on, it sets a Boolean in the model(and DB) telling if the contact has a user on not, additionally I use some JavaScript (Coffee) to toggle the whole user (Usuario) form part based on the checkboxe's value .
<div class="form-group">
<%= contato_form.label :possui_usuario, :class => 'inline-checkbox' do %>
Possui usuário
<%= contato_form.check_box :possui_usuario, {id: "hasUser", checked: #contato.possui_usuario} %>
<% end %>
</div>
</div>
<div id="userPart" class="findMe" <% unless #contato.possui_usuario %> style="display:none;" <% end %> >
<h2> Usuário: </h2>
<div class="container">
<%= contato_form.fields_for :usuario, #contato.usuario do |usuario_form| %>
<%= render partial: 'usuarios/campos_usuario', locals: {form: usuario_form, object: #contato} %>
<% end %>
</div>
</div>
<br/>
<div class="container-fluid text-right">
<%= contato_form.submit 'Confirmar', :class => 'btn-lg btn-success' %>
</div>
<% end %>
 The partial form for the Usuario model is rendering ok, but what I want to do is to only create and/or validate the user part if the checkbox is selected (if I say that the contact does have a user).
 Here's what I attempted last (there were many attempts):
At Contato model:
attr_accessor(:has_user)
#has_user = 0
before_validation do |record|
#has_user = record.possui_usuario
end
def self.user?
#has_user == 1
end
validates_presence_of :nome
validates_length_of :nome, in: 1..45
validates_presence_of :email
validates_format_of :email, with: email_regex
validates_associated :usuario, if: user?
Controller for Contato:
class ContatosController < ApplicationController
before_action :set_contato, only: [:show, :edit, :update, :destroy]
# GET /contatos
# GET /contatos.json
def index
#contatos = Contato.all
#page_title = 'Contatos'
end
# GET /contatos/1
# GET /contatos/1.json
def show
#page_title = 'Ver contato: ' + #contato.nome
end
# GET /contatos/new
def new
#contato = Contato.new
#contato.build_usuario
#contato.ativo = true
#page_title = 'Novo contato'
end
# GET /contatos/1/edit
def edit
#page_title = 'Editar contato: ' + #contato.nome
unless #contato.possui_usuario
#contato.build_usuario
end
end
# POST /contatos
# POST /contatos.json
def create
#contato = Contato.new(contato_params)
respond_to do |format|
if #contato.save
flash[:notice] = 'Contato foi criado com sucesso.'
format.html {redirect_to #contato}
format.json {render :show, status: :created, location: #contato}
else
flash[:warn] = "Erro ao criar contato."
format.html {render :new}
format.json {render json: #contato.errors, status: :unprocessable_entity}
end
end
end
# PATCH/PUT /contatos/1
# PATCH/PUT /contatos/1.json
def update
respond_to do |format|
if #contato.update(contato_params)
format.html {redirect_to #contato, notice: 'Contato foi atualizado com sucesso.'}
format.json {render :show, status: :ok, location: #contato}
else
format.html {render :edit}
format.json {render json: #contato.errors, status: :unprocessable_entity}
end
end
end
# DELETE /contatos/1
# DELETE /contatos/1.json
def destroy
#contato.destroy
respond_to do |format|
format.html {redirect_to contatos_url, notice: 'Contato deletado com sucesso.'}
format.json {head :no_content}
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_contato
#contato = Contato.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def contato_params
params.require(:contato).permit(:id, :empresa_id,
:ativo, :nome,
:cargo, :celular,
:email, :nascimento,
:observacoes, :mensagem_instantanea,
:tipo_msg_inst, :possui_usuario,
usuario_attributes: [:login, :password, :permissoes, :id, :contato_id, :_destroy])
end
end
Sorry for the long question and big code blocks.
I see two holes in the data presented currently ...
First, your controller action where create is called is where you should be testing to see if you are calling to the model / activerecord.
Something like ...
def create
if #contato && #contato.usuarios # might be able to just do last half
respond_to do |format|
if #contato = #contato.create!(contato_params) # note the bang or '!'
format.html { redirect_to #contato, notice: 'contato was successfully created.' }
else
format.html { render :new }
end
end
end
end
Without seeing your controller - I am going to guess you didn't nest your controller via Rails strong_param feature properly. Note here - these two won't run, I'm not quite sure what information is needed, but I wanted you to make sure if you are nesting your models and using a single controller - you are away you need to nest your models in strong_params (google search nested rails strong_params for thousands of help / hits).
params.require(:contato).permit(:login, :password, usuario: [id, ...] )
If that's not it - also tell us if all the functionality of create/read/update/destroy works normally & you are just looking to limit it to create in certain circumstances?
Update - based on the controller - just move your check for create from the model & move it to the controller at the start of the #create action ... maybe start with ...
def create
# Note - here you will have to inspect contato_params to find syntax
if contato_params[:usuario_attributes][:contato_id]
... rest of action wrapped in here ...
end
end
... once again ... you will need to work out exact syntax - but just like you did with the edit - this spot is where you control the creation - not in the model.
More specifically I see this #contato.possui_usuario in the form ... that's probably the variable you want to check against in your controller, but perhaps my suggestion is more important - I can't tell you that with certainty - I'm also not sure you need the has_user trick per say in model & might be tempted to do a controller version in the private method section ...
class ContatosController
private
def has_user?
... whatever ...
end
Clarification from comment:
If I move the control over the user form part to the controller (which
makes a lot of sense) how would I about canceling the
validates_associated part of the model in case the user decides that
this contact wont have any users?
You don't move the form control (defined as variable in the form), you move the model method that deals with the form control to the controller - then you can wrap it all in a transaction to rollback any other changes OR if you build your activerecord out with #build it will do it for you.

In Rails How to display errors in my comment form after I submit it?

I have a very straight-forward task to fulfil --- just to be able to write comments under posts and if the comments fail validation display error messages on the page.
My comment model uses a gem called Acts_as_commentable_with_threading, which creates a comment model after I installed.
On my post page, the logic goes like this:
Posts#show => display post and a form to enter comments => after the comment is entered, redisplay the Post#show page which has the new comment if it passes validation, otherwise display the error messages above the form.
However with my current code I can't display error messages if the comment validation fails. I think it is because when I redisplay the page it builds a new comment so the old one was erased. But I don't know how to make it work.
My codes are like this:
Comment.rb:
class Comment < ActiveRecord::Base
include Humanizer
require_human_on :create
acts_as_nested_set :scope => [:commentable_id, :commentable_type]
validates :body, :presence => true
validates :first_name, :presence => true
validates :last_name, :presence => true
# NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments.
#acts_as_votable
belongs_to :commentable, :polymorphic => true
# NOTE: Comments belong to a user
belongs_to :user
# Helper class method that allows you to build a comment
# by passing a commentable object, a user (could be nil), and comment text
# example in readme
def self.build_from(obj, user_id, comment, first_name, last_name)
new \
:commentable => obj,
:body => comment,
:user_id => user_id,
:first_name => first_name,
:last_name => last_name
end
end
PostController.rb:
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def show
#post = Post.friendly.find(params[:id])
#new_comment = Comment.build_from(#post, nil, "", "", "")
end
end
CommentsController:
class CommentsController < ApplicationController
def create
#comment = build_comment(comment_params)
respond_to do |format|
if #comment.save
make_child_comment
format.html
format.json { redirect_to(:back, :notice => 'Comment was successfully added.')}
else
format.html
format.json { redirect_to(:back, :flash => {:error => #comment.errors}) }
end
end
end
private
def comment_params
params.require(:comment).permit(:user, :first_name, :last_name, :body, :commentable_id, :commentable_type, :comment_id,
:humanizer_answer, :humanizer_question_id)
end
def commentable_type
comment_params[:commentable_type]
end
def commentable_id
comment_params[:commentable_id]
end
def comment_id
comment_params[:comment_id]
end
def body
comment_params[:body]
end
def make_child_comment
return "" if comment_id.blank?
parent_comment = Comment.find comment_id
#comment.move_to_child_of(parent_comment)
end
def build_comment(comment_params)
if current_user.nil?
user_id = nil
first_name = comment_params[:first_name]
last_name = comment_params[:last_name]
else
user_id = current_user.id
first_name = current_user.first_name
last_name = current_user.last_name
end
commentable = commentable_type.constantize.find(commentable_id)
Comment.build_from(commentable, user_id, comment_params[:body],
first_name, last_name)
end
end
comments/form: (this is on the Posts#show page)
<%= form_for #new_comment do |f| %>
<% if #new_comment.errors.any? %>
<div id="errors">
<h2><%= pluralize(#new_comment.errors.count, "error") %> encountered, please check your input.</h2>
<ul>
<% #new_comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>
I would instead use nested routes to create a more restful and less tangled setup:
concerns :commentable do
resources :comments, only: [:create]
end
resources :posts, concerns: :commentable
This will give you a route POST /posts/1/comments to create a comment.
In your controller the first thing you want to do is figure out what the parent of the comment is:
class CommentsController < ApplicationController
before_action :set_commentable
private
def set_commentable
if params[:post_id]
#commentable = Post.find(params[:post_id])
end
end
end
This means that we no longer need to pass the commentable as form parameters. Its also eliminates this unsafe construct:
commentable = commentable_type.constantize.find(commentable_id)
Where a malicous user could potentially pass any class name as commentable_type and you would let them find it in the DB... Never trust user input to the point where you use it to execute any kind of code!
With that we can start building our create action:
class CommentsController < ApplicationController
before_action :set_commentable
def create
#comment = #commentable.comments.new(comment_params) do |comment|
if current_user
comment.user = current_user
comment.first_name = current_user.first_name
comment.last_name = current_user.last_name
end
end
if #comment.save
respond_to do |format|
format.json { head :created, location: #comment }
format.html { redirect_to #commentable, success: 'Comment created' }
end
else
respond_to do |format|
format.html { render :new }
format.json { render json: #comment.errors, status: 422 }
end
end
end
private
# ...
def comment_params
params.require(:comment).permit(:first_name, :last_name, :body, :humanizer_answer, :humanizer_question_id)
end
end
In Rails when the user submits a form you do not redirect the user back to the form - instead you re-render the form and send it as a response.
While you could have your CommentsController render the show view of whatever the commentable is it will be quite brittle and may not even provide a good user experience since the user will see the top of the post they where commenting. Instead we would render app/views/comments/new.html.erb which should just contain the form.
Also pay attention to how we are responding. You should generally avoid using redirect_to :back since it relies on the client sending the HTTP_REFERRER header with the request. Many clients do not send this!
Instead use redirect_to #commentable or whatever resource you are creating.
In your original code you have totally mixed up JSON and HTML responses.
When responding with JSON you do not redirect or send flash messages.
If a JSON POST request is successful you would either:
Respond with HTTP 201 - CREATED and a location header which contains the url to the newly created resource. This is preferred when using SPA's like Ember or Angular.
Respond with HTTP 200 - OK and the resource as JSON in the response body. This is often done in legacy API's.
If it fails do to validations you should respond with 422 - Unprocessable Entity - usually the errors are rendered as JSON in the response body as well.
Added.
You can scrap your Comment.build_from method as well which does you no good at all and is very idiosyncratic Ruby.
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def show
#post = Post.friendly.find(params[:id])
#new_comment = #post.comments.new
end
end
Don't use line contiuation (\) syntax like that - use parens.
Don't:
new \
:commentable => obj,
:body => comment,
:user_id => user_id,
:first_name => first_name,
:last_name => last_name
Do:
new(
foo: a,
bar: b
)
Added 2
When using form_for with nested resources you pass it like this:
<%= form_for([commentable, comment]) do |f| %>
<% end %>
This will create the correct url for the action attribute and bind the form to the comment object. This uses locals to make it resuable so you would render the partial like so:
I'm assuming your form_for submits a POST request which triggers the HTML format in CommentsController#create:
def create
#comment = build_comment(comment_params)
respond_to do |format|
if #comment.save
make_child_comment
format.html
format.json { redirect_to(:back, :notice => 'Comment was successfully added.')}
else
format.html
format.json { redirect_to(:back, :flash => {:error => #comment.errors}) }
end
end
end
So, if #comment.save fails, and this is an HTML request, the #create method renders create.html. I think you want to render Posts#show instead.
Keep in mind that if validations fail on an object (Either by calling save/create, or validate/valid?), the #comment object will be populated with errors. In other words calling #comment.errors returns the relevant errors if validation fails. This is how your form is able to display the errors in #new_comment.errors.
For consistency, you'll need to rename #new_comment as #comment in the posts#show action, otherwise you'll get a NoMethodError on Nil::NilClass.
TL;DR: You're not rendering your form again with your failed #comment object if creation of that comment fails. Rename to #comment in posts, and render controller: :posts, action: :show if #comment.save fails from CommentsController#create
I have figured out the answer myself with the help of others here.
The reason is that I messed up with the JSON format and html format (typical noobie error)
To be able to display the errors using the code I need to change two places ( and change #comment to #new_comment as per #Anthony's advice).
1.
routes.rb:
resources :comments, defaults: { format: 'html' } # I set it as 'json' before
2.
CommentsController.rb:
def create
#new_comment = build_comment(comment_params)
respond_to do |format|
if #new_comment.save
make_child_comment
format.html { redirect_to(:back, :notice => 'Comment was successfully added.') }
else
commentable = commentable_type.constantize.find(commentable_id)
format.html { render template: 'posts/show', locals: {:#post => commentable} }
format.json { render json: #new_comment.errors }
end
end
end

Could find Addict without an ID

I'm trying to put a new form that creates new "Addicts" in a modal in my home page.
It's a simple form with 2 inputs, that when clicking on New, a modal pops up with that form in my index page.
I can't get it to work because it keeps saying "Couldnt find Addict without an ID".
My Pages Controller
class PagesController < ApplicationController
def home
#addict = Addict.find(params[:id])
#lanzaderas = Lanzadera.all
render 'index'
end
end
My Addict Controller
class AddictsController < ApplicationController
def index
#posts = Addict.all
end
def show
#addict = Addict.find(params[:id])
end
def new
#addict = Addict.new(params[:addict])
end
def create
#addict = Addict.new(params[:addict])
if #addict.save
redirect_to posts_path, :notice => "Your Addict was saved"
else
render "new"
end
end
def edit
end
def update
end
def destroy
end
end
end
My form in my modal
<%= form_for #addict do |f| %>
<%= f.input :name %>
<%= f.input :surname %>
<%= f.input :postal %>
<%= f.submit %>
<% end %>
I know it has something to do with the variable / id not being passed correctly in my Controller, but it's an error I get lots of times and don't know why I happens.
Thanks!
In def home in your PagesController you have this code:
#addict = Addict.find(params[:id])
I suspect, that you don't have the id for 'addict' in your parameters, when you visit your home action.
Do you want to display one particular addict in your 'home' page? If not, you can remove this line.
Update:
Change this in your AddictsController:
def new
#addict = Addict.new
end
In the new action you only "prepare" a new addict object. Using the find method is not possible, since the record hasn't been created yet.
If you're using Rails 4 you also have to permit your parameters (for security reasons; more info here: Railsguides: Strong Parameters)
In your case you have to do 2 things:
First: add this at the bottom of your AddictsController:
private
def addict_params
params.require(:addict).permit(:name, :surname, :postal)
end
Second: use this method in your create action instead of params[:addict]:
def create
#addict = Addict.new(addict_params)
if #addict.save
redirect_to posts_path, :notice => "Your Addict was saved"
else
render "new"
end
end

No method error issue rails

I'm a new rails developer who has a basic scaffolded crud application that I modified a bit.
I'm getting this error:
undefined method description for #<ActiveRecord::Relation:0x00000102df26d8>
when I visit john/recipes/46. Here's my view:
<h1 itemprop="name"><%= #recipe.name %></h1>
<ul>
<li><%= link_to 'Edit', edit_recipe_path(#recipe) %></li>
</ul>
<p itemprop="description"><%= #recipe.description %></p>
here's my routes:
match "/:username" => "recipes#index"
scope ':username' do
resources :recipes
end
here's my show index:
def show
#user = User.find_by_username params[:username]
#recipe = Recipe.where(:user_recipe_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #recipe }
end
end
and my model:
before_save :set_next_user_recipe_id
belongs_to :users
validates :user_recipe_id, :uniqueness => {:scope => :user_id}
def to_param
self.user_recipe_id.to_s
end
def set_next_user_recipe_id
self.user_recipe_id ||= get_new_user_recipe_id
end
def get_new_user_recipe_id
user = self.user
max = user.recipes.maximum('user_recipe_id') || 0
max + 1
end
attr_accessible :description, :duration, :author, :url, :name, :yield, :ingredients_attributes, :user_recipe_id, :directions_attributes, :tag_list, :image
The reason I'm doing a Recipe.where(:user_recipe_id => params[:id]) instead of Recipe.where(:id => params[:id]) is because I'm trying to get so instead of john/recipes/46 showing the 46th recipe in the database, instead to show the 46th recipe that belongs to John.
Thanks for all help!
You're only trying to look for one recipe, but your query is searching for multiples. When you use a plain where(...) without ending it with .first, Rails interprets it as "show me all (multiple) Recipes with this user id" instead of "show me the (one) recipe with this id".
So you need to either put .first at the end of your query:
#recipe = Recipe.where(:user_recipe_id => params[:id]).first
or use an ActiveRecord finder that only returns one record:
#recipe = Recipe.find_by_user_recipe_id(params[:id])

Rails says class is nil despite console showing database

I have a database of objects (tools) in my Ruby on Rails project. When I use "rails dbconsole" and
select * from tools;
it returns a whole list of tool objects. But when I try to view the following page, it gives me an error.
Page:
<!DOCTYPE html>
<html lang="en">
<%= stylesheet_link_tag "tools", :media => "all" %>
<body>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
</body>
</html>
Error:
undefined method `each' for nil:NilClass
When I change the code to add an if statement against nil objects, the page works (without displaying any tools).
<% if #tools.nil? %>
<% else %>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
<% end %>
So it seems like #tools doesn't have any values in it, but when I look at it in the dbconsole, there are tools there. I can't figure this out, and I've spent the past few days googling for answers, so any and all ideas would be welcome!
EDIT: Added tools_controller.rb
class ToolsController < ApplicationController
before_filter :check_authentication
def check_authentication
unless session[:user_id]
session[:intended_action] = action_name
session[:intended_controller] = controller_name
redirect_to new_session_url
end
end
def new
#tool = Tool.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #tool }
end
end
def show
end
def index
#tools = Tool.all
end
# GET /tools/1/edit
def edit
#tool = Tool.find(params[:id])
end
# POST /tools
# POST /tools.json
def create
#tool = Tool.new(params[:tool])
respond_to do |format|
if #tool.save
format.html { redirect_to #tool, :notice => 'tool was successfully created.' }
format.json { render :json => #tool, :status => :created, :location => #tool }
else
format.html { render :action => "new" }
format.json { render :json => #tool.errors, :status => :unprocessable_entity }
end
end
end
end
Loading #tools in a before_filter for every action as #nbarraille has suggested is a bad idea, because there are many (probably most) actions where you will definitely not need the full set of tools (e.g. create and destroy). The line #tools = Tool.all hits your database so you should minimize the number of times you use it.
For the case you have here, you only need to change your show action to get this to work:
def show
#tools = Tool.all
end
However, note that normally the show action is for displaying a single resource (tool), not the whole list of resources (which is normally done in the index action). It looks like you're deviating from the normal way of doing things, is there any particular reason why?
In order for the #tools variable to be accessible from your view, you need to declare it in your controller, like this:
#tools = Tool.all
If you want it to be only accessible from one page, just declare it in the according method.
Here is an example, assuming you want to make the variable available for your home/index page:
class HomeController < ApplicationController
def index
#tools = Tool.all
respond_to do |format|
format.html # index.html.erb
end
end
end
If you want it to be accessible in all your pages, you can declare it in the before_filter method of your ApplicationController.
Here is how to do this:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :load_variables
# Load variables to be used everywhere
def load_variables
#tools = Tool.all
end
end

Resources