FrozenRecord

repository·master·Indexed 19 days ago

https://github.com/byroot/frozen_record

A Ruby library providing an Active Record-like interface for read-only access to static data files stored in JSON or YAML. It features a non-string-typed querying interface (where, order, limit, offset), finder methods, calculation methods, and support for custom backends. Key capabilities include indexing for faster lookups, attribute deserialization for rich types, automatic reloading for development, and scan limits to prevent performance issues with large datasets.

Tokens
5.4K
Snippets
28
Records
30
Agent score
66%

What's inside frozen_record

  1. Test FrozenRecord models with fixtures

    master

    Use FrozenRecord::TestHelper to load and unload test fixtures during your test suite setup and teardown.

    require 'frozen_record/test_helper'
    
    # Setup
    FrozenRecord::TestHelper.load_fixture(Country, 'path/to/fixtures')
    
    # Teardown
    FrozenRecord::TestHelper.unload_fixtures
  2. Define FrozenRecord models

    master

    Models must inherit from FrozenRecord::Base. You must specify a base_path where your data files are located. This can be set globally for all models or specifically for each model class.

    FrozenRecord includes built-in backends for YAML (default) and JSON.

    class Country < FrozenRecord::Base
    end
    
    # Global base path
    FrozenRecord::Base.base_path = '/path/to/some/directory'
    
    # Per-model base path
    class Country < FrozenRecord::Base
      self.base_path = '/path/to/some/directory'
    end
  3. Configure record scanning limits

    master

    To prevent accidental performance issues with large unindexed datasets, you can set a limit on how many records FrozenRecord is allowed to scan. If a query exceeds this limit, an error is raised.

    This can be set globally or per model using max_records_scan.

    FrozenRecord::Base.max_records_scan = 500
  4. Enable automatic reloading for development

    master

    By default, data files are parsed once and cached in memory. To reflect changes in your data files without restarting your application, enable auto_reloading.

    • Set auto_reloading = true globally to affect all models.
    • Set auto_reloading on a specific model class to affect only that model.
    • For manual reloading, call load_records(force: true) on the model class.
    FrozenRecord::Base.auto_reloading = true
    # Or per model
    Country.auto_reloading = true
  5. Use the YAML backend for FrozenRecord

    master

    The FrozenRecord::Backends::Yaml module provides functionality for loading data from YAML files into FrozenRecord models. It supports both standard .yml files and .erb files (Embedded Ruby) for dynamic data generation.

    ERB Support and Naming

    • Standard YAML: Files ending in .yml are loaded directly.
    • ERB Files: Files ending in .erb are processed through the ERB engine before being parsed as YAML.
    • Naming Convention: To avoid deprecation warnings, if your YAML file contains ERB tags, ensure the file extension is explicitly .erb.

    Configuration for Legacy ERB Behavior

    If you are using files that contain ERB tags but do not have the .erb extension, you can enable legacy support using the deprecated_yaml_erb_backend setting. However, it is recommended to rename these files to include the .erb extension and set FrozenRecord.deprecated_yaml_erb_backend = false to align with future behavior.

    # Example of how the backend handles file loading logic
    # If using ERB, ensure the file is named with .erb extension
    # or configure the deprecated backend setting:
    FrozenRecord.deprecated_yaml_erb_backend = true
  6. Integrate FrozenRecord with Rails

    master
    FrozenRecord includes a Railtie that automatically integrates with Rails. When used in a Rails application, it registers an initializer that adds the FrozenRecord namespace to config.eager_load_namespaces. This ensures that all FrozenRecord-related classes and modules are properly eager-loaded during the Rails boot process.
  7. Define Scopes in models

    master

    You can define scopes using either the scope :symbol, lambda syntax or by defining class methods. Scopes allow you to chain reusable query logic.

    class Country < FrozenRecord::Base
      scope :european, -> { where(continent: 'Europe' ) }
    
      def self.republics
        where(king: nil)
      end
    end
    
    # Usage
    Country.european.republics
  8. Add indices for faster querying

    master

    By default, querying is a linear search (O(n)). For larger datasets or frequent queries, you can define indices using add_index.

    Limitations:

    • Composite index keys are not supported.
    • The primary key is not indexed by default.
    class Country < FrozenRecord::Base
      add_index :name, unique: true
      add_index :continent
    end
  9. Configure custom backends

    master

    You can use a custom backend by assigning a class to self.backend. A custom backend must implement two methods:

    1. filename(model_name): Returns the filename as a String.
    2. load(file_path): Reads the file and returns records as an Array of Hash objects.
    class Country < FrozenRecord::Base
      self.backend = FrozenRecord::Backends::Json
    end
    
    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
  10. Use Rich Types with the attribute method

    master

    The attribute method allows you to convert raw data into a custom class. The target class must implement a self.load(value) method that takes the raw value and returns the deserialized object.

    class Size = Struct.new(:length, :width, :depth) do
      def self.load(value)
        new(*value.split('x'))
      end
    end
    
    class Country < FrozenRecord::Base
      attribute :size, Size
    end
  11. Use the FrozenRecord query interface

    master

    FrozenRecord provides a modern, non-string-typed Active Record-like querying interface.

    Supported query methods:

    • where(hash_or_range)
    • where.not(hash)
    • order(column: direction)
    • limit(n)
    • offset(n)

    Supported finder methods:

    • find, first, last, to_a, exists?

    Supported calculation methods:

    • count, pluck, ids, minimum, maximum, sum, average

    Note: String-based queries (e.g., where('region = "Europe"')) are not supported.

    Country.
      where(region: 'Europe').
      where.not(language: 'English').
      where(population: 10_000_000..).
      order(id: :desc).
      limit(10).
      offset(2).
      pluck(:name)