Trying to choose between two paths on Rails view, depending on parameters - ruby-on-rails

I have a HABTM relationship between financing_campaign and financing_merchant.
In one specific view, there can be a link_to to 2 possible paths. If there is a #campaigns variable, then the path should be:
(to display the merchant in the context of the campaign)
financing_campaign_merchant_path(#campaign, merchant) -
/financing/campaigns/:id/merchants/:id
else, it should be:
(to display the merchant without the context of the campaign)
financing_merchant_path(merchant) -
/financing/merchants/:id
I'm trying to decide the best way to solve this. Currently we are using a ternary on the view, which seems ugly and confusing:
<% #merchants.each do |merchant| %>
<tr>
<td>
<%= link_to merchant.name, #campaign ? financing_campaign_merchant_path(#campaign, merchant) : financing_merchant_path(merchant) %>
</td>
...
</%end>
routes.rb:
namespace :financing do
resources :merchants
resources :campaigns, only: %i[index show edit update] do
resources :merchants, only: [:index, :show]
...
end
end
Thanks everyone!

Sometimes best to keep it simple. I think what you have is solid, particularly if you are only going to use it in one place.
If you want to beautify the view code, though, you could elect to move the ugliness to a helper class, i.e:
module FinancingHelper
def link_to_merchant(merchant, campaign=#campaign)
link_to merchant.name, campaign ? financing_campaign_merchant_path(campaign, merchant) : financing_merchant_path(merchant)
end
end
Then your view changes to:
<% #merchants.each do |merchant| %>
<tr>
<td>
<%= link_to_merchant merchant %>
</td>
...
</%end>
If #campaign is defined in context, it will be found in the helper and use it; otherwise, it will link to just the merchant. Or, you could explicitly pass a campaign parameter to check, but this just means more code to type, and it seems like you like the "less is more" approach!

Related

Not able to pass id from index in rails association

I have a Project model with one to many association with Financial, Financial model has one to many relationship with PaymentMilestone model. I want to add new_payment_milestone_path on the financial index.html.erb, but I am not able to pass financial_id on button click.
Here is my code:
financial index.html.erb
<% #financials.each do |financial| %>
<tr>
<td><%= financial.responsibility %></td>
<td><%= link_to " ", new_project_financial_payment_milestone_url(#financial, payment_milestone) %></td>
</tr>
<% end %>
financials_controller.rb
def index
#financials = #project.financials
end
routes.rb
resources :projects do
resources :financials do
resources :payment_milestones
end
end
Don't go nuts when resting:
Rule of thumb: resources should never be nested more than 1 level
deep. A collection may need to be scoped by its parent, but a specific
member can always be accessed directly by an id, and shouldn’t need
scoping (unless the id is not unique, for some reason)
- Jamis Buck
Making a pyramid out of your routes like you have makes things far more complex then they really should be. To unnest payment_milestones from projects you need to change your routes to:
resources :projects do
resources :financials
end
resources :financials, only: []
resources :payment_milestones, shallow: true
end
This will create these routes:
financial_payment_milestones GET /financials/:financial_id/payment_milestones(.:format) payment_milestones#index
POST /financials/:financial_id/payment_milestones(.:format) payment_milestones#create
new_financial_payment_milestone GET /financials/:financial_id/payment_milestones/new(.:format) payment_milestones#new
edit_payment_milestone GET /payment_milestones/:id/edit(.:format) payment_milestones#edit
payment_milestone GET /payment_milestones/:id(.:format) payment_milestones#show
PATCH /payment_milestones/:id(.:format) payment_milestones#update
PUT /payment_milestones/:id(.:format) payment_milestones#update
DELETE /payment_milestones/:id(.:format) payment_milestones#destroy
And you then can change your view to:
<% #financials.each do |financial| %>
<tr>
<td><%= financial.responsibility %></td>
<td><%= link_to "New milestone", new_financial_payment_milestone_path(financial) %></td>
</tr>
<% end %>
You want to use the local variable financial which is passed to the block and not the instance variable #financial. You should also always provide a link text for accessibility.
add this to button path-
new_project_financial_payment_milestone_url(#project, financial)

Ruby on Rails Custom Method/Task

I'm new to Ruby on Rails. I'm building out an app that does custom operations on defined nodes (the object). Here's my table:
<% #nodes.each do |node| %>
<tr>
<td><%= node.name %></td>
<td><%= link_to 'Bootstrap', node_path(node),
method: :bootstrap %>
</td>
</tr>
<% end %>
"Bootstrap" is the custom code that I want to run. I'm defined a custom method within my Controller:
class NodesController < ApplicationController
def bootstrap
......
end
........
end
How do I tie in my custom code to my Rails app so that when a user clicks on the link the code will be run against that object?
In config/routes.rb, you probably have:
resources :nodes
Change this to:
resources :nodes do
get 'bootstrap', on: :member
end
Then, run rake routes to see that you now have a new route method, bootstrap_node_path(node), and will link to /nodes/:id/bootstrap.
I recommend this over the other approach as it keeps your route details together, but that's just my personal opinion. I usually resort to custom routes as a last resort.

Rails: Having a button in a view call a method in a controller, route not working

Hi I'm making a rails app that uses Zendesk API calls. I have a controller that uses two classes I defined
class TicketsController < ApplicationController
require 'ticket_fields'
require 'ticket_search'
def getTickets
#search_fields = SearchFields.new(client)
#tickets = TicketSearch.new(client)
end
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
end
end
One class SearchFields uses the api to load values I want to filter tickets by into arrays. My view then uses these values to populate drop down lists.
The other class TicketSearch looks like this.
class TicketSearch
attr_reader :tickets, :text
def initialize(client)
#text = "query"
#tickets = Array.new
client.tickets.all do |resource|
#tickets << resource
end
end
def search_tickets(custom_search_fields)
querystring = "type:ticket+tags:"
custom_search_fields.each_with_index do |field, index|
unless field == ""
if index ==0
querystring += "#{field}"
else
querystring += " #{field}"
end
end
end
#text = querystring
end
end
What I want to happen in my view is when a button is pressed it changes the value of #text to the querystring generated by the drop down list options that were selected. I'm currently doing this for testing to see if my querystring is correct and the button works. What I eventually want it to do is send the querystring to the ZenDesk Server and returns the tickets I filtered for. the #tickets array would then be replaced with the filtered tickets the server returned. Currently my button code looks like this.
<%= button_to 'Search', :action => 'search_tickets' %>
with all the route code I've tried I either get an error upon starting the page. Or when I press the button nothing happens and the #text being displayed in my view remains "query". Can someone help explain what I need to do I don't quite understand how routes work.
==================================================================================
Hey so I made the changes you suggested and did some reading up on AJAX and js and I think I'm almost at the answer my view now looks like this
<div id="test" >
<%= render partial: 'text', locals: { text: #tickets.text} %>
<div id="test" >
and I created a partial _text file that looks like this
<p> Query: <%=text%> </p>
and a js file search_tickets.js.erb
$("#test").html("<%= escape_javascript(render partial: 'text', locals: { text: #tickets.text } ) %>");
any idea what may be going wrong everything loads up okay but the text remains the same in the partial i set up when i hit the button still
the console outputs this after the button is hit
ActionController::RoutingError (No route matches [POST] "/tickets/search_tickets"):
so I guess it may actually be a routing error my route looks like this
resources :tickets do
collection do
put :search_tickets
end
end
and the form tag calling the path looks like this
<%= form_tag search_tickets_tickets_path, remote: :true do %>
<table>
<tr>
<td align = "left" valign="middle"> <font size = 4> Customer Company </font> </td>
<td align = "left" valign="middle">
<%= select_tag "customer_company_id", options_for_select(#search_fields.customer_companies), :prompt => "Select One" %>
</td>
</tr>
......
<tr>
<td> </td>
<td align = "left" valign="middle">
<%= submit_tag "Search" %>
</td>
</tr>
</table>
<% end %>
==================================================================================
(Update)
I think I fixed my last problem by changing my form tag to this
<%= form_tag search_tickets_tickets_path(#tickets), method: :put, remote: :true do%>
however now I get this error from the terminal after I hit the button
NoMethodError (undefined method search_ticket' for nil:NilClass):
app/controllers/tickets_controller.rb:15:insearch_tickets'
how would I pass #tickets as a parameter through my route because clearly its not accessible by search_tickets right now as its giving a nil class error.
Variables
when a button is pressed it changes the value of #text to the querystring generated
It looks to me like you're confused with the stateless nature of Rails - in that, just because a view has been rendered doesn't mean the values / variables are still available for use.
It was mentioned in the comments that it seems you're basing a lot on experience with other frameworks / programming patterns. The best way to describe your solution is that Rails has to "refresh" all your variables / values each time it processes a request; consequently meaning that if you send a button request - you'll have to perform the request as if it were the first one
Ajax
The bottom line is that you need to use an ajax request to pull this off.
To do this, you'll be be best creating a form (not just a button_to), as this will give you the ability to send as many params as you want. You should use form_tag:
#config/routes.rb
resources :tickets do
collection do
get :search_tickets
end
end
#view
<%= form_tag tickets_search_tickets_path, remote: :true do %>
... #-> fields for your params
<%= submit_tag "Search" %>
<% end %>
This will give you the ability to define the following in your controller:
#app/controllers/tickets_controller.rb
Class TicketsController < ApplicationController
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
respond_to do |format|
format.js #-> loads /views/tickets/search_tickets.js.erb
format.html
end
end
end
#app/views/tickets/tickets_search.js.erb
//JS here to manipulate your original page
Requests
The bottom line here is that if you want to "manipulate" your view without refreshing, unlike "native" application frameworks, where you can rely on a persistent state, with Rails, you basically have to construct the request from scratch (IE passing all the params required for the method to run)

pass value of td element to rails controller

I'm trying to pass the value of a element to a rails controller!
Currently, I have something like this:
<td id="dbname"><%= link_to db, :action => :show %></td>
This represents a row in an html table, which contains a string value, e.g. "development".
When the user clicks on the "development" link, the <%= link_to ... %> grabs the value from the current clicked and passes that to a rails controller action, in this case the show action.
How can this be achieved!?
UPDATE - generating links:
<% #dbs.each do |db| %>
<tr>
<td id="dbname"><%= link_to db, :action => :show %> </td>
</tr>
UPDATE 2:
this is my index controller:
conn = Mongo::Connection.new
#dbs = conn.database_names #this returns an array of strings (which are the names of the databases)
Now I want to be able to click on of these databases and then to pass the clicked text to the rails controller show action. I'm not sure how I would generate a custom resources path for these links... but I was contemplating of doing it using Ajax or something javascript related. Maybe get the text of clicked link using jQuery and then send an Ajax request to the rails controller with the text as a parameter!
I think that it's a strange thing what you're trying to do, but a solution could be to use javascript to append the id to the href of each link as a query string.
If you could explain a little bit what you're trying to achieve maybe we could find a better solution.
Hope it helps!
Edit:
If you have a table of links I think that you should consider them as a resource and managing them the REST way.
Your controller should have an index and show action and you should declare the links as a resource in the routes file (maybe link it's a reserved word and you will have to use a different name, I'm not sure), the index action will fetch all the links and when you render them, you could specify the link for each one with something similar to "link_path(link.id)" (remember, you should have a show action defined) in the controller you will receive the link id so you could load it with a simple "find" and pass it to the view.
I recommend you to always look for the REST way to solve a problem in ROR.
Edit 2:
Ok let's see if this works for you:
I suppose that you have a model that represent those links that you're talkin about, for example:
class DataBaseLinks < ActiveRecord:Base
end
This model with be backed up by a table in your database, if you have generated it the rails way, you will also have an id column that identify each database link.
in your controller, let's say DataBaseLinksController, you'll have:
class DataBaseLinksController < ApplicationController
def index
#dabatase_links = DataBaseLink.all
end
def show
#database_link = DataBaseLink.find(params[:id])
end
end
(I've avoided all the validations and checks).
All you have to do in your index.html.erb is:
<% #database_links.each do |database_link| %>
<%= link_to database_link.name, database_link_path(database_link.id) %>
<% end %>
This will generate all the links with the correct path to the show action (maybe the route helper is a little bit different, but not so much).
Notice also that you'll have to add into your routes.rb the following line:
resources :database_links, :only => [:index, :show]
How do you see it?
Edit 3:
(I'll delete all my edited answers when we find a correct one)
Ok I'm going to suppose that you are not using something like mongoid so you don't have active record similar objects.
Have you tried this in your view:
<% dbs.each do |dbs_name| %>
<%= link_to dbs_name, :controller => "your_controller", :action => :show, :dbs_name => dbs_name %>
<% end %>

Suggestions needed on Rails route approach

Should my URL's in Rails look like:
http://foobar.com/articles?category=recent
- OR -
http://foobar.com/articles/recent
I find the former to be more RESTFUL, but the latter to be more cleaner (code-wise). For example, the code can look like:
In Article controller:
def index
if params[:category] == 'recent'
#articles = Article.by_recent
elsif params[:category] == 'expired'
#articles = Article.by_expired
end
end
In Article, index view:
<% unless #articles.blank? %>
<ul>
<% #articles.each do |article| %>
<li><%= article.title %></li>
<li><%= article.content %></li>
<% end %>
</ul>
<% end %>
With http://foobar.com/articles/recent, I would have to customize my routes. Something like:
match 'articles/:category' => 'articles#index'
Above will access the Article controller, index action. Or even:
resources :articles do
collection do
get 'recent'
end
end
Which will allow for urls like http://foobar.com/articles/recent, but needs a 'recent' action in the Article controller.
Both seem pretty useful, in the end of the day. But which is the general consensus? Using the query string approach (http://foobar.com/articles?category=recent) or the other way (http://foobar.com/articles/recent).
Looking forward to your suggestions.
Obviously like you said you could go either way, but since something like "recent" is a condition of or query against articles I would go with the former. If it were a nested attribute and its own model the second approach is arguably better, but since it isnt you really lose a lot of flexibility to change and refactor the approach down the road.
For instance if you wanted to do "old" as opposed to "recent" you would not be able to simply pass in an "old" parameter, but would have to hardcode that with its own action and views in the same controller as "recent" which is of course a lot more work for nothing.
In my opinion articles/recent is much cleaner as it avoids filling your controller action with conditionals and also looks nicer from a user perspective.

Resources