Skip to content

Instantly share code, notes, and snippets.

@rosylilly
Created January 31, 2018 17:19
Show Gist options
  • Save rosylilly/5cd2916a07387d9bce0091d34e0badff to your computer and use it in GitHub Desktop.
Save rosylilly/5cd2916a07387d9bce0091d34e0badff to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
class PartialValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if options[:if] && !valuable(record, options[:if])
return if options[:unless] && valuable(record, options[:unless])
value.validate
value.errors.messages.each_pair do |attr, messages|
messages.each do |message|
record.errors.add(:"#{attribute}.#{attr}", message)
end
end
end
protected
def valuable(record, opt)
opt = opt.to_proc if opt.is_a?(Symbol)
opt.call(record)
end
end
# frozen_string_literal: true
module Partializable
extend ActiveSupport::Concern
class Model
include ActiveModel::Model
attr_accessor :record, :mapping
def self.attributes
@attributes ||= []
end
def self.attr_reader(*attrs)
attributes.concat(attrs.map(&:to_sym))
attributes.uniq!
attrs.each do |attr|
attr = attr.to_s.to_sym
define_method(attr) do
self[attr]
end
end
end
def self.attr_writer(*attrs)
attributes.concat(attrs.map(&:to_sym))
attributes.uniq!
attrs.each do |attr|
attr = attr.to_s.to_sym
define_method(:"#{attr}=") do |val|
self[attr] = val
end
end
end
def self.attr_accessor(*attrs)
attr_reader(*attrs)
attr_writer(*attrs)
end
def [](attr)
attr = attr.to_s.to_sym
reader = (mapping || {}).fetch(attr) { attr }
record ? record_send(reader) : instance_variable_get(:"@#{attr}")
end
def []=(attr, val)
attr = attr.to_s.to_sym
writer = (mapping || {}).fetch(attr) { attr }
record ? record_send(:"#{writer}=", val) : instance_variable_set(:"@#{attr}", val)
end
def as_json
attrs = self.class.attributes.map do |key|
[key, send(key)]
end
Hash[attrs]
end
protected
def record_send(method, *args)
methods = method.to_s.split('.')
actual = methods.pop
rec = methods.inject(record) do |receiver, m|
receiver.send(m)
end
rec.send(actual, *args)
end
end
module ClassMethods
def partials
@partials ||= []
end
def partial_options
@partial_options ||= {}
end
def partialize(name, class_name: nil, mapping: {}, prefix: nil)
name = name.to_s.to_sym
partials.push(name)
klass = (class_name || name).to_s.classify.constantize
klass.attributes.each do |attr|
mapping[attr] ||= "#{prefix ? "#{prefix}_" : ''}#{attr}"
end
partial_options[name] = { mapping: mapping }
define_method(name) do
instance_variable_get(:"@#{name}") ||
instance_variable_set(:"@#{name}", klass.new(record: self, mapping: mapping))
end
define_method(:"#{name}=") do |origin|
partial = origin.dup
partial.instance_variable_set(:@record, self)
partial.instance_variable_set(:@mapping, mapping)
instance_variable_set(:"@#{name}", partial)
klass.attributes.each do |attr|
partial.send(:"#{attr}=", origin.send(attr)) unless mapping[attr].to_s.include?('.')
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment