rails controller model view advice for ContacUs page - ruby-on-rails-3

I have a simple static website written in rails 3.
The site has one controller called pages and each static page is served as view. Such as pages/home, pages/about, pages/prices, etc. This all works great.
I've now run into a problem where I need to add a simple contactus feature but I'm struggling to get my head round the model/controller/views for this.
I already have a pages controller with a contactus view, that view has details addresses etc. Now I somehow need to get a message model into the contactus view so I can populate the model attirbutes and send the email.
Can I / Should I just create a new message model from within the Pages Controller as in ,
class PagesController < ApplicationController
def contact
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
# TO DO send message here using OS mail program.
redirect_to root_url, notice: "Message sent! Thank you for contacting us."
else
render "new"
end
end
end
def about
end
def products
end
def portfolio
end
def services
end
end
Or should I take out the contactus view from the pages controller and make new controller called messages ?
Thanks.

I would have a separate controller called contact for example with new and create actions
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
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
end
Then a separate model to handle your messages
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body, :file
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
end
your attributes can be anything you like, obviously this is just an example of what you can do

Related

Clearance failure when forbidden password reset

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!

Rails and Validation

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.

Rails - Show Action & View for models with one-to-one association?

I have a simple app with a User model & an Instructor_Profile model. The associations between these two models is one-to-one. I cannot get my view show.html.erb to render. I am simply trying to display a single attribute from the Instructor_Profile model and I get this error:
NoMethodError in Instructor_profiles#show
undefined method `name' for nil:NilClass
Any help would be greatly appreciated!
Models:
class User
has_one :instructor_profile
class InstructorProfile
belongs_to :user
UsersController:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
UserMailer.welcome_email(#user).deliver
render 'static_pages/congratulations'
else
render 'new'
end
end
InstructorProfilesController:
def new
#instructor_profile = current_user.build_instructor_profile
end
def create
#instructor_profile = current_user.build_instructor_profile(params[:instructor_profile])
if #instructor_profile.save
flash[:success] = "Profile created!"
redirect_to root_path
else
....
end
end
def show
#user = User.find(params[:id])
#instructor_profile = #user.instructor_profile
end
Views/instructor_profiles/show.html.erb:
<p>Display Name: <%= #user.instructor_profile.name %></p>
It happens because #user.instructor_profile is nil.
That means there is no instructor_profile corresponding to the #user.
Please check the create method inside the UserController to confirm whether instructor_profile is creating or not. Code should be something like this,
#user.instructor_profile = InstructorProfile.new(name: "my_name")
#user.instructor_profile.save
edited:
has_one association doesn't mean that every user has to have an instructor_profile. So before you call the #user.instructor_profile.name, just confirm that #user have instructor_profile or not. In your view, you can easily solve this error by adding one condition..
<p>Display Name: <%= #user.instructor_profile ? #user.instructor_profile.name : "no instructor_profile present" %></p>.
One more thing, in instructor_profiles_controller/show, change the code into
#instructor_profile = InstructorProfile.find(params[:id])
#user = #instructor_profile.user

Rspec controller error? expecting <"index"> but rendering with <""> or it's working?

I'm new with rspec test and maybe there are something that I dont undertand.
if can any help me, I really appreciate some help.
File Structure:
app/models/booking.rb
app/models/user.rb
app/models/role.rb
app/models/ability.rb
app/controllers/bookings_controller.rb
app/views/bookings/index.html.erb
app/views/dashboard/index.html.erb
app/spec/controllers/bookings_controller_spec.rb
I read this link with a similar problem but it isn't solved
Rspec controller error expecting <"index"> but rendering with <"">
is similar, because if I change this line:
it 'should not render index template from bookings' do
get :index
=> response.should_not render_template(:index)
end
for this other:
it 'should not render index template from bookings' do
get :index
=> response.should render_template(:index)
end
I get the same mistake that in the link
expecting <"index"> but rendering with <"">
and I don't know why?
Here's my Code:
My Spec:
describe BookingsController do
context 'as guest' do
before(:each) do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.save
#when i save, with gem CanCan i assign a default role to #user
#with the default role the user only can see the views/dashboard/index.html.erb
end
it 'should not render index template from bookings' do
get :index
response.should_not render_template(:index)
end
end
end
Controller:
class BookingsController < ApplicationController
load_and_authorize_resource
def index
...
end
def show
...
end
end
My model:
class Booking < Activerecord::Base
paginates_per 20
def
...
end
def
...
end
end
User:
Class User < ActiveRecord::Base
after_save :set_default_role
rolify
.
.
.
.
def set_default_role
self.add_role :default
end
end
Role:
class Role < ActiveRecord::Base
ROLES = {"admin" => "Admin", "default" => "Default"}
.
.
.
.
scopify
end
Ability:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :data_consistency
can :read, Booking
end
end
end
CanCan authorizes model access not controller actions. For most other actions these two are more or less the same thing, but not for the index. On the index action CanCan adds a scope to the query for records that includes your authorization restrictions.
What this means is that your guest user will simply not be able to see any records, but the view will still render.
What you want is authentication (ie Devise) and use it from a before_filter in each controller that requires an authenticated user to access.
class BookingsController < ApplicationController
load_and_authorize_resource # Handles authorization
before_filter !authenticate_user # Handles authentication (included with Devise)
...
end
In my case, the problem was solved in before(:each) block!
My code works like this:
before :each do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.confirm!
sign_in #user
end

Rails virtual model

In my application I have a polymorphic Idea model. I want to use it with different entities like project, opportunity etc.
In the idea table it is differentiated using ideable_id and ideable_type.
So as an example, for a Project ideable_id is the project_id and ideable_type is Project.
For Opportunity ideable_id is the opportunity_id and ideable_type is Opportunity
I want to create an idea for a model called ideabank. ideabank is a virtual model, it's not an entity like project or opportunity. How should I create such a model without a database representation which will give me ideable_id and ideable_type?
Or should I leave the fields for ideable_id and ideable_type free while adding idea to ideabank?
If you are using rails 3, see this
http://railscasts.com/episodes/219-active-model
models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :content
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
messages_controller.rb
def create
#message = Message.new(params[:message])
if #message.valid?
# TODO send message here
flash[:notice] = "Message sent! Thank you for contacting us."
redirect_to root_url
else
render :action => 'new'
end
end