Search code examples
ruby-on-railsruby-on-rails-4rangedestroy

automatically add or delete an array of dates in a given range in Rails


I am breaking my head on this problem. I am making an appointment app that holds a date range in one model (Project) and holds the available dates in another model (ProjectDate). When a user creates a Project, he inputs a :start_date and :end_date dates to define the range of dates. The associated project_dates are created after the Project record is saved.

That all works fine.

What I can't get to work, is automatically creating or deleting ProjectDate records when the date range changes on the Project record.

For instance, if I changed a range from ( Jan.10 - Jan. 15) to (Jan. 8 - Jan.13) I would need to automatically create ProjectDate records for Jan.8 and Jan 9 and automatically remove records for Jan. 15 and Jan. 14.

The way I am trying to do this is to store the the original start_date an end_date in hidden fields to call them later in the controller...Below is my code...any help would be appreciated

Models

  class Project < ActiveRecord::Base
   has_many :project_dates, dependent: :destroy, inverse_of: :project
   accepts_nested_attributes_for :project_dates, allow_destroy: true

  class ProjectDates < ActiveRecord::Base
    belongs_to :project, inverse_of: :project_dates

Table Structures

Project table structure
  :id 
  :title (string)
  :start_date (date)
  :end_date(date)
  :status (boolean)

ProjectDates table structure
  :id 
  :project_id (integer)
  :schedule_date (date)
  :available (boolean)

form

  <%= form_for @project, url: update_date_range_path(:p => @project.id) do |f| %>  

  <%= f.hidden_field :org_start, :value => @project.start_date %>
  <%= f.hidden_field :org_end, :value => @project.end_date %>
  <%=  f.text_field  :end_date %>
  <%=  f.text_field  :start_date%>

  <%= f.submit 'SAVE CHANGES' %>       

  <% end %>

Controller

def edit_date_range 
 @project = Project.find(params[:p])
end

def change_start_add 
 start_range.each do |changed_date|
   @project.project_dates.create!(schedule_date: changed_date, available: true)
 end
end


def update_date_range 
 @project = Project.find(params[:p])
 if @project.update_attributes
   if params[:project][:start_date] <  params[:project][:org_start] 
    start_range = params[:project][:org_start].to_date .. params[:project][:start_date].to_date
    :change_start_add 
   else
    start_range = params[:project][:start_date].to_date .. params[:project][:org_start].to_date 
    :change_start_remove
   end
  redirect_to  k1s3_path(:p => @project.id, :t => @project.template_id, :prt => @role.id)
 else
  redirect_to  k1_edit_date_range_path(:p => @project.id, :t => @project.template_id, :prt => @role.id)
 end
end

UPDATE: The answer below answered my question...but I thought I'd post my finished code if it helps anyone:

model

 after_update :add_and_remove_dates
def dates_in_date_range
  (self.start_date.to_date .. self.end_date.to_date)
end

def add_and_remove_dates
  dates_in_date_range.each do |date|
  ProjectDate.find_or_create_by(schedule_date: date, available: true)
  ProjectDate.where('schedule_date < ? OR schedule_date > ?', start_date, end_date).destroy_all

  end  
end

controller

def update_date_range 
 @project = Project.find(params[:p])
   if @project.update_attributes(project_params)
    redirect_to  k1s3_path(:p => @project.id)
  else
   redirect_to  edit_date_range_path(:p => @project.id)
 end
end

Solution

  • You have :change_start_add which is a symbol, not a function. Try:

    if params[:project][:start_date] <  params[:project][:org_start] 
      start_range = params[:project][:org_start].to_date .. params[:project][:start_date].to_date
      change_start_add 
    else
      start_range = params[:project][:start_date].to_date .. params[:project][:org_start].to_date 
      change_start_remove
    end
    

    This might work better as a model callback.

    project.rb

    after_update :add_and_remove_dates
    
    def add_and_remove_dates
      dates.where('schedule_date < ? OR schedule_date > ?', start_date, end_date).destroy_all
      dates_in_date_range.each do |date|
        dates.find_or_create_by(schedule_date: date, available: true)
      end  
    end
    
    def dates_in_date_range
      # returns an array of all the dates in the date range.
    end