I'm trying to make a form that you can only access once before a certain boolean is set to true, and along with it you input some other info, the problem is that I don't know how to set it up properly, so far the form loads no problem, but it doesn't update the information of the user.
my main problem is that I don't know how to set up the routes and the form, since most of the time I just use resources: to make them automatically
here is my form :
<%= form_for #user, url: organizer_user_path, method: :put do |f| %>
.
.
.
<%end%>
my routes, which I don't think put is correct at all
get 'organizer/user', to: "users#organizer_form"
put 'organizer/user', to: "users#organizer_registration"
and my controller
def organizer_form
#user = User.find(current_user[:id])
end
def organizer_registration
#user = User.find(current_user[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
render 'organizer_form'
end
end
I'm sure the problem lies in how I define my routes and how they are called onto the form, any help?
here are the params that are submitted:
--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: "✓"
_method: put
authenticity_token: w9zTUQLK+4kZtbvRCfcYfZgn8ma4xSRdbTMy+fktpoHlGADxHSB6u9QBh3K3zv72V/Ys2b3Q1MiKC0Gr+OqNJw==
user: !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
organizer: 'true'
contact_name: Name
last_name: Last name
contact_email: mail1#mail.com
cellphone: '1234567'
company: ''
office_phone: ''
website: ''
permitted: false
commit: continuar
controller: users
action: organizer_registration
permitted: false
This is my user_params
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website)
end
Finally found the answer! I had
attr_accessor :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website
which somehow stopped the update from happening, so removing that made the updates save to the database.
Related
I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.
I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.
I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.
class UsersController < Clearance::UsersController
before_action :require_login, only: [:create] # does this need to be in both user controllers?
...
def user_params
params.require(:user)
end
end
And here is the clearance controller.
class Clearance::UsersController < ApplicationController
before_action :require_login, only: [:create]
require 'will_paginate/array'
def new
#user = user_from_params
render template: 'users/new'
end
def create
#user = user_from_params
#user.regenerate_password
if #user.save
sign_in #user unless current_user
UserMailer.welcome_email(#user).deliver!
redirect_to users_path
else
render template: 'users/new'
end
end
def edit
#user = User.friendly.find(params[:id])
end
def update
#user = User.friendly.find(params[:id])
if #user.update(permit_params)
redirect_to #user
flash[:success] = "This profile has been updated."
else
render 'edit'
end
end
private
def avoid_sign_in
redirect_to Clearance.configuration.redirect_url
end
def url_after_create(user)
dashboards_path(user)
end
def user_from_params
user_params = params[:user] || Hash.new
is_public = check_public_params(user_params)
first_name = user_params.delete(:first_name)
last_name = user_params.delete(:last_name)
email = user_params.delete(:email)
password = user_params.delete(:password)
parish = user_params.delete(:parish)
division = user_params.delete(:division)
admin = user_params.delete(:admin)
Clearance.configuration.user_model.new(user_params).tap do |user|
user.first_name = first_name
user.last_name = last_name
user.password = password
user.email = email
user.is_public = is_public
user.parish_id = parish.to_i
user.division = division
user.admin = admin
end
end
def permit_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
end
end
EDIT: relevant portions of routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
constraints Clearance::Constraints::SignedOut.new do
root to: 'high_voltage/pages#show', id: 'landing'
end
constraints Clearance::Constraints::SignedIn.new do
# root to: 'dashboards#index', as: :signed_in_root
root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
end
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
# root to: 'teams#index', as: :admin_root
# end
resources :users do
collection { post :import }
end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id.
So instead of...
<%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %>
I did
<%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %>
Thanks, Thoughbot, for this great gem!
I'm still (for those that have helped before) having issues validating data.
The Scenario
I have two models - user and accommodation with each user having one accommodation (has_one). I am able to access the logged in user using current_user.
The Problem
When I validate users upon registration everything works fine with validation error messages displayed accordingly for each validation rule. However, I am now trying to validate accommodations when they are entered and I get a Rails error page:
Unknown Action
The action 'show' could not be found for AccommodationsController
but interestingly the url has changed to /accommodations//edit which appears to be missing an id for the accommodations id (I do want it to divert to edit if everything is ok).
I don't think it's the validation rules themselves but more how I am handling redirection (which is confusing me to be honest!). The data saves correctly and redirects correctly if it passes the validation rules but not sure how to handle a "non-save" gracefully.
The Code
Accommodation Model
class Accommodation < ActiveRecord::Base
belongs_to :user
#validation rules
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
end
/accommodations/new.html.erb
...
<%= form_for :accommodation, url: accommodations_path do |f| %>
<% if #accommodation.errors.any? %>
<% #accommodation.errors.full_messages.each do |msg| %>
<p class="error"><%= msg %></p>
<% end %>
<% end %>
...
AccommodationsController (thanks to #depa for help with this)
...
def index
if current_user.accommodation.present?
redirect_to edit_accommodation_path(current_user.accommodation)
else
redirect_to new_accommodation_path(current_user.accommodation)
end
end
def new
#accommodation = Accommodation.new
end
def create
current_user.create_accommodation(accommodation_params)
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
end
def edit
end
def update
if #accommodation.update(accommodation_params)
flash[:success] = "Accommodation details updated successfully"
redirect_to edit_accommodation_path(#accommodation)
else
flash[:error] = "Accommodation details could not be updated"
render 'edit'
end
end
private
def accommodation_params
params.require(:accommodation).permit(:name, :email)
end
...
To handle your failed validations gracefully:
def create
#accommodation = current_user.build_accommodation(accommodation_params)
if #accommodation.save
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
else
flash.now.notice = "Error creating accommodation"
render "new"
end
end
Is accommodation_params actually set up on entry to create? I'd expect you to need to use params or params[:accommodation].
If it isn't, the create_accommodation call is going to fail, which will mean current_user.accommodation will be nil, which may well produce your error.
I am using the acts_as_taggable_on gem: https://github.com/mbleigh/acts-as-taggable-on
This gem accepts user input for tags as comma separated i.e., Clever, Cool, Joyful
I have a partial: shared/_micropost_form that asks for tags:
<%= form_for(#micropost) do |f| %>
<%= f.text_field :tag_list, placeholder: "Tags", id: "genre_tag_field" %>
<% end %>
However sometimes users enter the tags with hashtags i.e., #Happy #Drunk or #Stupid, #Drunk
How can I make it so that before a Micropost is created, it checks the entered :tag_list if each word starts with a # and if it does then remove the # and add a , (unless there is a , already) to the end of each word. So When the user enters #Happy #Drunk then the tags automatically save as Happy, Drunk
Micropost Model:
acts_as_ordered_taggable
Micropost Controller:
def create
#user = User.find(params[:user_id])
#micropost = #user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to :back
else
flash[:notice] = "Error!"
redirect_to :back
end
end
Thanks, appreciate any help. Is this possible without tossing the gem?
Though not verified in practice, here is a solution.
According to README, you can set the following in initializer to remove special characters in tag name:
ActsAsTaggableOn.force_parameterize = true
It seems acts_as_taggable_on doesn't have an installation command and a default initializer. You can put above setting in any file in /initializers/, and preferred a new file dedicated to this gem.
By setting that, all of your tag name inputs will be processed after saving like
my_string.parameterize
Test
"radical)(cc".parameterize
#=> "radical-cc"
"#Happy".parameterize
#=> "happy"
So I followed this wonderfully flawed tutorial:
http://matharvard.ca/posts/2011/aug/22/contact-form-in-rails-3/
...on making contact forms. It works great. The only problem is that it ONLY sends the subject. I think maybe the problem is in the notifications mailer:
notifications_mailer.rb
class NotificationsMailer < ActionMailer::Base
default :from => "noreply#youdomain.dev"
default :to => "you#youremail.dev"
def new_message(message)
#message = message
mail(:subject => "[YourWebsite.tld] #{message.subject}")
end
end
I would, of course, like it to send ALL the info the user submitted... (name, email address, subject, and body.
Also I was wondering how I could do a simple version of this with just the body where the subject is set to a default. (I want to have a small comment box that would send an email to me with the comment.) Would I have to make a whole new controller and model for that, or could this handle both?
UPDATE
Notifications Mailer View / new.html.erb
Name: <%= #message.name %>
Email: <%= #message.email %>
Subject: <%= #message.subject %>
Body: <%= #message.body %>
contact controller
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
flash[:success] = "Message was successfully sent."
redirect_to(root_path)
else
flash[:error] = "Please fill all fields."
render :new
end
end
end
message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body
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
Basically it works... but it only sends the subject. I also got it to send a complete mail once with everything BUT the subject... but I can't remember how I did it.
Should I just smash this computer into a million pieces and go on a rampage?
Sigh...
UPDATE AGAIN
This is what the emails say with the above settings:
Subject: [liquid.radio] Whatever The Subject is. Body: Completely
blank
This is what they said after whatever the hell I did two weeks ago.
Subject: Message from liquid.radio
Body:
A contact enquiry was made by Richard Pryor at 2013-06-17 23:36.
Reply-To: richard#pryor.com
Subject: Scared for no reason Body: Oh
no... Oh God no! What is that?!
All I did was mess around with the notifications controller. Although I don't remember... for the life of me... what I did. But, as you can see... it send the complete message as it should... but a completely different subject.
Really kinda need help here.
First, this project places production settings in config/application.rb. Move the GMail ActionMailer settings to config/environments/production.rb. Add the letter_opener gem to your Gemfile's development group.
# Gemfile
group :development do
gem 'letter_opener'
end
Add the letter_opener settings to your development environment.
# config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
Add active_attr gem
# Gemfile
gem 'active_attr'
Make sure you run bundle install
Replace your messages model with robust ActiveAttr implementation.
# app/models/message.rb
class Message
include ActiveAttr::Model
attribute :name
attribute :email
attribute :subject
attribute :body
validates_presence_of :name, :email, :subject, :body
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
end
Improve the routes.
# config/routes.rb
get 'contact' => 'contact#new', :as => 'contact'
post 'contact' => 'contact#create', :as => 'contact'
Make sure your email template is correct
# app/views/notifications_mailer/new_message.text.erb
Name: <%= #message.name %>
Email: <%= #message.email %>
Subject: <%= #message.subject %>
Body: <%= #message.body %>
Update: December 12, 2013
I've created a Rails 3.2 project on GitHub to assist anyone searching for a modern solution to creating contact forms in Rails. Merry Christmas!
https://github.com/scarver2/contact_form_app
Ultimately here's what I did... I realize this isn't a true solution. But I still don't have a different one.
notifications_mailer.rb
class NotificationsMailer < ActionMailer::Base
default :from => "example#gmail.com"
default :to => "example#gmail.com"
def new_message(message)
#message = message
mail(:subject => "[liquid.radio] #{message.subject}", :body => "
From: #{message.name}
Reply to: #{message.email}
Subject: #{message.subject}
Message: #{message.body}")
end
end
Not how the mailer is supposed to work at all... but at least it sends a complete message.
If you're in a time crunch... like I was... this will get you there. I will accept a real answer (probably scarver2's) once I stop getting blank emails any other way.
I have question model which has many options.
In my question controller new action I create five options ready for my user
def new
#question = Question.new
5.times.with_index do |index|
#question.options.build(:order => index)
end
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #question }
end
end
In the view I loop through all options
- form_for(#question) do |f|
.field
= f.label :title, t("question.title")
= show_errors_for(#question, :title)
= f.text_field :title
- #question.options.each do |option|
- f.fields_for :options, option do |o|
.field
= o.label :option, t("question.option_no", { :index => option.order })
= o.text_field :option
= o.hidden_field :order, :value => option.order
.actions
= f.submit t("add_question.create")
My question model looks like this
class Question < ActiveRecord::Base
attr_accessible :title, :options_attributes
belongs_to :user
has_many :options
accepts_nested_attributes_for :options, :reject_if => proc { |attributes| attributes['option'].blank? }
validates :title, :length => { :maximum => 100 }, :presence => true
validate :min_no_of_options
def min_no_of_options
if self.options.size < 3
errors.add_to_base "Must have at least three options"
end
end
end
And my question controller create action
def create
if current_user
#question = current_user.questions.build(params[:question])
else
#question = Question.new(params[:question])
end
if #question.save
redirect_to(#question, :success => t('question.flash_success'))
else
flash.now[:error] = t("question.flash_error")
render :action => "new"
end
end
Now, when I enter only two options in the form and hit the create button the validation prevents the model from being saved. Which is good. But when the create action renders the new action again, only the option fields that I filled are showing up. The three option fields which were left blank have disappeared.
If I replace the "#question.save" in my create action with "false", the behavior is the same. So this suggests that something in the way I create the #question variable in the create action is responsible for throwing away my empty options.
But if I instead remove the :reject_if from my question model the empty options are showing up after a failing question save as expected. (I have a presence validation for the option attribute in my option model) So this tells me that there is nothing wrong in the way I create the #question variable in the create action. It is not throwing away the empty options. So where they are kicked out?
There was one pretty similar question, but the answer in there is not something I would like to do. Though it might be something I have to do.
rails fields_for does not render after validation error on nested form
EDIT
After some more study with rails console I noticed that it truly is the creation of #question variable where the empty options get thrown away. This happens because I have the reject_if defined in the question model. After commenting the reject_if out from the model the empty options were added into the #question variable.
So I guess I need to remove the reject_if and use after_save callback to destroy empty options from the database. This way I will have the empty options going along with the question until the question is saved.
I'm answering my own question because I got the issue solved.
Blank "options" were removed from the "question" because of reject_if in the "question" model. The reject_if statement was applied when the code below was executed, and therefore blank "options" were removed.
#question = current_user.questions.build(params[:question])
I replaced the reject_if with an after_save callback which removes the options which were left blank.