Ruby on Rails - NoMethodError in Signups#new - ruby-on-rails

I'm getting this error while trying to pass "Innovation Cloud" lesson on Codecademy. I couldn't find any solution on Stack Overlow or GitHub.
NoMethodError in Signups#new
Showing /home/ccuser/workspace/learn-rails_innovation-cloud/innovation-cloud/app/views/signups/new.html.erb where line #40 raised:
undefined method `email' for #<Signup id: nil, created_at: nil, updated_at: nil>
Important fragments of code:
routes.rb:
Rails.application.routes.draw do
get "/thanks" => "pages#thanks"
resources :signups
root "signups#new"
end
new.html.erb
<%= form_for(#signup) do |f| %>
<div class="field">
<%= f.label :signup %><br>
<%= f.text_area :email %>
</div>
<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>
db/migration
class CreateSignups < ActiveRecord::Migration
def change
create_table :signups do |t|
t.string :email
t.timestamps
end
end
end
signups_controller.rb
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
private
def signup_params
params.require(:signup).permit(:email)
end
def create
#signup = Signup.new(signup_params)
if #signup.save
redirect_to '/thanks'
else
render 'new'
end
end
end

As the error implies, some code tried to call a method email on an instance of Signup. Its not clear what line of code corresponds to the line number 40, but my guess is its the following:
<%= f.text_area :email %>
Anytime you build a form with an ActiveRecord instance (as you do with form_for(#signup)), any input generated will expect an attribute on your model with the same name. In this case, its looking for an attribute named email on #signup. It does this so that the field can be populated with the current value of that attribute on the model. While this might seem unnecessary for a #new action, it makes perfect sense for an #edit action, and allows you to reuse the same form code for both.
Since I'm guessing you'd like to store the email along with the signup, you need to add the attributes to the Signup model with a migration like so:
rails generate migration AddEmailToSignup email:string
rake db:migrate

Related

Getting my Ruby on Rails page to save the user's email without using devise

I am building a one page website where visitors will simply be able to submit their email address. The only goal in the database is to get an email (no name, etc). There is only one page visible at first, which is the homepage. If the user submits an email already in use, it sends the user to an error page. If the email is not in use, it sends the user to a success page.
I have asked a question about this previously, and after a lot of comments and trial and error, it appeared that it worked and then it stopped working. When I do Rails C, there is only one user in the system and that user doesnt have an email...
Here is what my user migration looks like :
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email
t.timestamps
end
add_index :users, :email, unique: true
end
end
Here is what my user model looks like:
class User < ApplicationRecord
end
Here is what users/new.html.erb looks like:
<%= form_for #user, as: :post, url: users_path do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
<% end %>
Here is my user controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:email])
if #user.save
redirect_to '/users/success'
else
redirect_to '/users/error'
end
end
def show
end
end
Here are my routes:
Rails.application.routes.draw do
root "users#new"
resources :users
end
When i run the code, it renders the homepage but when i click on submit, it sends me on a page called show.html.erb with http://localhost:3000/users/error on my brownser. No users are being saved in the console.
EDIT:
My model is
class User < ApplicationRecord
validates_uniqueness_of :email
end
It is still not working....
change new.html.erb as
<%= form_with(model: #user, local: true) do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
your controller will be
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to user_path(#user), notice: "yeh!!!!"
else
redirect_to new_user_path, notice: "email already registered"
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email)
end
end
add
<p id="notice"><%= notice %></p> to your application.html.erb in layouts
rest as your question
There are a couple things wrong here.
You're so close, but you're misusing the as: attribute of form_for. Perhaps you think that will send as a POST request, but instead that is actually wrapping your form params in an object called "post". I saw this in the comments on another thread.
Remove the as: attribute and the helper will again wrap your params in the user object. While we're at it, you should also be able to remove the url: attribute as well since Rails form helpers are smart enough to infer that this is a new resourceful record and output the create URL as well as the POST action accordingly.
You need your controller to expect a whole "user" object instead of just checking for the email param. ALSO, assuming you're on Rails 4 or higher, you need to permit the email attribute to be mass-assigned on your User object. See the code.
def create
#user = User.new(params.require(:user).permit(:email)) # Not params[:email]
if #user.save
redirect_to '/users/success'
else
redirect_to '/users/error'
end
end
Also be careful about duplicate emails with different cases. The default in Rails is case-sensitive validation which means "JIM#gmail.com" would not trigger a validation error against "jim#gmail.com". You can fix this with.
class User < ApplicationRecord
validates :email, uniqueness: { case_sensitive: false }
end
BONUS!
Nowadays, it's better to move over to form_with (instead of form_for). It's on its way to becoming the new Rails standard and also makes a few of these things easier. The one point you'll want to keep in mind is that with form_with (and general Rails assumptions), forms are remote by default. So if you want to trigger a full page submit/refresh, add local: true to your form_with helper.
<%= form_with model: #user, local: true do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
<% end %>
As you are using resources in routes so def show is called when route is /users/:id. That's why its calling show.html.erb file.
When you try to validate an email, then in model write the validation for it
class User < ApplicationRecord
validates_uniqueness_of :email
end
Hope this helps.
Try to add validates_uniqueness_of in your model
class User < ApplicationRecord::Base
attr_accessor :email
validates_uniqueness_of :email
end
And
def show
#user = User.find(email: params[:email])
end
And if you wanna check all
def show
#user = User.all
end
Please try this.
I hope that helpful

Cannot create new object in rails

I am trying to create a basic Item creation in rails but I am having trouble creating new item. I want to create item's name, say Wash the dishes. These are the codes that I have:
Routes:
resources :items
ItemsController:
class ItemsController < ApplicationController
...
def new
#item = Item.new
end
def create
#item = Item.new(item_params)
if #item.save
flash[:notice] = "Item was saved!"
redirect_to #item
else
flash.now[:alert] = "ERROR. ERROR."
render :new
end
end
...
private
def item_params
params.require(:item).permit(:name, :list_id)
end
end
items/new.html.erb
<%= form_for :item do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Lastly, schema:
create_table "items", force: :cascade do |t|
t.string "name"
t.integer "list_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
...
I got several different error codes, but this is the one I am currently stuck at (other times it showed different error code or it would simply prints out "ERROR. ERROR." (alert that I setup when save fails)
Routing Error
No route matches [POST] "/items/new"
When I go to rake routes:
POST /items(.:format) items#create
new_item GET /items/new(.:format) items#new
I followed suggestion from this SO post to check my routes and this is what I have:
2.2.2 :019 > r = Rails.application.routes
=> #<ActionDispatch::Routing::RouteSet:0x007fff323c3230>
2.2.2 :020 > r.recognize_path "/items/new"
=> {:controller=>"items", :action=>"new"}
I have also gone to rails c and I was able to create new item manually. (i = Item.new(name:"Test 123"); i.save)
What did I miss?
The problem is with your form. To understand what's wrong, do the following:
Start the rails server using rails s
Go to http://localhost:3000/items/new
Instead of filling in the form fields, view the source page
Check the form tag. Its submitting the form data to /items/new. ie.the action attribute is set to /items/new. Why is that?
From the documentation:
When the model is represented by a string or symbol, if the :url option is not specified, by default the form will be sent back to the current url (We will describe below an alternative resource-oriented usage of form_for in which the URL does not need to be specified explicitly).
<form action="/items/new" accept-charset="UTF-8" method="post">
In your routes.rb, there's no route matching POST /items/new
So, modify your form to
<%= form_for :item, url: items_path do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
This generates a form tag which posts the data to /itemsrather than /items/new.
Or replace your form with
<%= form_for #item do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Now, the form will be submitted to /items. The advantage of using the second version is you can dry out your form for creating a new object and updating an existing object into a single view(partial).
For more, see http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object
Try this in items/new.html.erb
<%= form_for #item do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>

Undefined method 'to_key' error using form_for

I am getting the following error when trying to use form_for in my Rails application:
undefined method `to_key' for #<Table::ActiveRecord_Relation:0x8a09ca8>
My config/routes.rb is:
root 'welcome#index'
post 'foo', as: 'foo', to: 'welcome#index'
The controller is:
class WelcomeController < ApplicationController
def index
#tables = Table.all
end
def test
#tables = Table.all
end
end
And the welcome/index.html.erb view is:
<p>
<%= form_for #tables, :url => foo_path do |t| %>
<%= t.text_area :name %>
<% end %>
</p>
I've tried to do the url workaround that had been suggested in the documentation, but I'm still getting the same error.
Does anyone know what I am doing wrong? I would like to understand this bug a bit more so I can better deal with it.
As per your code, index is returning a collection. However your view tries to define a form for it. This is unlikely going to be succeed.
Form is for an object, not for collections.
Perhaps you can do something like
def new
#table = Table.new
end
and in new.html.erb
<%= form_for #table do |f| %>
...
<% end %>
And if you would like to stick with index.html.erb with a form. Then you have to edit your routes for index action and also in controller it should be for creating a new object.
def index
#table = Table.new
end
Hope it helps!
I see your code have 3 not true things
As RESFUL standard then:
index action always go through with get action so in route file you should define again same that:
root "wellcome#index"
get "foo", to: "wellcome#index", as: :foo
form_for usually use with model object but not collect as you use #tables, if model object not save into database form_for using to create 1 object to database, otherwise form_for using update that object
if you want create form at index action you can follow me:
def index
#tables = Table.all
#table = Table.new
end
index.html.erb file
<%= form_for #table do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
you need create tables_controller to process request from form send to server. you run: rails g controller tables
In table_controller.rb you write same as:
def create
#table = Table.new table_params
if #table.save
redirect_to root_path, notice: "success"
else
redirect_to root_path, alert: "fail"
end
end
private
def table_params
params.require(:table).permit :name
end
so that. end. Have nice day!

NoMethodError in StaticPages#manager

I'm rails noob. I have a problem with contact form.
I have an error: undefined method 'name' for nil:NilClass
<p>
<strong>Name:</strong>
<%= #contact_forms.name %>
</p>
My manager.html.erb is:
<div>
<p>
<strong>Name:</strong>
<%= #contact_forms.name %>
</p>
<p>
<strong>Text:</strong>
<%= #contact_forms.text %>
</p>
</div>
My contact_form_controller.rb is:
class ContactFormController < ApplicationController
def new
end
def create
#contact_forms = Contact_form.new(params[:contact_form])
#contact_forms.save
redirect_to root_path
end
def show
#contact_forms = Contact_form.all
end
end
My file with migrates:
class CreateContactForms < ActiveRecord::Migration
def change
create_table :contact_forms do |t|
t.string :name
t.string :phone
t.string :email
t.text :text
t.timestamps null: false
end
end
end
my static_pages_controller.rb is
class StaticPagesController < ApplicationController
def home
end
def manager
end
end
Thanks
undefined method `name' for nil:NilClass
You didn't defined #contact_forms in the manager method of static_pages controller, so is the error. Defining it like below should solve your problem.
class StaticPagesController < ApplicationController
def home
end
def manager
#contact_forms = ContactForm.all
end
end
Update:
You should also iterate over #contact_forms like below in the manager.html.erb
<div>
<% #contact_forms.each do |contact_form| %>
<p>
<strong>Name:</strong>
<%= contact_form.name %>
</p>
<p>
<strong>Text:</strong>
<%= contact_form.text %>
</p>
<% end %>
</div>
I'm rails noob
Welcome to the family! You're a newb, not noob :)
Pavan's answer is right; since you're new I wanted to give you some context:
undefined method 'name' for nil:NilClass
This error means you're trying to call a method on a variable which has not been defined / populated.
In your case, #contact_forms is not defined.
The confusing part for many new Ruby developers is that instead of halting the entire program, Ruby populates the NilClass & claims there to be an error with it
So whilst you'd expect it to say the variable was undeclared, it gives you a message about how a method doesn't work.
--
To resolve your issue, you need to use the following:
#config/routes.rb
resources :contact_forms
resources :static_pages
#app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def manager
#contact_forms = ContactForm.all
end
end
To steal from Pavan, this has to be backed up by looping through the #contact_forms variable (unless you've populated it with a single instance of the ContactForm model):
#app/views/static_pages/manager.html.erb
<% #contact_forms.each do |form| %>
<%= form.name %>
<% end %>
As an aside, I would never recommend calling a controller StaticPages.
When you get more into Ruby, you'll find out about the object orientated nature of the language:
I explain this a lot; basically, it means you have to keep your program centered around objects, in the case of Rails, are populated by the Models.
As such, you need to think about what data object you're trying to manipulate when showing this view. At present, it seems you want to show a contact form -- I'd put it in its own method in the ApplicationController:
#config/routes.rb
match "contact", to: "application#contact_form", via: [:get, :post]
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def contact_form
if request.post?
#post contact form
else
#load contact form
end
end
end

Rails: create action for associated Models

Pretty basic Rails question I assume but I can't figure out the simplicity of Rails.
My simple goal is to have a user submit a form indicating the amount of a donation, and have that donation linked to the user. I'm having trouble with the create action in the donations_controller.rb
I have a User.rb model and a Donation.rb model. The User.rb has_one :donation and Donation.rb belongs_to :user. I am also using Devise so I have the current_user method.
My donation table looks like this
class CreateDonations < ActiveRecord::Migration
def change
create_table :donations do |t|
t.integer :amount
t.integer :user_id
t.timestamps
end
add_index :donations, [:user_id, :created_at]
end
end
The _form.html.erb for the donations_controller.rb looks like this
<%= form_for #donation do |f| %>
<div class="field">
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
The create action in donations_controller.rb looks like this
def create
#donation = current_user.donation.build(params[:donation])
if #donation.save
flash[:success] = "Donation"
redirect_to root_path
else
render 'home/index'
end
end
I'm getting this error message when I submit the form.
NoMethodError in DonationsController#create
undefined method `build' for nil:NilClass
The method for building the has_one association is current_user.build_donation(params[:donation])

Resources