MongoDB recommends the schema versioning pattern to enable downstream consumers of data records understand its contents.
https://www.mongodb.com/blog/post/building-with-patterns-the-schema-versioning-pattern
schema_versioning_mongoid
is a gem that helps you maintain and version your MongoDB schemas in a Rails application. It allows you to automatically update or insert SCHEMA_VERSION
constants in your Mongoid models to help track changes.
- Auto-generation of
SCHEMA_VERSION
constant in your Mongoid models. - Centralized tracking of schema versions using a YAML file.
- Support for both inline and centralized schema versioning strategies.
Add this line to your application's Gemfile:
gem 'schema_versioning_mongoid'
And then execute:
$ bundle install
The inline strategy is available should you want your SCHEMA_VERSION to be a visible constant inside each model for added visibility and awareness. Though, this may be cumbersome for some.
To use the centralized strategy, you will need to create a YAML file to store the schema versions. This can be placed anywhere in your application, although we recommend storing it in the db or config directory. For example, db/schema_versions_centralized.yml.
Sample YAML Content
User: "some-uuid"
Product: "another-uuid"
Add the following initializer code to populate the SCHEMA_VERSION constants from the centralized YAML file. Create a new file config/initializers/mongoid_document_extension.rb
and add the following lines:
# config/initializers/mongoid_document_extension.rb
module Mongoid
module Document
def self.load_schema_versions_from_yaml(file_path)
centralized_schema_versions = safely_load_yaml(file_path)
return unless centralized_schema_versions
safely_set_constants(centralized_schema_versions)
end
def self.safely_load_yaml(file_path)
YAML.load_file(file_path).tap do |result|
puts("No Schema Version records at #{file_path}. Skipping...") unless result
end
rescue Errno::ENOENT, Psych::SyntaxError => e
puts("An error occurred while loading YAML from #{file_path}: #{e.message}")
nil
end
def self.safely_set_constants(centralized_schema_versions)
centralized_schema_versions.each do |klass_name, uuid|
begin
klass = klass_name.constantize
klass.const_set("SCHEMA_VERSION", uuid) unless klass.const_defined?("SCHEMA_VERSION")
rescue NameError => e
puts("Could not find class #{klass_name}: #{e.message}")
end
end
end
def self.included(base)
return unless base.is_a?(Class)
base.field :schema_version, type: String
base.before_create :set_schema_version
base.before_update :set_schema_version
base.send(:define_method, :set_schema_version) do
schema_version_constant = self.class.const_get("SCHEMA_VERSION", false) rescue nil
self.schema_version = schema_version_constant if schema_version_constant
end
end
end
end
begin
Mongoid::Document.load_schema_versions_from_yaml(Rails.root.join("db", "schema_versions_centralized.yml"))
rescue => e
puts("An error occurred while initializing Mongoid::Document: #{e.message}")
end
run the rake tasks below, for example: rake schema_version:run_all reject_patterns='concerns,history_tracker'
This task checks if the schema versions in your Mongoid models match the ones in the centralized YAML file. This is particularly useful in CI/CD pipelines to ensure that your schemas are up-to-date.
If you're using the centralized approach for schema versioning, this task will initialize a YAML file to store all the schema versions.
rake db:mongoid:init_schema_versioning
This task initializes an empty schema_versions.yml file. This file will hold the schema versions for each Mongoid model in your application.
Run the task as follows:
rake db:mongoid:init_schema_versioning
rake db:mongoid:check_schema_version[relative_path]
This task checks if the schema of a specified Mongoid model matches the schema listed in schema_versions.yml.
relative_path
: The relative path to the model's Ruby file, starting from the models directory.
Example:
rake db:mongoid:check_schema_version[user.rb]
rake db:mongoid:set_schema_version[relative_path]
Sets or updates the SCHEMA_VERSION constant for a specified Mongoid model.
relative_path
: The relative path to the model's Ruby file, starting from the app/models/ directory.
Example:
rake db:mongoid:set_schema_version[user.rb]
rake db:mongoid:update_schema_versions[reject_patterns]
This task will run the check_schema_version and set_schema_version tasks for all Mongoid models in your application, excluding models that match any of the patterns specified.
reject_patterns: Comma-separated string of patterns to reject. These should be substrings that might appear in the file path of models you wish to exclude.
Example:
rake db:mongoid:update_schema_versions reject_patterns='concerns,history_tracker,api_token'
rake db:mongoid:validate_schema_versions
This task will validate the schema of all Mongoid models against schema_versions.yml without modifying them.
Example:
rake db:mongoid:validate_schema_versions
This tool is bundled as a Rake task within your Rails application. Make sure you have updated to the latest version of the application that includes this tool.
- Ruby on Rails environment
- MongoDB
- Access to
db/schema_versions_centralized.yml
anddb/schema_versions.yml
files.
Run the Rake task using the following command to validate schema versions and display differences:
rake db:mongoid:validate_schema_versions_with_diff MODEL=YourModelName
Replace YourModelName
with the model name you'd like to visualize.
The output will provide you with a detailed comparison between the current and historical versions of the specified model, formatted similarly to GitHub diffs:
Differences for model: YourModelName
Comparing with Version 1 [timestamp uuid]:
+ added_field_1
+ added_field_2
- removed_field
~ changed_field
- Lines with
+
indicate fields that have been added in the current version. - Lines with
-
indicate fields that have been removed in the current version. - Lines with
~
indicate fields whose types have changed in the current version.
- Initialize schema versioning by running
rake db:mongoid:init_schema_versioning
. - Set or update the schema version for individual models with
rake db:mongoid:set_schema_version[relative_path]
. - Check if a model's schema matches the schema version with
rake db:mongoid:check_schema_version[relative_path]
. - Update the schema versions for all models with
rake db:mongoid:update_schema_versions[reject_patterns]
. - Validate all models' schemas with
rake db:mongoid:validate_schema_versions
. - Visualize schemas version difference with
rake db:mongoid:diff
.
Add this to your test suite to validate all the schema is update to date.
# spec/rails_helper.rb
RSpec.configure do |config|
config.before(:suite) do
if !SchemaVersioningMongoid::RSpecHelper.check_schemas_and_uuids('concerns,history_tracker')
puts "\nERROR: Schemas and/or UUIDs are not up-to-date!\n"
exit(1)
end
end
- Fork it ( https://github.com/offerzen/schema_versioning_mongoid/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
The gem is available as open-source under the terms of the MIT License.