Rails3 helpers and dynamic content - ruby-on-rails

I've been working to pull dynamic data from last.fm using youpy's "lastfm" gem. Getting the data works great; however, rails doesn't seem to like the dynamic portion. Right now, I have added the code to a helper module called "HomeHelper" (generated during the creation of the rails app) found in the helper folder:
module HomeHelper
##lastfm = Lastfm.new(key, secret)
##wesRecent = ##lastfm.user.get_recent_tracks(:user => 'weskey5644')
def _album_art_helper
trackHash = ##wesRecent[0]
medAlbumArt = trackHash["image"][3]
if medAlbumArt["content"] == nil
html = "<img src=\"/images/noArt.png\" height=\"auto\" width=\"150\" />"
else
html = "<img src=#{medAlbumArt["content"]} height=\"auto\" width=\"150\" />"
end
html.html_safe
end
def _recent_tracks_helper
lfartist1 = ##wesRecent[0]["artist"]["content"]
lftrack1 = ##wesRecent[0]["name"]
lfartist1 = ##wesRecent[1]["artist"]["content"]
lftrack1 = ##wesRecent[1]["name"]
htmltrack = "<div class=\"lastfm_recent_tracks\">
<div class=\"lastfm_artist\"><p>#{lfartist1 = ##wesRecent[0]["artist"]["content"]}</p></div>
<div class=\"lastfm_trackname\"><p>#{lftrack1 = ##wesRecent[0]["name"]}</p></div>
<div class=\"lastfm_artist\"><p>#{lfartist2 = ##wesRecent[1]["artist"]["content"]}</p></div>
<div class=\"lastfm_trackname\"><p>#{lftrack2 = ##wesRecent[1]["name"]}</p></div>
</div>
"
htmltrack.html_safe
end
end
I created a partial for each and added them to my Index page:
<div class="album_art"><%= render "album_art" %></div>
<div id="nowplayingcontain"><%= render "recent_tracks" %></div>
Great, this gets the data I need and displays on the page like I want; however, it seems that when the song changes, according to last.fm, it doesn't on my site unless I restart the server.
I've tested this using Phusion Gassenger and also WEBrick and it seems to do it on both. I had thought this might be an issue with caching of this particular page so I tried a couple of caching hacks to expire the page an reload. This didn't help.
I then came to conclusion that sticking this code in a helper file might not be the best solution. I don't know how well helpers handle dynamic content; such as this. If anyone has any insight on this, awesome!! Thanks everyone!

Your problem isn't that you're using a helper, the problem is that you're using class variables:
module HomeHelper
##lastfm = Lastfm.new(key, secret)
##wesRecent = ##lastfm.user.get_recent_tracks(:user => 'weskey5644')
that are initialized when the module is first read. In particular, ##wesRecent will be initialized once and then it will stay the same until you restart the server or happen to get a new server process. You should be able to call get_recent_tracks when you need it:
def _album_art_helper
trackHash = ##lastfm.user.get_recent_tracks(:user => 'weskey5644').first
#...
Note that this means that your two helpers won't necessarily be using the same track list.
You might want to add a bit of "only refresh the tracks at most once very minute" logic as well.

Related

Asynchronously call controller action to return URL to use with image_tag

I currently have the following view thats is loading a bunch of images, one for each character inside an array. The problem is that get_character_thumbnail calls an api to get the images url and thats being done synchronously when the user requests the page data.
<% #comic.characters.each do |character| %>
<div class = "col-sm-6 col-md-3">
<div class = "thumbnail">
<%= image_tag(get_character_thumbnail(character)) %>
</div>
<div class = "caption">
<p><%= character['name'] %></p>
</div>
</div>
<% end %>
I want to enforce that each call to get_character_thumbnail(character) will be done asynchronously and the page doesn’t get stuck.
def get_character_thumbnail(character)
response = HTTParty.get(character['resourceURI'], get_basic_api_options)
response['data']['results'][0]['thumbnail']['path'] + '/portrait_large.jpg'
end
Since I’m pretty new to ruby on rails I’m struggling a little bit to setup an ajax call to do the trick. Does anyone have a suggestion or a link that could help me?
Your routes should always return a response immediately. Here's one approach in which you're writing a custom endpoint to serve a single character image. Your html page would load quickly, then you can set the img src attributes to point at your image route.
Here's some example code to clarify:
some html.erb template
<% #comic.characters.each do |character| %>
<img src='/character_image/<%= character.id %>'>
<% end %>
a new route, get '/character_image/:id'
def character_image
#char = Character.find(id: params[:id])
img_path = "tmp/char_img_#{#char.id}.jpg"
unless File.exists?(img_path)
img_url = get_char_img(#char) # hit your API to get the url
`wget #{img_url} -O #{img_path}`
end
send_file img_path, type: 'image/jpg', disposition: 'inline'
end
This code will cache the image to avoid duplicate API requests if, say, your html page were refreshed.
I'm using tmp/ here because it's a write-enabled location on Heroku (which blocks filesystem writes to other locations). On other environments (locally, for example), you could choose to save the images to public/, which Rails serves statically by default.
You should make an AJAX call and get all the images in response.
Then store in Javascript object.
Reason for the Page Freeze -
Because you might not be using closure concept of JavaScript. When we load/read multiple image files on browser, it occur because in the first iteration File-1 wasn't load and it jump to next iteration which sometimes causes not to load few random image files and freezes the browser.
You can check my Image Preview library (https://github.com/palash-kulkarni/image_uploader/blob/master/image-uploader-1.0.0.js). Its in progress, but i know it will definitely help you.
Link to Demo (https://github.com/palash-kulkarni/image_uploader/tree/master/Demo)
$.each(files, function (_, file) {
if (image.types.test(file.type)) {
that.displayPreview(file, categoryName, image);
that.bindSingleDeleteEvent(categoryName, image);
} else {
that.clientMessage.display('#flash', image.failureMessage.invalidFileType);
}
});
// Iterate object of images
reader = new FileReader();
reader.onload = (function (img) {
return function (event) {
img.attr('src', event.target.result);
};
})(img);
reader.readAsDataURL(imageFile);
// End Iterator

Nokogiri Scraping Multiple URLS on Same Domain

I am trying to scrape multiple urls with Nokogiri that are on the same domain. For example cltampa.com/potlikker and cltampa.com/artbreaker. For both urls I am looking for the same two elements, the main header image for every blog post and the url of the headline. I have code that is working for grabbing images, but it is very inefficient and definitely not DRY. I need to grab the associated headline hrefs as well, so I can then wrap the images with them in my view.
My controller currently looks like this
def index
doc = Nokogiri::HTML(open('http://cltampa.com/blogs/potlikker'))
potlikker = doc.xpath('//*[#class="contentImageCenter"]/img/#src')
doc = Nokogiri::HTML(open('http://cltampa.com/blogs/artbreaker'))
artbreaker = doc.xpath('//*[#class="contentImageCenter"]/img/#src')
#images = potlikker + artbreaker
end
My view looks like this
<div id="container" class="container">
<% #images.each do |img|%>
<div class="item">
<img src="http://www.cltampa.com<%= img %>">
</div>
<% end %>
</div>
My first question is what is the most efficient way to parse multiple urls, because what I have now is not it. Would I create a separate method for that, any help on this would be awesome.
My next question is how would I grab the headline href at the same time as grabbing the image url. I have the xpath to grab both of them separately, but putting them together and then rendering them in my view is confusing me.
I've been referencing this answer Iterating through multiple URLs to parse HTML with Nokogori but haven't had any luck yet.
Thanks in advance.
PROGRESS UPDATE
def index
urls = %w[http://cltampa.com/blogs/potlikker http://cltampa.com/blogs/artbreaker http://cltampa.com/blogs/politicalanimals http://cltampa.com/blogs/earbuds http://cltampa.com/blogs/bedpost http://cltampa.com/blogs/dailyloaf]
#final_images = []
#final_urls = []
urls.each do |url|
blog = Nokogiri::HTML(open(url))
images = blog.xpath('//*[#class="contentImageCenter"]/img/#src')
images.each do |image|
#final_images << image
end
end
urls.each do |url|
blog = Nokogiri::HTML(open(url))
story_path = blog.xpath('//*[#class="postTitle"]/a/#href')
story_path.each do |path|
#final_urls << path
end
end
end
The above code is technically giving me what I need, just not sure on how to tie them together in the view. I need to wrap the final_urls around the final_images. I am sure there is a better way to do this, any info again is appreciated.
I should also add that I am experiencing a timeout on Heroku, so any advice on speeding this up, moving to a background task etc. would be very much appreciated. I am looking into it now, but not exactly sure the best route to take.

Ruby w/ Sinatra: what is the equivalent of a .js.erb from rails?

.js.erb's are nice, because you can use them to replace parts of a page without having to leave the current page, which gives a cleaner and unchopped up feel to the site / app.
Is there a way to use them in sinatra? or an equivalent?
Just add .js to the end of the symbol you're passing erb(). A la (to call mypage.js.erb):
erb "mypage.js".to_sym
Dirty, but it works.
Based on your description, I'm guessing that your desire is to have portions of a page editable and replaced via AJAX. If this is wrong, please clarify.
I do this in my Sinatra apps by including (my own) AJAXFetch jQuery library and writing code as shown below. This lets me use the partial both when rendering the page initially as well as when editing via AJAX, for maximum DRYness. The AJAXFetch library handles all AJAX fetch/swap through markup alone, without needing to write custom JS on the pages that use it.
helpers/partials.rb
require 'sinatra/base'
module Sinatra
module PartialPartials
ENV_PATHS = %w[ REQUEST_PATH PATH_INFO REQUEST_URI ]
def spoof_request( uri, headers=nil )
new_env = env.dup
ENV_PATHS.each{ |k| new_env[k] = uri.to_s }
new_env.merge!(headers) if headers
call( new_env ).last.join
end
def partial( page, variables={} )
haml page, {layout:false}, variables
end
end
helpers PartialPartials
end
routes/bug.rb
get '/bug/:bug_id' do
if #bug = Bug[params[:bug_id]]
# ...
haml :bug
end
end
# Generate routes for each known partial
partials = %w[ bugdescription bughistory bugtitle fixer
pain project relatedbugs status tags version votes ]
partials.each do |part|
[ part, "#{part}_edit" ].each do |name|
get "/partial/#{name}/:bug_id" do
id = params[:bug_id]
login_required
halt 404, "(no bug ##{id})" unless #bug = Bug[id]
partial :"_#{name}"
end
end
end
post "/update_bug/:partial" do
id = params[:bug_id]
unless params['cancel']=='cancel'
# (update the bug based on fields)
#bug.save
end
spoof_request "/partial/#{params[:partial]}/#{id}", 'REQUEST_METHOD'=>'GET'
end
views/bug.haml
#main
#bug.section
= partial :_bugtitle
.section-body
= partial :_bugdescription
<!-- many more partials used -->
views/_bugtitle.haml
%h1.ajaxfetch-andswap.editable(href="/partial/bugtitle_edit/#{#bug.pk}")= title
views/_bugtitle_edit.haml
%form.ajaxfetch-andswap(method='post' action='/update_bug/bugtitle')
%input(type="hidden" name="bug_id" value="#{#bug.id}")
%h1
%input(type="text" name="name" value="#{h #bug.name}")
%span.edit-buttons
%button(type="submit") update
%button(type="submit" name="cancel" value="cancel") cancel
sinatra really isn't meant to be a full stack framework. Its supposed to get you on the road very quickly. You could use an erb separately and then load into your sinatra code.

Template path in Rails 3

Let's say, I connected the route / to WelcomeController's index action.
Inside of the index.html.erb-Template I want to display the path of the template from Rails.root upwards, ie.
<h1> We are rendering: <%= how_do_i_do_this? %></h1>
to render to
<h1> We are rendering: app/views/presentation/index.html.erb</h1>
In Rails 2 I could access template.path, but this doesn't work anymore
Any ideas?
Because of how template rendering works in Rails, you will now be able to use __FILE__ for this instead. This works for me:
<%= __FILE__.gsub(Rails.root.to_s, "") %>
There may be a better way to do this however, but I couldn't find it when I went looking.
Ryan's answer works. If you also want to put your method in a helper, use Kernel#caller. Here is a method I'm using to do something similar:
def has_page_comment? code = nil
if code.nil?
# grab caller file, sanitize
code = caller.first.split(':').first.gsub(Rails.root.to_s,'').gsub('.html.erb','')
end
...
end

creating dynamic helper methods in rails

I am trying to create a bunch of dynamic helper methods like these:
show_admin_sidebar
show_posts_sidebar
show_users_sidebar
So far I have this in my helper.rb file:
#spits out a partial
def show_sidebar(name, show_sidebar = true)
#content_for_sidebar = render :partial => "partials/#{name}"
#show_sidebar = show_sidebar
end
def show_sidebar?
#show_sidebar
end
In my application layout file I have this: (NB - I'm using HAML):
- if show_sidebar?
= yield(:sidebar)
This allows me to say the following in my views:
- show_sidebar(:foo)
- show_sidebar(:bar)
And this renders the desired partial.
The problem with this is that I can only add one sidebar per page. So, I figure I need to have dynamic methods like: show_admin_sidebar, show_foo_sidebar.
So I have tried to do this:
def show_#{name}_sidebar(show_sidebar = true)
#name = name
#content_for_#{#name}_sidebar = render :partial => "partials/#{#name}"
#show_sidebar = show_sidebar
end
and then in my layout:
- if show_sidebar?
= yield("{#name}_sidebar")
But rails does not like this at all.
I have tried almost everything I can think of in my helper file and nothing works.
The reason I am using helper methods for this is because I want my content div to be 100% page width unless there is a sidebar present in which case the main content goes into a smaller div and the sidebar content goes into it's own..
If I can't get this working, then I can easily fix the problem by just adding the partials manually but I'd like to get my head round this....
Anyone got any experience with this kind of thing?
The entire approach to this was bizarrely overcomplicated, didn't follow Rails conventions at all, nor make the slightest bit of sense, and shame on prior respondents for enabling this approach instead of helping him to simplify. My apologies for being 13 months late with the answer.
Your controller should be deciding if a sidebar is to be shown or not, and setting an instance variable #side_bar_name to either nil or a sidebar name string. Then somewhere in shared view code, probably views/layouts/application.html.erb, you would have something as simple as this:
<% if #side_bar_name %>
<%= render :partial => "partials/#{#side_bar_name}" %>
<% end %>
Or better yet:
<%= render(:partial => "partials/#{#side_bar_name}") if #side_bar_name %>
If you want to use a helper (which is not a bad idea for keeping your code DRY and readable) it would basically be the same code, just moved into the helper.
<%= side_bar_helper %>
def side_bar_helper
render(:partial => "partials/#{#side_bar_name}") if #side_bar_name
end
What the controller does is up to you. It would probably do something like this:
if session[:show_side_bar]
# maybe use cookies instead of session, or store user preference in a database
#side_bar_name = session[:side_bar_name]
end
Here is a solution for you, however I wouldn't suggest too much metaprogramming:
#Add the following snippet to the proper helper module:
['admin','user','whatever'].each do |name|
class_eval{
"def show_#{name}_sidebar(show_sidebar = true)
#name = #{name}
#content_for_#{#name}_sidebar = render :partial => 'partials/#{#name}'
#show_sidebar = show_sidebar
end"
}
end
def show_#{name}_sidebar(show_sidebar = true)
That doesn't look like valid Ruby to me. Are you parsing and evaling this yourself or just throwing that right in the file and expecting it to work?

Resources