EDIT: SOLVED, view the solution in Brian Kunzig's answer+comments.
I've decided to try out Ruby on Rails and have been constantly running into this problem. I was searching around for problems like this but none of the solutions have done the trick for me. It seems that I constantly write a part of code that just isn't correct. So here are some code snippets:
(ignore what the code would be for, it's just to try stuff out)
My controller:
class AuthsController < ApplicationController
def index
#auths=Auth.all
end
def new
#auth=Auth.new
end
def show
#auth=Auth.find(params[:id])
end
end
My index.html.erb:
<div style="margin: 0 auto; width:50%; text-align: center">
<h1>Please authorise your use of this webpage and its database(s).</h1>
<%= form_for :auth, url: auths_path do |f| %>
<% if #auth.errors.any? %> ==> RAILS REPORTS ERROR IN CODE HERE
<div id="error_explanation">
<h2>
<%= pluralize(#auth.errors.count, "error") %> prohibited
this authentification from being saved:
</h2>
<ul>
<% #auth.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :username %><br>
<%= f.text_field :username %>
</p>
<p>
<%= f.label :password %><br>
<%= f.text_field :password %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
</div>
So, bottom line. Rails is saying that I cannot use #auth in the form_for block, for example. Or anywhere else for that matter.. It always says that it belongs to NilClass or something like that. It obviously wants me to instantiate it somehow, but isn't it enough to make the method new and put in the line: #auth=Auth.new ?
I'm just confused with this situation because I can't figure out how it's supposed to go. Thanks a lot !
P.S. I'm using <%= form_for :auth, url: auths_path do |f| %> because it won't accept #auth, that's what the error in the next line is. I have seen solutions to instantiate it "on the go" outside of the controller but I want to do it the way it's supposed to be done.
You should be putting this form in the new.html.erb file and not the index. The index is for listing entries while the new and create actions handle POST requests. You're getting an error because you're trying to list all Auth's when none have been created. Also, you're sending a form via a GET request if you're using standard rails routing. Use resourceful routing and put this form in your new view for that controller and it should work.
Routes file should be:
resources :auths
This will provide all the necessary routing automagically. If you type rake routes after modifying this you will see the newly generated urls and the helpers to them. You will notice it affords the create and update actions a POST/PUT request while others are GET.
Related
I'm new to RoR. Wanted to try if my next web app should be RoR based. Started out following this trail: https://guides.rubyonrails.org/getting_started.html. Worked like a charm in the beginning, but i'm unable to get the darn thing to create new records. Any hint as to what i'm missing is appreciated.
The error i'm getting is this:
D, [2020-12-18T09:59:56.917197 #132399] DEBUG -- : Createevent
F, [2020-12-18T09:59:56.917893 #132399] FATAL -- :
ActionController::ParameterMissing (param is missing or the value is empty: Event):
app/controllers/event_controller.rb:33:in `event_params'
My routing looks like this:
Rails.application.routes.draw do
post 'event/new', to: 'event#create'
resources :event
end
(I'm baffled by the need for specifying the POST above, but without it the create is never fired. ).
The eventcontroller looks like this:
class EventController < ApplicationController
def index
#events = Event.all
end
def show
#event = Event.find(params[:id])
end
def new
logger = Rails.logger
logger.info 'NewEvent'
#event = Event.new
end
def create
logger = Rails.logger
logger.debug 'Createevent'
#event = Event.new(event_params)
logger.debug 'Eventcreated'
if #event.save
redirect_to event_path
else
render :new
end
end
private
def event_params
params.require(:Event).permit(:EventName, :Description, :EventStart, :EventEnd, :Maxparticipants, :Waitlist )
end
end
Index and show works fine.
The new.html.erb looks like this:
<h1>New Event</h1>
dsfsdfds
<%= form_with model: #Event do |form| %>
<div>
<%= form.label :eventname %><br>
<%= form.text_field :EventName %>
<%= #event.errors.full_messages_for(:EventName).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Description %><br>
<%= form.text_field :Description %>
<%= #event.errors.full_messages_for(:description).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :EventStart %><br>
<%= form.text_field :EventStart %>
<%= #event.errors.full_messages_for(:eventstart).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Eventend %><br>
<%= form.text_field :Eventend %>
<%= #event.errors.full_messages_for(:eventend).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Maxparticipants %><br>
<%= form.text_field :Maxparticipants %>
<%= #event.errors.full_messages_for(:Maxparticipants).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Waitlist %><br>
<%= form.text_field :Waitlist %>
<%= #event.errors.full_messages_for(:waitlist).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<%= submit_tag "Create" %>
<% end %>
Routes:
Prefix Verb URI Pattern Controller#Action
event_new POST /event/new(.:format) event#create
event_index GET /event(.:format) event#index
POST /event(.:format) event#create
new_event GET /event/new(.:format) event#new
edit_event GET /event/:id/edit(.:format) event#edit
event GET /event/:id(.:format) event#show
PATCH /event/:id(.:format) event#update
PUT /event/:id(.:format) event#update
DELETE /event/:id(.:format) event#destroy
Things created using:
bin/rails generate model Event EventName:string Description:string EventStart:datetime EventEnd:datetime Maxparticipants:integer WaitList:integer
bin/rails generate controller Event index
Version:
About your application's environment
Rails version 6.0.3.4
Ruby version ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
RubyGems version 3.1.4
Rack version 2.2.3
Thanks to Hackman & nathanvda for clarifying. I scratched everything and started over and i finally got it working. Still way too much woodo and black magic for my taste though. The error message part got me baffled for three consecutive hours.
As stated i followed the guide and therefore ended up using (in new)
<%= form_with model: #event do |form| %>
<% if #event.errors.any? %>
<h2>Errors</h2>
<ul>
<% #event.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
...
Which works (well, sort of). The record gets saved if ok, the validations rules gets fired
class Event < ApplicationRecord
validates :eventname, presence: true
validates :description, presence: true, length: { minimum: 20 }
end
and if violated no record gets written to the database, but no error messages either. Nil. Nothing. After poking around on the internet i ended up changing to
<%= form_for #event do |form| %>
and then error reporting works. Only problem with this solution is that the use of form_for is discouraged as it is being rendered obsolete.
Final version ended up being:
<%= form_with model: #event, local: true do |form| %>
Which does the trick.
Next step in my evaluation will be the use of natural keys as the use of surrogate keys is not an option for some of the data structures needed in this project. (during my poking around i got the impression that natural keys are some kind of a sore tooth in RoR, but time will show.
To start out, your resources in the routes should be pluralized. So resources :event should be resources :events
Also the controller name should be pluralized. So EventController would become EventsController.
Now the needed routes should work fine and you can get rid of the specified POST in your routes.rb
Now inside your controller you have the event_params method. There it is preferred to downcase/snake_case the names like this:
def event_params
params.require(:event).permit(:event_name #etc)
end
If your column names in DB are EventName etc, I would advice to rename them.
Last thing: In your form you got #Event with uppercase while in the controller#new action you defined #event with lowercase. Use lowercase everywhere.
So if you had started as follows:
bin/rails generate model Event event_name:string description:string event\-start:datetime event_end:datetime max_participants:integer wait_list:integer
bin/rails generate controller events index
Then the generated code would work a lot better.
A few tips to clarify:
in ruby we only write classes with a capital, for variables we use snake case (everything lowercase and words connected with underscores). So by extension when generating a model all attributes should be snake cased
a controller in general uses the plural form, since it "controls" all the events (not just one).
I am building a basic bare bones social media app right now.
I have a user class and a status class.
For each status, there is a "creater" (a user object) and a "subject" (a user object that the status is about). I was able to create tags by using the acts_as_taggable_on gem. What ends up happening is when a user goes to create a post, he/she can select another user from a dropdown menu. The chosen user's id attribute is then stored.
Now I am trying to link to the chosen User's profile. This is my code for show statuses on a profile page.
<% if #statuses %>
<% #statuses.each do |status| %>
<div class="well">
<%= status.content %>
<br></br>
#link to user who's associated with the tagId
<%= link_to User.find(status.tag_list).profile_name, user_profile_path(User.find(status.tag_list).profile_name) %>
<hr />
<%= link_to time_ago_in_words(status.created_at), status_path(status) %> ago
</div>
<% end %>
<% end%>
this is the line where the above code breaks
<%= link_to User.find(status.tag_list).profile_name, user_profile_path(User.find(status.tag_list).profile_name) %>
Can anyone help me out with this?
Not surprised this line is failing:
<%= link_to User.find(status.tag_list).profile_name, user_profile_path(User.find(status.tag_list).profile_name) %>
A couple points:
It's a little cleaner to separate it onto multiple lines
I suspect your problem is because you're passing a profile_name to user_profile_path instead of an id, though I can't be certain without seeing your routes.
Try the following:
<% profile_user = User.find(status.tag_list) %>
<%= link_to profile_user.profile_name, user_profile_path(profile_user.id) %>
I created a sample webpage in rubyonrails which has two textbox and a button . When i enter some data in the text box and click the button no error appears . But the data is not stored in the data base . What is the mistake that i committed .
login.html.erb file :
<%= form_for #product, url:{action: "login"} do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
routes.rb file:
Sample::Application.routes.draw do
get "sample/login"
root 'sample#login'
post 'sample/:id' => 'sample#login'
end
sample controller file :
class SampleController < ApplicationController
def login
#product=Logz.new
end
end
and the model class name Logz contains the necessary field names
username and password . If there was any error i could manage . but it shows no errors.
I had the same problem once. I dont know how its happened . but i just changed the content in database.yml
localhost:yourdb_development
into
127.0.0.1:ypurdb_development
and i got it working.
You are sending the data to method login, but it just instantiate a new Product and it aren't receiving any attributes... and even if received... it are not saving the Product at all, so does not persist the data.
Try add a method create... that will be responsible for receive the data and save
class SampleController < ApplicationController
def create
#product = Logz.new(params[:product])
if #product.save
format.html { redirect_to 'sample#login', notice: 'Data saved successfully' }
else
flash[:notice] = 'A wild error appeared'
end
end
After that, create the route to post 'sample/create' and change the action to where your form send the data... form_for #product, action: "create", method: 'post'
Doing that... i will be possible to persist the data on your database...
PS:
You can use the content of that method inside your login method... but I dont recommend that... it is ugly and does not follos the conventions of rails.
I even recommend you to do a refactory... because it doesn't make sense access a SampleController in order to create a Product... that is persisted in an object called Logz...
The best practice is all follow the same name... LogzController, #logz, and finally your model Logz. and preferably your routes following the same pattern...
Another thing is, it would be nice to change your method login to a method call 'new' because that method you use to fill a new Logz... not to login...
ok ...
<%= form_for :article, url: articles_path do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
Ok, code above is from http://edgeguides.rubyonrails.org/getting_started.html
Question pertains to this part:
<%= form_for :article, url: articles_path do |f| %>
<% if #article.errors.any? %>
If I change :article to #article in first line, it still works. And it should, right because they're interchangeable, yes?
But if I change the second one like this:
#article.errors.any? to :article.errors.any?
it generates errors, specifically this:
undefined method 'errors' for :article:Symbol
Why??
Please do be gentle as I'm still trying to figure what may seems obvious to you.
Thinking out loud here, the reason why symbol doesn't work for second line is that it needs an instance of it to collect the errors, right?
But then, aren't symbols interchangeable with instances? Meaning that Rails should read that line and translate it into an instance, ie, it finds the instance of the model class, yes?
EDIT
apparently, I didn't read far enough again, and I didn't take to heart what I learned previously of form_for helper ...
EdgeGuides says this after the code:
The first parameter of the form_tag can be an object, say, #article which would cause the helper to fill in the form with the fields of the object. Passing in a symbol (:article) with the same name as the instance variable (#article) also automagically leads to the same behavior. This is what is happening here. More details can be found in form_for documentation.
The key is form_for helper.
That's why it doesn't work for the second line ... because it cannot figure out how to fill in the place of the symbol.
Hopes this helps some other poor guys/gals.
ps. feel free to correct me if my edit is wrong.
:article is only the model (not really sure :)
#article is an instance with data in it
when Article.new empty, Article.find(..) with data
In my applications I use always
<%= form_for #article do |f| %>
then I am sure that the form and the f variable reflects the instance #article
and all fields will be filled as expected.
In more complicated routes I use the url parameter as well.
<%= form_for :article, :url => articles_path do |f| %>
But it's only more clear if it goes in direction nested routes.
Although form_for :article and form_for #article both work, but that only means that the form_for method can gather the needed information from either #article or :article. It doesn't mean symbols and instance is interchangeable.
Rails 3.0 deprecated f.error_messages and now requires a plugin to work correctly - I however want to learn how to display error messages the (new) native way. I am following the getting started guide, which uses the deprecated method when implementing the comments form. For example:
<h2>Add a comment:</h2>
<%= form_for([#post, #post.comments.build]) do |f| %>
<%= f.error_messages %>
<div class="field">
<% f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here is the correct way to do it (as generated by the scaffold):
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
. . .
I understand that I use the #post variable in the latter example, but what variable do I reference in the former to get the error messages for comment creation?
The best and clean way to implement error_messages in your form is by implementing error_messages in a FormBuilder.
For example, here is the error_messages method I've implemented for my last project.
By implemeting your own FormBuilder you can follow the rules and styles of your webdesigner...
Here is an example that will output the errors list in ul/li's with some custom styles :
class StandardBuilder < ActionView::Helpers::FormBuilder
def error_messages
return unless object.respond_to?(:errors) && object.errors.any?
errors_list = ""
errors_list << #template.content_tag(:span, "There are errors!", :class => "title-error")
errors_list << object.errors.full_messages.map { |message| #template.content_tag(:li, message) }.join("\n")
#template.content_tag(:ul, errors_list.html_safe, :class => "error-recap round-border")
end
end
Then in my forms :
= f.error_messages
And that's all.
I'm pretty sure all you'd need to do is reference #post.comments
So you could do something like:
<% #post.comments.each do |comment| %>
<% if comment.errors.any? %>
<% comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
<% end %>
Or just pull all the errors out out:
comment_errors = #post.comments.map(&:errors)
and then loop through them in your display logic to output each of the comment errors.
This functionality exists as a standalone gem dynamic_form.
Add the the following to your Gemfile
gem 'dynamic_form'
From the github page:
DynamicForm holds a few helpers method to help you deal with your Rails3 models, they are:
input(record, method, options = {})
form(record, options = {})
error_message_on(object, method, options={})
error_messages_for(record, options={})
It also adds f.error_messages and f.error_message_on to your form builders.
Here is my solution to the whole error scene.
I created a partial which simply uses a model variable which one would pass when rendering it:
<%# app/views/errors/_error.html.erb %>
<%= content_for :message do %>
<% if model.errors.any? %>
<ul>
<% model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<% end %>
You can easily add dynamic html class and/or id names based on the name of the model, as well as generic ones.
I have things setup where my error messages render in all the same place in a layout file:
<%# app/views/layouts/application.html.erb %>
<%= yield :message %>
If one didn't want that functionality, removing the content_for in the partial would do the trick.
Then in really any view you want you can simply write:
<%= render 'errors/error', model: #some_model %>
One could further expand this by creating a partial which takes a collection and leverages the error partial above:
<%# app/views/errors/_collection.html.erb %>
<% collection.each do |model| %>
<%= render 'errors/error', model: model %>
<% end %>
Render it with:
<%= render 'errors/collection', collection: #some_model.some_has_many_association %>
I like this way. It is simple, easy to manage/maintain, and incredibly tweakable.
I hope this helps!
EDIT: Everything in HAML
-# app/views/errors/_error.html.haml
= content_for :message do
- if model.errors.any?
%ul
- model.errors.full_messages.each do |msg|
%li= msg
-# app/views/layouts/application.html.haml
= yield :message
= render 'errors/error', model: #some_model
-# app/views/errors/_collection.html.haml
- collection.each do |model|
= render 'errors/errors', model: #some_model
= render 'errors/_collection', collection: #some_model.some_has_many_association
I guess that the [#post, #post.comments.build] array is just passed to polymorphic_path inside form_for. This generates a sub-resource path for comments (like /posts/1/comments in this case). So it looks like your first example uses comments as sub-resources to posts, right?.
So actually the controller that will be called here is the CommentsController. The reason why Lukas' solution doesn't work for you might be that you actually don't use #post.comments.build inside the controller when creating the comment (it doesn't matter that you use it in the view when calling form_for). The CommentsController#create method should look like this (more or less):
def create
#post = Post.find(params[:post_id]
#comment = #post.comments.build(params[:comment])
if(#comment.save)
# you would probably redirect to #post
else
# you would probably render post#show or wherever you have the form
end
end
Then you can use the code generated by scaffolding, only replace #post instance variable with #comment in all the lines except form_for call.
I think it may also be a good idea to add the #comment = #post.comment.build to the controller method that displays this form and use form_for([#post, #comment], ...) to keep the form contents displayed in the form if there're errors.
If this doesn't work and you're not able to figure it out, please add your CommentsController#create method to the question.
I just looked into the docrails github issues, and they've decided to remove f.error_messages instead of explaining how to do validation for comments.
a rather simple implementation can be achieved with
class ActionView::Helpers::FormBuilder
def error_message(method)
return unless object.respond_to?(:errors) && object.errors.any?
#template.content_tag(:div, object.errors.full_messages_for(:"#{method}").first, class: 'error')
end
end
which allows one to use
<%= form.label :first_name %>
<%= form.text_field :first_name %>
<%= form.error_message :first_name %>
and with the following sass
#import variables
.error
padding: $margin-s
margin-left: $size-xl
color: red
.field_with_errors
input
border: 1px red solid
input:focus
outline-color: red
it looks like
using simple form gives you quite similiar functionality with more advanced functionality.
For example check out their examples with bootstrap