So I am currently enrolled in a TreeHouse tutorial for Ruby on Rails but as I follow along I am modifying some things that are being done to my own liking.
However, I have come to a problem that has me scratching my head.
What I would like to do is get the user_id of a user's show page. What the tutorial has done is create a new field for devise, profile_name.
I skipped this step, because i did not want my users to be identified that way. I wanted them to be identified by their "user_id" + "first_name" + "last_name". In doing what the tutorial did, what he achieved was that he created custom routes for each individual user signed up to his site by the use of their unique profile_name. In my case it would be "user_id" + "first_name" + "last_name". If someone could shed some light on this and hopefully also point me to a resource I could potentially learn more on this subject I would really appreciate it.
Thanks for reading and have a nice day! :D
This is what they had in the tutorial.
class ProfileController < ApplicationController
before_filter :authenticate_user!, only: [:show]
def show
#user = User.find_by_profile_name(params[:id])
//#user = User.find_by_user_id(params[:id]) //my code that I thought would work
if #user
#statuses = #user.statuses.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
end
ROUTES FILE
Rails.application.routes.draw do
get '/:id', to: 'profile#show'
You'll probably need to have slashes to separate your id/first_name/last_name params. The reason being is that let's say someone's identifier ends up as 105johnsmith - well, how do you know that their first name hasn't been entered as 5joh, and their last name as nsmith, leaving their id as 10, rather than 105? You don't.
However, if you want to assume that the first digits of the identifier are in fact the user's id, then you just need to take whatever's in params[:id] and extract the integer id from the string.
So you'd have code like:
id = params[:id].scan(/\d+/).first
#user = User.find_by_profile_name(id)
If you wanted to change the name of your :id parameter, you could, but it's not required in order for it to work.
And if you wanted to go down the (more sensible) path of putting slashes between the parameters (for a url like /105/john/smith, or even /105/johnsmith) then you'd just change your routes to be either:
get '/:id/:first_name/:last_name', to: 'profile#show' # maps /105/john/smith
# or
get '/:id/:full_name', to: 'profile#show' # maps /105/johnsmith
And you could then leave the code in your controller exactly the same.
To find User ID out of the box:
In Users Show Controller: #user = User.find(params[:id])
In USERS SHOW.HTML.ERB: <p><%- #user.id %><p>
Related
I'm wondering if it's possible to edit the default Rails routing convention to fetch a specific record based on a field that is not the ID?
For instance, instead of retrieving a specific record based on ID, with the verb/url combination:
GET /users/:id
Retrieve a specific record based on username, with the verb/url combination:
GET /users/:username
I don't see why this would be a problem theoretically, as long as usernames were required to be unique, but I'm having trouble understanding how to implement it based on the Rails Routing Guide.
I have gathered that I will need to add a line to my routes.rb file, to define a singular resource, just prior to:
resources :users
However, I'm having trouble understanding the syntax to accomplish this. Any help in understanding this would be greatly appreciated.
Yes it is possible and they are called Non Restful Routes in the rails documentation
A trivial example is doing the below in your routes.rb
get ':users/:show/:username', controller: "users", action: "show"
and in your UsersController you have a show action that looks like this:
def show
if params[:id].present?
#user = User.find(params[:id])
elsif params[:username].present?
#user = User.find_by(username: params[:username])
end
end
This way you support showing by id and username, if you want do disable support for either of them, modify the if clause as you wish
I think you are looking to change the to_param method like so:
class User < ActiveRecord::Base
def to_param
"#{id} #{name}".parameterize
end
end
This would give the url as: /user/id-name. If you want to get rid of the id before the name it gets a little more complicated. If you were just to remove it, it will more than likely break since ActiveRecord needs the id first for finds.
To get around this I would suggest using FriendlyId gem: https://github.com/norman/friendly_id
There is also a RailsCast showing how to use Friendly_id but its pretty straight forward.
The routes does not care if it is an ID or username.
It is really how you find it in the controller.
Just in the user show controller:
def show
#user = User.find_by_username params[:id]
end
I have generated scaffold and created a view called "appointment"
I wanted to added a template .erb file called inbox_mail.html.erb in appointment folder.
I did setting like this.
route.rb
get '/appointments/inbox_mail'
In appointment controller
class AppointmentsController < ApplicationController
def inbox_mail
end
end
Now running the link 3000/appointments/inbox_mail
but giving rise error as,
Mongoid::Errors::DocumentNotFound in AppointmentsController#show
Problem: Document(s) not found for class Appointment with id(s) delete_appointment. Summary: When calling Appointment.find with an id or array of ids, each parameter must match a document in the database or this error will be raised. The search was for the id(s): delete_appointment ... (1 total) and the following ids were not found: delete_appointment. Resolution: Search for an id that is in the database or set the Mongoid.raise_not_found_error configuration option to false, which will cause a nil to be returned instead of raising this error when searching for a single id, or only the matched documents when searching for multiples.
Help me in Rails4...!!!!
May be this is b'z of
def set_appointment
#appointment = Appointment.find(params[:id])
end
Yes, it is because of set_appointment method. I guess you should add :id segment to your route, like
match '/appointments/delete_appointment/:id', to: 'appointments#delete_appointment', via: :get
and this should work.
Delete something shouldn't be done through GET, you should use the DELETE method. So, when you create the link with "link_to" you should do:
link_to 'Delete appointment', delete_appointment_path(#appointment.id), method: :delete
you need a route like:
delete '/appointments/delete_appointment/:id', to: 'appointments#delete_appointment'
Then rails will take care of that and do a DELETE request with the appointment's id, then on your controller you can use #appointment = Appointment.find(params[:id])
You may want some kind of validation to render a "not found" template:
def delete_appointment
unless #appointment = Appointment.find(params[:id])
redirect_to appointment_not_found_path #something_like_that
end
end
EDIT: it looks like some before_filter is messing up there too, you talked about "delete_appointment", the error say the action called is "show" and you copied the code for the action/before_filter "set_appointment", check that first
EDIT 2: you say you are not doing any delete, then use get, the important part is the :id on the url if you need to find an appointment by an ID you need that on the url. If you don't need the ID then check your before filters, I guess you have something like
before_filter :set_appointment
you may want to skip that filter on delete_appointment
before_filter :set_appointment, except: :delete_appointment
StackOverflow seems to have this style of routes for questions:
/questions/:id/*slug
Which is easy enough to achieve, both in routes and to_param.
However, StackOverflow seems to also redirect to that path when just an ID is passed.
Example:
stackoverflow.com/questions/6841333
redirects to:
stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result/
Same goes for any variation of the slug
stackoverflow.com/questions/6841333/some-random-stuff
Will still redirect to the same URL.
My question is: Is this type of redirection typically handled in the controller (comparing the request to the route) or is there a way to do this in routes.rb?
The reason I wouldn't think this possible in the routes.rb file is that typically, you don't have access to the object (so you couldn't get the slug based off the ID, right?)
For anyone interested, Rails 3.2.13 and also using FriendlyID
Ok, so I think I've got this.
I was looking into doing something with middleware, but then decided that's probably not the place for this type of functionality (since we need to access ActiveRecord).
So I ended up building a service object, known as a PathCheck. The service looks like this:
class PathCheck
def initialize(model, request)
#model = model
#request = request
end
# Says if we are already where we need to be
# /:id/*slug
def at_proper_path?
#request.fullpath == proper_path
end
# Returns what the proper path is
def proper_path
Rails.application.routes.url_helpers.send(path_name, #model)
end
private
def path_name
return "edit_#{model_lowercase_name}_path" if #request.filtered_parameters["action"] == "edit"
"#{model_lowercase_name}_path"
end
def model_lowercase_name
#model.class.name.underscore
end
end
This is easy enough to implement into my controller:
def show
#post = Post.find params[:post_id] || params[:id]
check_path
end
private
def check_path
path_check = PathCheck.new #post, request
redirect_to path_check.proper_path if !path_check.at_proper_path?
end
My || in my find method is because in order to maintain resourceful routes, I did something like...
resources :posts do
get '*id' => 'posts#show'
end
Which will make a routes like: /posts/:post_id/*id on top of /posts/:id
This way, the numeric id is primarily used to look up the record, if available. This allows us to loosely match /posts/12345/not-the-right-slug to be redirected to /posts/12345/the-right-slug
The service is written in a universal fashion, so I can use it in any resourceful controller. I have't found a way to break it yet, but I'm open to correction.
Resources
Railscast #398: Service Objects by Ryan Bates
This Helpful Tweet by Jared Fine
I am looking to find the best way to setup the routes for my app.
The application allows users to register, post jobs and apply for jobs. The issue I am having is that two routes that should really show different things are linking through to the same pages.
My routes are as follows:
resources :users do
resources :apps
end
resources :jobs do
resources :apps
end
As a result of this I end up with two paths:
users/id/apps
jobs/id/apps
What I would like to do is use the first path to show job applications that a user has completed. I would then like to use the second path to show the owner of the job the applications they have received for that job.
The issue I am having is that both paths end up at apps#index
Any advice people can offer on how to best route this would be much appreciated! :)
Both those paths are potentially fine for what you want to do. When a request comes into
users/:user_id/apps
then you'll be able to do something like this in your controller to populate the list of apps:
#apps = User.find_by_id(params[:user_id]).apps
And in the case of the other path, you'll do the same but with params[:jobs_id], e.g.
#apps = Job.find_by_id(params[:job_id]).apps
In your controller you would have some conditional code that builds #apps depending on which path the request came in via (by looking to see if :user_id or :job_id is in params)... something like this:
if params[:user_id]
#apps = User.find_by_id(params[:user_id]).apps
elsif params[:job_id]
#apps = Job.find_by_id(params[:job_id]).apps
end
or maybe refactored to...
if params[:user_id]
#user = User.find(params[:user_id])
#apps = #user.apps
elsif params[:job_id]
#job = Job.find(params[:job_id])
#apps = #job.apps
end
If you use the "resources" keyword, rails will just map to the default routes.
If you would like specific routes to map to something else, you should consider using "match."
For example:
match "users/id/apps" => "users/id/apps"
match "jobs/id/owners" => "jobs/id/owners"
This page shows a more detailed usage of it: http://guides.rubyonrails.org/routing.html
You also have the options to change the route to something else or the code itself.
Hope that helps.
This is likely something easy to accomplish, but I'm having difficulty even articulating clearly, leading me to get all sorts of semi-germaine, but not quite what I'm after posts on SO when searching.
I have a resource, we'll just use User as a simple to discuss use case. I desire SEO friendly URLs, so instead of ~/users/:id, we have ~/users/:name, where the show action on my users controllers is doing a User.find_by_name(:name). Works great.
In various parts of the app, I want to redirect the end user back to my resource. The docs seem to indicate I should do
redirect_to #user, :notice => "your page is now DIAMONDS!"'
The problem here is that this automatically appears to use the :id value of the resource, which isn't what I'm after. I'm wondering if there's a way to just define a route in my routes file which is aware of my desire to use the name property--and then just redirect to the generated route_url helper. But I'm at a loss for how to do that.
In the interim, I'm resorting to this:
flash[:notice] = "Your account is now DIAMONDS. *fist bump*"
redirect_to :action => :show , :id => #user.name
This is less than ideal to me as it's a good bit of repeated code (I have lots of UI elements that will be linking back to my resource) and because I can't seem to figure out how to include the flash as part of the redirect_to method call---every combo of curly bracing w/in the redirect_to that includes a flash bombs on me.
I don't think it's germaine to my problem specifically, but I am doing this on Rails 3 in case it does have some implication in terms of options available to me.
Sorry for the noobishness :)
Pretty simple to do. The standardish way of doing this is:
/app/models/user.rb
def to_param
"#{id}-#{name}"
end
That's nice, and gives you nicer SEO -- http://yoursite.com/users/1345-supercool .... You can tweak it a bit to remove the '1345':
/app/models/user.rb
# add_column :permalink, :string
attr_protected :permalink
before_create :set_permalink
validates_uniqueness_of :permalink
def set_permalink
self.permalink = username.parameterize
end
def to_param
permalink
end
This will give you http://yoursite.com/users/supercool and will be a permanent URL, so that if the user changes their username later, the URL will stay the same and keep search engines happy.