{"id":13411811,"url":"https://github.com/byroot/frozen_record","last_synced_at":"2025-04-10T23:28:47.538Z","repository":{"id":14449184,"uuid":"17160802","full_name":"byroot/frozen_record","owner":"byroot","description":"Read only ActiveRecord-like interface to query static YAML files","archived":false,"fork":false,"pushed_at":"2025-01-15T14:06:20.000Z","size":221,"stargazers_count":412,"open_issues_count":7,"forks_count":46,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-03T16:47:03.035Z","etag":null,"topics":["ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byroot.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-02-25T03:55:27.000Z","updated_at":"2025-03-26T10:31:04.000Z","dependencies_parsed_at":"2024-01-03T02:24:31.623Z","dependency_job_id":"79a2889e-87c4-4d69-b7d3-f343f110b036","html_url":"https://github.com/byroot/frozen_record","commit_stats":{"total_commits":213,"total_committers":31,"mean_commits":6.870967741935484,"dds":0.2535211267605634,"last_synced_commit":"25d85a2d8623807f963d87da49ab50b2ef2a227b"},"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byroot%2Ffrozen_record","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byroot%2Ffrozen_record/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byroot%2Ffrozen_record/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byroot%2Ffrozen_record/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byroot","download_url":"https://codeload.github.com/byroot/frozen_record/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248314642,"owners_count":21083096,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ruby"],"created_at":"2024-07-30T20:01:17.161Z","updated_at":"2025-04-10T23:28:47.519Z","avatar_url":"https://github.com/byroot.png","language":"Ruby","readme":"# FrozenRecord\n\n[![Build Status](https://secure.travis-ci.org/byroot/frozen_record.svg)](https://travis-ci.org/byroot/frozen_record)\n[![Gem Version](https://badge.fury.io/rb/frozen_record.svg)](https://badge.fury.io/rb/frozen_record)\n\nActive Record-like interface for **read only** access to static data files of reasonable size.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'frozen_record'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install frozen_record\n\n## Models definition\n\nJust like with Active Record, your models need to inherits from `FrozenRecord::Base`:\n\n```ruby\nclass Country \u003c FrozenRecord::Base\nend\n```\n\nBut you also have to specify in which directory your data files are located.\nYou can either do it globally\n\n```ruby\nFrozenRecord::Base.base_path = '/path/to/some/directory'\n```\n\nOr per model:\n```ruby\nclass Country \u003c FrozenRecord::Base\n  self.base_path = '/path/to/some/directory'\nend\n```\n\nFrozenRecord has two built-in backends, for JSON and YAML.\nBackends are classes that know how to load records from a static file.\n\nThe default backend is YAML and it expects a file that looks like this:\n\n```yaml\n- id: 'se'\n  name: 'Sweden'\n  region: 'Europe'\n  language: 'Swedish'\n  population: 10420000\n- id: 'de'\n  name: 'Germany'\n  region: 'Europe'\n  language: 'German'\n  population: 83200000\n\n# …\n```\n\nYou can also specify a custom backend:\n\n```ruby\nclass Country \u003c FrozenRecord::Base\n  self.backend = FrozenRecord::Backends::Json\nend\n```\n\n### Custom backends\n\nA custom backend must implement the methods `filename` and `load` as follows:\n\n```ruby\nmodule MyCustomBackend\n  extend self\n\n  def filename(model_name)\n    # Returns the file name as a String\n  end\n\n  def load(file_path)\n    # Reads file and returns records as an Array of Hash objects\n  end\nend\n```\n\n## Query interface\n\nFrozenRecord aim to replicate only modern Active Record querying interface, and only the non \"string typed\" ones.\n\n```ruby\n# Supported query interfaces\nCountry.\n  where(region: 'Europe').\n  where.not(language: 'English').\n  where(population: 10_000_000..).\n  order(id: :desc).\n  limit(10).\n  offset(2).\n  pluck(:name)\n\n# Non-supported query interfaces\nCountry.\n  where('region = \"Europe\" AND language != \"English\"').\n  order('id DESC')\n```\n\n### Scopes\n\nBasic `scope :symbol, lambda` syntax is now supported in addition to class method syntax.\n\n```ruby\nclass Country\n  scope :european, -\u003e { where(continent: 'Europe' ) }\n\n  def self.republics\n    where(king: nil)\n  end\n\n  def self.part_of_nato\n    where(nato: true)\n  end\nend\n\nCountry.european.republics.part_of_nato.order(id: :desc)\n```\n\n### Supported query methods\n\n  - where\n  - where.not\n  - order\n  - limit\n  - offset\n\n### Supported finder methods\n\n  - find\n  - first\n  - last\n  - to_a\n  - exists?\n\n### Supported calculation methods\n\n  - count\n  - pluck\n  - ids\n  - minimum\n  - maximum\n  - sum\n  - average\n\n\n## Indexing\n\nQuerying is implemented as a simple linear search (`O(n)`). However if you are using Frozen Record with larger datasets, or are querying\na collection repeatedly, you can define indices for faster access.\n\n```ruby\nclass Country \u003c FrozenRecord::Base\n  add_index :name, unique: true\n  add_index :continent\nend\n```\n\nComposite index keys are not supported.\n\nThe primary key isn't indexed by default.\n\n## Rich Types\n\nThe `attribute` method can be used to provide a custom class to convert an attribute to a richer type.\nThe class must implement a `load` class method that takes the raw attribute value and returns the deserialized value (similar to\n[ActiveRecord serialization](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize)).\n\n```ruby\nclass ContinentString \u003c String\n  class \u003c\u003c self\n    alias_method :load, :new\n  end\nend\n\nSize = Struct.new(:length, :width, :depth) do\n  def self.load(value) # value is lxwxd eg: \"23x12x5\"\n    new(*value.split('x'))\n  end\nend\n\nclass Country \u003c FrozenRecord::Base\n  attribute :continent, ContinentString\n  attribute :size, Size\nend\n```\n\n## Limitations\n\nFrozen Record is not meant to operate on large unindexed datasets.\n\nTo ensure that it doesn't happen by accident, you can set `FrozenRecord::Base.max_records_scan = 500` (or whatever limit makes sense to you), in your development and test environments.\nThis setting will cause Frozen Record to raise an error if it has to scan more than `max_records_scan` records. This property can also be set on a per model basis.\n\n## Configuration\n\n### Reloading\n\nBy 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.\n\nFor such cases you can set `auto_reloading` to `true` either globally or on a model basis:\n\n```ruby\nFrozenRecord::Base.auto_reloading = true # Activate reloading for all models\nCountry.auto_reloading # Activate reloading for `Country` only\n```\n\nIf you need manual reloading, you can call `load_records(force: true)` manually on your class.\n\n## Testing\n\nTesting your FrozenRecord-backed models with test fixtures is made easier with:\n\n```ruby\nrequire 'frozen_record/test_helper'\n\n# During test/spec setup\ntest_fixtures_base_path = 'alternate/fixture/path'\nFrozenRecord::TestHelper.load_fixture(Country, test_fixtures_base_path)\n\n# During test/spec teardown\nFrozenRecord::TestHelper.unload_fixtures\n```\n\nHere's a Rails-specific example:\n\n```ruby\nrequire 'test_helper'\nrequire 'frozen_record/test_helper'\n\nclass CountryTest \u003c ActiveSupport::TestCase\n  setup do\n    test_fixtures_base_path = Rails.root.join('test/support/fixtures')\n    FrozenRecord::TestHelper.load_fixture(Country, test_fixtures_base_path)\n  end\n\n  teardown do\n    FrozenRecord::TestHelper.unload_fixtures\n  end\n\n  test 'countries have a valid name' do\n  # ...\n```\n\n## Contributors\n\nFrozenRecord is a from scratch reimplementation of a [Shopify](https://github.com/Shopify) project from 2007 named `YamlRecord`.\nSo thanks to:\n\n  - John Duff - [@jduff](https://github.com/jduff)\n  - Dennis O'Connor - [@dennisoconnor](https://github.com/dennisoconnor)\n  - Christopher Saunders - [@csaunders](https://github.com/csaunders)\n  - Jonathan Rudenberg - [@titanous](https://github.com/titanous)\n  - Jesse Storimer - [@jstorimer](https://github.com/jstorimer)\n  - Cody Fauser - [@codyfauser](https://github.com/codyfauser)\n  - Tobias Lütke - [@tobi](https://github.com/tobi)\n","funding_links":[],"categories":["Ruby","Gems"],"sub_categories":["Articles"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyroot%2Ffrozen_record","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyroot%2Ffrozen_record","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyroot%2Ffrozen_record/lists"}