routes.rb:
match 'first/#!/:name' => 'first#first'
first_controller.rb:
class FirstController < ApplicationController
def first
#name = params[:name]
end
end
But the #name variable is nil when I render the url: http://localhost:3000/first/#!/sayuj
Please help
Anything after the first # in the URL is not (usually) sent back to the server; it's used on client side only.
So the URL http://localhost:3000/first/#!/sayuj in the client will actually call the URL http://localhost:3000/first/ on server side.
See the following posts for more info:
http://code.google.com/web/ajaxcrawling/docs/getting-started.html
http://www.webmonkey.com/2011/02/gawker-learns-the-hard-way-why-hash-bang-urls-are-evil/
http://dannythorpe.com/2011/02/09/side-effects-of-hash-bang-urls/
http://rc3.org/2011/02/09/hash-bang-urls-and-overuse-of-ajax/
Jits is correct that the # in the url will drop the rest of the url, also, I believe your route is incorrect, it should look like:
match 'first/:name', :to => 'first#first'
documentation is at Engine yard rails 3 routes.
Related
I would like to redirect from a directory to a subdomain in Ruby on Rails.
As it is, it goes to slug.example.com. How can I redirect it to xxx.example.com?
# slug = 'xxx'
get '/users/:slug', to: redirect(subdomain: 'slug', path: '')
You have to use a block as documented in https://guides.rubyonrails.org/routing.html#redirection and https://api.rubyonrails.org/v6.1.4/classes/ActionDispatch/Routing/Redirection.html#method-i-redirect.
But proably hte easiest way is to subclass the existing OptionRedirect class:
class SubdomainFromSlugRedirect < ActionDispatch::Routing::OptionRedirect
# something that won't appear in your routes
SUBDOMAIN_TOKEN = 'qwertyuiopasdfghjklzxcvbnm'
def options
super.merge(subdomain: SUBDOMAIN_TOKEN)
end
def path(params, _request)
super.sub(SUBDOMAIN_TOKEN, params[:slug])
end
end
and then use it in the router
get '/users/:slug', to: redirect(SubdomainFromSlugRedirect)
StackOverflow seems to have this style of routes for questions:
/questions/:id/*slug
Which is easy enough to achieve, both in routes and to_param.
However, StackOverflow seems to also redirect to that path when just an ID is passed.
Example:
stackoverflow.com/questions/6841333
redirects to:
stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result/
Same goes for any variation of the slug
stackoverflow.com/questions/6841333/some-random-stuff
Will still redirect to the same URL.
My question is: Is this type of redirection typically handled in the controller (comparing the request to the route) or is there a way to do this in routes.rb?
The reason I wouldn't think this possible in the routes.rb file is that typically, you don't have access to the object (so you couldn't get the slug based off the ID, right?)
For anyone interested, Rails 3.2.13 and also using FriendlyID
Ok, so I think I've got this.
I was looking into doing something with middleware, but then decided that's probably not the place for this type of functionality (since we need to access ActiveRecord).
So I ended up building a service object, known as a PathCheck. The service looks like this:
class PathCheck
def initialize(model, request)
#model = model
#request = request
end
# Says if we are already where we need to be
# /:id/*slug
def at_proper_path?
#request.fullpath == proper_path
end
# Returns what the proper path is
def proper_path
Rails.application.routes.url_helpers.send(path_name, #model)
end
private
def path_name
return "edit_#{model_lowercase_name}_path" if #request.filtered_parameters["action"] == "edit"
"#{model_lowercase_name}_path"
end
def model_lowercase_name
#model.class.name.underscore
end
end
This is easy enough to implement into my controller:
def show
#post = Post.find params[:post_id] || params[:id]
check_path
end
private
def check_path
path_check = PathCheck.new #post, request
redirect_to path_check.proper_path if !path_check.at_proper_path?
end
My || in my find method is because in order to maintain resourceful routes, I did something like...
resources :posts do
get '*id' => 'posts#show'
end
Which will make a routes like: /posts/:post_id/*id on top of /posts/:id
This way, the numeric id is primarily used to look up the record, if available. This allows us to loosely match /posts/12345/not-the-right-slug to be redirected to /posts/12345/the-right-slug
The service is written in a universal fashion, so I can use it in any resourceful controller. I have't found a way to break it yet, but I'm open to correction.
Resources
Railscast #398: Service Objects by Ryan Bates
This Helpful Tweet by Jared Fine
I've run into a weird problem and after a bunch of research can't get any closer. I've got several forms that upload files via Carrierwave. When I upload the information, part of the route gets cut off (I think).
For example, I have a multi-part form submitting to:
https:/domain/programs/223/add_file as POST
but on submission I get the error
No route matches [POST] "/223/add_file"
even though what's in my address bar is the complete route. And if submit the complete route as a GET request it works fine. When I run rake routes the route shows up just fine.
Here is a subset of my route:
resources :programs do
match "add_file" => "programs#add_file"
If it matters, I'm running Rails 3.2.2 with Passenger on Apache. The problem only happens on this production server, never in development.
Any ideas? I'm stuck on this one as it effects multiple routes and I've tried defining a custom route just for that form with no luck.
Update:
When I remove multi-part => true or the file_field_tag from the form it fixes the problem. It's still an issue but seems to be less about routing than about the form with file uploads.
Create passenger_extension.rb in the lib folder with this code:
Passenger 3
module PhusionPassenger
module Utils
protected
NULL = "\0".freeze
def split_by_null_into_hash(data)
args = data.split(NULL, -1)
args.pop
headers_hash = Hash.new
args.each_slice(2).to_a.each do |pair|
headers_hash[pair.first] = pair.last unless headers_hash.keys.include? pair.first
end
return headers_hash
end
end
end
Passenger 5
module PhusionPassenger
module Utils
# Utility functions that can potentially be accelerated by native_support functions.
module NativeSupportUtils
extend self
NULL = "\0".freeze
class ProcessTimes < Struct.new(:utime, :stime)
end
def split_by_null_into_hash(data)
args = data.split(NULL, -1)
args.pop
headers_hash = Hash.new
args.each_slice(2).to_a.each do |pair|
headers_hash[pair.first] = pair.last unless headers_hash.keys.include? pair.first
end
return headers_hash
end
def process_times
times = Process.times
return ProcessTimes.new((times.utime * 1_000_000).to_i,
(times.stime * 1_000_000).to_i)
end
end
end # module Utils
end # module PhusionPassenger
And then in 'config/application.rb' do:
class Application < Rails::Application
...
config.autoload_paths += %W(#{config.root}/lib)
require 'passenger_extension'
end
And then restart a webserver.
NOTICE: I'm not sure that this doesn't break any other functionality so use it on your own risk and please let me know if you find any harm from this approach.
One issue here is you're not specifying whether the route is defined on the collection or a member. Which one of these is the correct route?
programs/:id/add_file
programs/add_file
You should construct your routes like this:
resources :programs do
post 'add_file', :on => :member
end
or
resources :programs do
member do
post 'add_file'
end
end
The above will take post requests at programs/:id/add_file and send them to ProgramsController.add_file with the params[:id] as the program id.
If you want this on the collection, you could do:
resources :programs do
post 'add_file', :on => :collection
end
or
resources :programs do
collection do
post 'add_file'
end
end
This would take post requests at programs/add_file and send them to ProgramsController.add_file, but no params[:id] would be set.
In general you should always specify whether routes are on the collection or member, and you should specify which verb a route should accept (ie use 'get' or 'post' etc. instead of 'match').
Try the above and see if that solves your problem, if not please let me know and I'll take another look.
I think you may need to add
:via => [:post]
to your route specification. It seems odd that it'd work on development and not on production, but as I understand rails routing, the matcher that you've added is only going to respond to get.
Try changing your match to
match "add_file" => "programs#add_file", :via => [:post]
Also, based on the answer just submitted by Andrew, you're probably better off using the member specifier to be explicit about the fact that the operation is happening on a particular Program with a particular id, and not the collection. It also should save some code in your add_file method which is probably working hard to get the id parameter from the url.
I think I'm running across a conflict due to names:
Two models: store coupon
Url needed that will display coupons: http://localhost/coupons/:store_name ('coupons' is written in the url, not replaced with anything)
Controller name: coupons_controller
Here is what I have in my routes right now:
match '/coupons/:store_name' => 'coupons#index', :as => :stores
When I try to do redirect stores_path(store) in another controller, I get this error:
No route matches {:controller=>"coupons"}
Any clues? I'm new to rails so I bet it's a silly mistake.
UPDATE
Is there a central place to tell the dynamic _path() functions to use a specific url structure? i.e. Instead of having to do the following everywhere:
redirect_to stores_path(:store_name => store.store_name)
Instead using just:
redirect_to stores_path(store)
yes you can, redefine to_param in your model:
class Store < ...
def to_param
store_name
end
end
redirect_to stores_path(:store_name => store)
should work if it doesn't (cannot confirm right now), you should be able to do the (little hacky)
redirect_to stores_path+"?store_name=yourstorename"
Doing it the restful way, you should probably have something like this (in your routes):
resources :stores do
resources :coupons # this will give you e.g. /stores/:store_id/coupons for the coupons#index action
end
If you want to use the store name instead of the ID, just search SO for using "slug" or have a look here: getting a 'name' based URL in RESTful routes instead of an id based url or ID + Slug name in URL in Rails (like in StackOverflow)
I need to get url info in my plugin's module.
request.request_uri is unavailable.
Has ruby/rails an analog of $_SERVER['REQUEST_URI'] as php?
For example:
module MyPlugin
module Routing
def self.getOpts
# HIRE I NEED TO ANALYZE URL and return hash with resulting param
return {controller: :divisions, action: :show, id: 11, as: :current}
end
end
end
# extend routing
module ActionDispatch::Routing
class Mapper
def my_rout
match 'articles', MyPlugin::Routing.getOpts
end
end
end
# In config/routes.rb
Rails::application.routes.draw do
my_rout
end
That's what I need for example:
We get an url http://mysite.ru/slug_division_1/slug_division_2
division with id 2 have in DB table a field 'handler' with value 'any_controller#any_action'
In MyPlugin::Routing i'm doing analyze the url path and get from DB the value 'any_controller#any_action'
MyPlugin::Routing.getOpts return params {controller: :any_controller, action: :any_action, id: 2, as: :current}
From ActionDispatch::Routing.Mapper.my_rout we set new rout like this
match 'slug_division_1/slug_division_2', {controller: :any_controller, action: :any_action, id: 2, as: :current}
Just a little hack.
How to make the request_uri a global variable in rails?
1) Add to folder 'vendor/plugins/myplugin/lib/myplugin' file request_global.rb with folowing code:
module Rack
class MethodOverrideWithParams < Rack::MethodOverride
def call(env)
$request = Rack::Request.new(env) # $request is global vriable
super(env)
end
end
end
2) In vendor/plugins/myplugin/lib/myplugin.rb add:
require 'myplugin/request_global'
3) In config/application.rb add:
config.middleware.swap 'Rack::MethodOverride', 'Rack::MethodOverrideWithParams'
4) VoilĂ , $request is now available from anywhere point the Rails application!
In my case it's useless because doing every time a database query for each $request to get the handler need to reload the routes (Rails::Application.reload_routes!). It degrades the performance. Defining all possible routes is more profitable even if these routes a few thousand. Reload the routes occurs only if someone edited the divisions.
Yes, try using request.request_uri.
request.request_uri is the way to find the information you want. If you don't have access to it then you need to add a parameter to your function and call it from some place that does have access.
From memory Controllers do have access to the request_uri.
I think what you actually want is just one controller/action route in your table that accepts any paths, and in the controller action do the lookup and call the relevant function. Check out path globbing (using the '*' character in Rails 2.3, not sure if you're using Rails 3), and see if that fits what you are doing.
Then what you'll have is a single route that takes a long path, the controller and action breaks that path up into its own parts, performs the lookup, and calls the appropriate function (or other controller/action).
However I have to say a few words about a few things that I think you're doing wrong.
Unless you're doing something very tricky and quite out of the ordinary, don't store the controller and action in the database. That's what the ruby routes are for themselves. If you need to store these in a database then it sounds to me like you're doing it wrong.
You shouldn't hard code an id number into a route, let that be a variable that gets set. So instead of hard coding /some/thing/23 to match 'some/thing/23', instead make the route match 'some/thing/:id', and you'll get :id => 23 automatically.