Skip to content

Instantly share code, notes, and snippets.

@rosiehoyem
Last active August 29, 2015 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rosiehoyem/259f4e235c72ba16b6c1 to your computer and use it in GitHub Desktop.
Save rosiehoyem/259f4e235c72ba16b6c1 to your computer and use it in GitHub Desktop.
Meta Programming: instance_variable_set() and instance_variable_get()
# /app/models/digitize_step
class DigitizeStep < ActiveRecord::Base
belongs_to :process_step
end
# /app/models/final_maps
class FinalTpopMap < ActiveRecord::Base
has_many :process_steps
end

The TerraPop Metadata tracks and documents the full process of preparing the data that will be fed into the TerraPop application. part of this process involveds manipulating geographic data, namely maps. These maps may come from a variety of sources, then depending on the final map needed to match a set of variables, these maps are run through a series of processing steps. There are 10 different processing steps to be exact and no single complete process will look the same.

My task at hand was to construct a streamlined UI that would allow users to craft a full map process with any combination of map steps. The main models involved in this process look a bit like this (code samples were simplified for demonstration purposes):

And all of the process steps, 10 in total, were structured like this: /app/models/process_steps

The first tricky issue I'd like to tackle is to discuss how I created the methods in the ProcessStepsController to handle a process_step with an association to to 1 of potentially 10 different step types.

To faciliate this connection, I created a field in the step_type_id field in the ProcessStep model forming a has_one/belongs_to relationship with a domain model consisting of the 10 different steps. Here is how this domain model was seeded: /db/seeds.rb

To make it possible to write simple crud methods in the ProcessStepsController required heavy use of meta programming, including the send(), instance_variable_set(), and instance_variable_get() methods. For example, to set instance variables for the set step_type = StepType.find(@process_step.step_type_id).name.downcase + "_step" instance_variable_set('@'+ step_type, @process_step.send(step_type))

Put it all together, and the create method looks like this: /app/controllers/process_steps_controller.rb

(See bottom of post for full ProcessStepsController code.)

# /app/models/process_steps
class ProcessStep < ActiveRecord::Base
belongs_to :user
belongs_to :step_type
belongs_to :final_tpop_map
has_one :digitize_step, dependent: :destroy
accepts_nested_attributes_for :digitize_step, allow_destroy: true
has_one :disaggregate_step, dependent: :destroy
accepts_nested_attributes_for :disaggregate_step, allow_destroy: true
has_one :regionalize_step, dependent: :destroy
accepts_nested_attributes_for :regionalize_step, allow_destroy: true
has_one :subset_map_step, dependent: :destroy
accepts_nested_attributes_for :subset_map_step, allow_destroy: true
has_one :dissolve_step, dependent: :destroy
accepts_nested_attributes_for :dissolve_step, allow_destroy: true
has_one :transfer_poly_step, dependent: :destroy
accepts_nested_attributes_for :transfer_poly_step, allow_destroy: true
has_one :code_match_step, dependent: :destroy
accepts_nested_attributes_for :code_match_step, allow_destroy: true
has_one :verify_step, dependent: :destroy
accepts_nested_attributes_for :verify_step, allow_destroy: true
has_one :lsib_align_step, dependent: :destroy
accepts_nested_attributes_for :lsib_align_step, allow_destroy: true
has_one :harmonize_step, dependent: :destroy
accepts_nested_attributes_for :harmonize_step, allow_destroy: true
validates :step_type_id, :presence => true
end
# /app/controllers/process_steps_controller.rb
def create
@process_step = ProcessStep.new(process_step_params)
respond_to do |format|
if @process_step.save
step_type = StepType.find(@process_step.step_type_id).name.downcase + "_step"
instance_variable_set('@'+ step_type, @process_step.send('create_' + step_type))
if instance_variable_get('@'+ step_type).update(process_step_params[step_type + "_attributes"])
format.html { redirect_to country_final_tpop_map_process_steps_path(country_id: @country.id, final_tpop_map_id: @final_tpop_map.id), notice: 'Process step was successfully created.' }
format.json { render :show, status: :created, location: @process_step }
end
else
format.html { render :new }
format.json { render json: @process_step.errors, status: :unprocessable_entity }
format.js
end
end
end
# /db/seeds.rb
step_types = StepType.create([{ name: 'Digitize'},
{ name: 'Subset Map'},
{ name: 'Dissolve'},
{ name: 'Transfer Poly'},
{ name: 'Code Match'},
{ name: 'Harmonize'},
{ name: 'Regionalize'},
{ name: 'Disaggregate'},
{ name: 'Verify'},
{ name: 'LSIB Align'}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment