FixedRecord
FixedRecord provides ActiveRecord-like read-only access to a set of records described in a YAML file.
Why is this useful? Occasionally you have tabular data which hardly ever changes and can easily be edited by hand. Although this data could be placed in a database, it may not be worth the overhead involved (loading a database, maintaining database code, etc.).
It may be quicker and simpler to implement this as an array or hash of objects in a YAML file, and use this gem to provide access to the data.
See the Usage section below.
Installation
Add this line to your application's Gemfile:
gem 'fixed_record'
And then execute:
$ bundle
Or install it yourself as:
$ gem install fixed_record
Usage
Array of Records
Create a YAML file defining an array of records like this:
-
name: Risky Thinking
url: https://www.riskythinking.com/
-
name: Krebs on Security
url: https://krebsonsecurity.com/
Then to load these, create a class
require 'fixed_record'
class MyFavoriteWebsite < FixedRecord
data "#{Rails.root}/data/my_favorite_websites.yml", required: [:name, :url], optional: [:title]
# Return hostname of url for company
def hostname
URI.parse(url).host
end
end
The collection can then be enumerated as required:
MyFavoriteWebsite.each do |w|
puts w.name
puts w.hostname
end
Or can be accessed as an array:
MyFavoriteWebsite.all.is_a?(Array) # true
A count of the number of records is available:
puts MyFavoriteWebsite.count
As is the filename and the set of valid field names:
puts MyFavoriteWebsite.filename # .../my_favorite_websites.yml
puts MyFavoriteWebsite.valid_keys.include?( :name ) # true
Value presence
It's possible to check whether a value is supplied for an optional field for an item:
puts MyFavoriteWebsite.all.first.present?(:title) # true
This makes it possible to distinguish between an optional value being omitted and explicitly being given the value nil.
The declared class will also include all the methods from the Enumerable module.
Hash of Records
Create a YAML file my_web_pages.yml defining a hash of records like this:
StaticPage#first:
title: First Page
description: Welcome to the First Page
StaticPage#last:
title: Last Page
short_title: LastP
description: Welcome to the Last Page
Then to load these, create a class
require 'fixed_record'
class MyWebPages < FixedRecord
data "#{Rails.root}/data/my_web_pages.yml", required: [:title,:description], optional: [:short_title]
end
The collection can be accessed by index:
MyWebPages['StaticPage#first'].title # First Page
MyWebPages['StaticPage#last'].description # Welcome to he Last page
MyWebPages['StaticPage#first'].key # StaticPage#fifst
The collection can then be enumerated as required:
MyWebPages.each do |k,v|
puts k
puts v.title
end
Or can be accessed as an hash:
MyWebPages.all.is_a?(Hash) # true
A count of the number of records is available:
puts MyWebPages.count
The declared class will also include all the methods from the Enumerable module.
Singleton Record
Create a YAML file site_settings.yml defining a single records like this:
sessionExpiry: 3600
phone: +1.613.555.1212
address: 500 Main Street, Anytown, Antartica
Then to load these, create a class
require 'fixed_record'
class SiteSettings < FixedRecord
data "#{Rails.root}/data/site_settings.yml", singleton: true
end
The values can be accessed by index:
SiteSettings['phone'] # '+1.613.555.1212'
SiteSettings['address'] # '500 Main Street, Anytown, Antartica'
SiteSettings[:phone] # This works too
The required and optional arguments can be used to check for inadvertent errors being introduced into the YAML file. An ArgumentError will be raised if the name is not defined in the file and is not declared as optional.
Error Checking
Some basic sanity checks are performed on the YAML file to catch common errors:
- It must define a non-empty array or hash of records
- If the optional
required:oroptional:arguments are given, then each record must have all the required fields and may also have any of the optional fields. - If niether the
required:oroptional:arguments are given, then each record must have the same set of fields.
An ArgumentError exception will be thrown if any validation errors are detected. A system-dependent error (probably Errno::ENOENT) will be thrown if the file cannot be read.
Additional validations can be performed by defining a validate method. e.g.
require 'fixed_record'
require 'uri'
class MyFavoriteWebsite < FixedRecord
data "#{Rails.root}/data/my_favorite_websites.yml", required: [:name, :url]
# Check that the url can be parsed
def validate( values, index )
begin
URI.parse(url)
rescue URI::InvalidURIError -> e
raise e.class, "#{filename} index #{index} has invalid url: #{e.message}"
end
end
end
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in fixed_record.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/m-z-b/fixed_record.