Ordering data w/ Middleman and Contentful - ruby-on-rails

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

Related

Conditionally insert date separator to array of chat messages

I'm working on a Chat feature, similar to Slack, where the messages are grouped by date. I'm using the groupdate gem to put all of the messages together in a hash, and then print them out in the browser where messages are grouped by the date they were sent on:
#messages = #chatroom.messages.group_by_day { |message| message.created_at }
- #messages.each do |day, messages|
%li.date_divider
= "#{day.strftime("%B %d")}"
%ul
- messages.each do |message|
%li= message.content
For performance, it's crazy to load months worth of messages all at once and I'm trying to think how I can approach this by paginating the messages and using some sort of infinite autoscroll. I can get this working fine using #pagy, #messages = pagy(#chatroom.messages) but all of it goes to hell when I use the group_by_day and returned in a hash. I was curious if you anyone could shed some input on how to approach this where I can still benefit from the user-experience by grouping messages by date, but also benefit from the performance in the case outlined above.
Alternative to using group_by_day, one suggestion is to paginate an array of messages ordered by date and conditionally add the extra date display in the loop to display when the next item have a new date compared to the previous one, but I'm unsure how to do this off the top of my head.
You're not far of an actual working version, when you do:
# controller
#pagy, #messages = pagy(#chatroom.messages)
The messages are stored in #messages. When you call #chatroom.messages.group_by_day you call group_by_day upon all messages belonging to the chatroom. Instead use #messages.group_by_day which holds the messages that are retuned by pagy.
-# view
- #messages = #messages.group_by_day { |message| message.created_at }
- #messages.each do |day, messages|
-# ...
For infinite scroll there you might want to use a library. The basic idea is to fetch messages from the server when the user scrolls close to the top or bottom of the page. You then insert them in the correct place in the HTML using JavaScript.
Note that you don't have to do one or the other. You could have an infinite scroll that groups the messages by date. Although it might be somewhat harder to implement.
I'm leaving the details out of this answer since that deserves it's own question. Note that you shouldn't create a new question instantly. First have a look around the internet for different libraries or methods to create your own, try to work something out. Then if you get stuck or have a specific question you should create a new question.
One thing you could do is to save the last date in a variable at the end of the outer loop (so after your messages have been printed. Once the condition day != previous_day is met, you print the divisor
You can pass a custom search to pagy in order to display the items in the way you want to be displayed. For example do something like:
#pagy, #messages = pagy(Chatroom.customSearch(params[:q]), items: 20)
In your Chatroom model you can create the customSearch function. Find all chatRoom messages you want, order and send them back to pagy. Hope it helps.

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.

How to properly parse JSON into usable output (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>

Problems working with the output of the YouTube_It gem in Rails

I've been messing around with the Youtube_It gem for the past couple of days as I'm trying to integrate it into a Rails 4 app I'm currently working on.
When I run a search query with it, it outputs an array with a ton of results. As long as I'm in the Rails console, there's no problem with manipulating it. A brief example :
require 'youtube_it'
client = YouTubeIt::Client.new(:dev_key => "dev_key")
data = client.videos_by(:query => "penguin")
data.videos.each { |v| puts v.unique_id }
This outputs a nice, tidy list of all the unique id's that were returned from the query.
When I try to do something similar within a view (I know it shouldn't really be in the view, it's simply there for testing at this point) it just outputs a huge jumbled list of what appears to be XML. This only seems to happen when I try to iterate using .each.
When I do something like :
<% data = client.videos_by(:query => "penguin") %>
<%= data.videos[1].unique_id %>
This returns exactly what I was expecting, one unique_id from the index that I chose. Great! But how do I get ALL the unique id's?
That's where I'm stuck and would really appreciate some help! :)
OK, two reasons (working from the gist you gave me on IRC, located here):
1) You are not actually using the same code as in the console. Your console uses puts unique_id which will print the value of the unique ID, but you are just using unique_id which will get the ID... and then do nothing with it. What you want is probably something like data.videos.map(&:unique_id) (or data.videos.map { |v| v.unique_id } in long form) which will return you an array of the IDs.
2) You are using <%=, which means 'evaluate this ruby line and output the return value onto the page'. The return value of an each statement is the object you called each on - ie. data.videos, so that is what is getting printed out.

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

Resources