some time ago forum was created in public directory of a rails app. then forum was moved to a sub-domain.
I've created a redirect for 'domain.com/forum' => 'forum.domain.com by editing routes & creating redirect action.
My question is: how may i preserve url params (ex. 'domain.com/forum?thread1&=1' => 'forum.domain.com?thread1=1' & etc.)
My code as follows:
routes.rb:
map.forum '/forum', :controller => "application",
:action => "redirect_to_forum"
application_controller.rb
def redirect_to_forum
redirect_to "http://forum.domain.com"
end
You can try with getting request url in a hash :-> and then try to preserve your parameters,
on the top of the page use
require 'cgi'
and then get the url wherever you want to get it and use it. After getting parameters in hash u can use them to reconstruct your new url.
parameters = CGI::parse(request.url)
parameter will contain the hash of your all parameters.
Related
Hi I'm having trouble downloading multiple files with axlsx. The problem is I'm sending an array of Id's to the controller and asking it to download the report using the render command. It raises an AbstractController::DoubleRenderError. I was thinking of overriding the error but realized it's a bad idea, I don't know what else to do... Any suggestions? Thanks.
My controller code looks like this:
def download_report
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
render :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
end
end
My axlsx template:
wb = xlsx_package.workbook
wb.add_worksheet(name: "Reports") do |sheet|
wb.styles do |s|
# template code
end
end
It is the built in expectation of Rails that you would call render once per request. And, the browser is going to expect one response per request. So, you are going to have to do something else!
You can use render_to_string, and combine the results into a zip file, serving that. See the bottom of this response.
Or, you could create a single spreadsheet and have each user's report show up on their own worksheet.
Or, on the client side, you could use javascript to request each spreadsheet and download each one separately.
The zip one would be something like this code, which uses render_to_string, rubyzip, and send_data:
def download_report
compressed_filestream = Zip::ZipOutputStream.write_buffer do |zos|
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
content = render_to_string :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
zos.put_next_entry("user_#{user_id}.xlsx")
zos.print content
end
end
compressed_filestream.rewind
send_data compressed_filestream.read, :filename => 'download_report.zip', :type => "application/zip"
end
Axlsx requires rubyzip, so you should have it already. And you probably want to lookup each user and use their name for the spreadsheet, unless you have it otherwise.
I want to have a GET route that will query an API to collect data, and then redirect to a POST with that data to save to the DB. For example:
get '/query/twitter/company/:name' do
get_number_of_tweets_for_day( params[:name] )
end
POST '/company/tweets/' do
company.tweets.create(:date => time_now, :count => num_tweets)
end
How do I set the parameters from the data returned by the function in the GET route, and pass them to the POST route so I can save to the DB?
Your code has two completely separate endpoints, which are called in separate API requests. You could make it a single POST request, i.e.:
post '/company/:name/tweets/' do
num_tweets = get_number_of_tweets_for_day( params[:name] )
company.tweets.create(:date => time_now, :count => num_tweets)
end
As an alternative, for persisting data between subsequent requests, you would typically use sessions:
enable :sessions
get '/query/twitter/company/:name' do
session['num_tweets'] = get_number_of_tweets_for_day( params[:name] )
end
post '/company/tweets/' do
company.tweets.create(:date => time_now, :count => session['num_tweets'])
end
A redirect is not possible from GET to POST, because browsers will keep the request method the same after a redirect. You would have to make your first route a POST too.
I'm new to Rails. I'm developing a store builder.
What I want
I want a root level url for each shop.
http://greatsite.com/my-shop-name
My Solution
shop_controller.rb
def show
if params[:url]
#shop_ref = params[:url]
#shop = Shop.where(:url => #shop_ref).first
else
#shop_ref = params[:id]
#shop = Shop.find(#shop_ref)
redirect_to "/" + #shop.url
return
end
if #shop.nil?
render 'show_invalid_shop', :object => #shop_ref and return
end
render 'show' => #shop
end
def create
#shop_url = (0...8).map{65.+(rand(25)).chr}.join.downcase
#shop = Shop.new(:url => #shop_url)
if #shop.save
redirect_to "/" + #shop.url
else
render :action => "new"
end
end
routes.rb
...
resources :shops
match ':url' => 'shops#show', :constraints => { :url => /[a-z|0-9]{4,30}/ }
...
The Problem
Crap Performance. (It's ugly as sin too, of course.)
Every time someone creates a new shop (which is one click from our home page), it creates a new shop and does a redirect. In New Relic, I see this is killing performance - a lot of time is spent in "Request Queuing".
Is there any neater and faster way of achieving what I want?
I'm not sure why the redirects would be causing such a headache, but:
Could you do something like:
Create the shop via an AJAX call.
On a successful create via AJAX render the show view, and return the html "string".
Replace the contents of the page with JS, and use pushstate to update the URL.
Might be useful to look at: http://pjax.heroku.com/
It's not exactly pretty, but if redirects are really that bad it might help?
I wouldn't recommend this, as it violates the REST principle...
But you could have create call/render the show action after it's done it's object creation (just like you do with "new" when it fails). That would eliminate the redirect but still show the same content as if it had.
There's a lot of reasons why you wouldn't want to do this. I'd look for performance improvements in other places first.
I have an application controller method called redirect back or default which is used to redirect users to the page they were requesting after login
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
I would like to be able to optionally add URL parameters (for some analytics tracking) to the url, but am not sure of the best way. I'd like to change the method signature to this
def redirect_back_or_default(default, params=nil)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
and somehow attach the params to the existing URL. Is there a standard ruby or ROR way to do this? I could obviously brute force check to see if there is a query string as part of the URL with regex and manually build the query string, but I was hoping there is an easier standard way of doing this.
From here:
To pass parameters with redirect_to
you simply add them. Like ...
redirect_to :controller => 'another', :action => 'def', :param1 => 'some', :param2 => 'thing', :param => 'else'
standart approach
def redirect_to_back_or_default(default = "/")
back = case request.env["HTTP_REFERER"]
when request.fullpath
default
when nil
default
else
:back
end
redirect_to back
end
I am trying to use OmniAuth to handle the OAuth flow for a small-ish Sinatra app. I can get 37signals Oauth to work perfectly, however I'm trying to create a strategy for Freshbooks Oauth as well.
Unfortunately Freshbooks require OAuth requests to go to a user specific subdomain. I'm acquiring the subdomain as an input and I then need to persistently use the customer specific site URL for all requests.
Here's what I've tried up to now. The problem is that the new site value doesn't persist past the first request.
There's to to be a simple way to achieve this but I'm stumped.
#Here's the setup -
def initialize(app, consumer_key, consumer_secret, subdomain='api')
super(app, :freshbooks, consumer_key, consumer_secret,
:site => "https://"+subdomain+".freshbooks.com",
:signature_method => 'PLAINTEXT',
:request_token_path => "/oauth/oauth_request.php",
:access_token_path => "/oauth/oauth_access.php",
:authorize_path => "/oauth/oauth_authorize.php"
)
end
def request_phase
#Here's the overwrite -
consumer.options[:site] = "https://"+request.env["rack.request.form_hash"]["subdomain"]+".freshbooks.com"
request_token = consumer.get_request_token(:oauth_callback => callback_url)
(session[:oauth]||={})[name.to_sym] = {:callback_confirmed => request_token.callback_confirmed?,
:request_token => request_token.token,
:request_secret => request_token.secret}
r = Rack::Response.new
r.redirect request_token.authorize_url
r.finish
end
Ok, here's a summary of what I did for anyone who comes across this via Google.
I didn't solve the problem in the way I asked it, instead I pushed the subdomain into the session and then I overwrite it whenever the site value needs to be used.
Here's the code:
#Monkeypatching to inject user subdomain
def request_phase
#Subdomain is expected to be submitted as <input name="subdomain">
session[:subdomain] = request.env["rack.request.form_hash"]["subdomain"]
consumer.options[:site] = "https://"+session[:subdomain]+".freshbooks.com"
super
end
#Monkeypatching to inject subdomain again
def callback_phase
consumer.options[:site] = "https://"+session[:subdomain]+".freshbooks.com"
super
end
Note that you still have to set something as the site when it's initialised, otherwise you will get errors due to OAuth not using SSL to make the requests.
If you want to see the actual code I'm using it's at: https://github.com/joeharris76/omniauth I'll push the fork up to the main project once I've battle tested this solution a bit more.