How to use a friendly id only in one namespace - ruby-on-rails

I'm using the gem friendly_id in rails 4 to make nice urls for my user's product listings. It works by overriding the to_param. Now it is working wonderfully in the Shop namespace which is the public facing part of my site however in the Admin namespace I'd rather be using the regular ids because I don't need them there and I'd rather the urls be shorter.
I thought this would be easy but after digging it actually seems somewhat complicated. The to_param, because it's part of the model, has no real concept of what controller it is being called in. So the only option I see is to override url_for so it uses id in the Admin namespace. I'm not sure how to do that and if that really is the best coarse of action because messing with url_for seems a little dangerous.

With FriendlyId5 finders are not overwritten by default.
friendly_id :foo, use: :slugged # you must do MyClass.friendly.find('bar')
# or...
friendly_id :foo, use: [:slugged, :finders] # you can now do MyClass.find('bar')
This way, I guess you can use the first option, and depending on the namespace call
MyClass.friendly.find('params[:id]')
or
MyClass.find('params[:id]')

You might want to use friendly_id in case if you do not want to mess with to_params. It is easier to implement to make excellent friendly urls that are both machine and human friendly
http://kapilrajnakhwa.com/blogs/user-friendly-urls-with-friendly_id-gem-in-rails-4

Related

Rails named route with dynamic segments URL generation from model

Ruby on Rails 4.2+ only, please!
I've been looking all over for tips on how to make URLs pretty in Rails, and I'm struggling to see a solution I like.
What I want:
Hypothetical example: given Topic, Course, etc. models that have a bunch of fields (including URL-friendly slugs), I want to be able to
# ../routes.rb
# Match urls of the form /edu/material-engineering. These are read-only
# public URLs, not resources.
get 'edu/:slug', to: 'education#topic', as: :learn_topic
get 'edu/course/:id/slug', to: 'education#course', as: :learn_course
...
# I also have admin-only resource-oriented controllers for managing
# the content, but that's separate.
namespace :admin do
resource :topic
resource :course
...
end
# ../some_view.html.erb
# Generate URLS like this:
<%= link_to topic.name, learn_topic_path(topic) %>
<%= link_to course.name, learn_course_path(course) %>
What I don't want:
Messing with to_param. That's a dirty hack and completely breaks separation of concerns.
Resource/RESTful routing. There are no CRUD operations here other than "read."
link_to 'text', course_path(id: course.id, slug: course.slug). This completely defeats the purpose of not requiring views to know what params are required to generate a URL for a course.
EDIT: I know FriendlyId exists, but I'm precisely trying to understand how this sort of thing can be done and what the mechanics are, so that's not a solution for now.
There has to be a way to tell the named route helper topic_path(topic) to take the required parameters in the route (e.g, :slug, :id, whatever else) from the topic model object.
Anybody know? Thanks!
The best I've been able to come up with: just override the *_path helpers with my own implementation.
If you know a way to make the default helpers work, please chime in!
This problem boils down to one issue: the auto-generated *_path and *_url helpers don't give me the flexibility I want. What I want them to do is trivial, so without another option, I can just write my own:
module ApplicationHelper
def learn_topic_path(topic)
"/edu/#{topic.slug}"
end
...
end
Writing a few _path/_url helper overrides avoids all kinds of complication, and allows you to keep out of to_param, avoid including new plugins, etc.
One could probably go another step forward and generate the static components of the route from known routing rules, as well as infer what attributes one needed to extract from a model if the dynamic segment names line up to the model attribute names, but that starts to break down once you do more complicated things or add multiple models (e.g., 'edu/:topic_slug/:course_slug').
The big downside to doing this is that you now have to update routes in two places every time you change them: the route definition itself in routes.rb as well as the corresponding route helper in application_helper.rb. I can live with that for now.
You can use FriendlyId gem to achieve that.
Here's the link:
https://github.com/norman/friendly_id/blob/master/README.md
Let me know if you have questions.

Things to change when using username slug/short URL in rails

I'm pretty new at rails, so forgive me if I'm overlooking simple things or the rails way. My objective is to completely replace URLs of the form
/users/1
with
/username
for all purposes. (I think exposing IDs scaffolding publicly is like walking around with a bone sticking out of your arm.) But implementing seems a little more complicated than I expected. This seems to change the entire way rails indexes and looks up users, rather than just substituting a lookup method.
Although I've kind of gotten this to function using the to_param override in my user.rb file, I've read this means I'll have indexing problems down the road when using params([:username]), and I'm not sure how it will impact my
(a) session model at new user creation, and
(b) #User usage in the user/show.html.erb file.
So, I've either consulted the following pages (or asked the questions):
Ruby on rails routing matching username
customize rails url with username
Routing in Rails making the Username an URL:
routing error with :username in url
Correct routing for short url by username in Rails
rails3, clean url, friendly_id, sessions
The major issues I'd like to understand from this question:
What functionality do I lose by transitioning to this? That is, what things currently "just work" in rails that I'll have to address and rewrite if I pursue this replacement?
As a practice, is this something better to replace with friendly_id? My concern here is that creating a slug column in my DB identical to the username seems a little non-DRY and makes me uncomfortable, and I'd rather avoid dependencies on external gems where possible.
What does my users#show need to look like?
You should check out Friendly ID. Makes doing what you're trying to do incredibly easy.
https://github.com/norman/friendly_id
There's a Railscast for it, too.
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=asciicast
If your username contains a special characters like #, -, . and got an error that says "No route matches" then you need to filter its route. See below:
match "/user/:username" => 'users#show', :as => :profile, :username => /[\.a-zA-Z0-9_#-]+/
After working around this for a couple weeks, I'd say the best answer as of Aug 2, 2012 is that if you do this, you violate many rails conventions and rip apart the very fabric of time and space itself.
Ugly scaffolding in the URLs is a necessary part of rails' approach to RESTfulness.

Friendly URLs in Github

How has Github managed to get friendly URLs for representing repos of users? For a project called abc by username foo, how do they work around with a URL like: http://github.com/foo/abc. Are they fetching the abc model for the DB from the title in the URL (which sounds unreasonable as they are modifying the titles). How are they transferring the unique ID of the abc repo which they can fetch and show in the view?
The reason I ask is that I am facing a similar problem of creating friendlier URLs to view a resource. MongoDB's object IDs are quite long and make the URL look horrific. Is there a workaround? All the tutorials that demonstrate CRUD (or REST) URLs for a resource always include the object's unique ID(e.g. http://mysite.org/post/1 or http://mysite.org/post/1/edit. Is there a better way to do it?
Not having seen their code, I couldn't tell you exactly how they do it, but if you're using Rails there are at least two Ruby gems that will give you similar results:
Take a look at Slugged and friendly_id
http://github.com/foo/abc is a unique repository identifier (for that repo's master branch). I'd assume that somewhere they have a table that looks like:
repository-id | user-id | project-id
and are just looking up based on user and project rather than repository-id.
You'd need to do some domain-specific mapping between internal and user-friendly ids, but you'd need to make sure that was a 1:1 mapping.
See this rails cast on methods, gems and solutions to common problems you might get while modifying the application to use friendly urls.
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=asciicast
(although Ryan Bates deserves the rep+ for this)
I mocked a structure like this using FriendlyID and Nested Resources.
Essentially, use friendly ID to get the to_param-ish slugs in your routes, then set up nested resources. Using GitHub as an example:
routes.rb
resources :users do
resources :repositories
end
Then in your controller, say, for repositories, you can check the existence of params[:user_id] and use that to determine the user from the route. The reason I check for existence is because I did something like (roughly):
/myrepositories/:repository_id
/:user_id/:repository_id
So my controller does:
def show
#user = params[:user_id] ? User.find(params[:user_id]) : current_user
end
I followed this tutorial here to get started with this same project.
This is called URL rewriting if the web server does it (such as Apache), and routing when it happens in a web application framework (such as Ruby on Rails).
http://www.sinatrarb.com/intro#Routes
http://httpd.apache.org/docs/current/mod/mod_rewrite.html

url optimization in rails?

Hello I want to create seo optimize url in rails.Same like done in stackoverflow.
Right now this is my url
http://localhost:3000/questions/56
I want to make it something like this:-
http://localhost:3000/questions/56/this-is-my-optimized-url
i am using restful approach.
is there any plug-in available for this.
I know you asked for a plugin, but a dead simple approach is just to override the to_param method in your model. You can just append the seo name to the id.
For example:
class Question < ActiveRecord::Base
#has attribute name.
def to_param
"#{id}-#{name.parameterize}"
end
end
The path/url helpers will then generate a path like so:
show_question_path(#question)
>> /questions/12345-my-question-name
You do not need to do anything to your routes.
Your controller will remain Question.find(params[:id]) as the param will have to_i called on it, which will strip off the name and just return the id.
I highly recommend looking into the friendly_id plugin. The FriendlyId Guide is a great place to start. I have been using this in production for a number of months and it works great.
Using the cached slugs feature makes this a very scalable solution. Additionally I'd recommend using it combination with stringex if you deal with non-ASCII characters.

How to create search engine friendly url for rails models?

I looked into this plugin called acts_as_permalinkable at github and found it to be useful. Do you know of any other plugin that generates search engine friendly urls for ruby on rails models in a better way?
friendly_id might do the trick for you.
acts_as_urlnameable does the trick as well. I haven't used permalinkable, so I can't say that it is better. Does it have some specific deficiencies that concern you?
It lives at: http://code.helicoid.net/svn/rails/plugins/acts_as_urlnameable/
there is always just to_params in your model which can display basically whatever you want. Just note that your methods in that model will have to change, when you use find.
e.g. in the post.rb file
def to_param
name.parameterize
end
and in your posts_controller.rb methods that usually pick up the params[:id] call, you just have to change them to:
#post = Post.find_by_name(params[:id])
no plugin, no fuss and still pretty urls.

Resources