ActiveRecord-like interface for read only access to static data files.
Add this line to your application's Gemfile:
gem 'frozen_record'
And then execute:
$ bundle
Or install it yourself as:
$ gem install frozen_record
Just like with ActiveRecord, your models need to inherits from FrozenRecord::Base
:
class Country < FrozenRecord::Base
end
But you also have to specify in which directory your data files are located. You can either do it globaly
FrozenRecord::Base.base_path = '/path/to/some/directory'
Or per model:
class Country < FrozenRecord::Base
self.base_path = '/path/to/some/directory'
end
You can also specify a custom backend. Backends are classes that know how to load records from a static file. By default FrozenRecord expects an YAML file, but this option can be changed per model:
class Country < FrozenRecord::Base
self.backend = FrozenRecord::Backends::Json
end
A custom backend must implement the methods filename
and load
as follows:
module MyCustomBackend
extend self
def filename(model_name)
# Returns the file name as a String
end
def load(file_path)
# Reads file and returns records as an Array of Hash objects
end
end
FrozenRecord aim to replicate only modern ActiveRecord querying interface, and only the non "string typed" ones.
e.g
# Supported query interfaces
Country.
where(region: 'Europe').
where.not(language: 'English').
order(id: :desc).
limit(10).
offset(2).
pluck(:name)
# Non supported query interfaces
Country.
where('region = "Europe" AND language != "English"').
order('id DESC')
Basic scope :symbol, lambda
syntax is now supported in addition to class method syntax.
class Country
scope :european, -> { where(continent: 'Europe' ) }
def self.republics
where(king: nil)
end
def self.part_of_nato
where(nato: true)
end
end
Country.european.republics.part_of_nato.order(id: :desc)
- where
- where.not
- order
- limit
- offset
- find
- first
- last
- to_a
- exists?
- count
- pluck
- ids
- minimum
- maximum
- sum
- average
Querying is implemented as a simple linear search (O(n)
). However if you are using Frozen Record with larger datasets, or are querying
a collection repetedly, you can define indices for faster access.
class Country < FrozenRecord::Base
add_index :name, unique: true
add_index :continent
end
Composite index keys are not supported.
The primary key isn't indexed by default.
By default the YAML files are parsed once and then cached in memory. But in development you might want changes to be reflected without having to restart your application.
For such cases you can set auto_reloading
to true
either globally or on a model basis:
FrozenRecord::Base.auto_reloading = true # Activate reloading for all models
Country.auto_reloading # Activate reloading for `Country` only
Testing your FrozenRecord-backed models with test fixtures is made easier with:
require 'frozen_record/test_helper'
# During test/spec setup
test_fixtures_base_path = 'alternate/fixture/path'
FrozenRecord::TestHelper.load_fixture(Country, test_fixtures_base_path)
# During test/spec teardown
FrozenRecord::TestHelper.unload_fixtures
Here's a Rails-specific example:
require "test_helper"
require 'frozen_record/test_helper'
class CountryTest < ActiveSupport::TestCase
setup do
test_fixtures_base_path = Rails.root.join(%w(test support fixtures))
FrozenRecord::TestHelper.load_fixture(Country, test_fixtures_base_path)
end
teardown do
FrozenRecord::TestHelper.unload_fixtures
end
test "countries have a valid name" do
# ...
FrozenRecord is a from scratch reimplementation of a Shopify project from 2007 named YamlRecord
.
So thanks to:
- John Duff - @jduff
- Dennis O'Connor - @dennisoconnor
- Christopher Saunders - @csaunders
- Jonathan Rudenberg - @titanous
- Jesse Storimer - @jstorimer
- Cody Fauser - @codyfauser
- Tobias Lütke - @tobi