Dry repeated methods and routes in Rails - ruby-on-rails

I'm new to ruby/rails and I have this situation with 3 resources which uses comments provided by the acts_as_votable gem. The thing is, I have added the necessary methods and routes in all of the resources, resulting in a complete non-dry aberration.
My routes are:
Rails.application.routes.draw do
root to: 'home#index'
get 'home/index', to: 'home#index'
resources :users
get 'thinga/tagged/:tag', to: 'thinga#tags', as: 'tagged_thingas'
get 'thingb/tagged/:tag', to: 'thingb#tags', as: 'tagged_thingbs'
resources :thinga do
member do
get :like
get :dislike
get :unvote
end
end
resources :thingb do
member do
get :like
get :dislike
get :unvote
end
end
resources :thingc, only: [:create, :update, :destroy] do
member do
get :like
get :dislike
get :unvote
end
end
devise_for :users, path: 'auth',
:controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
end
In the controllers the repeated code is
def like
#thinga = Thinga.find(params[:id])
#thinga.liked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def dislike
#thinga = Thinga.find(params[:id])
#thinga.disliked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def unvote
#thinga = Thinga.find(params[:id])
#thinga.unvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
Can someone help me to learn how to dry off this cases please?. Thank you.

Try rails routes concerns
http://edgeguides.rubyonrails.org/routing.html#routing-concerns
And I think you'll be able to do this
concern :votable do
member do
get :like
get :dislike
get :unvote
end
end
resources :thinga, :thingb, :concerns => :votable
resources :thingc, :concerns => :votable, only: [:create, :update, :destroy]
For the controllers - Make a VotablesController and have the ThingasController or ThingbsController subclass it. You can call a before action to set the #votable instance variable and have the subclasses set the variable. The shared methods will be in one place.
class VotablesController < ApplicationController
before_action :set_votable
def like
#votable.liked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def dislike
#votable.disliked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def unvote
#votable.unvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
end
in the subclasses (this is an example for thinga)
class ThingasController < VotablesController
def set_votable
#votable = Thinga.find(params[:id])
end
end

Ruby provides a lot of scope for using code to write code (metaprogramming), so you should be able to write a method that will do the job of adding the repeated routes for you. Perhaps the following code will run:
Rails.application.routes.draw do
def create_votes_resources(name, options = {})
resources name, options do
member do
get :like
get :dislike
get :unvote
end
end
end
create_votes_resources :thing_a
create_votes_resources :thing_b
create_votes_resources :thing_c, only: [:create, :update, :destroy]
end

Related

Rails Resources new and edit routes redirecting to root page instead of their respective routes

Basically, I'm trying to update the layout of my portfolio/new form but whenever I type in 'localhost:3000/portfolios/new' I get redirected to my home page. Same with 'localhost:3000/portfolios/(:id)/edit'
My routes are below:
Rails.application.routes.draw do
#Devise Routes
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
#homepages routes
get 'about-me', to: 'pages#about'
get 'contact', to: 'pages#contact'
# blog
resources :blogs do
member do
get :toggle_status
end
end
#portfolio
resources :portfolios, except: [:show]
get 'portfolio/:id', to: 'portfolios#show', as: 'portfolio_show'
get 'react-items', to: 'portfolios#react'
# setting root path --> ex: localhost:3000/
root to: 'pages#home'
end
Here is my controller:
class PortfoliosController < ApplicationController
before_action :set_portfolio_item, only: [:edit, :update, :show, :destroy]
layout "portfolio"
access all: [:show, :index, :react], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
def index
#portfolio_items = Portfolio.all
end
# custom scope
def react
#react_portfolio_items = Portfolio.react
end
def show
end
def new
# new portfolio item is initialized.
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Portfolio Item was successfully created.' }
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'Portfolio Item was successfully updated.' }
else
format.html { render :edit}
end
end
end
def destroy
# destroy the record
#portfolio_item.destroy
# redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Record was removed.' }
end
end
private
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
technologies_attributes: [:name]
)
end
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
end
So overall I'm not sure why it's doing that. When I do rails routes I can see the correct paths but when I input them in the browser, they do not work.
Any help would be greatly appreciated.
I think you had reverted access to create/update/edit/destroy.
Remove except condition from access_all for user create/update/edit/destroy
when user trying to opening a page which doesn't has permission to view, it will be redirect_to its root_path by default.

No route matches ... missing required keys

I have two controllers and models Projects and Schemas. Schemas belongs_to projects. Projects has_many schemas. I am looking for http://localhost:3000/projects/SLUG-PROJECT/schemas/SLUG-SCHEMA.
Following is my SchemaController code:
class Projects::SchemasController < ApplicationController
before_action :set_schema, only: [:show, :edit, :update, :destroy]
before_action :set_project, only: [:index, :show, :new, :edit, :update, :destroy]
def index
#schemas = Schema.all
end
def show
end
def new
#schema = Schema.new
end
def edit
end
def create
#schema = Schema.new(schema_params)
respond_to do |format|
if #schema.save
format.html { redirect_to project_url(#schema.project_id), notice: 'Schema was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #schema.update(schema_params)
format.html { redirect_to project_url(#schema.project_id), notice: 'Schema was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#schema.destroy
respond_to do |format|
format.html { redirect_to project_url(#schema.project_id), notice: 'Schema was successfully destroyed.' }
end
end
private
def set_schema
#schema = Schema.find(params[:id])
end
def set_project
#project = Project.friendly.find(params[:project_id])
end
def schema_params
params.require(:schema).permit(:number, :identification, :reference, :name, :description, :author, :controller, :priority, :notes, :status, :cycle, :slug, :project_id)
end
end
This is my code:
respond_to do |format|
if #schema.update(schema_params)
format.html { redirect_to project_url(#schema.project_id), notice: 'Schema was successfully updated.' }
else
format.html { render :edit }
end
It works for index and show pages, but I get the following error for update, edit, and destroy:
ActionController::UrlGenerationError in Projects::SchemasController#update
No route matches {:action=>"show", :controller=>"projects", :id=>nil} missing required keys: [:id]
Can anybody help me figure out what is going on?
What you’re looking for is nested routes. In this case you could include this route declaration:
resources :projects do
resources :schemas
end
In addition to the routes for projects, this declaration will also route schemas to an SchemasController. The schema URLs require a project:
/projects/:project_id/schemas/:id
This will also create routing helpers such as project_schemas_url and edit_project_schema_path. These helpers take an instance of Project as the first parameter: project_schemas_url(#project).
And remember to always instantiate schemas on an existing project, say:
#project.schemas.build

Can't redirect Devise sign page to show profile

I'm hoping someone can help. I am using the Devise gem for registering and signing in users. I have a Profile controller. When an existing user logs in, I want them to be diverted to the Profile's show.html.erb page in order to view their profile. I would expect this would be done under the Sessions controller but it doesn't seem to do anything
The Sessions controller code is:
class Registrations::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
protected
def after_sign_in_path_for(resource)
profile_path(resource)
end
However, when a user registers, the redirect works successfully under the Registrations controller below:
class RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
protected
def after_sign_up_path_for(resource)
new_profile_path(resource)
end.
I also want to have a link to the users Profile page when they are logged in but when I do it throws up the following error
application.html.erb code for the link is below (I have tried a number of different variables in place of the '#profile' but with no success)
<li><%= link_to 'Show Profile', profile_path(#profile), :class => 'navbar-link' %></li>
The error I receive is:
ActionController::UrlGenerationError in Profiles#index
No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]
My routes (which I'm not sure are setup correctly:
Rails.application.routes.draw do
resources :profiles
get 'profiles/:id', to: 'profiles#show'
get '/profiles/new' => 'profiles#new'
get '/profiles/edit' => 'profiles#edit'
get '/profiles/index' => 'profiles#index'
root to: 'pages#index'
devise_for :users, :controllers => { :registrations => "registrations" }
Lastly, my Profile controller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#search = Profile.search(params[:q])
#profiles = #search.result(distinct: true)
end
def show
#profile = Profile.find(params[:id])
end
def new
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Your Profile was successfully created' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entry }
end
end
end
def edit
#profile = Profile.find(params[:id])
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profile_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
def set_profile
#profile = Profile.find(params[:id])
end
private
def profile_params
params[:profile][:user_id] = current_user.id
params.require(:profile).permit(:full_name, :contact_number, :location, :makeup_type, :bio, :user_id, :image)
end
end
Any help is most appreciated.
Ok so there are two problems:
Redirecting after sign in
Url generation error in the application layout
Redirecting after sign in
You need to add the controller to your routes definition (like you have registrations.
devise_for :users, controllers: { registrations: "registrations", sessions: 'registrations/sessions' }
Url generation error in the application layout
I assume that the profile model is associated with the user (e.g. profile belongs_to user, or maybe user has_one profile). I also assume that you want to have a link for the current user's profile.
If that is the case then you could most likely do something like this:
<%= if current_user %>
<li>
<%= link_to 'Show Profile', profile_path(current_user.profile), :class => 'navbar-link' %>
</li>
<% end %>
Otherwise, you should set #profile in some before_action in the application controller or in any controller that uses the application layout.
In your application controller you want something like this
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(user)
profile_path(current_user)
end
#user is the model name for example that you created with devise
end

Rails: Application.html.erb suddently stopped rendering

I was doing something completely different with my app, when suddently my application.html.erb stopped rendering in my views. I even took back all the changes I had made and that did not fix the problem. What can have happened? I've searched everywhere and tried different things, nothing works. Everything else works fine. Here are the files with the changes.
application_controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
end
def createRound
#gamerounds = Gameround.all
#gameround = Gameround.new({endtime: 'John Appleseed', active: true})
respond_to do |format|
if #gameround.save
format.html { redirect_to #gameround, notice: 'Gameround was successfully created.' }
format.json { render :show, status: :created, location: #gameround }
else
format.html { render :new }
format.json { render json: #gameround.errors, status: :unprocessable_entity }
end
end
end
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def admin_user
#admin_user = User.find_by(username: 'Admin')
end
protected
def authenticate_user
if session[:user_id]
# set current user object to #current_user object variable
#current_user = User.find session[:user_id]
return true
else
redirect_to(:controller => 'sessions', :action => 'login')
return false
end
end
def save_login_state
if session[:user_id]
redirect_to(:controller => 'sessions', :action => 'profile')
return false
else
return true
end
end
end
game_session controller
class GamesessionsController < ApplicationController
before_action :set_gamesession, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user, :except => [:index, :show, :new ]
layout :application
# GET /gamesessions
# GET /gamesessions.json
def index
#gamesessions = Gamesession.all
end
# GET /gamesessions/1
# GET /gamesessions/1.json
def show
end
# GET /gamesessions/new
def new
#gamesession = Gamesession.new
unless !session[:user_id]
if !current_user.admin?
redirect_to '/play'
end
end
end
# GET /gamesessions/1/edit
def edit
end
# POST /gamesessions
# POST /gamesessions.json
def create
Gamesession.delete_all
#gamesession = Gamesession.new(gamesession_params)
createRound
end
# PATCH/PUT /gamesessions/1
# PATCH/PUT /gamesessions/1.json
def update
respond_to do |format|
if #gamesession.update(gamesession_params)
format.html { redirect_to #gamesession, notice: 'Gamesession was successfully updated.' }
format.json { render :show, status: :ok, location: #gamesession }
else
format.html { render :edit }
format.json { render json: #gamesession.errors, status: :unprocessable_entity }
end
end
end
# DELETE /gamesessions/1
# DELETE /gamesessions/1.json
def destroy
#gamesession.destroy
respond_to do |format|
format.html { redirect_to gamesessions_url, notice: 'Gamesession was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_gamesession
#gamesession = Gamesession.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def gamesession_params
params.require(:gamesession).permit(:players, :flares, :aliens, :gamesetup, expansion:[], level:[])
end
end
config.routes
Rails.application.routes.draw do
resources :gamerounds do
resources :currentplayers
end
resources :gamesessions
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# Artikkel, Alien liste
resources :expansions do
resources :aliens
end
resources :users
# You can have the root of your site routed with "root"
root 'gamesessions#new'
get "signup", :to => "users#new"
get "login", :to => "sessions#login"
post "login_attempt", :to => "sessions#login_attempt"
get "logout", :to => "sessions#logout"
get "profile", :to => "sessions#profile"
get "setting", :to => "sessions#setting"
get "play", :to => "gamesessions#index"
get "aliens", :to => "aliens#index"
#match ':controller(/:action(/:id))(.:format)'
# Example of regular route:
# get 'products/:id' => 'catalog#view'
# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
# Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Example resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', on: :collection
# end
# end
# Example resource route with concerns:
# concern :toggleable do
# post 'toggle'
# end
# resources :posts, concerns: :toggleable
# resources :photos, concerns: :toggleable
# Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
end
You are defining the initialize method in your controller without calling super.
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
super
end

Rails association new_path error

I have two models post and topic in my rails app
class Post < ActiveRecord::Base
#relation between topics and post
belongs_to :topic
#post is valid only if it's associated with a topic:
validates :topic_id, :presence => true
#can also require that the referenced topic itself be valid
#in order for the post to be valid:
validates_associated :topic
end
And
class Topic < ActiveRecord::Base
#relation between topics and post
has_many :posts
end
I am trying to create association between both of them.
I want multiple post corresponding to each topic
I have used nested routes
Rails.application.routes.draw do
# nested routes
resources :topics do
resources :posts
end
resources :userdetails
devise_for :users, :controllers => { :registrations => "registrations" }
My Post controller looks like
class PostsController < ApplicationController
# before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :has_userdetail_and_topic, :only =>[:new, :create]
# GET /posts
# GET /posts.json
#for new association SAAS book
protected
def has_userdetail_and_topic
unless(#topic =Topic.find_by_id(params[:topic_id]))
flash[:warning] = 'post must be for an existing topic'
end
end
public
def new
#post = #topic.posts.build
###topic = Topic.find(params[:topic_id1])
end
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
##topic.posts << #post
##current_user = current_user.id
#current_user.posts << #topic.posts.build(params[:post])
##post = Post.new(post_params )
##post.userdetail_id = current_user.id
#Association functional between topic and post
#Class variable used
###topic.posts << #post
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:topic_id,:issue, :description, :rating, :userdetail_id)
end
end
I am trying to navigate from topics/index via code <td><%= link_to 'Write', new_topic_post_path(#topic) %> </td>
but when i try to go at localhost:3000/topics]
I am getting error
No route matches {:action=>"new", :controller=>"posts", :topic_id=>nil} missing required keys: [:topic_id]
Can any body tell me about this error, as i am new to rails please clearly specify answer.
And I have one more doubt, please tell me if i am doing association between topic and post incorrectly.I have confusion about this line of code -
#topic.posts << #post
What the error missing required keys: [:topic_id] is telling you is that you need to provide a hash with the key topic_id:
<%= link_to 'Write', new_topic_post_path(topic_id: #topic) %>
Passing a resource as to a route helper only works for the id param:
<%= link_to #topic, topic_path(#topic) %>
Is a kind of shorthand for:
<%= link_to #topic, topic_path(id: #topic.to_param) %>
Addition:
#prcu is also correct. The #topic record needs to be saved to the database. Records which are not saved do not have an id since the database assigns the id column when the record is inserted.
You also need to set the #topic instance variable in PostsController:
#topic = Topic.find(params[:id])
This is commonly done with a before filter:
before_filter :set_topic, only: [:new]
def set_topic
#topic = Topic.find(params[:id])
end
The same also need to be done in TopicsController#index.
#topic is not set or it's not persisted. You can not use topic not saved to db in this helper.

Resources