Has-Meta

A key/value store solution for Rails apps with bloated tables

Build Status

Installation

Add this line to your application's Gemfile:

gem 'has-meta'

And then execute:

$ bundle

Or install it yourself as:

$ gem install has-meta

Then, install migrations:

rake has_meta_engine:install:migrations

Finally, review the migrations and migrate:

rails db:migrate

Usage

Declaring Meta Attributes

Suppose we have a model Part and only a minority of our parts records have the attribute catalog_number populated. We want to move catalog_number to our key/value store.

To create a new meta attribute on an Active Record model, add this line to your model:

has_meta :catalog_number

Or specify multiple meta attributes on the model:

has_meta :catalog_number, :other_attribute

You may also choose to migrate existing data from a table:

$ rake "has_meta_engine:data_mover[parts, catalog_number, integer, catalog_number]"

Once that data is moved generate a migration to remove the column and run the migration:

$ rake generate migration RevmoveCatalogNumberFromParts catalog_number:integer
$ rake db:migrate

And finally, declare the meta attribute in the model

has_meta :catalog_number

Getting and Setting Meta Attributes

Now, we can use normal getters and setters to access the attribute:

new_part = Part.create name: 'Fancy new part'  
new_part.catalog_number = 12345  
new_part.save

new_part.catalog_number  
# => 12345

You can update the attribute any way you would with other attributes managed by Active Record:

new_part.update catalog_number: 67890  
new_part.catalog_number # => 67890  

new_part.attributes = {catalog_number: 12345}  
new_part.catalog_number # => 12345  

Meta attributes may also represent an Active Record model. Perhaps some of our parts may conform to a uniform standard represented by class Standard. Just declare the meta attribute :standard and has-meta will treat the meta attribute as a one-to-one relation if the attribute corresponds to an Active Record model in your app.

has_meta :catalog_number, :standard

Now you can get or set the attribute using either object or the object id as you would with any other attribute:

new_standard = Standard.create name: 'Some great standard'  
new_part.standard = new_standard  
new_part.standard # => #<Standard id: 1, name: "Some great standard">  
new_part.standard_id # => 1  

newer_standard = Standard.create name 'An even better standard'  
new_part.standard.id = newer_standard.id  
new_part.standard # => #<Standard id: 2, name: "An even better standard">  
new_part.standard_id # => 2  

Finding by meta attributes

find_by_attribute_name methods are provided for meta attributes. For attributes representing an Active Record model, use find_by_attribute_id:

Part.find_by_catalog_number 12345  
# => #<Part id: 1, name: "Fancy new part">

Part.find_by_standard_id 2  
# => #<Part id: 1, name: "Fancy new part">

You may also use with_meta method to return a scope of parts with correspoding meta attribute values:

another_part = Part.create name: 'Another fancy new part'
another_part.update standard: new_standard

Part.with_meta standard: new_standard
# => #<ActiveRecord::Relation [#<Part id: 1, name: "Fancy new part">, 
    #<Part id: 2, name: "Another fancy new part">]>

with_meta accepts the any: true option to match any condition provided:

yet_another_part = Part.create name: 'Yet another fancy new part'
yet_another_part.update catalog_number: 12345


Part.with_meta({standard: new_standard, catalog_number: 12345}, any: true)
# => #<ActiveRecord::Relation [#<Part id: 1, name: "Fancy new part">, 
  #<Part id: 2, name: "Another fancy new part">, 
  #<Part id: 3, name: "Yet another fancy new part">]>

Calling excluding_meta will return all records not meeting the criteria:

Part.excluding_meta catalog_number: 12345
# => #<ActiveRecord::Relation [#<Part id: 2, name: "Another fancy new part">]>

TODO/Known Issues

has-meta was developed for Active Record 4.2+ and MySQL 5.5. PRs for supporting earlier versions of Active Record and/or PostgreSQL are welcome!

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/protrainings/has-meta.

License

Has-Meta is available as open source under the terms of the MIT License.

About ProTrainings

Has-Meta was written by Dan Drust. It is maintained and funded by Protrainings, LLC. Has-Meta, names, and logos are copyright ProTrainings, LLC.