Skip to content

Instantly share code, notes, and snippets.

@cableray
Forked from sj26/apple_record.rb
Last active July 8, 2017 19:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cableray/bd36b7e1c49a11661c7c to your computer and use it in GitHub Desktop.
Save cableray/bd36b7e1c49a11661c7c to your computer and use it in GitHub Desktop.
How to do STI without separate controller per child: Utilize descendent tracking and override model_name so they use the same URL helpers and parameters as their base class. Makes things like responders and form_for work as expected, while preserving things like to_partial_path.
class AppleRecord < Record
end
module InheritanceBaseNaming
class Name < ActiveModel::Name
delegate :param_key, :singular_route_key, :route_key, to: :base_name
def base_name
@base_name = @klass.base_class.model_name
end
end
def model_name
Name.new(self)
end
end
class OrangeRecord < Record
end
class Record < ActiveRecord::Base
extend InheritenceBaseNaming
def [](type)
descendents.find { |descendent| descendent.name == type }
end
end
class RecordsController < ApplicationController
before_filter :prepare_records
before_filter :prepare_record, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def new
respond_with @record = Record[params.require(:type)].new
end
def create
respond_with @record = Record[params.require(:record_type)].create(record_params)
end
def show
respond_with @record
end
def edit
respond_with @record
end
def update
@record.update_attributes(record_params)
respond_with @record
end
def destroy
@record.destroy
respond_with @record
end
private
def prepare_records
@records = Record.scoped
end
def prepare_record
@record = @records.find(params[:id])
end
def record_params
params.require(:record).permit(:name, :content)
end
end
@contentfree
Copy link

Results in stack level too deep because model_name on the base_class delegates to model_name on the base_class :)

@contentfree
Copy link

This fixes:

module InheritanceBaseNaming
  class Name < ActiveModel::Name
    delegate :param_key, :singular_route_key, :route_key, to: :base_name

    def base_name
      @base_name ||= @klass.base_class.model_name
    end
  end

  def model_name
    @model_name ||= (self == base_class) ? super : Name.new(self)
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment