Created
September 26, 2016 14:25
-
-
Save anonymous/9d17c066a1991079991a7dd4d59f1296 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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