Last active
August 17, 2018 18:42
-
-
Save ErvalhouS/bd2d9edecf181e1f4be44d481038b73f to your computer and use it in GitHub Desktop.
A way to receive and retrieve non-migrated fields using a SQL database with JSON field support. The only thing you need to do is create a JSON column called `fields` in the table and include this module into your model.
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
# frozen_string_literal: true | |
# A module to abstract a noSQL aproach into SQL records, using a `fields` | |
# JSON column. | |
module CustomFields | |
# Overriding `method_missing` does the magic of assigning an absent field | |
# into `.fields` JSON as a key-value pair, or recovering it's value if exists. | |
def method_missing(meth, *args, &block) | |
raise NoMethodError, "#{table_name} should have a `fields` JSON column" unless can_no_sqlize? | |
no_sqlize(meth, *args, &block) | |
rescue StandardError | |
super | |
end | |
# Used to map which methods can be called in the instance, it is based on | |
# the premisse that you can always assign a value to any field and recover | |
# only existing fields or the ones that are in the `.fields` JSON. | |
def respond_to_missing?(method_name, include_private = false) | |
method_name.to_s.ends_with?('=') || | |
try(:fields).try(:[], method_name.to_s).present? || | |
super | |
end | |
# Overriding a ActiveRecord method that is used in `.create` and `.update` | |
# methods to assign a value into an attribute. | |
def _assign_attribute(key, value) | |
initialize_fields | |
if value.nil? | |
public_send('fields=', try(:fields).try(:except, key)) | |
else | |
public_send("#{key}=", value) | |
end | |
end | |
private | |
# Checks for `.fields` presence and initializes it. | |
def initialize_fields | |
try(:fields=, {}) unless try(:fields).present? | |
end | |
# The behavior that a noSQL method should have... | |
def no_sqlize(meth, *args, &_block) | |
initialize_fields | |
if meth.to_s.ends_with?('=') | |
fields[meth.to_s.chomp('=')] = args.first | |
else | |
try(:fields).try(:[], meth.to_s) | |
end | |
end | |
# Checks if requirements are met for the feature | |
def can_no_sqlize? | |
self.class.column_names.include?('fields') | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment