Im attempting to see reviews for a specific game that ive created. Right now my reviews/index show for all games, not just the specific game im looking for. Im assuming its just something im missing in my params but not sure...
these my are controllers...
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except:[:index, :show]
before_action :correct_user, only: [:edit, :update, :destroy]
def index
#reviews = Review.all
end
def show
#review = Review.find(params[:id])
end
...
class GamesController < ApplicationController
before_action :set_game, only: [:show, :edit, :update, :destroy]
def index
#games = Game.all
end
def show
end
my routes ...
reviews#index
POST /games/:game_id/reviews(.:format) reviews#create
new_game_review GET /games/:game_id/reviews/new(.:format) reviews#new
edit_game_review GET /games/:game_id/reviews/:id/edit(.:format) reviews#edit
game_review GET /games/:game_id/reviews/:id(.:format) reviews#show
PATCH /games/:game_id/reviews/:id(.:format) reviews#update
PUT /games/:game_id/reviews/:id(.:format) reviews#update
DELETE /games/:game_id/reviews/:id(.:format) reviews#destroy
games GET /games(.:format) games#index
POST /games(.:format) games#create
new_game GET /games/new(.:format) games#new
edit_game GET /games/:id/edit(.:format) games#edit
game GET /games/:id(.:format) games#show
PATCH /games/:id(.:format) games#update
PUT /games/:id(.:format) games#update
DELETE /games/:id(.:format) games#destroy
root GET / games#index
this is how im attempting to do my link_to but its showing all the games reviews in my seeds not just a single game and all its reviews...
<%= link_to 'reviews', game_reviews_path(game_id: #game.id), #reviews %>
first RoR prj on my own, any help would be great!!
thanks!
The reason is not working is because you are not using the game_id you are passing to the ReviewsController. You need to query for Reviews belonging to the specific Game.
def index
#reviews = Review.where(game_id: params[:game_id]).all
end
Related
I have these before_actions set in my Controller:
before_action :require_user, unless: :public?, only: [:show]
before_action :require_user, only: [:index, :edit, :update]
Basically I am trying execute the filter required_front_user in the show action only if the public? is false.
for the rest of actions I want the filter to be executed always.
Looks like the first before_action set up is ignored and totally override by the second set up.
Is it possible to combine both combinations using before_action statements or do I have to implement this logic in the filter it self?
Update
This also doesn't work:
before_action :require_user, if: :public?, only: [:index, :edit, :update]
before_action :require_user, unless: :public?, only: [:show, :index, :edit, :update]
I thought if public? returns true the first set up will be loaded, and if false the second set up will be loaded. It happens that only the second set up is loaded and if public? == true the before_action is never triggered
Update 2
This is what I found it works:
before_action :require_user_when_public, if: :public?, only: [:index, :edit, :update]
before_action :require_user_when_no_public, unless: :public?, only: [:show, :index, :edit, :update]
protected
def require_user_when_public
require_user
end
def require_user_when_no_public
require_user
end
Which is very ugly :/
before_action :require_user, only: require_user_before_actions
private
def require_user_before_actions
actions = [:index, :edit, :update]
actions << :show unless public?
actions
end
The most clean way I have found is:
before_action :require_user, only: [:index, :edit, :update]
before_action :require_user_when_no_public, unless: :public?, only: [:show]
protected
def require_user_when_no_public
require_user
end
I've only tested this a tiny bit so not super sure it'll work, but maybe something like:
before_action :require_user, if: ->(c) {
[:index, :edit, :update, !public? && :show].include?(c.action_name.to_sym)
}
As a possibly dumb / broken (that seems to work in a basic test for me as far as I can tell) alternative, possibly something like:
class <<self
alias_method :old_before_action, :before_action
end
def self.before_action(*callbacks)
options = callbacks.extract_options!
if options[:only] && options[:only].is_a?(Proc)
only_proc = options.delete(:only)
if_proc = ->(c) { Array(only_proc.(c)).reject(&:blank?).map(&:to_s).to_set.include? c.action_name }
options[:if] = Array(options[:if]).unshift(if_proc)
end
old_before_action(*callbacks, options)
end
before_action :require_user, only: ->(c) { [:index, :edit, :update, !public? && :show] }
I have a class like the following:
class Child < ApplicationRecord
belongs_to :father
belongs_to :mother
end
My goal is to create endpoints
base-url/father/children #get all children for father
base-url/mother/children #get all children for mother
I'm wondering what the correct way of nesting these resources would be I know I can do it one way like:
class ChildrenController < ApplicationController
before action :set_father, only: %i[show]
def show
#children = #father.children.all
render json: #children
end
...
But how can I get the same for base-url/mother/children, is this possible through nested resources?
I know I can code the routes.rb to point at a specific controller function if I need to but I would like to understand if I'm missing something, i'm unsure from reading the active record and action pack docs if I am.
The Implementation I went with is as follows:
My child controller:
def index
if params[:mother_id]
#child = Mother.find_by(id: params[:mother_id]).blocks
render json: #child
elsif params[:father_id]
#child = Father.find_by(id: params[:father_id]).blocks
render json: #child
else
redirect_to 'home#index'
end
end
...
My routes.rb file:
Rails.application.routes.draw do
resources :mother, only: [:index] do
resources :child, only: [:index]
end
resources :father, only: [:index] do
resources :child, only: [:index]
end
...
base_url/mother/{mother_id}/children #get all children for mother
base_url/father/{father_id}/children #get all children for father
I manage the authorization of users in my app with the pundit gem. Everything works fine for the user. Now I created a second devise model: Employers. I want to show a specific page to both logged in user as well as logged in employers. How do I do that?
Here is my policy for the model:
class CurriculumPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def create?
return true
end
def show?
record.user == user || user.admin
end
def update?
record.user == user || user.admin
end
def destroy?
record.user == user || user.admin
end
end
And here is my controller for the index page which I want to make accessible:
class CurriculumsController < ApplicationController
skip_before_action :authenticate_user!, only: [:new, :create, :index]
before_action :set_curriculum, only: [:show, :edit, :update, :destroy]
def index
# #curriculums = policy_scope(Curriculum).order(created_at: :desc)
if params[:query]
#curriculums = policy_scope(Curriculum).joins(:user)
.where('users.job_category ILIKE ?', "%#{params[:query]}%")
.where(
'job_category ILIKE :query', query: "%#{params[:query]}%"
)
else
#curriculums = policy_scope(Curriculum).order(created_at: :desc)
end
end
private
def set_curriculum
#curriculum = Curriculum.find(params[:id])
end
def curriculum_params
params.require(:curriculum).permit(:doc)
end
end
You can have workaround here like below for each actions
def show?
true if #user.class.table_name == "employees"
end
I am receiving this error when trying to create a new 'Pin' in IRB. For example:
irb(main):001:0> #pin = Pin.first
NameError: uninitialized constant Pin
OR
irb(main):001:0> #pin = Pin.new
NameError: uninitialized constant Pin
I must of changed something as it was working before. Unfortunately, I cannot find the error
Here is my pins controller:
class PinsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :set_pin, only: [:show, :edit, :update, :destroy]
def index
#pins = Pin.all
end
def show
#pin = Pin.find params[:id]
end
def new
#pin = Pin.new
end
def edit
end
def create
#pin = Pin.new(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render action: 'new'
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render action: 'edit'
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find(params[:id])
end
def correct_user
#pin = current_user.pins.find(params[:id])
redirect_to pins_path, notice: "Not allowed!" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description)
end
end
Here is are my associations, pin.rb
class Pin < ApplicationRecord
belongs_to :user
end
And my associations for User.rb:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
end
and my routes
Rails.application.routes.draw do
resources :pins
devise_for :users
root "pages#home"
get "about" => "pages#about"
end
irb does not automatically load your Rails environment, which is why it does not have access to your models (or helpers, or database, or anything else). However, the "rails console" is an irb session that does load all of your Rails classes, database connections, etc.
To start the rails console:
rails c
Which is shorthand for:
rails console
This starts the rails console for your development environment. You can make it connect to your test environment:
rails c RAILS_ENV=test
or to your production environment:
rails c RAILS_ENV=production
While I'm reading Rails 4 in Action, I'm trying to implement my own application, so it doesn't look same as in the book.
The book's corresponding commit is Section 7.2.3: Only admins can create or delete projects
In my case, admins can only delete the item (item corresponds to the project in the book.).
My repo https://github.com/tenzan/shop and deployed http://ichiba-demo.herokuapp.com/
The rule I want to apply is:
A regular user (you can login with staff#example.com/password) can do everything except destroy action.
Admin (admin#example.com/password) only can destroy.
To realise that I have:
In admin/items_controller.rb:
class Admin::ItemsController < Admin::ApplicationController
def destroy
#item = Item.find(params[:id])
#item.destroy
flash[:notice] = 'Item has been deleted.'
redirect_to items_path
end
private
def item_params
params.require(:item).permit(:name, :quantity)
end
end
In controllers/items_controller.rb:
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update]
def index
#items = Item.all
end
def new
#item = Item.new
end
def create
#item = Item.new(item_params)
if #item.save
flash[:notice] = 'Item has been created.'
redirect_to #item
else
flash.now[:alert] = 'Item has not been created.'
render 'new'
end
end
def show
end
def edit
end
def update
if #item.update(item_params)
flash[:notice] = 'Item has been updated.'
redirect_to #item
else
flash.now[:alert] = 'Item has not been updated.'
render 'edit'
end
end
private
def set_item
#item = Item.find(params[:id])
rescue ActiveRecord::RecordNotFound
flash[:alert] = 'The item could not be found.'
redirect_to items_path
end
def item_params
params.require(:item).permit(:name, :quantity)
end
end
In routes.rb:
Rails.application.routes.draw do
namespace :admin do
root 'application#index'
resources :items, only: :destroy
end
devise_for :users
root 'items#index'
resources :items, only: [:index, :show, :edit, :update, :new, :create] do
resources :comments
end
end
Questions:
Do I have to specify actions in the routes.rb, as I already have specified who can use what actions in their corresponding controllers? I didn't notice any change when I remove them from the routes.rb...
Am I violating DRY concept, when I specify actions in 2 places, i.e. in the routes.rb and controllers/items_controllers.rb ?
I'll be happy if you point out other places to improve to meet best practice.
PS: The subject maybe vague, please feel free to edit it.
Do I have to specify actions in the routes.rb, as I already have
specified who can use what actions in their corresponding controllers?
Yes. For instance, if you'd have only one action in items_controller.rb controller (let say show), and left
resources :items do # no specified actions
#...
end
in routes.rb it would generate all routes for items controller (for new, create, edit, destroy, update etc). But specifying actions in routes.rb you limit generated routes to only needed.
Am I violating DRY concept, when I specify actions in 2 places, i.e.
in the routes.rb and controllers/items_controllers.rb ?
No. Because you actually specify actions in controller, in routes.rb you only specify routes.
I'll be happy if you point out other places to improve to meet best
practice.
This line:
resources :items, only: [:index, :show, :edit, :update, :new, :create] # better to use %i() notation, eg only: %i(index show edit update new create)
could be written as:
resources :items, except: :destroy
Regarding your admin user - to allow only him to destroy, just check if current_user is admin. If you'll have more, than one action which is allowed to be performed by only admin, you can create before_action in controller:
before_action :check_admin?, only: %i(destroy another_action)
private
def check_admin?
# your logic to check, if user is admin
end
You can also be interested in going through Ruby style guide.
Even though you're not violating DRY directly, you're muddying up the REST architecture by moving a single entity's actions to different controllers. You don't need a specific controller or namespace for admins - you just need to assert that the user is an administrator before proceeding with the delete action.
Since you have already added the admin column to your devise model, you can move the delete action to ItemsController
def destroy
if current_user.try(:admin?)
#item = Item.find(params[:id])
#item.destroy
flash[:notice] = 'Item has been deleted.'
else
flash[:alert] = 'Only admins can delete items.'
end
redirect_to items_path
end
Your routes would be cleaner since your admin namespace would be used only for user moderation. The only route for items would be:
resources :items do
resources :comments
end