I'm trying to build a link shortener. The intended behavior is that on the first page (new) the user inserts his long link and presses a button, then he gets redirected to an another page called result, where a preset message will be waiting for him, along with both his short and long link.
I'm struggling with controllers, however, as no matter what I do something always comes wrong. Right now my controller looks like this:
class UrlsController < ApplicationController
def new
#short_url = Url.new
end
def create
#short_url = Url.new(url_params)
if #short_url.save
flash[:short_id] = #short_url.id
redirect_to "/urls/result"
else
render action: "new"
end
end
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
def result
end
private
def url_params
params.require(:url).permit(:url)
end
end
And the routes.rb:
Rails.application.routes.draw do
resources :urls, :only => [:show, :new, :create, :result]
get 'urls/result' => 'urls#result'
root to: redirect('/urls/new')
end
When I submit the link, however, rails returns the following error:
Couldn't find Url with 'id'=result
Extracted source (around line #17):
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
It seems I don't understand the logic behind it. What's going wrong? Isn't the show bit supposed to be a redirect that happens when I click the shortified link?
Rails routes have priority in the order they are defined. Since your SHOW route declaration is before get 'urls/result' => 'urls#result' the url gets matched as /urls/id=result.
Simply move your custom route above the resources block or use a collection block.
resources :urls, :only => [:show, :new, :create, :result] do
collection do
get 'result'
end
end
Using the collection and member blocks tells Rails to give priority to the routes inside over the normal CRUD actions such as show.
Related
I am a part of a team that is creating a chess app in Rails 5. I have a controller named "chess_pieces.rb". In there I have the following code:
def update
#piece = ChessPiece.find(params[:id])
#game = Game.find_by_id(#piece.game_id)
#move = #piece.moves.all
#piece.update_attributes(pieces_params)
if #piece.valid?
redirect_to game_path(#game)
else
render :edit, status: :unprocessable_entity
end
update_moves
end
def update_moves
puts "The piece has been updated!"
#piece = ChessPiece.find(params[:id])
#piece.moves.update(count: 19991)
#piece.save
puts #piece.moves.count
end
As you can see the update method, is updating a piece when it is moved, specifically the x_position and y_position on the chess board. Now, how do I make the method update_moves update the attributes of a table called moves which is associated with the chess_piece.rb model. I am calling this method in the update method after a pieceĀ“s location on the board is updated.
I want to update the count of how many moves the piece has made + some other stuff.
My routes are the following:
Rails.application.routes.draw do
devise_for :users
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'games#home'
resources :games do
resources :pieces, only: [:show, :update]
patch 'update_moves', on: :member
end
end
Is the relationis is (piece has_many moves) ?
If so, I don't think you can update all moves of a piece with #piece.moves.update(count: 19991) . You should update them individually with an iteration or a map.
Note: If you are trying to add a new move record to the piece then you should use #piece.moves.build(count: 19991)
If the relation is one to one then you should try replacing #piece.moves.update(count: 19991) with #piece.moves.count = 19991 ?
In my Rails routes.rb file I'm wanting to do something like the following.
get '/:id' => 'pages#show'
get '/:id' => 'articles#show'
So that if a visitor types in
http://www.example.com/about-this-site
The pages controller in the above example would get first shot at handling it. Then if not, the next controller in line would get a shot.
REASONs for wanting to do this:
1) I'm trying to port my Wordpress site over without establishing new urls for all my pages and blog posts. As it stands, all of my blog post files and pages are accessed directly off the root uri '/' folder.
2) Because I'm not able to, it's a learning thing for me. But, I want to do it without a hack.
How about redirecting to the second controller from your first controller?
in PagesController
def show
unless Page.find_by(id: params[:id])
redirect_to controller: :articles, action: :show, id: params[:id]
end
end
in ArticlesController
def show
# Handle whatever logic here...
end
Edit
If you really don't want to redirect then you can consolidate the logic into a single action:
def show
if Page.find_by(id: params[:id])
render :show
elsif Article.find_by(id: params[:id])
render controller: :articles, action: :show
else
# Handle missing case, perhaps a 404?
end
end
However, I'd recommend using a redirect if possible. It's a cleaner solution and keeps your controller code isolated.
When I go to the characters controller, show action, all the normal params[:id] is as how it should be according to REST.
In the show view, I render a partial. In that partial, I have a link that goes to the vote_socionics action. This action is defined under a socionics_votes module, which gets included by the characters controller. (I have it set up this way because I have other controllers that also include this module).
My problem is that when I click on this link, and it goes to the set_votable private method within the socionics_votes_module.rb file, the params[:id] is no longer present. Using pry, I found that it actually turns into params[:character_id]
Questions:
1) Why does this happen (is it because it goes to a "different" controller, even if it's a module?)
2) How do I work around this? I would think that it would be more elegant to have it be params[:id], instead of having to do an if-else to account for both keys.
characters_controller.rb
class CharactersController < ApplicationController
include SocionicsVotesModule
def show
#character = Character.find(params[:id])
end
characters/show.html.haml
= render partial: 'votes/vote_socionics',
locals: { votable: #votable, votable_name: #votable_name, socionics: #socionics }
_vote_socionics.html.haml
= link_to content_tag(:div,"a"), send("#{votable_name}_vote_socionics_path", votable, vote_type: "#{s.type_two_im_raw}"),
id: "vote-#{s.type_two_im_raw}",
class: "#{current_user.voted_on?(votable) ? 'voted' : 'not-voted'}",
method: :post,
data: { id: "#{s.type_two_im_raw}" }
socionics_votes_module.rb
module SocionicsVotesController
extend ActiveSupport::Concern
included do
before_action :set_votable
end
private
def set_votable
votable_constant = controller_name.singularize.camelize.constantize
#votable = votable_constant.find(params[:id]) # This is where it fails, since there is no params[:id], and rather, params[:character_id]
end
def set_votable_name
#votable_name = controller_name.singularize.downcase
end
routes.rb
concern :socionics_votes do
post 'vote_socionics'
end
resources :characters, concerns: :socionics_votes
resources :celebrities, concerns: :socionics_votes
resources :users, concerns: :socionics_votes
The URL of the link in the partial when hovered over.
localhost..../characters/4-cc/vote_socionics?vote_type=neti
Something like .find(params[:id] || params[:"#{#votable_name}_id"]) didn't work, and seems silly.
You need to add the vote_socionics route as a member of the resource:
concern :socionics_votes do
member do
post 'vote_socionics'
end
end
This way the id parameter gets set correctly
In my rails app, I have a namespace in my route file,
namespace :account do
resources :activities
end
My controller is
class Account::ActivitiesController < Account::AccountController
before_action :find_activity, only: [:show, :edit]
def index
#activities = Activity.all
end
def show
end
private
def find_activity
#activity = Activity.find(params[:id])
end
def activity_params
params.require(:activity).permit(:name, :description)
end
end
In my index view I'n trying to access the show page by do this:
= #activities.each do |activity|
= link_to "show", account_activity_path(activity)
When I'm running rake route, I get this result:
account_activities GET /account/activities(.:format) account/activities#index
POST /account/activities(.:format) account/activities#create
new_account_activity GET /account/activities/new(.:format) account/activities#new
edit_account_activity GET /account/activities/:id/edit(.:format) account/activities#edit
account_activity GET /account/activities/:id(.:format) account/activities#show
PATCH /account/activities/:id(.:format) account/activities#update
PUT /account/activities/:id(.:format) account/activities#update
DELETE /account/activities/:id(.:format) account/activities#destroy
When I'm taping directly localhost:3000:/account/activities/1, I'm going to the right page, but when I click on the show link in my index view, I'm getting this error:
No route matches [GET] "/account/undefined"
I'm using rails 4, and everything worked fine until today. I don't see what's happen so if you have any ideas, could be great
Thanks a lot
For anyone else that stumbles upon this issue, hopefully this will help. Check your javascript events!
I had a shared JS file between two HTML pages. Two different people were working on each page and both people attached a CHANGE event handler to an element ID.
$("#site").change(function() {
var url = $(this).data('url');
window.location = url;
}
Both events were executing on each page, causing the UNDEFINED route.
I have an account/settings page people can visit to update their account. It's a singular resource, so they can (or should) only be able to update their own account settings. I'm running into a weird URL format when there are form errors displayed.
If they are on /account/settings/edit and try to submit the form with errors (not a valid email address, for example) they are redirected to /account/settings.1 where it shows them what went wrong (in our example, not a valid email address).
Everything "works" but I was wondering why there is a .1 being appended to the URL. I figured they would be sent back to account/settings or account/settings/edit where they can correct the error. Am I doing something wrong?
routes.rb
namespace :account do
resource :settings, :only => [:show, :edit, :update]
end
settings_controller.rb
def edit
#account = Account.find(session[:account][:id])
end
def update
#account = Account.find(session[:account][:id])
if #account.update_attributes(params[:account])
redirect_to account_settings_path
else
render 'edit'
end
end
rake routes
edit_account_settings GET /account/settings/edit(.:format) account/settings#edit
account_settings GET /account/settings(.:format) account/settings#show
account_settings PUT /account/settings(.:format) account/settings#update
Make sure you generate your paths using edit_account_settings_path, NOT edit_account_settings_path(#user). For singular resources you shouldn't pass in a resource, because, as you say, there is only one of them.