Rails - routes - weird errors - ruby-on-rails

I am making a rails 4 app.
I just created a scaffold for Universities.
The universities table has three attributes, being :name, :logo, :post_code.
I created three universities as a test. They come up in the console.
The university.rb model has these associations:
The university model has these associations:
has_many :faculties
has_many :students
has_many :academics#, through: :researchers
has_many :educators
has_many :faculties
has_many :courses, through: :faculties
has_many :programs#, through: :academics
has_many :alumnus
has_one :policy_ip
has_one :policy_publication
has_one :policy_commerciality
has_many :eaip_assets
has_many :commercial_ip_assets
has_many :community_activities
has_many :ip_transfer_successes
has_many :spin_outs
has_many :ip_asset_managers#, through: :universities
has_many :expression_of_interest_options#, through: :ip_asset_manager, -> { where submitted: true }
has_many :expression_of_interest_assignments#, through: :ip_asset_manager, -> { where submitted: true }
has_many :expression_of_interest_licensings#, through: :ip_asset_manager, -> { where submitted: true }
has_many :expression_of_interest_collaborations#, through: :ip_asset_manager, -> { where submitted: true }
has_many :expression_of_interest_spin_outs#, through: :ip_asset_manager, -> { where submitted: true }
has_many :awards
has_many :profiles
The view university#show has:
<div class="col-md-7 col-md-offset-1">
<div class="profilet"><%= #university.name %></div>
<%= link_to 'Edit', edit_university_path(#university) %> |
<%= link_to 'Back', universities_path %>
The first error I get (on each of my 3 records) is:
undefined method `name' for nil:NilClass
When I change the line so that it is on:
<div class="profilet"><%= #university.try(:name) %></div>
I get an error on the 'edit' link that says:
No route matches {:action=>"edit", :controller=>"universities", :id=>nil} missing required keys: [:id]
When I delete the edit link and try again, I get a blank page with a 'back' link.
I can't understand whats going wrong. My console shows I definitely have 3 records in the university table (each with an id 1, 2 or 3). My routes are resources - (as they are with all of my other models) and this is the first problem I've encountered with the edit link.
I have strong params in my universities controller for each of the three attributes in the table, so I"m stuck for things to try to do to fix this.
Any ideas?
The universities controller has:
class UniversitiesController < ApplicationController
# before_action :set_university, only: [:show, :edit, :update, :destroy]
# GET /universities
# GET /universities.json
def index
#universities = University.all
end
# GET /universities/1
# GET /universities/1.json
def show
end
# GET /universities/new
def new
#university = University.new
end
# GET /universities/1/edit
def edit
end
# POST /universities
# POST /universities.json
def create
#university = University.new(university_params)
respond_to do |format|
if #university.save
format.html { redirect_to #university, notice: 'University was successfully created.' }
format.json { render action: 'show', status: :created, location: #university }
else
format.html { render action: 'new' }
format.json { render json: #university.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /universities/1
# PATCH/PUT /universities/1.json
def update
respond_to do |format|
if #university.update(university_params)
format.html { redirect_to #university, notice: 'University was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #university.errors, status: :unprocessable_entity }
end
end
end
# DELETE /universities/1
# DELETE /universities/1.json
def destroy
#university.destroy
respond_to do |format|
format.html { redirect_to universities_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_university
#university = University.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def university_params
params[:university].permit(:name, :logo, :post_code)
end
end

It means you didn't have object university in the actions. You have to enable the line below:
before_action :set_university, only: [:show, :edit, :update, :destroy]

The associated instance variable #university is nil therefore the route helper could not generate a route for it.
Please check your controller's edit action and fix the problem there.

You have #university with the id=nil, if you want to use edit url helper you need to provide #university entity with the id. In other words you can edit record that exists in DB, otherwise you should create new.

Wow you have a lot of problems here.
undefined method `name' for nil:NilClass
This means one of your variables is not initialized. The cause of this is where the fun begins.
When you define <%= #university.x %>, you're calling a method on your #university variable. If that variable is not populated with any data, or is undefined, Ruby automatically assigns the NilClass class to it.
You mention you're seeing this error when referencing the #university variable. This means either in your routes or controller, it's not being populated.
Here's how to fix it:
#config/routes.rb
resources :universities #-> url.com/universities/:id
#app/controllers/universities_controller.rb
class UniversitiesController < ApplicationController
before_action :set_university, only: [:show, :edit, :update, :destroy]
def index
#universities = University.all
end
def edit
end
def show
end
end
This should give you the capacity to use the following:
#app/views/universities/show.html.erb
<% if #university %>
<%= #university.name %>
<% end %>

Related

How to list all child objects for all parent objects?

What I have at the moment is pretty standard set of code, where all child objects can be list under only their parent objects.
customer.rb
class Customer < ApplicationRecord
has_many :bookings, dependent: :delete_all
end
booking.rb
class Booking < ApplicationRecord
belongs_to :customer
has_many_attached :images
end
routes.rb
Rails.application.routes.draw do
resources :customers do
resources :bookings
end
end
bookings_controller.rb
This has been automatically generated. I only removed comments and json
related lines.
class BookingsController < ApplicationController
before_action :set_customer
before_action :set_booking, only: %i[show edit update destroy]
def index
#bookings = Booking.all.with_attached_images
end
def show; end
def new
#booking = #customer.bookings.build
end
def edit; end
def create
#booking = #customer.bookings.build(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to #customer, notice: 'Booking was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #booking.update(booking_params)
format.html { redirect_to [#customer, #booking], notice: 'Booking was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#booking.destroy
respond_to do |format|
format.html { redirect_to customer_bookings_url, notice: 'Booking was successfully destroyed.' }
end
end
private
def set_customer
#customer = Customer.find(params[:customer_id])
end
def set_booking
#booking = #customer.bookings.find(params[:id])
end
def booking_params
params.require(:booking).permit(:name, :category, :rooms, :wifi, :phone, :address, :description, :available, :check_in, :check_out, :customer_id, images: [])
end
end
I want to list all child objects for all parent objects.
I guess, I will have to modify routes as follows
routes.rb
Rails.application.routes.draw do
root 'customers#index'
resources :customers do
resources :bookings
end
resources :bookings
end
I will also need to modify bookings_controller.rb
By commenting out the line before_action :set_customer, otherwise I will get an error like Couldn't find Customer without an ID
And I will have to put #customer = Customer.find(params[:customer_id]) for all methods except index. Which means I won't be following DRY concept...
Any other better approach to solve this?
Your approach is the best already in my opinion, just need to utilize Rails helpers correctly to keep your code DRY.
By commenting out the line before_action :set_customer, otherwise I
will get an error like Couldn't find Customer without an ID
And I will have to put #customer = Customer.find(params[:customer_id])
for all methods except index. Which means I won't be following DRY
concept...
NO you don't have to.
If the index action of customers/bookings_controller is not used anywhere else then just remove that action from the controller file and specify the same in the route file as:
resources :customers do
resources :bookings, except: :index
end
If the index action is still being used in other places then Rails callbacks can be declared with except option as below to specify that the set_customer will be called for all actions except the index.
before_action :set_customer, except: :index
More about Rails Controller Callback options here
Other points that you may want to check:
dependent: :delete_all. With this, there will be orphan active_storage_attachments records in your db when you delete a customer. Because it triggers the callback that deletes only the associated bookings when leave the attached images of those bookings untouched. Reference
resources :bookings (last line of your route file). If you only have the index action in the controller, you should declare the same here also as resources :bookings, only: :index

Ruby on rails - undefined method in show.html.erb

I am trying to display the name of the faculty instead of just the ID.
Everything else works except that part where I try to display the name of the faculty. I get an error saying "undefined method" .
I know I am doing something wrong, but I cannot figure it out at all even though I have been looking at this for hours. I am a completely beginner and I would really appreciate your help.
Thank you.
show.html.erb
<p id="notice"><%= notice %></p>
<!-- notice is a ruby method, and its results comes here inside the tags
used when you want the errow page to show on the next page -->
<p>
<strong>Name:</strong>
<%= #student.name %>
</p>
<p>
<strong>Faculty:</strong>
<%= #student.faculty_id %>
<%= #name.faculty_id %>
</p>
<strong>Grade:</strong>
<%= #student.grade%>
</p>
<%= link_to 'Edit', edit_student_path(#student) %> |
<%= link_to 'Back', students_path %>
student.rb
class Student < ActiveRecord::Base
belongs_to :faculty
end
class Name < ActiveRecord::Base
belongs_to :faculty
end
faculty.rb
class Faculty < ActiveRecord::Base
has_many :student
# belongs_to :faculty
has_many :name
end
This is my students_controler.rb
class StudentsController < ApplicationController
before_action :set_student, only: [:show, :edit, :update, :destroy]
# GET /students
# GET /students.json
def index
#students = Student.all
end
# GET /students/1
# GET /students/1.json
def show
end
# GET /students/new
def new
#student = Student.new
end
# GET /students/1/edit
def edit
end
# POST /students
# POST /students.json
def create
#student = Student.new(student_params)
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice: 'Student was successfully created.' }
format.json { render :show, status: :created, location: #student }
else
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /students/1
# PATCH/PUT /students/1.json
def update
respond_to do |format|
if #student.update(student_params)
format.html { redirect_to #student, notice: 'Student was successfully updated.' }
format.json { render :show, status: :ok, location: #student }
else
format.html { render :edit }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
# DELETE /students/1
# DELETE /students/1.json
def destroy
#student.destroy
respond_to do |format|
format.html { redirect_to students_url, notice: 'Student was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_student
#student = Student.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def student_params
params.require(:student).permit(:name, :faculty_id)
end
end
The error is undefined method `faculty_id' for nil:NilClass
I am trying to display the name of the faculty instead of just the ID
Since you're a beginner, let me explain it for you...
--
You're currently calling #student.faculty_id
This is the foreign_key of the #student object -- the identifier which links this student object to the appropriate faculty object.
In short, it means that this attribute is a part of the student schema -- you want one which is part of the faculty schema. Thus, you either need to use delegate to call the name attribute from faculty, or just call it directly:
#student.faculty.name
There are deeper problems with your model associations.
The above is how they should be set up:
#app/models/student.rb
class Student < ActiveRecord::Base
belongs_to :faculty
end
#app/models/faculty.rb
class Faculty < ActiveRecord::Base
has_many :students
end
The above will allow you to call the following:
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def show
#student = Student.find params[:id]
end
end
#app/views/students/view.html.erb
<%= #student.faculty.name %>
You must remember that Rails works on top of a relational database. This works by allowing you to call related objects by virtue of their foreign key.
I can explain more if required.
<%= #name.faculty_id %> won't work.
In your controller eager load the faculty
def show
#student = Student.includes(:faculty)
end
Either do
<% if #student.faculty.present? %>
<%= #student.faculty.name %>
<% end %>
or you could get the faculty in the controller and assign it to a variable
def show
#student = Student.includes(:faculty)
#faculty = #student.faculty
end
Then you can use that
<% if #faculty.present? %>
<%= #faculty.name %>
<% end %>
Error which I see is
class Faculty < ActiveRecord::Base
has_many :student
# belongs_to :faculty
has_many :name
end
Should be
class Faculty < ActiveRecord::Base
has_many :students
# belongs_to :faculty
has_many :names
end
I don't know your error due to this is or not but has_many is not with singular form

Creating a join table record through two different controllers in rails

I'm trying to get my head around the best way to add a record to a join table through alternative controllers in rails.
I have various models in my app that will require this, but I'm focusing on these two first before I transcribe the method into others, so shall use this as the example. I have a Venue and Interest model which are to be connected through VenuesInterests model (it has a couple of extra optional attributes so isn't a HABTM relationship). A user can admin a Venue instance and/or an Interest instance and therefore there should be an ability to select Venues to attach to an Interest and likewise Interests to attach to a Venue. This should be done with an Add Venues link on the Interest instance view and an Add Interests link on the Venue instance view. This would then take the user to a list of the relevant instances for them to select ones they would like to select.
Here are my models:
Venue.rb
class Venue < ActiveRecord::Base
has_many :interests, through: :venue_interests
has_many :venues_interests, dependent: :destroy
accepts_nested_attributes_for :venues_interests, :allow_destroy => true
end
Interest.rb
class Interest < ActiveRecord::Base
has_many :venues, through: :venue_interests
has_many :venues_interests, dependent: :destroy
end
VenuesInterests.rb
class VenuesInterest < ActiveRecord::Base
belongs_to :interest
belongs_to :venue
validates :interest_id, presence: true
validates :venue_id, presence: true
end
This all seems fine, however it's the controller and views that I'm struggling with. I've tried adding an extra method add_interest to the Venues controller to do the job of the create method in the VenuesInterests controller, so that there will be a different view when adding Venues to an Interest than there would be adding Interests to a Venue, otherwise I don't know how I would do this. My current Venues controller is as follows:
VenuesController.rb:
class VenuesController < ApplicationController
before_filter :authenticate_knocker!, only: [:new, :edit, :create, :update, :destroy]
respond_to :html, :json
def index
#venues = Venue.all.paginate(page: params[:page]).order('created_at DESC')
end
def show
#venue = Venue.find(params[:id])
#hash = Gmaps4rails.build_markers(#venue) do |venue, marker|
marker.lat venue.latitude
marker.lng venue.longitude
marker.infowindow venue.name
end
end
def new
#venue = Venue.new
end
def edit
#venue = Venue.find(params[:id])
end
def create
#venue = current_knocker.venues.create(venue_params)
respond_to do |format|
if #venue.save!
format.html { redirect_to #venue, notice: 'Venue was successfully created.' }
format.json { render json: #venue, status: :created, location: #venue }
else
format.html { render action: "new" }
format.json { render json: #venue.errors, status: :unprocessable_entity }
end
end
end
def update
#venue = Venue.find(params[:id])
#venue.update_attributes(venue_params)
respond_to do |format|
if #venue.update_attributes(venue_params)
format.html { redirect_to(#venue, :notice => 'Your Venue was successfully updated.') }
format.json { respond_with_bip(#venue) }
else
format.html { render :action => "edit" }
format.json { respond_with_bip(#venue) }
end
end
end
def destroy
end
def add_interests
#venues_interests = VenuesInterest.new
#interests = Interests.all.paginate(page: params[:page]).order(:name)
end
private
def venue_params
params.require(:venue).permit(:admin... etc)
end
end
This isn't currently working as I'm not sure how to reference other classes within a controller, but the important thing I'd like to know is is there a better way to do this or am I (kind of) on the right track? If anyone has a good method (perhaps a jQuery plugin) for allowing multiple selection of instances for the view, that would be great too!
In my opinion, I would take advantage of the existing update method to add the relationship between Interest and Venue. I can do like this:
def update
#venue = Venue.find(params[:id])
#venue.update_attributes(params[:venue_params])
if params[:interest_ids].present?
#venue.interests = Interest.where(id: params[:interest_ids])
#venue.save
end
#more code to handle the rendering
end

in nested form- Can't mass-assign protected attributes:

I have read through several threads and nothing so far. I am trying to nest one form in another. I am getting the can't mass assign protected attributes error. \
app/controllers/projects_controller.rb:46:in new'
app/controllers/projects_controller.rb:46:increate'
Projects_controller.rb
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render json: #project, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
project.rb
WF::Application.routes.draw do
resources :line_items
resources :projects do
resources :line_items
end
devise_for :users
get 'about' => 'pages#about'
get 'Production' => 'projects#index'
root :to => 'pages#home'
end
here is the error...
ActiveModel::MassAssignmentSecurity::Error in ProjectsController#create
Can't mass-assign protected attributes: line_item
here is my project model
class Project < ActiveRecord::Base
attr_accessible :quantity
# may be unnessary
attr_accessible :line_items_attributes
belongs_to :user
has_many :line_items
accepts_nested_attributes_for :line_items, :allow_destroy => true
end
Assuming you're trying to create line items through your project model, you'll need to make sure you have the following lines in your Project model:
# project.rb
Class Project < ActiveRecord::Base
attr_accessible :line_items_attributes
accepts_nested_attributes_for :line_items
...
end
Rails is trying to protect you from accidentally assigning values you don't mean to.
You can tell Rails what values are ok to assign in this way:
attr_accessible :value1, :value2
If you add that line to the top of the Project model (replace :value1 and :value2 with the actual names of your columns), it should allow you to do what you're attempting.
For more info, here's the docs.

Matching records to nested routes in show action

How can you limit the records that are available to the show action? The problem I'm having is that you can manually change the ID in the URL and look at projects that do not belong to the company.
My Routes Look like this:
/companies/:id/projects/:id
This is the show action
projects_controller.rb
def show
#project = Project.find(params[:id])
#company = Company.find(params[:company_id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end
routes.rb
resources :companies do
resources :projects
resources :employees
resources :requests do
put 'accept', :on => :member
end
end
project.rb
class Project < ActiveRecord::Base
attr_accessible :title
belongs_to :company
validates :title, presence: true
end
company.rb
class Company < ActiveRecord::Base
attr_accessible :name
has_many :projects
end
Assuming you have a has_many relationship between Company and Project I would change your controller code to this:
def show
#company = Company.find(params[:company_id])
#project = #company.projects.find(params[:id])
end
Keep in mind though that this does not really solve your problem as people can still change the company_id and view other companies easily. What you need is a more solid authorization framework like CanCan that prevents unauthorized access to resources.
May be, you should use smth like this:
#project = #company.projects.find(params[:id])
Check this for details.
Try to change the action to this
def show
#company = Company.find(params[:company_id])
#project = #company.projects.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end

Resources