Im building a basic chat room website with multiple chat rooms that users can comment on.
I managed to get my messages/comments to show up on each Room.show page, however when I go to enter
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
I receive a nil can't be converted to a Time value error. Also, I cannot get the Users name to show up. It just registers blank.
Here is the section of my Room.show.html.erb that is registering an error
<% #message.each do |message| %>
<div class="row">
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
<p><%= message.body %></p>
</div>
<% end %>
Here is my Message controller
def show
#rooms = Room.all
#message = #room.message
end
Here is my Routes file
Rails.application.routes.draw do
devise_for :users
#resources :messages
resources :users
resources :rooms do
resources :messages
end
root 'rooms#index'
end
Message.rb
class Message < ApplicationRecord
belongs_to :user
belongs_to :room
end
And room.rb
class Room < ApplicationRecord
has_many :message
end
Using updated version of rails.
message.created_at is nil. You can verify it by changing your code slightly:
<%= "#{time_ago_in_words(message.created_at || Time.now)} ago" %>
This will always show less than a minute ago because message.created_at is nil.
<%= "#{time_ago_in_words(message.created_at)} ago" if message.created_at %>
This will not show anything because message.created_at is nil.
I suggest that you use a debugging library like pry to help you troubleshoot this. You could do something like this:
<% #message.each do |message| %>
<div class="row">
<% binding.pry unless message && message.created_at %>
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
<p><%= message.body %></p>
</div>
<% end %>
That will set a breakpoint that will be triggered if message or message.created_at are nil, and allow you to use the Rails console to inspect the variables to help narrow down the problem.
Like others mentioned, you will need to make sure your models are appropriately constructed:
class Room < ApplicationRecord
has_many :messages
end
You should also check your schema.rb to ensure that the Message model has the following:
t.datetime "created_at", null: false
t.index ["room_id"], name: "index_messages_on_room_id"
The first is needed because without it message.created_at will not exist at all, and the second is needed for the association between the two models.
Your controller code doesn't make any sense and I'm assuming that you manually typed it into your question rather than copying and pasting it. You define #rooms as an ActiveRecord::Relation collection object, but you call #room.message. If you meant #rooms.message then that won't work because you're trying to call an instance method on a collection. I'm not sure what you meant here because the code doesn't make sense.
Additionally, you're not implementing a clean CRUD solution. Your Messages controller's show method should be for rendering a single Message object, not for rendering a collection of rooms' messages.
In general there are many problems with the code, the structure, and the example you have typed out. At the very least though, you must make sure that message.created_at is not nil before attempting to call time_ago_in_words, and that all traces back to:
making sure your models are correctly formed and associated with each other
making sure your controller is fetching real objects
making sure your view is appropriately iterating over those real objects
Related
I have a Plan model that has_many Versions. I'm nesting Version inputs inside a Plan form, and even though the validations seem to be working, the form inputs don't display any errors.
I don't know if this is relevant, but because the nested inputs are scattered around the form, I open the nested input blocks with simple_fields_for :versions_attributes[0] instead of simple_fields_for :versions because I want to be specific that all the inputs around the form correspond to the same object. Otherwise the hash would be built with a different key for each different block (eg: versions_attributes: { 0: {amount: value}, 1: {another_field: another_value} } instead of versions_attributes: { 0: {amount: value, another_field: another_value}}.
plan.rb:
class Plan < ApplicationRecord
has_many :versions
accepts_nested_attributes_for :versions
validates_associated :versions
end
version.rb
class Version < ApplicationRecord
belongs_to :plan
validates :amount, presence: true
end
plans_controller.rb
class PlansController < ApplicationController
def new
#plan = current_account.plans.build
#version = #plan.versions.build
end
def create
#plan = current_account.plans.build(plan_params)
if #plan.save
redirect_to plan_path(#plan)
else
#plan = #base_plan.plans[0]
render :new
end
end
def plan_params
params[:plan][:versions_attributes]["0"]&.merge!(
account_id: current_account.id,
)
params.require(:plan).permit(
:name,
:short_description,
versions_attributes: %i[id account_id amount],
)
end
end
form.html.erb:
<%= simple_form_for [#plan] do |f| %>
<%= f.input :name %>
<%= f.input :short_description %>
<%= f.simple_fields_for "versions_attributes[0]" do |v| %>
<%= v.input :amount %>
<% end %>
<% end %>
Problem:
#version.errors contains a hash with the object's validation errors. However, the related inputs don't render validation errors nor are the form-group-invalid has-error CSS classes added (provided by the simple_form gem).
My first guess is that it's got something to do with the create action. My second guess is that it's got something to do with the way I'm opening the nested input's blocks (described above).
Either way, I'm confused because #version.errors contains the nested object's errors.
I'm trying to get a simple button press that will store current user's id into a field but getting an error that says
ActionController::ParameterMissing (param is missing or the value is empty: request):
Here's my code.
The button code
<%= form_for(request.accept, remote: true) do |f| %>
<%= f.submit "Accept", class: "btn btn-primary" %>
<% end %>
request_controller
def accept
#request.ssp_id = current_user.id
#request.save
flash[:success] = "The request have been accepted!"
end
Thanks in advance.
The ParameterMissing error is probably because you have specified to require request model in your parameters through strong_params.
Since you are trying to update an existing record with the current_user you don't need a form.
Update your accept action in the RequestsController:
def accept
#request = Request.find params[:id]
if #request.update_attribute(:ssp, current_user)
redirect_to requests_path
flash[:success] = "The request have been accepted!"
end
end
Request model
class Request < ApplicationRecord
belongs_to :ssp, class_name: "User"
end
And your routes:
resources :requests do
member do
get "accept"
end
end
<%= link_to 'Accept request', accept_request_path(request) %>
Also as a recommendation try to use a different name for your model since the word request is wide use in Rails. I don't know if this could be a problem latter on.
I'm trying to learn web development as I go (I just need need to get this one project done. I don't plan on touching the subject ever again.) and I've run into the problem of getting data from a rails web page to its corresponding controller. My end goal is to get data from javascript variables and pass that to ruby, but I've decided to take small steps so for now I'm trying to get a button_to to send some hard coded strings from the new.html.erb to the corresponding create method in the controller. I've probably tried a hundred combinations of view, controller and, routs code and I can't get any of them to work. Here is the current iteration of my code for the controller, view, and routes (I'm not sure if routes even matters).
ponies_controller.rb
def create(name, pro)
##pony = Pony.new(params[:id])
#pony = Pony.new(name: name, profession: pro)
respond_to do |format|
if #pony.save
format.html { redirect_to #pony, notice: 'Pony was successfully created.' } |~
format.json { render :show, status: :created, location: #pony }
else
format.html { render :new }
format.json { render json: #pony.errors, status: :unprocessable_entity }
end
end
end
new.html.erb
<h1>New Pony</h1>
<%= render 'form', pony: #pony %>
<%= link_to 'Back', ponies_path %>
<%= button_to "create_pony", {action: create("s","ss")}, remote: true,from_class: "create_pony" %>
routes.rb (Not sure if this is important)
Rails.application.routes.draw do
resources :ponies
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
post 'ponies/:id/create' => 'pony#create', as: :create_pony
end
While this code doesn't work I think it shows how I think things should work. I feel like I should just be able to call the method and be done with things, but that is obviously wrong. What should I be doing to get button_to to post a new "pony"?
Here's the Rails way to do this in the simplest way (assuming you've set up your Pony model correctly):
# ponies_controller.rb
def create
#pony = Pony.new(pony_params)
if #pony.save
# success
else
# errors
end
end
private
def pony_params
params.require(:pony).permit(:name, :profession) # whitelist the parameters you want to accept from the pony creation form
end
and your pony form should like this
# new.html.erb
<h1> New Pony </h1>
<%= form_for #pony, remote: true do |f| %>
<%= f.text_field :name %> # this will be passed to the controller in the params hash
<%= f.text_field :profession %> # this too
<%= f.submit %>
<% end %>
also, resources :ponies will create all the routes you need for ponies so no need to define one yourself.
This must be a common need but I can't seem to find a definitive answer on the most rubyesque way. I need to create a fairly complex algorithm to dynamically calculate course grades in a rails 4.1 app.
Specifically, I have a model, "course", and whenever an instance of it is displayed in the view, I want to dynamically calculate the current grade (a decimal value, calculated from many course.field values) and display it as a letter value using a switch/case. My assumption was that I could do this in the controller (but it almost seems like it's complex enough to warrant it's own -- module? In C++ I would create a class). At the same time, since it is created dynamically, it seemed like bad form to create a current_grade field for it in the model, so it's not one I can pass back and forth as one of the allowable params (that I know of-- can one pass a variable in the params that is not represented in the db?).
In my initial research I see suggestions of hidden_field_tags and helper_methods and all_helpers and modules and global modules and more. Under time pressure, I dread beginning down the wrong path. Which is the better approach? Or a good high level doc for reference?
As an example, here is one view in which I would like to calculate current grade, compare it to desired grade, and display accordingly.
# index.html.erb
<% #courses.each do |course| %>
<li>
<%= my_algorithm_to_calculate_curr_grade(many course.fields used to caluculate)
<= course.desired_grade ? "set li to <Color: red>" : "set li to <Color: green>" %>
<%= course.course_name %>
Current Calculation: <%= display_results_of_previous_calculation %>
(Goal: <%= course.desired_grade %>)
<%= link_to 'Show', course %>
<%= link_to 'Edit', edit_course_path(course) %>
<%= link_to 'Drop Course Without Penalty', course, method: :delete, data: { confirm: 'Are you sure?' } %>
</li>
<% end %>
It's hard to tell from your question if course.fields are attributes of Course or different model(s). If all the fields are Course attributes, I would put it as an instance method on Course.
class Course < ActiveRecord::Base
def calculated_grade
# fun algorithm
end
end
If course.fields need to be loaded from the database, I'd probably go with a Plain Old Ruby Object (PORO), maybe call it CourseGradeCalculator (put it in app/models, why not? It's business logic)
class CourseGradeCalculator
attr_reader :course, :fields, :grade
def initialize(course, fields)
#course = course
#fields = fields
#grade = calculate_grade
end
private
def calculate_grade
# fun algorithm
end
end
# controller
#course = Course.preload(:fields).find(params[:id]
# view
CourseGradeCalculator.new(#course, #course.fields)
This is an error I can not seem to figure out I believe I have it routed. This is the error
No route matches {:action=>"ticket_action", :controller=>"tickets"}
I get this error after this code
<h4>New Action</h4>
<% form_tag :action => 'ticket_action' do %>
<p><b>Description</b><br/>
<%= text_area 'description', 'description', 'rows' => 5 %><br/>
User: <%= select("actUser", "user_id", User.find(:all).collect{|u| [u.name, u.id] } )%>
<% end %>
I have this on my ticket_controller.rb is that the proper placement for that
#action
def ticket_action
#act = Action.new(
"ticket_id" => flash[:ticket_id],
"description" => params[:description]['description'],
"user_id" => params[:actUser]['user_id']
)
routes
actions GET /actions(.:format) actions#index
POST /actions(.:format) actions#create
new_action GET /actions/new(.:format) actions#new
edit_action GET /actions/:id/edit(.:format) actions#edit
action GET /actions/:id(.:format) actions#show
PUT /actions/:id(.:format) actions#update
DELETE /actions/:id(.:format) actions#destroy
tickets GET /tickets(.:format) tickets#index
POST /tickets(.:format) tickets#create
new_ticket GET /tickets/new(.:format) tickets#new
edit_ticket GET /tickets/:id/edit(.:format) tickets#edit
ticket GET /tickets/:id(.:format) tickets#show
PUT /tickets/:id(.:format) tickets#update
DELETE /tickets/:id(.:format) tickets#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
clients GET /clients(.:format) clients#index
POST /clients(.:format) clients#create
new_client GET /clients/new(.:format) clients#new
edit_client GET /clients/:id/edit(.:format) clients#edit
client GET /clients/:id(.:format) clients#show
PUT /clients/:id(.:format) clients#update
DELETE /clients/:id(.:format) clients#destroy
It would be helpful to post the route to debug this problem, your route may refer to tickets yet your class is ticket.
You should look into restful routes, especially given your use case. It seems you should really have an actions controller (ActionsController, named controllers/actions_controller.rb) and then post to the create action and provide a restful route (resources :actions)
My suggestion would be to read up on rest and rails first.
Additionally the flash isn't where you should store your ticket_id, ideally you should retrieve it in your actions controller's create action by posting to /action/ticket_action/1 and retrieving the id by accessing params[:id] in the controller. If you really must, store it in the session (session[:ticket_id] = "1") but 'rest' is where you should be headed. The flash will be removed and should only be set in the controller and then displayed on the next page, it will be deleted thereafter.
Update: ok thanks for posting your routes.
You can add the missing route like this if you want:
resources :tickets do
member do
post 'ticket_action'
end
end
But it would be better to follow this pattern:
In actions controller:
def new
#action = Action.new
end
Your form should look a bit like this, Rails will know to post to actions#create because #action is a new record (you can check #action.new_record? if you want)
<%= form_for #action do |f| %>
<%= f.text_area :description, :rows => 5 %>
<%= f.hidden_field :ticket_id, flash[:ticket_id] %>
<%= f.select :user_id, User.find(:all).collect{|u| [u.name, u.id] } %>
<%= f.submit "Create" %>
<% end %>
Then in your actions controller:
def create
#action = Action.new(params[:action])
end
or with less magic:
def create
#action = Action.new(:user_id => params[:action][:user_id],
:description => params[:action][:description],
:ticket_id => params[:action][:ticket_id])
if #action.save
redirect_to actions_path(#action, :notice => "Created action")
else
render :new # any errors will be in #action.errors
end
end
You should really be setting the ticket_id in the actions controller's new method though.
def new
#action = Action.new(:ticket_id => params[:ticket_id])
end
And then in your form:
<%= f.hidden_field :ticket_id %>
Your file name should be "tickets_controller.rb", plural.