url_for does not work with custom route - ruby-on-rails

I have a custom route that looks like:
match 'firsttime' => 'registrations#new', :via => :get, \
:defaults => {:promotion_path => :firsttime}
The goal with the above route is to be able to have a url like http://www.mysite.com/firsttime which maps to the "new" method for the registrations controller, with the promotion for that registration being the "firsttime" promotion.
In one of my models, I have a shortcut method to try and generate this url:
class Registration < ActiveRecord::Base
include ActionView::Helpers
include ActionDispatch::Routing
include Rails.application.routes.url_helpers
belongs_to :promotion
def get_share_link()
promotion_path = promotion.url_path
url_to_share = url_for :controller => 'registrations', :action => 'new', :promotion_path => promotion_path
end
end
Calling the method get_share_links() fails with the error:
No route matches {:controller=>"registrations", :action=>"new",
:promotion_path=>"firsttime"}
What am I doing wrong, or am I even using the right method for this?

routes are used in the Controller. Stick this as a before filter in your controller instead.
Edit #1
Is this devise or another gem for authentication?
EDIT #2
Can you get to http://www.mysite.com/registrations/new?promotion=firsttime presuming it's localhost for now of course, since you are in dev mode.
Question #1
What do you mean by "In one of my models, I have a shortcut method to try and generate this url:" what are you generating this for and where is it used? mailings? It seems your route works, it's just not being made when you want it to, and it's unclear where you want it to be used.
Answer
does it work with only_path: false supplied as an option?

Related

How to handle this routing situation is Rails

i prefer writing the routes of my Rails applications by hand and i now have a situation where i am not sure what is the best way to do things. I want to have a building controller that shows a different page for every building like :
building/town_center
building/sawmill
..
Each of the above should have its own action and view page. I would normally create a route like:
scope :path => '/building', :controller => :building do
get '/view/:slug' => :view, :as => 'view_building'
end
But this only specifies a single action that would then need to call another internal controller method to redirect to the needed template to show. So, i would like your opinion, would you just specify a different route for every building(and action) explicitly ? Or just redirect in the view_building action ?
I think you are after something like this:
match "/building/:name", :to => "buildings#show", :as => :building
Then in your controller action 'show' just render template for the building name:
render :template => 'buildings/#{params[:name]}'

customize rails url with username

I want to copy the twitter profile page and have a url with a username "http://www.my-app.com/username" and while I can manually type this into the address bar and navigate to the profile page I can't link to the custom URL.
I think the problem is in the routes - here's the code in my routes.rb
map.connect '/:username', :controller => 'users', :action => 'show'
Also, I have Question and Answer models and I want to link to them with the customized URL like so:
http://www.my-app.com/username/question/answer/2210
There's nothing wrong with your route. Just remember to define it at the end, after defining all other routes. I would also recommend using RESTful routes and only if you want to have better looking URLs use named routes. Don't use map.connect. Here's some good reading about Rails routes.
Here's how this could look:
map.resources :questions, :path_prefix => '/:username' do |question|
question.resources :answers
end
map.resources :users
map.user '/:username', :controller => 'users', :action => 'show'
Just a draft you can extend.
To create urls you need to define to_param method for your user model (read here).
class User < ActiveRecord::Base
def to_param
username
end
end
I know this questions is old but it will help someone.
You could try the below. I've used it in a rails 4 project and all seems to be working great. The reason for the as: :admin is I also had a resources posts outside of this scope. It will add a admin to the helper calls e.g. admin_posts_path
scope ":username", module: 'admin', as: :admin do
get '', to: 'profiles#show'
resources :posts
end
I have used like this
In View part
portfolio.user.name,:id =>portfolio) %>
and in rout.rb
map.show_portfolio "portfolios/:username", :action => 'show_portfolio', :controller => 'portfolios'

Complex Rails Routing

I would like to do something like github has with nested urls, and like How do I route user profile URLs to skip the controller? but not really sure how to go on with it.
For example, looking at a commit they have: ':user/:repo/commit/:sha', with the controller being commit. How would I replicate this type of nested resource?
thank you :)
If commit is a RESTful controller that uses :sha instead of an id to find records. You could do this instead:
map.resource :commits, :path_prefix => ':user/:repo', :as => 'commit'
It will create the standard RESTful routes that look like http://yoursite.tld/:user/:repo/commit/:id
Again, if you'll never be translating the id portion in the url to a commit id, then there's no reason you can't use it as a :sha value.
example:
class CommitController < ApplicationController
def show
#commit = Commit.find(:first, :conditions => {:sha => params[:id]})
end
...
end
You may also want to over ride to_param in the commit model to return the sha value.
class Commt < ActiveRecord::Base
...
def to_param
sha
end
end
So that now link_to commit_url(#commit, :user => current_user, :repo => #repo) will provide a url that matches your scheme.
How about
map.connect ':user/:repo/commit/:sha', :action => :index
Or use map.resource instead of map.connect if you need a RESTful route.
In the controller, the URL information can be retrieved from params, for example params[:user] returns the username.
You can name your routes as you like, and specify which controllers and actions you'd like to use them with.
For example, you might have:
map.connect ':user/:repo/commit/:sha', :controller => 'transactions', :action => 'commit'
This would send the request to the 'commit' method in 'transactions' controller.
The other variables can then be accessed in your controller using params:
#user = User.find(params[:user])
#repo = Repo.find(params[:repo])

Ruby on Rails conditional routing

I have a site listing many jobs, but I also want each account to be able to access its jobs in one place. Thus, I am using these routes:
map.resources :jobs
map.resource :account, :has_many => :jobs
This gets me URLs like localhost/jobs/ and localhost/account/jobs. However, both seem to render JobsController::index. How can I either make a conditional in the index action (how do I access whether account/jobs or just jobs was specified in the URL?) or change the account route to render a different action? What's the proper way to do this?
You can use a block when creating your routes, and then pass a :controller parameter, like so
map.resource :account do |account|
# If you have a special controller 'AccountJobsController'
account.resources :jobs, :controller => "account_jobs"
end
It may be cleaner for you to put your controllers into a directory structure, and then you can reference them in a nested way. For example:
map.resource :account do |account|
account.resources :jobs, :controller => "accounts/jobs"
end
If you use the above snippet, you should then create a controller in app/controllers/accounts/jobs_controller.rb, which is defined like so:
class Account::JobsController < ApplicationController
##
## etc.
##
end
You can always use rake routes to check which routes have been generated and which controllers they'll use.
Adding a requirement to the resource definition allows you to pass extra parameters
map.resources :jobs
map.resource :account, :has_many => :jobs, :requirements => {:account => true}
Then params[:account] will be set if the routing url was 'http://www.mysite.tld/account/jobs' and unset if it it was 'http://www.mysite.tld/jobs'
As with all other restful routing the action depends on the context.
GET requests without an id route to index.
GET requests with an id route to show
POST requests route to create
PUT requests route to update
DELETE requests route to destroy.
If you run "rake routes" you should see something like this
account_jobs GET /accounts/:account_id/jobs/:job_id {:controller => 'jobs', :action => 'index'}
This means when your action is called via the /account/jobs route you should have an :account_id parameter. You can then do your logic switch based on the existence of this param:
if params[:account_id].nil?
...
else
...
end

How do I route user profile URLs to skip the controller?

Right now my user profile URLs are like so:
http://example.com/users/joeschmoe
And that points to the show method in the user controller.
What I'd ideally like to do is offer user profile URLs like this:
http://example.com/joeschmoe
So, what sort of route and controller magic needs to happen to pull that off?
I disagree with what jcm says about this. It's not a terrible idea at all and is used in production by the two biggest social networks Facebook and MySpace.
The route to match http://example.com/username would look like this:
map.connect ':username', :controller => 'users', :action => 'show'
If you want to go the subdomain route and map profiles to a URL like http://username.example.com/, I recommend using the SubdomainFu plugin and the resulting route would look like:
map.root :controller => 'users', :action => 'show' , :conditions => {:subdomain => /.+/}
These broad, catch all routes should be defined last in routes.rb, so that they are of lowest priority, and more specific routes will match first.
I also recommend using a validation in your User model to eliminate the possibility of a user choosing a username that will collide with current and future routes:
class User < ActiveRecord::Base
validates_exclusion_of :username, :in => %w( messages posts blog forum admin profile )
…
end
This does not make sense unless you have no controllers. What happens when you want to name a controller the same as an existing user? What if a user creates a username the same as one of your controllers? This looks like a terrible idea. If you think the /user/ is too long try making a new custom route for /u/
So your custom route would be...
map.connect 'u/:id', :controller => 'my/usercontroller', :action => 'someaction'
In routes.rb this should do the trick:
map.connect ":login", :controller => 'users', :action => 'show'
Where login is the name of the variable passed to the show method. Be sure to put it after all other controller mappings.
Well, one thing you need is to ensure that you don't have name collisions with your users and controllers.
Once you do that you, can add a route like this:
map.connect ':username', :controller => 'users', :action => 'show'
Another thing people have done is to use subdomains and rewrite rules in the web server, so you can have http://joeshmoe.example.com
In Rails 4 to skip controller from url you have to do add path: ''.
resources :users, path: '' do
end

Resources