I am working on a messaging app in ruby and i am currently encountering a blocker which i can not fix. I have been using tutorials for this and i think part of the reason i cannot find the solution is because of that. My app allows the users to log in and sign up,they can then add,view and edit contact. Finally the can send a message to different recipients. The problem is, i cannot get the recipients in the contacts and send them a message. I am only able to select my name as a user(which is not what its intended to do). I have attached the controllers used here:
contacts_controller
class ContactsController < ApplicationController
def index
#contacts = Contact.all
end
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success]= "new contact successfully added!"
redirect_to contacts_path
else
render 'new'
end
end
def edit
#contact = Contact.find(params[:id])
end
def update
#contact = Contact.find(params[:id])
permitted_columns = params.require(:contact).permit(:name, :company, :email, :phone)
#contact.update_attributes(permitted_columns)
redirect_to contacts_path
end
def destroy
#contact = Contact.find(params[:id])
#contact.destroy
redirect_to contacts_path
end
private
def contact_params
params.require(:contact).permit(:name, :company, :email, :phone)
end
end
messages_controller
class MessagesController < ApplicationController
def index
#messages = Recipient.where(:user_id => current_user.id).order('created_at DESC')
end
def new
#message = Message.new
#recipients = Contact.all
end
def create
#message = current_user.messages.build(message_params)
if #message.save
flash[:success]= "Message sent!"
redirect_to contacts_path
else
flash[:alert]= "sorry!message unsent"
render :new
end
end
private
def message_params
params.require(:message).permit(:body, :sender_id, user_tokens:[])
end
end
users_controller
class UsersController < ApplicationController
def index
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/contact'
else
flash[:register_errors] = user.errors.full_messages
redirect_to '/'
end
end
private
def user_params
params.require(:user).permit(:fname, :lname, :email, :password, :password_confirmation)
end
end
sessions_controller
class SessionsController < ApplicationController
def create
user = User.find_by(email:login_params[:email])
if user && user.authenticate(login_params[:password])
session[:user_id] = user.id
redirect_to '/contact'
else
flash[:login_errors] = ['invalid username or password']
redirect_to '/'
end
end
def destroy
session[:user_id] = nil
redirect_to '/', notice: 'Successfully logged out!'
end
private
def login_params
params.require(:login).permit(:email,:password)
end
end
The _recipient.html.erb is rendered by the new.html.erb. Here is the code:
<div class="container vertical-center">
<div id ="stream">
<%= form_for :message, url:messages_path do |f| %>
<%= f.text_area :body, id: "url", placeholder: "Message", class: "message_body" %>
<div id="stream-list" class="follow-list">
<ul>
<% #recipients.each do |contact| %>
<label for="user<%=contact.id%>" >
<li id="stream_item">
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]",user.id, #message.users.include?(user), id: "user#{user.id}" %>
</li>
</label>
<br>
<% end %>
</ul>
</div>
<div class="stream-footer">
<%= f.button :submit, class: "btn btn-success" %>
<% end %>
</div>
</div>
</div>
Here is the error when i try to write a message
It's not very clear why you use local variable user in your view template. I think it's just an error and contact variable is supposed to be used instead:
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]", contact.id, #message.users.include?(contact), id: "user#{contact.id}" %>
Also, a small HTML error: ul tag should contain li tags; other tags are not allowed as direct descendants. So I would also rewrite that list as:
<ul>
<% #recipients.each do |contact| %>
<li id="stream_item">
<label for="user<%=contact.id%>" >
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]", contact.id, #message.users.include?(contact), id: "user#{contact.id}" %>
</label>
</li>
<br>
<% end %>
</ul>
Related
This is my first Rails app and have hit another wall. I have a User model and a Country model. They have a many-to-many relationship, which I join together with a Trip model.
A user can maintain a list of countries that they have been to. On the Country page, I want to have a simple bootstrap button so the current_user can add or remove the country to their list.
I am using a partial that looks like the below to at least render buttons on all the pages.
_add_remove_countries.html.erb
<% if #user.countries.exists?(#country.id) %>
<%= form_for(#user) do |f| %>
<%= f.submit "Remove Country", class: "btn btn-info" %>
<% end %>
<% else %>
<%= form_for(#user) do |f| %>
<%= f.submit "Add Country", class: "btn btn-info" %>
<% end %>
<% end %>
I have tried a few different things, with no luck so I have just reverted to the basic structure. I am currently using a form_for, however that is just what has worked best so far, I am not tied to that solution.
Below are my controllers if needed, I have not set up a Trips controller as I am only using it to join the User and Country Model (maybe I need to set one up?).
users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
def update
redirect_to user_path
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
countries_controller.rb
class CountriesController < ApplicationController
before_action :require_user, only: [:index, :show]
def index
#countries = Country.all
#sort = CS.countries.sort_by {|key, value| value}
#sort = #sort.first #sort.size - 2
end
def show
#country = Country.find(params[:id])
#user = User.find(session[:user_id])
end
end
my suggestion using collection_select (and click link in case you would like to know more about collection_select) to add countries to user while editing user, below is sample code to help (using edit method)
user_controller
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
# ---> here additional code to edit method
def edit
#user = User.find(params[:id])
#countries = Country.all
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to user_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:username,
:email,
:password,
:password_confirmation,
:country_ids => [])
# country_ids is an array that will save data for countries that user have been to
end
end
now this is the fun one, in your views\user\edit.html.erb
<%= form_for #user do |f| %>
<!-- simple -->
<p>Email : </p>
<p><%= f.text_field :email %></p>
<!-- if you using bootstrap -->
<div class="row form-group">
<%= f.label "email", :class => 'control-label col-sm-3' %>
<div class="col-sm-5">
<%= f.text_field :email, :class => 'form-control' %>
</div>
</div>
<!-- other inputs (password / password_confirmation) -->
<%= f.collection_select :country_ids, #countries, :id, :name, {}, { multiple: true, class: 'form-control' } %>
<% end %>
I'm a bit of a rails newb here and need some help figuring this out. I have an argument error that is thrown every time I try and create or edit a new "topic."
ArgumentError
Here is the code for the "show:"
<h1><%= #topic.title %></h1>
<%= link_to "Edit", edit_topic_path(#topic), class: 'btn btn-success' %>
<%= link_to "Delete Topic", #topic, method: :delete, class: 'btn btn-danger', data: {confirm: 'Are you sure you want to delete this topic?'} %>
<% if policy(Bookmark.new).create? %>
<%= link_to "New Bookmark", new_topic_bookmark_path(#topic), class: 'btn btn-success' %>
<% end %>
<% #topic.bookmarks.each do |bookmark| %>
<div class="media-body">
<div class="row">
<div class="col-md-2">
<div class="container">
<img src="http://icons.better-idea.org/icon?url=<%= bookmark.url %>&size=120">
<div class="media-heading">
<%= link_to bookmark.name, topic_bookmark_path(#topic, bookmark) %>
</div>
</div>
</div>
</div>
<div class="col-md-1">
<%= render partial: 'likes/like', locals: {bookmark: bookmark} %>
</div>
</div>
<% end %>
Here is the "topics" controller:
class TopicsController < ApplicationController
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
flash[:notice]= "Topic was saved."
redirect_to #topic
else
flash.now[:alert]= "The topic could not be saved. 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
flash[:notice]= "The topic was saved sucessfully."
redirect_to #topic
else
flash.now[:alert]= "There was an error saving the topic. Please try again."
render :edit
end
end
def destroy
#topic = Topic.find(params[:id])
if #topic.destroy
flash[:notice]= "\"#{#topic.title}\" was deleted successfully."
redirect_to topics_path
else
flash.now[:alert] = "There was an error in deleting this topic."
render :show
end
end
def topic_params
params.require(:topic).permit(:title)
end
end
Update(1): New error after deleting "policy" New Error
Here is the "application policy that uses Pundit:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && ( record.user == user )
end
def edit?
update?
end
def destroy?
user.present? && (record.user == user)
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
Problem is with Policy made with the Pundit gem. Can you check something called the BookmarkPolicy or something similar, or at least post that here. Did you forget to include Pundit in your controller ?
You're using the Pundit gem but I don't see the authorize method in your controller. From Pundit's documentation:
Supposing that you have an instance of class Post, Pundit now lets you
do this in your controller:
def update
#post = Post.find(params[:id])
authorize #post
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
The authorize method automatically infers that Post will have a
matching PostPolicy class, and instantiates this class, handing in the
current user and the given record.
The source code in the screenshot and the code you posted is not the same. Your code:
<% if policy(Bookmark.new).create? %>
Screenshot:
<% if (Bookmark.new).create? %>
Rails is correctly reporting that Bookmark.new does not have a create? method, because it's missing the policy() method call.
Is your file saved? Are you sure you're changing the correct file?
I want to implement a reset password functionality so I have followed this railscast, I receive the mail with the link to redirect to an edit password page but I get an error here.
View
<h1>Reset Password</h1>
<%= form_for #user, :url => password_reset_path(params[:id]) do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit "Update Password" %></div>
<% end %>
The error is :First argument in form cannot contain nil or be empty
I'm assuming that #user is empty, I'm new on RoR and I don't know why I get this error
Password Controller
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
render :layout => false
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
end
Change your def edit to
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
you have to add
#user = User.new
to your new method.
you have also another error for your create method. there is no user creation.
class PasswordResetsController < ApplicationController
def new
#user = User.new
render :layout => false
end
def create
#user = User.new user_params
if #user.save
# your code to render success
else
# your code to render error
end
end
private
def user_params
params.require(:user).permit(:email) # add more
end
end
This is the answer to '#user.update_attributes(params[:user])' with forbidden attributes error.
Rails 4 has new feature known as strong parameters.
Change your password controller to:
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(user_params)
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :email_id, :password)
end
end
After following Michael Hartl's Rails Tutorial I moved the 'micropost feed' from / to /members, and now when I submit a post that doesn't validate properly (too many characters, missing content etc.) rails returns an error saying:
ArgumentError in MicropostsController#create
First argument in form cannot contain nil or be empty
The related interface test error returns:
FAIL["test_micropost_interface", MicropostsInterfaceTest, 2015-06-22 11:13:28 +0800]
test_micropost_interface#MicropostsInterfaceTest (1434942808.57s)
Expected at least 1 element matching "div#error_explanation", found 0..
Expected 0 to be >= 1.
test/integration/microposts_interface_test.rb:19:in `block in <class:MicropostsInterfaceTest>'
How can I fix these errors so user friendly error messages (div#error_explanation) will display correctly?
Supporting info
MembersController:
class MembersController < ApplicationController
before_filter :logged_in_user
def index
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
MicropostsController:
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
# redirect_to root_url
redirect_to members_path
else
#feed_items = []
#micropost = []
render 'members/index'
end
end
def destroy
#micropost.destroy
flash[:success] = "Micropost deleted"
# redirect_to request.referrer || root_url
redirect_to request.referrer || members_path
end
private
def micropost_params
params.require(:micropost).permit(:content, :picture)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
# redirect_to root_url if #micropost.nil?
redirect_to members_path if #micropost.nil?
end
end
_micropost_form.html.erb
<%= form_for(#micropost, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost (420 chars max)..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="picture">
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<% end %>
<script type="text/javascript">
$('#micropost_picture').bind('change', function() {
size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert('Maximum file size is 5MB. Please choose a smaller file.');
}
});
</script>
app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 420 }
validate :picture_size
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end
end
Update
_error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
If you look at the error
First argument in form cannot contain nil or be empty
You can clearly make out from it that the first argument that's #micropost variable is nil. Now move to the controller and see if you have set that variable or not.
In else part of create action, inside microposts you have #micoposts = [], an empty array which is then passed on to your template and causing error.
Fix:
Change create method to this:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
# redirect_to root_url
redirect_to members_path
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'members/index'
end
end
I'm a Rails newbie, and I have a RoR app that I'm working on, the app is supposed to allow invited guests to RSVP to a wedding.
I have allowed for fields to be dynamically added, in order to include additional guests (family of an invited guest). But when I add the dynamic field and add the names, only the last name is displayed in the index.
Is there anything specific I need to do to render all other names together in a field within the table?
This is my current controller thus far:
class GuestsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def index
#guests = Guest.all
end
def new
#guest = Guest.new
end
def create
#guest = Guest.all
#guest = Guest.create(guest_params)
if #guest.save
respond_to do |format|
format.html { redirect_to :back, notice: 'Thank you for replying' }
format.js
end
else
respond_to do |format|
format.html { render 'new' }
format.js
end
end
end
def destroy
#guest = Guest.find(params[:id])
#guest.destroy
redirect_to guests_path
end
private
def guest_params
params.require(:guest).permit(:status, :name, :message)
end
end
Place Guest in place of User
users controler
def new
#users = []
5.times do
#users << User.new
end
end
if params.has_key?("user")
User.create(users_params(params["user"]))
else
params["users"].each do |user|
if user["name"] != nil || user["age"] != nil
User.create(users_params(user))
end
end
end
def users_params(my_params)
my_params.permit(:name, :age)
end
users_form
<%= form_tag users_path do %>
<% #users.each do |user| %>
<%= fields_for 'users[]', user do |u| %>
<div class="field">
<%= u.label :name %><br>
<%= u.text_field :name %>
</div>
<div class="field">
<%= u.label :age %><br>
<%= u.number_field :age %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>