Rails' routes: how to pass "all" request params in a redirect - ruby-on-rails-3

I want to redirect all root requests to a /pages/home url, but I want to keep all the params used in the original request.
So:
http://myserver.com/?param1=value1&param2=value2
Becomes
http://myserver.com/pages/home?param1=value1&param2=value2
There are several SO questions about passing params in a redirect but I haven't found any related to passing request's params.

The other answer works but leaves a ? at the end in case there are no parameters.
This seems to do the trick without the side effects:
root to: redirect(path: '/quick_searches/new')
Hope it helps!

# routes.rb
root :to => redirect { |params, request| "/pages/home?#{request.params.to_query}" }
Update 1
You can also play with the request.params to build the new path:
root :to => redirect { |params, request| "/pages/#{request.params[:page]}.html?#{request.params.to_query}" }

In Rails 5, this can be now be done concisely simply by rewriting ONLY the path component in the redirect (so all original query params will be retained):
# in routes.rb
root to: redirect(path: '/pages/home')
Relevant documentation link:
http://api.rubyonrails.org/classes/ActionDispatch/Routing/Redirection.html

Building on #fguillen's answer for Rails4
You need two lines in routes.rb
# in routes.rb
match "/pages/home", to: 'pages#index', via: :get
match "/", to: redirect{ |params, request| [ "/pages/home", request.env["rack.request.query_string"] ].join("?") }, via: :all
The join on an array also handles the dangling question mark at the end in case there are no parameters.

Related

Force Swagger UI To Load https path when hosted on Heroku

I have a rails 4 app with a Grape API and Swagger through the gem grape-swagger and grape-swagger-ui gems.
In dev everything works well, I load http://localhost:3000/api/swagger and the swagger header's text input along the top loads the expected url, http://localhost:3000/api/swagger_doc. This points properly to the file it seeks, swagger_doc.json.
I've pushed this app to heroku, which forces https connections. Unfortunately, when loading https://my-app.herokuapp.com/api/swagger the swagger header's text input along the top loads http://my-app.herokuapp.com/api/swagger_doc instead of loading https://my-app.herokuapp.com/api/swagger_doc (http vs https).
I've tried coming at this from the heroku side with things like:
routes.rb
unless Rails.env.development?
get "*path" => redirect("https://my-app.herokuapp.com%{path}"), :constraints => { :protocol => "http://" }
post "*path" => redirect("https://my-app.herokuapp.com%{path}"), :constraints => { :protocol => "http://" }
end
config/environments/production
config.force_ssl = false
config/environments/production
#config.force_ssl = false
And I've come at it with trying to set or manipulate the base_path attribute of add_swagger_documentation.
app/controllers/api/base.rb
base_path: "my-app.herokuapp.com",
app/controllers/api/base.rb
base_path: "http://my-app.herokuapp.com",
app/controllers/api/base.rb
base_path: = lambda do |request|
return "http://my-app.herokuapp.com"
end
app/controllers/api/base.rb
base_path: lambda { |request| "http://#{request.host}:#{request.port}" }
I recently clicked "view raw" on one of my resources and noticed that it was picking up my changes to base_path but that base_path isn't even used to populate the url in the text input in the swagger header. It seems to be generated from a js file. I'm unable to edit it and would happily accept a hack to do so as a solution. Here's that raw output:
https://gist.github.com/johnnygoodman/5fd246765dc5236fb8c4
The line of interest is:
"basePath":"http://localhost:3000/my-app.herokuapp.com"
Which would break the app if it was being populated and used, but it is not. I don't see an option in the grape-swagger gem that I can use to pass in this variable and change the path to https.
In conclusion:
I'd like the swagger text input box to load https://my-app.herokuapp.com/api/swagger_doc when I visit https://my-app.herokuapp.com/api/swagger.
Anyone know a hack to accomplish this on heroku?
I was able to work around this. I suggest:
Do not use + uninstall #gem 'grape-swagger-ui'
Use and install gem 'grape-swagger-rails' and follow the docs here: https://github.com/ruby-grape/grape-swagger-rails

Rails 3 Getting different request from same url depending on action

I am creating an ajax request and building the url as follows:
function submitYear(year){
new Ajax.Request('update_make/'+year, { method: 'get'});
}
When I am at the new action for my car_infos controller, http://localhost:3000/car_infos/new this Ajax request works fine. I get a request that says:
Started GET "/car_infos/car_infos/update_make/2011"
The route matches up and all is well. However, if there is an error in the create the url becomes http://localhost:3000/car_infos and then when my ajax request triggers I get this with a routing error:
Started GET "/update_make/2002"
No route matches "/update_make/2002"
Here is what happens in my controller when create fails:
format.html { render :action => "new" }
I understand why I am getting the routing error, because I don't have a route set up as /update_make/. Here is my route.
match 'car_infos/update_make/:year', :controller => 'car_infos', :action => 'update_make'
So two questions.
Why does my get request change when the url changes from car_infos/new to car_infos
How do I resolve this so when I create the url in the javascript it works for both cases? I don't think putting a route for /update_make is the answer. If I redirect to /new then I lose the field values and the error message.
Thanks,
You're sending your ajax request to a relative path, so if your browser location path changes the request path changes too. The browser location path could be changing because of a redirect in Rails or in javascript.
Is this a typo (the 2x "/car_infos")?
Started GET "/car_infos/car_infos/update_make/2011
You could spell out the absolute url in your javascript:
function submitYear(year){
var absoluteUrl = 'http://' + window.location.host +
'/car_infos/update_make/' + year;
new Ajax.Request(absoluteUrl, { method: 'get'});
}

Rails: how to pass a url parameter?

I need to pass the url for each post into user model so it can be shared to twitter. Right now I can pass attributes of the post, such as title and content, which is shared to twitter, but I can't seem to figure out how to pass the post url. Thanks in advance.
post.rb
after_commit :share_all
def share_all
if user.authentications.where(:provider => 'twitter').any?
user.twitter_share(self)
end
end
user.rb
def twitter_share(post)
twitter.update("#{post.title}, #{post.content}") #<--- this goes to twitter feed
end
I haven't tried or tested it but I guess you can do something like:
def share_all
if user.authentications.where(:provider => 'twitter').any?
user.twitter_share(title, content, post_url(self, :host => "your_host"))
end
end
Prior to that, in your model add this:
include ActionController::UrlWriter
This will make the url helper available in your model as well. You can read this to get more information about it.
Please try this as well (found it on this page again):
Rails.application.routes.url_helpers.post_url(self, :host => "your_host")
[EDIT]
I have just read your gist, what you should do is this instead:
## posts.rb
after_commit :share_all
def share_all
# note that I am using self inside the method not outside it.
url = Rails.application.routes.url_helpers.post_url(self, :host => "localhost:3000")
if user.authentications.where(:provider => 'twitter').any?
user.twitter_share(url)
end
end
Or:
include ActionController::UrlWriter #very important if you use post_url(..) directly
after_commit :share_all
def share_all
# if you use the url helper directly you need to include ActionController::UrlWriter
url = post_url(self, :host => "localhost:3000")
if user.authentications.where(:provider => 'twitter').any?
user.twitter_share(url)
end
end
It is very important that you get that url inside the share_all method and not outside it, because self has not the same value whether it's inside or outside. When it's inside the method, self references the instance of Post on which the share_all method is called. When it's outside it's the class Post itself.
I have tested those two variants and they work just well :).

How do I write a Rails 3.1 engine controller test in rspec?

I have written a Rails 3.1 engine with the namespace Posts. Hence, my controllers are found in app/controllers/posts/, my models in app/models/posts, etc. I can test the models just fine. The spec for one model looks like...
module Posts
describe Post do
describe 'Associations' do
it ...
end
... and everything works fine.
However, the specs for the controllers do not work. The Rails engine is mounted at /posts, yet the controller is Posts::PostController. Thus, the tests look for the controller route to be posts/posts.
describe "GET index" do
it "assigns all posts as #posts" do
Posts::Post.stub(:all) { [mock_post] }
get :index
assigns(:posts).should eq([mock_post])
end
end
which yields...
1) Posts::PostsController GET index assigns all posts as #posts
Failure/Error: get :index
ActionController::RoutingError:
No route matches {:controller=>"posts/posts"}
# ./spec/controllers/posts/posts_controller_spec.rb:16
I've tried all sorts of tricks in the test app's routes file... :namespace, etc, to no avail.
How do I make this work? It seems like it won't, since the engine puts the controller at /posts, yet the namespacing puts the controller at /posts/posts for the purpose of testing.
I'm assuming you're testing your engine with a dummy rails app, like the one that would be generated by enginex.
Your engine should be mounted in the dummy app:
In spec/dummy/config/routes.rb:
Dummy::Application.routes.draw do
mount Posts::Engine => '/posts-prefix'
end
My second assumption is that your engine is isolated:
In lib/posts.rb:
module Posts
class Engine < Rails::Engine
isolate_namespace Posts
end
end
I don't know if these two assumptions are really required, but that is how my own engine is structured.
The workaround is quite simple, instead of this
get :show, :id => 1
use this
get :show, {:id => 1, :use_route => :posts}
The :posts symbol should be the name of your engine and NOT the path where it is mounted.
This works because the get method parameters are passed straight to ActionDispatch::Routing::RouteSet::Generator#initialize (defined here), which in turn uses #named_route to get the correct route from Rack::Mount::RouteSet#generate (see here and here).
Plunging into the rails internals is fun, but quite time consuming, I would not do this every day ;-) .
HTH
I worked around this issue by overriding the get, post, put, and delete methods that are provided, making it so they always pass use_route as a parameter.
I used Benoit's answer as a basis for this. Thanks buddy!
module ControllerHacks
def get(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "GET")
end
# Executes a request simulating POST HTTP method and set/volley the response
def post(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "POST")
end
# Executes a request simulating PUT HTTP method and set/volley the response
def put(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "PUT")
end
# Executes a request simulating DELETE HTTP method and set/volley the response
def delete(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "DELETE")
end
private
def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
parameters ||= {}
process(action, parameters.merge!(:use_route => :my_engine), session, flash, method)
end
end
RSpec.configure do |c|
c.include ControllerHacks, :type => :controller
end
Use the rspec-rails routes directive:
describe MyEngine::WidgetsController do
routes { MyEngine::Engine.routes }
# Specs can use the engine's routes & named URL helpers
# without any other special code.
end
– RSpec Rails 2.14 official docs.
Based on this answer I chose the following solution:
#spec/spec_helper.rb
RSpec.configure do |config|
# other code
config.before(:each) { #routes = UserManager::Engine.routes }
end
The additional benefit is, that you don't need to have the before(:each) block in every controller-spec.
Solution for a problem when you don't have or cannot use isolate_namespace:
module Posts
class Engine < Rails::Engine
end
end
In controller specs, to fix routes:
get :show, {:id => 1, :use_route => :posts_engine}
Rails adds _engine to your app routes if you don't use isolate_namespace.
I'm developing a gem for my company that provides an API for the applications we're running. We're using Rails 3.0.9 still, with latest Rspec-Rails (2.10.1). I was having a similar issue where I had defined routes like so in my Rails engine gem.
match '/companyname/api_name' => 'CompanyName/ApiName/ControllerName#apimethod'
I was getting an error like
ActionController::RoutingError:
No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}
It turns out I just needed to redefine my route in underscore case so that RSpec could match it.
match '/companyname/api_name' => 'company_name/api_name/controller_name#apimethod'
I guess Rspec controller tests use a reverse lookup based on underscore case, whereas Rails will setup and interpret the route if you define it in camelcase or underscore case.
It was already mentioned about adding routes { MyEngine::Engine.routes }, although it's possible to specify this for all controller tests:
# spec/support/test_helpers/controller_routes.rb
module TestHelpers
module ControllerRoutes
extend ActiveSupport::Concern
included do
routes { MyEngine::Engine.routes }
end
end
end
and use in rails_helper.rb:
RSpec.configure do |config|
config.include TestHelpers::ControllerRoutes, type: :controller
end

URL Rewriting on Heroku

I have two domain names assigned to my heroku app. I want to make sure that all requests to one domain are permanently redirected to the other domain.
How can I do that on Heroku?
Assuming you are using Rails 3, you can take advantage of the new routing system.
constraints :host => "invalid.domain.com" do
match "/*path", :to => proc { |env|
req = ActionDispatch::Request.new(env)
[301, { "Location" => "http://valid.domain.com#{req.fullpath}" }, ["You are being redirected."]]
}
end
This is just an example. Feel free to refactor the lambda into a custom class.
class ApplicationController
before_filter :ensure_domain
TheDomain = 'myapp.mydomain.com'
def ensure_domain
if request.env['HTTP_HOST'] != TheDomain
redirect_to TheDomain
end
end
end
You can do this via a before_filter in the application controller - Heroku give an example at the bottom of their docs at http://docs.heroku.com/custom-domains or a contraint matched route in your application routes.rb using the redirect method.
John.