How to display username next to message in rails app - ruby-on-rails

I have built a simple chat application by following this tutorial -> https://www.youtube.com/watch?v=nRP91C1uX-w. On top of that, I have added a simple user authentication system using the clearance gem. I want to add a way to display the clearance account logged in next to the message sent by the user.
For example something like this -> test#gmail.com: Anyone online?
I have tried finding tutorials on how to do this but it just comes up with irrelevant videos that don't help my cause.

Welcome to SO. It is always better if you can add code to your questions, it helps with context, especially since links alone might not exist in the future.
That tutorial is really basic, highlighting the ActionCable and js features alone, but adding the user isn't too tough.
#1 Add a migration to attach the user to the message model
rails g migration add_user_to_messages user:belongs_to
Edit the migration to allow for no user (you might change this later)
def change
add_reference :messages, :user, null: true, foreign_key: true
end
Add messages to the user.rb model
has_many :messages
Add user to the message.rb model
belongs_to :user, optional: true
Migrate
rake db:migrate
#2 Add the user to the form
(app/views/landing/index.html.erb in the tutorial)
<%= form_for #message, remote: true do |f| %>
<%= f.text_field :content %>
<%= f.hidden_field :user_id, value: current_user.try(:id) %>
<%= f.submit %>
<% end %>
#3 Permit the user in the messages controller
def message_params
params.require(:message).permit(:content, :user_id)
end
#4 Send the email address with the message in the ActionCable
(after the message has saved app/controllers/messages_controller.rb)
ActionCable.server.broadcast('room_channel', content: #message.content, user: #message.user.try(:email))
#5 Pass the user data through the JS on new messages
(append the user to the html that is passed when a new message is created, in the channel js)
received(data) {
$("#msg").append(`<div class='message'>${data.user} - ${data.content}</div>`)
}

Related

Create and update with checkboxes and simple form

I am trying to do something basic but for some reason that's not working and not returning me any errors.
I have a User model and a ManualBill model.
The Manual Bill belongs to a User (I can select who paid it when I create it and it belongs to this User).
But then, once the Manual Bill is created, I also want to be able to add "manual_bills_payers", who are the ones who did not pay the bill but have to refund it.
In the end, that makes my models look like this:
class ManualBill < ApplicationRecord
belongs_to :flat
belongs_to :user
has_many :manual_bill_payers
has_many :users, through: :manual_bill_payers
end
And
class ManualBillPayer < ApplicationRecord
belongs_to :manual_bill
belongs_to :user
end
I've created an update form for the ManualBill, in which I am going to select several users (who are going to be manual_bill_payers) with checkboxes (I am in a loop which is why I called the manual_bill "bill" here.
<%= simple_form_for [#flat, bill] do |f| %>
<%= f.error_notification %>
<%= f.association :manual_bill_payers, as: :check_boxes,
:label_method =>
lambda { |owner| "#{owner.email}" },collection: User.all,
input_html: {class: "input-
bar-settings"} %>
<%= f.submit 'Add payers', class: "btn-form" %>
<% end %>
I am really confused of what to do here.
What I want to do is basically CREATE several new manual_bill_payers when updating the manual_bill by selecting users who are in my flat (all the Users of the db in the exemple above). I also want to be able to remove them by unchecking the boxes.
I've basically tried everything... from changing the models to put "has_and_belongs_to_many" instead of the "through" association, to changing the :manual_bill_payers into :users in the simple_form.
Most of the time the update goes through (no errors and redirect me to the show page) but I still get an empty array when I do ManualBillPayer.all or when I check my manual_bill.manual_bill_payers.
My update method here just in case
def update
#manual_bill = ManualBill.find(params[:id])
authorize #manual_bill
if #manual_bill.update(manual_bill_params)
redirect_to flatshare_path(#manual_bill.flat)
else
redirect_to root_path
end
end
def manual_bill_params
params.require(:manual_bill).permit(:manual_bill_payer_id)
end
I don't have any create method in this controller because the manual_bill is created somewhere else, which is why the last method is pretty empty
Where am I wrong? Is there something I am completely minsunderstanding?
I've put a raise in the update to check the params
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"280izDKna4QWfr0OhZJW6u/
UWcqlxo56yR17fBDX4QFKhNG94mYqbBLPuq+ifo3mNIV10GFk1RK7Hr5AnmosOA==",
"manual_bill"=>{"manual_bill_payer_ids"=>["", "1", "35", "34"]},
"commit"=>"Add payers",
"flatshare_id"=>"15",
"id"=>"15"}
Thanks a lot

Properly define constraint for a true one to one

We have in our system a one to one relation.
Let's use the typical example of user -> address:
class User < ApplicationRecord
belongs_to :address
end
class Address < ApplicationRecord
has_one :user
end
In this scenario, the user has an address_id field, the address doesn't have any database field that references the user.
When I now go ahead and create a user and an address, everything is fine:
user = User.create(address: Address.create)
But when I create an additional address with the existing user I will end up with 2 addresses.
address.create(user: user)
Usually not a big deal, but for reporting purposes stuff can go wrong.
So my question: how do I make sure there is only one user associated with one address and vice versa?
To truly enforce it, you can define a unique index on the users.address_id column.
In general I'd suggest just not using Address.create though: if you use address.create_user, Active Record will do a better (though still not perfect, especially in the face of concurrent requests) job of keeping things under control for you.
(Creating a user from an address doesn't make much sense, of course... if you went down that path, I'd consider flipping the relationship to make the Address feel more subordinate to the User. The point is that manipulating it via the has_one side will get you more has_one-aware behaviour.)
I believe that the issue you're running into might have more to do with the controller than with model constraints.
Your User model could accept_nested_attributes_for :address.
Your new and edit actions could look something like this:
def new
#user = User.new
#user.build_address
end
def edit
#user = User.find(params[:id])
#user.build_address if #user.address.blank?
end
And your user form could have something like:
<%= fields_for :address do |address_form| %>
<%= address_form.text_field :street_line_one %>
<%= address_form.text_field :street_line_two %>
<%= address_form.text_field :city %>
<%= address_form.text_field :postal_code %>
<%= address_form.text_field :region_name %>
<% end %>
This should ensure that a new object is created when a new user is created, and should ensure that the existing address is updated when the user with an existing address is updated.
If that's still not working as expected, you'll have to add a bit more detail like what your controller and form look like to know exactly how to solve your issue.

How to render different forms built on the same model in rails

I'm building an application where users can sign up, browse grants and then apply to the ones that they qualify for. So far I have models for each: users, grants, and applications. To summarize the user clicks an apply button on the grant page, they are sent to a unique form that is specific to that grant which they fill out as their application. The part I'm struggling with is that each grant will have a unique application form.
My thought so far is to have all possible fields in the application model and then have multiple partials (with only the fields necessary for that grant) that are called based on the grant id. I tried changing the render on submissions/new to:
<%= render :partial => "submissions/#{#grant}" %>
which intends to render the partial based on the grant_id of the previously visited grant page.
_submission/1.html.erb
_submission/2.html.erb
_submission/3.html.erb
This doesn't work as is but I'm not sure what I'm missing.
Another idea is to add a separate submission model that will link grants and applications through a join table but I still have the problem of calling a unique form for each grant.
Does anyone have a solution to the render problem or suggestions of alternate methods of creating many unique applications without building a new model for each?
I had to implement something similar to this recently. I rendered the same form in multiple locations across the site, but assigned them a unique content_type through a hidden field tag. Based on their content type that was attached to the form, the appropriate email would be sent out to them. I'll list the process I used, I think it can be easily modified to suit your needs.
First I created a migration called contacts. For you I guess it would be applications?
rails g migration contacts
Then i added the following fields,
class CreateContacts < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :name
t.string :phone
t.string :email
t.text :comment
t.string :content_type
t.timestamps
end
end
end
Add whatever fields you feel necessary for your application. Keep content_type though.
rake db:migrate
I then generated a contacts controller
rails g controller Contacts
,and filled it out as below. Pretty standard but take note of content_type being one of our permitted parameters.
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success] = "Message Sent!"
redirect_to root_path
else
flash[:danger] = "Error occurred"
render :new
end
end
private
def contact_params
params.require(:contact).permit(:name, :phone, :email, :comment, :contact_type)
end
end
Now our form. Under the views/contacts folder that has been generated, create a folder and call it partials. Within this folder create a file and call it _form.html.erb .The underscore is important as it's a partial. Sorry if i'm over explaining some things.
_form.html.erb
<%= form_tag contacts_path do %>
<%= hidden_field_tag 'contact[contact_type]', contact_type %>
<%= text_field_tag 'contact[name]', nil, required: true, placeholder: "Name" %>
<%= text_field_tag 'contact[phone]', nil, placeholder: "Phone" %>
<%= email_field_tag 'contact[email]', nil, required: true, placeholder: "Email" %>
<%= text_area_tag 'contact[comment]', nil, required: true, placeholder: "Comments" %>
<%= submit_tag "Submit" %>
<% end %>
Then in my views, wherever I want a form or application in your case, I would render the form partial and add the contact type. You could leave it as contact_type or call it application_type or whatever you want when you first generate your migration as above.
<%= render partial: '/contacts/partials/form', locals: {contact_type: 'become_partner'} %>
In my contact model contact.rb , I would add some validations on the fields I must have, and also write a method for calling my ContactMailer(which i haven't created yet).
class Contact < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
after_create :send_email
private
def send_email
if self.contact_type == 'become_partner'
ContactMailer.partner_email(self).deliver
else
ContactMailer.contact_email(self).deliver
end
end
end
Our send_email method is where we will query the type of content_type attached to the form. You may have
if self.contact_type == 'application_1(for example)'
ContactMailer.application_1(self).deliver
elsif self.contact_type == 'application_2'
ContactMailer.application_2(self).deliver
else
ContactMailer.application_3(self).deliver
end
Add as many elsif as you need.
Let's now create our contact mailer. In the folder app/mailers create the file contact_mailer.rb
class ContactMailer < ActionMailer::Base
default to: 'justin#socialplayground.com.au'
def contact_email(contact)
#contact = contact
mail(from: #contact.email, subject: 'Contact Form Message').deliver
end
def partner_email(contact)
#contact = contact
mail(from: #contact.email, subject: 'Contact Form Message').deliver
end
end
Here we create all the actions for our different forms to send each their own email. In the above example I had contact_email as my main homepage form, partner_email for the form in the partner signup section etc. You may have application_1, application_2 etc, and feel free to change the subject to whatever you want. This is what shows up in your email inbox before you open it. The name of the sender and the brief message, so "new application for ... " in your case, for example.
AND FINALLY..
Our html email that gets sent.
Under views create a folder called contact_mailer, and within this folder create the emails with the associated names. So for me, for my main home page form I had the email contact_email.html.erb, my partner form I created partner_email.html.erb.
<!DOCTYPE Html>
<html>
<head>
<title></title>
</head>
<body>
<p>New message from RuNpiXelruN's Contact Form!, from <%= "#{#contact.name}, #{#contact.email}" %></p>
</br>
<p><%= #contact.name %></p>
<p><%= #contact.phone %></p>
<p><%= #contact.comment %></p>
</body>
</html>
This is what will be in the email when you open it up. Change the message as you please.
All should now work smoothly, using only one form and model for your entire site!
Also, I meant to mention that if you have some fields that are specific to some applications, create them when you do your initial migration and query for the content_type in the main _form partial. i.e if content_type is ..this..
text_field_tag 'contact[...example..]'
for example.
One more thing, now that we are using one form, and one create method, it makes it a little difficult to render :new in our controller if there is an error. This is why you may have noticed that I've added the required: true to the inputs of the form I need, to handle errors here. That way the form won't be allowed to submit unless it has been filled correctly.
Test and make sure all the cogs are turning correctly. Watch your server log as it's happening. I deployed my app to Heroku. To get this to actually email, you need one more configuration step. I handled it with sendgrid. If you don't know how, and you get all this working as you'd like, reach out and i'll show you. Good luck!
Justin

Automatically creating a record for the user in rails

I am working on status feature for my rails app. I have everything set up and it works perfectly fine. For the interface, I am using the best_in_place gem to allow users to edit in place for the status. The code is given below.
<% if #scoreboard.status.content.present? %>
<div id="statuscontent">
<%= render 'statusedit' %>
</div>
<% else %>
<%= render 'statusupdate' %>
<% end %>
The statusedit partial contains the best_in_place code and the statusupdate partial contains the normal form for creating the status. Codes for both partials are given below.
statusedit
<%= best_in_place [#scoreboard, #status], :content, place_holder: "Enter a status", as: "textarea" %>
<%= link_to "delete", [#scoreboard, #status], method: :delete, data: {confirm: "Are you sure"} %>
statusupdate
<%= form_for [#scoreboard, #status] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, class: "form-control", id:"status" %>
<%= f.submit "Post", class: "btn btn-primary", id: "status-button" %>
<% end %>
The above code is working perfectly fine, however, I am struggling to set up the interface I want. The problem is that best_in_place editing only works if the status has been created before. Therefore, I have to create the status using the form and then I can use in place editing. However, I want the interface such that users can use in place editing without the need of physically creating a status first. Is there a way to create a default status for every user? Or a before_update function that maybe creates a status before the user tries to update.
I tried using Ajax for creating status to achieve the interface but it didn't work as intended. Also, I feel there must be a simpler solution to this problem. I tried setting :default => "please upload status" in the database but that doesn't actually create a default record. It acts like a placeholder but you still have to click post for the record to be created. Is there a way to somehow automatically create a default status for each user or set a value in the database. I have read a few stack-overflow posts on this but nothing really points to the right direction. There must be a simpler way of doing this in rails. Any documentation or suggestions would be a great help. As always, any help is always greatly appreciated!! Thanks
I have added the relevant migration and model files.
status model
class Status < ActiveRecord::Base
belongs_to :scoreboard
end
scoreboard model
class Scoreboard < ActiveRecord::Base
has_one :status
end
Status migration file
class CreateStatuses < ActiveRecord::Migration
def change
create_table :statuses do |t|
t.text :content
t.references :scoreboard, index: true
t.timestamps null: false
end
add_foreign_key :statuses, :scoreboards
end
end
Answer for your questions.
Is there a way to create a default status for every user ?
Yes
You could create a default record in database after change_column.
def up
change_column :users, :admin, :boolean, default: false # I'm assuming you are saving default as false in users table but you could change accordingly all thing.
end
before_update function that maybe creates a status before the user tries to update.
instead of before_update one could use after_save callback.
As mentioned in official Document
after_save runs both on create and update, but always after the more
specific callbacks after_create and after_update, no matter the order
in which the macro calls were executed.
And lastly
There must be a simpler way of doing this in rails
Use ActiveRecord Migrations.
Apologies if this answer is not specific; you wrote a lot.
You're best setting a default value for an attribute at the database level.
Yes, you can use the before_create callback:
#app/models/scoreboard.rb
class Scoreboard < ActiveRecord::Base
before_create :set_default_status
private
def set_default_status
self[:status] = "default"
end
end
The problem with this is that it adds application-level logic which can be handled by the db:
$ rails g migration CreateStatusDefault
# db/migrate/create_status_default________.rb
class CreateStatusDefault < ActiveRecord::Migration
def change
change_column :scoreboards, :status, default: "Default"
end
end
$ rake db:migrate
Creating a default value in the db basically means that unless you send a value from your model at create / update, you're going to get the default.
The beauty of setting it in the DB is that your Rails app has absolutely zero work to do for it, making it much more efficient.
--
I subsequently found that your status "object" is actually a model in itself, which is probably where your issues are coming from.
If that is the case, you'll need to build the associative object before you create your main/parent one:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :statuses
before_create -> (model) { model.statuses.build }
end
We use this to build a profile for each User who signs up to our applications; it creates a "blank" associative record for your parent model.
This is something which would give you a "blank" status object, but as you've not posted your code, I really don't know.
A good tip with SO is code > text.
If in doubt, post code.
Is there a way to create a default status for every user?
Yep you can write it yourself. Something like this ...
User < ActiveRecord::Base
has_many :statuses
after_create :create_first_status
def create_first_status
self.statuses.create some_status_attribute: "some value"
end
end

Display several nested attributes in Rails form

I've been looking for a solution for a few days, in a Rails 4.1 app, so here is my question :
In a Rails app, I have my model User and Adress.
class User < ActiveRecord::Base
has_many :adresses
accepts_nested_attributes_for :adresses
class Adress < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
In my form, I make a form_tag for User, no problem.
But, how I can display to the final user, in a form, 2 adresses fields?
I use <%= f.fields_for :adress %> to display one, it's ok. But if I display two forms (so the user can enter 2 adresses) they have both the same name and the request post only keep one.
I read the doc at http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
but, still, I don't get it.
Is there a proper way to do it?
Thanks
I would suggest you to prepare two addresses in new action, add them to the use and then in the form reneder it with foreach.
I found this kind of solution here : http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Since you have multiple addresses I think foreach is way to go.
So, to help anyone who is noob in Rails and stuck the same way I was :
In your controller :
#user = User.new
#user.adresses = Adress.new, Adress.new
In your view, form :
<%= form_for #user do |f| %>
<%= f.fields_for :adresses do |a| %>
<%= wp.text_field :name %>
<% end %>
<% end %>
will print the name field for adress two times.
(thanks again to #NickCatib)

Resources