Routes with Custom Views - ruby-on-rails

I'm trying to work out how to get a custom route working with rails to perform a date query. I'd like to call this report if possible. So I access my route on the browser and get the following.
http://localhost:3000/trips/report
Couldn't find Trip with id=report
Like it's trying to read in report as an ID? I can't quite work out where I've gone wrong here? In my routes.rb file I've create the following entry.
match 'trips/report' => 'trips#report'
With the following in my trips controller.
def report
#trips.all :condition => ["DATE(date) = DATE(?)", Time.now]
respond_to do |format|
format.html
format.json { render json: #trips }
end
end
I'm probably doing something very silly! Hopefully someone can help me along the right track?

You certainly have declared a trips resource in your routes.rb, and the route GET 'trips/:id' generated by the resource have a higher priority then the match 'trips/report' defined later (Rails uses the first matching rule). If it's the case, declare your report route like this:
resources trips do
collection do
get 'report'
end
end
Look at this chapter in Rails Routing Guide for more information.

Related

Ruby on Rails - PUT Method Creates Extra Params Entry

So I observed some weird behaviour while implementing an endpoint for a RESTful API I am creating for a mobile client. I am using a PUT method to update an attribute on the User model. I send the user's id as a URL parameter and the value to update inside a JSON object. Everything seems to work just fine but when I check the parameters via the rails logs I noticed something strange. For some reason there is an extra parameter being sent to the backend that I can't seem to explain. Here are the logs I am seeing when I call this endpoint from the mobile client:
Parameters: {"enable_security"=>true, "id"=>"7d7fec98-afba-4ca9-a102-d5d71e13f6ce", "user"=>{}}
As can be seen above an additional "user"=>{} is appended to the list of parameter entries. I see this when I print out the params object as well. I can't seem to explain where this is coming from. I also checked the mobile client just to be safe and there is no where in code where I send a parameter with a key user. This is very puzzling to me and makes me think I am missing something fairly simple. Why is there an empty object with the user key being sent to the backend RESTful API?
Update to Provide More Information
Here is the code that gets called when the user hits the endpoint that updates the user User model:
#PUT /:id/user/update_security_settings
def update_security_settings
#user = User.find_by_id(params[:id])
#user.advanced_security_enabled = params[:enable_security]
respond_to do |format|
if #user.save
response = {:status => "200", :message => "User's security settings updated."}
format.json { render json: response, status: :ok }
else
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Update in Response to User's Comments
Here are the routes that pertain to the user_controller, the view controller that defines all endpoints that deal with creating and updating the User model.
post '/user/upload_profile', to: 'user#upload_profile'
get '/:id/user', to: 'user#find_user'
put '/:id/user/update_security_settings', to: 'user#update_security_settings'
resources :user, :defaults => { :format => 'json' }
Does this comment really mirror your actual route?
#PUT /:id/user/update_security_settings
I'd expect it to be /user/:id/update_security_settings instead.
Can you show us your config/routes.rb - My wild guess is that your routes are somehow configured to expect an actual nested user param, which you don't send (of course) and therefor appears empty in the logs.
Update:
Some of your routes are unusual. You actually don't need the find_user route as it should be covered under resources :user as show action (provided you defined a show method in your controller, which is the default way to retrieve a single resource item; so no need for find_user)
For custom routes like your update_security_settings action I'd suggest to stick to the default pattern, like resource/:id/actionand nesting it in the default resourceful route. Putting the id before the resource is very unusual, confusing and may actually be related to your issue (thoguh I#m not sure about that). Try cleaning up your routes.rb liek this:
# notice that resources expects the plural form :users
resources :users do
member do
patch :update_security_settings
post :upload_profile
# any other custom routes
end
end
This will result in routes like GET /users (index), GET /users/1 (show) and PATCH /users/1/update_security_settings.
More on routing an be found here: Rails Guides | Routing From The Outside In
Please check if the changes above remove your empty user param.
Check your configuration in
config/initializers/wrap_parameters.rb
wrap_parameters format: [] this list should not contain json then it will wrap the parameters to the root of you controller for all the json request. Refer api docs

Rails 404 error for html format, but no issue with json format

This is puzzling me. I have a Rails application which, in production on Heroku, is returning 404 errors for many records, when requested as follows:
https://myapplication.heroku.com/records/500
The page you were looking for doesn't exist.
while the JSON version of the same page is fine:
https://myapplication.heroku.com/records/500.json
There are a few instances of model that work just fine with the regular HTML version:
https://myapplication.heroku.com/records/600
Here is the relevant part of config/routes.rb:
resources :records do
member do
get 'revisions'
end
end
And here is the #show action of the records controller:
# GET /records/1
# GET /records/1.json
def show
#r = Record.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #r }
end
end
This was all working fine yesterday, but today I did some database migrations on the records table and a lot of other changes to records. What's puzzling me is that the JSON part works, and the records do exist. I even tried adding the following to routes.rb and moving it to the top of the file:
get 'records/:id', to: 'records#show', constraints: { :id => /\d/ }
But it did nothing. Appending .html to the GET request in URL bar does not do anything either.
Any ideas would be greatly appreciated!
Edit: Here is a gist with the Heroku log.
Firstly, I would refactor your controller, like so:
#app/controllers/records_controller.rb
respond_to :html, :json
def show
#r = Record.find params[:id]
respond_with #r
end
Your code looks fine to me.... So I would suggest we take a deeper look into (asking some comments):
This was all working fine yesterday, but today I did some database
migrations on the records table
I was using PaperTrail for Versions and in the view for Record called User.find(#r.versions.last.whodunnit.to_i), but #r.versions.last.whodunnit.to_i was 0 and the user could not be found.
I was thrown by the 404 error because I thought it was somehow not finding the primary Record record in the database, when it was actually something else.
After enabling logging, this was not hard to debug. (I thought I had enabled logging before, but apparently not.)
Thank you all for your help!

StackOverflow Style Routes with Smart Redirects

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

How to dynamically add a route to a scoped resource in Rails3?

right now I am trying to generalize some of my code. So far it went well, I wrote a few mixins which I can dynamically add to Controllers or Models in order to get things done while obeying DRY.
But with my "Searchform-Helper" I hit a corner in which, right now, I am a bit clueless.
I have a mixin 'SearchIndexController' which adds the methods needed to search for data within a searchindex-table.
After including the mixin I can initialize search-actions within the according controller calling this method:
def init_searchaction(object, name=nil)
singular = object.to_s.classify
plural = singular.pluralize
name = "search_#{singular}".to_sym if name.nil?
unless self.respond_to?(name)
define_method(name) do
# init
success=false
#TODO
# >>> DRAW NEW ROUTE TO THIS ACTION <<<
# evaluate searchform input for Searchindex-Call
needle = params[:query]
success, x, notice = execute_search("#{singular}", needle)
# send selected/filtered data to page
respond_to do |format|
format.js {
render :update do |page|
page.call "sidx_updateSearchResultContentAtIdTag", "##{plural.downcase} tbody", "#{render x}" if success
page.call "sidx_updateNotice", success, "#{notice}"
page.call "sidx_stopSpinner"
end
}
end
end
else
logger.warn("#{__FILE__}:#{__LINE__}:#{self.to_s}: search-action for '#{self.class.name}' can not be created, it already exists!")
end
end
So lets say I have a User-Controller. Within the Userform I have the need to search for several objects. Lets assume I want to be able to search for users, departments and clients... with my mixin I'd just have to initialize the searchactions like this:
init_searchaction :user
init_searchaction :department
init_searchaction :client, :find_clients
these would create actions within the including controller that are called
search_user
search_department
find_clients
The only thing missing is a way to get a route for them. I don't want to have to define the route upfront. I just want to 'init_searchaction' and have the mixin create the necessary route.
So... would it be possible to add the route to the accoring search-action from withing the mixins init_searchaction method dynamically? I think the necessary code would be placed at the #TODO mark in the code example above. But I still haven't found out how to do it... I mean, actually I would be surprised if it would not be possible.
Would anyone have an idea as how to do this? Thanks in advance for any idea that leads to the solution!
You can add work around standart dynamic route
match ':controller(/:action(/:id(.:format)))'
change it to your goals and enjoy :)

Accessing a resource in routes.rb by using attributes other than Id

I have the following in my routes.rb
map.resources :novels do |novel|
novel.resources :chapters
end
With the above defined route, I can access the chapters by using xxxxx.com/novels/:id/chapters/:id.
But this is not what I want, the Chapter model has another field called number (which corresponds to chapter number). I want to access each chapter through an URL which is something like
xxxx.com/novels/:novel_id/chapters/:chapter_number. How can I accomplish this without explicitly defining a named route?
Right now I'm doing this by using the following named route defined ABOVE map.resources :novels
map.chapter_no 'novels/:novel_id/chapters/:chapter_no', :controller => 'chapters', :action => 'show'
Thanks.
:id can be almost anything you want. So, leave the routing config untouched and change your action from
class ChaptersControllers
def show
#chapter = Chapter.find(params[:id])
end
end
to (assuming the field you want to search for is called :chapter_no)
class ChaptersControllers
def show
#chapter = Chapter.find_by_chapter_no!(params[:id])
end
end
Also note:
I'm using the bang! finder version (find_by_chapter_no! instead of find_by_chapter_no) to simulate the default find behavior
The field you are searching should have a database index for better performances

Resources