User defined routes in Rails - ruby-on-rails

There is a lot of good information on routing in Rails. I must be missing something, but I can't seem to find a good example of a Rails application that allows dynamically defined user specific routes.
For example, my application is hosted at:
www.thing.com
... and serves out user generated content.
I'd like to give the user an option to define a suffix that let's them share a somewhat customized URL to their content. For example, if a user 'joe' generates some car info they might want to make it avilable via joescars at:
www.thing.com/joescars
Maybe later they decide they want to serve it out under 'carsbyjoe' at:
www.thing.com/carsbyjoe
I can handle limiting what suffixs are valid. Is there a Rails way to codify this kind of dynamic routing?

There is a way to do this. In your config/routes file add a route that says get '/:user_route' => 'somecontroller#someaction'. You'll have to put it at the very bottom because routes are matched from top to bottom and this will match things like /users or other routes you'll likely want directed elsewhere.
Then, in your controller you can access params[:user_route] to show the appropriate content. There are a number of ways to store this custom content in your database, depending on your needs. You might have a model representing these custom routes like CustomRoute.find_by_route(params[:user_route]), or maybe each user will have a custom route so you could do User.find_by_route(params[:user_route]).custom_page and each User has one custom_page.

Related

Rails: Configure routes to attempt to find records from two classes

This is my current route configuration:
resources :organizations, path: ''
resources :users, path: ''
I want to create a similar experience to what GitHub does. When using GitHub, you can access organization and user profile pages by entering "https://github.com/#{username}"
Now, the routes configuration above leads to the obvious problem that accessing organizations works fine while accessing a user fails because Rails only considers the organizations route and does not attempt to find a user.
Note: I am using friendly id to use usernames in my URL's and also made sure that usernames are unique across both ActiveRecord classes.
How do I do what I want to do?
You can create additional controller like PageOwnerController and pass request to it:
get ':page_owner_nick', to: 'page_owners#show', as: :page_owner
In show action you can manually find desired record by params[:page_owner_nick].
Advice: it looks like you have many a lot of similar logic between users and organizations - take a look on STI. Using STI allow you to write common code easier, but at the same time to separate different logic.

How to check if a route already exists , while generating a dynamic route in rails

In my application i need to generate vanity urls for all my users like:
profilename/joinme, where profile name will be unique.
There is a problem , if the profile name is an already existing route like if:
profile_name = users then route will be users/joinme and there is already a resource with the name users.
So i want to check upon creation of these vanity urls that they does not interfere with my existing routes.
Is there an easy way to do that ?
one way that i can think of is getting the formatted output of "rake routes" in a file and then checking for the existence of that on creation of every routes.
You should create a list of banned words and check that the username doesn't match any of this words using a validation. An easy way could be:
validate :check_banned_words
def check_banned_words
if %w( admin join login help register ).include? self.username
errors.add(:username, "this is a banned word!!")
end
end
If the list of banned words is too long, put it into a yaml file, or create a model just for banned words, in that way you can add new words without redeploy your app
Instead of generating dynamic route, you can use dynamic segments in routes.
For example:
match ":username/joinme", :to => "users#joinme"
Then you can customize behavior in controller as you want.
For the reference:
Rails Routes - Dynamic Segments
Also, if you want to control the process of creating new users to prohibit the use of names that are in conflict with the existing routes, you can use the approach described in this answer.

How to handle unpredictable routes?

Assume the following paths to be legitimate and resolving:
http://test.local/wizards/home
http://test.local/wizards/wizardfest2012/dates
http://test.local/dragons/
http://test.local/dragons/blog/stop-slaying-us
http://test.local/
This is (if you couldn't tell) for a CMS that includes a blog, so the slugs would be generated by the user. I have some routes to process first for reserved namespaces (admin, for example).
I assume that the user generated routes need to be routed to a Page controller - but, I don't think pragmatically adding a line to routes.rb is efficient. My question then, is how do I process the first part of the params (in this case, wizards and dragons) to get the correct information from the model?
Here's one of my ideas - split (somehow) the first part of the slug (again, wizards and dragons and pass the rest of the slug (for example, /wizardfest2012/dates) to the model to fetch the associated content.
Any thoughts on the most efficient way to do this?
I am not sure whether I understand what you want to achieve, but maybe this is what you want:
constraints :camp => /wizards|dragons/ do
match ':camp/home' => "pages#home"
match ':camp/blog/:title' => "pages#blog"
# ...and all the routes with known components
match ':camp/*other' => "pages#other"
end
You may create a before_filter which will recognize the params[:camp] and prepare the necessary models or whatever is needed.
The other action will receive the string "wizardfest2012/dates" as params[:other]. I hope that it was what you needed.
The "Rails Routing from the Outside In" guide may be worth reading, unless you have already read it.

Basic Rails 3 Routing Question

I am trying to make some clean URLs in a Rails3 application I am working on... but I am having a hard time understanding how to (or if I even should) customise my routes to make this work.
Here is the example:
I have a list of Stores. Each store is in a category (health, sports etc). Each store has a location.
I have 2 ways I'd like to present the data. One display is a list of all the stores in a directory type structure, the other is on a map.
Ideally I'd like my URLs to work something like this:
/stores/health/map (or /stores/map/health) to show just the health stores on a map (where essentially the map parameter is effecting which view is displayed, but still using the Index controller... which using a collection in my route doesn't seem to suit)
The other URL I'd like is /stores/sports/ to show just the sports stores in a directory view (the default) for example...
I am not entirely clear how I can manipulate the routes to handle this...
Here is my current Route which isn't really doing it for me:
resources :stores do
collection do
get 'map'
end
end
On top of that, I'd like to be able to add filters without using ?query=params... so:
/stores/sports/hockey , would essentially filter out only hockey stores...
I have no issues doing this with ?query, it's just putting my params into a nicer URL that I'm trying to achieve.
The documentation does not seem to outline what I am trying to do, so Im assuming what Im trying to do is wrong.
Is this breaking REST? Am I looking at it all backwards?
Thanks for your help, JD
You might be overthinking this. :-)
If you want to route HTTP Get of 'stores/health/map' to the StoresController with an action name of, say, health_map, what you need to do is:
get 'stores/health/map' => 'stores#health_map'
Anything that is a clean URL and doesn't modify data and uses HTTP GET is RESTful. (And that is coming from a co-author of a book on REST). It is when you wish to modify data that you need to be more careful on how you use methods.
To do filtering, try something like:
get '/stores/sports/:filter' => 'stores#sports'
The value of the filter will come into your method as params[:filter]

Why do I need to work harder to make my Rails application fit into a RESTful architecture?

I started a Rails project recently and decided to use RESTful controllers. I created controllers for my key entities (such as Country) and added index, new, edit, create, show, update and delete. I added my map.resources :country to my routes file and life was good.
After development progressed a little, I started to encounter problems. I sometimes needed extra actions in my controller. First there was the search action that returned the options for my fancy autocompleting search box. Then came the need to display the countries in two different ways in different places in the application (the data displayed was different too, so it wasn't just two views) - I added the index_full action. Then I wanted to show a country by name in the URL, not by id so I added the show_by_name action.
What do you do when you need actions beyond the standard index, new, edit, create, show, update, delete in a RESTful controller in Rails? Do I need to add (and maintain) manual routes in the routes.rb file (which is a pain), do they go in a different controller, do I become unRESTful or am I missing something fundamental?
I guess I am asking, do I need to work harder and add actions into my routes.rb file for the privilege of being RESTful? If I wasn't using map.resources to add the REST goodies, the standard :controller/:action, :controller/:action/:id routes would handle pretty much everything automatically.
I would treat search as a special case of index. Both actions return a collection of resources. The request parameters should specify things like page, limit, sort order, and search query.
For example:
/resources/index # normal index
/resources/index?query=foo # search for 'foo'
And in resources_controller:
before_filter :do_some_preprocessing_on_parameters
def index
#resources = Resource.find_by_param(#preprocessed_params)
end
As for index_full and search_by_name, you might look at splitting your current controller into two. There's a smell about what you've described.
Having said that, you're absolutely right that there's no point in forcing your app to user restful routes when it doesn't deliver anything over /:controller/:action/:id. To make the decision, look how frequently you're using the restful resource route helpers in forms and links. If you're not using them, I wouldn't bother with it.
If I go beyond the standard CRUD actions with my models, I normally just add the methods as required. Searching is something I add to many controllers, but not every one, so I add it and maintain the routes normally:
map.resources :events, :collection => { :search => :get }
Moving these actions to an entirely separate controller might keep some of your controllers RESTful, but I find that keeping them in context is far more useful.
REST does not specify that you can't have additional views. No real world application is going to be able use only the supplied actions; this is why you can add your own actions.
REST is about being able to make stateless calls to the server. Your search action is stateless each time as the data so far is supplied back, correct? Your alternate display action is also stateless, just a different view.
As to if they should be manual routes or a new controller, that depends on how distinct the activity is. Your alternate view, if it provides a full set of CRUD (create, read, update, delete) operations would do well to be in a new controller. If you only have an alternate view to the data, I would just add an alternate view action.
In other words, it doesn't sound like your application is failing to be RESTful, it is more an issue of realizing that the automatically generated feature set is a starting point, not a conclusion.
In my opinion they may have gone a bit off the rails here. What happened to DRY?
I'm just getting back into Rails not having done much development with it since beta and I'm still waiting for the light-bulb to come on here. I'm still giving it a chance but if it hasn't happened for me by the end of my current project I'll probably just drop-back to the old standard routes and define the methods as I actually need them for the next one.
I won't go on to explain more about REST since I think that has been answered in this question, however I will talk a little bit about the default route.
My main problem with the default route is that if you have multiple sites using the same Rails app it can look horrible.
For example there may be controllers that you don't want people to be able to see on one app:
http://example1.somesite.com/example_2/foo/bar/1
compare this to
/:controller/:action/:id
This would go to the controller example_2/foo, action bar and id 1
I consider this to be the main flaw of Rails' default route and this is something that RESTful routes (with subdomain extensions) or only named routes (map.connect 'foo' ... ) can fix.
To remain RESTful in your design, you need to rethink what you call a resource.
In your example a show action for a search controller, (search resource) is the direction to remain restful.
In mine, I have a dashboard controller (show) and controllers for single fields of in-place ecditors (show and update)

Resources