How to schedule Posts in Rails 6 - ruby-on-rails

devs i'm trying to schedule some posts in my blog module, its a simple scaffold posts, this is my schema.rb
create_table "posts", force: :cascade do |t|
t.string "titulo"
t.string "descripcion"
t.datetime "inicio_publicacion"
t.datetime "fin_publicacion"
t.boolean "ver_proveedores"
t.boolean "ver_clientes"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "published", default: true
end
I Put that published attribute in my model for a reason, to evaluate if the user when entering the initial publication date is equal to the current one, the value of that field should be true so that once I have that I can put another logic in my view to a tag that says "published" or not "unpublished" depending on that value, but I see that it is not working or maybe I am confused and there is another way to do it.
I also thought about placing an accessor method :published but the values ​​it returns are "nil".
attr_accessor :published
before_save :publicaciones
def publicaciones
if self.inicio_publicacion == Date.today
self.published == true
else
self.published == false
end
end
end

inicio_publicacion has a datetime type. It is literraly "day" and "time".
If you want to ensure that something happened today, it should be
inicio_publicacion.to_date == Date.today
or even easier
inicio_publicacion.today?

Double equals is comparison, single equals to set.
def publicaciones
if self.inicio_publicacion == Date.today
self.published = true
else
self.published = false
end
end
But it can also be written much simpler:
def publicaciones
published = inicio_publicacion.today?
end

Related

Rails Custom Validator

I have a custom rails validator below that checks whether a new booking is available within the existing bookings based on the start and end date. I'm struggling to figure how I can add a separate 'room' validation. Each booking is for a room, so I want to make sure that the date range is only for the specified room and not for ALL bookings, just the bookings for the individual room. Any thoughts?
validates :start_date, :end_date, :presence => true, availability: true
class AvailabilityValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
bookings = Booking.where(void:'f')
date_ranges = bookings.map { |b| b.start_date..b.end_date }
date_ranges.each do |range|
if range.include? value
record.errors.add(attribute, "not available")
end
end
end
end
EDIT:
Following up on the schema. Fairly simple, all bookings are made by parent associated users ("user_id"), and room is simply another integer attribute within the record (there are 10 rooms).
create_table "bookings", force: :cascade do |t|
t.datetime "start_date"
t.datetime "end_date"
t.boolean "void", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.integer "room"
t.index ["user_id"], name: "index_bookings_on_user_id"
end

Why does setting a default value for an existing column in an ActiveRecord Migration not extend to existing associations on production?

If I add a default value to an existing column through an ActiveRecord Migration, when deploying my changes to production, existing associations are not affected.
I can drop to a rails production console and iterate over every single record and set the value on the new column to false on each record however it's tedious and doesn't scale well.
class AddDefaultValuesToAFewColumns < ActiveRecord::Migration[5.2]
def change
change_column :downloads, :is_deleted, :boolean, :default => false
end
end
create_table "downloads", force: :cascade do |t|
t.string "version"
t.string "comment"
t.string "contributors"
t.string "release_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "download_url"
t.boolean "is_deleted", default: false
end
The expected result would be for associations when queried from the rails console to return false for is_deleted, rather it returns nil. Why is this and what alternative solutions are there?
That's how it works. When you change the column default value, you are configuring the default value for new records, not existing ones. If you want to update existing values with false then do something like Download.where(is_deleted: nil).update_all(is_deleted: false) right after the change_column line:
class AddDefaultValuesToAFewColumns < ActiveRecord::Migration[5.2]
def change
change_column :downloads, :is_deleted, :boolean, :default => false
Download.where(is_deleted: nil).update_all(is_deleted: false)
end
end

Search if vehicle is available between dates

I've created a application where a user can reserve a vehicle (minibus) I've got the application working where a user can reserve a vehicle but the vehicle cannot be double booked. I've got a simple search working which allows the user to search for make or model. I'm looking to expand this search so a user can search for all vehicles which are available for a date the user wants to book. I'm not too sure how to approach this can someone please help!.
I've got a Vehicle table, User table and a Reservation table.
my schema look like (I've only included the tables concerned):
create_table "reservations", force: :cascade do |t|
t.date "startDate"
t.date "endDate"
t.integer "noOfDays"
t.integer "costs"
t.integer "vehicle_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_reservations_on_user_id"
t.index ["vehicle_id"], name: "index_reservations_on_vehicle_id"
end
create_table "users", force: :cascade do |t|
t.string "address"
t.string "city"
t.date "dateofbirth"
t.string "email"
t.string "fname"
t.string "postcode"
t.string "sname"
t.string "user_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "contact_no"
end
create_table "vehicles", force: :cascade do |t|
t.string "description"
t.integer "door"
t.string "fuel"
t.string "img"
t.string "make"
t.string "model"
t.date "motEnd"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.date "motDue"
t.date "motStart"
t.integer "price"
t.string "reg"
t.integer "seats"
t.string "transmission"
t.date "year"
t.decimal "engine"
end
I've already got the overlap booking working heres a like to the solution I used: Avoid double bookings for a minibus reservation system
I'm trying to expand my search so the user can search between two dates on the index page for all available vehicles during that time. I just don't know how I'd get this to work so the system shows available vehicles for that date.
My current simple search:
Vehicles controller:
def vehicle_search
vehicles = Vehicle.fuzzy_search(params[:search_string])
#vehicles = Kaminari.paginate_array(vehicles.order :make).page(params[:page]).per(3)
if #vehicles.empty?
flash.now[:alert] = "No records found - displaying all records ..."
#vehicles = Vehicle.order(:make).page(params[:page]).per(3)
end
render :action => "index"
end
vehicle.rb
def self.fuzzy_search(search_string)
search_string ||= ""
make = search_string
model = search_string
price = search_string
fuel = search_string
transmission = search_string
self.where("make LIKE ? OR model LIKE ? OR price LIKE ? OR fuel LIKE ? OR transmission LIKE ?", make, model, price, fuel, transmission)
end
Search partials
<%= form_tag my_path, :method => 'post', :multipart => true do %>
<!--vehicle details-->
<%= text_field_tag :search_string, "", class: 'form-control custom2' %>
<%= submit_tag 'Search', class: "btn btn-default" %>
<% end %>
vehicles/index.html.erb
<a>Make, Model, Fuel Type, Transmission: </a>
<%= render(:partial=>'/vehicle_search',:locals=> { :my_path => "/vehicles/vehicle_search" })%>
I will be working with the following logic to determine a time overlap:
(StartA <= EndB) and (EndA >= StartB)
If the above is true the periods overlap. You can check this out in the Stack Overflow question: Determine Whether Two Date Ranges Overlap
So with that out of the way let's create two scopes on Reservation. One to get overlapping reservations that overlap with one point in time, and one to get overlapping reservations that overlap with a time window.
class Reservation < ApplicationRecord
# code ...
# assuming Reservation has start_time and end_time attributes
# and you validate that start_time is always before end_time
# time param can also be a date or datetime
def self.overlap(time)
# Reservation start_time is less than or equal to the given time
# and reservation end_time is greater than or equal to the given
# time.
where(arel_table[:start_time].lteq(time))
.where(arel_table[:end_time].gteq(time))
end
def self.overlap_window(start_time, end_time)
# Reservation start_time is less than or equal to the given
# end_time and reservation end_time is greater than or equal
# to the given start_time.
where(arel_table[:start_time].lteq(end_time))
.where(arel_table[:end_time].gteq(start_time))
end
# code ...
end
Now you can fetch the vehicles quite easy by checking which vehicles are not occupied in for example a certain time window.
Vehicle.where.not(id: Reservation.select(:vehicle_id).overlap_window(Time.now, 4.hours.from_now))
Note: Start time must always be before the end time (for both the reservation and the time window) for this to work as explained (mainly in the comments) in the date overlap question linked at the start.

I wanna use model attribute in view Rails

I am trying to use attribute value in model but its not working..
I have three models:
models/resident.rb
class Resident < ActiveRecord::Base
belongs_to :hostel
has_one :user,dependent: :delete
end
models/user.rb
class User < ActiveRecord::Base
belongs_to:resident
end
models/hostel.rb
class Hostel < ActiveRecord::Base
has_many :residents
has_one :rate_card,dependent: :delete
end
Schema
Resident
create_table "residents", force: :cascade do |t|
t.string "room_number"
t.string "roll_number"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "hostel_id"
end
User
create_table "users", force: :cascade do |t|
t.string "roll_number"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "resident_id"
end
Hostel
create_table "hostels", force: :cascade do |t|
t.string "hostel"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Now i want to use the hostel attribute value in users/show.html.erb
I am able to do this :
<%if #user.resident.roll_number=="101303110"%>
if the roll number is present then returning true..
but if is use :
<%if #user.resident.hostel=="J"%>
and if J is a hostel present in Hostel model then it is returning false.
But when we put<%#user.resident.hostel%> in show.html.erb then it is showing value J.
How should I use related models attributes in each other view?
Given your associations, #user.resident.hostel would load a hostel. But you want to compare the hostel string on the hostel. Therefore your comparison should be:
<% if #user.resident.hostel.hostel == 'J' %>
Explanation:
#user # returns your a user
#user.resident # follows `belongs_to :resident` and
# returns a resident
#user.resident.hostel # follows `belongs_to :hostel` on the resident and
# returns a hostel
#user.resident.hostel.hostel # returns the value store in the `hostel`
# column of that `hostel`
Btw. I would argue that chaining calls like that violates the Law of Demeter. But it is hard to suggest any alternative without having more insights into your app.

ActiveRecord - How to know if a field exists on another table?

I want to update the status in the winners table into trues if a user with same mail id is signed up. I'm stuck with active record.
schema.rb
create_table "winners", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "phonenumber"
t.boolean "status", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
table 2
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "nickname"
t.string "image"
t.integer "score", default: 0
end
model/user.rb
after_create :update_status
def update_status
#if self[:email] = #any mail in winners table
#update the corresponding winners row with status.
if Winners.where(email = :email)
update attributes
end
How to make such queries with active record?
If you want to do it with an after_create hook in your model/user.rb:
after_create :update_status
def update_status
Winner.where(:email => self.email).each do |winner|
winner.status = true
winner.save
end
end
You might argue updating winners based on a user signing up should be a controller task, not the models though. I am a friend of explicitly calling stuff in your controller, instead of letting it happen automagically. That's more a thing of personal taste though, this should be perfectly fine.
You can try this, I hope this will help you.
In model/user.rb
after_create :update_status
def update_status
#update the corresponding winners row with status.
winner = Winner.where("email = ?", self.email).first
unless winner.blank
winner.status = true # Or false
winner.save! # Or If You want to skip validation then use winner.save(validate: false)
end
end

Resources