diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index d4087f7..31d3872 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,40 +1,67 @@ // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. // // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details // about supported directives. // //= require jquery //= require jquery_ujs //= require bootstrap.min //= require_tree . //= require highcharts $(document).ready(function() { $(".include_files-plus-sign").click(function() { addIncludedFileTextArea(); }); $(".exclude_directions-plus-sign").click(function() { addExcludeDirectionsTextArea(); }); + $(".schedule_run_form_plus").click(function() { + addScheduleRun(); + }); + $(".schedule_run_form_remove").click(function() { + removeScheduleRun(); + }); }); function addIncludedFileTextArea() { var textArrea = $('.include_files:last').clone(true).val(""); $('.include_files-plus-sign:first').parent().remove(); textArrea.insertAfter('.include_files:last'); $('.include_files:last input').val(""); } function addExcludeDirectionsTextArea() { var textArrea = $('.exclude_directions:last').clone(true).val(""); $('.exclude_directions-plus-sign:first').parent().remove(); textArrea.insertAfter('.exclude_directions:last'); $('.exclude_directions:last input').val(""); } + +function addScheduleRun() { + var scheduleRun = $('.schedule_run_form:last').clone(); + scheduleRun.insertAfter('.schedule_run_form:last'); + $('.schedule_run_form:last input').val(''); + if ($('.schedule_run_form').size() > 1) { + $(".schedule_run_form_remove").show(); + }; +} + +function removeScheduleRun() { + if ($('.schedule_run_form').size() > 1) { + $('.schedule_run_form:last').remove(); + if ($('.schedule_run_form').size() == 1) { + $(".schedule_run_form_remove").hide(); + }; + } + else { + alert('nothing to remove'); + }; +} diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index c5ef177..4824a49 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -1,51 +1,52 @@ class SchedulesController < ApplicationController before_action :fetch_host, only: [:new, :create] before_action :fetch_job_id, only: [:new, :create] def new @schedule = @host.schedules.new end def show end def edit end def update end def create @schedule = @host.schedules.new(fetch_params) @schedule.runtime = params[:schedule][:runtime] if params[:schedule][:runtime] if @schedule.save flash[:success] = 'Schedule created successfully' if @job_id.present? redirect_to edit_host_job_path(@host, @job_id, schedule_id: @schedule.id) else redirect_to new_host_job_path(@host, schedule_id: @schedule.id) end else render :new end end def destroy end private def fetch_host @host = current_user.hosts.find(params[:host_id]) end def fetch_job_id @job_id = @host.job_templates.find(params[:job_id]).id if params[:job_id].present? end def fetch_params - params.require(:schedule).permit(:name) + params.require(:schedule). + permit(:name, { schedule_runs_attributes: [[:level, :month, :day, :time]] }) end end diff --git a/app/models/schedule.rb b/app/models/schedule.rb index a026cee..ccad7ab 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -1,45 +1,22 @@ class Schedule < ActiveRecord::Base - DEFAULT_RUNS = [ - 'Level=Full 1st sun at ', - 'Level=Differential 2nd-5th sun at ', - 'Level=Incremental mon-sat at ' - ] - - attr_accessor :runtime - - serialize :runs, JSON + has_many :schedule_runs belongs_to :host - validates :name, :runs, presence: true + validates :name, presence: true validates :name, uniqueness: { scope: :host } validates_with NameValidator - before_validation :set_runs, if: Proc.new { |s| s.runtime.present? } + accepts_nested_attributes_for :schedule_runs def to_bacula_config_array ['Schedule {'] + [" Name = \"#{name_for_config}\""] + - runs.map {|r| " Run = #{r}" } + + schedule_runs.map {|r| " Run = #{r.schedule_line}" } + ['}'] end def name_for_config [host.name, name].join(' ') end - - private - - def set_runs - if valid_runtime? - self.runs = DEFAULT_RUNS.map { |r| r + runtime } - else - self.errors.add(:runtime, :not_valid_24h_time) - false - end - end - - def valid_runtime? - runtime && runtime[/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/] - end end diff --git a/app/views/schedules/_form.html.erb b/app/views/schedules/_form.html.erb index 4ebd63e..f53d299 100644 --- a/app/views/schedules/_form.html.erb +++ b/app/views/schedules/_form.html.erb @@ -1,27 +1,49 @@ <%= bootstrap_form_for(@schedule, url: host_schedules_path(@host), layout: :horizontal, label_col: 'col-xs-3', control_col: 'col-xs-8') do |f| %> - <% if @schedule.errors.any? %> -
-

<%= pluralize(@schedule.errors.count, "error") %> prohibited this schedule from being saved:

+ <%= f.text_field :name, required: true %> +
+ <% if ['schedule_runs.day','schedule_runs.month','scheduled_runs.time'] - @schedule.errors.keys %> +
<% end %> -
- <%= f.text_field :name %> - <%= f.time_field :runtime, placeholder: 'HH:MM' %> - <%= (hidden_field_tag :job_id, @job_id) if @job_id%> + <%= f.fields_for :schedule_runs_attributes, index: nil do |r| %> +
+ <%= r.select :level, options_for_select(ScheduleRun.levels.keys) %> + <%= r.text_field :month, placeholder: '[month | month-range]' %> +

eg: jan-mar, feb, monthly

+ <%= r.text_field :day, placeholder: '[week | week-range] day | day-range', + required: true %> +

+eg: first sun, second-fifth mon, mon-sat +

+ <%= r.time_field :time, placeholder: 'HH:MM', required: true %> +
+
+ <% end %> + +
+ <%= link_to '#', class: 'schedule_run_form_remove', style: 'display:none;' do %> + + <% end %> +
+
+ <%= link_to '#', class: 'schedule_run_form_plus' do %> + + <% end %>
+ <%= (hidden_field_tag :job_id, @job_id) if @job_id%>
<%= f.submit class: 'btn btn-success' %>
<% end %> diff --git a/spec/controllers/schedules_controller_spec.rb b/spec/controllers/schedules_controller_spec.rb index 9a1e4a9..cc4d7f7 100644 --- a/spec/controllers/schedules_controller_spec.rb +++ b/spec/controllers/schedules_controller_spec.rb @@ -1,95 +1,95 @@ require 'spec_helper' describe SchedulesController do let(:host) { FactoryGirl.create(:host) } let(:user) { FactoryGirl.create(:user) } before do host.users << user controller.stub(:current_user) { user } end describe 'GET #new' do before { get :new, host_id: host.id } it 'initializes a schedule' do expect(assigns(:schedule)).to be end it 'sets the schedule\'s host' do expect(assigns(:schedule).host).to eq(host) end it 'renders' do expect(response).to render_template(:new) end end describe 'POST #create' do context 'with valid params' do let(:params) do { host_id: host.id, - schedule: { name: FactoryGirl.build(:schedule).name, runtime: '19:17' } + schedule: { name: FactoryGirl.build(:schedule).name } } end it 'creates the schedule' do expect { post :create, params }. to change { host.schedules(true).count }.by(1) end it 'redirects to a new job form' do post :create, params expect(response).to redirect_to(new_host_job_path(host, schedule_id: Schedule.last.id)) end context 'and an existing job' do let(:job) { FactoryGirl.create(:job_template, host: host) } it 'redirects to the job\'s edit form' do post :create, params.merge(job_id: job.id) expect(response). to redirect_to(edit_host_job_path(host, job, schedule_id: Schedule.last.id)) end end end context 'with invalid host' do it 'raises not found error' do expect { post :create, { host_id: -1, schedule: { invalid: true } } }.to raise_error(ActiveRecord::RecordNotFound) end end context 'with invalid params' do let(:params) do { host_id: host.id, schedule: { invalide: :foo } } end it 'initializes a schedule with errors' do post :create, params expect(assigns(:schedule)).to be end it 'does not create the schedule' do expect { post :create, params }. to_not change { Schedule.count } end it 'renders :new' do post :create, params expect(response).to render_template(:new) end it 'assigns the host to schedule' do post :create, params expect(assigns(:schedule).host).to eq(host) end end end end diff --git a/spec/factories/schedule.rb b/spec/factories/schedule.rb index f4c49a3..cc83ac3 100644 --- a/spec/factories/schedule.rb +++ b/spec/factories/schedule.rb @@ -1,9 +1,6 @@ FactoryGirl.define do factory :schedule do host sequence(:name) { |n| "Schedule #{n}" } - runs ['Level=Full 1st sun at 2:05', - 'Level=Differential 2nd-5th sun at 2:05', - 'Level=Incremental mon-sat at 2:05'] end end diff --git a/spec/factories/schedule_run.rb b/spec/factories/schedule_run.rb new file mode 100644 index 0000000..375bdc5 --- /dev/null +++ b/spec/factories/schedule_run.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :schedule_run do + schedule + level 0 + day 'first sun' + time '12:34' + end +end diff --git a/spec/models/schedule_spec.rb b/spec/models/schedule_spec.rb index 3725d4a..dd92bc0 100644 --- a/spec/models/schedule_spec.rb +++ b/spec/models/schedule_spec.rb @@ -1,58 +1,46 @@ require 'spec_helper' describe Schedule do context 'validates' do it 'presence of name' do expect(Schedule.new).to have(2).errors_on(:name) end - it 'presence of runs' do - expect(Schedule.new).to have(1).errors_on(:runs) - end - context 'schedule name is unique in the host\'s scope' do let!(:schedule_1) { FactoryGirl.create(:schedule, name: 'Schedule_1') } let(:schedule_2) { FactoryGirl.build(:schedule, name: 'Schedule_1') } let(:schedule_3) { FactoryGirl.build(:schedule, name: 'Schedule_1', host: schedule_1.host) } it 'two schedules of diferent hosts can have the same name' do expect(schedule_2).to be_valid end it 'two schedules of the same host can NOT have the same name' do expect(schedule_3).to_not be_valid end end end describe '#to_bacula_config_array' do - let(:runs) do - [ - 'Full 1st sun at 23:05', - 'Differential 2nd-5th sun at 23:50', - 'Incremental mon-sat at 23:50' - ] - end - let(:schedule) do - FactoryGirl.create(:schedule, name: 'Test Schedule', runs: runs) + FactoryGirl.create(:schedule, name: 'Test Schedule') end + let!(:schedule_run) { FactoryGirl.create(:schedule_run, schedule: schedule) } + subject { schedule.to_bacula_config_array } it 'is a schedule type resource' do expect(subject.first).to eq('Schedule {') expect(subject.last).to eq('}') end it 'contains the name' do expect(subject).to include(" Name = \"#{[schedule.host.name, schedule.name].join(' ')}\"") end it 'contains the runs' do - runs.each do |r| - expect(subject).to include(" Run = #{r}") - end + expect(subject).to include(" Run = #{schedule.schedule_runs.first.schedule_line}") end end end