Adjust routes.rb for method call in Controller - ruby-on-rails

I have an online portfolio created in Rails featuring different projects. I want to be able to filter the projects by keywords. My approach is to define a method for each keyword in the ProjectsController and link the keywords to call the methods.
For Example Keyword = graphic_design:
<%= link_to 'Graphic Design', :action => "filter_"+#project.keyword.to_s %>
That way I get the error:
Couldn't find Project with ID=filter_graphic_design
This is quite obvious to me. My question: Is there a way to tell the routes.rb to behave differently only for 'filter_' methods? Any other suggestions?

Your approach is wrong. Why do you need a filter_ method for each keyword in the first place? Its pretty simple a solution. First define a named route in your routes.rb:
map.filter '/projects/:filter_this_for_me', :controller => 'projects', :action => 'filter'
In your views,
<%= link_to 'Graphic Design', filter_path("filter_" + #project.keyword.to_s) %>
In your filter action,
def filter
logger.info("Parameters that is being received: #{params}")
filter_what = params[:filter_this_for_me]
if(!filter_what.nil? && !filter_what.blank?)
# Here filter_what will have "filter_graphic_design" or "filter_something"
# With which you can filter any data that you want.
# Filter your projects here.
end
end

I think something like this could work
map.connect "/projects/filter_{filter}", :controller => 'projects', :action => 'filter'
Haven't tried it though

Related

Better routes in rails with params

I've been working on a movie application in Rails. I have a controller/view for the actor. When passing params to the actors controller i want the URL to be pretty. Now it looks like this: http://localhost:3000/actors/show?actor=Hayley+Atwell and i want it to look like /actors/show/Hayley+Atwell or /actors/Hayley+Atwell.
How do i do this? My link in the movies view is:
<%= link_to a.name, {:controller => 'actors', :action => 'show', :actor => a.name}, :class => "label label-default" %>
My routes.rb is now like this:
get 'actors/show'
I recommend you use friendly_id gem. It perfectly satisfies your needs!
https://github.com/norman/friendly_id
You can use the following in your ./config/route.rb file:
get '/actors/:actor', to: 'actors#show'
I'll give a series of refactoring advice as follow:
Try to make use of the actor's id instead of the name
Change the route to: get '/actors/show/:id', to: 'actors#show'
Then, you can now change the link in your view to something like:
<%= link_to a.name, {:controller => 'actors', :action => 'show', :id => a.id}, :class => "label label-default" %>
Note: the : part in the :id of your route means that this could be interchanged to anything, and whatever comes in that place will be interpreted as the id. So, in /actors/show/7, the id of the actor is 7, you can now find the actor with this id in your controller action, and do anything you want with it.
If I'm right than you try to create RESTful routes anyway.
So I would recommend you to use the Rails' resources method in your routes:
Rails.application.routes.draw do
resources :actors # If you want only the :show action than add:
# , only: [:show]
end
This automatically creates the proper routes for you.
To get the names into the URL you should use the friendly_id gem. It has a great community and is very well tested.

How to create an arbitrary link in Rails?

link_to and helpers use the names of my models and their IDs while I want to have a couple of different, arbitrary, variables in my link. I have no problems to route them, I actually keep the default routing as well, but I suddenly stuck that I cannot easily generate an arbitrary link. For instance I want to have a link like ":name_of_board/:post_number", where :name_of_board and :post_number are variables set by me, and when I use link_to I get instead "posts/:id", where "posts" it's the name of the controller. While it's not hard to use an arbitrary id like
link_to 'Reply', :controller => "posts", :action => "show", :id => number
I cannot get how I can get rid of "posts". So, is there an easy way to generate a link by variables or to convert a string into link? Sure, I can add other queries to the line above, but it will make the link even more ugly, like "posts/:id?name_of_board=:name_of_board".
You can create additional routes for your posts resource in your routes.rb, or make standalone named routes:
resources :posts do
get ':name_of_board/:id' => 'posts#show', as: :with_name_of_board
end
get ':name_of_board/:id' => 'posts#show', as: :board
Now this
#name_of_board = "foo"
#post_id = 5
link_to 'Reply', posts_with_name_of_board_path(#name_of_board, #post_id)
link_to 'Reply', board_path(#name_of_board, #post_id)
would link to /posts/foo/5 and /foo/5 respectively.
You should first edit your route entry, for example a classic show route is the follow:
get "post/:id" => "post#show", :as => :post
# + patch, put, delete have the same link but with different method
And you can call it with the following helper
link_to "Show the post", post_path(:id => #post.id)
You can edit or create a new entry in the routes, applying the parameters you want to use, e.g.:
get "post/:id/:my_platform" => "post#show", :as => :post_custom
Then
link_to "Show the post with custom", post_custom_path(:id => #post.id, :my_platform => "var")
Finally, the link generated for this last entry is for example:
"/post/3/var"
Even in this situation, you can add some other params not defined in the routes, e.g.:
link_to "Show post with params", post_custom_path(:id => #post.id, :my_platform => "var", :params1 => "var1", :params2 => "var2")
=> "/post/3/var?params1=var1&params2=var2"
RoR match your variable defined in the routes when you render a link (remember that these variables are mandatory), but you can ever add other vars that come at the end of the url ("?...&..")

Passing path to current_page method

I have a helper method to help to determine whether or not a navigation menu item should be active/inactive.
def events_nav_link
nav_item = 'Events'
if current_page?(events_path) # additional conditions here
# do nothing
else
nav_item = link_to nav_item, events_path
end
content_tag(:li, nav_item + vertical_divider, :class => 'first')
end
I want this link to be inactive not only for the events_path page, but for other pages as well. Problem is that I don't know what param to pass the current_page?(path) method based on this route:
map.list_events '/events/:league/:year/:month/:day', :controller => 'events', :action => 'list'
This route generates a path such as /pow or /chow/2011/09/25. Leagues can be pow, chow or wow.
I like unobtrusive JS approach with add/remove classes and unwrap() deactivated links, but it requries specific rules.
The Rails way is to use link_to_unless_current built-in helper to highlight and unlink on current page href.
You're looking for named routes. In your routes.rb file, add
:as => :foo
after the route. Then use
if current_page(events_path) || current_page(foo_path)
in your condition.
This is what I do:
application_helper.rb:
def already_here?(this_controller,this_action)
controller_name == this_controller && action_name == this_action ? true : false
end
in a view:
<%= already_here?("users","index") ? "Manage Users" : link_to("Manage Users", users_path, :title => "Manage Users.") %>
Of course, you can abstract it further by passing the title of the link and path to the helper if you want, but that's a bit of a hassle (to me).
UPDATE: never mind, see mikhailov's answer- much cleaner (why reinvent the wheel?) :)

Rails form trouble

So, I have something of an odd controller/view setup, editing a Product model object occurs in the listings controller/view. There is a long-winded explanation for why this is, but I digress. However, when I submit the form, I get the error Couldn't find Product without an ID . What gives? Strangely, when I look at the params sent with the request, the ID attribute is assigned to the 'format' key. ?!.
The controller code is very simple. Edit Action:
def edit
#edit = Product.find(params[:id])
end
Update Action:
def update
#edit = Product.find(params[:id])
if #edit.save
redirect_to :url => listings_display_path
end
end
Here is my form_for code:
<% form_for #edit, :url => (listings_update_path(#edit)) do |f| %>
Edit, the trace:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"st17LW0S9uENaV8UBcxKUfRH67oo+r3TuFAxiPKMCEc=",
"product"=>{"brand"=>"test_brand",
"productname"=>"test_product",
"category"=>"test_category",
"regprice"=>"4",
"saleprice"=>"2",
"description"=>"description"},
"commit"=>"Submit",
"format"=>"21"}
Edit: routes.rb
resources :product do
resources :promotions
collection do
get "replace"
end
end
#listings
match 'listings/index' => 'listings#index'
match 'listings/display' => 'listings#display'
match 'listings/edit' => 'listings#edit'
match 'listings/update' => 'listings#update'
Edit: create action
def create
#product = Product.new(params[:product])
#product.user = current_user
if #product.save
redirect_to :action => 'index'
end
end
First, for an alternate approach to editing multiple records on a single view, try this railscast: http://railscasts.com/episodes/198-edit-multiple-individually
Second, this is unconventional but your whole approach is unconventional...
You can stick a hidden field in the form with the ID in it. Something like:
<%= f.hidden_field, :product, :id %>
Then check your params hash and the id will be in there. You should be able to access it in the controller using something similar to:
# untested
#edit = Product.find(params[:product][:id])
Off the top of my head I'm not sure how it will be stored in your params hash but it will be there and you'll be able to access it like any other hash attribute.
Good luck!
--EDIT--
Also, regarding your comment about lack of flexibility in Rails -- one thing I've learned is that Rails isn't inflexible, but it is highly optimized for particular conventions. The developers refer to this as being "highly opinionated" software, which means:
It can do just about anything you care to do it any way you want to do it, but...
There's almost always a "preferred" way which is better, faster (harder stronger lol).
You would save yourself tons of time and energy -- and probably have a lot of fun -- grabbing the Beginning Rails 3 book. You could work through it in a weekend, and when you were done you would have a great primer on the "Rails Way" which would hopefully help you go from "I don't get why Rails doesn't do this or that" to "I get how Rails works and how easy it is to do what I want to do by following X pattern". That's what happened for me anyhow.
Again good luck!
Try this
<%= form_for #edit, :url => listings_path(#edit), :html => {:method => :put} do |f| %>
You need to fix the routes that are for a single product instance to have the id:
match 'listings/index' => 'listings#index'
match 'listings/:id/display' => 'listings#display'
match 'listings/:id/edit' => 'listings#edit'
match 'listings/:id/update' => 'listings#update'

how do I create a custom route in rails where I pass the id of an existing Model?

I created the following route:
map.todo "todo/today",
:controller => "todo",
:action => "show_date"
Originally, the 'show_date' action and associated view would display all the activities associated for that day for all the Campaigns.
This ended up being very slow on the database...it would generate roughly 30 records but was still slow.
So, I'm thinking of creating a partial that would first list the campaigns separately.
If someone clicked on a link associated with campaign_id = 1, I want it to go to the following route:
todo/today/campaign/1
Then I would like to know how to know that the '1' is the campaign_id in the controller and then just do its thing.
The reason I want a distinct URL is so that I can cache this list. I have to keep going back to this and it's slow.
NOTE: It's possibly the problem actually is that I've written the queries in a slow way and sqlite isn't representative of how it will be in production, in which case this work-around is unnecessary, but right now, I need a way to get back to the whole list quickly.
The code above by #Damien is correct but incomplete. It should be:
map.todo "todo/today/campaign/:id", :controller => "todo", :action => "show_date"
in your views all you have to do is:
<%= link_to "Campaign 1", todo_path(:id => 1) %>
or simply
<%= link_to "Campaign 1", todo_path(1) %>
and the particular campaign id can be fetched using params[:id] in your action.
And yeah, sqlite is not production ready.
EDIT: The latter part is quite easy to implement:
However, you have to change the route slightly,
The route will now become,
map.todo "todo/today/:campaign/:id", :controller => "todo", :action => "show_date"
in your views:
<%= link_to "Campaign 1", todo_path(:campaign => "campaign", :id => 1) %>
In your todo controller, show_date action:
def show_date
#IF YOU ARE USING THIS REPEATEDLY IN LOTS OF DIFFERENT ACTIONS THEN A BETTER PLACE FOR THIS WOULD BE AS A HELPER IN application_controller.rb
if params[:id].nil? && params[:campaign].nil?
#DO SOMETHING WHEN BOTH ARE NIL,
elsif params[:campaign]!="campaign"
#DO SOMETHING WITH CAMPAIGN BEING SOMETHING OTHER THAN "CAMPAIGN"
elsif params[:campain]=="campaign" && params[:id].nil?
#DO SOMETHING WITH ID BEING NIL.
else
#FIND YOUR CAMPAIGN HERE.
end
end
Hope this helps. :)
Just with the following :
map.todo "todo/today/:id",
:controller => "todo",
:action => "show_date"
This will create the /todo/today/:id url where id is whatever you set in the url.
You can then access it in your controller with params[:id].
You might be interested in reading Rails Routing from the Outside In, particularly the section about resources.

Resources