Save form data(text field) in database in rails - ruby-on-rails

I have created a user controller with login and logout.
After login user should be able to give some comment in text box input and it should be saved in db.
How to associate the comment to the user. My users controller is
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/url'
else
redirect_to '/signup'
end
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
my urls controller is
class UrlsController < ApplicationController
def new
end
def create
url = Url.new(url_params)
url.save
redirect_to #url
end
def url_params
params.require(:url).permit(:url)
end
end
I am getting error in url_params. How it should be for a text field?

For example you need to create Comment model
class Comment < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :comments
end
and create CommentsController
class CommentsController < ApplicationController
before_action :set_user
def create
#user.comments.create(comment_params)
redirect_to root_url
end
private
def set_user
#user = User.find(session[:user_id])
end
def comment_params
params.require(:comment).permit(:text)
end
end

Related

Retrieving data from polymorphic tables in rails

I have some polymorphic relationships set up and are working well for the primary purpose. That is for a User to be able to Comment on both Articles and Coffeeshops.
However I'm struggling with being able to display the users list of comments on their profile page. In the future I also want the user to be able to 'favourite' and 'want to go to' different coffeeshops which I would also want to show up on their profile page. I'm hoping once I get the logic for display current comments, the rest will be a breeze ;)
So what I have:
Models
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
Comment Controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :authenticate_user!
before_action :comment_auth, only: [:edit, :update, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(allowed_params)
#comment.user_id=current_user.id if current_user
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #commentable
else
render 'edit'
end
end
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
flash[:success] = "Comment Destroyed!"
redirect_to :back
end
end
private
def allowed_params
params.require(:comment).permit(:name, :body)
end
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:body).merge(user_id: current_user.id)
end
Profile Controller
class ProfileController < ApplicationController
before_action :authenticate_user!
def index
end
def show
#user = User.find.current_user(params[:id])
#comments = #commentable.comments
end
In views/profile/show.html.erb. I was trying to do:
<h3>Your Latest Comment</h3>
<%=#comment.user.body%>
But this clearly isn't right as I get Couldn't find User without an ID. From ProfileController#show
update
If I change ProfileController to
before_action :authenticate_user!
def index
#user = User.find.current_user(params[:user_id])
end
def show
#comments = #commentable.comments
end
I get an error for undefined comments.
ok first return this to show moving it to index is not solving a problem the index is not called so write show like this.
def show
#user = current_user #you get instance of a user that is logged in
#comments = #user.comments
end
I do not know if you have user_id in your comment migration but if you do not have you must write
class User < ApplicationRecord
has_many :comments, as: :commentable
end
view
<h3>Your Latest Comment</h3>
<%=#comments.try(&:last).try(&:body)%>

Undefined method songs

I am trying to submit songs to a particular event, and the only thing that is stopping me is an error message. I have gotten this to work when a user has events, but not when a event has songs. Here is what my code looks like:
Event model:
class Event < ApplicationRecord
belongs_to :user
has_many :songs, dependent: :destroy
end
Song model:
class Song < ApplicationRecord
belongs_to :event
validates :artist, presence: true
validates :title, presence: true
end
Events controller
class EventsController < ApplicationController
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
redirect_to root_url
else
redirect_to root_url
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :code)
end
end
Song controller
class SongsController < ApplicationController
def create
#song = current_event.songs.build(song_params)
if #song.save
flash[:success] = "Song Created"
redirect_to root_url
else
render 'users/show'
end
end
def destroy
end
private
def song_params
params.require(:song).permit(:artist, :title)
end
end
sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
def current_event
#current_event ||= Event.find_by(id: session[:event_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
#current_user = nil
end
end
Any help on this would be fantastic!
Your current_event method is looking for a session to get the event_id
def current_event
#current_event ||= Event.find_by(id: session[:event_id])
end
But you are not setting a session for this.
You are on the user session:
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
The result being that current_event is likely not returning an event object (its nil) and therefore .songs is not a valid method to call on it.
So you need to either set a session with event_id or come at it a different way (not using sessions).
Hope it helps

how to use namespace in gem Pundit

I have 2 controller, 1 for user and 1 for admin.
controllers/articles_controller.rb
class ArticlesController < ActionController::Base
...
def show
#article = Article.find(parmas[:id])
authorize #article
end
...
end
controllers/admin/articles_controller.rb
class Admin::ArticlesController < AdminController
...
def show
#article = Article.find(parmas[:id])
authorize #article
end
...
end
And i have 2 file policy
policies/article_policy.rb
class ArticlePolicy
extend ActiveSupport::Autoload
autoload :Admin
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def show?
# allow show for every user.
true
end
end
And one file policies/admin/article_policy.rb
class Admin::ArticlePolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def show?
# only show if use have role manager
user.manager?
end
end
but when i use a account user to show articles at /admin/articles/1/. It show normaly, Should is "Access denied".
How to fix this? (I use gem pundit 1.10).
Use the authorize method to pass the namespace as a parameter.
class ArticlesController < ActionController::Base
...
def show
#article = Article.find(parmas[:id])
authorize [:admin, #article]
end
...
end

How to retrieve nested model data from another model? : undefined method error

So the basis of my code so far is:
a customer has_one calendar
a calendar belongs_to a customer
a calendar has_many events
an event belongs_to a calendar
I am trying to, when creating a new event, specify the customer and calendar it belongs to but it throws error "undefined method `Calendar'":
class EventsController < ApplicationController
def new
#event = Event.new
#currentcalendar = current_customer.calendar # this is where it is failing
end
def create
if #event = #currentcalendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
this is my current_customer method within application_controller:
def current_customer
if (customer_id = session[:customer_id])
#current_customer ||= Customer.find_by(id: customer_id)
elsif (customer_id = cookies.signed[:customer_id])
customer = Customer.find_by(id: customer_id)
if customer && customer.authenticated?(cookies[:remember_token])
session[:customer_id] = customer.id #log in
#current_customer = customer
end
end
end
Here are the related controller files. Customer:
class CustomersController < ApplicationController
def new
#customer = Customer.new
#businesses = Business.all
#calendar = Calendar.new
end
def create
#customer = Customer.create(customer_params)
#calendar = #customer.build_calendar
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password, :business_id)
end
Calendar:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
end
I'm very new to Ruby/ Rails and so can't figure this out by myself. Is this problem occurring because I have wrongly created my calendar? I wanted it to be created when its user is created, which works, but I just don't know how to get to the calendar and user within the events controller.
Thanks for your help!
EDIT: these are the model classes.
customer:
class Customer < ActiveRecord::Base
belongs_to :business
has_one :calendar
has_secure_password
attr_accessor :remember_token
#remembers a user in the database for use in persistent sessions
def remember
self.remember_token = Customer.new_token
update_attribute(:remember_digest, Customer.digest(remember_token))
end
def Customer.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def forget
update_attribute(:remember_digest, nil)
end
def Customer.new_token
SecureRandom.urlsafe_base64
end
#returns true if the given token matches the digest
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
calendar:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
event:
class Event < ActiveRecord::Base
belongs_to :calendar
end
Your current_customer can be nil at times. To avoid this you can add a before_filter callback that checks if there is a customer that is logged in or not.
In your application_controller create a method called customer_found?
def customer_found?
current_customer.present?
end
Change your events controller to
class EventsController < ApplicationController
before_filter :customer_found?
before_filter :prepare_calendar, only: [:new, :create]
def new
#event = Event.new
end
def create
if #event = #current_calendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def prepare_calendar
#current_calendar = current_customer.calendar
end
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
Since you did not assign your #current_calendar in your create method then you are gonna get undefined method build for nil class. You need to initialize the variable since it can not get it from the new method. Each action has its own independent variables so make sure to prepare all necessary variables before using them.

Pundit- Index Method for Admin and Users

So, I'm trying to use the gem pundit. I'm just trying to figure out how to have an index view for users and admins. I want to render all results for an admin and only related posts for a user. I've googled and searched on github, but I'm not find any luck. What do I have to put in my policy and controller?
original code
class PostsPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#post = model
end
def index?
#current_user.admin?
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = Post.order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
second update
class PostsPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
third update
class PostPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
def index?
user.admin? || user.posts.count > 0
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
** final update with working code **
class PostPolicy
attr_reader :user, :model
def initialize(user, model)
#user = user
#post = model
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
def index?
user.admin? || user.posts.count
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize Post
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
What you're looking for is a Scope:
class PostsPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
end
Then in your controller
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
Edit
As a side note, authorize User will probably not serve you well in the long run. You're essentially creating an index policy that would need to serve every index. If you want to to authorize visibility to the index page you can still do something like this in your policy:
def index?
user.admin? || user.posts.count > 0
end
Assuming that relationship is set up, then you would call authorize Post in your index controller before your policy_scope.

Resources