I'm having trouble with configuring the model which is generated by scaffold in my Rails 4 application.
These are my models:
class Contact < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :contact
after_create :make_contact
def make_contact
create_contact(
:country => "USA",
:city => "Newyork"
)
end
end
As you can see, I'm creating a Profile model instance for each User when they registered for the site.
I generated User model with Devise GEM and generated Contact model with rails scaffold generator.
1) I want my Users to only update or view their profile. I want to prevent them to list all profiles, destroy their profiles or create a new profile. What is the best approach to do this?
2) I want my application to redirect automatically to the users related profile page when they visit /contacts route.
3) User can't be able to see other users profiles by changing the URL like /contacts/1, contacts/2 etc.
How can I do this?
Thanks.
User before_filter/before_action in your controller
def UsersController < ApplicationController
before_filter :restrict_user, :only => [:show, :edit, :update]
private
def restrict_user
redirect_to :root, :alert => "Not authorized" unless params[:id] = current_user.id
end
end
In your routes, you can specify only the actions that you want
resources :users, :only => [:new, :create, :edit, :update, :show] #index and destroy are not in the list
You can do the same in contacts controller too
Related
I'm have some issues with devise. I've added a users table (devise), an appointments table and a profiles table to my app. One user should have many appointments, and have one profile.
The problem is that I can't display the profile for that user. It tells me that there is No route matches [GET] "/profile/1"
I wonder if anyone can point out where I'm going wrong, and why?
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :profiles
resources :appointments
root 'page#home'
get 'page/testimonials'
get '/signedinuserprofile' => 'profiles#signedinuserprofile'
#get 'page/home'
profiles controller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
end
def signedinuserprofile
profile = Profile.find_by_user_id(current_user.id)
if profile.nil?
redirect_to "/profile/new"
else
#profile = Profile.find_by_user_id(current_user.id)
redirect_to "/profile/#{#profile.id}"
end
end
application controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
"/signedinuserprofile"
end
end
sessions controller:
class SessionsController < Devise::SessionsController
#after_sign_in_path_for is called by devise
def after_sign_in_path_for(user)
"/signedinuserprofile" # here I provide the path for the user's profile
end
end
You're trying to use a resourceful route yet you don't have a show action in your controller. Add a show action and find the user from the params #profile.id
You are essentially redirecting to an action that you don't have in your profiles controller.
I have a user model and profile model using devise.
user has_one profile
profile belongs_to user
How can I throw an error if a user that already has a profile associate to them when they try to create another profile.
so if a user goes to example.com/profiles/new it would throw the error
Well you could do something like that:
profiles_controller.rb
def new
if current_user.profile.empty?
# create profil for user
else
# raise error which doesn't make sense or redirect like
redirect_to user_profile_path
end
end
#auL5agoi answer doesn't prevent someone from accessing the create action though. You want to run the check on both actions.
def ProfilesController < ApplicationController
before_action :check_profile_presence, only: [:new, :create]
def new
end
def create
end
private
def check_profile_presence
redirect_to user_profile_path if current_user.profile.exists?
end
end
http://guides.rubyonrails.org/action_controller_overview.html#filters
The best practice is to change your model! This will prevent problems in your db ... add to your model/profile.rb.
class Profile < ApplicationRecord
belongs_to :user
validates_uniqueness_of :user_id
#[... other codes...]
end
Have a nested resource as such
class Dealer < ActiveRecord::Base
has_many :vehicles
end
and
class Vehicle < ActiveRecord::Base
belongs_to :dealer
end
below are my routes.
resources :dealers do
resources :vehicles, :except => [:index]
end
resources :vehicles, :only => [:index]
looking at the wiki at the github page for cancan I did the following:
class VehiclesController < ApplicationController
load_and_authorize_resource :dealer
load_and_authorize_resource :vehicle, :through => :dealer
def index
#vehicles = Vehicle.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #vehicles }
end
end
end
but now when the admin tries to go to the index page with the abilities:
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
end
end
I get
Couldn't find Dealer with id=
what do i need to change for admin to still be able to do all the actions and yet have others be checked before they can do any action.
The problem is not that he is not authorized to this action. The problem is that CanCan tries to fetch an instance of dealer to load all its vehicles and you have not provided a :dealer_id within params[:dealer_id]. Cancan assumes you would be loading only dealer's vehicles in this controller because you used an load_and_authorieze :through. This authorization should be used within Dealers::VehiclesController.
As long as you only loading vehicles just use load_and_authorize_resource :vehicle. And because load_and_authorize will set #vehicles for you within the before filter there is also no need to load the vehicles explicitly with Vehicle.all.
load_and_authorize is just a convenient method and it assumes some defaults. Once you will come to a point where you have some more complex use case. It will be time to throw away this method and just use CanCan's authorize!, can? and a properly configured Vehicle .accessible_by (for listing) methods.
When using load_and_authorize_resource :vehicle, through: :dealer it expects to receive a dealer_id in the request in order to authorize the dealer.
Since you use except: :index in your routes dealer_id will not be automatically included in the request.
If you don't want to authorize the dealer in the index action you can do something like this (taken from Can Can wiki)
class CommentsController < ApplicationController
load_and_authorize_resource :post
load_and_authorize_resource :through => :post
skip_authorize_resource :only => :show
skip_authorize_resource :post, :only => :show
end
I'm trying to get my rails app to redirect to the edit_profile_path after sign up through devise, but am having serious problems trying to work out how. I've spent the past four hours on here trying to find a solution, but can't seem to find the problem.
My current routes.rb:
resources :users, :only => [:index] do
member do
get :favourite_users, :favourited_users
end
resources :posts
resources :profiles, only: [:show, :edit]
end
My RegistrationsController.rb
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
edit_profile_path(resource)
end
end
My profile/edit.html.erb:
class User < ActiveRecord::Base
before_create :build_profile #creates profile at user registration
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
end
My profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
def name
first_name + " " + last_name[0]
end
end
My ProfilesController.rb
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def show
end
def profile
end
def edit
#profile = current_user.profile
end
end
I've tried as many solutions as I can but all I seem to get is:
undefined method `profile_path' for #<#<Class:0xb50b8618>:0xb50abe54>
or if I remove the (resources) from the edit_profile_path(resource) in the RegistrationsController:
No route matches {:action=>"edit", :controller=>"profiles"} missing required keys: [:id]
Any help would be greatly appreciated!
Have you tried running the following from console:
rake routes
This will output all of the routes your app knows about. I'm hoping I read your routes file right (hard to look at without the formatting), but with it being a nested route, I think you actually want to use
edit_user_profile_path(resource)
I have these 3 models: and here is what i need:
Basically I have records, and a user must hold a specific role assigned to him to be able to make changes to that record. each different record can have multiple roles and each role can belong to many different versions of a record. now that im having a version control, each single role can belong to many different versions of a record( different versions of the one record can be associated through the common secondary_id attribute for records).
What is the best way to establish this relationship between the three models?
thanks
Use cancan, http://github.com/ryanb/cancan. It'll give you exactly what you need.
Try this:
Change your User model:
class User
has_many :roles
has_many :records
def has_access_to?(obj)
roles.exists?(:id => obj.role_ids)
end
end
Controller code:
class RecordsController < ApplicationController
before_filter :require_user # checks if the user is logged in
before_filter :load_record, :except => [:index]
before_filter :require_permission, :except => [:new, :create, :index]
private
def require_permission
return true if current_user.has_access_to?(#record)
render :text => "You don't have permission to complete this action.",
:status => '401 Unauthorized'
return false
end
def load_record
case(action.to_sym)
when :new, :create
#record = current_user.records.build(params[:record])
when :edit, :update, :show, :destroy
#record = current_user.records.find(params[:id])
end
end
end