Rails output polymorphic associations - ruby-on-rails

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.

Related

How do I elegantly check for presence of both the object and associated objects?

I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|

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 shows rows from model, which aren't created

I have this part of code:
<% current_user.meta_accounts.each do |meta_account| %>
<%= content_tag(:li, link_to(meta_account.name, 'javascript:void(0)')) %>
<% end %>
So, I want Rails to show all my MetaAccounts in list, but I get this:
<li>Wallet</li>
<li>Credit card</li>
<li>Debit card</li>
<li>Debts</li>
<li>javascript:void(0)</li> #This is the problem
So, it also shows me MetaAccount, which isn't created yet.
In my MetaAccounts table I have this. I'm using Postgres.
So, it also shows me the last row, where row_number is *. I don't know why, and how to avoid this.
Thanks for any help!
Try:
<% current_user.meta_accounts.select(&:persisted?).each do |meta_account| %>
<%= content_tag(:li, link_to(meta_account.name, 'javascript:void(0)')) %>
<% end %>
The * row you see in PostgreSQL is not an actual record, it's just a quick way to create new records. If you want to be sure, run the following query:
SELECT COUNT(*) FROM MetaAccounts WHERE user_id=1
It will return 4.
I think the problem comes from an unsaved MetaAccount object in current_user.meta_accounts. This could be the case for instance in a form, where you build an empty object bound to the form. Make sure you don't have a call to current_user.meta_accounts.build(...).
If so, you can simply skip in your each loop all MetaAccount objects with a blank name.

Presenting Information on home page from another model? Ruby on rails

I have a posts model... I am trying to display this model on the index page of the webpage.
<% #posts.each do |post| %>
<%= #posts[1].title %>
<%end%>
The problem with the above code is that it display the title 3 times because i have 3 posts. How do i edit this code to make it only display once.
You meant to do this:
<% #posts.each do |post| %>
<%= post.title %>
<%end%>
The each enumerator passes its variables one at a time into a block with the do |variable| syntax. So post is each item in #posts, one after another.
This is a really fundamental concept to Ruby and Rails: without understanding this further you probably won't get very far. I would recommend you go and read Programming Ruby -- a really excellent tutorial and reference to the Ruby programming language -- to understand how blocks and enumerators work.
If you use 'each' it displays all the posts because in your controller you had #posts = Posts.all. This gives all the posts written till now into the #posts instance variable and each enumerator prints all the posts
If you want only the title to be displayed and that too only for the first post, then you can write like this in your view
<%= #posts.first.title %>

Rails Check if User Id is in Array

I'm trying to build a condition based on wether or not a "user" is a "member". Basically I need a way of checking if the current_user.id matches any of the user_id of any members. The non-working code I have right now is:
<% if current_user = #page.members %>
you can view this content.
<% end %>
I'm looking for something along the lines of: "If current_user.id exists in the "user_id" of any members."
Something like this, based on the field names in your question:
<% if #page.members.map(&:user_id).include? current_user.id %>
You can view this content
<% end %>
Assuming your #page.members variable contains an array, you can use the include? method:
<% if #page.members.include? current_user %>
you can view this content.
<% end %>
If you're using an array of ids, you will of course need to change the test slightly to look for the current user's id:
<% if #page.members.include? current_user.id %>
you can view this content.
<% end %>
#member_ids = #page.members.map{|m| m.id()}
then check for the condition as below
#memeber_ids.include? current_user.id()
Has said before include? should do the thing.
I'm just answering to tell you about a gem called CanCan, that gives you easy access for authorization "helpers".
Why you should use CanCan instead of doing what you are actually doing?
Don't reinventing the weel most of the times it's a goob practice.
You are placing business logic on the view (bad practice).
CanCan most likely has been developed thinking on security, and all the best practices in mind.
You save some developing hours.
Sorry if I repeated myself.

Resources