I'm using this to_param
def to_param
[id, [first_name.parameterize,last_name.parameterize].join("_")].join("-")
end
to produce an address that look like
http://www.example.com/users/34-joe_smith
but if I change the address to
http://www.example.com/users/34-joe_notsmith
It still directs me to user with id 34.
How can I enforce it to go to 404 page or root_path?
Since your search is being performed on the id and not the user's name it won't really care what value succeeds the id in the url. You can extend your show action to find the user based on id, first name and last name but I don't think that's going to end up being a very clean solution.
Instead, I highly recommend you take a look at the Friendly_ID gem which will enable you to generate pretty URLs with non-pk fields such as user's name easily with very little configuration. There's an excellent railscast on the subject: pretty urls with friendlyid
Related
I've created my REST API based on Michael Scott from the Office, so far if you go onto the main part of the API it displays all the quotes /api/v1/quotes/ and you can get individual ones if you add an id number such as /api/v1/quotes/5,but I want users to be able to get a random quote when they put in a GET request.
This would preferably be done with an extra part of the URL such as /api/v1/quotes/random.
I've looked at a lot online but I can't figure this out. Do I put my code into the quotes controller and create another function or do I have to put this somewhere else such as routes.db? Also do I put in the SQL function of RANDOM or is there a better and more efficient way to randomise it. My database only has 50 records in it and it's done in mysql2.
Thanks in advance.
It's my first time posting here as usually I hate asking for help as I think I can always figure it out myself but I'm extremely new to Ruby so I'm fairly clueless on how to solve this. If you need me to clarify my question then just let me know.
You can do that in Model as well as controller.
Model:
class Model < ApplicationRecord
def self.random
Model.limit(1).order("RANDOM()").first
end
end
Controller: In method show
def show
render json: Model.find_by(id: params[:id]) || Model.random
end
I hope that helpful.
I would configure a quotes/random collection route:
# in config/routes.rb
resources :quote do
get 'random', on: :collection
end
and use a controller method like this:
# in controllers/quotes_controller.rb
def random
#quote = Quote.order('RAND()').first
render :show
end
Please note: Returning a single random row from a table is database specific – for MySQL
ORDER BY RAND() LIMIT 1
seems to be the way to go.
Here's a breakdown of the situation -
Desired behavior: user can sign_up through Devise gem by providing their email address only. Web app generates and db stores a temporary password, unknown to user.
Logic: This is meant to be a 'gradual sign-up' process to a web application still under construction (we want to begin capturing potential users without providing access to the web app as it's still in partial development). The email is to be used for communication purposes until final release.
Problem: Devise gem requires user to input email && password during sign_up process. We've found no obvious way to circumvent the dual requirement. User failing to provide password generates error.
Potential solution: After searches and many tries, this seems to be the closest alternative (found here).
generated_password = Devise.friendly_token.first(8)
user = User.create!(:email => email, :password => generated_password)
Question: While this potential solution makes sense, we're REALLY new to this and don't understand in which file to place this code within the Devise configuration, and how to call it.
All help appreciated.
LM
OK, I kept digging until I found what I was looking for (here) - maybe it can help you too.
In your model:
before_validation :generate_password, :on => :create
def generate_password
o = [('a'..'z'), ('A'..'Z'), (0..9)].map{|i| i.to_a}.flatten
self.password = self.password_confirmation = (0..16).map{ o[rand(o.length)] }.join if self.password.blank?
end
Now i have something like this
http://myapp.com/pages/1
http://myapp.com/pages/2
http://myapp.com/pages/3
http://myapp.com/pages/4
And each page belong to one user
What i need is to each user to set it's own custom name for the page.
I was thinking of using the friendly_id gem http://norman.github.com/friendly_id/
but I don't find any method to directly edit the slug to set a custom friendly url
how should i proceed?
FriendlyID is a great gem.
It shouldn't be hard to implement user defined page URL.
Create table pages with user_id and link
class User < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :user
has_friendly_id :link # link is name of the column whose value will be replaced by slugged value
On the page#new you add an input for the link attribute.
Alternatively, you could set friendly_id on title or something else with :use_slug => true option. This way FriendlyID will take the title and modify it so it doesn't have and restricted characters. It will use it's own table to store slugs. Use cached_slug to increase performanse.
Updated
To give users a choice whether they wan't to set a custom link, you could do this:
Set friendly_id on the link field without slugs..
Make a virtual attribute permalink so you could show it in your forms.
In the before_filter, check whether the permalink is set.
If it is, write it to the link field.
If it's not, write title to the link field.
FriendlyID uses babosa gem to generate slugs. If you decide to use it as well, this is how your filter could look like:
protected
def generate_link
#you might need to use .nil? instead
self.link = self.permalink.empty? ? make_slug(self.title) : make_slug(self.permalink)
end
def make_slug(value)
value.to_slug.normalize.to_s #you could as well use ph6py's way
end
Adding to_param method to one of the models should help:
def to_param
"#{id}-#{call_to_method_that_returns_custom_name.parameterize}"
end
Hope this is what you are looking for :)
I am not using the friendly_url gem and am not sure whether my way is efficient. But it works fine for me.
I have a model called Node with id and friendly url field called url_title.
My routes.rb file:
resource 'nodes/:url_title', :to => 'Nodes#view'
nodes_controller.rb
class NodesController <ActiveController
def view
#node = Node.find_by_url_title(:params(url_title))
end
end
And use the #node variable to populate your view.
Now, whenever I type www.example.com/nodes/awesome-title , it takes me to the proper page. One argument against this can be need to create an index on a non-primary field. But I think that might be required for better performance even in the friendly_url gem. Also, the non-primary field url_title needs to be unique. Again, this might be required even for correct working for friendly_url .
Feel free to correct me if I am wrong in these assumptions.
There are a variety of ways, you can achieve this-
1) using Stringex
2) sluggable-finder
3) friendly_id
A complete step by step methodology with reasons for each to be used can be found out here. Happy reading!
so i want to make a page the displays a Phrase by select (initially) at random from the database. on that page i want a <%= link_to "next"%> but i was wondering if there was an efficient way to ensure that the next record exists
currently I'm using just
# #phrase is current phrase
<%= link_to "next", phrase_path( Phrase.find( #phrase.id + 1 ) ) %>
yes, i know i should call a #next from the controller, or better yet have a next method in the model to call #phrase.next, but this is for illustrative purposes.
but this often turns up an ActiveRecord::RecordNotFound error because some phrases have been deleted from the db (due to moderation, error, etc...). I could rescue from this and loop that till it works in the controller then pass it or something, but that seems like a bad solution, and not particularly 'railsy'
is there a convenient solution to this anyone has found
figured it out
based on this link which is a little outdated, uses named_scope from back in rails 2. I first rewrote it using the new rails 3 scope style, but then just changed it to a method. just used
def next
Phrase.where("id > ?", self.id).order("id ASC").first
end
def previous
Phrase.where("id < ?", self.id).order("id DESC").first
end
Try creating a next/previous scope on your model, as suggested in http://steve.dynedge.co.uk/2010/01/13/random-previous-and-next-entries-from-active-record-models-using-offset/
This will allow you to do something like:
Phrase.next(5) or Phrase.next(#phrase.id)
Why don't you create a method in the controller called next and pass in the current record id. It would be trivial from there to redirect the user back to the show page for that next resource.
If you are deadset on creating the link in advance, look into creating a helper method to find the next record that exists and make it available in your views. Then you could call that whenever you needed the id of the next available record.
Something like will_paginate might be of help too. I know your page size is just one, but the essence of what you're doing is pagination.
Has anyone tried before using subdomain-fu to change domain.com/user/1 to domain.com/UserName ?
Are there any other plugins to make this happen on rails 3?
You can do this by "overwriting" the to_param method in your User model.
Assuming you are using a User model, as you stated put some code like this in it:
#cleans up the url so it's purdy.
def to_param
loginName # you probably want login_name or just name, but whatever it's your code
end
So notice that you now have to change your User#show controller call though, since you really are doing a
User.find_by_name(params[:id]
Lastly, we should be able to change the routes so that the last route in your routes.rb file says:
match ':id' => 'users#show'
What you want is called pretty URLs. Take a look here: Pretty (dated) RESTful URLs in Rails