As a new to Ruby on rails, I stumble on a part of my app. I read the basics of RoR framework and know the 'convention over configuration' feature of rails MVC. I have two tables, one is apps_events and another is apps_events_attributes. The id of the first one is the foreign key of the second and in has many relationship. The app_events table has a field of foreign key attribute 'app_id', so selecting on a particular app I will be redirected to its events and attributes. There is also a field called 'is_standard' which actually distinguish the event type whether it's a Standard or Custom event.
Now I have to render those events and its attributes of a particular app in two different tab on the view layer with it's attributes using nested_form_for feature. User can toggle to Standard and Custom event through this tab click. Can anyone suggest me how will I achieve the same and can show me the ideal flow of this scenario (model name and checking part of 'is_standard', propagate the same in controller and render to the view)?
By the way, can I use different controller over the same model and if I do the same then is it capable of doing the same CRUD functionality for different Event and its attributes?
I have all it done alone and it is not very hard what I think at first, all trick is done by JQuery... and share the concept if somebody got same problem as me
My Models are
class AppsEvent < ActiveRecord::Base
belongs_to :app
has_many :apps_events_attributes, :foreign_key => 'apps_event_id',
:class_name => 'AppsEventsAttribute',
:dependent => :destroy
accepts_nested_attributes_for :apps_events_attributes,
:reject_if => lambda { |a| (a[:name].blank? && a[:description].blank?) },
:allow_destroy => true
validates_presence_of :description
validates_presence_of :name
validates_presence_of :code
validates_presence_of :apps_events_attributes, :message => "can't be empty"
end
and
class AppsEventsAttribute < ActiveRecord::Base
set_table_name :apps_events_attributes
belongs_to :apps_event, :foreign_key => 'apps_event_id'
attr_accessible :id, :apps_event_id, :name, :description, :attribute_type, :is_std, :created_at, :updated_at
def type
self.attribute_type
end
end
and My Controller is...
class AppsEventsController < ApplicationController
layout :layout_by_resource
before_filter :initialize_default_app
before_filter :check_permission
before_filter :load
def load
#app = current_user.find_app(params[:app_id])
#apps_events = AppsEvent.where(:app_id => #app.id)
#apps_event = AppsEvent.new
end
def index
#app = current_user.find_app(params[:app_id])
#default_app = App.default(current_user)
#apps_events = AppsEvent.where(:app_id => #app.id)
#apps_event_jsons = Hash.new
#apps_events.each do |app_event|
json = Hash.new
json['User_ID'] = 548741213
json['Session_ID'] = 2568639390
json['Action_Type'] = app_event.code
json['eventsData'] = {}
app_event.apps_events_attributes.each do |apps_event_attributes|
if (apps_event_attributes.attribute_type == 'Integer')
json['eventsData'][apps_event_attributes.name] = 1234
elsif (apps_event_attributes.attribute_type == 'Float')
json['eventsData'][apps_event_attributes.name] = 1234.23
else
json['eventsData'][apps_event_attributes.name] = 'abcd'
end
end
#apps_event_jsons[app_event.id] = json
end
end
def new
#apps_event = AppsEvent.new
#app = current_user.find_app(params[:app_id])
#apps_event.app_id = #app.id
#apps_event.apps_events_attributes.build
#action = 'create'
render 'edit'
end
def edit
#app = current_user.find_app(params[:app_id])
#apps_event = AppsEvent.find(params[:id])
#action = 'update'
end
def create
#apps_event = AppsEvent.new(params[:apps_event])
#show_custom_event = 'true'
#apps_event.name = #apps_event.name.strip
respond_to do |format|
if #apps_event.save
format.html {
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully created #{#apps_event.name} custom definition.")
}
format.js {
flash[:notice] = 'Successfully created event.'
#apps_events = AppsEvent.where(:app_id => #app.id)
}
else
#app = current_user.find_app(params[:app_id])
if (#apps_event.apps_events_attributes == nil || #apps_event.apps_events_attributes.size <= 0)
#apps_event.apps_events_attributes.build
end
#apps_event.populate_code
#action = 'create'
format.html {
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in creating #{#apps_event.name} custom definition.")
}
format.js
end
end
end
def update
#apps_event = AppsEvent.find(params[:apps_event][:id])
params[:apps_event][:name] = params[:apps_event][:name].strip
respond_to do |format|
if #apps_event.update_attributes(params[:apps_event])
format.html {
if(#apps_event.is_std == 'y')
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully updated #{#apps_event.name} standard definition.")
else
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully updated #{#apps_event.name} custom definition.")
end
}
format.js {
flash[:notice] = 'Successfully updated event.'
#apps_events = AppsEvent.where(:app_id => #app.id)
render :nothing => true
}
else
#app = current_user.find_app(params[:app_id])
if (#apps_event.apps_events_attributes == nil || #apps_event.apps_events_attributes.size <= 0)
#apps_event.apps_events_attributes.build
end
#apps_event.populate_code
#action = "update"
format.html {
if(#apps_event.is_std == 'y')
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in updating #{#apps_event.name} standard definition.")
else
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in updating #{#apps_event.name} custom definition.")
end
}
format.js
end
end
end
def delete
if AppsEvent.delete(params[:id])
redirect_to "/app/#{params[:app_id]}/apps_events", :notice => "Successfully deleted #{#apps_event.name} custom definition."
else
redirect_to "/app/#{params[:app_id]}/apps_events",
:alert => "Error in deleting #{#apps_event.name} custom definition."
end
end
end
and I have 5 view files which are index.html.erb, edit.js.erb, _form_custom.html.erb, _form_standard.html.erb and _events.html.erb beside that have also a helper file for update, create and delete using ajax call by setting remote => true. In index file I am doing partial rendering all events(_events.html.erb) and here I done the trick :P
My _events.html.erb
<% for apps_event in #apps_events %>
<% if (apps_event.is_std == 'y') %>
<div class="standardEvent showStandard">
<ul>
<li class="column_1"><span style="font-weight: bold;"><%= apps_event.name %></span></li>
<li class="column_2"><span><%= apps_event.code %></span></li>
<li class="column_3"><span><%= apps_event.description %></span></li>
<li class="column_4">
<%= link_to edit_apps_event(apps_event) %>
</li>
<li class="column_5">
</li>
</ul>
</div>
<% else %>
<div class="customEvent showCustom" style="display:none">
<ul>
<li class="column_1"><span style="font-weight: bold;"><%= apps_event.name %></span></li>
<li class="column_2"><span><%= apps_event.code %></span></li>
<li class="column_3"><span><%= apps_event.description %></span></li>
<li class="column_4">
<%= link_to edit_apps_event(apps_event) %>
</li>
<li class="column_5">
<%= remove_apps_event_prompt_link(apps_event) %>
</li>
</ul>
</div>
<% end %>
<div class="clrBoth"></div>
<% end %>
now you can figure out the left part mean -- JQuery part to hide or show a div.
Related
I have a form I'm using for submission..I've got a gnarly method "create_shift" in my controller to handle said form...but here is the catcher... have to use a preexisting database and the relations are....interesting
the form fails to submit with a "method_missing" error. Doing a assignment.errors gives me this output that I do. not. understand.
What on earth is this telling me? I have missing form fields? When I try to submit the form with "attendance_type" and "call_status_type" coded in via my "create_shift" I still get a different error which I'll list at the bottom of this post...
So first the error output I get with NO.."attendance_type" and "call_status_type" added in my "create_shift"...
_form.html.erb
<!--This is NEW action form-->
<%= form_for #assignment, :url => #my_url, remote: true do |f| %>
<!-- #FIXME need a fields_for 4 the volunteer_event-->
<div class="">
<div class="modal-body d-flex">
<div class="col-sm-8 border-right">
<section id="location-date-time-notes" class="flex">
<% if #assignment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#assignment.errors.count, "error") %> prohibited this assignment from being saved:</h2>
<ul>
<% #assignment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<!--VOLUNTEER SHIFT-->
<!--TODO: make this a partial under field_for-->
<%= f.fields_for :volunteer_event do |builder| %>
<%= render 'assignments/volunteer_shift_fields', vs: builder %>
<% end %>
<!--TODO: Volunteer Shift end -->
<div id="time-row" class="d-flex flex-row">
<label for="assignment_time" class="col-sm-3 p-2">
<i class="fa fa-clock-o fa-lg" aria-hidden="true"></i> Time:
</label>
<div class="col- p-2">
<div class="myStartTime" id="start_time_<%= #assignment.id %>">
<%= f.time_select :start_time %>
</div>
</div>
<div class="col- p-2"><i class="fa fa-arrows-h fa-lg" aria-hidden="true"></i></div>
<div class="col-sm-3 p-2">
<div class="myEndTime" id="end_time_<%= #assignment.id %>">
<%= f.time_select :end_time %>
</div>
</div>
</div>
<div class="d-flex flex-row">
<label for="assignment_notes" class="col-sm-3 p-2">
<i class="fa fa-clipboard fa-lg" aria-hidden="true"></i> Notes:
</label>
<div class="col-sm-9 p-2">
<div class="d-flex flex-row">
<span> Notes only get saved if a contact is assigned the shift, and get removed when the contact is removed from the shift.</span>
<div class="">
<%= f.label :notes %>
<%= f.text_area :notes %>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="col-sm-4">
<!-- Contact Section-->
<div id="contact_section">
<% if #assigned_contacts && #assigned_contacts.length > 0 %>
<h2>Previously Assigned Contacts</h2>
<% #assigned_contacts.each do |c| %>
<%= label_tag "assigned_contacts[#{c.id}]", "Are you sure you want to remove the currently scheduled volunteer, #{c.display_name} (##{c.id}), from the assignment(s)?" %>
<%= check_box_tag "assigned_contacts[#{c.id}]", "replace", #replaced_contacts.include?(c.id) %>
<% end %>
<% end %>
<input id="contact_element_prefix" name="contact_element_prefix" type="hidden" value="contact">
<div class="name large flex-row">
<%= f.label :contact_id %><%= f.text_field :contact_id %>
</div>
<div id="display-contact" class="d-flex flex-row">
<% if f.object.contact_id %>
<%= render partial: 'contacts/contact_display', locals: { contact:f.object.contact} %>
<% else %>
<div>no contact attatched- _form.html called</div>
<%#= link_to 'Show Contact', contact_path(f.object.contact_id), remote: true %>
<% end %>
</div>
<!-- FIXME: replace this logic stack with AJAX-->
<%#= contact_field("#obj", "contact_id",
:locals => {:options => {
:object_name => f.object_name.to_s,
:field_name => 'contact_id',
:on_display => 'display_disciplinary_notes(); display_contact_notes();'
}}
) %>
<%= f.label :closed, "Is this slot closed?" %>
<%= f.check_box :closed %>
<!--Contact Section END-->
<!--Attendance / Call Status start-->
<% if f.object.id && f.object.contact_id %>
<div class="flex-row">
<div class="col-25"><label for="assignment_attendance_type_id">Attendance:</label></div>
<div class="col-75"><%= select(f.object_name,
"attendance_type_id",
AttendanceType.all.sort_by(&:id).collect {|p| [ p.name, p.id ] },
:include_blank => true) %></div>
</div>
<div class="flex-row">
<div class="col-25"><label for="assignment_call_status_type_id">Call status:</label></div>
<div class="col-75"><%= select(f.object_name,
"call_status_type_id",
([["not called yet", ""]] + CallStatusType.all.sort_by(&:id).collect {|p| [ p.name, p.id ] }),
:include_blank => false) %></div>
</div>
<% end %>
<!-- Attendance / Call Status End-->
<!-- LOCK VERSION-->
<div class="flex-row">
<div class="col-25">
<%= f.label :lock_version %>
</div>
<div class="col-75">
<%= f.number_field :lock_version %>
</div>
</div>
<!-- LOCK end-->
</div>
</div>
</div>
<div class="modal-footer d-flex justify-content-between">
<div class="edit_icons d-flex flex-row">
<div class="d-flex flex-column">
<!-- <i class="fa fa-share-alt-square fa-lg" aria-hidden="true"></i> Split-->
<!-- <i class="fa fa-files-o fa-lg" aria-hidden="true"></i> Copy-->
</div>
<div class="d-flex flex-column">
<%#= link_to '<i class="fa fa-pencil-square-o fa-lg" aria-hidden="true"></i>Edit'.html_safe, edit_assignment_path, remote: true%>
<!-- <i class="fa fa-refresh fa-lg" aria-hidden="true"></i> Reassign-->
</div>
</div>
<div>
<button type="button" class="btn btn-secondary mr-2" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary"><%= f.submit "Submit" %></button>
<!-- <input id="assignment_submit" name="commit" type="submit" value="Update">-->
</div>
</div>
</div>
<% end %>
Here is the Model
assignment.rb
class Assignment < ApplicationRecord
# attr_accessor :volunteer_event ,:contact_id #why is this volunteer_event and not volunteer_shift???
belongs_to :volunteer_shift
has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type
belongs_to :contact ,optional: true
validates_presence_of :volunteer_shift #belongs_to takes care of this now
validates_associated :volunteer_shift
belongs_to :attendance_type
belongs_to :call_status_type
validates_presence_of :set_date, :if => :volshift_stuck #belongs_to takes care of this now??
accepts_nested_attributes_for :volunteer_shift, allow_destroy: true
delegate :set_date, :set_date=, :to => :volunteer_shift
delegate :set_description, :set_description=, :to => :volunteer_shift
has_one :contact_volunteer_task_type_count, lambda{||
{:conditions => 'contact_volunteer_task_type_counts.contact_id = #{defined?(attributes) ? contact_id : "assignments.contact_id"}', :through => :volunteer_shift, :source => :contact_volunteer_task_type_counts}
}
scope :date_range, lambda { |range|
joins(volunteer_shift: :volunteer_event)
.where(volunteer_shifts: { volunteer_events: { date: range } })
}
scope :is_after_today, lambda {||
{ :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) > ?', Date.today] }
}
scope :on_or_after_today, lambda {||
{ :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) >= ?', Date.today] }
}
scope :not_cancelled, -> { where('(attendance_type_id IS NULL OR attendance_type_id NOT IN (SELECT id FROM attendance_types WHERE cancelled = \'t\'))')}
scope :roster_is_limited_by_program, -> {where("roster_id IN (SELECT id FROM rosters WHERE limit_shift_signup_by_program = 't')").joins(:volunteer_shift)}
def real_programs
return [] unless self.volunteer_shift&.roster
return [] unless self.volunteer_shift.roster.limit_shift_signup_by_program
self.volunteer_shift.roster.skeds.select{|x| x.category_type == "Program"}.map{|x| x.name}
end
attr_accessor :attendance_type_id
# TODO: find all time_range_s methods and either pull out to DRY or give unique names
def time_range_s
return "" unless start_time and end_time
(start_time.strftime("%I:%M") + ' - ' + end_time.strftime("%I:%M")).gsub( ':00', '' ).gsub( ' 0', ' ').gsub( ' - ', '-' ).gsub(/^0/, "")
end
def description
return unless volunteer_shift
self.volunteer_shift.volunteer_event.date.strftime("%D") + " " + self.time_range_s + " " + self.slot_type_desc
end
def roster_title
return unless volunteer_shift
self.volunteer_shift.roster.name
end
def date
return unless volunteer_shift
volunteer_shift.date
end
#full calendar uses this method name....see the assignment.json.jbuilder
def event_date
return unless volunteer_shift
self.date
end
def slot_type_desc
b = (self.volunteer_shift.volunteer_task_type_id.nil? ? self.volunteer_shift.volunteer_event.description : self.volunteer_shift.volunteer_task_type.description)
b = b + " (#{self.volunteer_shift.description})" if self.volunteer_shift.description and self.volunteer_shift.description.length > 0
b
end
def display_name
((!(self.volunteer_shift.description.nil? or self.volunteer_shift.description.blank?)) ? self.volunteer_shift.description + ": " : "") + self.contact_display
end
def cancelled?
(self.attendance_type&.cancelled)
end
def attended?
(self.attendance_type and !self.attendance_type.cancelled)
end
def contact_display
if self.closed
"(closed)"
elsif contact_id.nil?
return "(available)"
else
self.contact.display_name + "(#{self.voltask_count})"
end
end
before_validation :set_values_if_stuck
def set_values_if_stuck
return unless (volshift_stuck || volunteer_shift)
volunteer_shift.set_values_if_stuck(self)
end
after_destroy { |record| if record.volunteer_shift&.stuck_to_assignment; record.volunteer_shift.destroy; else VolunteerShift.find_by_id(record.volunteer_shift_id).fill_in_available; end}
after_save {|record| if record.volunteer_shift&.stuck_to_assignment; record.volunteer_shift.save; end}
after_save { |record| VolunteerShift.find_by_id(record.volunteer_shift_id).fill_in_available }
def volunteer_shift_attributes=(attrs)
return unless volunteer_shift
self.volunteer_shift.attributes=(attrs) # just pass it up
end
def volshift_stuck
return unless volunteer_shift
self.volunteer_shift&.stuck_to_assignment
end
#for fullcalendar
def all_day_event?
self.start_time == self.start_time.midnight && self.end_time == self.end_time.midnight ? true : false
end
end
and controller
class AssignmentsController < ApplicationController
before_action :set_assignment, only: [:show, :edit, :update, :destroy]
skip_before_action :verify_authenticity_token #TODO refactor this line to be very specific
# GET /assignments or /assignments.json
def index
# #assignments = Assignment.limit(20)
# #assignments = Assignment.where(start: params[:start]..params[:end])
#assignments = Assignment.date_range(params[:start]..params[:end])
end
# GET /assignments/1 or /assignments/1.json
def show
end
# GET /assignments/new
def new
# #assignment = Assignment.new
add_shift
# #assignment.volunteer_shift.build
#my_url = {:action => "create", :id => params[:id]}
end
# GET /assignments/1/edit
def edit
end
def edit_old #dumb "stuff" from older app
if #assignment
#assignments = [#assignment]
else
begin
#assignments = params[:id].split(",").map{|x| Assignment.find(x)}
#assignment = #assignments.first
rescue
flash[:error] = $!.to_s
redirect_skedj(request.env["HTTP_REFERER"], "")
return
end
end
#referer = request.env["HTTP_REFERER"]
#my_url ||= {:action => "update", :id => params[:id]}
render :action => 'edit'
end
# POST /assignments or /assignments.json
def create
create_shift
# #assignment = Assignment.new(assignment_params)
#
# # error wants contact.id not contact_id ???
#
# respond_to do |format|
# if #assignment.save
# format.html { redirect_to #assignment, notice: "Assignment was successfully created." }
# format.json { render :show, status: :created, location: #assignment }
# else
# format.html { render :new, status: :unprocessable_entity }
# format.json { render json: #assignment.errors, status: :unprocessable_entity }
# end
# end
end
def add_shift # FIXME: evil brought over from old app
ve = nil
if !params["id"].blank?
ve = VolunteerEvent.find_by_id(params["id"])
else
ve = VolunteerEvent.new
end
vs = ve.volunteer_shifts.new
vs.program = Program.find_by_name("intern")
# vs.slot_count = 1
vs.volunteer_event_id = ve.id if ve.id
vs.volunteer_event = ve
a = vs.assignments.new
a.volunteer_shift = vs
vs.stuck_to_assignment = true
vs.not_numbered = true
#assignments = vs.assignments = [a]
#referer = request.env["HTTP_REFERER"]
#my_url = {:action => "create_shift", :id => params[:id]}
#assignment = a
# binding.pry
# render :partial => 'assignments/new'
# render :partial => 'assignments/edit' #<--original
end
def create_shift # FIXME: evil brought over from original code base
# #fonso = #assignment.inspect
ve = nil
# Fixme: building volunteer shifts variable "vs" and associating with assignment
if !params["id"].blank?
ve = VolunteerEvent.find(params["id"])
else
if params["roster_id"].blank? || params["assignment"]["set_date"].blank?
ve = VolunteerEvent.new # won't save
else
ve = Roster.find_by_id(params["roster_id"]).vol_event_for_date(params["assignment"]["set_date"])
end
end
vol_shift = ve.volunteer_shifts.new
vol_shift.stuck_to_assignment = true
vol_shift.not_numbered = true
#fixme: volunteer shifts variable "vol_shift" and association with assignment end
call_status_type = CallStatusType.find_by_id(params["assignment"]["call_status_type_id"]) #fixme: this does not fix the problem
attendance_type = AttendanceType.find_by_id(params["assignment"]["attendance_type_id"]) #fixme: this does not fix the problem
# FIXME: vs.attributes=(params["assignment"]["volunteer_shift_attributes"]) # original needs to be rebuilt 4 this system
h0 = {"volunteer_task_type_id" => params["volunteer_task_type_id"]}
h1 = {"roster_id" => params["roster_id"]}
h2 = {"program_id" => params["program_id"]}
h3 = {"set_description" => params["set_description"]}
#h4 = {"attendance_type" => attendance_type.name} #fixme: this does not fix the problem
#h5 = {"call_status_type" => call_status_type} #fixme: this does not fix the problem
hash_arr = [h0,h1,h2,h3] #fixme: no h4 or h5
volunteer_shift_attributes = hash_arr.reduce { |acc, h| (acc || {}).merge h }
#binding.pry
vol_shift.attributes = volunteer_shift_attributes #fixme: fracks up here but why?
#fixme: vs.attributes fix end
#FIXME: building variable - #assignments END
assignment = vol_shift.assignments.new
vol_shift = assignment.volunteer_shift
assignment.attributes = (assignment_params) #<----magic happens here
#assignments = [assignment]
#fixme: building variable - #assignments END
#FIXME: wtf is it and why is it?
vol_shift.assignments = [assignment]
vol_shift.set_values_if_stuck #fixme: <---- drill into this one
binding.pry
vs.assignments = []
#success = assignment.valid? && vs.save #fixme: <--------what was the valid? error here?
#assignment = assignment #fixme: <-----------------------#assignment is finally built here
# fixme: the above lines are merging params from one into the other in the old app. to create the new volunteer_shift.
respond_to do |format|
if #success
vol_shift = vol_shift.reload
#assignment = a = vol_shift.assignments.new
a.volunteer_shift = vol_shift
# a.volunteer_shift_id = vs.id
a.attributes = (params["assignment"])
#assignments = vol_shift.assignments = [a]
format.html { redirect_to #assignment, notice: "Assignment was successfully created." }
format.json { render :show, status: :created, location: #assignment }
else
vs.destroy
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #assignment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /assignments/1 or /assignments/1.json
# def update -original
#
# #assignment.update(assignment_params)
# end
def update
unless params[:assignment]
redirect_to :action => "index"
return
end
#my_url = {:action => "update", :id => params[:id]}
last_id = nil
begin
#assignments = params[:id].split(",").map{|x| last_id = x; Assignment.find(x)}
rescue ActiveRecord::RecordNotFound
flash[:jsalert] = "The assignment (##{last_id.to_i.inspect}) seems to have disappeared or never existed. It is possible somebody else has modified or deleted it."
rt = params[:assignment].delete(:redirect_to)
redirect_skedj(rt, "")
return
end
lv = params["lock_versions"]
ac = params["assigned_contacts"] || {}
#assigned_contacts = []
#replaced_contacts = []
ret = true
#assignments.each do |as|
as.lock_version = lv[as.id.to_s]
if as.lock_version_changed?
as.errors.add("lock_version", "is stale for this assignment, which means it has been edited by somebody else since you opened it, please try again")
ret = false
end
if as.contact_id && as.contact_id.to_s != params[:assignment][:contact_id].to_s
#assigned_contacts << as.contact
unless ac[as.contact_id.to_s] && ac[as.contact_id.to_s] == "replace"
as.errors.add("contact_id", "has been changed, please confirm below that the volunteer who is already assigned to the shift should be removed")
ret = false
else
#replaced_contacts << as.contact_id
end
end
end
rt = params[:assignment].delete(:redirect_to)
js_alert = nil
if ! ret
#assignment = Assignment.new
#assignment.volunteer_shift = #assignments.first.volunteer_shift
#assignment.attributes=(params[:assignment]) # .. ? .delete("volunteer_shift_attributes")
end
#assignments.each{|x|
if ret
#assignment = x
bc = x.contact_id
ret = !!(x.update_attributes(params[:assignment]))
if bc != x.contact_id and x.first_time_in_area?
alert = "#{x.contact.display_name} (##{x.contact_id}) has never logged hours for the #{x.volunteer_shift.volunteer_task_type.description} task type. Please remind the volunteer of the requirements for this area."
if x.volunteer_shift.volunteer_event and x.volunteer_shift.volunteer_event.notes and x.volunteer_shift.volunteer_event.notes.length > 0
alert += "\n\nSome suggested notes saved in the database for this event are:\n" + x.volunteer_shift.volunteer_event.notes
end
js_alert = alert
end
end
}
if ret && #assignment.contact and not #assignment.contact.is_old_enough?
msg = "This volunteer is not yet #{Default['minimum_volunteer_age']} years old (based on their saved birthday: #{#assignment.contact.birthday.to_s}).\nPlease remind the volunteer that they must have an adult with them to volunteer."
if js_alert == nil
js_alert = msg
else
js_alert = msg + "\n\n" + js_alert
end
end
flash[:jsalert] = js_alert if js_alert
if ret
flash[:notice] = 'Assignment was successfully updated.'
redirect_skedj(rt, #assignment.volunteer_shift.date_anchor)
else
#referer = rt
render :action => "edit"
end
end
# DELETE /assignments/1 or /assignments/1.json
def destroy
#assignment.destroy
# NOTE: comment original out 4 now
# respond_to do |format|
# format.html { redirect_to assignments_url, notice: "Assignment was successfully destroyed." }
# format.json { head :no_content }
# end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_assignment
#assignment = Assignment.find(params[:id])
end
# Only allow a list of trusted parameters through.
def assignment_params
#fixme: ,volunteer_shift_attributes: [:???, :???, :???] <--- insert this below?
params.require(:assignment).permit(:title, :set_date, :date, :date_range, :volunteer_shift_id, :contact_id, :start_time, :end_time, :start, :end, :attendance_type_id, :notes, :call_status_type_id, :closed, :lock_version, :color, :description, volunteer_shift_attributes: [:volunteer_task_type_id,:roster_id,:program_id,:set_description,:set_date,:id,:destroy])
# params.require(:assignment).permit(:title, :set_date, :date_range, :contact_id, :start_time, :end_time, :start, :end, :attendance_type_id, :notes, :call_status_type_id, :closed, :lock_version, :color, volunteer_shift_attributes: [:volunteer_task_type_id,:roster_id,:program_id,:set_description,:set_date,:id,:destroy])
end
end
oh and the volunteer_shift.rb model
class VolunteerShift < ApplicationRecord
validates_presence_of :roster_id
validates_presence_of :end_time
validates_presence_of :start_time
has_many :assignments
belongs_to :volunteer_default_shift
belongs_to :volunteer_task_type
belongs_to :roster
belongs_to :volunteer_event
belongs_to :program
has_many :contact_volunteer_task_type_counts, :primary_key => 'volunteer_task_type_id', :foreign_key => 'volunteer_task_type_id' #:through => :volunteer_task_type
def validate
errors.add("end_time", "is before the start time") unless self.start_time && self.end_time && self.start_time < self.end_time
end
def self.week_for_date(d)
long_time_ago = Date.new(1901, 12, 22)
difference = (d - long_time_ago).to_int
((difference / 7) % 2 ) == 0 ? "A" : "B"
end
def weeknum
1 + ((self.date.day - 1) / 7)
end
def week
VolunteerShift.week_for_date(self.date)
end
def slot_number=(a)
write_attribute(:slot_number, a)
self.not_numbered = (!slot_number)
end
def set_description
self.description
end
def set_description=(desc)
self.description=(desc)
end
def set_date_set
binding.pry
#set_date_set #fixme: <----- nil here...breaking here?
end
def set_date=(val)
binding.pry
#set_date_set = true
#set_date = val
end
def set_date
#set_date_set ? #set_date : self.volunteer_event.date
end
def set_values_if_stuck(assn_in = nil)
return unless self.stuck_to_assignment #<---it's a boolean in the database
assn = assn_in || self.assignments.first
return unless assn
self.start_time = assn.start_time
self.end_time = assn.end_time
return unless self.volunteer_event_id.nil? || self.volunteer_event.description.match?(/^Roster #/)
# return unless set_date_set #fixme:<--- pry me...is this asking if date is set??
roster = Roster.find_by_id(self.roster_id)
if roster and !(set_date == nil || set_date == "")
ve = roster.vol_event_for_date(set_date)
ve.save! if ve.id.nil?
self.volunteer_event = ve
self.volunteer_event_id = ve.id
else
if self.volunteer_event.nil?
self.volunteer_event = VolunteerEvent.new
end
end
end
def shift_display
time_range_s + ((!(self.description.nil? or self.description.blank?)) ? (": " + self.description) : "")
end
def time_range_s
return unless self.read_attribute(:start_time) and self.read_attribute(:end_time)
(self.my_start_time("%I:%M") + ' - ' + self.my_end_time("%I:%M")).gsub( ':00', '' ).gsub( ' 0', ' ').gsub( ' - ', '-' ).gsub(/^0/, "")
end
def my_start_time(format = "%H:%M")
read_attribute(:start_time).strftime(format)
end
def self._parse_time(time)
Time.mktime(2000, 01, 01, *time.split(":").map(&:to_i))
end
def my_start_time=(str)
write_attribute(:start_time, VolunteerShift._parse_time(str))
end
def my_end_time(format = "%H:%M")
read_attribute(:end_time).strftime(format)
end
def my_end_time=(str)
write_attribute(:end_time, VolunteerShift._parse_time(str))
end
def self.range_math(*ranges)
... #cutting this out so the question can fit this post
end
def fill_in_available
return if self.stuck_to_assignment #<-- it's a boolean in the database
Thread.current['volskedj_fillin_processing'] ||= []
if Thread.current['volskedj_fillin_processing'].include?(self.id)
return
end
begin
Thread.current['volskedj_fillin_processing'].push(self.id)
Assignment.where(volunteer_shift_id: self.id).select{|x| x.contact_id.nil? and !x.closed}.each{|x| x.destroy}
inputs = [[(self.read_attribute(:start_time)), (self.read_attribute(:end_time))]]
Assignment.where(volunteer_shift_id: self.id).select{|x| !x.cancelled?}.each{|x|
inputs.push([(x.start_time), (x.end_time)])
}
results = self.class.range_math(*inputs)
results.each{|x|
a = Assignment.new
a.volunteer_shift_id, a.start_time, a.end_time = self.id, x[0], x[1]
a.volunteer_shift = self
a.closed = self.volunteer_event.nowalkins
a.save!
}
ensure
Thread.current['volskedj_fillin_processing'].delete(self.id)
end
end
after_save :fill_in_available
def date
self.volunteer_event.date
end
def date_display
self.date.strftime('%A, %B %d, %Y').gsub( ' 0', ' ' )
end
def date_anchor
self.date ? self.date.strftime('%Y%m%d') : ''
end
def time_shift(val)
self.start_time += val
self.end_time += val
end
def left_method_name
[self.volunteer_task_type_id.nil? ? self.volunteer_event.description : self.volunteer_task_type.description, self.slot_number].select{|x| !x.nil?}.join(", ")
end
def left_unique_value
left_method_name
end
def description_and_slot
((self.volunteer_task_type_id || -1) * 1000) + (self.not_numbered ? 0 : self.slot_number)
end
def weekday
Weekday.find_by_id(self.date.strftime("%w"))
end
end
And now here is the error output I get WITH.."attendance_type" and "call_status_type" added in my "create_shift"...
(FYI: they are added by h4 and h5 to the hash in the create_shift method)...I get this error...
ActiveModel::UnknownAttributeError - unknown attribute 'attendance_type' for VolunteerShift.:
h4 = {"attendance_type" => attendance_type.name} #fixme: this does not fix the problem is the cause of your problem here.
h4 get's wrapped up into an array and then the whole array get's shoved into volunteer_shift_attributes
volunteer_shift_attributes = hash_arr.reduce { |acc, h| (acc || {}).merge h }
Which in turn get's assigned to the volunteer_shift record that does not have an attendence_type column in your database. To get past this error just comment out the line
#h4 = {"attendance_type" => attendance_type.name} #fixme: this does not fix the problem is the cause of your problem here.
This leaves you with a logic error, you presumably need to set the attendance_type value on something, probably your assignment record but get that commented out and see where that leaves you, you can worry about the assignment after you have got past this error. That's a logic problem to solve not a coding error
I get this error and for the life of me I can't figure out why. Help would be appreciated. :
error 3: error displayed after changes
error 4: after User.all.each do |user|
Error: Undefined method 'each' for nil: nilClass
my ruby/haml code is as follows
viewer code:
-# This file is app/views/projects/index.html.haml
%h1 All Project Tasks
= form_tag projects_path, :method => :get do
Include:
- #all_users.each do |user|
= user
= check_box_tag "users[#{user}]", 1, ("checked" if #filtered_users.find_index(user))
= submit_tag 'Refresh', :id => "users_submit"
%table#projects
%thead
%tr
%th{:class => ("hilite" if params[:sort] == "title")}= link_to "Title", {:controller => "projects", :sort => "title", :filter => #filtered_users.to_s}, :id => "title_header"
%th Task Details
%th Assigned Usertimot
%th{:class => ("hilite" if params[:sort] == "due_date")}= link_to "Due Date", {:controller => "projects", :sort => "due_date", :filter => #filtered_users.to_s}, :id => "due_date_header"
%tbody
- #projects.each do |project|
%tr
%td= project.title
%td= link_to "More about #{project.title}", project_path(project)
%td= project.user
%td= project.due_date.to_formatted_s(:long)
= link_to 'Add new project task', new_project_path
controller code:
class ProjectsController < ApplicationController
def show
id = params[:id] # retrieve project task ID from URI route
#project = Project.find(id) # look up project task by unique ID
# will render app/views/projects/show.<extension> by default
def index
#projects_users = Project.all_users
# remembered settings
if (params[:filter] == nil and params[:users] == nil and params[:sort] == nil and
(session[:filter] != nil or session[:users] != nil or session[:sort] != nil))
if (params[:filter] == nil and session[:filter] != nil)
params[:filter] = session[:filter]
end
if (params[:sort] == nil and session[:sort] != nil)
params[:sort] = session[:sort]
end
redirect_to projects_path(:filter => params[:filter], :sort => params[:sort], :users => params[:users])
else
if (params[:filter] != nil and params[:filter] != "[]")
#filtered_users = params[:filter].scan(/[\w-]+/)
session[:filter] = params[:filter]
else
#filtered_users = params[:users] ? params[:users].keys : []
session[:filter] = params[:users] ? params[:users].keys.to_s : nil
end
end
session[:sort] = params[:sort]
session[:users] = params[:users]
if (params[:sort] == "title")
if ( params[:users]or params[:filter] )
#projects = Project.find(:all, :order => "title")
end
end
if (params[:sort] == "due_date")
if ( params[:users]or params[:filter] )
#projects = Project.find(:all, :order => "due_date")
end
if (params[:sort] == nill)
if(params[:users] or params[:filter])
#projects = Project.all
end
end
end
end
def new
# default: render 'new' template
end
def create
#project = Project.create!(project_params)
flash[:notice] = "#{#project.title} was successfully created."
redirect_to projects_path
end
def edit
#project = Project.find params[:id]
end
def update
#project = Project.find params[:id]
#project.update_attributes!(project_params)
flash[:notice] = "#{#project.title} was successfully updated."
redirect_to project_path(#project)
end
def destroy
#project = Project.find(params[:id])
#project.destroy
flash[:notice] = "Project '#{#project.title}' deleted."
redirect_to projects_path
end
private
def project_params
params.require(:project).permit(:title, :description, :extended_description, :user, :due_date)
end
end
end
i understand that the spacing for haml may be a little off, just the nature of trying to format the code block thanks in advance!
viewer code:
class Project < ActiveRecord::Base
def self.all_users
allUsers = []
Project.all.each do |project|
if (allUsers.find_index(project.user) == nil)
allUsers.push(project.user)
end
end
return allUsers
end
end
You are probably getting the error on this line in your view:
#all_users.each do |user|
The reason for the error as I see it is that you don't have #all_users instantiated anywhere in your controller's index action method.
First switch #all_users to #projects_users. Also it appears that your all_users method in project.rb is overly complex and is returning nil. Try modifying project.rb to the following:
class Project < ActiveRecord::Base
def self.all_users
all.includes(:user).map(&:user).uniq
end
end
Undefined method 'each' for nil: nilClass
This error basically means you don't have any data in your variable.
In other languages, it would mean you've not delcared the variable. Because Ruby is object orientated, it will populate the variable with the nilClass class.
Many new Ruby devs are thrown by the "undefined method" exception message; it's the nilClass you have to look out for.
--
To explain the error properly, because Ruby is object orientated, every variable is actually a data object, represented by a class. In Rails, you can define these classes as models (User.find etc).
Unlike other languages, Ruby treats these objects as is -- it uses methods on them. Other languages fit data into functions, E.G PHP's each function:
#PHP
<$ each($people) $>
#Ruby
<% #people.each do |person| %>
Thus, the "no method" error basically means that Ruby cannot find the method you're calling on the nilClass. It throws developers because they think that "I have the x method on the User class", not realizing that the variable has been populated by the nilClass instead.
The short of it is that you have to either make your calls conditional, or populate the variable properly.
The error appears to be here:
#app/views/project/index.html.haml
#all_users.each do |user|
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def index
#projects_users = Project.all_users
end
end
You're not assigning #all_users at all
You're using an inefficient way to get "all users"
Here's what I'd do:
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def index
#users = Project.all_users
end
end
#app/models/project.rb
class Project < ActiveRecord::Base
scope :all_users, -> { joins(:users) } #-> this needs to be tested
end
#app/views/projects/index.haml
- #users.each do |user|
= user.name
I am pretty inexperienced with pure SQL, you'll be best referring to the joins documentation for a clearer perspective.
Hi I'm trying to create an event depending on the eventable type. the eventable type is either group or shop.
Writing my code, I'm currently sure that their is a better way create my routes and controller (still a rails newbie)
Is there a way to create only one new and create method pass in the eventable type ?
Models:
class Event < ActiveRecord::Base
belongs_to :eventable, polymorphic: true
class Group < ActiveRecord::Base
has_many :events, as: :eventable
class Shop < ActiveRecord::Base
has_many :events, as: :eventable
Routes:
resources :events do
collection do
get :new_national_event
get :new_local_event
post :create_national_event
post :create_local_event
end
do
event-controller:
def index
#search = Search.new(params[:search])
#shop = find_user_shop(#search.shop_id)
#group = #shop.group
#shop_events = #shop.events
#group_events = #group.events
end
def new_national_event
#user = current_user
#event = #user.group.events.new
end
def new_local_event
#shop = find_user_shop(#search.shop_id)
#event = #shop.events.new
end
def create_national_event
user = current_user
#event = user.group.events
if #event.save!
flash.now[:notice] = "Votre événement national a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement national"
render :new
end
end
def create_local_event
user = current_user
#event = user.group.shop.events
if #event.save!
flash.now[:notice] = "Votre événement local a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement local"
render :new
end
end
views:
index.html.slim
= link_to new_national_event_events_path
= link_to new_local_event_events_path
new_national_event_events_path.html.slim
= form_for #event, :url => create_national_event_events_path, :method => :post do |f|
div class="field"
= f.text_field :title, :required => true
div class="field"
= f.text_field :threshold, :required => true
div class="form-actions"
=f.submit "Create", class: "btn blue"
If it is just the routing you are concerned with, and not the number of actions, you can use constraints to allow a single path variable send the request to one of a multiple number of actions, this can be useful in some cases where you may want to have multiple buttons to multiple actions all reading from a single form, or if you just want to simplify your routes variable naming.
use the commit_param_routing gem and in your routes file you can write something like:
resources :events do
collection do
post :save, constraints: CommitParamRouting.new(EventController::CREATENATIONAL), action: :create_national_event
post :save, constraints: CommitParamRouting.new(EventController::CREATELOCAL), action: :create_local_event
end
end
add the constants to your controller:
class EventController
CREATENATIONAL = "create national"
CREATELOCAL = "create local"
.....
end
and then all that is left is to add them to your view file submit buttons:
div class="form-actions"
.row
.col-xs-2
=f.submit EventController::CREATENATIONAL
.col-xs-2
=f.submit EventController::CREATELOCAL
sorry for if its not quite what you were looking for or unclear, my first answer!
UPDATE: The answer bellow is correct. Just wanted to update what I did to solve the problem.
First I had to delete all my previous lines in the rails console.
Then I used the bye bug gem in my lines controller at the bottom of the create method to discover where the next bug occurred. I created a test line that I needed to delete again. so I ran
Line.last.delete in console.
This is the way my lines controller create method looks now (working no bugs)
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
if params[:line][:previous_line_id].empty?
#line.story = Story.create
#line.save
else
#line.story = #line.previous_line.story
#line.save
end
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
Finally I ran #Lines.each { |line| line.update.attribute(:story_id: 3)}
This gave the necessary association between lines and story.
ORIGINAL POST BELLOW.
I'm getting this error in my rails app. I think that when I create a new line or start a story, it doesn't automatically add it to a story object. I've listed my show.html.erb file as well as my lines controller.rb file.
What am I missing? How do I get the controller to add data to the story object correctly?
Thanks!
I added a few lines of code to my lines controller:
class LinesController < ApplicationController
def new
params[:previous_line_id].nil? ? #line = Line.new : #line = Line.find(params[:previous_line_id]).next_lines.create
#lines = #line.collect_lines
#ajax = true if params[:ajax]
render :layout => false if params[:ajax]
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
end
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
flash[:error] = "Please sign in or register before creating a line!"
unless params[:line][:previous_line_id].empty?
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
else
redirect_to root_path
end
end
end
# params[:id] should correspond to the first line of the story.
# if params[:deeper_line_id] is not nil, that means that they want to render up to the nested line id
def show
#lines = Line.find(params[:id]).collect_lines
#next_lines = #lines.last.next_lines.ranked
#lines.last.update_attribute(:score, #lines.last.score + 1)
end
def select_next
#line = Line.find(params[:id])
#line.update_attribute(:score, #line.score + 1)
#lines = [#line]
#next_lines = #line.next_lines.ranked
render :layout => false
end
def send_invite
if user_signed_in?
UserInvite.send_invite_email(current_user,Line.find(params[:id]), params[:email]).deliver
flash[:notice] = "Your invite was sent!"
else
flash[:error] = "Please sign in"
end
redirect_to Line.find(params[:id])
end
private
def line_params
params.require(:line).permit(:text, :previous_line_id, :user_id)
end
end
I added these lines to the controller pictured above
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
Here is my show.html.erb file
<div class="row">
<div class="col-lg-2">
</div>
<div class="box-container col-lg-7 ">
<div id="story" class="box">
<% #lines.each do |line| %>
<span class="story-line" data-id="<%=line.id%>"><%= link_to line.text, '#', :class=>"story-line" %></span>
<% end %>
</div>
<div id="next-steps">
<%= render 'next_steps' %>
</div>
<span style="font-size:.9em; margin-bottom:15px; display:block;">*If the links don't work, try refreshing.</span>
</div>
<div class="col-lg-2" style="padding-right:25px;">
<%= render 'invite' %>
Your Fellow Collaborators: <br />
<div class="collaborators">
<% #lines.last.story.collaborators.uniq.each do |collaborator| %>
<%= link_to profile_path(:id => collaborator.id) do %>
<%= image_tag collaborator.profile_image_uri, :class => "prof-icon" %>
<% end %>
<% end %>
</div>
Story model
class Story < ActiveRecord::Base
has_many :lines
has_and_belongs_to_many :collaborators, :class_name => "User", :join_table => "collaborators_stories", :association_foreign_key => :collaborator_id
def first_line
self.lines.first_lines.first_lines.first
end
end
Here is my lines.rb file
class Line < ActiveRecord::Base
scope :first_lines, -> { where previous_line_id: nil}
scope :ranked, -> { order("score + depth DESC")}
belongs_to :user
belongs_to :story
belongs_to :previous_line, :class_name => "Line", :foreign_key => "previous_line_id"
has_many :next_lines, :class_name => "Line", :foreign_key => "previous_line_id"
validates_presence_of :text
after_create :update_depths
def update_depths
line = self.previous_line
while !line.nil?
line.update_attribute(:depth, line.depth + 1)
line = line.previous_line
end
end
def first_line
line = self
while !line.previous_line.nil?
line = line.previous_line
end
line
end
def collect_lines
line = self
lines = [self]
while !line.previous_line.nil?
lines.unshift(line.previous_line)
line = line.previous_line
end
lines
end
end
Problem is orphaned lines in your database. Look for them and associate it to a story, or delete it:
How to find orphaned records:
http://antonzolotov.com/2013/01/26/how-to-find-and-delete-orphaned-records-with-ruby-on-rails.html
Then review the create method to ensure a line should be part of a story:
#short example review activerecord relations
#story = Story.find(params[:story_id])
story.lines.create(line_params)
That should work.
EDIT:
def self.find_orphan_ids
Lines.where([ "user_id NOT IN (?) OR story_id NOT IN (?)", User.pluck("id"), Story.pluck("id") ]).destroy_all
end
I've build quite complex form which creates one prescription with many realtions. I am using this syntax in view:
- provide(:title, 'Create prescription')
%h1 Add medicines to prescription
.row
.span6.offset3
= form_for #prescription do |f|
= render 'shared/error_prescription_messages'
%p
= f.hidden_field :patient_id, :value => params[:patient_id]
= f.hidden_field :user_id, :value => current_user.id
= f.fields_for :relations do |builder|
= render 'child_form', :f => builder
%p= f.submit "Submit"
chlid_form is quite simple :
- it=f.options[:child_index].to_i
- n= it.to_s
%h2
= "Medicine ##{it+1}"
= f.hidden_field :medicine_id, :id => "my_medicine_id#{it}"
- if params[:prescription].nil? || params[:prescription][:relations_attributes][n.to_sym][:medicine_name].nil?
= f.autocomplete_field :medicine_name, autocomplete_medicine_name_relations_path, :id_element => "#my_medicine_id#{it}"
- else
= f.autocomplete_field :medicine_name, autocomplete_medicine_name_relations_path, :id_element => "#my_medicine_id#{it}", :value => params[:prescription][:relations_attributes][n.to_sym][:medicine_name]
= f.label :amount, "Amount of medicine boxes"
= f.number_field :amount, :value => 1
= f.label :daily
= f.number_field :daily, :value => 1
= f.label :period_in_days, "Duration of treatment (in days)"
= f.number_field :period_in_days, :value => 1
So as you can see I'm using f.options[:child_index] to get index of child (0,1,2...) cause I generate multiple items with this particular form. I then put it to variable it and sucessfully use it in :id_element => "#my_medicine_id#{it}" which works PERFECTLY fine (creates my_medicine_id0, my_medicine_id1 ....) Although it doesn't work in this line:
:value => params[:prescription][:relations_attributes][n.to_sym][:medicine_name]
where n is just n=it.to_s.
I though somethings wrong in controller but if I change this line to whatever
:value => params[:prescription][:relations_attributes]**[:'0']**[:medicine_name] or any other integer from 0 to 4 everything works great, but I NEED dynamic change in this one. So I got proof that it DOES work because it generates integer fine here "#my_medicine_id#{it}" but won't work in hash! And when I print the whole hash from params I get this:
{"patient_id"=>"7", "user_id"=>"1", "relations_attributes"=>{"0"=>{"medicine_id"=>"13490", "medicine_name"=>"Locacid 500 mcg/g (0,05%) (1 tuba 30 g)", "amount"=>"0", "daily"=>"1", "period_in_days"=>"1"}, "1"=>{"medicine_id"=>"", "medicine_name"=>"", "amount"=>"1", "daily"=>"1", "period_in_days"=>"1"}, "2"=>{"medicine_id"=>"", "medicine_name"=>"", "amount"=>"1", "daily"=>"1", "period_in_days"=>"1"}, "3"=>{"medicine_id"=>"", "medicine_name"=>"", "amount"=>"1", "daily"=>"1", "period_in_days"=>"1"}, "4"=>{"medicine_id"=>"", "medicine_name"=>"", "amount"=>"1", "daily"=>"1", "period_in_days"=>"1"}}}
so to get the values I need it's pretty obvious that
params[:prescription][:relations_attributes][SOME_KIND_OF_INETEGER][:medicine_name] should work, but doesn't.
Controller code:
class PrescriptionsController < ApplicationController
before_action :signed_in_user
before_action :doctor_user, only: [:new, :create]
before_action :pharmacist_user, only: [:update]
def new
#prescription =Prescription.new
5.times { #prescription.relations.build }
end
def create
#prescription = Prescription.new(new_prescription_params)
if #prescription.save
flash[:success] = "Prescription created."
redirect_to #prescription
else
5.times { #prescription.relations.build }
render 'new', :prescription => params[:prescription]
end
end
def show
#prescription = Prescription.find(params[:id])
#medicines = #prescription.medicines.paginate(page: params[:page], :per_page => 10)
end
def update
#prescription = Prescription.find(params[:id])
#patient = Patient.find(params[:patient_id])
if !prescription_expired?(#prescription)
#prescription.realized = 1
if #prescription.save
flash[:success] = "Prescription realized."
redirect_to #patient
else
redirect_to root_url
end
else
flash[:notice] = "Can't realize, prescription expired."
redirect_to #patient
end
end
private
def new_prescription_params
params.require(:prescription).
permit(:patient_id, :user_id, relations_attributes: [:medicine_id, :medicine_name, :amount, :daily, :period_in_days])
end
def doctor_user
redirect_to(root_url) unless current_user.function == "doctor"
end
def pharmacist_user
redirect_to(root_url) unless current_user.function == "pharmacist"
end
def prescription_expired?(presc)
presc.created_at < 1.month.ago
end
def signed_in_user
unless signed_in?
store_location
flash[:notice] = "Please log in."
redirect_to login_url
end
end
end
I run out of ideas so I ask you guys if anyone can help. Thanks.
There is no point in using params in your view since you already assigned those to your models. Also when you rendering your new action, those params doesn't exist as nothing has been send to the server yet. Just get rid of all the values from inputs.
Your partial should look like:
- it=f.options[:child_index].to_i
- n= it.to_s
%h2
= "Medicine ##{it+1}"
= f.hidden_field :medicine_id, :id => "my_medicine_id#{it}"
= f.autocomplete_field :medicine_name, autocomplete_medicine_name_relations_path
= f.label :amount, "Amount of medicine boxes"
= f.number_field :amount
= f.label :daily
= f.number_field :daily
= f.label :period_in_days, "Duration of treatment (in days)"
= f.number_field :period_in_days
If you want your fields to have default value, set default value inside your database.