i have a model which has_many relation to a version model. Any action on the parent model need to be tracked. At the form for delete i have added a nested form to enter a ticket number which will be added to the versions. How could i check in the model validations if the ticket is given? I will write the version before the destroy on the model is called.
# model.rb
class Model < ActiveRecord::Base
has_many :versions,
:as => :version_object
end
# models_controller.rb
def destroy
@model = Model.find(params[:id])
self.write_versions('Destroy')
@Model.destroy
respond_to do |format|
...
end
end
# delete.html.erb
<%= form_for [@model.parent_object, @model], :html => { :class => 'form-horizontal' }, :method => :delete
do |f| %>
<div class="form-actions">
<%= f.fields_for :versions, @model.versions.build do |v| %>
<%= v.text_field :ticket, {:class => 'text_field', :placeholder => 'Ticket Nummer'}%>
<% end %>
<%= f.submit 'Delete Model', :class => 'btn btn-danger' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
:back, :class => 'btn' %>
</div>
<% end %>
I already tried to implement a before_destroy method to check if a version with the 'Destroy' action is written but this won't work because the key fields to identify the certain action could be exist more than one time. The versions are incremental and can be rolled back step by step to get an older version and my model could have more than one relation identifier at the lifetime of his parent.
An solution would be to check the existence of the ticket at the controller through the params, but validations should be at the model.
I don't want to use a versioning gem.
Anybody a hint how to implement such a validation?
You could try encapsulating the versioning logic entirely in your Model
class and not accessing the .versions
relation in the controllers and templates at all. Consider something like this:
# model.rb
class Model
has_many :versions
attr_accessor :_current_action, :_ticket_number
validates :_current_action, presence: true
validates :_ticket_number, presence: true
after_create :create_new_version
before_destroy :create_new_version
def set_next_action(action, ticket_number)
self._current_action = action
self._ticket_number = ticket_number
end
private
def create_new_version
unless _current_action.present? && _ticket_number.present?
raise RuntimeError, "Missing versioning action or ticket number"
end
# adjust this to store the actual model data as well
versions.create!(action: _current_action, ticket_number: _ticket_number)
self._current_action = nil
self._ticket_number = nil
end
end
Then, this is how you do an update:
<!-- edit.html.erb -->
<%= form_for @model, method: :patch do |f| %>
<!-- other fields -->
<%= text_field_tag :_ticket_number, class: "text_field" %>
<%= f.submit "Update model" %>
<% end %>
In the controller:
# models_controller.rb
def update
@model = Model.find(params[:id])
@model.set_next_action "Update", params[:_ticket_number]
if @model.update(params[:model])
# ...
else
render :edit
end
end
This is how you do a delete:
<!-- edit.html.erb -->
<%= form_for @model, method: :delete do |f| %>
<!-- other fields -->
<%= text_field_tag :_ticket_number, class: "text_field" %>
<%= f.submit "Delete model" %>
<% end %>
In the controller:
# models_controller.rb
def destroy
@model = Model.find(params[:id])
@model.set_next_action "Destroy", params[:_ticket_number]
if @model.valid? # validates presence of ticket number
@model.destroy
# ...
else
render :edit
end
end
Note: I didn't test this code, so may need a few changes before it will work.