{"id":13879596,"url":"https://github.com/palkan/store_attribute","last_synced_at":"2025-05-14T06:13:02.169Z","repository":{"id":9327998,"uuid":"61708674","full_name":"palkan/store_attribute","owner":"palkan","description":"ActiveRecord extension which adds typecasting to store accessors","archived":false,"fork":false,"pushed_at":"2025-05-09T16:36:47.000Z","size":150,"stargazers_count":413,"open_issues_count":1,"forks_count":17,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-09T16:51:28.347Z","etag":null,"topics":["activerecord","hacktoberfest","store-accessors"],"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/palkan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null},"funding":{"github":"palkan"}},"created_at":"2016-06-22T10:00:00.000Z","updated_at":"2025-05-09T16:36:45.000Z","dependencies_parsed_at":"2025-01-23T05:04:38.827Z","dependency_job_id":"c5d8bb2e-c2a4-4693-a283-f00ae3e6a6f1","html_url":"https://github.com/palkan/store_attribute","commit_stats":{"total_commits":113,"total_committers":13,"mean_commits":8.692307692307692,"dds":"0.23008849557522126","last_synced_commit":"17bab1b664a4a200529e9a9505a6b8e1e194d478"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palkan%2Fstore_attribute","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palkan%2Fstore_attribute/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palkan%2Fstore_attribute/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palkan%2Fstore_attribute/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/palkan","download_url":"https://codeload.github.com/palkan/store_attribute/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254080865,"owners_count":22011514,"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":["activerecord","hacktoberfest","store-accessors"],"created_at":"2024-08-06T08:02:26.222Z","updated_at":"2025-05-14T06:13:02.163Z","avatar_url":"https://github.com/palkan.png","language":"Ruby","funding_links":["https://github.com/sponsors/palkan"],"categories":["Ruby","Gems"],"sub_categories":["Attributes Management"],"readme":"[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/store-attribute-defaults.html#task)\n[![Gem Version](https://badge.fury.io/rb/store_attribute.svg)](https://rubygems.org/gems/store_attribute)\n![Build](https://github.com/palkan/store_attribute/workflows/Build/badge.svg)\n\n## Store Attribute\n\nActiveRecord extension which adds typecasting to store accessors.\n\nOriginally extracted from not merged PR to Rails: [rails/rails#18942](https://github.com/rails/rails/pull/18942).\n\n### Install\n\nIn your Gemfile:\n\n```ruby\n# for Rails 6.1+ (7 is supported)\ngem \"store_attribute\", \"~\u003e 1.0\"\n\n# for Rails 5+ (6 is supported)\ngem \"store_attribute\", \"~\u003e 0.8.0\"\n\n# for Rails 4.2\ngem \"store_attribute\", \"~\u003e 0.4.0\"\n```\n\n### Usage\n\nYou can use `store_attribute` method to add additional accessors with a type to an existing store on a model.\n\n```ruby\nstore_attribute(store_name, name, type, options)\n```\n\nWhere:\n\n- `store_name` The name of the store.\n- `name` The name of the accessor to the store.\n- `type` A symbol such as `:string` or `:integer`, or a type object to be used for the accessor.\n- `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`, `default`. Regular `store_accessor` options, such as `prefix`, `suffix` are also supported.\n\nType casting occurs every time you write data through accessor or update store itself\nand when object is loaded from database.\n\nNote that if you update store explicitly then value isn't  type casted.\n\nExamples:\n\n```ruby\nclass MegaUser \u003c User\n  store_attribute :settings, :ratio, :integer, limit: 1\n  store_attribute :settings, :login_at, :datetime\n  store_attribute :settings, :active, :boolean\n  store_attribute :settings, :color, :string, default: \"red\"\n  store_attribute :settings, :colors, :json, default: [\"red\", \"blue\"]\n  store_attribute :settings, :data, :datetime, default: -\u003e { Time.now }\nend\n\nu = MegaUser.new(active: false, login_at: \"2015-01-01 00:01\", ratio: \"63.4608\")\n\nu.login_at.is_a?(DateTime) # =\u003e true\nu.login_at = DateTime.new(2015, 1, 1, 11, 0, 0)\nu.ratio # =\u003e 63\nu.active # =\u003e false\n# Default value is set\nu.color # =\u003e red\n# Default array is set\nu.colors # =\u003e [\"red\", \"blue\"]\n# A dynamic default can also be provided\nu.data # =\u003e Current time\n# And we also have a predicate method\nu.active? # =\u003e false\nu.reload\n\n# After loading record from db store contains casted data\nu.settings[\"login_at\"] == DateTime.new(2015, 1, 1, 11, 0, 0) # =\u003e true\n\n# If you update store explicitly then the value returned\n# by accessor isn't type casted\nu.settings[\"ratio\"] = \"3.141592653\"\nu.ratio # =\u003e \"3.141592653\"\n\n# On the other hand, writing through accessor set correct data within store\nu.ratio = \"3.141592653\"\nu.ratio # =\u003e 3\nu.settings[\"ratio\"] # =\u003e 3\n```\n\nYou can also specify type using usual `store_accessor` method:\n\n```ruby\nclass SuperUser \u003c User\n  store_accessor :settings, :privileges, login_at: :datetime\nend\n```\n\nOr through `store`:\n\n```ruby\nclass User \u003c ActiveRecord::Base\n  store :settings, accessors: [:color, :homepage, login_at: :datetime], coder: JSON\nend\n```\n\n### Using defaults\n\nWith `store_attribute`, you can provide default values for the store attribute. This functionality follows Rails behaviour for `attribute ..., default: ...` (and is backed by Attribute API).\n\nYou must remember two things when using defaults:\n\n- A default value is only populated if no value for the **store** attribute was set, i.e., only when creating a new record.\n- Default values persist as soon as you save the record.\n\nThe examples below demonstrate these caveats:\n\n```ruby\n# Database schema\ncreate_table(\"users\") do |t|\n  t.string :name\n  t.jsonb :extra\nend\n\nclass RawUser \u003c ActiveRecord::Base\n  self.table_name = \"users\"\nend\n\nclass User \u003c ActiveRecord::Base\n  attribute :name, :string, default: \"Joe\"\n  store_attribute :extra, :expired_at, :date, default: -\u003e { 2.days.from_now }\nend\n\nDate.current #=\u003e 2022-03-17\n\nuser = User.new\nuser.name #=\u003e \"Joe\"\nuser.expired_at #=\u003e 2022-03-19\nuser.save!\n\nraw_user = RawUser.find(user.id)\nraw_user.name #=\u003e \"Joe\"\nraw_user.expired_at #=\u003e 2022-03-19\n\nanother_raw_user = RawUser.create!\nanother_user = User.find(another_raw_user.id)\n\nanother_user.name #=\u003e nil\nanother_user.expired_at #=\u003e nil\n```\n\nBy default, Store Attribute returns the default value even when the record is persisted but the attribute name is not present:\n\n```ruby\nuser = User.create!(extra: {})\nuser.expired_at #=\u003e 2022-03-19\n```\n\nYou can disable this behaviour by setting the `store_attribute_unset_values_fallback_to_default` class option to `false` in your model:\n\n```ruby\nclass User \u003c ApplicationRecord\n  self.store_attribute_unset_values_fallback_to_default = false\nend\n\nuser = User.create!(extra: {})\nuser.expired_at #=\u003e nil\n```\n\nYou can also configure the global default for this option in an initializer or application configuration:\n\n```ruby\n# config/initializers/store_attribute.rb\n# # or\n# config/application.rb\nStoreAttribute.store_attribute_unset_values_fallback_to_default = false\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at [https://github.com/palkan/store_attribute](https://github.com/palkan/store_attribute).\n\nFor local development, you'll need PostgreSQL up and running. You can either install it on your host machine or run via Docker as follows:\n\n```bash\ndocker run --name store_attribute_postgres -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_USER=$USER -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres\ndocker exec -it store_attribute_postgres createdb -U $USER store_attribute_test\n```\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalkan%2Fstore_attribute","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalkan%2Fstore_attribute","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalkan%2Fstore_attribute/lists"}