I would like to display posts by users that I follow on a stream page.
I have a post controller which displays ALL posts from any users. Again I only want posts from users I follow to be displayed on the stream page. I created a new controller called 'Stream'. I am having a difficult time trying to get the users posts who I follow to display on my stream index.
Thank you in advance.
Stream Controller
class StreamController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.where(follower_id: current_user.id, followed_id: current_user.id)
end
end
Stream Index
<div class="page-header">
<center><strong><h1> Stream Page </h1></strong></center>
</div>
Post Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 40)
end
def show
end
def new
#post = current_user.posts.build
end
def edit
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
def correct_user
#post = current_user.posts.find_by(id: params[:id])
redirect_to posts_path, notice: "Not authorized to edit this post" if #post.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:description, :image)
end
end
Users Controller
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :admin_user, only: :destroy
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
def index
#users = User.paginate(page: params[:page], :per_page => 20)
end
def show
#user = User.find(params[:id])
if #user
#posts = #user.posts.order("updated_at DESC")
render actions: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Your account has been deleted."
redirect_to root_path
end
def correct_user
#user = User.find(params[:id])
redirect_to root_path
end
def admin_user
redirect_to root_path unless current_user.admin?
end
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
Migration
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
Relationship
I'd personally use a scope with an association:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :posts
has_many :subscribed, class_name: "Relationship", foreign_key: "followed_id"
has_many :followers, class_name: "Relationship", foreign_key: "follower_id"
end
#app/models/post.rb
Class Post < ActiveRecord::Base
belongs_to :user
scope :subscribed, ->(followers) { where user_id: followers }
end
#app/models/relationship.rb
Class Relationship < ActiveRecord::Base
#fields id | user_id | follower_id | created_at | updated_at
belongs_to :user
end
This will give you the ability to call the following:
#app/controllers/stream_controller.rb
Class StreamController < ApplicationController
def index
#posts = Post.subscribed current_user.followers
end
end
--
Alternative
An alternative would be as follows:
#app/views/stream/index.html.erb
<% current_user.subscribed.each do |followed| %>
<% followed.posts.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
--
Whilst I'm not sure if this will work out of the gate, it's the way I'd create the functionality you're seeking. Essentially, you have to be able to pass the "related" users to your query call, which will then return the objects belonging to the followed relation
The post variable in the stream controller needs to be an instance variable #posts to be available in the view, and to match the #posts instance variable you have in the view.
I guess it should look #posts = Post.subscribed #user.following instead of "current_user.followers". This should solve you problem of displaying people who are following you instead of showing users you follow.
Related
I am trying to build Nested Resources Route
First I generate user it's work will and create a user category controller and create item controller but I have a problem when I create an item
First category controller
class Api::V1::CategoriesController < ApplicationController
before_action :authenticate_with_token!, only: [:create, :update, :destroy]
before_action :set_category, only: [:show, :update, :destroy]
# GET /categories
def index
#categories = current_user.category.all
render json: #categories
end
# GET /categories/1
def show
render json: #category
end
# POST /categories
def create
#category = current_user.categories.new(category_params)
if #category.save
render json: #category, status: :created
else
render json: #category.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /categories/1
def update
if #category.update(category_params)
render json: #category
else
render json: #category.errors, status: :unprocessable_entity
end
end
# DELETE /categories/1
def destroy
#category.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_category
#category = current_user.category.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def category_params
params.require(:category).permit(:title)
end
end
second item controller
class Api::V1::ItemsController < ApplicationController
before_action :authenticate_with_token!, only: [:create, :update, :destroy]
before_action :set_item, only: [:show, :update, :destroy]
# GET /items
def index
#items = current_type.items.all
render json: #items
end
# GET /items/1
def show
render json: #item
end
# POST /items
def create
#item = current_type.item.new(item_params)
if #item.save
render json: #item, status: :created, location: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /items/1
def update
if #item.update(item_params)
render json: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# DELETE /items/1
def destroy
#item.destroy
#item.image.purge
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = current_type.item.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def item_params
params.require(:item).permit(:title, :price, :image)
end
end
Application controller
class ApplicationController < ActionController::API
def current_user
#current_user ||= User.find_by(auth_token: request.headers['Authorization'])
end
def authenticate_with_token!
render json: { errors: "Not authenticated" },status: :unauthorized unless user_signed_in?
end
def user_signed_in?
current_user.present?
end
def prepare_user
#user = User.find(params[:id])
end
def current_type
#current_type ||= Type.find(params[:id])
end
end
my routes
Rails.application.routes.draw do
devise_for :users
namespace :api do
namespace :v1 do
resources :sessions, :only => [:create, :destroy]
resources :users, :only => [:show, :create, :update, :destroy] do
resources :categories do
resources :items
end`
end
end
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
when I try to create the item
ActiveRecord::RecordNotFound (Couldn't find Type without an ID):
If you create an item you do a POST request to the path: /api/v1/users/:user_id/categories/:category_id/items
So the url wil look something like: /api/v1/users/1/categories/1/items
Then I guess the params that you receive are: user_id and category_id and you are missing the param ID that you are looking for.
You should check the logs for what params you receive and adjust your code to pick the right param.
Im getting this error in my my controller. It can't find the products id. Not to sure why it's getting an error.
class ProductsController < ApplicationController
before_action :set_product, only: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#products = current_user.products
end
def show
end
def new
#product = current_user.products.build
end
def edit
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to listing_products_path(#product), notice: "Saved..."
else
flash[:alert] = "Something went wrong..."
render :new
end
end
def update
if #product.update(product_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:description, :features, :listing, :location, :photo_upload, :pricing)
end
end
I a user has to be signed in order to create a product. In my models i have a products belongs_to user and User has_many products
You are trying to load the product with an id from the params before your index action. But index routes usually do not provide any params[:id].
To fix this bug just change
before_action :set_product, only: [:index, :new, :create]
to
before_action :set_product, only: [:show, :edit, :update]
Id No need for index action because this display list of All records change
before_action :set_product, only: [:index, :new, :create]
To
before_action :set_product, only: [:show, :edit, :update]
I am new in rails.
I have some little problem. I need add project to some user profile page. For example: User has to go to the pages of projects, and use button "add to profile".
It should add the project to the User Profile.
But when I use "add"
ActiveRecord::RecordNotFound (Couldn't find Profile with 'id'=):
app/controllers/projects_controller.rb:15:in `add'
I have three models:
class ProjectList < ActiveRecord::Base
belongs_to :profile
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :project_lists
has_many :profiles, through: :project_lists
has_many :charts
end
class Profile < ActiveRecord::Base
belongs_to :user
has_many :project_lists
has_many :projects, through: :project_lists
has_attached_file :avatar, styles: { thumb: ["64x64#", :jpg, :png] }
validates_attachment :avatar, content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }
end
My controllers:
class ProjectsController < ApplicationController
before_action :find_project, only: [:show, :edit, :update, :destroy]
def index
#projects = Project.all.paginate(page: params[:page], per_page: 5)
end
def new
#project = Project.new
end
def add
#profile = Profile.find(params[:profile_id])
#profile.projects << project
end
def edit
end
def update
if #project.update(project_params)
redirect_to projects_path
else
render 'edit'
end
end
def create
#project = Project.new(project_params)
##project.profiles.build(params[:profile_id])
if #project.save
flash[:create] = 'Project created'
redirect_to projects_path
else
render 'new'
end
end
def show
end
def destroy
#project.destroy
flash[:destroy] = 'Delete project'
redirect_to projects_path
end
private
def project_params
params.require(:project).permit(:project_name, :project_id, :description)
end
def find_project
#project = Project.find(params[:id])
end
end
profile controller:
class ProfilesController < ApplicationController
#before_action :authenticate_user!, except: [:index]
before_action :find_profile, only: [ :show, :edit, :update, :destroy]
def index
#profiles = Profile.all.paginate(page: params[:page], per_page: 5)
end
def show
end
def new
#profile = Profile.new
end
def add
end
def edit
end
def create
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.json { head :no_content }
format.js
format.html do
redirect_to '/'
end
else
format.json { render json: #profile.errors.full_messages,
status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.js
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
private
def profile_params
params.require(:profile).permit(:name, :birthday, :biography, :user_id, :avatar, :profile_id)
end
def find_profile
#profile = Profile.find(params[:id])
end
end
In my views a use helper:
<%= link_to 'Add', add_to_profile_path(#project), :class => 'btn btn-sm btn-info' %>
My routes:
Rails.application.routes.draw do
devise_for :users
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
resources :profiles
resources :charts
get '/projects/add_project/:project_id', to: 'projects#add', as: 'add_to_profile'
resources :projects
root 'profiles#index'
end
In console:
Started GET "/projects/add_project/2" for 127.0.0.1 at 2017-02-17 17:12:55 +0200
Processing by ProjectsController#add as HTML
Parameters: {"project_id"=>"2"}
Profile Load (0.1ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" = ? LIMIT 1 [["id", nil]]
Completed 404 Not Found in 4ms (ActiveRecord: 0.1ms)
ActiveRecord::RecordNotFound (Couldn't find Profile with 'id'=):
app/controllers/projects_controller.rb:15:in `add'
I be grateful for any help.
I found the solution.
First I changed model name project_list to profiles_projects.
Create ProfilesProjects controller for my model
class ProfilesProjectsController < ApplicationController
def create
#profiles_projects = _current_profile.profiles_projects.new(:project_id => params[:project_id])
if #profiles_projects.save
render status: 201, json: #profiles_projects
end
end
private
def _current_profile
#current_profile = current_user.profile
end
end
and it's working for me. Now I can add projects to Profile.
maybe it will help someone
I believe I followed all the steps correctly from the friendly_id github page. And I know it works because it changed my url from /1 to /sample-url. However, the problem is I can't edit and destroy anymore the pins in which the url I have changed.
I hope someone can help me fix this one. Thank you!
/pins_controller.rb
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
respond_to :html
def index
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 8)
respond_with(#pins)
end
def show
respond_with(#pin)
end
def new
#pin = current_user.pins.build
respond_with(#pin)
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: "Pin was successfully created."
else
render action: "new"
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: "Pin was successfully updated."
else
render action: "edit"
end
end
def destroy
#pin.destroy
respond_with(#pin)
end
def upvote
#pin = Pin.find(params[:id])
#pin.upvote_by current_user
redirect_to :back
end
def downvote
#pin = Pin.find(params[:id])
#pin.downvote_from current_user
redirect_to :back
end
private
def set_pin
#pin = Pin.friendly.find(params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
def pin_params
params.require(:pin).permit(:description, :image)
end
end
/pin.rb
class Pin < ActiveRecord::Base
acts_as_votable
belongs_to :user
has_attached_file :image, :styles => { :medium => '300x300>', :thumb => '100x100>' }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]
validates :image, presence: true
validates :description, presence: true
extend FriendlyId
friendly_id :description, use: :slugged
end
The culprit is #pin = current_user.pins.find_by(id: params[:id]) in correct_user.
Note that for edit, update and destroy actions, you are fetching the pin twice. Once in set_pin and once in correct_user. In the correct_user, you only need to check if the #pin.user_id == current_user.id.
Also, the way you have it now, your user authentication in authenticate_user! runs last, which will cause an error if an unauthenticated user submits a request to the edit action.
class PinsController < ApplicationController
#authenticate_user! must go first
before_action :authenticate_user!, except: [:index, :show]
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
respond_to :html
.... your actions here
private
def set_pin
#pin = Pin.friendly.find(params[:id])
end
def correct_user
unless #pin.user_id == current_user.id
redirect_to pins_path, notice: "Not authorized to edit this pin"
#you must return false to halt
false
end
end
def pin_params
params.require(:pin).permit(:description, :image)
end
end
My Users are Still getting set to Guest regardless of me setting their roles as Registered once registered. I'm really wondering why? Below is an image
class ApplicationController < ActionController::Base
include SessionsHelper
before_filter { |c| Authorization.current_user = c.current_user }
helper :all
protect_from_forgery
protected
def permission_denied
flash[:error] = "Sorry, you are not allowed to access that page."
redirect_to root_url
end
end
User Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.role == ["registered"]
if #user.save
flash[:success] = "Welcome to the Sample App!"
log_in #user
redirect_to #user
# Handle a successful save.
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :username, :id, :role)
end
end
USER MODEL
class User < ActiveRecord::Base
has_many :roles, :through => :interviews, :source => :user
before_create :set_default_role
ROLES = %w[ admin creator registered guest ]
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
private
def set_default_role
self.role = 'registered'
end
end
Authorization rules
authorization do
role :admin do
has_permission_on [:interviews, :exhibitions, :posts, :comments, :abouts, :products, :available_works], :to => [:index, :new, :show, :new, :create, :edit, :update, :destroy]
end
role :creator do
has_permission_on :interviews, :to => [:index, :show, :new, :create, :edit, :update, :destroy]
has_permission_on :comments, :to => [:edit, :update, :create, :destroy]
end
role :guest do
has_permission_on [:interviews, :exhibitions, :posts, :abouts, :products, :available_works, :home], :to => [:index, :show]
has_permission_on [:users], :to => [:new, :show, :edit, :create, :update, :destroy, :index]
end
role :registered do
has_permission_on [:interviews, :exhibitions, :posts, :abouts, :products, :available_works, :home], :to => [:index, :show]
has_permission_on [:sessions, :users], :to => [:new, :show, :edit, :create, :update, :destroy, :index]
has_permission_on :comments, :to => [:edit, :update, :create]
end
end
Interview Controller
class InterviewsController < ApplicationController
before_action :set_interview, only: [:show, :edit, :update, :destroy]
filter_resource_access
# GET /interviews
# GET /interviews.json
def index
if params[:tag]
#interviews = Interview.all.tagged_with(params[:tag])
else
#interviews = Interview.all
end
end
# GET /interviews/1
# GET /interviews/1.json
def show
end
# GET /interviews/new
def new
#interview = Interview.new
end
# GET /interviews/1/edit
def edit
#interview = Interview.find(params[:id])
end
# POST /interviews
# POST /interviews.json
def create
#interview = Interview.new(interview_params)
respond_to do |format|
if #interview.save
format.html { redirect_to #interview, notice: 'Interview was successfully created.' }
format.json { render :show, status: :created, location: #interview }
else
format.html { render :new }
format.json { render json: #interview.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /interviews/1
# PATCH/PUT /interviews/1.json
def update
respond_to do |format|
if #interview.update(interview_params)
format.html { redirect_to #interview, notice: 'Interview was successfully updated.' }
format.json { render :show, status: :ok, location: #interview }
else
format.html { render :edit }
format.json { render json: #interview.errors, status: :unprocessable_entity }
end
end
end
# DELETE /interviews/1
# DELETE /interviews/1.json
def destroy
#interview.destroy
respond_to do |format|
format.html { redirect_to interviews_url, notice: 'Interview was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_interview
#interview = Interview.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def interview_params
params.require(:interview).permit(:title, :description, :tags, :image, :taggings, :tag_list)
end
end