I am using Stream Rails to build an app with a feed. The user has some page which other users can follow. For that I created a page feed group. Here is my Post model -
class Post < ApplicationRecord
belongs_to :page
belongs_to :author, class_name: "User"
has_one :post_item, inverse_of: :post
accepts_nested_attributes_for :post_item
include StreamRails::Activity
as_activity
def activity_object
self.post_item
end
def activity_actor
self.author
end
end
However, the posts go to user's feed. How can I also make it go to the page feed group? I looked through the github code, and I think I have to override activity_owner_feed? But I also want the author to have a list of all the posts they have created. So the posts need to be in the user feed too.
Here is the corresponding code from my controller -
# this method creates the post
# This puts the post in user feed
# I also want to add the post to page feed
def create
#post = current_user.posts.build post_params
#post.save
render json: { success: true }
end
# show all posts of a user
def show
feed = StreamRails.feed_manager.get_user_feed(current_user.id)
enricher = StreamRails::Enrich.new
results = feed.get()['results']
#activities = enricher.enrich_activities(results)
render json: { activity: #activities }
end
Corresponding routes -
post 'post/create'
post 'post/show'
You need to override activity_notify and return an array of feeds to be notified.
It translates to to target in the generated activity.
Related
So i have 2 models Users and books. Books has user_id,name, User has name,adress. Both have views. I did create a link_to in user view to redirect to books view, but i want to redirect by id.
What i mean if i click in User1 link_to, i want to view only user1 books(books that have only user_id:1 if it is possible to see the most recent created at the beginning, so if i have created book.id=5 would get first and book.id=1 would be lastone).
User model:
Class User < ApplicationRecord
has_many :books
Book Model:
Class Book < ApplicationRecord
belongs_to :user
Controller book show:
def show
#book = Book.all
end
if you need anything else, just ask in comments and i will edit. Also sorry if im not explaining correctly.
In your books#index controller action
def index
#books = Book.all
#books = #books.where(user_id: params[:user_id]) if params[:user_id].present?
end
And in your users#show view
link_to "This user books", books_path(user_id: #user.id)
So if you will go to /books you will see all books
But if you click the link on user's page -- you will see only this user books
For me, the best soluction is leave the default methods and create a new one:
def show_user_books
#books = Book.where(user_id:params[:user_id]).order(created_at: :desc) if params[:user_id].present?
redirect_to books_path
end
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!
I am attempting to locate a parent object in a nested controller, so that I can associate the descendant resource with the parent like so:
# teams_controller.rb <snippet only>
def index
#university = Univeresity.find(params[:university_id])
#teams = #university.teams
end
When I call find(params[:university_id]) per the snippet above & in line 6 of teams_controller.rb, I receive ActiveRecord::RecordNotFound - Couldn't find University without an ID.
I'm not only interested in fixing this issue, but would also enjoy a better understanding of finding objects without having to enter a University.find(1) value, since I grant Admin the privilege of adding universities.
The Rails Guides say the following about the two kinds of parameters in a website:
3 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of
parameters possible in a web application. The first are parameters
that are sent as part of the URL, called query string parameters. The
query string is everything after “?” in the URL. The second type of
parameter is usually referred to as POST data. This information
usually comes from an HTML form which has been filled in by the user.
It’s called POST data because it can only be sent as part of an HTTP
POST request. Rails does not make any distinction between query string
parameters and POST parameters, and both are available in the params
hash in your controller:
It continues a little further down, explaining that the params hash is an instance of HashWithIndifferentAccess, which allows usage of both symbols and strings interchangeably for the keys.
From what I read above, my understanding is that Rails recognizes both parameters (URL & POST) and stores them in the same hash (params).
Can I pass the params hash into a find method in any controller action, or just the create/update actions? I'd also be interested in finding a readable/viewable resource to understand the update_attributes method thats called in a controller's 'update' action.
Please overlook the commented out code, as I am actively searching for answers as well.
Thanks in advance.
Here are the associated files and server log.
Webrick
teams_controller.rb
class TeamsController < ApplicationController
# before_filter :get_university
# before_filter :get_team
def index
#university = University.find(params[:univeristy_id])
#teams = #university.teams
end
def new
#university = University.find(params[:university_id])
#team = #university.teams.build
end
def create
#university = University.find(params[:university_id])
#team = #university.teams.build(params[:team])
if #team.save
redirect_to [#university, #team], success: 'Team created!'
else
render :new, error: 'There was an error processing your team'
end
end
def show
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def edit
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def update
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
if #team.update_attributes(params[:team])
redirect_to([#university, #team], success: 'Team successfully updated')
else
render(:edit, error: 'There was an error updating your team')
end
end
def destroy
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
#team.destroy
redirect_to university_teams_path(#university)
end
private
def get_university
#university = University.find(params[:university_id]) # can't find object without id
end
def get_team
#team = #university.teams.find(params[:id])
end
end
team.rb
class Team < ActiveRecord::Base
attr_accessible :name, :sport_type, :university_id
has_many :home_events, foreign_key: :home_team_id, class_name: 'Event'
has_many :away_events, foreign_key: :away_team_id, class_name: 'Event'
has_many :medias, as: :mediable
belongs_to :university
validates_presence_of :name, :sport_type
# scope :by_university, ->(university_id) { where(team_id: team_id).order(name: name) }
# scope :find_team, -> { Team.find_by id: id }
# scope :by_sport_type, ->(sport_type) { Team.where(sport_type: sport_type) }
# scope :with_university, joins: :teams
# def self.by_university(university_id)
# University.where(id: 1)
# University.joins(:teams).where(teams: { name: name })
# end
def self.by_university
University.where(university_id: university_id).first
end
def self.university_join
University.joins(:teams)
end
def self.by_sport_type(sport_type)
where(sport_type: sport_type)
end
def self.baseball
by_sport_type('Baseball/Softball')
end
end
university.rb
class University < ActiveRecord::Base
attr_accessible :address, :city, :name, :state, :url, :zip
has_many :teams, dependent: :destroy
validates :zip, presence: true, format: { with: /\A\d{5}(-\d+)?\z/ },
length: { minimum: 5 }
validates_presence_of :name, :address, :city, :state, :url
scope :universities, -> { University.order(name: 'ASC') }
# scope :by_teams, ->(university_id) { Team.find_by_university_id(university_id) }
# scope :team_by_university, ->(team_id) { where(team_id: team_id).order(name: name)}
def sport_type
team.sport_type
end
end
views/teams/index.html.erb
Placed in gists for formatting reasons
rake routes output: (in a public gist)
enter link description here
rails console
You're not going to want to have both:
resources :universities #lose this one
resources :universities do
resources :teams
end
As for params... you have to give a param. So, when you go to http://localhost:3000/teams there are no params, by default. If you go to http://localhost:3000/teams/3 then params[:id] = 3 and this will pull up your third team.
Keep in mind the nomenclature of an index. The index action of Teams, is going to list all of the teams. All of them. There is no one University there, so what are you actually trying to find? If anything, you'd have, for your University controller:
def show
#university = University.find(params[:id])
#teams = #university.teams
end
so, the address bar will be showing http://localhost:3000/universities/23, right? params[:id] = 23, then you can find the teams associated with that university.
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.