I'm trying to make a contact form for Rails 4. But there're only tutorials for Rails 3.
But I got this error:
undefined method `name' for #<Message:0xa461fcc>
As I can understand it can't "see" the model.
index.html.erb
<%= form_for Message.new, :url => new_contact_path do |form| %>
<fieldset class="fields">
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
... same fieldsets...
<fieldset class="actions">
<%= form.submit "Send" %>
</fieldset>
<% end %>
in my model message.rb i got:
class Message
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
include ActionView::Helpers::TextHelper
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
and in routes i just got:
resources :contact
That's the controller code:
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(contact_params)
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def contact_params
params.require(:message).permit(:name, :email, :subject, :body)
end
end
Thanks in advance!
You need to add attr_accessor :name, :email, :subject, :and_so_on to your class to that these methods are defined.
When using form.label, Rails will try doing your_message.label, but this method is not defined since you're not using active record but only few bits from active model.
Using attribute accessors should do the trick.
Related
I make objects in controller's loop.
I need to check pet_name array before loop starts.
(because i got undefined method 'each' for nil:NilClass when
params[:pet_name].each do |pid|) runs)
But my validation always called after User.new or User.create.
I want to change to validate as when i push submit button and check validation, and redirects back when pet_name array is nil.
Ho can i change my code?
Controller
def create
user_name = params[:user_name]
params[:pet_name].each do |pid|
#user = User.new
#user.name = user_name
#user.pet_name = pid
render :new unless #user.save
end
redirect_to users_path
end
User.rb
class User < ApplicationRecord
has_many :pet
validates :name, presence: true
validates :pet_name, presence: true
validate :check_pet
def check_pet
if pet_name.nil?
errors.add(:pet_name, 'at least one pet id')
end
end
end
Prams structure
{ name: 'blabla', pet_name: ['blabla', 'blablabla', 'bla'] }
Sorry but that isn't even close to how you approach the problem in Rails.
If you want a user to have many pets and accept input for the pets when creating users you need to create a working assocation to a Pet model and have the User accept nested attributes:
class User < ApplicationRecord
has_many :pets # has_many assocations should always be plural!
validates :name, presence: true
validates :pets, presence: true
accepts_nested_attributes_for :pets
end
# rails g model pet name user:reference
class Pet < ApplicationRecord
belongs_to :user
validates :name, presence: true
end
class UsersController < ApplicationController
def new
#user = User.new(user_params)
3.times { #user.pets.new } # seeds the form with blank inputs
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user,
success: 'User created',
status: :created
else
render :new,
status: :unprocessable_entity
end
end
private
def user_params
params.require(:user)
.permit(:name, pets_attributes: [:name])
end
end
<%= form_with(model: #user) do |form| %>
<div class="field">
<%= form.label :name %>
<%= form.text_input :name %>
</div>
<fieldset>
<legend>Pets</legend>
<%= form.fields_for(:pets) do |pet_fields| %>
<div class="nested-fieldset">
<div class="field">
<%= pet_fields.label :name %>
<%= pet_fields.text_input :name %>
</div>
</div>
<% end %>
</fieldset>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is a pretty advanced topic and you should have your Rails CRUD basics well figured out before you attempt it. You should also consider if you instead want to use a separate controller to create the pets one by one as a nested resource after creating the user.
Model User:
class User < ApplicationRecord
has_one :address, foreign_key: :user_id
accepts_nested_attributes_for :address
end
Model Address
class Address < ApplicationRecord
belongs_to :user, optional: true
end
Controller User, everything happen here
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:city, :street, :home_number, :post_code, :country])
end
end
Updating form:
<%= form_for #user, :html => { :id => "update-form", :class => "update-form"} do |f| %>
<%= f.text_field :name %>
<%= f.text_field :email %>
<%= f.fields_for :address do |a| %>
<%= a.text_field :city %>
<%= a.text_field :street %>
<%= a.number_field :home_number %>
<%= a.text_field :post_code %>
<%= a.text_field :country %>
<% end %>
<%= f.submit %>
<% end %>
When I submitting my form, it shows me everything is fine, I mean "Update successfully", but in database its looks like new record is added to address table, but user table is updated properly. Can someone give me explanation why? I am looking answers in google but nothing helps me.
When I submitting my form, it shows me everything is fine, I mean
"Update successfully", but in database its looks like new record is
added to address table, but user table is updated properly. Can
someone give me explanation why?
This is due to the nature of strong params. It expects :id to be permitted for the nested_attributes to update properly, else a new record is created instead. Permit the :id and you are good to go.
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:id, :image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
try below code in your controller:
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
end
I am starting to develop in ROR. The user history that I am doing now is a Contact Page. The codes for the MVC are listed below:
app/controller/contatos_controller.rb
class ContatosController < ApplicationController
def new
#contato = Contato.new
end
def create
#contato = Contato.new(secure_params)
if #contato.valid?
flash[:notice] = "Mensagem enviada de #{#contato.name}."
redirect_to root_path
else
render :new
end
end
private
def secure_params
params.require(:contato).permit(:name, :subject, :email, :content)
end
end
app/models/Contato.rb
class Contato
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :subject, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :subject
validates_presence_of :email
validates_presence_of :content
validates_format_of :email,
with: /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
validates_length_of :content, :maximum => 500
end
app/views/contatos/new.html.erb
<h3>Contato</h3>
<div class="form">
<%= simple_form_for #contato do |form| %>
<%= form.error_notification %>
<%= form.input :name, autofocus: true %>
<%= form.input :subject %>
<%= form.input :email %>
<%= form.input :content, as: :text %>
<%= form.button :submit, 'Submit', class: 'submit' %>
<% end %>
</div>
config/routes.rb
Rails.application.routes.draw do
resources :contatos, only: [:new, :create]
root 'static_pages#home'
end
When I try to access http://localhost:3000/contatos/new, the following error is displayed:
NameError in ContatosController#new
uninitialized constant ContatosController::Contato
app/controllers/contatos_controller.rb:4:in `new'
What I found about this error is that it is related to typos, but this does not seem to be my case. It's probably a silly mistake, but I could not find it. Can anybody help me?
As #Robert already noted in his comment, you need to name your Contato model file in lowercase.
However, since Ruby is looking for your model in the controller itself (ContatosController::Contato), you can solve this problem by putting double colon before the model name like this:
#contato = ::Contato.new
This will force Ruby interpreter to look for Contato model in the "root/top" namescope.
Hi this question is basically the same as this one, which had no responses. I'm trying to combine the Devise registration form to include fields that produce not only a "user", but a "customer" object, an "account" object for that customer, and an "address" for that customer.
When visitor clicks "Sign Up", the registration form should include the standard Devise stuff, but also the fields for the creation of the other objects. Instead, I get this error:
NoMethodError in Registrations#new
undefined method `build_address' for #
Extracted source (around line #6):
<div class="panel panel-default" style="width: 14em;">
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
<%= form_for(resource, as: resource_name, url: >registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<h3>User Info</h3>
Rather than explaining all the relationships, here are the models:
user.rb
class User < ActiveRecord::Base
before_create :generate_id
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
has_one :administrator
has_one :customer
has_many :accounts, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :accounts, :allow_destroy => true
has_one :address, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :address, :allow_destroy => true
validates_uniqueness_of :email, :case_sensitive => false
validates_uniqueness_of :id
validates :username,
:presence => true,
:uniqueness=> {
:case_sensitive => false
}
# User ID is a generated uuid
include ActiveUUID::UUID
natural_key :user_id, :remember_created_at
belongs_to :user
# specify custom UUID namespace for the natural key
uuid_namespace "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :timeoutable, :recoverable, :trackable, :validatable
# Generate a random uuid for new user id creation
def generate_id
self.id = SecureRandom.uuid
end
# Allow signin by either email or username ("lower" function might have to be removed?)
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions.to_h).first
end
end
end
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
has_one :address
has_many :accounts
validates :phone1, :firstname, :lastname, presence: true
end
account.rb
class Account < ActiveRecord::Base
belongs_to :customer
belongs_to :user
has_one :acct_type
has_many :acct_transactions
validates :acct_type, presence: true
end
address.rb
class Address < ActiveRecord::Base
belongs_to :customer
belongs_to :user
validates :zip_code, presence: true
validates :address1, presence: true
has_one :zip_code
has_one :state, through: :zip_code
end
The two controllers in question:
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
# GET /users/sign_up
def new
#user = current_user
#customer = nil ##user.customer
#account = nil ##customer.account
#address = nil ##customer.address
# Override Devise default behavior and create a customer, account, and address as well
build_resource({})
resource.build_customer
respond_with self.resource
build_resource({})
resource.build_account
respond_with self.resource
build_resource({})
resource.build_address
respond_with self.resource
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
.permit(:username, :email, :password, :password_confirmation,
:customer_attributes => [:phone1, :phone2, :title, :firstname, :lastname],
:account_attributes => :acct_type,
:address_attributes => [:address1, :address2, :zip_code])
}
end
end
addresses_controller.rb (The important parts)
def new
#customer = current_user.customer
#address = #customer.address.build(:customer_id => #customer.id,
:address1 => nil,
:address2 => nil,
:zip_code => nil)
end
def create
#customer = current_user.customer
#address = #customer.address.build(:customer_id => #customer.id,
:address1 => nil,
:address2 => nil,
:zip_code => nil)
respond_to do |format|
if #address.save
format.html { redirect_to #address, notice: 'Address was successfully created.' }
format.json { render :show, status: :created, location: #address }
else
format.html { render :new }
format.json { render json: #address.errors, status: :unprocessable_entity }
end
end
end
And here is the view where the exception is raised (It's really long so actually the important parts):
<h1>Create an account</h1>
<div class="form-group">
<div class="panel panel-default" style="width: 14em;">
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<h3>User Info</h3>
<div class="form-group">
<!-- fields for User object -->
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username, autofocus: true %>
</div>
...
<!-- fields for Customer object -->
<%= f.fields_for :customer do |customer_fields| %>
<div class="field">
<%= customer_fields.label :firstname %>
<%= customer_fields.text_field :firstname %>
</div>
...
<!-- fields for Account object -->
<%= f.fields_for :account do |account_fields| %>
<div class="field">
<%= account_fields.label :acct_type %>
<%= account_fields.text_field :acct_type %>
</div>
<% end %>
<!-- fields for Address object -->
<%= f.fields_for :address do |address_fields| %>
<div class="field">
<%= address_fields.label :address1 %>
<%= address_fields.text_field :address1 %>
</div>
...
The exception is pointing to the block of statements at the top...
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
... which has given me trouble before. Before the current error I was getting a similar error from the second line ("build_account"). But that turned out to be a pluralization issue, which I believe I've fixed. Since the HTML is read sequentially, it would seem that there is no problem with the first two build_ methods. Why is there then a problem with the build_address method?
I need to fix this error before I can know if the whole thing will actually work or not. Any ideas?
Thanks
It's Rails 4.1.8 / Devise 3.4.1
The trouble turned out to be the syntax I was using create multiple resource objects. It would pass one, but ignored the rest. What I ended up doing to make it work (or at least make the error go away) was to override the build_resource method to accept an array of parameters for each object to be instantiated:
def new
#user = current_user
build_resource({})
self.resource[:customer => Customer.new, :account => Account.new, :address => Address.new]
respond_with self.resource
end
def build_resource(hash=nil)
hash ||= params[resource_name] || {}
self.resource = resource_class.new(hash)
end
def create
# Override Devise default behavior and create a customer, account, and address as well
resource = build_resource(params[:user])
if(resource.save)
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
render :action => "new"
end
end
Also, I removed the three lines at the top of the form view as they attempted to do some sort of pre-validation in the view and just caused problems. Plenty of validation will happen when the form is submitted. This seems to be doing something good. Now I'm working with the form view and having trouble getting each part to render. Fields_for is rendering fields for User and Account models, but not Customer or Address.
I'm trying to build a contact form in Rails without storing the mails in my database. But I'm getting an undefined method 'name' for nil:NilClass error when I send the form.
My MessagesController
def create
#message = Message.new(message_params)
if #message.valid?
# TODO send message here
Messages.new_messages_email(#mailer).deliver
redirect_to root_url, notice: "Message sent! Thank you for contacting us."
else
redirect_to root_url, notice: "Something went wrong, try again!"
end
end
private
def message_params
params.require(:message).permit(
:name,
:message,
:email
)
end
My Messages model
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :message
validates_presence_of :name
validates :email, :email_format => {:message => 'is not looking good'}
validates_length_of :message, :maximum => 500
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
The email body
!!!
%html
%body
%p
= #mailer.name
Schreef het volgende:
%p= #mailer.message
%p= #mailer.email
And in my routes I have
resources :messages
I forgot to post my Messages mailer
class Messages < ActionMailer::Base
default from: "info#domein.nl"
def new_messages_email(mailer)
#mailer = mailer
mail(to: 'peter#no-illusions.nl',
subject: 'Iemand wilt contact met U')
end
end
For completion my form,
= form_for #message do |f|
.field
%br/
= f.text_field :name, :placeholder => "Naam"
.field
%br/
= f.text_field :email, :placeholder => "Emailadres"
.field
%br/
= f.text_area :message, :rows => 5, :placeholder => "Uw bericht"
.actions= f.submit "Verstuur bericht", :id => "submit"
In my MessagesController I define the paramaters for the create function, but there's something I'm doing wrong, forgetting or overlooking which causes the error.
In your controller should be probably:
Messages.new_messages_email(#message).deliver # not #mailer
Besides that, you have to reinitialize #message within your mailer, e.g:
class Messages < ActionMailer::Base
def new_messages_email(msg)
#message = msg
end
end