Mapping domain.com/user/1 to domain.com/loginName - ruby-on-rails-3

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

Related

rails routing- not going to 404 on wrong address

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

Rails Routing based on Parameters

I am working on a Rails app, and I am looking for a way to route to different actions in the controller based on the existence of parameters in the url.
For example I want website.com/model to route to model#index, however I want website.com/model?opt=dev to route to model#show. Is there some way this can be done?
Use route constraints to look at the request object and see if it has URL parameters. If you're using restful routes, you want to put this "one-off" before the restful route. Something like this:
get 'users' => 'users#show', constraints: { query_string: /.+/ }
resources :users
So what this is saying is that if you request "/users?opt=dev" then it will match your special case. Otherwise, it falls through to your normal restful route to the index action. Your model#show action will then have to know to pick up the param[:opt] and do whatever with it.
Also, note that the regex is very loose and it's simply checking for ANY param...you'll want to tighten that up to fit whatever you're trying to do.
Not strictly the same, but if you came to this post and were wondering how to do the same via a POST, then you can do it based on the request_paramters.
for your routes.rb ..
module MyConstraintName
extend self
def matches?(request)
request.request_parameters["routeFlag"] == "routeToModelShow"
end
end
match "pages/:id", :via=>:post, :controller=>"model", :action=>"show", :constraints => MyConstraintName
and in your form for example..
<%= hidden_field_tag :routeFlag, "routeToModelShow" %>

Rail3 | How to create standard route/action for ALL controllers?

Well, DRY! So i thought it should be easy to add a new action (like the existing new, edit) to all my controllers (in my case copy). But how do you setup a new route for ALL controllers?
Without going in to 'loops' (i.e. %w().each ...) inside the routes.rb ?
I mean, we want DRY right? So you don't want copy your action inside the routes file for each resource. I guess you should be able to extend the default actions/routes (index, new, edit,etc.) easy?
Thanks!
AFIK no way to do this by default. You could monkey-patch resources to include this functionality:
https://github.com/rails/rails/blob/b229bc70e50ec0887c5bb3aaaa9c6ee8af054026/actionpack/lib/action_dispatch/routing/mapper.rb#L982
...but my hunch is you would be better off re-considering whether this functionality can be created another way, since what you want to do is "off the Rails".
One option is create a CloneController#new that accepts a model and id and creates a clone. This seems like it would be drier, and wouldn't require you to pepper a gazillion "clone_article" "clone_blog" "clone_user" paths all over the place.
Obviously you would want to carefully white-list the models/ids that can be passed in.
Looking through the source there isn't a way to add to the default actions for a resource.
But, as #juwiley says, the methods resources :item is just a shortcut for creating a load of member and collection methods.
All you need to do is something like this
class ActionDispatch::Routing::Mapper
def resources_with_copy(*resources, &block)
block_with_copy = lambda do
block.call
member do
post :copy
end
end
resources(*resources, &block_with_copy)
end
end
Then in your routes.rb just say
resources_with_copy :items
resources_with_copy :posts do
member do
post :share
end
end
...

Passing :new to Rails url_for

Maybe I'm stupid but Rails provides this nifty syntax for generating URL's like so:
url_for([user, comment]) # => /users/1/comment/1
Passing :edit allows me to create something like this:
url_for([:edit, user, comment]) # => /users/1/comment/1/edit
but is there some way to do following?
url_for([:new, user, comments]) # => NoMethodError: undefined method `new_user_comments_url'
UPDATE: Added more information.
My routes.rb:
resources :users do
resources :comments
end
resources :posts do
resources :comments
end
My problem here is, that I can't use Rails auto-generated url helper (user_comments_url), because I'm sharing the views for both user comments and post comments.
There are two workarounds (but no one feels like the "Rails"-way) for my problem:
Adding logic to the view, e.g. some if conditions.
Defining my own url helpers like new_parent_comment(user_or_blog).
Ok, found a solution, but I'm not sure if this is the intended one:
url_for([:new, user, :comment]) # => '/users/1/comments/new'
url_for([:new, post, :comment]) # => '/posts/1/comments/new'
Stuck with the same problem, and found next solution (tested on Rails 5.2):
url_for([user, Comment, action: :new])
where Comment model class name.
By the way, action also could be :edit.
According to the Rails Docs url_for uses the class name of the object passed to generate the RESTful route. It also states that with nested routes it can not make this assumption correctly:
If you have a nested route, such as admin_workshop_path you’ll have to call that explicitly (it’s impossible for url_for to guess that route).
I would suggest using a named route here something like new_user_comment_path(). I am assuming you have set up your routes.rb something like:
resources :users do
resources :comments do
end
end
Additionally you can run rake routes to print out the proper names for all your routes.
Hope this helps,
/Salernost
Could this simply be a typo? I think the last line should read comment, not comments:
url_for([:new, user, comment])
(Assuming your comment variable has been defined.)

How to user defined friendly URLs in Rails 3?

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!