Caching Twitter Results with Dalli - ruby-on-rails

I'm new to Ruby (and Rails) and was hoping you could help clear up some confusion I'm experiencing.
I'm trying to integrate the Twitter gem into my website in order to get a user's latest tweet, and grab the link to their profile picture. The gem works great up until (what I think is) the 100th API call in an hour, after which Twitter will cut you off.
From what I've gathered, I need to cache the result for ~1 minute using memcache. There was some great pseudocode here but unfortunately, it was a bit over my head. I was hoping I could get some more specifics.
At the moment, I'm unsure where would I place that code? I want to display the twitter information in the application layout view, so would it go into a method in the application_helper.rb file?
My best attempt at figuring this out resulted in the following code, which throws a "Missing Helper File" error.
module ApplicationHelper
require "memcache"
def twitter
cache = MemCache.new
twitter = cache.get("twitter").first
if twitter.nil?
begin
twitter = Twitter.user("TwitterName")
cache.set("twitter", twitter, :expires_in => 1.minute) if twitter
rescue
twitter = default
end
end
return twitter
end
end

First enable caching and memcache for your environment (e.g. config/environments/production.rb)
# Use a different cache store in production
config.cache_store = :mem_cache_store
Then in the view you want to show tweets do something like this
<% cache("tweets", :expires_in => 42.minutes) do %>
<% Twitter.user_timeline("User").each do %>
.....
<% end %>
<% end %>

Related

How do I add page caching to my Rails app for visitors?

Say I have a Rails 4.2 site on Heroku with many Posts. When a post is viewed, the application hits the database to get the post's data and associated data from other models. Visitors (non-logged-in users) to my website should see Posts without any customizations, so displaying these pages should not require accessing the database. How can I set up simple page or action caching (or an equivalent) for all visitors to my site, so the database is skipped? Could I also let a CDN take over rendering these pages?
For starters, take a look at the Rails caching guide. Both page and action caching have been removed from Rails.
You can try fragment caching, which will grab the generated HTML of the post and store it away, though you'll need a conditional based on whether or not there is a current_user.
A super-simple version might look like:
app/views/posts/show.html.erb
<% cache_unless (current_user, #post) do %>
<%= #post.body %>
<% end %>
The cache method is going to be a read action if a cache exists for that post, and it will be a generate and write action if that cache doesn't exist.
Caching with an ActiveRecord object creates a key that includes updated_at by default, so if a post changes, the first visitor that loads the post will have a longer wait time, and every subsequent visitor will get the benefits of caching.
As for your other question, there are many other Ruby-based tools for serving static pages, and a review of all those is beyond my knowledge, and probably beyond the scope of this question. :)
Lanny Bose's answer really looks the best, but I'll add this link:
https://devcenter.heroku.com/articles/http-caching-ruby-rails#public-requests
The whole page is useful, but in particular it talks about marking requests as public or private. They are private by default, but if you mark it as public it allows pages to be served by an intermediary proxy cache.
The linked page talks about Rack::Cache + memcache as a proxy cache; I'm not sure if this still exists in rails 4.
This second heroku page talks about action caching, which could cache a specific action for you:
https://devcenter.heroku.com/articles/caching-strategies#action-caching
You don't really want caching here. You just want a conditional in both controller and view, like
# controller
if logged_in?
#post = Post.find(params[:id])
# load more records
end
# view
<% if #post %>
<%= #post.title %>
...
<% else %>
You're not logged in, blah blah.
<% end %>
.. assuming the controller is more than just loading a post record. I don't necessarily agree with conditionally loading the record as above, but your use case may require it.
Alternatively, if you DO require caching for performance reasons, you can just elegantly include the current user id in the fragment cache key like
<% cache "post-#{#post.id}-#{current_user ? current_user.id : 0}" do %>
<% if logged_in? %>
... # expensive operations
<% else %>
Please login to see more..
<% end %>
<% end %>
so that the cache works with the same code for users or anons.

How to use grackle gem to find hashtags in rails 3.2

I am using grackle gem to get the tweets on rails 3.i am able to get the tweets and show them on the view. Now I want to search the hashtags using this gem. Is it possible. And if yes,then please tell me how because I have already registered to twitter API and got the secret token and I am still trying to modify the code so as to get the hashtags, I already made use of the twitter API and I am able to get the output but how to modify/implement it in my working code.
my tweet.rb file
Class Tweet < ActiveRecord::Base
require 'grackle'
require 'twitter'
require "rubygems"
MY_APPLICATION_NAME = "mike1011"
attr_accessible :content, :created
####Connect to the Twitter API and pull down the latest tweets"""
def self.get_latest
##the call to twitter api works but how to modify this further to use the search api**
tweets = client.search.home_timeline? :screen_name => MY_APPLICATION_NAME # hit the API
##this works on twitter console
##https://api.twitter.com/1.1/search/tweets.json?q=%23haiku
end
private
def self.client
Grackle::Client.new(:auth=>{
:type=>:oauth,
:consumer_key=>'xxxxxxxxxxxxxxx',
:consumer_secret=>'xxxxxxxxxxxxxxx',
:token=>"2227985911-xxxxxxxxxxxxxxxxxx",
:token_secret=>"xxxxxxxxxxxxxxxxxxx"
})
end
end
My view file where I want the user to search the hashtags by using the search button and value filled in the textbox
<fieldset style="height:auto;width:auto">
<%= text_field_tag 'Search', 'Enter your hashtags'%>
<%= button_tag(type: 'button') do
content_tag(:strong, 'Search in twitter!',:id=>"search_button")
end
%>
</fieldset>
Yes, it is possible. If you are not going to save the tweets to a database it is better to create an api so you can call it from any place or put it all inside of a controller.
A benefit of doing so is that you can then use parameters.
Take a look at my code: https://github.com/eflores1975/stock-dashboard/blob/master/app/controllers/tweets_controller.rb
Basically it loads the tweets to an array and then using ActiveSupport::JSON.decode I convert it in a consumable object and start to massage it to suit my needs.
And,finally, I can use it as:
http://gentle-tor-3942.herokuapp.com/tweets/get_tweets?symbol=$aapl
Where symbol=$aapl is the hash tag that I am looking for.

Need help integrating youtube_it into my rails app

I am pretty new to rails so this may be an easy question, but I was looking to create a Rails app that would use youtube in it. I have found that youtube_it seems to be the gem of choice for a task like this but I am having trouble using it. Basically, I want to use the gem so that I can get videos from specific users, i.e. Stanford University and create a list of those videos with links to another page that has the video information and player. To test this out I tried the follow code:
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :yt_client
private
def yt_client
#yt_client ||= YouTubeIt::Client.new(:dev_key => dev_key)
end
end
home_controller.rb
class HomeController < ApplicationController
def index
#playlists = yt_client.playlists('stanforduniversity')
end
end
index.html.erb
<h3>Home</h3>
<% #playlists.each do |playlist| %>
<p>Playlist: <%= playlist %></p>
<% end %>
The problem with this is all I get for output on my home page is a list of something like this: #
My questions then are: is there a way to change this output into an actual title? Am I doing something wrong/forgetting a step? Or should I just use python code to use the Google API and put all the videos into my database(I already have some code for this) and just access it using my rails app?
Hope this is clear enough.
it looks like what you want to print out is the name of the playlist - but that is an attribute of the playlist object, so you'll need something like:
<% #playlists.each do |playlist| %>
<p>Playlist: <%= playlist.title %></p>
<% end %>
otherwise ruby is trying to "print" the playlist object - which just doesn't work the way you expect.
Note: I've not used this gem either, I'm gathering this info from the gem docs here: https://github.com/kylejginavan/youtube_it
https://github.com/kylejginavan/youtube_it or youtube_it is the new version of youtube_g. This was forked and enhanced. If you need any enhancements please reach out to me.
you have a complete demo that I made here
http://youtube-it.heroku.com/
included source!

Basecamp API Rails

I was wondering if someone could do me massive favour..
I really don't understand how to make use of APIs - so I was wondering if, using Basecamp as an example, someone could talk me though the basics.
So far I have an application with a dashboard controller/view, I have put basecamp.rb into my /lib directory, added the following to my application_controller:
def basecamp_connect
Basecamp.establish_connection!('XXXXXX.basecamphq.com', 'USER', 'PASS', false)
#basecamp = Basecamp.new
end
Obviously changing the required parts to my credentials.
Next up I have added the following to my dashboard_controller:
def index
Basecamp::TodoList.find(:all)
end
Next I presume I have to somehow list the Todos on the dashboard using some sort of loop.
Am I doing the right thing, if so - how on earth do I display all the todo items and if not - what am I doing wrong/missing.
It doesn't have to be todos, anything from Basecamp or any other popular API service would be a good start. It's just that I happen to have a basecamp account!
Thanks,
Danny
Your view expects to have some variables defined. You can loop through those variables and display their content as you want.
So you could do, in your action :
def index
#list = Basecamp::TodoList.find(:all)
end
Then in your view you have access to the #list variable and you can to the following :
<ul>
<% #list.each do |item| %>
<li><%= item.to_json</li>
<% end %>
</ul>
Replacing the json dump by the elements as you wish to display them of course.
You might want to read the rails guides to get a lot more of informations.

Best way to combine fragment and object caching for memcached and Rails

Lets say you have a fragment of the page which displays the most recent posts, and you expire it in 30 minutes. I'm using Rails here.
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
Obviously you don't need to do the database lookup to get the most recent posts if the fragment exists, so you should be able to avoid that overhead too.
What I'm doing now is something like this in the controller which seems to work:
unless Rails.cache.exist? "views/recent_posts"
#posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
Is this the best way? Is it safe?
One thing I don't understand is why the key is "recent_posts" for the fragment and "views/recent_posts" when checking later, but I came up with this after watching memcached -vv to see what it was using. Also, I don't like the duplication of manually entering "recent_posts", it would be better to keep that in one place.
Ideas?
Evan Weaver's Interlock Plugin solves this problem.
You can also implement something like this yourself easily if you need different behavior, such as more fine grained control. The basic idea is to wrap your controller code in a block that is only actually executed if the view needs that data:
# in FooController#show
#foo_finder = lambda{ Foo.find_slow_stuff }
# in foo/show.html.erb
cache 'foo_slow_stuff' do
#foo_finder.call.each do
...
end
end
If you're familiar with the basics of ruby meta programming it's easy enough to wrap this up in a cleaner API of your taste.
This is superior to putting the finder code directly in the view:
keeps the finder code where developers expect it by convention
keeps the view ignorant of the model name/method, allowing more view reuse
I think cache_fu might have similar functionality in one of it's versions/forks, but can't recall specifically.
The advantage you get from memcached is directly related to your cache hit rate. Take care not to waste your cache capacity and cause unnecessary misses by caching the same content multiple times. For example, don't cache a set of record objects as well as their html fragment at the same time. Generally fragment caching will offer the best performance, but it really depends on the specifics of your application.
What happens if the cache expires between the time you check for it in the controller
and the time it's beeing checked in the view rendering?
I'd make a new method in the model:
class Post
def self.recent(count)
find(:all, :limit=> count, :order=>"updated_at DESC")
end
end
then use that in the view:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<% Post.recent(20).each do |post| %>
...
<% end %>
<% end %>
For clarity, you could also consider moving the rendering of a recent post into its own partial:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>
You may also want to look into
Fragment Cache Docs
Which allow you to do this:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
Controller
unless fragment_exist?("recent_posts")
#posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
Although I admit the issue of DRY still rears its head needing the name of the key in two places. I usually do this similar to how Lars suggested but it really depends on taste. Other developers I know stick with checking fragment exist.
Update:
If you look at the fragment docs, you can see how it gets rid of needing the view prefix:
# File vendor/rails/actionpack/lib/action_controller/caching/fragments.rb, line 33
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
Lars makes a really good point about there being a slight chance of failure using:
unless fragment_exist?("recent_posts")
because there is a gap between when you check the cache and when you use the cache.
The plugin that jason mentions (Interlock) handles this very gracefully by assuming that if you are checking for existence of the fragment, then you will probably also use the fragment and thus caches the content locally. I use Interlock for these very reasons.
just as a piece of thought:
in application controller define
def when_fragment_expired( name, time_options = nil )
# idea of avoiding race conditions
# downside: needs 2 cache lookups
# in view we actually cache indefinetely
# but we expire with a 2nd fragment in the controller which is expired time based
return if ActionController::Base.cache_store.exist?( 'fragments/' + name ) && ActionController::Base.cache_store.exist?( fragment_cache_key( name ) )
# the time_fraqgment_cache uses different time options
time_options = time_options - Time.now if time_options.is_a?( Time )
# set an artificial fragment which expires after given time
ActionController::Base.cache_store.write("fragments/" + name, 1, :expires_in => time_options )
ActionController::Base.cache_store.delete( "views/"+name )
yield
end
then in any action use
def index
when_fragment_expired "cache_key", 5.minutes
#object = YourObject.expensive_operations
end
end
in view
cache "cache_key" do
view_code
end

Resources