RoR - Link_to with nested hash from an each loop - ruby-on-rails

Having some trouble with nested hash parameters. Product has many features. Say product 1 has features foo and baz.
I'd like to pass both the product and all of the features to query string parameters in a link_to so that it ends up looking like this:
"/puppies/new?features%5Bbaz%5D=qux&features%5Bfoo%5D=bar&product=1"
I am currently trying this, which gets a syntax error for reasons that are probably obvious to people that are not me.
<% Product.each do | product | %>
<%= link_to(new_puppy_path(product: product, features: { product.features.each
{ | feature| feature.name : 'feature.'} } ), class: 'slorp') do %>
// stuff inside the link
<% end %>
<% end %>
Any idea what I'm doing stupid here?
UPDATE: I have updated the code to:
<%= link_to(new_puppy_path(product: product, features: product.features.each{|feature| {feature.name.to_sym => feature.feature_colors.first}}), class: 'image') do %>
This is much closer, as my output URL is now:
/puppies/new?features%5B%5D=3&features%5B%5D=2&product=2
I am just missing the name of the feature in-between the %5B and %5D - not sure why the feature name is not showing up.

You need to use map instead of each. Each returns the original array itself on which it is operating. While map returns the elements from the block.

Fixed. Pulled creating the hash up into the model:
def reco_features
list = Hash.new
feature_colors.each do |feature_color|
list[feature_color.feature.name] = feature_color.id
end
return list
end
Then updated the link_to:
<%= link_to(new_puppies_path(product: product, features: features.reco_features ), class: 'slorp') do %>
Not sure why the inline wasn't working, but this fixed it. Required some additional context outside of the initial question, which I did not realize at the time. Apologies and thanks to all.

Related

How do I group objects returned by a REST API based on a value inside that object?

I'm pretty new to ruby/rails so bear with me.
I'm attempting to take the results returned by the JIRA rest API and render them in a view. I can do that pretty easily using the jira-ruby gem. The problem I'm having is grouping the results by a specific object inside the object returned by the API (in this case, a "components" field object inside of a "issue" object). I've attempted to use group_by and chunk for this but I'm basically getting the inverse of what I want. Both methods return the same result.
In my controller I have:
#issues = #jira_client.Issue.all
In my view I have:
<% #issues.chunk {|issue_comp| issue_comp.fields["components"]}.each do |comps, issues| %>
<h2>
<% comps.each do |comp| %>
<%= comp["name"] %>
<% end %>
</h2>
<ul>
<% issues.each do |issue| %>
<li><p><%= link_to issue.key, "http://localhost:2990/jira/browse/#{issue.key}" %> - <%= issue.summary %></p></li>
<% end %>
</ul>
<% end %>
What I end up with is:
CompA CompB
IssueA
CompC CompD
IssueB
CompA CompC CompD
IssueC
etc.
What I want is:
CompA
IssueA
IssueC
CompB
IssueA
CompC
IssueB
IssueC
CompD
IssueB
IssueC
The object returned by the API is a pretty convoluted object (i.e. giant array of hashes inside arrays inside of hashes). So, I have to dig pretty deep to get at the component name.
I get the feeling that this shouldn't be as complicated as it seems but I have a terrible habit of making things more complicated than they need to be. What am I doing wrong here?
EDIT: I created a gist of the full dump that is returned with the above call. Notice the "components" array:
jira-ruby gem dump for all issues
I took a look at the data you're getting back from Jira. This is how it looks to me:
There is an outer array of Jira Issues.
Each issue has an "attrs" hash
Each "attrs" hash contains components.
If this understanding is correct, I think you are attempting to invert that structure so that you can get a complete list of components, then iterate over each of them, and show the Issues that belong to that component.
If that understanding is correct, you have two basic choices:
Check if you can ask Jira for that information (so you don't have to generate it yourself), or
Build your own data structure (in memory on in a local DB as you prefer):
Some sample code for building a useful structure in-memory:
# in a controller, model, or service class (as you wish)
#components = {}
#jira_issues_array.each do |jira_issue| # from your API call
jira_issues[:components].each do |jira_component|
#components[jira_component[:key]] ||= { name: jira_component[:name], issue_keys: [] }
#components[jira_component[:key]][:issue_keys] << jira_issue[:key]
end
end
In your view, you could iterate over #components like this:
# some html.erb file:
<h1>Components and Issues</h1>
<ul>
<% #components.keys.each do |component_key, component| %>
<li><%= component[:name] %>
<ul> <!-- nested -->
<% component[:issue_keys].each do |issue_key| %>
<%= #jira_issues_array.find { |issue| issue[:key] == issue_key }[:name] %>
<% end %>
</ul>
</li>
<% end %>
</ul>
Note: Like a typical lazy programmer, I haven't tried this out, but it's really intended to show how you might go about it. For example, each issue's name is embedded in the attrs section, so you'll need to dig that out a bit.
Finally, if anyone would find this useful, I use this to analyse and reformat JSON.
HTH - any questions or problems, post a comment.

Rails output polymorphic associations

I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.

RoR - Missing Keys in Hashes inside of URL Parameter Array

I am creating a series of link_to’s, and I am passing some nested information as an array into each URL. My desired outcome looks like so:
?features%5B%5D%5BThick%5D=98&features%5B%5D%5BThin%5D=99
//For some legibility
?features[][Thick]=98&features[][Thin]=99
However, the keys to the hashes inside of the array are not showing up, and I am instead seeing:
?features%5B%5D%5B%5D=98&features%5B%5D%5B%5D=99
//For some legibility
?features[][]=98&features[][]=99
The erb that is creating this series of URLs is here:
<% #products.each |product| do %>
<%= link_to "", new_line_item_path(product_id: product, features: [product.features.each{|feature| {feature.name.to_sym => feature.feature_color_default}}])%>
<% end %>
Is this just a syntactic mistake or is it because I am taking the wrong approach?
**Perhaps this is too much information for this issue but Products have many Features which in turn have many Colors through Feature_Colors.
I'm not sure why the above wasn't working but I made the following changes and am all set. Hopefully the keywords here help someone else if they are making the same mistakes.
First, I pulled the hash creation out into the Product model, like so:
def default_features
list = Hash.new
features.each do |feature|
list[feature.name] = feature.feature_color_default_id
end
return list
end
Then I changed the link_to:
<%= link_to "", new_line_item_path(product_id: product, features: [product.default_features])%>
Working as desired now.

Exception Handling: "undefined method `____' for nil:NilClass"

One of the most common reasons my web application fails is because a user sometimes lacks a certain attribute that a view expects it to have. For instance, most users in my application have an education (school, degree, etc.) entry in our system, but some users do not. Assuming my view looks something like this:
<% #educations.each do |education| %>
<%= education.school %>
<%= education.degree %>
<% end %>
I want to avoid "Pokemon" exception handling and feel that there has to be a better way around dealing with a "undefined method `degree' for nil:NilClass" error in the case that a user does not have an education entry in our database. This just seems like an ugly/tedious fix:
<% #educations.each do |education| %>
<% if education.school %>
<%= education.school %>
<% end %>
<% if education.degree %>
<%= education.degree %>
<% end %>
<% end %>
Any input is appreciated. Thank you!
As long as you know the first object you're working on won't be nil, the easiest way is to just do this:
- #educations.each do |education|
= education.try :school
= education.try :degree
The #try method is pretty handy. You can also call .to_s on anything you think might be nil, Ie:
- #educations.each do |education|
= education.school.to_s
= education.degree.to_s
This will convert nils to an empty string. This isn't as useful in the view IMO, but comes in handy a lot of times if you have input that is expecting to be a string and might be empty. Ie a method like:
def put_in_parenthesis(string)
"(" + string.to_s + ")"
end
You have a couple of options here.
The easiest to implement is the try method. It is used like so:
<%= education.try( :degree ) %>
The problem is that try() is viewed as a bit of an anti-pattern. As the reference indicates, you can achieve similar functionality with something like:
<%= education && education.degree %>
This isn't really a lot different, intellectually, in my opinion. A popular way of handling this a little more cleanly is the Null Object pattern, which is basically an object with defined neutral ("null") behavior.

How to add "0," after each loop in Ruby

How to add "0," after each loop in Ruby (At the data: part of the series)
So first loop would
show only the value
next loop 0,value
next loop 0,0,value
next loop 0,0,0,value
etc..
series: [
<% #prot = ProjectTask.where("project_id = ? AND taskType = ?" ,#project.id, "Pre-Sales")%>
<% #prot.each do |prt| %>
<% hoursSum = 0 %>
{
name: '<%= prt.task_name%>',
data: [(here after each loop of #prot i want to add "0," here)<% #taskHours = Effort.where(:project_task_id => prt.id) %>
<% #taskHours.each do |th| %>
<% hoursSum = hoursSum + th.hours %>
<% end %>
<%= hoursSum%>
]
},<% end %>
<% #prot.each_with_index do |index, prt| %>
...
<%= "0," * index %>
...
<% end %>
This logic seems too complex to be in a view - I would migrate it to a helper function that builds up the data array and call that instead.
I think you'll find it a lot easier to do what you want then without having to deal with all the clutter of the erb tags etc. There's lots of ways you could do it - Yossi's suggestion of using each_with_index is a perfectly good one.
Two little things though - I would advise against shortened names for stuff like #prot - just call it #project_task. It's more readable and you can guarantee you call it the same thing throughout your code (instead of having some places where you call it #prot, others where it's #ptask etc) which will save you more time than you lose typing a longer name, I promise you.
Also - you use camelCase for some of your variables - I would advise sticking with the Ruby convention of snake_case.

Resources