• Stars
    star
    184
  • Rank 209,187 (Top 5 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 12 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Implementation of EAV pattern for ActiveRecord models

Looking for contributors

Unfortunately, I don't have enough time to work on this gem now. If you feel you can contribute to it, please, reach me via email, and I'll grant permissions to this project.

hydra_attribute

Gem Version Build Status Coverage Status Code Climate Dependency Status Inline docs

Demo | Wiki | RDoc

hydra_attribute is an implementation of EAV (Entity-Attribute-Value) pattern for ActiveRecord models. It allows to create or remove attributes in runtime. Also each record may have different sets of attributes, for example: Product with ID 1 can have different set of attributes than Product with ID 2.

Notice

Until the first major version is released:

  • each new minor version doesn't guarantee back compatibility with previous one

Requirements

  • ruby >= 1.9.2
  • active_record >= 3.2

Installation

Add the following line to Gemfile:

gem 'hydra_attribute'
# or for rails 4
# gem 'hydra_attribute', github: 'kostyantyn/hydra_attribute', branch: 'rails4'
# or for rails 4.1
# gem 'hydra_attribute', github: 'kostyantyn/hydra_attribute', branch: 'rails4.1'

and run bundle install from your shell.

Then we should generate our migration:

rails generate migration create_hydra_attributes

The content should be:

class CreateHydraAttributeTables < ActiveRecord::Migration
  def up
    create_hydra_entity :products do |t|
      # add here all other columns that should be in the entity table
      t.timestamps
    end
  end
      
  def down
    drop_hydra_entity :products
  end
end

or if we have the entity table already

class CreateHydraAttributeTables < ActiveRecord::Migration
  def up
    migrate_to_hydra_entity :products
  end
      
  def down
    rollback_from_hydra_entity :products
  end
end

Usage

Create model

rails generate model Product --migration=false
rake db:migrate

and include HydraAttribute::ActiveRecord to Product class

class Product < ActiveRecord::Base
  include HydraAttribute::ActiveRecord
end

Create hydra attributes

Product.hydra_attributes.create(name: 'color', backend_type: 'string', default_value: 'green')
Product.hydra_attributes.create(name: 'title', backend_type: 'string')
Product.hydra_attributes.create(name: 'total', backend_type: 'integer', default_value: 1)

Creating method accepts the following options:

  • name. The required parameter. Any string is allowed.

  • backend_type. The required parameter. One of the following strings is allowed: string, text, integer, float, boolean and datetime.

  • default_value. The optional parameter. Any value is allowed. nil is default.

  • white_list. The optional parameter. Should be true or false. false is default. If white_list: true is passed, this attribute will be added to white list and will be allowed for mass-assignment. This parameter is in black list for creation by default so if you want to pass it, you have to pass the role as: :admin too.

      Product.hydra_attributes.create({name: 'title', backend_type: 'string', white_list: true}, as: :admin)

Create records

Product.create
#<Product id: 1, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "green", title: nil, total: 1>
Product.create(color: 'red', title: 'toy')
#<Product id: 2, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "red", title: "toy", total: 1>
Product.create(title: 'book', total: 2)
#<Product id: 3, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "green", title: "book", total: 2>

Add new hydra attribute in runtime

Product.hydra_attributes.create(name: 'price', backend_type: 'float', default_value: 0.0)
Product.create(title: 'car', price: 2.50)
#<Product id: 4, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "green", title: "car", total: 2, price: 2.5>

Create hydra set

Hydra set allows to set the unique attribute list for each entity.

hydra_set = Product.hydra_sets.create(name: 'Default')
hydra_set.hydra_attributes = Product.hydra_attributes.where(name: %w(color title price))

Product.create(color: 'black', title: 'ipod', price: 49.95, total: 5) do |product|
  product.hydra_set_id = hydra_set.id
end
#<Product id: 5, hydra_set_id: 1, created_at: ..., updated_at: ..., color: "black", title: "ipod", price: 49.95>

Notice: the total attribute has been skipped because it doesn't exist in hydra set.

Obtain data

Product.where(color: 'red')
# [#<Product id: 2, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "red", title: "toy", price: 0.0, total: 1>]
Product.where(color: 'green', price: nil)
# [
    #<Product id: 1, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "green", title: nil, price: 0.0, total: 1>,
    #<Product id: 3, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "green", title: "book", price: 0.0, total: 2>
# ]

Notice: the attribute price has been added in runtime. Records that had been created before this attribute don't have it therefore they satisfy the following condition: where(price: nil)

Order data

Product.order(:color, :title).first
#<Product id: 5, hydra_set_id: 1, created_at: ..., updated_at: ..., color: "black", title: "ipod", price: 49.95>
Product.order(:color, :title).reverse_order.first
#<Product id: 2, hydra_set_id: nil, created_at: ..., updated_at: ..., color: "red", title: "toy", price: 0.0, total: 1>

Select concrete attributes

Product.select([:color, :title])
# [
    #<Product id: 1, hydra_set_id: nil, color: "green", title: nil>,
    #<Product id: 2, hydra_set_id: nil, color: "red", title: "toy">,
    #<Product id: 3, hydra_set_id: nil, color: "green", title: "book">,
    #<Product id: 4, hydra_set_id: nil, color: "green", title: "car">,
    #<Product id: 5, hydra_set_id: 1, color: "black", title: "ipod">
# ] 

Notice: id and hydra_set_id attributes are forcibly added because they are important for correct work.

Group by attribute

Product.group(:color).count
# {"black"=>1, "green"=>3, "red"=>1}

Wiki Docs

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request