Hope you can help, I'm kind of at my wit's end here....I'm working on a Rails App and running Koala to connect with the Facebook Graph API. I want to maintain an up-to-date list of a user's friends who are using my app so I have been trying to implement the Realtime Updates functionality of the Koala gem. My app is deployed on Heroku and I'm running Resque with Redis-to-Go to manage calls from Facebook... unfortunately, I don't seem to be receiving any even though my test pings to the Facebook server are all returning true. Here's the code I'm working with:
Realtime Controller:
class RealtimeController < ApplicationController
skip_before_filter :logged_in_check
layout nil
VERIFY_TOKEN = "purple-rain"
def create
Friendship.real_time_update!(params)
render :text => 'success'
end
def index
Rails.logger.info("RealTimeController verification")
render :text=>Koala::Facebook::RealtimeUpdates.meet_challenge(params, VERIFY_TOKEN)
end
end
Friendship Controller:
class Friendship < ActiveRecord::Base
attr_accessible :friend_id, :user_id
belongs_to :users, :foreign_key => "user_id"
belongs_to :friends_ids, :class_name => "users", :foreign_key => "friend_id"
def self.get_friends(user)
#facebook ||= Koala::Facebook::API.new(user.oauth_token)
#friends = #facebook.get_connection(user.fbid, "friends")
#friends.each do |friend|
if #friend = User.find_by_fbid(friend["id"])
friendship = Friendship.find_or_create_by_user_id_and_friend_id!(user.id,
#friend.id)
end
end
end
def self.real_time_update!(payload)
RealtimeUpdate.new(payload).enqueue_updates!
end
class RealtimeUpdate < Struct.new(:payload)
def enqueue_updates!
remove_duplicate_ids.each do |entry|
if (user = User.find_by_uid(entry['uid']).try(:user))
Resque.enqueue(Resque::Job::UpdateFacebookFriends, user.id,
:since=>entry['time'])
end
end
end
protected
def remove_duplicate_ids
payload['entry'].each_with_object({}) do |entry, hash|
hash[entry['uid']] ||= [] << entry
end.values.collect { |update_payloads| update_payloads.min { |entry1, entry2|
entry1['time']<=>entry2['time'] } }
end
end
end
Resque Job
class Resque::Job::UpdateFacebookFriends
#queue = "facebook_friends"
def self.perform(uid, opts={})
::Timeout.timeout(1800) do
Friendship.get_friends!(User.find_by_fbid(uid))
end
end
end
Happy to provide any other info I can!!!
Thanks in advance!
Make sure that you have specified read_friendlists permission in the rails configuration:
provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], :scope => 'read_friendlists, friends_location'
And please use function get_connections:
#facebook ||= Koala::Facebook::API.new user.oauth_token
#friends = #facebook.get_connection user.fbid, :friends
Related
Been trying to get fb_graph working so i can get things like someones friendlist and i cannot get rid of this error. The ActiveSupport::Memoizable is included in the facebook class. Trying figure it out from a fb_graph example application here https://github.com/nov/fb_graph_sample
image of error: http://imgur.com/VXSHhJf
facebook model:
class Facebook < ActiveRecord::Base
def profile
#profile ||= FbGraph::User.me(self.access_token).fetch
end
class << self
extend ActiveSupport::Memoizable
def config
#config ||= if ENV['fb_client_id'] && ENV['fb_client_secret'] && ENV['fb_scope']
{
:client_id => ENV['fb_client_id'],
:client_secret => ENV['fb_client_secret'],
:scope => ENV['fb_scope'],
}
else
YAML.load_file("#{Rails.root}/config/facebook.yml")[Rails.env].symbolize_keys
end
rescue Errno::ENOENT => e
raise StandardError.new("config/facebook.yml could not be loaded.")
end
def app
FbGraph::Application.new config[:client_id], :secret => config[:client_secret]
end
def auth(redirect_uri = nil)
FbGraph::Auth.new config[:client_id], config[:client_secret], :redirect_uri => redirect_uri
end
def identify(fb_user)
_fb_user_ = find_or_initialize_by_identifier(fb_user.identifier.try(:to_s))
_fb_user_.access_token = fb_user.access_token.access_token
_fb_user_.save!
_fb_user_
end
end
end
and here is facebooks_controller
require 'rack/oauth2'
class FacebooksController < ApplicationController
before_filter :require_authentication, :only => :destroy
rescue_from Rack:.center.hero-unit
%h1 Welcome to Dropshare
%h2
This is the home page for Dropshare
%p (at least for time being)
= render 'layouts/facebook_signup'
= render 'layouts/drive_signup'
/
<haml:loud> provide(:title, 'Home')</haml:loud>
<h1>Home</h1>
<p>This is the home page (for the time being) for Dropshare</p>
Sign up now!
:OAuth2::Client::Error, :with => :oauth2_error
# handle Facebook Auth Cookie generated by JavaScript SDK
def show
auth = Facebook.auth.from_cookie(cookies)
authenticate Facebook.identify(auth.user)
redirect_to dashboard_url
end
# handle Normal OAuth flow: start
def new
client = Facebook.auth(callback_facebook_url).client
redirect_to client.authorization_uri(
:scope => Facebook.config[:scope]
)
end
# handle Normal OAuth flow: callback
def create
client = Facebook.auth(callback_facebook_url).client
client.authorization_code = params[:code]
access_token = client.access_token! :client_auth_body
user = FbGraph::User.me(access_token).fetch
authenticate Facebook.identify(user)
redirect_to dashboard_url
end
def destroy
unauthenticate
redirect_to root_url
end
private
def oauth2_error(e)
flash[:error] = {
:title => e.response[:error][:type],
:message => e.response[:error][:message]
}
redirect_to root_url
end
end
Solution
replace
ActiveSupport::Memoizable
with memoist and require 'memoist'
I think you may be running into the fact that ActiveSupport::Memoizable was deprecated & removed from Rails.
https://github.com/rails/rails/commit/36253916b0b788d6ded56669d37c96ed05c92c5c
The author of that gem is running this version of Rails in their gemfile, so I would presume it's supported through this:
gem 'rails', '~>3.2.11'
I'm guessing you're running a newer version of Rails.
I am new to rails developement and to the MVC architecture. I have a little application where I can add Videos' URLs from Dailymotion or Youtube and get the tweets related to that URL using the twitter gem in Ruby on Rails.
Now i'm able to store the tweets like this : (This is the video controller)
def show
#video = Video.find(params[:id])
# Creating a URL variable
url = #video.url
# Search tweets for the given video/url
#search = get_client.search("#{#video.url} -rt")
# Save tweets in database
#search.collect do |t|
tweet = Tweet.create do |u|
u.from_user = t.user.screen_name.to_s
u.from_user_id_str = t.id.to_s
u.profile_image_url = t.user.profile_image_url.to_s
u.text = t.text.to_s
u.twitter_created_at = t.created_at.to_s
end
end
I'm not sure if this is the right way to do it (doing it in the controller ?), and what I want to do now is to specify that those tweets that have just been stored belong to the current video. Also I would like to have some sort of validation that makes the controller look in the database before doing this to only save the new tweets. Can someone help me with that ?
My models :
class Video < ActiveRecord::Base
attr_accessible :url
has_many :tweets
end
class Tweet < ActiveRecord::Base
belongs_to :video
end
My routes.rb
resources :videos do
resources :tweets
end
This is an example of a "fat controller", an antipattern in any MVC architecture (here's a good read on the topic).
Have you considered introducing a few new objects to encapsulate this behavior? For example, I might do something like this:
# app/models/twitter_search.rb
class TwitterSearch
def initialize(url)
#url = url
end
def results
get_client.search("#{#url} -rt")
end
end
# app/models/twitter_persistence.rb
class TwitterPersistence
def self.persist(results)
results.map do |result|
self.new(result).persist
end
end
def initialize(result)
#result = result
end
def persist
Tweet.find_or_create_by(remote_id: id) do |tweet|
tweet.from_user = screen_name
tweet.from_user_id_str = from_user_id
tweet.profile_image_url = profile_image_url
tweet.text = text
tweet.twitter_created_at = created_at
end
end
private
attr_reader :result
delegate :screen_name, :profile_image_url, to: :user
delegate :id, :user, :from_user_id, :text, :created_at, to: :result
end
Notice the use of find_or_create_by ... Twitter results should have a unique identifier that you can use to guarantee that you don't create duplicates. This means you'll need a remote_id or something on your tweets table, and of course I just guessed at the attribute name (id) that the service you're using will return.
Then, in your controller:
# app/controllers/videos_controller.rb
class VideosController < ApplicationController
def show
#tweets = TwitterPersistence.persist(search.results)
end
private
def search
#search ||= TwitterSearch.new(video.url)
end
def video
#video ||= Video.find(params[:id])
end
end
Also note that I've removed calls to to_s ... ActiveRecord should automatically convert attributes to the correct types before saving them to the database.
Hope this helps!
I'm working on a small project and I'm a little stuck. I've follow a few tutorials online to get things moving and here is what I came up with. Everything works when it comes to authenticating with Twitter and using the Twitter gem to run something like;
current_user.twitter.home_timeline(count:10)
However, since Twitter has limits, I decided that the "current_user" that is authenticated with Omniauth, his/her tweets would be saved into the Database related to that user by User_id. Here's what I've got so far;
CONTROLLER:
def pull
current_user.newtweets.pull_tweets
end
MODEL (tweets.rb)
class Newtweets < ActiveRecord::Base
attr_accessible :content, :followers, :retweets, :screen_name, :time_date, :tweet_id,
:user_id
belongs_to :user
def self.pull_tweets
Twitter.home_timeline(since_id: maximum(:tweet_id)).each do |tweet|
unless exists?(tweet_id: tweet.id)
create!(
tweet_id: tweet.id,
followers: tweet.user.followers_count,
retweets: tweet.retweet_count,
time_date: tweet.created_at,
content: tweet.text,
screen_name: tweet.user.screen_name,
)
end
end
end
MODEL(user.rb)
...
has_many :newtweets
...
ROUTE:
match 'refresh' => 'tweet#pull'
So, I'm new at this, but that is what I've constructed so far. I do get an error with the setup above; "uninitialized constant User::Newtweet"
Now, if I go to the Controller and replace the following line in the Controller;
def pull
current_user.newtweets.pull_tweets.limit(100)
end
with;
def pull
Newtweets.pull_tweets
end
I get another error; Could not authenticate you
Any help on this would be great.
I figured it out, or so I think but I'm sure its not the cleanest approach but it works for what I need. Here it is;
def self.pull
tweets = User.current.twitter.home_timeline(since_id: maximum(:tweet_id))
tweets.each do |tweet|
unless exists?(tweet_id: tweet.id)
Newtweets.create({tweet_id: tweet.id, followers: tweet.user.followers_count, retweets: tweet.retweet_count, time_date: tweet.created_at, content: tweet.text, screen_name: tweet.user.screen_name, user_id: User.current })
end
end
end
I've been building messaging in a rails app for users to be able to send each other messages. I've looked at a few gems such as mailboxer but ultimately decided to build my own.
I'm hoping someone can help me put these pieces together. I've been following a similar question's answer here.
I'm testing in the rails console and I keep getting the following error:
undefined method `send_message' for #
How can I fix this?
Controller
class MessagesController < ApplicationController
# create a comment and bind it to an article and a user
def create
#user = User.find(params[:id])
#sender = current_user
#message = Message.send_message(#sender, #user)
flash[:success] = "Message Sent."
flash[:failure] = "There was an error saving your comment (empty comment or comment way to long)"
end
end
Routes
resources :users, :except => [ :create, :new ] do
resources :store
resources :messages, :only => [:create, :destroy]
end
Messages Model
class Message < ActiveRecord::Base
belongs_to :user
scope :sent, where(:sent => true)
scope :received, where(:sent => false)
def send_message(from, recipients)
recipients.each do |recipient|
msg = self.clone
msg.sent = false
msg.user_id = recipient
msg.save
end
self.update_attributes :user_id => from.id, :sent => true
end
end
You are invoking the method on a class level: Message.send_message. For this to work, it would expect a declaration like this:
def self.send_message(from, recipients)
# ...
end
But, you got this instead:
def send_message(from, recipients)
# ...
end
So, either invoke the method on the instance you need it for, or refactor to make it work on a class level.
I've set up authentication with the Foursquare API and my Rails app, and now it's time to add functionality. I am not too proficient with Rails - it is not my first language. I want to allow a merchant to connect with Foursquare (Working) and then direct the merchant to a page where they can see unique visitors to their venue, the number of checkins to the venue, and who the mayor of the venue is. I know what endpoints to use, I'm just not sure how to implement them in rails. (Access Token, etc.)
Thanks!
After Answer
I'm trying to implement Turd Ferguson's answer, but I'm not getting anywhere. I keep getting an error saying the method is undefined. I want to try a simple venue search as soon as the user is authenticated. (Create Action)
Also, I'm using OmniAuth for authentication.
sessions_controller.rb
class SessionsController < ApplicationController
require 'foursquare'
def create
auth_hash = request.env['omniauth.auth']
venues = Foursquare::search_venues("starbucks")
render :text => venues
end
def failure
end
def destroy
session[:user_id] = nil
render :text => "Logged out!"
end
def callback
code = params[:code]
#access_token = foursquare.access_token(code, callback_session_url)
session[:access_token] = #access_token
redirect_to examples_path
end
end
foursquare.rb
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => '0YO3F0JNZIPVKG1DE01MNPB132D4JZ0QYRQSOWTZQKHHOPKB', :client_secret => 'GMBOGWUNL2GIKZZXQPSLE4BMFNGB5LDHQREH2UKUCK1TJ1L0')
end
end
Have you looked into using a gem such as foursquare2?
Using the gem you could create a Foursquare class like:
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => 'your_client_id', :client_secret => 'your_secret')
end
end
You could then call this anywhere you wanted by doing something like:
venues = Foursquare::search_venues "foobar"