├── .github ├── logo-dark.svg ├── logo-light.svg └── workflows │ └── main.yml ├── .gitignore ├── .standard.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console ├── release └── setup ├── db └── migrate │ └── 20241128150951_create_rails_vaults.rb ├── lib ├── generators │ └── rails_vault │ │ ├── add_generator.rb │ │ ├── install_generator.rb │ │ └── templates │ │ └── vault.rb.tt ├── rails_vault.rb └── rails_vault │ ├── base.rb │ ├── engine.rb │ ├── vaults.rb │ └── version.rb └── rails_vault.gemspec /.github/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/logo-light.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Ruby 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | name: Ruby ${{ matrix.ruby }} 14 | strategy: 15 | matrix: 16 | ruby: 17 | - '3.1.0' 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Set up Ruby 22 | uses: ruby/setup-ruby@v1 23 | with: 24 | ruby-version: ${{ matrix.ruby }} 25 | bundler-cache: true 26 | - name: Run Standard 27 | run: bundle exec standardrb 28 | - name: Run the default task 29 | run: bundle exec rake 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /.standard.yml: -------------------------------------------------------------------------------- 1 | ruby_version: 3.1.0 2 | ignore: 3 | - "tmp/**/*" 4 | - "test/**/*" 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec 6 | 7 | group :development do 8 | gem "standard" 9 | end 10 | 11 | group :development, :test do 12 | gem "minitest" 13 | gem "debug", "~> 1.9", ">= 1.9.2" 14 | end 15 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rails_vault (0.1.0) 5 | rails (>= 7.2.2) 6 | store_attribute (>= 1.3) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (7.2.2.1) 12 | actionpack (= 7.2.2.1) 13 | activesupport (= 7.2.2.1) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | zeitwerk (~> 2.6) 17 | actionmailbox (7.2.2.1) 18 | actionpack (= 7.2.2.1) 19 | activejob (= 7.2.2.1) 20 | activerecord (= 7.2.2.1) 21 | activestorage (= 7.2.2.1) 22 | activesupport (= 7.2.2.1) 23 | mail (>= 2.8.0) 24 | actionmailer (7.2.2.1) 25 | actionpack (= 7.2.2.1) 26 | actionview (= 7.2.2.1) 27 | activejob (= 7.2.2.1) 28 | activesupport (= 7.2.2.1) 29 | mail (>= 2.8.0) 30 | rails-dom-testing (~> 2.2) 31 | actionpack (7.2.2.1) 32 | actionview (= 7.2.2.1) 33 | activesupport (= 7.2.2.1) 34 | nokogiri (>= 1.8.5) 35 | racc 36 | rack (>= 2.2.4, < 3.2) 37 | rack-session (>= 1.0.1) 38 | rack-test (>= 0.6.3) 39 | rails-dom-testing (~> 2.2) 40 | rails-html-sanitizer (~> 1.6) 41 | useragent (~> 0.16) 42 | actiontext (7.2.2.1) 43 | actionpack (= 7.2.2.1) 44 | activerecord (= 7.2.2.1) 45 | activestorage (= 7.2.2.1) 46 | activesupport (= 7.2.2.1) 47 | globalid (>= 0.6.0) 48 | nokogiri (>= 1.8.5) 49 | actionview (7.2.2.1) 50 | activesupport (= 7.2.2.1) 51 | builder (~> 3.1) 52 | erubi (~> 1.11) 53 | rails-dom-testing (~> 2.2) 54 | rails-html-sanitizer (~> 1.6) 55 | activejob (7.2.2.1) 56 | activesupport (= 7.2.2.1) 57 | globalid (>= 0.3.6) 58 | activemodel (7.2.2.1) 59 | activesupport (= 7.2.2.1) 60 | activerecord (7.2.2.1) 61 | activemodel (= 7.2.2.1) 62 | activesupport (= 7.2.2.1) 63 | timeout (>= 0.4.0) 64 | activestorage (7.2.2.1) 65 | actionpack (= 7.2.2.1) 66 | activejob (= 7.2.2.1) 67 | activerecord (= 7.2.2.1) 68 | activesupport (= 7.2.2.1) 69 | marcel (~> 1.0) 70 | activesupport (7.2.2.1) 71 | base64 72 | benchmark (>= 0.3) 73 | bigdecimal 74 | concurrent-ruby (~> 1.0, >= 1.3.1) 75 | connection_pool (>= 2.2.5) 76 | drb 77 | i18n (>= 1.6, < 2) 78 | logger (>= 1.4.2) 79 | minitest (>= 5.1) 80 | securerandom (>= 0.3) 81 | tzinfo (~> 2.0, >= 2.0.5) 82 | ast (2.4.2) 83 | base64 (0.2.0) 84 | benchmark (0.4.0) 85 | bigdecimal (3.1.9) 86 | builder (3.3.0) 87 | concurrent-ruby (1.3.5) 88 | connection_pool (2.5.0) 89 | crass (1.0.6) 90 | date (3.4.1) 91 | debug (1.10.0) 92 | irb (~> 1.10) 93 | reline (>= 0.3.8) 94 | drb (2.2.1) 95 | erubi (1.13.1) 96 | globalid (1.2.1) 97 | activesupport (>= 6.1) 98 | i18n (1.14.7) 99 | concurrent-ruby (~> 1.0) 100 | io-console (0.8.0) 101 | irb (1.15.1) 102 | pp (>= 0.6.0) 103 | rdoc (>= 4.0.0) 104 | reline (>= 0.4.2) 105 | json (2.10.1) 106 | language_server-protocol (3.17.0.4) 107 | lint_roller (1.1.0) 108 | logger (1.6.6) 109 | loofah (2.24.0) 110 | crass (~> 1.0.2) 111 | nokogiri (>= 1.12.0) 112 | mail (2.8.1) 113 | mini_mime (>= 0.1.1) 114 | net-imap 115 | net-pop 116 | net-smtp 117 | marcel (1.0.4) 118 | mini_mime (1.1.5) 119 | mini_portile2 (2.8.8) 120 | minitest (5.25.4) 121 | net-imap (0.5.6) 122 | date 123 | net-protocol 124 | net-pop (0.1.2) 125 | net-protocol 126 | net-protocol (0.2.2) 127 | timeout 128 | net-smtp (0.5.1) 129 | net-protocol 130 | nio4r (2.7.4) 131 | nokogiri (1.18.2) 132 | mini_portile2 (~> 2.8.2) 133 | racc (~> 1.4) 134 | parallel (1.26.3) 135 | parser (3.3.7.1) 136 | ast (~> 2.4.1) 137 | racc 138 | pp (0.6.2) 139 | prettyprint 140 | prettyprint (0.2.0) 141 | psych (5.2.3) 142 | date 143 | stringio 144 | racc (1.8.1) 145 | rack (3.1.10) 146 | rack-session (2.1.0) 147 | base64 (>= 0.1.0) 148 | rack (>= 3.0.0) 149 | rack-test (2.2.0) 150 | rack (>= 1.3) 151 | rackup (2.2.1) 152 | rack (>= 3) 153 | rails (7.2.2.1) 154 | actioncable (= 7.2.2.1) 155 | actionmailbox (= 7.2.2.1) 156 | actionmailer (= 7.2.2.1) 157 | actionpack (= 7.2.2.1) 158 | actiontext (= 7.2.2.1) 159 | actionview (= 7.2.2.1) 160 | activejob (= 7.2.2.1) 161 | activemodel (= 7.2.2.1) 162 | activerecord (= 7.2.2.1) 163 | activestorage (= 7.2.2.1) 164 | activesupport (= 7.2.2.1) 165 | bundler (>= 1.15.0) 166 | railties (= 7.2.2.1) 167 | rails-dom-testing (2.2.0) 168 | activesupport (>= 5.0.0) 169 | minitest 170 | nokogiri (>= 1.6) 171 | rails-html-sanitizer (1.6.2) 172 | loofah (~> 2.21) 173 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 174 | railties (7.2.2.1) 175 | actionpack (= 7.2.2.1) 176 | activesupport (= 7.2.2.1) 177 | irb (~> 1.13) 178 | rackup (>= 1.0.0) 179 | rake (>= 12.2) 180 | thor (~> 1.0, >= 1.2.2) 181 | zeitwerk (~> 2.6) 182 | rainbow (3.1.1) 183 | rake (13.2.1) 184 | rdoc (6.12.0) 185 | psych (>= 4.0.0) 186 | regexp_parser (2.10.0) 187 | reline (0.6.0) 188 | io-console (~> 0.5) 189 | rubocop (1.71.2) 190 | json (~> 2.3) 191 | language_server-protocol (>= 3.17.0) 192 | parallel (~> 1.10) 193 | parser (>= 3.3.0.2) 194 | rainbow (>= 2.2.2, < 4.0) 195 | regexp_parser (>= 2.9.3, < 3.0) 196 | rubocop-ast (>= 1.38.0, < 2.0) 197 | ruby-progressbar (~> 1.7) 198 | unicode-display_width (>= 2.4.0, < 4.0) 199 | rubocop-ast (1.38.0) 200 | parser (>= 3.3.1.0) 201 | rubocop-performance (1.23.1) 202 | rubocop (>= 1.48.1, < 2.0) 203 | rubocop-ast (>= 1.31.1, < 2.0) 204 | ruby-progressbar (1.13.0) 205 | securerandom (0.4.1) 206 | standard (1.45.0) 207 | language_server-protocol (~> 3.17.0.2) 208 | lint_roller (~> 1.0) 209 | rubocop (~> 1.71.0) 210 | standard-custom (~> 1.0.0) 211 | standard-performance (~> 1.6) 212 | standard-custom (1.0.2) 213 | lint_roller (~> 1.0) 214 | rubocop (~> 1.50) 215 | standard-performance (1.6.0) 216 | lint_roller (~> 1.1) 217 | rubocop-performance (~> 1.23.0) 218 | store_attribute (2.0.0) 219 | activerecord (>= 6.1) 220 | stringio (3.1.3) 221 | thor (1.3.2) 222 | timeout (0.4.3) 223 | tzinfo (2.0.6) 224 | concurrent-ruby (~> 1.0) 225 | unicode-display_width (3.1.4) 226 | unicode-emoji (~> 4.0, >= 4.0.4) 227 | unicode-emoji (4.0.4) 228 | useragent (0.16.11) 229 | websocket-driver (0.7.7) 230 | base64 231 | websocket-extensions (>= 0.1.0) 232 | websocket-extensions (0.1.5) 233 | zeitwerk (2.6.18) 234 | 235 | PLATFORMS 236 | arm64-darwin-23 237 | x86_64-linux 238 | 239 | DEPENDENCIES 240 | debug (~> 1.9, >= 1.9.2) 241 | minitest 242 | rails_vault! 243 | standard 244 | 245 | BUNDLED WITH 246 | 2.3.3 247 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Rails Designer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rails_vault 2 | 3 | Simple and easy to add settings, preferences and so on to any model. See [an example](#usage). 4 | 5 | 6 | **Sponsored By [Rails Designer](https://railsdesigner.com/)** 7 | 8 | 9 | 10 | 11 | 12 | Rails Designer 13 | 14 | 15 | 16 | Want to make JavaScript your second-favorite language? 👉 [JavaScript for Rails Developers](https://javascriptforrails.com/) 17 | 18 | 19 | ## Installation 20 | 21 | ```bash 22 | bundle add rails_vault 23 | rails generate rails_vault:install 24 | rails db:migrate 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | Generate a vault: 31 | ```bash 32 | rails generate rails_vault:add MODEL::VAULT [field:type field:type] 33 | ``` 34 | 35 | Example: 36 | 37 | ```bash 38 | rails generate rails_vault:add User::Preferences \ 39 | time_zone:string \ 40 | datetime_format:string \ 41 | hotkeys_disabled:boolean 42 | ``` 43 | 44 | This will: 45 | 1. Create a vault class at **app/models/users/preferences.rb** 46 | 2. Add `vault :preferences` to your User model 47 | 48 | ### Define vault attributes 49 | 50 | ```ruby 51 | class User::Preferences < RailsVault::Base 52 | vault_attribute :time_zone, :string, default: "UTC" 53 | vault_attribute :datetime_format, :string, default: "dd-mm-yyyy" 54 | vault_attribute :hotkeys_disabled, :boolean, default: false 55 | end 56 | ``` 57 | 58 | ### Read and write values 59 | 60 | ```ruby 61 | user = User.first 62 | user.create_preferences # => User::Preferences Create (6.3ms) INSERT INTO "rails_vaults" … 63 | user.preferences.time_zone # => "UTC" 64 | user.preferences.hotkeys_disabled? # => false 65 | 66 | user.preferences.update time_zone: "Amsterdam", hotkeys_disabled: true 67 | 68 | user.preferences.time_zone # => "Amsterdam" 69 | user.preferences.hotkeys_disabled? # => true 70 | ``` 71 | 72 | > **Note**: currently **rails_vault** is only tested against PostgreSQL, but PRs for improvements to other databases are appreciated. 73 | 74 | 75 | ## Contributing 76 | 77 | This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `be standardrb` before submitting pull requests. Run tests via `rails test`. 78 | 79 | 80 | ## License 81 | 82 | rails_vault is released under the [MIT License](https://opensource.org/licenses/MIT). 83 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "minitest/test_task" 5 | 6 | Minitest::TestTask.create 7 | 8 | require "standard/rake" 9 | 10 | task default: %i[test standard] 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "rails_vault" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require "irb" 11 | IRB.start(__FILE__) 12 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION=$1 4 | 5 | if [ -z "$VERSION" ]; then 6 | echo "Error: The version number is required." 7 | echo "Usage: $0 " 8 | exit 1 9 | fi 10 | 11 | printf "module RailsVault\n VERSION = \"$VERSION\"\nend\n" > ./lib/rails_vault/version.rb 12 | bundle 13 | git add Gemfile.lock lib/rails_vault/version.rb 14 | git commit -m "Bump version for $VERSION" 15 | git push 16 | git tag v$VERSION 17 | git push --tags 18 | gem build rails_vault.gemspec 19 | gem push "rails_vault-$VERSION.gem" 20 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | -------------------------------------------------------------------------------- /db/migrate/20241128150951_create_rails_vaults.rb: -------------------------------------------------------------------------------- 1 | class CreateRailsVaults < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :rails_vaults do |t| 4 | t.belongs_to :resource, polymorphic: true, null: false 5 | t.string :scope, null: false 6 | 7 | if t.respond_to?(:jsonb) 8 | t.jsonb :payload, null: false, default: {} 9 | else 10 | t.json :payload, null: false, default: {} 11 | end 12 | 13 | t.timestamps 14 | end 15 | 16 | add_index :rails_vaults, :scope 17 | if connection.adapter_name.downcase == "postgresql" 18 | add_index :rails_vaults, :payload, using: :gin 19 | else 20 | add_index :rails_vaults, :payload 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/generators/rails_vault/add_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails/generators/named_base" 4 | 5 | module RailsVault 6 | module Generators 7 | class AddGenerator < Rails::Generators::NamedBase 8 | source_root File.expand_path("../templates", __FILE__) 9 | 10 | argument :attributes, type: :array, default: [], banner: "field:type field:type" 11 | 12 | def create_vault_file 13 | template "vault.rb", File.join("app/models", class_path, "#{file_name}.rb") 14 | end 15 | 16 | def inject_vault_into_model 17 | parent_class = class_name.split("::").first 18 | 19 | inject_into_class "app/models/#{class_path.join("/")}.rb", parent_class do 20 | " vault :#{plural_name}\n" 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/rails_vault/install_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails/generators/base" 4 | 5 | module RailsVault 6 | module Generators 7 | class InstallGenerator < Rails::Generators::Base 8 | source_root File.expand_path("../templates", __FILE__) 9 | 10 | def create_migrations 11 | rails_command "railties:install:migrations FROM=rails_vault", inline: true 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/generators/rails_vault/templates/vault.rb.tt: -------------------------------------------------------------------------------- 1 | class <%= class_name %> < RailsVault::Base 2 | <%- attributes.each do |attribute| -%> 3 | vault_attribute :<%= attribute.name %>, :<%= attribute.type %> 4 | <%- end -%> 5 | end 6 | -------------------------------------------------------------------------------- /lib/rails_vault.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "store_attribute" 4 | require "rails_vault/version" 5 | require "rails_vault/engine" 6 | require "rails_vault/base" 7 | require "rails_vault/vaults" 8 | 9 | module RailsVault 10 | class Error < StandardError; end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rails_vault/base.rb: -------------------------------------------------------------------------------- 1 | module RailsVault 2 | class Base < ActiveRecord::Base 3 | self.table_name = "rails_vaults" 4 | 5 | belongs_to :resource, polymorphic: true 6 | 7 | class << self 8 | def inherited(subclass) 9 | super 10 | 11 | scope_name = subclass.name.demodulize.underscore 12 | 13 | subclass.vault_scope(scope_name) 14 | end 15 | 16 | def vault_scope(scope_name) 17 | default_scope { where(scope: scope_name) } 18 | end 19 | 20 | def vault_attribute(key, *attributes) 21 | options = attributes.extract_options! 22 | 23 | store_attribute :payload, key, *attributes, **options 24 | end 25 | end 26 | 27 | def vault_attributes = payload.keys 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/rails_vault/engine.rb: -------------------------------------------------------------------------------- 1 | module RailsVault 2 | class Engine < ::Rails::Engine 3 | isolate_namespace RailsVault 4 | 5 | initializer "rails_vault.active_record" do 6 | ActiveSupport.on_load(:active_record) do 7 | include RailsVault::Vaults 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rails_vault/vaults.rb: -------------------------------------------------------------------------------- 1 | module RailsVault 2 | module Vaults 3 | extend ActiveSupport::Concern 4 | 5 | class_methods do 6 | def vault(association_name, class_name: nil) 7 | has_one( 8 | association_name, 9 | as: :resource, 10 | class_name: class_name.presence || "#{self}::#{association_name.to_s.camelize}", 11 | dependent: :destroy 12 | ) 13 | end 14 | 15 | def vaults(*association_names) 16 | association_names.each do |association_name| 17 | has_one(association_name, as: :resource, dependent: :destroy) 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/rails_vault/version.rb: -------------------------------------------------------------------------------- 1 | module RailsVault 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /rails_vault.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/rails_vault/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "rails_vault" 7 | spec.version = RailsVault::VERSION 8 | spec.authors = ["Rails Designer Developers"] 9 | spec.email = ["devs@railsdesigner.com"] 10 | spec.summary = "Simple and easy to add settings, preferences and so on to any model." 11 | spec.description = "Rails Vault is a simple to use gem to add settings and preferences to ActiveRecord models. The settings are stored as JSON in a separate table." 12 | spec.homepage = "https://railsdesigner.com/rails-vault/" 13 | spec.license = "MIT" 14 | 15 | spec.metadata["homepage_uri"] = spec.homepage 16 | spec.metadata["source_code_uri"] = "https://github.com/Rails-Designer/rails_vault/" 17 | 18 | spec.files = Dir["{bin,db,lib}/**/*", "Rakefile", "README.md", "rails_vault.gemspec", "Gemfile", "Gemfile.lock"] 19 | 20 | spec.required_ruby_version = ">= 3.1.0" 21 | spec.add_dependency "rails", ">= 7.2.2" 22 | spec.add_dependency "store_attribute", ">= 1.3" 23 | end 24 | --------------------------------------------------------------------------------