I built out a recipe book for my wife and her family and I seem to be having issues with getting the notes that people add to display, mainly when someone posts a note it replaces the previous note with the new one instead of displaying all of the notes. I tried adding a notes table and controller and all of that and it kept throwing errors and just wouldn't work. So I have tried to make a model method to just show all of the notes but it gives me an error in my update action. This is my first real project out of my Bootcamp so I am still fairly green at all of this but I want to show my controller and model with the method that I thought that would work. I don't know if I was going down the right track with the model and controller and just didn't quite get all of the syntax right or if the model method is the correct path.
Model:
class Recipe < ApplicationRecord
validates :name, presence: true
def ingredients_list
ingredients.split(", ")
end
def directions_list
directions.split(". ")
end
def recipe_notes
recipe_notes = notes.all
end
end
Controller:
class RecipesController < ApplicationController
def index
recipes = Recipe.all
render json: recipes
end
def show
recipe = Recipe.find_by(id: params[:id])
render json: recipe
end
def create
recipe = Recipe.new(
name: params["name"],
contributor: params["contributor"],
ingredients: params["ingredients"],
directions: params["directions"],
category: params["category"]
)
if recipe.save
render json: recipe
else
render json: {errors: recipe.errors.full_messages},
status: 422
end
end
def update
recipe_id = params["id"]
recipe = Recipe.find_by(id: recipe_id)
recipe.name = params["name"] || recipe.name
recipe.contributor = params["contributor"] || recipe.contributor
recipe.ingredients = params["ingredients"] || recipe.ingredients
recipe.directions = params["directions"] || recipe.directions
recipe.category = params["category"] || recipe.category
recipe.notes = params["notes"] || recipe.notes
if recipe.save
render json: recipe
else
render json: {errors: recipe.errors.full_messages},
status: 422
end
end
def destroy
recipe_id = params["id"]
recipe = Recipe.find_by(id: recipe_id)
recipe.destroy
render json: {message: "Recipe Deleted"}
end
end
This was my schema before I removed all of the notes stuff:
create_table "notes", force: :cascade do |t|
t.integer "recipe_id"
t.string "note"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "recipes", force: :cascade do |t|
t.string "name"
t.string "ingredients"
t.string "directions"
t.string "category"
t.string "contributor"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "notes"
end
This was the model before I tried to move to a model method:
class Recipe < ApplicationRecord
validates :name, presence: true
has_many :notes
def ingredients_list
ingredients.split(", ")
end
def directions_list
directions.split(". ")
end
end
class Note < ApplicationRecord
belongs_to :recipe
end
This was the controller that I set up for the notes before I removed it:
class NotesController < ApplicationController
def show
note = Note.find_by(id: params[:id])
render json: note
end
end
I also had the route set up for a show action too
Related
Here is my code:
patient_profile.rb
class PatientProfile < ApplicationRecord
belongs_to :patient
............
attr_encrypted :dob, key: Base64.decode64(SECRET_KEY)
validates_presence_of :dob
end
db/migrate/20200618205840_patient_profiles.rb
class PatientProfiles < ActiveRecord::Migration[6.0]
def change
create_table :user_profiles do |t|
t.string :encrypted_gender
t.string :encrypted_gender_iv
t.string :encrypted_address
t.string :encrypted_address_iv
.....................
t.date :encrypted_dob
t.date :encrypted_dob_iv
t.integer :patient_id, index: true
t.timestamps
end
end
end
patients_controller.rb
class PatientsController < ApplicationController
# POST /api/v1/signup
post '/signup' do
patient = Patient.new(params[:patient])
patient.password = params[:patient][:password]
patient.unique_code = SecureRandom.hex(6)
patient.confirmation_token = (SecureRandom.random_number(9e4) + 1e4).to_i
patient.confirmation_sent_at = Time.now
if patient.save
token = JsonWebToken.encode(patient_id: patient.id)
send_email(patient,
"Account Verification",
"Your verification code is #{patient.confirmation_token}")
response = { patient: patient_response(patient), token: token }
[200, response.to_json]
else
halt 422, error_message(patient.errors.full_messages)
end
rescue ActiveRecord::RecordInvalid => error
halt 422, error_message(error.message)
end
In postman we can post date and it shows posted.
But in database dob field is null.
What's an issue with this?
Thanks in advance.
Here is log:
I changed:
t.date :encrypted_dob
t.date :encrypted_dob_iv
to
t.string :encrypted_dob
t.string :encrypted_dob_iv
and it works fine and saves encrypted dob in a database.
I am attempting to build a chess application, and am testing the logic of the ability to only select pieces that match the color of the player. The method in the Piece model is passing all RSpec tests, but I run into an error while testing the show action in the pieces controller. I have narrowed down the issue to the pieces controller being unable to access the white_player_id column. It is able to access all other columns in the games table. I do not have white_player or black_player models, but I do assign the IDs based on the current_user. When a game is created, the current_user automatically becomes the white player (user.id = white_player_id), and the black player is the second user who joins the game after its creation.
How can I get the pieces controller to access the white_player_id value? I don't want to create white_player and black_player models, because they are only needed for color checks.
If it matters, the database I am using is PostgreSQL.
This is the Piece model:
class Piece < ApplicationRecord
belongs_to :game
validates :x, numericality: true
validates :y, numericality: true
scope :black_pieces, ->() { where(color: 'black') }
scope :white_pieces, ->() { where(color: 'white') }
scope :active, -> { where(captured: false) }
def white?
color == 'white'
end
def black?
color == 'black'
end
def piece_color_matches_user_color?(user)
if color == 'white' && user.id == game.white_player_id
true
elsif color == 'black' && user.id == game.black_player_id
true
else
false
end
end
# ...
end
This is the Game model:
class Game < ApplicationRecord
has_many :pieces
belongs_to :user
before_save :start_game_when_black_player_is_added
after_create :populate
scope :available, -> { where(state: "pending") }
after_create :current_user_is_white_player
def add_black_player!(player)
self.black_player_id = player.id
self.total_players = 2
save
end
def current_user_is_white_player
self.white_player_id = user_id
end
def populate
(1..8).each do |piece|
pieces.create(x: piece, y: 2, color: 'white', type: 'Pawn')
pieces.create(x: piece, y: 7, color: 'black', type: 'Pawn')
end
["Rook", "Knight", "Bishop", "King", "Queen", "Bishop", "Knight", "Rook"].each.with_index(1) do |klass, index|
pieces.create(x: index, y: 1, color: 'white', type: klass)
pieces.create(x: index, y: 8, color: 'black', type: klass)
end
end
end
This is the User model:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :games
end
This is the Pieces controller:
class PiecesController < ApplicationController
before_action :authenticate_user!
skip_before_action :verify_authenticity_token
def show
#piece = Piece.find_by_id(params[:id])
if #piece.blank?
return render_not_found
end
if #piece.game.white_player_id # white_player_id does not exist to the controller
# returns 404 error
# if #piece.piece_color_matches_user_color?(current_user)
return render plain: "Success"
else
render_not_found
end
# #game = #piece.game
# #pieces = #game.pieces.all
end
def update
#piece = Piece.find_by_id(params[:id])
#game = #piece.game
x_target = piece_params[:x].to_i
y_target = piece_params[:y].to_i
if #piece.attempt_move(x_target, y_target)
#piece.save
else
return render_not_found
end
render plain: "Success"
end
private
def render_not_found(status=:not_found)
render plain: "#{status.to_s.titleize} :(", status: status
end
def piece_params
params.require(:piece).permit(:x, :y)
end
end
This is the schema:
ActiveRecord::Schema.define(version: 20180109032015) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "games", force: :cascade do |t|
t.string "name"
t.boolean "finished"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "total_players"
t.bigint "white_player_id"
t.bigint "black_player_id"
t.string "state", default: "pending", null: false
t.bigint "user_id"
t.bigint "winner_id"
end
create_table "pieces", force: :cascade do |t|
t.bigint "game_id"
t.string "type"
t.integer "x"
t.integer "y"
t.string "color"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "captured", default: false
t.index ["game_id"], name: "index_pieces_on_game_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
This is the error:
PiecesController pieces#show action should return success if the piece color matches the user color
Failure/Error: expect(response).to have_http_status :success
expected the response to have a success status code (2xx) but it was 404
# ./spec/controllers/pieces_controller_spec.rb:48:in `block (3 levels) in <top (required)>'
This is the Controller spec:
RSpec.describe PiecesController, type: :controller do
describe 'pieces#show action' do
it 'should return success if the piece color matches the user color' do
game = FactoryBot.create(:game)
piece = game.pieces.active.find_by({x: 1, y: 2})
sign_in game.user
get :show, params: { id: piece.id }
expect(response).to have_http_status :success
end
end
end
This is the Model spec:
RSpec.describe Piece, type: :model do
describe '#piece_color_matches_user_color?' do
it '#piece_color_matches_user_color? returns true if the piece color and user color match' do
game = FactoryBot.create(:game)
piece = game.pieces.active.find_by({x: 1, y: 2})
user = game.user
result = piece.piece_color_matches_user_color?(user)
expect(result).to eq true
end
end
end
Great and well-described question!
With ActiveRecord, after it queries your database it maps each returned column to a public method inside itself; so there's nothing stopping you from accessing the column directly your controller — or any class.
Having said, you have an after_create callback in your game.rb which is assigning a User#id to your Game#white_player_id, but it's not saving that column. Changing it to before_create :current_user_is_white_player should get you on the right track.
I'm building a store in rails. A store has orders, order_items, users..
Right now when customer adds order_item to a cart, order with unique id is created automatically, and order_items are saved under order_items model. There is also validation saying that only 3 order_items are allowed in one order.
If possible, I would like to remove those order_items that are saved to a model after 5 minutes. 5 minutes counter should start after saving all 3 order_items.
What I did:
I added order_cleaner. to config/initializers/order_cleaner.rb in order to do background process and periodically check the database and remove those saved order_items..
Here is how it looks:
Thread.new do
while true
# Get all orders with at least 3 order_items
orders = Orders.joins(:order_items).group('orders.id').having('count(order_id) >= 3')
orders.each do |o|
# Delete associated order_item if it's older than 5 minutes
o.order_items.each {|oi| oi.destroy! if oi.updated_at < 5.minutes.ago }
end
sleep 1.minute
end
end
I check back after 5 minutes after adding 3 order_items and all order_items are still present. They haven't been removed. What could be the issue and do you have any other solution to achieve this?
Thanks
Relevant code:
class Order < ActiveRecord::Base
belongs_to :order_status
belongs_to :user
has_many :order_items
validates_length_of :order_items, maximum: 3 #only 3 order_items are allowed within an order for each user
before_create :set_order_status
before_save :update_subtotal
def subtotal
order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
end
private
def set_order_status
self.order_status_id = 1
end
def update_subtotal
self[:subtotal] = subtotal
end
end
order_item.rb
class OrderItem < ActiveRecord::Base
belongs_to :product
belongs_to :order
validates_associated :order
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
validate :product_present
validate :order_present
before_save :finalize
def unit_price
if persisted?
self[:unit_price]
else
product.price
end
end
def total_price
unit_price * quantity
end
private
def product_present
if product.nil?
errors.add(:product, "is not valid or is not active.")
end
end
def order_present
if order.nil?
errors.add(:order, "is not a valid order.")
end
end
def finalize
self[:unit_price] = unit_price
self[:total_price] = quantity * self[:unit_price]
end
end
order_items_controller.rb
class OrderItemsController < ApplicationController
def create
#order = current_order
#order_item = #order.order_items.new(order_item_params)
#order.user_id = current_user.id
#order.save
session[:order_id] = #order.id
respond_to do |format|
format.js { flash[:notice] = "ORDER HAS BEEN CREATED." }
end
end
def update
#order = current_order
#order_item = #order.order_items.find(params[:id])
#order_item.update_attributes(order_item_params)
#order_items = #order.order_items
end
def destroy
#order = current_order
#order_item = #order.order_items.find(params[:id])
#order_item.destroy
#order_items = #order.order_items
end
private
def order_item_params
params.require(:order_item).permit(:quantity, :product_id, :user_id)
end
end
schema.rb
create_table "order_items", force: :cascade do |t|
t.integer "product_id"
t.integer "order_id"
t.decimal "unit_price", precision: 12, scale: 3
t.integer "quantity"
t.decimal "total_price", precision: 12, scale: 3
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "has_ordered"
end
create_table "orders", force: :cascade do |t|
t.decimal "subtotal", precision: 12, scale: 3
t.decimal "tax", precision: 12, scale: 3
t.decimal "shipping", precision: 12, scale: 3
t.decimal "total", precision: 12, scale: 3
t.integer "order_status_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "ordered"
t.date "first_item_added_at"
t.date "first_order_added_at"
end
There was a bug in Orders.joins should be Order.joins
Also you could use a gem like https://github.com/plashchynski/crono to schedule jobs, in the way of cron for unix.
I am trying to add items to a cart. I keep getting
Undefined local variable or method document_id' for #<Cart:0x8fc0130>
Rails.root: C:/Users/cmendla/RubymineProjects/technical_library
app/models/cart.rb:22:inadd_item'
app/controllers/carts_controller.rb:7:in `add_to_cart' Request
Parameters:
{"_method"=>"post",
cart.rb
has_many :items
def add_item(product_id)
$test1 = 'add item 2'
item = items.where('document_id = ?', document_id).first
if item
increase the quantity of product in cart
item.quantity + 1
save
else
product does not exist in cart
product = Product.find(product_id)
items << product
document = Document.find(document_id)
items << document
cart.items << Item.new(document_id: document_id)
end
save
end
Application controller:
application_controller.rb
def current_cart
if session[:cart_id]
#current_cart ||= Cart.find(session[:cart_id])
end
if session[:cart_id].nil?
#current_cart = Cart.create!
session[:cart_id] = #current_cart.id
end
#current_cart
end
.
class Item < ActiveRecord::Base
belongs_to :cart
end
carts controller:
def add_to_cart
# #cart = current_cart
current_cart.add_item(params[:document_id])
redirect_to carts_path(current_cart.id)
# redirect to shopping cart or whereever
end
Routes.rb
post '/add_to_cart/:doc_id' => 'carts#add_to_cart', :as => 'add_to_cart'
Schema:
create_table "items", force: :cascade do |t|
t.integer "document_id"
t.string "user"
t.integer "cart_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "items", ["cart_id"], name: "index_items_on_cart_id"
create_table "carts", force: :cascade do |t|
t.string "user"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I am calling the add action with the following link in my documents.index
<td><%= link_to "add", add_to_cart_path(document.id), :method => :post %></td>
item = items.where('document_id = ?', document_id).first
On that line document_id is not defined. should that actually be product_id?
I'm trying to create many absents at once (for different students) and I'm pretty new to rails. I keep getting an error that "First argument in form cannot contain nil or be empty".
I'm also wondering whether I need a new method at all.
Here is my form
= form_for #absent, url: absent_path, method: :post do |f|
table.table
thead
tr
th Name
th Absent
tbody
- #students.each do |student|
tr
td = student.full_name
td = f.check_box :student_ids, student.id, #absent.students.include?(student), name: "absent[student_ids][]", id: "student_id_#{student.id}"
= f.submit 'Save'
Here is my controller
def new
#absent = Absent.new
end
def create
absent_params[:student_ids].each do |student_id|
#absent = Absent.new(student_id: student_id)
if #absent.save
redirect_to absent_path
else
render :index
end
end
end
private
def absent_params
params.require(:absent).permit(
:student_ids
)
end
Here is my schema:
create_table "absents", force: true do |t|
t.integer "student_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "absents", ["student_id"], name: "index_absents_on_student_id", using: :btree
create_table "students", force: true do |t|
t.string "full_name"
t.datetime "created_at"
t.datetime "updated_at"
end
Here are my models:
class Student < ActiveRecord::Base
has_many :absents, foreign_key: :student_id
end
class Absent < ActiveRecord::Base
belongs_to :student, foreign_key: :student_id
end