{"id":19339816,"url":"https://github.com/owaiswiz/preflex","last_synced_at":"2025-04-23T02:30:57.505Z","repository":{"id":222209043,"uuid":"755955853","full_name":"owaiswiz/preflex","owner":"owaiswiz","description":"A simple but powerful Rails engine for storing preferences, feature flags, etc. With support for reading/writing values client-side as well!","archived":false,"fork":false,"pushed_at":"2024-09-24T18:02:25.000Z","size":108,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T11:07:44.909Z","etag":null,"topics":["feature-flags","feature-flags-management","preferences","rails","ruby","settings-management","settings-storage"],"latest_commit_sha":null,"homepage":"https://owaiskhan.me/post/simple-rails-engine-manage-user-preference-settings-feature-flags","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/owaiswiz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-02-11T15:22:22.000Z","updated_at":"2025-03-20T23:15:25.000Z","dependencies_parsed_at":"2024-02-20T17:42:31.446Z","dependency_job_id":"b60cb4ad-02c3-4a1c-bdc9-57f8ee4dc6e5","html_url":"https://github.com/owaiswiz/preflex","commit_stats":null,"previous_names":["owaiswiz/preflex"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/owaiswiz%2Fpreflex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/owaiswiz%2Fpreflex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/owaiswiz%2Fpreflex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/owaiswiz%2Fpreflex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/owaiswiz","download_url":"https://codeload.github.com/owaiswiz/preflex/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250357574,"owners_count":21417309,"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":["feature-flags","feature-flags-management","preferences","rails","ruby","settings-management","settings-storage"],"created_at":"2024-11-10T03:23:55.893Z","updated_at":"2025-04-23T02:30:57.127Z","avatar_url":"https://github.com/owaiswiz.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Preflex\n\u003ca href=\"https://badge.fury.io/rb/preflex\"\u003e\u003cimg src=\"https://badge.fury.io/rb/preflex.svg\" alt=\"Gem Version\" height=\"18\"\u003e\u003c/a\u003e \u003cimg alt=\"Gem Total Downloads\" src=\"https://img.shields.io/gem/dt/preflex\"\u003e\n\n\nA simple but powerful Rails engine for storing user preferences, feature flags, etc. With support for reading/writing values client-side!\n\n## Dependency\nRequires Rails 6.1+. (i.e works on Rails 6.1, Rails 7, Rails 7.1, ...)\n\n## Installation\n\nAdd `preflex` to your `Gemfile`.\n```ruby\ngem 'preflex'\n```\n\nRun bundle install\n```shell\nbundle install\n```\n\nRun the Preflex installer.\n```shell\nbundle exec rails preflex\n```\nThis will generate a create a table to store preferences, generate a dummy initializer file, mount the engine at `/preflex` in your `routes.rb` as well as ask you if you want to create an example preference class.\n\n## Usage\n\n### Create a preference sub-class\n\nIf you used the installer above, it would've already done this for you, after prompting you. But for context, here's what a preference class looks like\n\n```ruby\n# app/models/user_preference.rb\nclass UserPreference \u003c Preflex::Preference\n  preference :playback_rate,  :integer, default: 1\n  preference :volume,         :integer, default: 80\n  preference :theatre_mode,   :boolean, default: false\n  preference :favorite_colors :json,    default: ['red', 'blue']\nend\n```\n\n### Reading \u0026 writing values from Ruby\n\n```ruby\nUserPreference.for(current_user).get(:playback_rate) # =\u003e 1\nUserPreference.for(current_user).set(:volume, 100)\nUserPreference.for(current_user).get(:volume)        # =\u003e 100\n```\n\n### Reading \u0026 writing values from Ruby (Simpler)\n\nIf you just specify a `current_owner` class method that returns the owner of the preference in context of a controller request like below:\n\n```ruby\nclass UserPreference \u003c Preflex::Preference\n  ...\n  ...\n\n  # This is the owner of this preference\n  # It can be any unique object.\n  # Either a ActiveRecord object like instance of your User/Customer/Account model.\n  # Or even just a unique number/string/etc.\n  def self.current_owner(controller_instance)\n    controller_instance.current_user\n  end\nend\n```\n\n\nYou would then simply be able to read/write preferences any where in the app like (as long as you are in context of a controller request):\n```ruby\nUserPreference.get(:playback_rate)      # =\u003e 1\nUserPreference.get(:theatre_mode)       # =\u003e false\nUserPreference.set(:theatre_mode, true) # =\u003e true\n\n# In case you'd like to be a bit more explicit,\n# you can also do this:\nUserPreference.current.get(:playback_rate) #=\u003e 1\n```\n\n### Reading and writing values from JavaScript\n\nMake sure you've specified `current_owner` as described above. And then add this to the head tag in your layout file (e.g app/views/layouts/application.html.erb)\n\n```erb\n\u003c!-- e.g in app/views/layout/application.html.erb --\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003c%= Preflex::PreferencesHelper.script_tag(UserPreference) %\u003e\n    ...\n  \u003c/head\u003e\n  ...\n\u003c/html\u003e\n```\n\nIf you've got multiple preference classes, make sure you list them all here when rendering the script tag. e.g:\n```erb\n\u003c%= Preflex::PreferencesHelper.script_tag(UserPreference, FeatureFlags, CustomerSettings) %\u003e\n```\n\nYou'll then be able to read and write values from Javascript like so:\n```js\nconsole.log(UserPreference.get('playback_rate'))     // =\u003e 1\nconsole.log(UserPreference.get('favorite_colors'))   // =\u003e ['red', 'blue']\n\nUserPreference.set('favorite_colors', ['orange', 'white'])\n\nconsole.log(UserPreference.get('favorite_colors'))   // =\u003e ['orange', 'white']\n```\nThis will update things on the client instantly + also send a request to your server to persit the preference change in the database.\n\n#### Events - Listening to when a preference was updated\n```js\ndocument.addEventListener('preflex:preference-updated', (e) =\u003e { console.log(\"Event detail:\", e.detail) })\n\nUserPreference.set('favorite_colors', ['orange', 'white'])\n// =\u003e Event detail: { klass: 'UserPreference', name: 'favorite_colors', value: ['orange', 'white'] }\n\n```\n\n\n## FAQs\n\n### How are things stored?\n\nWhen you first install the gem and run `bundle exec rails preflex`, we create a single table in your database called `preflex_preferences`. All preferences are stored there.\n\nWhen you create a new preference class, it's just an STI child of the `Preflex::Preference` model that the engine defines. So you don't need to run any migrations to create new tables/etc. later.\n\nIf you read/write values client side, we also store a copy in local storage when you write things client side (so your preference values are always instantly updated client-side). But we also do a POST request to let the server know of the change.\n\n\n### What data types can I use in my preferences?\nI'd recommend limiting yourself to `:integer, :boolean, :string, :big_integer, :float, :json`.\n\nPreflex uses [store_attribute](https://github.com/palkan/store_attribute) under the hood, so any thing that it supports should work, but if you also read/write values from the client side, things like date time might not be serialized properly.\n\n### How are things updated from the client side?\nUpdating things from JavaScript will instantly update a copy of the data in local storage. And emit a `preflex:preference-updated` event. It will also do a POST request to your server to persist the change in the database.\n\nPreflex defines a super-simple `Preflex::PreferencesController` with a single update action to handle that. It will:\n1. Get the preference class from params,\n2. Load it's current instance - e.g UserPreference.current\n   (so you must define `current_owner` as described above in your preferences class if updating things client side)\n3. Get the name and value in params and do the update.\n\n\n### Can I define multiple classes for storing different types of preferences?\nYes, you can create as many preference classes as you want grouping different domains of preference settings.\n\nFor example, you might have a multi-tenant application that has multiple `Account` objects, and there are also `User` objects which can belong to multpile  accounts.\n\nSo as an example, you can do a preference class for account, to store account level preferences like feature flags. And another preference class for user, for things like their playback rate, dark mode preference.\n\n```ruby\n# app/models/feature_flags.rb\nclass FeatureFlags \u003c Preflex::Preference\n  preference :new_navigation,   :boolean, default: false\n  preference :email_builder_v2, :boolean, default: true\n\n  def self.current_owner(controller_instance)\n    controller_instance.current_account\n  end\nend\n\n# app/models/user_preference.rb\nclass UserPreference \u003c Preflex::Preference\n  preference :dark_mode,     :boolean, default: false\n  preference :playback_rate, :integer, default: 1\n\n  def self.current_owner(controller_instance)\n    controller_instance.current_user\n  end\nend\n```\n\n## Questions/Issues\n\nFeel free to open an issue if you've got a question/problem.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fowaiswiz%2Fpreflex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fowaiswiz%2Fpreflex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fowaiswiz%2Fpreflex/lists"}