[Ruby on Rails]Redirect to RESTful URL - ruby-on-rails

I am working on a movie rails project and I have to redirect the url to be RESTful, but I have no idea how to make it done. The following information may be useful:
I fail to: GET /movies when selecting a movie rating should redirect to a RESTful route
rake routes:
Prefix Verb URI Pattern Controller#Action
root GET / movies#index
movies GET /movies(.:format) movies#index
POST /movies(.:format) movies#create
new_movie GET /movies/new(.:format) movies#new
edit_movie GET /movies/:id/edit(.:format) movies#edit
movie GET /movies/:id(.:format) movies#show
PATCH /movies/:id(.:format) movies#update
PUT /movies/:id(.:format) movies#update
DELETE /movies/:id(.:format) movies#destroy
The UI of the webpage
Some descriptions of what I have to do: (generally saying if the user intentionally deletes some attributes from the url, I have redirect it to the correct url):
To be RESTful, we want to preserve the property that a URI that results in a sorted/filtered view always contains the corresponding sorting/filtering parameters.
The code I have implemented to do the task:
if !(params.has_key?(:sort_by)) #check the url has this parameter
if session[:sort_by] #has cookie
redirect_to movies_path(:sort_by => session[:sort_by]) #redirect using cookie, not sure need to add :ratings
else
session[:sort_by] = "id" # default sorting
end
end
if !(params.has_key?(:ratings))
if session[:ratings]
redirect_to movies_path(:ratings => session[:ratings]) # error: AbstractController::DoubleRenderError
else
session[:rating] = {"G"=>"1", "PG"=>"1", "PG-13"=>"1", "R"=>"1"}
end
end
:sort_by is the attribute to control how is the movies sorted, e.g. session[:sort_by] = "date" means sorted on the release date.
:ratings is something like: "ratings"=>{"G"=>"1", "PG-13"=>"1", "R"=>"1"}
An example of URL: /movies?ratings%5BG%5D=1&ratings%5BPG%5D=1&ratings%5BPG-13%5D=1&ratings%5BR%5D=1&sort_by=title
if I type something like .../movies, the url should be generated from cookie(if any). But I am having some problems:
for example we check that sort_by is missing, and there is session for sort_by, do we still need to supply session[:ratings] to movies_path()? And what to do if it is the first time I come to this website (no session[:ratings]/session[:sort_by]) but I still need the RESTful URL(all checked boxes are 'clicked' by default)
Some more instructions:
So you should redirect when parameters are missing in URL, instead of in URL + session.
It means that when ‘sort_by’ is not in the URL, but in the session, you should complete this URL with session data, and redirect to the complete URL (so fill in if ‘ratings’ is also missing).
Now what if a parameter is missing in both URL and session? You should rely on some initialization rules. For ‘ratings’, you should check all ratings; and for ‘sort_by’, you can set the keyword to ‘id’, which is how items are sorted by default (the order of movies in the array returned by Movie.all).

Something like this should work
def index
session[:sort_by] = if params[:sort_by].present?
params[:sort_by]
else
session[:sort_by] || 'id'
end
session[:rating] = if params[:rating].present?
params[:rating]
else
session[:rating] || { 'G' => '1', 'PG' => '1', 'PG-13' => '1', 'R' => '1' }
end
if !(params[:sort_by].present? && params[:rating].present?)
redirect_to movies_path(sort_by: session[:sort_by], rating: session[:rating])
return
end
end
First couple lines set up the session to the parameter value if one was passed in, otherwise checks for an existing session value, and then falls back to the default.
Then, we look to see if either the sort_by or the rating parameter were missing, we'll redirect to include both of them from the new values in the session.
I believe this hits all your criteria. The main problem you were having is that as soon as you call redirect_to, it's going to try and redirect, it doesn't like having code (especially other redirect or render calls) after it. So you need to check any conditions you need and then lump it all into one redirect call and then return from the method so it doesn't try to also render.

Related

Trouble defining custom route in Rails - all ajax requests are defaulting to the show route

I'm trying to do an ajax call from the client which aggregates and then returns a single random ad from the server.
client side ajax call
getRandomPerk: function()
{
return $.ajax({url: url + "/advertisements/get_random_ad.json", type: "GET",dataType: "json", error: this.handleAPIError});
},
routes.rb
resources :advertisements do
#collection do
# get :get_random_ad
#end
member do
get :get_random_ad
end
end
controller advertisement_controller.rb
def get_random_ad
#advertisement = Advertisement.find_random_advertisement()
end
model advertisement.rb
def self.find_random_advertisement
#ids = Advertisement.select("SELECT id FROM advertisements")
Advertisement.find(1)
end
(this is supposed to return a random one but I've got it returning a specific one for debug purposes)
Server log
[time:0.000] Started GET "/advertisements/get_random_ad.json" for 10.240.0.75 at 2016-07-27 18:04:17 +0000
[time:0.200] Processing by AdvertisementsController#show as JSON
[time:0.201] Parameters: {"id"=>"get_random_ad"}
[time:0.207] Advertisement Load (0.1ms) SELECT advertisements.* FROM advertisements WHERE advertisements.id = 0 LIMIT 1
[time:0.208] Completed 404 Not Found in 5.9ms
[time:0.209]
ActiveRecord::RecordNotFound (Couldn't find Advertisement with id=get_random_ad):
app/controllers/advertisements_controller.rb:16:in show'
Why is the rails logger saying "processing by AdvertisementsController#show"? Shouldn't it say "processing by AdvertisementsController#get_random_ad"? Maybe this is a fundamental misunderstanding on my part, but I thought that if you defined a new route in the routes.rb file then it would process all requests to that URL.
Since you're not using the #show action, you can use the only or except arguments. something like this should work:
resources :advertisements, except: [:show] do
member do
get :get_random_ad
end
end
However, if you're only using :get_random_ad, you can omit the resource and instead use
get :random_ad, to: 'advertisements#get_random_ad', as: :random_ad

Call to a collection path keeps passing an id

I have a search method in a rails app I'm writing. Here is an excerpt.
The intent is to use regex to match all items with a description which matches a search query . Then, all matching item ids are passed as a string to another page. (this all takes place in a Search controller).
The item controller then catches that string of ids, splits them up into an array and displays a list of the matching objects.
I'm currently getting a problem however, whereby rails is attaching to the redirect_to statement another paramater (which according to the trace is {"id" => "search"} and is then searching for an Item with ID ":a1". It obviously can't find one (ids are all numeric) and therefore the app crashes. Can anyone work out why this is the case?
IN THE SEARCH CONTROLLER
rquery = Regexp.new(Regexp.escape(#query), Regexp::IGNORECASE)
item_list = []
Item.all.each{|item| item_list << item if rquery.match(item.shortdescr)}
unless item_list == nil
ids = ""
maybe_matters.each do |matter|
ids += item.id.to_s + " "
end
redirect_to search_items_path(ids: ids)
return
end
IN THE ITEM CONTROLLER
def search
authorize! :show, Item
#id = params[:ids].split
#search = []
#id.each do |id|
#search << id.to_i
end
#items = Item.accessible_by(current_ability).where(id: #search).order('completed desc nulls last').page params[:page]
end
EDIT
In case it's relevant, this is (a very small) part of the routes file:
resources :items, only: [:index, :show] do
collection do
post :search
end
end
I'd recommend making search a GET request. Make sure the path to that action is a collection action in your routes.rb. If your controller is named ItemsController:
resources :items do
get 'search', on: :collection
end
You can pass your search params as a URL parameter:
GET /items/search?query=searchterm
EDIT
Just realized the POST in your routes. You can't redirect to a POST path which is probably why you're getting weird behavior. Change search action to a GET and the issue will at least be partially resolved.
Also, how are you handling URL encoding? Characters with spaces will get encoded to weird values like %20 which might give the unexpected results you're seeing when you call stuff like params[:ids].split.
If possible, I'd recommend consolidating this into a single controller action rather than redirecting.

rails 4 redirect back with new params

Is it possible to redirect back to the referring url with a new param in the query string?
something like this:
redirect_to :back, custom_param='foo'
Try this:
# get a URI object for referring url
referrer_url = URI.parse(request.referrer) rescue URI.parse(some_default_url)
# need to have a default in case referrer is not given
# append the query string to the referrer url
referrer_url.query = Rack::Utils.parse_nested_query(referrer_url.query).
# referrer_url.query returns the existing query string => "f=b"
# Rack::Utils.parse_nested_query converts query string to hash => {f: "b"}
merge({cp: 'foo'}).
# merge appends or overwrites the new parameter => {f: "b", cp: :foo'}
to_query
# to_query converts hash back to query string => "f=b&cp=foo"
# redirect to the referrer url with the modified query string
redirect_to referrer_url.to_s
# to_s converts the URI object to url string
You can put it in session and then redirect back
session[:somekey] = value
redirect_to :back
Another approach could be to pass it via "flash" (it goes away automatically after the redirect request):
flash[:somekey] = 'some value'
redirect_to :back
However, as one of my colleagues noted, a better way is probably to return it as a query param, to keep it stateless and distinguish the two URLs. It seems there isn't a nice, built-in Rails way to do that, which makes me wonder what else is wrong with this approach.

Getting a . instead of a / in my link(path)

# GET system_accounts/:id
def show
# If a system account already exists in session, it was found from a search with the account id
# Otherwise, this is a new search for a system account by the given id
if params[:id]
Rails.logger.debug { "Querying for the account with id: #{params[:id]}" }
response = query_account(CGI.escape(params[:id]))
if response.is_a?(Net::HTTPSuccess)
#account = JSON.parse(response.body)
unless #account.empty?
redirect_to system_accounts_path(params[:id])
end
end
The above is my show action. So the thing is if i search with my id=2 i should get the resultant link to be system_accounts/2 but what i get is system_accounts.2 . why a . instead of a / . Anything i am missing out on ?
if you are closely following the rails convention, you should use system_account_path(params[:id]) (without an s). 2 is interpreted as the format so it is appending .2 to the url because system_accounts_path most probably points to the index action of system_accounts controller. remove the s to point to the show action.

interning empty string error

Currently, in my request model I have:
belongs_to :requestor, :class_name => 'User'
So the requestor is the current_user.
Problem is when the current_user clicks the create button to submit a form for a request, all of the attributes get updated to the database that are in the form.
But since requestor_id is not a value to fill out in the form, that brings back a value of null in the database when the new request record is created.
What I want is an integer (which equates to the primary key of the Users table) updated in the requestor_id column in the request table when the user clicks the create button.
So I thought that maybe adding a requestor_id as a symbol in the params for the create action would solve that:
def create_common
#a = Request.new
b = #a.requestor_id
#resource = yield params[:contact + "#{b}".to_sym]
self.resource = #resource
But instead it returns the following error:
interning empty string
Thanks for any suggestions.
I kept getting this error. I traced it to very simple code and reproduced in the console with this:
ruby-1.8.7-p174 :084 > a = 'fred'
=> "fred"
ruby-1.8.7-p174 :085 > a.to_sym
=> :fred
ruby-1.8.7-p174 :086 > a = ''
=> ""
ruby-1.8.7-p174 :087 > a.to_sym
ArgumentError: interning empty string
from (irb):87:in `to_sym'
from (irb):87
Can you not just pass the Request the current_user when you create it?
#req = Request.new(:requestor => current_user)
I am not quite sure what the yield params statement is meant to be doing,
If I'm understanding your question correctly, you want to assign the current_user's id as the requestor id to the Request model?
# assign values passed in via form
#request = Request.new(params[:request])
# assign current_user to request
#request.requestor = current_user
# save the request
#request.save!
Hope this helps!
I had a '.' in an error message similar to:
errors.add('You entered a non-supported widget.')
and was getting the "interning empty string error"
This post saved me:
http://www.tonyamoyal.com/2009/10/20/interning-empty-string-error-in-ruby-on-rails/
Simply changing to:
errors.add('You entered a non-supported widget')
fixed it. Rails 2.3.4 and Ruby 1.8.5

Resources