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
Related
I'm trying to implement retweet functionality on my app.
So I have my retweet_id in my tweets model
tweets schema
| user_id | content | created_at | updated_at | retweet_id
tweets.rb
belongs_to :user
has_many :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'
user.rb
has_many :tweets
And in my tweets controller
tweets_controller.rb
...
def retweet
#retweet = Tweet.new(retweet_params)
if #retweet.save
redirect_to tweet_path, alert: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end
Private
...
def retweet_params
params.require(:retweet).permit(:retweet_id, :content).merge(user_id: current_user.id)
end
In my view
tweets/show.html.erb
<%= link_to 'Retweet', retweet_tweet_path(#tweet.id), method: :post %>
My routes
resources :tweets do
resources :comments
resources :likes
member do
post :retweet
end
end
So when I try this I get an error
param is missing or the value is empty: retweet
So I remove .require from 'retweet_params' and that removes that error (though i'm unsure of how wise that is)
Then the link works but won't retweet - reverting to the fallback root_path specified in my action instead.
Unpermitted parameters: :_method, :authenticity_token, :id
Redirected to http://localhost:3000/
I'm not sure what i'm doing wrong. How can I get my retweets working? ty
The reason retweet_params raises an error is because your link link_to 'Retweet', retweet_tweet_path(#tweet.id), method: :post doesn't contain parameters like a new or edit form does. Instead you should create a new tweet that reference to tweet you want to retweet.
before_action :set_tweet, only: %i[show edit update destroy retweet]
def retweet
retweet = #tweet.retweets.build(user: current_user)
if retweet.save
redirect_to retweet, notice: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end
private
def set_tweet
#tweet = Tweet.find(params[:id])
end
The above should automatically link the new tweet to the "parent". If this doesn't work for some reason you could manually set it by changing the above to:
retrweet = Tweet.new(retweet_id: #tweet.id, user: current_user)
The above approach doesn't save any content, since this is a retweet.
If you don't want to allow multiple retweets of the same tweet by the same user, make sure you have the appropriate constraints and validations set.
# migration
add_index :tweets, %i[user_id retweet_id], unique: true
# model
validates :retweet_id, uniqueness: { scope: :user_id }
How do we access the content of a retweet? The answer is we get the content form the parent or source (however you want to call it).
There is currently no association that lets you access the parent or source tweet. You currently already have:
has_many :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'
To easily access the source content let's first add an additional association.
belongs_to :source_tweet, optional: true, inverse_of: :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'
has_many :retweets, inverse_of: :source_tweet, class_name: 'Tweet', foreign_key: 'retweet_id'
With the above associations being set we can override the content getter and setter of the Tweet model.
def content
if source_tweet
source_tweet.content
else
super
end
end
def content=(content)
if source_tweet
raise 'retweets cannot have content'
else
super
end
end
# depending on preference the setter could also be written as validation
validates :content, absence: true, if: :source_tweet
Note that the above is not efficient when talking about query speed, but it's the easiest most clear solution. Solving parent/child queries is sufficiently difficult that it should get its own question, if speed becomes an issue.
If you are wondering why I set the inverse_of option. I would recommend you to check out the section Active Record Associations - 3.5 Bi-directional Associations.
Right now the error you're seeing is the one for strong params in Rails. If you can check your debugger or the HTTP post request that's being sent, you'd find that you don't have the params that you're "requiring" in retweet_params
def retweet_params
params.require(:retweet).permit(:retweet_id, :content).merge(user_id: current_user.id)
end
This is essentially saying that you expect a nested hash for the params like so
params = { retweet: { id: 1, content: 'Tweet' } }
This won't work since you're only sending the ID. How about something like this instead?
TweetsController.rb
class TweetsController < ApplicationController
def retweet
original_tweet = Tweet.find(params[:id])
#retweet = Tweet.new(
user_id: current_user.id,
content: original_tweet.content
)
if #retweet.save
redirect_to tweet_path, alert: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end
end
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!
New to Ruby on Rails 4.0 and I am having some trouble getting my JSON to add the data from my joined table. I am using AngularJS on the front end, and I can't seem to find a good example on here to help me out.
In short, I have a Invite with a User ID. I have a User with a User ID. Knowing the Invite ID, I want to get the name of the User that corresponds to that invite.
Invite 1: UserID: 10
Invite 2: UserID: 11
Invite 3: UserID: 12
UserID: 10 Name: Owen
UserID: 11 Name Greg
Controller - Invites
# GET /invites/1
# GET /invites/1.json
def show
#invite = Invite.includes(:user).find(params[:id]).to_json(include: :user)
end
Model - Invite
class Invite < ActiveRecord::Base
belongs_to :user
has_one :meal
has_one :event
has_one :registry
end
Model - User
class User < ActiveRecord::Base
has_one :invite
end
However I only get this whenever I check the /invites/1.
{"id":1,"created_at":"2013-12-06T20:14:39.001Z","updated_at":"2013-12-06T20:58:15.597Z","event_id":7,"meal_id":8,"registry_id":0,"rsvp":false,"user_id":9}
Any help here is much appreciated!
Your show should be:
def show
#invite = Invite.includes(:user).find(params[:id])
respond_to do |format|
format.json { render :json => #invite.to_json(include: :user) }
end
end
I recommend you to use JBuilder http://rubygems.org/gems/jbuilder . You can use #invite.to_json, but JBuilder gives you much more control on the output.
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
I have two models:
class Solution < ActiveRecord::Base
belongs_to :owner, :class_name => "User", :foreign_key => :user_id
end
class User < ActiveRecord::Base
has_many :solutions
end
with the following routing:
map.resources :users, :has_many => :solutions
and here is the SolutionsController:
class SolutionsController < ApplicationController
before_filter :load_user
def index
#solutions = #user.solutions
end
private
def load_user
#user = User.find(params[:user_id]) unless params[:user_id].nil?
end
end
Can anybody help me with writing a test for the index action? So far I have tried the following but it doesn't work:
describe SolutionsController do
before(:each) do
#user = Factory.create(:user)
#solutions = 7.times{Factory.build(:solution, :owner => #user)}
#user.stub!(:solutions).and_return(#solutions)
end
it "should find all of the solutions owned by a user" do
#user.should_receive(:solutions)
get :index, :user_id => #user.id
end
end
And I get the following error:
Spec::Mocks::MockExpectationError in 'SolutionsController GET index, when the user owns the software he is viewing should find all of the solutions owned by a user'
#<User:0x000000041c53e0> expected :solutions with (any args) once, but received it 0 times
Thanks in advance for all the help.
Joe
EDIT:
Thanks for the answer, I accepted it since it got my so much farther, except I am getting another error, and I can't quite figure out what its trying to tell me:
Once I create the solutions instead of build them, and I add the stub of the User.find, I see the following error:
NoMethodError in 'SolutionsController GET index, when the user owns the software he is viewing should find all of the solutions owned by a user'
undefined method `find' for #<Class:0x000000027e3668>
It's because you build solution, not create. So there are not in your database.
Made
before(:each) do
#user = Factory.create(:user)
#solutions = 7.times{Factory.create(:solution, :owner => #user)}
#user.stub!(:solutions).and_return(#solutions)
end
And you mock an instance of user but there are another instance of User can be instanciate. You need add mock User.find too
before(:each) do
#user = Factory.create(:user)
#solutions = 7.times{Factory.create(:solution, :owner => #user)}
User.stub!(:find).with(#user.id).and_return(#user)
#user.stub!(:solutions).and_return(#solutions)
end
I figured out my edit, when a find is done from the params, they are strings as opposed to actual objects or integers, so instead of:
User.stub!(:find).with(#user.id).and_return(#user)
I needed
User.stub!(:find).with(#user.id.to_s).and_return(#user)
but thank you so much shingara you got me in the right direction!
Joe