I am extremely new to Ruby on Rails and while I am learning a lot fairly quickly, I have run into some issues with the proper syntax to interact between the Model and Controller layers. I ham working on a toy project that simulates a Jurassic Park management app. The db schema is as follows:
schema.rb
ActiveRecord::Schema.define(version: 2021_01_24_134125) do
create_table "cages", force: :cascade do |t|
t.string "name"
t.integer "max_capacity"
t.integer "number_of_dinosaurs"
t.string "power_status"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "dinosaurs", force: :cascade do |t|
t.string "name"
t.string "species"
t.string "diet_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "cage_id", null: false
t.index ["cage_id"], name: "index_dinosaurs_on_cage_id"
end
add_foreign_key "dinosaurs", "cages"
end
I have already written a few helper methods in both the dinosaur and cage models, but when I try to actually use them in the cage.controller or dinosaur.controller, I run into some issues with how to do so. These are the methods below:
cage.rb
class Cage < ApplicationRecord
has_many :dinosaurs
validates :name, :max_capacity, :power_status, presence: true
validates_uniqueness_of :name
def dinosaur_count
dinosaurs.count
end
def at_capacity?
return dinosaur_count == max_capacity
end
def is_powered_down?
return power_status == "DOWN"
end
def has_herbivore
dinosaurs.where(diet_type:"Herbivore").count > 0
end
def has_carnivore
dinosaurs.where(diet_type:"Carnivore").count > 0
end
def belongs_in_cage(diet)
return true if dinosaur_count == 0
return false if diet != 'Carnivore' && has_carnivore
return false if diet != 'Herbivore' && has_herbivore
return true if dinosaurs.where(diet_type: diet).count > 0
return false
end
def has_dinosaurs?
return dinosaur_count > 0
end
end
dinosaur.rb
class Dinosaur < ApplicationRecord
belongs_to :cage
validates :name, :species, :diet_type, :cage_id, presence: true
validates_uniqueness_of :name
def set_cage(c)
return false if c.at_capacity?
cage = c
end
def move_dino_to_powered_down_cage(c)
return false if c.is_powered_down?
cage = c
end
def is_herbivore?
return diet_type == "Herbivore"
end
def is_carnivore?
return diet_type == "Carnivore"
end
end
I have tried something like this in the cage.controller update, but it is ignored when updating a cage's power status for example.
if #cage.is_powered_down? == "true"
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #cage.errors, status: :unprocessable_entity }
else
format.html { redirect_to #cage, notice: "Cage was successfully updated." }
format.json { render :show, status: :ok, location: #cage }
end
Would anyone be able to assist me with this at all?
Ah yes, well #cage.is_powered_down? is returning a Boolean, so you can just do:
if #cage.is_powered_down?
Related
In trying to run a setup rake tasks(to populate my dbase). It is telling me that questions must exist. When I do a count on 'Question.count' - it returns 4 - so I know questions exist.
What am I missing? typo? syntax? When I look at similiar SO, it shows as some sort of typo but I am not seeing it.
Here is the code that is not working:
def add_option_group
puts " * Add Option Groups...\n"
OptionGroup.transaction do
create_option_group(
option_group_name: "Always-Never",
question_id: Question.find_by(question_name: "Do you have an updated photo?")
)
create_option_group(
option_group_name: "Yes-No",
question_id: Question.find_by(question_name: "Do you have a bio saved (updated in last 12 months)?")
)
end
end
def create_option_group(options={ })
puts " * CREATE OptionGroup Section...\n"
option_group_attributes = {}
attributes = option_group_attributes.merge options
option_group = OptionGroup.create! attributes
option_group.save!
option_group
end
I am getting this error message:
Add Option Groups...
CREATE OptionGroup Section...
rake aborted!
ActiveRecord::RecordInvalid: Validation failed: Questions must exist
/Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:314:in create_option_group' /Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:298:in block in add_option_group'
/Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:297:in add_option_group' /Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:51:in create_sample_data!
Migration files:
***** migrations ******
class CreateOptionGroups < ActiveRecord::Migration[6.1]
def change
create_table :option_groups do |t|
t.bigint :question_id
t.text :option_group_name
t.timestamps
end
end
end
class CreateQuestions < ActiveRecord::Migration[6.1]
def change
create_table :questions do |t|
t.bigint :assessment_section_id
t.bigint :input_type_id
t.text :question_name
t.string :question_subtext
t.boolean :question_required_yn
t.boolean :answer_required_yn
t.boolean :allow_multiple_options_answers_yn
t.integer :dependent_question_id
t.integer :dependent_question_option_id
t.integer :dependent_answer_id
t.timestamps
end
end
end
Here is schema
t.bigint "question_id"
t.bigint "option_choice_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "questions", force: :cascade do |t|
t.bigint "assessment_section_id"
t.bigint "input_type_id"
t.text "question_name"
t.string "question_subtext"
t.boolean "question_required_yn"
t.boolean "answer_required_yn"
t.boolean "allow_multiple_options_answers_yn"
t.integer "dependent_question_id"
t.integer "dependent_question_option_id"
t.integer "dependent_answer_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Add foreign key:
class ForeignMoreKeysToModels < ActiveRecord::Migration[6.1]
def change
add_foreign_key :option_groups, :questions, validate: false
add_foreign_key :option_choices, :option_groups, validate: false
end
end
In the model:
class Question < ApplicationRecord
has_one :assessment_section
belongs_to :assessment_section, optional: true
has_many :option_groups
end
class OptionGroup < ApplicationRecord
has_many :option_choices
belongs_to :questions
end
What am I missing?
thx.
Two issues:
In the OptionGroup class, it should say belongs_to :question.
Try adding .id to the question lookup:
create_option_group(
option_group_name: "Always-Never",
question_id: Question.find_by(question_name: "Do you have an updated photo?").id
)
I'm trying to "edit a gig" on my app but it's giving me the error that the gig has no user_id. Creating a new gig is no issue. I feel like it's just poor syntax I didn't look over correctly but I have no idea. I've tried everything.
NoMethodError at /gigs/:id/edit
undefined method `user_id' for nil:NilClass
file: gigs_controller.rb location: block in <class:GigController> line: 43
Below is my App Controller, Gig Controller, schema, and log.
Thanks!
APP CONTROLLER
require './config/environment'
class ApplicationController < Sinatra::Base
configure do
set :public_folder, 'public'
set :views, 'app/views'
enable :sessions
set :session_secret, "secret"
end
get '/' do
erb :welcome
end
get '/logout' do
session.clear
redirect to '/'
end
#helpers
helpers do
def logged_in?
!!current_user
end
def current_user
User.find_by_id(session[:user_id])
end
def is_authorized?(id)
current_user.id == id.to_i
end
end
end
GIG CONTROLLER
class GigController < ApplicationController
get '/gigs' do
if logged_in?
#gigs = Gig.all
erb :'/gigs/index'
else
redirect to '/signup'
end
end
get '/gigs/new' do
if logged_in?
#gigs = Gig.all
erb :'gigs/new'
else
redirect to '/login'
end
end
post '/gigs' do
if logged_in?
#gig = current_user.gigs.create(bands: params[:bands], location: params[:location], date: params[:date], time: params[:time])
"Event has been added."
redirect "/gigs"
else
"When creating a new Event, please provide Artists, a Location, a date and a time."
redirect '/gigs'
end
end
get '/gigs/:id' do
if logged_in?
#gig = Gig.find(params[:id])
erb :"gigs/show"
else
redirect '/login'
end
end
get '/gigs/:id/edit' do
if logged_in?
#gig = Gig.find(params[:id])
if current_user.id == #gig.user_id
erb :"gigs/edit"
else
"You are not authorized to edit this Event."
redirect to '/login'
end
end
end
end
SCHEMA-
ActiveRecord::Schema.define(version: 20200209173236) do
create_table "genres", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "gigs", force: :cascade do |t|
t.string "bands"
t.string "location"
t.date "date"
t.string "time"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
ERROR-
D, [2020-02-10T13:53:35.237402 #11377] DEBUG -- : ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
D, [2020-02-10T13:53:35.257906 #11377] DEBUG -- : User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 4]]
D, [2020-02-10T13:53:35.262209 #11377] DEBUG -- : Gig Load (0.1ms) SELECT "gigs".* FROM "gigs" WHERE "gigs"."id" = ? LIMIT 1 [["id", nil]]
2020-02-10 13:53:35 - ActiveRecord::RecordNotFound - Couldn't find Gig with 'id'=:
/Users/melc/.rvm/gems/ruby-2.6.1/gems/activerecord-4.2.11.1/lib/active_record/core.rb:155:in `find'
/Users/melc/nyc-shows/app/controllers/gigs_controller.rb:42:in `block in <class:GigController>'
We are building a scheduling app in class, and attempting to create a function to allow for the copying of a previous shift, and all employees under that shift to the next week of scheduling. Here is my code currently:
def copy
set_calendar
#past_shift = Shift.find(params[:id])
if #calendar.users.owners.include?(current_user) || #calendar.users.managers.include?(current_user)
#shift = #past_shift.clone
if #shift.save
render json: ("Shift copied successfully"), status: :ok
else
render json: #usershift.errors, status: :uprocessable_entity
end
else
render json: ("You do not have access to copy shifts"), status: :unauthorized
end
end
I've used both .clone and .dup, but it seems it only copies the shifts over, without copying the employees associated with those shifts. Any help or guidance in the right path would be appreciated.
Below is the entire controller for shifts:
class ShiftsController < ApplicationController
def index
set_calendar
if params["start_date"] && params["end_date"]
start_date = params["start_date"].to_date
end_date = params["end_date"].to_date
if #calendar.users.include?(current_user)
#shifts = Shift.where(
calendar_id: #calendar.id,
start_time: start_date.beginning_of_day .. end_date.end_of_day)
render "/shifts/index.json", status: :ok
else
render json: '{}', status: :unauthorized
end
else
render json: {'error': 'start date and end date are required'}, status: :unprocessable_entity
end
end
def myschedule
#user = User.find(params[:user_id])
#publishedshifts = #user.shifts.where(published: true)
if #user
render "/shifts/index2.json", status: :ok
else
render json: ('You do not have access to these shifts'), status: :unauthorized
end
end
def create
set_calendar
if #calendar.users.owners.include?(current_user) || #calendar.users.managers.include?(current_user)
#shift = Shift.new(shift_params)
if #shift.save
render "/shifts/create.json", status: :ok
else
render json: #shift.errors
end
else render json: ('You do not have access to create a shift.'), status: :unauthorized
end
end
def copy
set_calendar
#past_shift = Shift.find(params[:id])
if #calendar.users.owners.include?(current_user) || #calendar.users.managers.include?(current_user)
#shift = #past_shift.clone
if #shift.save
render json: ("Shift copied successfully"), status: :ok
else
render json: #usershift.errors, status: :uprocessable_entity
end
else
render json: ("You do not have access to copy shifts"), status: :unauthorized
end
end
def update
set_calendar
set_shift
if #calendar.users.owners.include?(current_user) || #calendar.users.managers.include?(current_user)
if #shift.update_attributes(shift_params)
render "/shifts/update.json", status: :ok
else
render json: #shift.errors, status: :uprocessable_entity
end
else render json: ("You don't have access to update shifts."), status: :unauthorized
end
end
def destroy
set_calendar
set_shift
if #calendar.users.owners.include?(current_user) || #calendar.users.managers.include?(current_user)
if #shift.destroy
render json: ("Shift deleted!"), status: :ok
else
render json: #shift.errors, status: :uprocessable_entity
end
else render json: ("You don't have access to delete shifts.")
end
end
private
def set_calendar
#calendar = Calendar.find(params[:calendar_id])
end
def set_shift
#shift = Shift.find(params[:id])
end
def shift_params
params.permit(:start_time, :end_time, :calendar_id, :capacity, :published)
end
end
Schema:
create_table "users", force: :cascade do |t|
t.string "email"
t.string "phone_number"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "api_token"
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
end
create_table "usershifts", force: :cascade do |t|
t.bigint "user_id"
t.bigint "shift_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["shift_id"], name: "index_usershifts_on_shift_id"
t.index ["user_id"], name: "index_usershifts_on_user_id"
end
create_table "shifts", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.bigint "calendar_id"
t.integer "capacity"
t.boolean "published"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["calendar_id"], name: "index_shifts_on_calendar_id"
end
Ok, I suppose you don't want to clone user records when cloning shift. Also, based on the migrations, it seems that Shift and User models are connected through usershifts table, ie Shift model has_many :users, through :usershifts.
If so, the code can be:
#shift = #past_shift.dup
#shift.users = #past_shift.users
if #shift.save
...
else
...
end
#past_shift.dup returns a duplicate of the record that hasn't been assigned an id yet and is treated as a new record. #shift.users = #past_shift.users copies associated users (it does not create new records in users but only in join table usershifts, and these records are not saved yet). #shift.save saves it all :)
Having major Rails issues currently. I'm creating a School-Dashboard app that takes an xref table called Enrollments that relates Courses and Students.
Any time I try to update a grade for an Enrollment, I constantly get this line
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: UPDATE
"enrollments" SET "grade" = ?, "updated_at" = ? WHERE "enrollments"."" IS NULL
This line does not appear when I update attributes for Courses or Students. Only for the :grade attribute in Enrollment.
For some reason, it isn't being read properly, even though it is a legitimate attribute in my db for Enrollment (check out my schema).
I do all of my preliminary work in the rails sandbox.
Using ruby 2.1.1, Rails 4.1.0.rc1
I'd really love some help here.
Here are my corresponding models
class Student < ActiveRecord::Base
has_many :enrollments
has_many :courses, through: :enrollments
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :students, through: :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
The controllers:
Students
class StudentsController < ApplicationController
def index
#students = Student.all
end
def new
#student = Student.new
end
def show
end
def update
#student.update_attributes(student_params) ? redirect_to #student : render 'edit'
end
def create
#student = Student.new(student_params)
#student.save ? redirect_to #student : render 'new'
end
def destroy
end
def edit
end
private
def student_params
params.require(:student).permit(:first_name, :last_name, :student_number, :email)
end
end
Courses
class CoursesController < ApplicationController
def index
#courses = Course.all
end
def new
#course = Course.new
end
def show
end
def update
#course.update_attributes(course_params) ? redirect_to #course : render 'edit'
end
def create
#course = Course.new(course_params)
#course.save ? redirect_to #course : render 'new'
end
def destroy
end
def edit
# code here
end
private
def course_params
params.require(:course).permit(:course_name, :course_number)
end
end
Enrollments
class EnrollmentsController < ApplicationController
attr_accessor :course_id, :student_id, :grade
def index
#enrollments = Enrollment.all
end
def new
#enrollment = Enrollment.new
end
def create
#enrollment = Enrollment.new(enrollment_params)
#enrollment.save ? redirect_to #enrollment : render 'new'
end
def update
#enrollment.update_attributes(enrollment_params) ? redirect_to #enrollment : render 'edit'
end
def show
end
def destroy
#enrollment.destroy
end
def edit
# code here
end
private
def enrollment_params
params.require(:enrollment).permit(:course_id, :student_id, :grade)
end
end
And finally my schema.rb
ActiveRecord::Schema.define(version: 20140417152720) do
create_table "courses", force: true do |t|
t.string "course_name"
t.integer "course_number"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "enrollments", id: false, force: true do |t|
t.integer "course_id", null: false
t.integer "student_id", null: false
t.decimal "grade", precision: 5, scale: 2
t.datetime "created_at"
t.datetime "updated_at"
end
# noinspection RailsParamDefResolve
add_index "enrollments", ["course_id", "student_id"], name: "index_enrollments_on_course_id_and_student_id"
# noinspection RailsParamDefResolve
add_index "enrollments", ["student_id", "course_id"], name: "index_enrollments_on_student_id_and_course_id"
create_table "students", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.integer "student_number"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Looks like I figured it out on my own!
So here's a bit of rails convention that needs to be addressed. The problem was with my database setup for 'Enrollments'. When I run the command
rails g migration CreateJoinTableEnrollments course student
Rails does too much work for me in my migration file (except for the table name and grade, I added that)
class CreateJoinTableEnrollments < ActiveRecord::Migration
def change
create_join_table :courses, :students, table_name: :enrollments, id: false, force: true do |t|
t.index [:course_id, :student_id], null: false
t.index [:student_id, :course_id], null :false
t.decimal :grade, precision: 5, scale: 2
t.timestamps
end
end
end
In reality, I didn't need any of that. In order to manipulate specific data in a row for Enrollments, there has to be an identifier for that row. With id: false, force: true that option got nullified. I also simplified things with the indexing. I just created regular old columns instead. Now my migration file looks like this.
class CreateJoinTableEnrollments < ActiveRecord::Migration
def change
create_table :enrollments do |t|
t.integer :course_id, null: false
t.integer :student_id, null: false
t.decimal :grade, precision: 5, scale: 2
t.timestamps
end
end
end
And with that, no issues! I've just been breaking my head over that for the past 2 days. Hope this helps anyone else who has this issue.
This is my setup:
user.rb
acts_as_authentic do |c|
c.logged_in_timeout(1.minutes)
end
user_session.rb
def to_key
new_record? ? nil : [ self.send(self.class.primary_key) ]
end
self.logout_on_timeout = true
application_controller.rb
helper_method :current_user_session, :current_user
private
def current_user_session
logger.debug "ApplicationController::current_user_session"
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
logger.debug "ApplicationController::current_user"
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def require_user
logger.debug "ApplicationController::require_user"
unless current_user
#store_location
flash[:warning] = "You must be logged in to access this page"
#redirect_to new_user_session_url
redirect_to root_url
return false
end
end
def require_no_user
logger.debug "ApplicationController::require_no_user"
if current_user
#store_location
flash[:warning] = "You must be logged out to access this page"
redirect_to account_url
return false
end
end
But when I load my page, I will get the error
undefined method `logged_out?' for #<User:0x00000103ee8348>
I try to read the official GitHub page of Authlogic, but I still don't know, what I miss... Could anyone give me a tip for fix it?
Many thanks in advance!
I was having the exact same problem, and it boiled down to the fact I didn't have all of the necessary columns in my User model.
My original User model (from db/schema.rb) was pretty minimalistic:
create_table "users", :force => true do |t|
t.string "username"
t.string "name"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
t.string "perm", :default => "employee"
end
However, I added the column t.datetime :last_requested_at to my model, as well as a few others that may or may not be needed. My final User model looks like:
create_table "users", :force => true do |t|
t.string "username"
t.string "name"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
t.string "perm", :default => "employee"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "login_count", :default => 0, :null => false
t.integer "failed_login_count", :default => 0, :null => false
t.datetime "last_request_at"
t.datetime "current_login_at"
t.datetime "last_login_at"
t.string "current_login_ip"
t.string "last_login_ip"
end
After adding in the other columns, I no longer get the undefined method 'logged_out?'... error.
Good luck!
(reference/more info: http://www.michaelhamrah.com/blog/2009/05/authlogic-and-openid-on-rails/ -- search in the page for logged_out?, the explanation was about 3/4 of the way down the post.)
If you want to know if a user is logged out, you can do:
if current_user_session
...
This condition will return true if the user is logged in (there is a session), and false if they are logged out (the session is nil).
As for the error message, undefined method 'logged_out?' for #<User:0x00000103ee8348> means that you haven't defined a method called logged_out?, so it doesn't know what you mean.
Authlogic doesn't define a logged_out? method for the User model, and neither have you, so there's nothing to call. The reason is that the state of either being "logged in" or being "logged out" doesn't have anything to do with the User model, but instead is a property of whether or not a given user has an active UserSession record.