Forked from ismailakbudak/ruby_custom_field_with_postgresql_and_metaprogramming.rb
Created
July 22, 2019 18:14
-
-
Save marlosirapuan/bbda37a72eda7149a59b9c2d25ceddbd to your computer and use it in GitHub Desktop.
Custom field example for Ruby on Rails with Activerecord & PostgreSQL & Metaprogramming. Example: https://github.com/lab2023/postgresql_jsonb_ransack_rails_5 Demo: https://rails-custom-field-ransack.herokuapp.com
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
begin | |
require 'bundler/inline' | |
rescue LoadError => e | |
$stderr.puts 'Bundler version 1.10 or later is required. Update your Bundler' | |
raise e | |
end | |
gemfile(true) do | |
source 'https://rubygems.org' | |
gem 'activerecord', '5.1.3' | |
gem 'pg', '~> 0.18.4' | |
end | |
# Require gems | |
require 'active_record' | |
require 'minitest/autorun' | |
require 'logger' | |
# DB configurations | |
db_config = { | |
adapter: 'postgresql', | |
host: 'localhost', | |
database: 'custom_field', | |
username: 'postgres' | |
} | |
admin_config = { | |
database: 'postgres', | |
schema_search_path: 'public' | |
} | |
db_config_admin = db_config.merge(admin_config) | |
ActiveRecord::Base.logger = Logger.new(STDOUT) | |
ActiveRecord::Base.establish_connection(db_config_admin) | |
ActiveRecord::Base.connection.drop_database(db_config[:database]) | |
ActiveRecord::Base.connection.create_database(db_config[:database]) | |
# Migrations | |
ActiveRecord::Schema.define do | |
create_table :posts, force: true do |t| | |
t.integer :status, default: 0 | |
t.jsonb :metadata, default: {}, null: false | |
end | |
add_index :posts, :metadata, using: :gin | |
end | |
# Helpers | |
def custom_field_method(field) | |
"metadata_#{field}" | |
end | |
# Models | |
class Post < ActiveRecord::Base | |
end | |
# Custom fields | |
# You can store this information in database as jsonb datatype | |
POST_FIELDS = { | |
title: { | |
name: 'Title', | |
validations: [], | |
input_type: 'string' | |
}, | |
description: { | |
name: 'Description', | |
validations: [], | |
input_type: 'string' | |
}, | |
number: { | |
name: 'Number', | |
validations: [:number], | |
input_type: 'number' | |
} | |
}.freeze | |
POST_FIELDS.each do |field, options| | |
field = field.to_s | |
Post.class_eval do | |
method = custom_field_method(field) | |
options[:validations].each do |validation| | |
validates_numericality_of method if validation == :number | |
end | |
end | |
Post.instance_eval do | |
method = custom_field_method(field) | |
define_method "#{method}=" do |val| | |
metadata[field] = val | |
end | |
define_method method do | |
metadata[field] | |
end | |
end | |
end | |
# Tests | |
class PostTest < Minitest::Test | |
def test_metadata_fields | |
post = Post.new( | |
metadata_title: 'title', | |
metadata_description: 'description', | |
metadata_number: 1 | |
) | |
assert_equal true, post.save | |
assert_equal 'title', post.metadata_title | |
assert_equal 'description', post.metadata_description | |
end | |
def test_valid_metadata_number | |
post = Post.new(metadata_number: 1) | |
assert_equal true, post.save | |
assert_equal true, post.valid? | |
end | |
def test_valid_negative_metadata_number | |
post = Post.new(metadata_number: -122) | |
assert_equal true, post.save | |
assert_equal true, post.metadata_number.is_a?(Integer) | |
post = Post.new(metadata_number: -122.33) | |
assert_equal true, post.save | |
assert_equal true, post.metadata_number.is_a?(Float) | |
end | |
def test_invalid_metadata_number | |
post = Post.new(metadata_number: 'title') | |
assert_equal false, post.save | |
assert_equal true, post.errors[:metadata_number].any? | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment