How to properly parse JSON into usable output (Ruby on Rails) - ruby-on-rails

I'm using an API that I call with something like this:
Controller:
#bounces = SendgridToolkit::Bounces.new(API_USERNAME, API_PASSWORD)
View:
And that returns data like this: (for JSON, though an XML call is also possible)
[{"status"=>"550", "created"=>2015-07-06 18:37:38 UTC, "reason"=>"550 Unrouteable address ", "email"=>"jake#fake.com"}]
It's useful to me, but unfortunately it's not the way I want to present it in my Rails application. What's the best way to transform this data, since I don't have access to the poorly formatted string itself, only the code that requests it from the application I'm interacting with?
I'm having some issues coming up with the right question to ask Google, and would appreciate any suggestions you have to offer from experience.
Thanks in advance for your help!
EDIT:
For example, this returns an error no matter where I put it (The view or the controller), saying that string is an undefined method:
string = '[{"status":"550","reason":"550 Unrouteable address ","email":"jake#fake.com"}]'
#showbounces = #bounces.retrieve.to_json JSON.parse(string)
#newsletters = Newsletter.all
returns:
no implicit conversion of Symbol into Integer:
#showbounces = #bounces.retrieve.to_json JSON.parse(string)
Ultimately I'd like to extract select information from the JSON/XML and use it for things like graphs in my view that I can format with CSS and HTML.

You don't need to parse the JSON output string from the web service. The SendgridToolkit gem takes care of that. It returns the data as Ruby objects (arrays and/or hashes), ready to be used in your code.
For example, in your controller:
def index
bounces = SendgridToolkit::Bounces.new(API_USERNAME, API_PASSWORD)
#all_bounces = bounces.retrieve
end
and then in your view:
<ul>
<% #all_bounces.each do |bounce| %>
<li>Bounced <%= bounce['email'] %> (<%= bounce['reason'] %>)</li>
<% end %>
</ul>

Related

Ordering data w/ Middleman and Contentful

I feel like I'm taking crazy pills here! I've looked all over the internet and cannot find a solution...
The problem:
I am using contentful and middleman-contentful to essentially create a blog. The content type I am interested in displaying and ordering is called "post". So far I have managed to pull all my posts into /data/blog/posts using the following configuration:
activate :contentful do |f|
f.space = { blog: 'xxxxxxxx' }
f.access_token = 'xxxxxxxxxxxxxxxxx'
f.content_types = { posts: 'xxxxxxxx'}
end
note: I intentionally left out the IDs and tokens
The problem is when I attempt to output those posts in my .erb file using the following:
<% data.blog.posts.each do |id, post| %>
<h1><%= post.title %></h1>
<% end %>
This results in a list of post tiles (as expected) but there appears to be no order to the output. I would expect the output to be ordered by creation date by default. How can one go about adding this order or any other order for that matter?
PS, one of the problems seems to be that data.blog.posts is an object with post IDs as keys. That seems problematic. Thanks for your help! I'm just getting started with middleman so forgive my ignorance!
By default, Contentful does not provide any particular order for your entries.
You can specify order in the cda_query configuration parameter. You can find more information on this section of the contentful_middleman documentation: https://github.com/contentful/contentful_middleman#configuration
Therefore your configuration block should like like the following:
activate :contentful do |f|
f.space = { blog: 'xxxxxxxx' }
f.access_token = 'xxxxxxxxxxxxxxxxx'
f.content_types = { posts: 'xxxxxxxx'}
f.cda_query = { order: 'sys.createdAt' }
end
For more information on all the ordering possibilities, you can take a look at the Contentful Delivery API documentation: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/order
Hope you find this useful

can't convert String into Integer from inside hash

I am trying to display a given value from a block of search data per result in the listing. I am getting the error "can't convert String into Integer" How do I display the next level array inside my linked_agents?
My code for the particular column is such
<td>
<% if result['primary_type'] === "resource" or result['primary_type'] === "digital_object" or result['primary_type'] === "accession" %>
<%= display_agents(result) %>
<% end %>
</td>
and the code
def display_agents(hash, opts = {})
object = JSON.parse( hash["json"] )["linked_agents"]
object2 = object["_resolved"]
html = "<div class='audit-display-compact'><small>"
html << "<ul style='list-style-type:none'>"
html << "<li>#{object2}</li>"
html << "</ul>"
html << "</small></div><div class='clearfix'></div>"
html.html_safe
end
Here is what is inside linked_agents.
[{"_resolved"=>{"names"=>[{"sort_name"=>"John Smith"}]}}]
How can I get all the sort_name's to display the data? There could be more than one _resolved each containing a sort_name.
Thanks
More data would've been more helpful than less data, but essentially your problem is this: "json" contains a string. Nothing more. Not an object. Nothing you could reference with results[]. Assuming the JSON is all properly formatted, what you would need to do is something like this:
<%= JSON.parse( result["json"] )["linked_agents"]["_resolved"]["sort_name"] %>
Now, this is horribly sloppy, and not the way you're going to want to accomplish your goals (assuming this is even effective with your data set). In actuality, what you'll want to do is parse that JSON into a variable, and work through in hash form.
It is difficult to clearly keep the topics at work here because there isn't a clear question. I would encourage you to make sure you have a basic familiarity of ruby arrays, hashes, and nested structures consisting thereof. Additionally, take a look at how you're retrieving the data that goes into results["json"].
There are many ways to accomplish many things once your data is properly structured, but you need to have a clear understanding of these types of structure before you can work magic (and you need to possess a basic knowledge of what you aren't understanding before we can provide you with solutions).
I hope that is helpful. Feel free to discuss points of confusion in the comment, and I will update this answer as we approach a solution for your problem.

Better ways to return HTML from a method in rails?

this is my first post here so be nice ;-)
I am currently learning the ropes of RoR (and general programming concepts) and I'm not sure if how I am returning some data from a method is 'the right way' (or maybe the 'rails way') to do so.
I have a form that a user can enter a value into and my 'app' will poll the requested data from an external web service.
In my view, I have a table to contain said data and in one cell I've included a call to the following method:
extract of view:
<tr>
<td>Subscriber</td>
<%= is_subscribed?(#search_result.expiry) %>
</tr>
So, I'm calling this little is_subscribed? method (which I've stored in a helper) as per below:
def is_subscribed?(sub_date)
if sub_date >= Date.today
return '<td class="text-success">Yes</td>'.html_safe
else
return '<td class="bg-danger">No</td>'.html_safe
end
end
Depending on the result of the comparison, I return some HTML with one class and a value or some HTML with another class.
The above does work and the class is applied correctly in the resulting HTML. What I would like to know is whether there a simpler way to do this, is this way bad practise? I am also curious how would someone else handle this sort of task.
Thanks!
It's fine how you've done it. Here's a variant:
def subscribed_td(sub_date)
sub_date >= Date.today ?
content_tag(:td, 'Yes', class: 'text-success') :
content_tag(:td, 'No', class: 'bg-danger')
end
The main difference is simply the function name is more accurate imo, as I'd expect a function called is_subscribed? to return a boolean. It's also using content_tag to be a bit more concise, and a ternary instead of the if-then (which is not to everyone's taste). You could try to be fancy here with just a single content_tag expression and then use ternaries inside it to vary the arguments, but that's OTT DRY-ness imo.

Display all versions of individual records in Papertrail

I'm building a league system and currently it stores and updates the players 'elo score' depending on the result. Now, I'm trying to add in 'HighCharts' to display the players elo score over the season in a sweet looking line chart. Someone suggested I use Papertrail to store the updates and I have got that all installed.
Now here comes my problem, I can't seem to figure out how to spit out the users elo_score versions in an array easy for 'HighCharts' to use. I can get the last updates to elo_score:
Last updated score = <%= #player.versions.last.reify.elo_score %>
But I can't seem to find the syntax to spit out all the 'versions' for 'elo_score'. Something like "1000, 1020, 1043, 1020".
I've also tried:
<%= #player.versions.map { |version| version.reify.elo_score} %>
But this gives me "undefined method `elo_score' for nil:NilClass". While just <%= #player.versions.map { |version| version.reify %> spits out all information in the record and obviously not just the elo_score.
Can anyone help? Sorry if I've not made this clear, I'm absolute brand new to rails, and this is just a fun project in my spare time but I'm having a blast!
Thanks alot!
What you did here:
#player.versions.map { |version| version.reify.elo_score }
Is perfectly fine to take all those scores and put them in an array. The problem that you're getting (the nil:NilClass stuff) is coming because at least one reify is nil. That is, that some version doesn't have a reify.
If each version is supposed to have a reify, be sure to add that as a model validation, and find in your code where the reify is being set and see why it's nil.
If it's okay for a version to have a nil reify, you could accomplish it a number of ways, but the straightforward and explicit way would look like this:
elo_scores = []
#player.versions.each do |version|
unless version.reify.nil?
elo_scores << version.reify.elo_score
end
end
I would suggest putting this in to a method, like get_elo_scores, and then you could more easily call it like:
#player.get_elo_scores
EDIT For clarification from the comments:
Your User model (or Player model, whatever you named it) should have a method that looks like this:
def get_elo_scores
elo_scores = []
self.versions.each do |version|
unless version.reify.nil?
elo_scores << version.reify.elo_score
end
end
return elo_scores
end
I apologize for not making this clearer, but you won't have access to #player within this method because that only exists in the context of your controller and view. The above is now a proper instance method: it will call .versions upon itself, and the rest is fine. I also added an explicit return call at the end.
Now you will be able to call #player.get_elo_scores on any User (or Player) object.
Hope that helps!
Here's a one-liner version of #MrDanA's answer :
elo_scores = self.versions.map{|version| version.reify.elo_scores}
note that you can't check if version.reify.nil? though

When using a date, what can I do with a nil value?

So I've got an object in my database with a date field, except sometimes it will be nil. Is there a way in the view I can show this as a string value. Something like TBA maybe?
<%= #event.date || "TBA" %> should do it.
In response to your comments, yes, you could do this in the model but it's a bad idea. Why?
First of all, it is about presentation of the data so for that reason it belongs in the view.
Secondly, it could break things. If you did it in the model, #event.date would sometimes return a date and sometimes a string. What would happen if you called #event.date.hour and date was "TBA"? You'd get an error. The only fix would be to check for it everywhere, which would be horrible.
If you really are going to be doing it a lot you could create a helper method in application_helper.rb that could look something like this:
def date_or_tba(date)
date || "TBA"
end
So you could then write in your view:
<%= date_or_tba #event.date %>
Which isn't much less typing but would have the not inconsiderable advantage of restricting the use of the string "TBA" to only one place - which means if you ever need to change it (for I18n purposes for example) - it's really easy.

Resources