Skip to content

Instantly share code, notes, and snippets.

@ErvalhouS
Last active August 17, 2018 18:42
Show Gist options
  • Save ErvalhouS/bd2d9edecf181e1f4be44d481038b73f to your computer and use it in GitHub Desktop.
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.
# 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