Mechanical
Simple gem which allows to store multiple models in a single table in DB (Postgres only). It's using STI and Postgres JSONB data type.
The whole schema is defined in one initializer file, and new attributes could be easily added just by adding new attribute in that file. So you don't need to create a migration.
Usage
- add in the Gemfile
gem "mechanical"
- execute
bundle install
- execute
rails g mechanical initializer
- execute
rake db:migrate
- edit
config/initializers/mechanical.rb
and define your schema - start app
rails s
- you can use models
Mechanical::Model::MODEL_NAME
in you app like a regular model.
You need to define attribute and it's type (it's importan to check this gem: https://github.com/madeintandem/jsonb_accessor). In addition you can specify relations, validations, add instance or class methods. Behavior is the same as with AR model.
Mechanical.setup do |config|
model "Account" do
field :name, type: :string
field :balance, type: :integer
has_many :posts
validates :name, presence: true
validates :balance, numericality: true
end
model "Post" do
field :title, type: :string
field :description, type: :text
field :published_on, type: :date
field :account_id, type: :integer
field :active, type: :boolean, default: true
belongs_to :account, optional: true
has_one_attached :file
validates :title, presence: true
scope :active, -> { jsonb_where(:mechanical_data, active: true) }
after_create :say_hello
add_methods do
def self.total_posts
self.count
end
def title_uppercase
title&.upcase
end
def say_hello
puts "HELLO FROM initializer #{self}"
end
end
end
end
After Installation you can do the following:
post = Mechanical::Model::Post.create(title: "Hello world", description: "this is description")
post.persisted? # true
post.title # "Hello world"
post.description # "this is description"
# for querying use: https://github.com/madeintandem/jsonb_accessor
Mechanical::Model::Post.create(title: "ABC", description: "this is description")
Mechanical::Model::Post.create(title: "TRE", description: "this is second description")
Mechanical::Model::Account.create(name: "XYZ", balance: 100)
Mechanical::Model::Account.create(name: "MTR", balance: 200)
Mechanical::Model::Post.jsonb_where(:mechanical_data, { title: 'ABC' }).count # 1
Mechanical::Model::Account.mechanical_data_where(balance: { greater_than_or_equal_to: 150 }).count # 1
Mechanical::Model::Account.mechanical_data_where(balance: 10..250).count # 2
If you are building form and want to use this model you can do it same way as using regular model:
Example of very simple controller:
def index
@post = Mechanical::Model::Post.new
end
def submit_post
@post = Mechanical::Model::Post.new(params.require(:post).permit!)
@post.user = User.first
@post.save
if @post.save
redirect_to root_path
else
@posts = Mechanical::Model::Post.all
render :index
end
end
View:
<%= simple_form_for @post, url: submit_post_url, html: { novalidate: true } do |f| %>
<%= f.error_messages %>
<%= f.association :account %>
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :published_on %>
<%= f.input :file %>
<%= f.input :active %>
<br/>
<%= f.submit %>
<% end %>
As you can see it supports active storage.
Installation
Add this line to your application's Gemfile:
gem 'mechanical'
And then execute:
$ bundle
Contributing
You are welcome to contribute.
License
The gem is available as open source under the terms of the MIT License.