Skip to content

Instantly share code, notes, and snippets.

Created September 26, 2016 14:25
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 anonymous/9d17c066a1991079991a7dd4d59f1296 to your computer and use it in GitHub Desktop.
Save anonymous/9d17c066a1991079991a7dd4d59f1296 to your computer and use it in GitHub Desktop.
# This is a base class for tableless models that provides ActiveRecord association semantics (i.e. belongs_to) and
# validations. The use case is for quickly writing simple_form based templates for non-table resources.
class FakeModel
include ActiveModel::Model
#include ActiveModel::AttributeMethods
include WithAssociation
private_class_method
def self.initialize_derived
@base_attributes_ = []
end
def self.inherited(derived)
derived.send(:initialize_derived)
end
def self.add_base_attribute_(attr)
@base_attributes_ << attr
end
def initialize
@attributes_ = []
end
class << self
attr_reader :base_attributes_
# Grants helper methods for a Rails foreign key association to a POR object. This is used to give
# non AR classes associations to make things like simple_form and validations play nice.
def belongs_to(key, opts = {})
column = "#{key}_id" #opts[:foreign_key] || "#{key}_id"
add_base_attribute_(column)
id_writer = "#{column}=".to_sym
id_reader = "#{column}".to_sym
id_interrogator = "#{column}?".to_sym
obj_reader = key
obj_writer = "#{key}=".to_sym
attr_key = key
obj_class = opts[:class] || opts[:class_name].try(&:constantize) || key.to_s.camelcase.constantize
define_method("attr_") do
@attr_ ||= {}
end
define_method("attr_cached_") do
@attr_cached_ ||= {}
end
define_method(:[]) do |key|
public_send(key)
end
define_method(:[]=) do |key, value|
public_send("#{key}=", value)
end
define_method(obj_reader) do
obj_id = self.attr_[attr_key]
return nil if obj_id.nil?
cached = self.attr_cached_[attr_key]
if cached.nil?
cached = self.attr_cached_[attr_key] = obj_class.find(self.attr_[attr_key])
end
return cached
end
define_method(obj_writer) do |obj|
if obj.nil?
invalidate_cached_(attr_key)
self.attr_.delete(attr_key)
else
self.attr_[attr_key] = obj.id
self.attr_cached_[attr_key] = obj
end
end
define_method(id_reader) do
self.attr_[attr_key]
end
define_method(id_writer) do |obj|
# this seems to be how ar behaves. this is useful for passing in empty params from the controller
# directly into the model to set an empty foreign key.
self.attr_[attr_key] = obj.empty? ? nil : obj.to_i
invalidate_cached_(attr_key)
end
define_method(id_interrogator) do
!self.attr_[attr_key].nil?
end
define_method(:invalidate_cached_) do |key|
self.attr_cached_.delete(key) if self.attr_cached_[key]
end
return nil
end
def associates_all(*args)
keys.each { |key| associates(key) }
end
end
def attributes
att = @attributes_ + self.class.base_attributes_
att.map! { |a| [a.to_s, public_send(a)] }
Hash[att]
end
def self.attribute(id)
define_method(id) do
attributes_add(id)
singleton_class.instance_eval do
define_method(id) do
instance_variable_get("@#{id}")
end
end
instance_variable_get("@#{id}")
end
define_method("#{id}=") do |arg|
attributes_add(id)
singleton_class.instance_eval do
define_method(id) do
instance_variable_set("@#{id}", arg)
end
end
instance_variable_set("@#{id}", arg)
end
end
private
def attributes_add(sym)
@attributes_ ||= []
@attributes_ << sym unless @attributes_.include?(sym)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment