Reputation: 2609
Error: undefined method 'before_save' for YearsController:Class Did you mean? before_action
https://guides.rubyonrails.org/active_record_callbacks.html talks about "objects may be created, updated, and destroyed" and I want to interrupt the process if the image being added via ActiveStorage already exists to avoid duplicates. before_save
is one of the available callbacks.
rails (6.0.0). I'm not experienced with ActiveRecord callbacks but the https://api.rubyonrails.org Active Record callbacks lists before_save
, but not before_action
. But no error with before_action.
I'm not even sure when I need to activate the callback, but am working my way through what works. What callbacks are allowed? And not sure what before_action
would be.
years_controller.rb
class YearsController < ApplicationController
helper_method :sort_column, :sort_direction
before_action :set_year, only: [:show, :edit, :update, :destroy] # 2019.06.20 Can't see that this is being used anywhere
before_action :set_s3_direct_post, only: [:new, :edit, :create, :update]
# before_action :dup_check, only: [:new, :edit, :update] # runs, but not what I think I want
before_save :dup_check, only: [:new, :edit, :update] # undefined method `before_save' for YearsController:Class Did you mean? before_action
PAGE_SIZE = 10
def index
@years = Year.order(sort_column + " " + sort_direction)
respond_to do |format|
format.html {}
format.json { render json: @years }
end
end
def documents
@years = Year.all
end
def map_one # mapping one connection/year
# need to use the year passed in and then make it for a year
years = Year.where( year_date: '1865-01-01' .. '1995-12-31' )
end
def search
# Copeland except within the else
@page = (params[:page] || 0).to_i
if params[:keywords].present?
@keywords = params[:keywords]
year_search_term = YearSearchTerm.new(@keywords)
@years = Year.where(
year_search_term.where_clause,
year_search_term.where_args).
order(year_search_term.order).
offset(PAGE_SIZE * @page).limit(PAGE_SIZE)
else
@years = Year.order(sort_column + " " + sort_direction)
end
respond_to do |format|
format.html {}
format.json { render json: @years }
end
end
def summary
@years = Year.order(:year_date)
respond_to do |format|
format.html {}
format.json { render json: @years }
end
end
def show
end
# GET /years/new
def new
# All from Tutorial Points Tutorial
@year = Year.new({:year_date => "1900-09-01"}) # This is default. Helps to change when going through a group.
@locations = Location.all
@people = Person.all
end
# GET /years/1/edit
def edit
@positions = Position.all # Needed for Positions/ Titles list to work, However not added to database
end
# POST /years
# POST /years.json
def create
@year = Year.new(year_params)
respond_to do |format|
if @year.save
format.html { redirect_to @year, notice: 'Connection was successfully created.' }
format.json { render :show, status: :created, location: @year }
else
format.html { render :new }
format.json { render json: @year.errors, status: :unprocessable_entity }
end
end
repopulateResidResto()
end
# PATCH/PUT /years/1
# PATCH/PUT /years/1.json
def update
respond_to do |format|
if @year.update(year_params)
format.html { redirect_to @year, notice: 'Connection was successfully updated.' }
format.json { render :show, status: :ok, location: @year }
else
format.html { render :edit }
format.json { render json: @year.errors, status: :unprocessable_entity }
end
end
repopulateResidResto() # nice to have some error handling here
end
# DELETE /years/1
# DELETE /years/1.json
def destroy
@year.destroy
respond_to do |format|
format.html { redirect_to years_url, notice: 'Connection was successfully destroyed.' }
format.json { head :no_content }
end
repopulateResidResto()
end
private
# Use callbacks to share common setup or constraints between actions.
def set_year
@year = Year.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def year_params
params.require(:year).permit(:year_date, :person_id, :location_id, :resto, :resto_name, :resid, :title, :source, :source_url, :ref_image, :doc_image , :ref_url, :notes, :ref_url, :caption, documents: []) # document replaces _images, brackets for has many
end
def set_s3_direct_post
@s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
end
def dup_check
# if active_storage_blobs(:checksum ) == an existing blob
# let's first run a test seeing if a know checksum exists. That works. Now get the existing checksums
test_checksum = '0M4nc4nuUaVuqo3+sJw+Lg==' # will get for selected image at some point
test_checksum == '0M4nc4nuUaVuqo3+sJw+Lg==' ? (puts "years_controller:158. test_checksum #{test_checksum} does exist") : (puts "years_controller:158. test_checksum #{test_checksum} does not exist") # Need the ()
puts "years_controller:160. dup_check entered via before_action. before_save results in an errro"
end
# NOT CLEAR ANY OF THIS IS NEEDED. BUT PUT IT IN AGAIN WHEN CREATE DIDN'T WORK
def person_params
params.require(:person).permit(:last_name, :given_name, :full_name, :full_name_id)
end
def sort_column
Year.column_names.include?(params[:sort]) ? params[:sort] : "year_date" # it does matter what this last field is.
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
end
end
year.rb
class Year < ApplicationRecord
belongs_to :location
belongs_to :person
has_many :resto_resid_lines
has_many_attached :documents # ActiveStorage
has_rich_text :caption # ActionText 2019.12.05 not implemented because form needs major changes to work
scope :with_eager_loaded_documents, -> { eager_load(documents_attachments: :blob) }
default_scope -> { order(:year_date) }
validates :person_id, presence: true
validates :location_id, presence: true
validates_presence_of :resto, {:if => :resto_name?, message: 'Click on Restaurant (above) since a restaurant name has been selected' }
validates :resto, :presence => { :if => :resto_name?, message: 'Select Restaurant since a restaurant name has been specified' } # is this the same as above, but not working?
def self.search(search)
where("year_date ILIKE ? OR resto ILIKE ? OR resto_name ILIKE ? OR resid ILIKE ? OR title ILIKE ? OR source ILIKE ? OR source_url ILIKE ? OR ref_url ILIKE ? OR notes ?", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%")
end
def map_popup
"#{person.given_name} #{person.last_name} was a #{title} at #{location.address} in #{year_date.to_formatted_s(:month_year)}"
end
def next
Year.where("id > ? AND year_date = ?", id, year_date).first
end
def previous
Year.where("id < ? AND year_date = ?", id, year_date).last
end
private
def resid_xor_resid
if [resto, resid].compact.count != 1
errors.add(:base, "Select Residence or Restaurant, but not both")
end
end
end
Lots of cruft because this is my first app and I've been working at it for years.
Upvotes: 0
Views: 1025
Reputation: 802
Good thing you said you will try to research yourself before coming back.
What you are missing is that some callbacks are specific to the type of class you are calling them into. I hope it is obvious to you by now that save is something particular to model and controller has nothing to with it (@record.save
, remember?). On the other hand, before_action
and after_action
are the ones used in controllers as controllers have actions. Reference: Ruby-on-Rails guides
Solution:
class Year < ApplicationRecord
before_save :is_duplicate?
private
def is_duplicate
return Year.where([the_attributes_that_make_it_duplicate]).present? # or something like that :P
# throw :abort or return false (I guess you can't through in this case)
end
end
class YearsController < ApplicationController
before_action :dup_check
private
def dup_check
set_year
redirect_to your_desired_path if Year.where([the_attributes_that_make_it_duplicate]).present?
end
end
Upvotes: 0