Rails 4 Strong Parameters in generic controller - ruby-on-rails

I have a "generic" controller in charge of managing all public pages and actions
class PublicController < ApplicationController
def index
end
def contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
end
But when I want to access the "contact us" link I have the following error
param is missing or the value is empty: contact
Is it possible to operate strong parameters inside a "generic" controller or should I only use them as part of a controller named "Contact" ?

That looks like the error is because you've not got a contact parameter in the parameters hash. You want something more like the following:
def contact
#contact = Contact.new
end
def send_contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
Or
def index
#contact = Contact.new
end
def contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
Essentially you should only be calling contact_params on the action you're posting to.

Related

Contact won't work due to Rails 5 update [duplicate]

This question already has answers here:
ActiveModel::ForbiddenAttributesError when creating new user
(8 answers)
Closed 4 years ago.
I'm Working on upgrading a Rails 3v project to 5v. Everything seems to be working but I get this error:
ActiveModel::ForbiddenAttributesError in ContactsController#create
ActiveModel::ForbiddenAttributesError
#contact = Contact.new params[:contact]
Here is My Model:
class Contact < ApplicationRecord
belongs_to :contactable, :polymorphic => true
attribute :contact
validates :email, :presence => true
validates :name, :presence => true
validates :body, :presence => true
validates :email, email: true, allow_blank: true
scope :are_read, -> { where("state = ? ","read") }
scope :are_unread, -> { where("state = ?","unread") }
state_machine :state, :initial => :unread do
event :reading do
transition :unread => :read
end
end
end
and
Here is my Controller:
# -*- encoding : utf-8 -*-
class ContactsController < ApplicationController
def new
#contact = Contact.new
if params[:product].present?
#contact.contactable = Product.find params[:product]
end
if params[:rental].present?
#contact.contactable = Rental.find params[:rental]
end
end
def create
# #user = User.new(user_params)
#contact = Contact.new params[:contact]
if #contact.save
UserMailer.contact_notification(#contact).deliver
redirect_to thanks_contact_path
else
render :new
end
end
def thanks
end
end
Try to the following to create a private method with Rails strong parameters under the controller
private
def contact_params
params.require(:contact).permit(:name, :email, :body)
end
Change like this
def create
#contact = Contact.new(contact_params)
....
end
instead of this
#contact = Contact.new params[:contact]
Rails 4 onwards you cannot use params directly for mass assignment.
You need to use Strong Params to whitelist the parameters.
#contact = Contact.new params[:contact]
Use
def create
#contact = Contact.new(contact_params)
...
end
private
def contact_params
params.require(:contact).permit(:contact, :params, :attributes_here) # <= Change this
end

undefined method `build_profile' for #<User:0x000000035b9ae8>

I can't figure out the problem with my code. I'm trying to add a profile to my user. For this I get my user ID and attach this to my profile in a DB. However after submitting the form it gives the following error: NoMethodError in ProfilesController#create
class ProfilesController < ApplicationController
# GET to /users/:user_id/profile/new
def new
#profile = Profile.new
end
# POST to /users/:user_id/profile
def create
# Ensure that we have the user who is filling out form
#user = User.find(params[:user_id])
# Create profile linked to this specific user
#profile = #user.build_profile( profile_params )
if #profile.save
flash[:success] = "Profile updated!"
redirect_to root_path
else
render action: :new
end
end
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :phone_number, :contact_email, :banking)
end
end
Your models need to be some thing like this... I assume you are missing a has_many or belongs_to in the user modal.
class User
has_many :profile
# or belongs_to :profile
end
class Profile
belongs_to :user
# or has_many :users
end
Give your modals if it doesn't work, we can fix it up.
try to replace
#user = User.find(params[:user_id])
#profile = #user.build_profile( profile_params )
by
#profile.user_id = current_user.id
for user_id it depend how you named your user id foreign key
You may do
def create
#profile = Profile.new(profile_params)
if #profile.save
flash[:success] = "Profile updated!"
redirect_to root_path
else
render action: :new
end
end
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :phone_number, :contact_email, :banking, :user_id)
end
Or
def profile_params
params.require(:profile).permit(:first_name, :last_name, :phone_number, :contact_email, :banking).merge!(user: current_user)
end
Regarding the error "NoMethodError in ProfilesController#create" it may happen because it is not declared in the routes.rb or it does a HTTP Get instead of a Post.
in the routes.rb file,
resources :users do
resources :profiles
end
http://guides.rubyonrails.org/routing.html#nested-resources

How to retrieve nested model data from another model? : undefined method error

So the basis of my code so far is:
a customer has_one calendar
a calendar belongs_to a customer
a calendar has_many events
an event belongs_to a calendar
I am trying to, when creating a new event, specify the customer and calendar it belongs to but it throws error "undefined method `Calendar'":
class EventsController < ApplicationController
def new
#event = Event.new
#currentcalendar = current_customer.calendar # this is where it is failing
end
def create
if #event = #currentcalendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
this is my current_customer method within application_controller:
def current_customer
if (customer_id = session[:customer_id])
#current_customer ||= Customer.find_by(id: customer_id)
elsif (customer_id = cookies.signed[:customer_id])
customer = Customer.find_by(id: customer_id)
if customer && customer.authenticated?(cookies[:remember_token])
session[:customer_id] = customer.id #log in
#current_customer = customer
end
end
end
Here are the related controller files. Customer:
class CustomersController < ApplicationController
def new
#customer = Customer.new
#businesses = Business.all
#calendar = Calendar.new
end
def create
#customer = Customer.create(customer_params)
#calendar = #customer.build_calendar
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password, :business_id)
end
Calendar:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
end
I'm very new to Ruby/ Rails and so can't figure this out by myself. Is this problem occurring because I have wrongly created my calendar? I wanted it to be created when its user is created, which works, but I just don't know how to get to the calendar and user within the events controller.
Thanks for your help!
EDIT: these are the model classes.
customer:
class Customer < ActiveRecord::Base
belongs_to :business
has_one :calendar
has_secure_password
attr_accessor :remember_token
#remembers a user in the database for use in persistent sessions
def remember
self.remember_token = Customer.new_token
update_attribute(:remember_digest, Customer.digest(remember_token))
end
def Customer.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def forget
update_attribute(:remember_digest, nil)
end
def Customer.new_token
SecureRandom.urlsafe_base64
end
#returns true if the given token matches the digest
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
calendar:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
event:
class Event < ActiveRecord::Base
belongs_to :calendar
end
Your current_customer can be nil at times. To avoid this you can add a before_filter callback that checks if there is a customer that is logged in or not.
In your application_controller create a method called customer_found?
def customer_found?
current_customer.present?
end
Change your events controller to
class EventsController < ApplicationController
before_filter :customer_found?
before_filter :prepare_calendar, only: [:new, :create]
def new
#event = Event.new
end
def create
if #event = #current_calendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def prepare_calendar
#current_calendar = current_customer.calendar
end
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
Since you did not assign your #current_calendar in your create method then you are gonna get undefined method build for nil class. You need to initialize the variable since it can not get it from the new method. Each action has its own independent variables so make sure to prepare all necessary variables before using them.

Instantiate a ruby class no initialize method but with attr_accesors?

The following code works but I don't understand why.
The Model: I have a Class called Contact that doesn't have an initialize method (i.e it inherits the initialize method from the default Object class).
class Contact
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :email
validates_presence_of :content
...
end
The controller: I have a ContactsController with a 'create' method that instantiates the Contact class passing along some parameters through the 'secure_params' method.
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
# THIS IS THE LINE THAT I DON'T UNDERSTAND
#contact = Contact.new(secure_params)
if #contact.valid?
#contact.update_spreadsheet
UserMailer.contact_email(#contact).deliver
flash[:notice] = "Message sent from #{#contact.name}."
redirect_to root_path
else
render :new
end
end
private
def secure_params
params.require(:contact).permit(:name, :email, :content)
end
end
Where do this parameters go to if there is no initialize method that sets them to instance variables and the default behavior of the 'new' method (inherited from the Ruby's Object class) does nothing with passed in parameters?
Why do they end up being set as instance variables? (something to do with the attr_accesors?)
You are including ActiveModel::Model which defines the initialize method that sets the values.

Rails Validates don't work on update

I've got a problem with validators. I have a "contact" model which contains two fields firstname and lastname and I want both required on CREATE and UPDATE method. When I create a record with no data, the server return me a 422 and do the rollback. This is ok. But when I update a record the server don't return the error 422 although the server does the rollback. And I need the return of the error to manage it on the client side.
So I use validators like this :
class Contact < ActiveRecord::Base
validates :lastname, presence: true
validates :firstname, presence: true
end
and my controller is:
class ContactsController < ApplicationController
respond_to :json
def index
respond_with Contact.all
end
def create
respond_with Contact.create(contact_params)
end
def show
respond_with Contact.find(params[:id])
end
def edit
respond_with Contact.find(params[:id])
end
def update
respond_with Contact.find(params[:id]).update(contact_params)
end
def destroy
respond_with Contact.find(params[:id]).destroy
end
private
def contact_params
params.require(:contact).permit(:lastname, :firstname, :position)
end
end
I have a serializer:
class ContactSerializer < ActiveModel::Serializer
attributes :id, :lastname, :firstname, :created_at, :updated_at
end
Someone could help me, please ?
Thanks by advance.
Contact.find(params[:id]).update(contact_params)
returns a Boolean, hence you are telling Rails to render a boolean (which will render a 200 with the boolean serialized as JSON).
Same for destroy. You need to pass the instance.
def update
contact = Contact.find(params[:id])
contact.update(contact_params)
respond_with contact
end
def destroy
contact = Contact.find(params[:id])
contact.destroy
respond_with contact
end
It's also a good habit to extract the finder in a before_action.
before_action :find_contact
def update
#contact.update(contact_params)
respond_with #contact
end
def destroy
#contact.destroy
respond_with #contact
end
protected
def find_contact
#contact = Contact.find(params[:id])
end
You can refactor the other actions to remove the duplicate finder.
you can try the following code, it could be tempo fix
def update
#contact = Contact.find params[:id]
#contact.update contact_params
if #contact.errors.count > 0
# do you like
else
respond_with #contact
end
end

Resources