I'm building a blog that includes tiered admin permissions. When I'm signed in using our seeded admin account I can see the index just fine. When I go to the page not signed in, something that should be filtered by the before_action I get an error saying undefined method `admin=' for nil:NilClass in my index view.
When I'm not signed in, current_user should be nil and the if statement in the view should resolve then as false. Code snippets for reference are below.
Here's the view:
<div id="blog">
<%= render 'blogs/blog_header' %>
<div class="messages">
<%= render 'layouts/flash_messages' %>
</div>
<%= will_paginate #bloggers %>
<% #bloggers.each do |blogger| %>
<div class="post_wrapper">
<h2 class="title"><%= blogger.name %></h2>
<p class="date_and_author" style="display:inline"><%= blogger.email %></p>
<% if current_blogger.admin = 1 %>
<p class="date_and_author" style="display:inline">||</p>
<button class="button" style="display:inline"><%= link_to "delete", blogger, method: :delete,
data: { confirm: "You sure?" } %></button>
<% end %>
</div>
<% end %>
<%= will_paginate %>
The controller:
class BloggersController < ApplicationController
before_action :signed_in_blogger, only: [:show, :edit, :update, :destroy]
# GET /bloggers
# GET /bloggers.json
def index
#bloggers = Blogger.paginate(page: params[:page], per_page: 20)
end
# GET /bloggers/1
# GET /bloggers/1.json
def show
#blogger = Blogger.find(params[:id])
end
# GET /bloggers/new
def new
#blogger = Blogger.new
end
# GET /bloggers/1/edit
def edit
end
# POST /bloggers
# POST /bloggers.json
def create
#blogger = Blogger.new(blogger_params)
if #blogger.save
blog_sign_in #Blogger
flash.now[:success] = "New account successfully created."
redirect_to "/blogs"
else
render 'new'
end
end
# PATCH/PUT /bloggers/1
# PATCH/PUT /bloggers/1.json
def update
respond_to do |format|
if #blogger.update(blogger_params)
format.html { redirect_to #blogger, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #blogger }
else
format.html { render :edit }
format.json { render json: #blogger.errors, status: :unprocessable_entity }
end
end
end
# DELETE /bloggers/1
# DELETE /bloggers/1.json
def destroy
Blogger.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to bloggers_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def blogger_params
params.require(:blogger).permit(:name, :email, :password, :admin, :password_confirmation)
end
# Confirms a signed-in user
def signed_in_blogger
unless blog_signed_in?
flash[:error] = "Please sign in."
redirect_to blog_signin_path
end
end
end
And the helper:
# Logs in the given blogger
def blog_sign_in(blogger)
session[:blogger_id] = blogger.id
end
# Returns the current logged-in blogger (if any)
def current_blogger
#current_blogger ||= Blogger.find_by(id: session[:blogger_id])
end
# Returns true if the blogger is logged in, false otherwise.
def blog_signed_in?
!current_blogger.nil?
end
def blog_sign_out
session[:blogger_id] = nil
#current_blogger = nil
end
Could this be somewhat related to a separate error in which when trying to create a new blogger, after hitting create, I get "undefined method id for nil:Class?" Any help would be greatly appreciated.
Pasting blogger model per request:
class Blogger < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :admin, :remember_token
has_secure_password
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: true
validates :password, length: { minimum: 5 }
validates :password_confirmation, presence: true
def Blogger.new_remember_token
SecureRandom.urlsafe_base64
end
def Blogger.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = Blogger.digest(Blogger.new_remember_token)
end
end
If you're not signed in and current_blogger is nil as you say, admin won't be an available method. To avoid this error, change your if statement in the view to something like this.
<% if current_blogger && current_blogger.admin == 1 %>
You could also use the try method, which will also work
<% if current_blogger.try(:admin) == 1 %>
Related
Im trying to upload a photo through my Rails app and everything is fine until I click on "update...". Then it throws me an "ActionController::UnknownFormat" error and fails. Here you can find the form I'm using, the update controller and the model I'm referring to.
Form:
<%= form_with(model: current_user, local: true, html: {multipart: true}) do |form| %>
<div class="field">
<%= form.label :profile_pic %>
<%= form.file_field :profile_pic, id: :profile_pic %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Update method:
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
render action: :edit
end
end
end
Error seems to be here:
respond_to do |format|
model:
require 'digest'
class User < ApplicationRecord
mount_uploader :profile_pic, ProfilePicUploader
attr_accessor :password
before_save :encrypt_new_password
after_create :build_profile
has_one :profile
has_many :trips
has_many :comments, through: :trips, source: :comments
has_many :posts, through: :trips, source: :posts
scope :recent_comments, ->{where("comments.created_at > ? AND user_id = ?", [6.months.ago, self.id]).limit(3)}
#friends
has_many :users
validates :email, uniqueness: {case_sensitive: false, message: 'El correo debe ser Ășnico'}, length: {in: 6..50, too_short: "debe tener al menos %{count} caracteres"}, format: {multiline: true,with: /^.+#.+$/, message: "formato de correo no valido"}
validates :password, confirmation: true, length: {within: 4..20}, presence: {if: :password_required?}
validates :password_confirmation, presence: true
def self.authenticate(email,password)
user = find_by_email(email)
return user if user && user.authenticated?(password)
end
def authenticated?(password)
self.hashed_password == encrypt(password)
end
protected
def encrypt_new_password
return if password.blank?
self.hashed_password = encrypt(password)
end
def password_required?
hashed_password.blank? || password.present?
end
def encrypt(string)
Digest::SHA1.hexdigest(string)
end
def build_profile
Profile.create(user: self, name: self.name, bio:"Im using Tripper!")
end
end
If anyone could tell me please what did I do wrong...
You have used the respond_to block, but you didn't specify the format in which edit action should be rendered.
Try updating the update action as below:
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render 'edit'} # Specify the format in which you are rendering the 'edit' page
format.json { render json: #user.errors } # If required, specify a json format as well
end
end
end
Try to the following by updating the update action
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
flash[:notice] = 'User was successfully updated.'
format.html {redirect_to user_path(#user) }
format.json { render :show, status: :ok, location: #user }
else
render action: :edit
end
end
end
Hope to help
I have problem with the comments.
If I comment a status whatever it is, the comment will be displayed on the last status.
Hope you could help, I can edit with more code if needed, let me know what you need.
_form.html.erb
<%= simple_form_for([status, status.comments.new]) do |f|%>
<%= f.input :content %>
<%= f.button :submit, "Comment", class:"btn btn-primary"%>
<% end %>
index.html.erb
<% #statuses.each do |status| %>
<%= image_tag status.user.avatar.thumb if status.user.avatar?%>
<%= status.user.full_name%>
<%= simple_format(status.content) %>
<%= link_to time_ago_in_words(status.created_at) + " ago", status %>
<% if status.user == current_user %>
<span class="admin">
<%= link_to "Edit", edit_status_path(status) %> |
<%= link_to "Delete", status, method: :delete, data: {confirm: "Are you sure?"} %>
</span>
<% end %>
<% status.comments.each do |comment| %>
<%= comment.content %>
<% end %>
<%= render partial: "comments/form", locals: {status: status} %>
<% end %>
routes.rb
Rails.application.routes.draw do
get 'profiles/show'
devise_for :users
devise_scope :user do
get 'register', to: 'devise/registrations#new', as: :register
get 'login', to: 'devise/sessions#new', as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
end
resources :statuses do
resources :comments
end
resources :comments
get 'feed', to: "statuses#index", as: :feed
root "statuses#index"
get '/:id', to: "profiles#show"
end
statuses_controller.rb
class StatusesController < ApplicationController
before_filter :authenticate_user!, only: [:new, :create, :edit, :update]
before_action :set_status, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
#statuses = Status.all
#comments = Comment.all
end
def show
#status = Status.find(params[:id])
#comments = #status.comments.all
end
def new
#status = Status.new
#comment = #status.comments.build
end
def create
#status = Status.new(status_params)
#status.user = current_user
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render :show, status: :created, location: #status }
else
format.html { render :new }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #status.update(status_params)
format.html { redirect_to #status, notice: 'Status was successfully updated.' }
format.json { render :show, status: :ok, location: #status }
else
format.html { render :edit }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def destroy
#status.destroy
respond_to do |format|
format.html { redirect_to statuses_url, notice: 'Status was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_status
#status = Status.find(params[:id])
end
def status_params
params.require(:status).permit( :content)
end
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#status = Status.find_by(params[:status_id])
#comment = #status.comments.create(params_comment)
#comment.save
redirect_to statuses_path
end
def index
#statuses = Status.all
#comments = Comment.all
#comment = Comment.find_by(params[:id])
end
def new
#status = Status.find(params[:status_id])
#comment = Comment.new
end
private
def params_comment
params.require(:comment).permit(:content)
end
end
my models
class Comment < ActiveRecord::Base
belongs_to :status
belongs_to :user
end
class Status < ActiveRecord::Base
belongs_to :user
has_many :comments
default_scope -> { order(created_at: :DESC)}
validates :content, presence: true,
length: {minimum: 2}
validates :user_id, presence: true
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
mount_uploader :avatar, AvatarUploader
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
has_many :statuses
has_many :users
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A[a-zA-Z0-9_-]+\z/,
message: "Must be formatted correctly." }
def full_name
first_name + " " + last_name
end
def gravatar_url
stripped_email = email.strip
end
end
Thank you
This is when I commen as "Nelly Johan" here on my own status...
And this my comment not where I expected:
So I am working on an assignment at the moment, where I am trying to display favorited posts. I currently have the favorited post displayed, but when I click it, it doesn't doesn't redirect me to anywhere.
Here is the code I currently have:
User#show where I am currently trying to display the favorited posts:
<div class="row">
<div class="col-md-8">
<div class="media">
<br />
<% avatar_url = #user.avatar_url(128) %>
<% if avatar_url %>
<div class="media-left">
<%= image_tag avatar_url, class: 'media-object' %>
</div>
<% end %>
<div class="media-body">
<h2 class="media-heading"><%= #user.name %></h2>
<small>
<%= pluralize(#user.posts.count, 'post') %>,
<%= pluralize(#user.comments.count, 'comment') %>
</small>
</div>
</div>
</div>
</div>
<h2>Posts</h2>
<%= posts_exists? %>
<%= render #user.posts %>
<h2>Comments</h2>
<%= comments_exists? %>
<%= render #user.comments %>
<h2>Favorites</h2>
<% #posts.each do |post| %>
<%= render partial: 'votes/voter', locals: { post: post } %>
<%= link_to post.title, topic_post_path(#topic, post) %>
<%= image_tag current_user.avatar_url(48), class: "gravatar" %>
<%= post.comments.count %> Comments
<% end %>
The error is occuring on the following line:
<%= link_to post.title, topic_post_path(#topic, post) %>
Here is the output from the error:
ActionView::Template::Error (No route matches {:action=>"show", :controller=>"posts", :id=>"54", :topic_id=>nil} missing required keys: [:topic_id]):
29: <h2>Favorites</h2>
30: <% #posts.each do |post| %>
31: <%= render partial: 'votes/voter', locals: { post: post } %>
32: <%= link_to post.title, topic_post_path(#topic, post) %>
33: <%= image_tag current_user.avatar_url(48), class: "gravatar" %>
34: <%= post.comments.count %> Comments
35: <% end %>
app/views/users/show.html.erb:32:in `block in _app_views_users_show_html_erb__1919900632491741904_70127642538380'
app/views/users/show.html.erb:30:in `_app_views_users_show_html_erb__1919900632491741904_70127642538380'
Obviously Topid.id is nil, but I can't figure out why. I'm going to provide you with everything I think you could need? I know this is probably a simple nooby issue, but I've been stuck on it for nearly an entire day already.
Here is my User#Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new
#user.name = params[:user][:name]
#user.email = params[:user][:email]
#user.password = params[:user][:password]
#user.password_confirmation = params[:user][:password_confirmation]
if #user.save
flash[:notice] = "Welcome to Bloccit #{#user.name}!"
create_session(#user)
redirect_to root_path
else
flash[:error] = "There was an error creating your account. Please try again."
render :new
end
end
def show
#user = User.find(params[:id])
#posts = #user.posts.visible_to(current_user)
#posts = Post.joins(:favorites).where('favorites.user_id = ?', #user.id)
#favorites = current_user.favorites
end
end
Here is my Post#Controller:
class PostsController < ApplicationController
before_action :require_sign_in, except: :show
before_action :authorize_user, except: [:show, :new, :create]
def show
#post = Post.find(params[:id])
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
end
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(post_params)
#post.user = current_user
if #post.save
#post.labels = Label.update_labels(params[:post][:labels])
flash[:notice] = "Post was saved."
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
#post.assign_attributes(post_params)
if #post.save
#post.labels = Label.update_labels(params[:post][:labels])
flash[:notice] = "Post was updated."
redirect_to [#post.topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :edit
end
end
def destroy
#post = Post.find(params[:id])
if #post.destroy
flash[:notice] = "\"#{#post.title}\" was deleted successfully."
redirect_to #post.topic
else
flash[:error] = "There was an error deleting the post."
render :show
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def authorize_user
post = Post.find(params[:id])
unless current_user == post.user || current_user.admin?
flash[:error] = "You must be an admin to do that."
redirect_to [post.topic, post]
end
end
end
Here is my Topics#Controller:
class TopicsController < ApplicationController
before_action :require_sign_in, except: [:index, :show]
before_action :authorize_user, except: [:index, :show]
def index
#topics = Topic.all
end
def show
#topic = Topic.find(params[:id])
end
def new
#topic = Topic.new
end
def create
#topic = Topic.new(topic_params)
if #topic.save
#topic.labels = Label.update_labels(params[:topic][:labels])
redirect_to #topic, notice: "Topic was saved successfully."
else
flash[:error] = "Error creating topic. Please try again."
render :new
end
end
def edit
#topic = Topic.find(params[:id])
end
def update
#topic = Topic.find(params[:id])
#topic.assign_attributes(topic_params)
if #topic.save
#topic.labels = Label.update_labels(params[:topic][:labels])
flash[:notice] = "Topic was updated."
redirect_to #topic
else
flash[:error] = "Error saving topic. Please try again."
render :edit
end
end
def destroy
#topic = Topic.find(params[:id])
if #topic.destroy
flash[:notice] = "\"#{#topic.name}\" was deleted successfully."
redirect_to action: :index
else
flash[:error] = "There was an error deleting the topic."
render :show
end
end
private
def topic_params
params.require(:topic).permit(:name, :description, :public)
end
def authorize_user
unless current_user.admin?
flash[:error] = "You must be an admin to do that."
redirect_to topics_path
end
end
end
Here is my User Model:
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
before_save { self.email = email.downcase }
before_save { self.role ||= :member }
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, length: { minimum: 1, maximum: 100 }, presence: true
validates :password, presence: true, length: { minimum: 6 }, if: "password_digest.nil?"
validates :password, length: { minimum: 6 }, allow_blank: true
validates :email,
presence: true,
uniqueness: { case_sensitive: false },
length: { minimum: 3, maximum: 100 },
format: { with: EMAIL_REGEX }
has_secure_password
enum role: [:member, :admin]
def favorite_for(post)
favorites.where(post_id: post.id).first
end
def avatar_url(size)
gravatar_id = Digest::MD5::hexdigest(self.email).downcase
"http://gravatar.com/avatar/#{gravatar_id}.png?s=#{size}"
end
end
Here is my Topic Model:
class Topic < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :labelings, as: :labelable
has_many :labels, through: :labelings
end
Here is my Post Model:
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
has_many :comments, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :labelings, as: :labelable
has_many :labels, through: :labelings
has_many :favorites, dependent: :destroy
default_scope { order('rank DESC') }
scope :visible_to, -> (user) { user ? all : joins(:topic).where('topics.public' => true) }
validates :title, length: { minimum: 5 }, presence: true
validates :body, length: { minimum: 20 }, presence: true
validates :topic, presence: true
validates :user, presence: true
def up_votes
votes.where(value: 1).count
end
def down_votes
votes.where(value: -1).count
end
def points
votes.sum(:value)
end
def update_rank
age_in_days = (created_at - Time.new(1970,1,1)) / 1.day.seconds
new_rank = points + age_in_days
update_attribute(:rank, new_rank)
end
end
Any insight anyone could provide, I would be extremely grateful for. If you have the time to explain where I went wrong as well, that would be even more helpful.
User#show where I am currently trying to display the favorited posts
But you're not setting #topic in your User#show action. That's why it's nil.
def show
#user = User.find(params[:id])
#posts = #user.posts.visible_to(current_user)
#posts = Post.joins(:favorites).where('favorites.user_id = ?', #user.id)
#favorites = current_user.favorites
# your #topic object is not in here?
end
Since a post belongs_to a topic you could do something like this:
<%= link_to post.title, topic_post_path(post.topic, post) %>
Here is the error I receive:
Here is a Gist of the files (some of you may find this easier to read):
https://gist.github.com/drichards2013/7902811
Here is index.html.erb:
<%= render 'pages/home' if !user_signed_in? %>
<div id="things" class="transitions-enabled">
<% #things.each do |thing| %>
<div class='panel panel default'>
<div class="box">
<%= link_to image_tag(thing.image.url(:medium)), thing %>
<div class='panel-body'>
<% if thing.link.blank? %>
<strong><%= thing.title %></strong>
<% else %>
<strong><%= link_to thing.title, "http://#{thing.link}"%></strong>
<% end %>
<p><%= thing.description %></p>
By <%= link_to thing.user.username, user_path(thing.user) %>
<% if thing.user == current_user %>
<%= link_to edit_thing_path(thing) do %>
<span class='glyphicon glyphicon-edit'></span>
<% end %>
<%= link_to thing_path(thing), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class='glyphicon glyphicon-trash'></span>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails, class: 'pull-left' %>
Here is thing.rb:
class Thing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
has_attached_file :image, :styles => { :large => '500x500>', :medium => '300x300>', :thumb => '100x100>' }
validates :image, presence: true
validates :title, presence: true, length: { minimum: 5, maximum: 50 }
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is things_controller.rb:
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
has_many :things
validates :name, presence: true, length: { minimum: 2, maximum: 20}
validates :username, presence: true, length: { minimum: 2, maximum: 20}
validates :username, uniqueness: true
validates :email, presence: true
validates :email, uniqueness: true
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" , :nav => "25x25"}
extend FriendlyId
friendly_id :username
def show
end
#follow features
has_many :followed_users, through: :relationships, source: :followed
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy!
end
def feed
Thing.from_users_followed_by(self)
end
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
end
Here is users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find_by_username(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find_by_username(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
What I have tried
I went to the console, did thing = Thing.last, then tried to call thing.user, and that returned nil. So it appears the user_id isn't saving. How can I make that happen?
This is where the "try" method is your friend.
You're trying to get the "username" value from your user, which is missing.
If you were to write your line like:
thing.user.try(:username)
Then it would not crash, and it would fetch the username if user existed.
Your user is not saving because your one-to-many relationship is not set up. You might want to add a user_id to your things table.
Fixed by adding :user_id to the thing_params method in my things controller.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link, :user_id)
end
Hello I'm working in a application where you can vote for a product and from the New action of my vote view I get this error:
ActiveModel::MassAssignmentSecurity::Error in VotesController#create
Can't mass-assign protected attributes: product, user
I make a test on rails console and it works. So I don't know what it's going on.
Here are the models:
class Product < ActiveRecord::Base
attr_accessible :title
has_many :votes
has_many :users, :through => :votes
has_attached_file :photo, :styles => { :medium => "300x300" }
before_save { |product| product.title = title.titlecase }
validates :title, presence: true, uniqueness: { case_sensitive: false }
validates :photo, :attachment_presence => true
end
class User < ActiveRecord::Base
has_many :votes
has_many :products, :through => :votes
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
class Vote < ActiveRecord::Base
attr_accessible :product_id, :user_id
belongs_to :product
belongs_to :user
end
Here is the vote controller
class VotesController < ApplicationController
# GET /votes
# GET /votes.json
def index
#votes = Vote.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #votes }
end
end
# GET /votes/1
# GET /votes/1.json
def show
#vote = Vote.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #vote }
end
end
# GET /votes/new
# GET /votes/new.json
def new
#vote = Vote.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #vote }
end
end
# GET /votes/1/edit
def edit
#vote = Vote.find(params[:id])
end
# POST /votes
# POST /votes.json
def create
#vote = Vote.new(params[:vote])
respond_to do |format|
if #vote.save
format.html { redirect_to #vote, notice: 'Vote was successfully created.' }
format.json { render json: #vote, status: :created, location: #vote }
else
format.html { render action: "new" }
format.json { render json: #vote.errors, status: :unprocessable_entity }
end
end
end
# PUT /votes/1
# PUT /votes/1.json
def update
#vote = Vote.find(params[:id])
respond_to do |format|
if #vote.update_attributes(params[:vote])
format.html { redirect_to #vote, notice: 'Vote was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #vote.errors, status: :unprocessable_entity }
end
end
end
# DELETE /votes/1
# DELETE /votes/1.json
def destroy
#vote = Vote.find(params[:id])
#vote.destroy
respond_to do |format|
format.html { redirect_to votes_url }
format.json { head :no_content }
end
end
end
and here is the new vote view
<%= form_for(#vote) do |f| %>
<% if #vote.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#vote.errors.count, "error") %> prohibited this vote from being saved:</h2>
<ul>
<% #vote.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :product %><br />
<%= f.text_field :product %>
</div>
<div class="field">
<%= f.label :user %><br />
<%= f.text_field :user %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Please I really need your help to solve this issues, it was very difficult to find a tutorial with has_many :through that include the complete MVC example, I think my problem is on the view, but I don't know.
Thanks!
That error message tells you everything you need to know if you look closely at it.
ActiveModel::MassAssignmentSecurity::Error in VotesController#create
Can't mass-assign protected attributes: product, user
you may not be familiar with the term "mass-assignment". its the assignment of 1 or more of an objects attributes at the time of creation. i.e. in VotesController#create.
when unprotected, mass-assignment opens you up to hackers assigning values to any and all of an objects attributes in your site's forms wether you meant to give access or not.
thats where attar_accessible comes in. it forces you to be explicit about what attributes of a model your users should have access to. any not passed as symbols into the macro will be protected attributes as in Can't mass-assign protected attributes: product, user.
the scaffolding set attr_accessible :product_id, :user_id when it created your model but it didnt know you were going to assign these with objects rather than id values.
you can fix this one of 2 ways.
change your form so that the hash-like params variable assigns like this
params[vote][product_id]
or change your model like
attr_accessible :product, :user