### Gradual Rollout Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md A step-by-step process for gradually rolling out a new feature, starting with beta users, then premium, and finally everyone. ```ruby # Deploy new feature # 1. Add to flags definition Account.flag :features, [..., :new_feature, ...] # 2. Enable for beta users only Account.where(role: :beta_tester).features.set_all!(:new_feature) # 3. Monitor and gather feedback # Metrics.gauge("new_feature.usage", Account.where_features(:new_feature).count) # 4. Enable for premium customers Account.where(plan: 'premium').features.set_all!(:new_feature) # 5. Enable for everyone Account.features.set_all!(:new_feature) # 6. Remove old code paths # 7. In next release, remove flag from definition ``` -------------------------------- ### Example: Querying Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Demonstrates various query examples using the scope methods. ```ruby # Setup Profile.create(languages: [:english]) Profile.create(languages: [:spanish]) Profile.create(languages: [:english, :spanish]) Profile.create(languages: []) # Queries Profile.where_languages(:english).count #=> 2 (1st and 3rd) Profile.where_languages(:english, :spanish).count #=> 3 (all except 4th) Profile.where_all_languages(:english, :spanish).count #=> 1 (only 3rd) Profile.where_not_languages(:english).count #=> 2 (2nd and 4th) Profile.where_not_all_languages(:english, :spanish).count #=> 3 (1st, 2nd, 4th) # Can chain with other scopes Profile.where_languages(:english).where(name: 'John') Profile.joins(:users).where_languages(:english) ``` -------------------------------- ### Gradual Disabling Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Illustrates the process for gradually disabling a deprecated feature, starting with user notifications. ```ruby # Deprecated feature # 1. Warn users # Account.where_features(:old_feature).notify_deprecation ``` -------------------------------- ### Migration Example Source: https://github.com/kenn/active_flag/blob/master/README.md Recommended migration setup for the integer column used to store flags, including default value and optional limit. ```ruby t.integer :languages, null: false, default: 0, limit: 8 # OR add_column :users, :languages, :integer, null: false, default: 0, limit: 8 ``` -------------------------------- ### Setup Permission System Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines a Grant model with flag-based permissions. ```ruby class Grant < ActiveRecord::Base belongs_to :user belongs_to :resource, polymorphic: true flag :permissions, [ :view, :edit, :delete, :admin, :share, :download ] end ``` -------------------------------- ### Query by Permission Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying grants based on permissions. ```ruby # All admins of this document document.grants.where_permissions(:admin) # All users who can edit document.grants.where_permissions(:edit) # All users who can both edit AND delete document.grants.where_all_permissions(:edit, :delete) # Public documents (everyone has view permission) Grant.where_permissions(:view).where(user: nil) ``` -------------------------------- ### Customization Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md An example of how to extend ActiveFlag behavior by customizing flag definitions and overriding methods. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish] # Access Definition for custom logic self.languages.keys self.languages.maps # Override methods if needed def languages=(value) # Custom assignment logic super(value) end end ``` -------------------------------- ### Track User Languages Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of setting and saving user languages. ```ruby user = User.find(1) # User speaks English and Spanish user.user_language.languages = [:english, :spanish] user.user_language.save # Check supported languages user.user_language.languages.to_human #=> ["English", "Spanish"] ``` -------------------------------- ### Fixture Setup (Code) Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Calculating fixture values for user preferences in code. ```ruby alice_prefs = User.preferences.to_i([:dark_mode]) User.create(email: 'alice@example.com', preferences: alice_prefs) ``` -------------------------------- ### Setup Language Preferences Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines supported languages for a user. ```ruby class UserLanguage < ActiveRecord::Base flag :languages, [ :english, :spanish, :french, :german, :chinese, :japanese, :portuguese, :russian ] end ``` -------------------------------- ### Set vs Set! Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Demonstrates the difference between in-memory modification and immediate persistence. ```ruby profile.languages.set(:french) # In memory only profile.save # Persist profile.languages.set!(:french) # In memory + saved immediately ``` -------------------------------- ### Inheritance Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Demonstrates how flag definitions are inherited by subclasses. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish] end class AdminProfile < Profile # Inherits :languages flag # Redefining raises RuntimeError end ``` -------------------------------- ### Dynamic Method Generation Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md Examples of dynamic methods generated by Active Flag on models using `define_method` and `define_singleton_method`. ```ruby define_method(column) do # instance getter define_method("#{column}=") do |arg| ... # instance setter define_singleton_method(relation_name) do # class method: Model.languages define_singleton_method("where_#{column}") do |*args| ... # scope ``` -------------------------------- ### Grant Permissions Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of granting and revoking permissions. ```ruby # Create new grant with view permission document.grants.create!(user: collaborator, permissions: [:view]) # Add edit permission grant = document.grants.find_by(user: collaborator) grant.permissions.set!(:edit) grant.permissions.set!(:download) # Revoke permission grant.permissions.unset!(:edit) ``` -------------------------------- ### Setup Notification Channels Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines notification channels for a user. ```ruby class User < ActiveRecord::Base flag :notification_channels, [ :email, :sms, :push, :slack, :webhook, :in_app ] end ``` -------------------------------- ### to_i method example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Examples demonstrating the conversion of keys to a bitmask integer. ```ruby Definition.to_i(:english) #=> 1 Definition.to_i([:english, :spanish]) #=> 3 (1 + 2) Definition.to_i(['english', 'spanish']) #=> 3 Definition.to_i([:english, :english, :spanish]) #=> 3 (duplicates removed) Definition.to_i([]) #=> 0 Definition.to_i(nil) #=> 0 ``` -------------------------------- ### Example Usage Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Demonstrates various ways to set and clear language flags on a profile object. ```ruby profile.languages = [:english, :spanish] profile.languages = ['english', 'spanish'] # Strings work too profile.languages = :english # Single symbol profile.languages = [:english, :english] # Duplicates are handled profile.languages = [] # Clear all flags profile.languages = nil # Clear all flags (treated as 0) ``` -------------------------------- ### Profile Setup Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines a Profile model with a set of boolean flags for various attributes. ```ruby class Profile < ActiveRecord::Base belongs_to :user flag :attributes, [ :verified, :premium, :public, :show_email, :show_location, :show_phone, :newsletter, :featured ] end ``` -------------------------------- ### Example Usage of Notification Service Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Demonstrates setting user notification channels and sending a notification. ```ruby user.notification_channels = [:email, :slack] user.save NotificationService.notify(user, "New message", :message) # Sends via email and Slack, but not SMS/push ``` -------------------------------- ### ActiveRecord::RecordInvalid Handling Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example demonstrating how to handle ActiveRecord::RecordInvalid using a begin-rescue block. ```ruby begin profile.languages.set!(:spanish) rescue ActiveRecord::RecordInvalid => e puts "Failed to update: #{e.message}" end ``` -------------------------------- ### Check Permissions Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of checking user permissions for a resource. ```ruby grant = Grant.find_or_create_by(user: user, resource: document) if grant.permissions.edit? # Allow editing end unless grant.permissions.view? raise PermissionDenied end ``` -------------------------------- ### User Preferences Setup Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines user preferences using the flag macro with a list of possible preferences. ```ruby class User < ActiveRecord::Base flag :preferences, [ :email_notifications, :push_notifications, :sms_notifications, :marketing_emails, :dark_mode, :compact_layout, :beta_features ] end ``` -------------------------------- ### i18n Translation Example Source: https://github.com/kenn/active_flag/blob/master/README.md Example of how to set up Japanese translations for flag human names using the i18n system. ```yaml ja: active_flag: profile: languages: english: 英語 spanish: スペイン語 chinese: 中国語 french: フランス語 japanese: 日本語 ``` -------------------------------- ### SimpleForm Example Source: https://github.com/kenn/active_flag/blob/master/README.md Example of using ActiveFlag with SimpleForm to create check boxes for languages. ```ruby With SimpleForm = simple_form_for(@profile) do |f| = f.input :languages, as: :check_boxes, collection: Profile.languages.pairs ``` -------------------------------- ### Find Users by Language Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying users based on their language preferences. ```ruby # Find all English speakers User.joins(:user_language).where(user_language: UserLanguage.where_languages(:english)) # Find translation needed (Spanish speakers without French support) spanish_speakers = User.joins(:user_language).where(user_language: UserLanguage.where_languages(:spanish)) need_french = spanish_speakers.where(user_language: UserLanguage.where_not_languages(:french)) ``` -------------------------------- ### Feature Setup Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines a Feature model with a status flag to manage its lifecycle. ```ruby class Feature < ApplicationRecord flag :status, [ :in_development, :in_qa, :staged, :canary, :released, :deprecated, :archived ] end ``` -------------------------------- ### i18n Example Configuration Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example YAML configuration for i18n translations of flag keys. ```yaml ja: active_flag: profile: languages: english: 英語 spanish: スペイン語 chinese: 中国語 ``` -------------------------------- ### Bulk Permission Grant Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of granting permissions to multiple users efficiently. ```ruby # Grant all team members view permission team.users.each do |user| Grant.find_or_create_by(user: user, resource: document) .permissions.set!(:view) end # Or more efficiently: team_ids = team.user_ids Grant.where(user_id: team_ids, resource: document) .permissions.set_all!(:view) ``` -------------------------------- ### Checking and Modifying Flags - Complete Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/value.md A complete example demonstrating how to define and use flags with the Active Flag gem. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end profile = Profile.first # Inspection profile.languages.english? #=> true or false profile.languages.to_a #=> [:english, :spanish] profile.languages.to_human #=> ["English", "Spanish"] profile.languages.raw #=> 3 (binary: 0011) ``` -------------------------------- ### Achievement System Setup Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Sets up the flag macro for an achievement system on a PlayerProfile model. ```ruby class PlayerProfile < ActiveRecord::Base flag :achievements, [ :completed_tutorial, :defeated_first_boss, :collected_rare_item, :reached_level_10, :beat_speedrun, :found_secret, :mastered_skill ] end ``` -------------------------------- ### Query Examples Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Examples of querying language preferences using the Active Flag feature. ```ruby Profile.where_languages(:english).count Profile.where_all_languages(:english, :spanish).count ``` -------------------------------- ### Query Public Profiles Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying profiles based on their attribute flags. ```ruby # Find all public profiles Profile.where_attributes(:public) # Find public profiles with email visible Profile.where_all_attributes(:public, :show_email) # Find profiles to feature Profile.where_attributes(:featured) ``` -------------------------------- ### pairs method example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example showing how to retrieve a hash mapping human-readable strings to their corresponding keys. ```ruby Profile.languages.pairs #=> {"English"=>:english, "Spanish"=>:spanish, "Chinese"=>:chinese} ``` -------------------------------- ### FormBuilder Example Source: https://github.com/kenn/active_flag/blob/master/README.md Example of using ActiveFlag with FormBuilder to create check boxes for languages. ```ruby With FormBuilder = form_for(@profile) do |f| = f.collection_check_boxes :languages, Profile.languages.pairs ``` -------------------------------- ### Query Features by Status Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying features based on their status flags. ```ruby # Find in-progress features Feature.where_status(:in_development, :in_qa) # Find what's in staging Feature.where_status(:staged) # Find released features (not deprecated/archived) released = Feature.where_status(:released) not_deprecated = released.where_not_status(:deprecated) ``` -------------------------------- ### Instance Operations Source: https://github.com/kenn/active_flag/blob/master/_autodocs/index.md Examples of performing operations on flag instances. ```ruby profile.languages.english? # Check profile.languages.set!(:spanish) # Set & save profile.languages.unset!(:chinese) # Unset & save profile.languages = [:english] # Assignment profile.languages.to_a # Get array of keys ``` -------------------------------- ### Unset for all Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of unsetting a feature for all users. ```ruby Account.features.unset_all!(:old_feature) ``` -------------------------------- ### Query Users by Channel Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying users based on their notification channel preferences. ```ruby # Find users who want SMS User.where_notification_channels(:sms) # Find users who configured multiple channels # (can use SQL directly for this) User.where("BIT_COUNT(notification_channels) >= 2") # Users who explicitly disabled everything User.where("notification_channels = 0") ``` -------------------------------- ### Forms Source: https://github.com/kenn/active_flag/blob/master/_autodocs/index.md Example of integrating flags with form builders. ```erb <%= f.input :languages, as: :check_boxes, collection: Profile.languages.pairs %> ``` -------------------------------- ### NoMethodError Fix Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example showing how to fix NoMethodError by using set?(key) or unset?(key) which are safer alternatives. ```ruby # Instead of: profile.languages.french? # NoMethodError if :french not defined # Do this: profile.languages.set?(:french) #=> false (no error) # Or check existence first: if profile.languages.respond_to?(:french?) profile.languages.french? end ``` -------------------------------- ### Lazy Translation Caching Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md Example demonstrating how Active Flag caches translated strings per locale to avoid redundant lookups. ```ruby def humans @humans ||= {} @humans[I18n.locale] ||= begin # Compute translations for current locale @keys.map { |key| [key, human(key)] }.to_h end end ``` -------------------------------- ### Querying Source: https://github.com/kenn/active_flag/blob/master/_autodocs/index.md Examples of querying records based on flag values. ```ruby Profile.where_languages(:english) # Any Profile.where_all_languages(:english, :spanish) # All Profile.where_not_languages(:english) # None ``` -------------------------------- ### Polyglot Feature Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of using flags for article languages and querying articles by language. ```ruby class Article < ActiveRecord::Base flag :languages, [ :english, :spanish, :french, :german, :chinese, :japanese ] end # Article available in English, Spanish, and French article.languages = [:english, :spanish, :french] article.save # Find articles available in user's language user_languages = current_user.languages.to_a Article.where(language: user_languages) # or use scope # Better: Article.where_languages(*user_languages) ``` -------------------------------- ### RSpec Testing Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md RSpec examples for testing user preferences with ActiveFlag. ```ruby describe User do describe 'preferences' do let(:user) { User.create } it 'can set preferences' user.preferences.set!(:dark_mode) expect(user.preferences.dark_mode?).to be(true) end it 'can unset preferences' user.preferences = [:dark_mode] user.save user.preferences.unset!(:dark_mode) expect(user.preferences.dark_mode?).to be(false) end it 'scopes correctly' User.create(preferences: [:dark_mode]) User.create(preferences: []) expect(User.where_preferences(:dark_mode).count).to eq(1) end end end ``` -------------------------------- ### Query Users by Preference Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying User records based on specific preferences being set or not set. ```ruby # Find users who want email notifications User.where_preferences(:email_notifications) # Find users who have notifications enabled (any type) User.where_preferences(:email_notifications, :push_notifications, :sms_notifications) # Find users who have NOT opted into marketing User.where_not_preferences(:marketing_emails) # Find users with both dark mode AND beta features User.where_all_preferences(:dark_mode, :beta_features) ``` -------------------------------- ### Install Gem Source: https://github.com/kenn/active_flag/blob/master/README.md Instructions for adding the ActiveFlag gem to your project's Gemfile. ```ruby gem 'active_flag' ``` -------------------------------- ### Class Method: where_all_[column] Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of using `where_all_[column]` to find records with all of the specified keys set. ```ruby Profile.where_all_languages(:english, :spanish) #=> SELECT * FROM profiles WHERE languages & 3 = 3 ``` -------------------------------- ### Example Usage of flag() Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Demonstrates how to define a flag column and access its Definition object. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end # These return Definition objects: Profile.languages #=> Definition instance Profile.where(id: 1).languages #=> Definition instance (scoped) ``` -------------------------------- ### Bulk Operations Source: https://github.com/kenn/active_flag/blob/master/_autodocs/index.md Examples of performing bulk operations on flags. ```ruby Profile.languages.set_all!(:verified) Profile.where(active: true).languages.unset_all!(:beta) ``` -------------------------------- ### Querying Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Examples of querying records based on flag status using ActiveFlag's custom query methods. ```ruby # Find records with any flag Profile.where_languages(:english, :spanish) # Find records with all flags Profile.where_all_languages(:english, :spanish) # Find records without a flag Profile.where_not_languages(:french) # Find records without all flags Profile.where_not_all_languages(:english, :spanish) # Combine with other conditions Profile.where_languages(:english).where(active: true) Profile.joins(:users).where_languages(:spanish) ``` -------------------------------- ### set_all! Method Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Demonstrates setting a single flag key to true for all records using SQL UPDATE. ```ruby def set_all!(key) # Returns nil (updates in DB) end ``` ```sql UPDATE "table" SET column = COALESCE(column, 0) | bit_mask ``` ```ruby # Set Chinese flag for all profiles Profile.languages.set_all!(:chinese) # Set for scoped records only Profile.where(id: [1, 2, 3]).languages.set_all!(:chinese) # For complex scopes, resolve to IDs first: Profile.joins(:users).languages.set_all!(:chinese) #=> ArgumentError # Instead: ids = Profile.joins(:users).ids Profile.where(id: ids).languages.set_all!(:chinese) #=> Works ``` -------------------------------- ### Query Players by Achievements Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Examples of querying PlayerProfile records based on specific achievements being set, not set, or all set. ```ruby # Players who completed the tutorial PlayerProfile.where_achievements(:completed_tutorial) # Players who've defeated the first boss AND reached level 10 PlayerProfile.where_all_achievements(:defeated_first_boss, :reached_level_10) # Players who haven't unlocked a specific achievement PlayerProfile.where_not_achievements(:mastered_skill) # Count players with rare achievement PlayerProfile.where_achievements(:found_secret).count ``` -------------------------------- ### Nested Caching Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of including flag state in a cache key for nested caching scenarios. ```ruby class User < ActiveRecord::Base flag :preferences, [:theme, :layout] def cache_key_with_flags [cache_key, preferences.raw].join("/") end # Used in view caching: # <% cache @user %> # User display # <% end %> end ``` -------------------------------- ### Binding Pattern Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md Illustrates the binding pattern where the `Value` object is associated with its model instance and definition. ```ruby value = definition.to_value(instance, integer) value.with(instance, definition) # Bind instance and definition profile.languages.set(:french) profile.languages.french? # Can check the value you just set profile.save # Can save the instance ``` -------------------------------- ### Bulk Update Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example of unsetting a language preference in bulk. ```ruby Profile.languages.unset_all!(:english) ``` -------------------------------- ### Column Type Definition Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Example of correct integer column type for ActiveFlag. ```ruby # Correct t.integer :languages # Incorrect - won't work t.string :languages ``` -------------------------------- ### Disable for old users gradually Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Example of disabling a feature for old users gradually. ```ruby Account.where(updated_at: ..1.month.ago).features.unset_all!(:old_feature) ``` -------------------------------- ### Class Method: where_[column] Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of using `where_[column]` to find records with any of the specified keys set. ```ruby Profile.where_languages(:english, :spanish) #=> SELECT * FROM profiles WHERE languages & 3 > 0 ``` -------------------------------- ### Scope Delegation Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md Demonstrates how scope is delegated when accessing a flag on a relation, leading to a duplicated Definition with the scope applied. ```ruby Profile.where(active: true).languages ↓ Relation has generated_relation_methods that returns: Definition#with_scope(relation) ↓ Returns Definition copy with @scope set to the relation ↓ Definition#set_all!(:flag) uses @scope for UPDATE WHERE ``` -------------------------------- ### Querying Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Examples of how to query records based on flag values using ActiveFlag's scope methods. ```ruby # Any flag Profile.where_languages(:english, :spanish) # All flags Profile.where_all_languages(:english, :spanish) # No flag Profile.where_not_languages(:french) # Missing at least one Profile.where_not_all_languages(:english, :spanish) # Combining queries Profile.where_languages(:english).where(active: true) Profile.joins(:users).where_languages(:spanish) ``` -------------------------------- ### keys attribute example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example showing how to retrieve the frozen symbol keys for a flag column. ```ruby Profile.languages.keys #=> [:english, :spanish, :chinese, :french, :japanese] ``` -------------------------------- ### Migration Column Configuration Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of how to configure the integer column in a migration for storing flags, including options for 64-bit integers. ```ruby # Migration create_table :profiles do |t| t.integer :languages, null: false, default: 0, limit: 8 # for 64 flags t.integer :preferences, null: false, default: 0 # for 32 flags end ``` -------------------------------- ### Database Migration Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Example database migration for creating a table with a flag column. Shows options for 32 or 64 flags. ```ruby # db/migrate/[timestamp]_create_profiles.rb create_table :profiles do |t| t.integer :languages, null: false, default: 0 # for up to 32 flags # OR t.integer :languages, null: false, default: 0, limit: 8 # for up to 64 flags t.timestamps end ``` -------------------------------- ### with_scope Method Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Demonstrates returning a copy of Definition bound to a specific relation scope. ```ruby def with_scope(scope) # Returns Definition (scoped copy) end ``` ```ruby scoped_def = Profile.languages.with_scope(Profile.where(active: true)) scoped_def.set_all!(:verified) #=> Only updates active profiles ``` -------------------------------- ### Class Method: where_not_all_[column] Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of using `where_not_all_[column]` to find records missing at least one of the specified keys. ```ruby Profile.where_not_all_languages(:english, :spanish) #=> SELECT * FROM profiles WHERE languages & 3 < 3 ``` -------------------------------- ### Dynamic Predicates using method_missing Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of using dynamic predicate methods (e.g., `english?`) provided by the Value object. ```ruby profile.languages.english? #=> true or false (checks if flag is set) profile.languages.spanish? #=> true or false ``` -------------------------------- ### to_a method example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/value.md Inherited from Set. Returns an array of all flag keys currently set. ```ruby profile.languages.to_a #=> [:english, :spanish] profile.languages.to_a.size #=> 2 ``` -------------------------------- ### Class Method and Scope Usage Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Shows how to use the class method to get flag definitions and scope methods for querying. ```ruby # Class method returns Definition Profile.languages #=> # Profile.languages.maps #=> {:english=>1, :spanish=>2, :chinese=>4, :french=>8, :japanese=>16} # Scope methods for querying Profile.where_languages(:english) #=> SELECT * FROM profiles WHERE languages & 1 > 0 Profile.where_all_languages(:english, :spanish) #=> SELECT * FROM profiles WHERE languages & 3 = 3 ``` -------------------------------- ### column attribute example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example showing how to retrieve the name of the integer column storing the bit array. ```ruby Profile.languages.column #=> :languages ``` -------------------------------- ### Defining a Flag Column Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Example of how to define a flag column on an ActiveRecord model using the `flag` method. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese, :french, :japanese] end ``` -------------------------------- ### to_array Method Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Demonstrates converting an integer bitmask back to an array of flag keys. ```ruby def to_array(integer) # Array end ``` ```ruby Definition.to_array(1) #=> [:english] Definition.to_array(3) #=> [:english, :spanish] Definition.to_array(7) #=> [:english, :spanish, :chinese] Definition.to_array(0) #=> [] Definition.to_array(1000) #=> [] (if bit positions don't match any keys) ``` -------------------------------- ### maps attribute example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Example showing how to retrieve the hash mapping keys to their bit mask values. ```ruby Profile.languages.maps #=> {:english=>1, :spanish=>2, :chinese=>4, :french=>8, :japanese=>16} ``` -------------------------------- ### Complete CRUD Workflow Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Illustrates a full Create, Read, Update, and Delete workflow with flags. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end # Create with flags profile = Profile.create(languages: [:english, :spanish]) # Read flags profile.languages.english? #=> true profile.languages.to_a #=> [:english, :spanish] Profile.languages.keys #=> [:english, :spanish, :chinese] # Update flags profile.languages.set(:chinese) profile.save ``` -------------------------------- ### Querying with Scopes Source: https://github.com/kenn/active_flag/blob/master/README.md Detailed examples of using the generated query scopes for different flag-based filtering conditions. ```ruby Profile.where_languages(:french) #=> SELECT * FROM profiles WHERE languages & 8 > 0 Profile.where_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 > 0 Profile.where_all_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 = 10 Profile.where_not_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 = 0 Profile.where_not_all_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 < 10 ``` -------------------------------- ### Feature Flags / Rollout Setup Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Defines feature flags for an Account model, including common features like UI versions and access controls. ```ruby class Account < ActiveRecord::Base flag :features, [ :v2_ui, :dark_mode, :api_access, :advanced_reporting, :white_label, :sso, :custom_domain ] end ``` -------------------------------- ### Bulk Update SQL Examples Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md Illustrates the SQL generated for setting and unsetting flags in bulk using bitwise operations and COALESCE to handle NULL values. ```ruby # set_all!(:chinese) UPDATE "profiles" SET languages = COALESCE(languages, 0) | 4 # unset_all!(:chinese) UPDATE "profiles" SET languages = COALESCE(languages, 0) & ~4 ``` -------------------------------- ### ArgumentError: More Complex Scope Fix Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Another example of fixing ArgumentError by resolving a more complex scope to IDs first. ```ruby # Instead of: Profile.where(active: true).order(:created_at).limit(100).languages.set_all!(:flag) # Do this: ids = Profile.where(active: true).order(:created_at).limit(100).ids Profile.where(id: ids).languages.set_all!(:flag) ``` -------------------------------- ### ActiveRecord::RecordInvalid Workaround Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example showing the workaround for ActiveRecord::RecordInvalid by using the non-bang method and calling save manually. ```ruby profile.languages.set(:spanish) if profile.save # Success else puts "Validation errors: #{profile.errors}" end ``` -------------------------------- ### Instance Getter and Setter Usage Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Demonstrates how to use the instance getter to retrieve flags and the instance setter to modify them. ```ruby # Instance getter returns a Value object profile = Profile.first profile.languages #=> # # Instance setter accepts array of symbols profile.languages = [:spanish, :french] profile.languages.spanish? #=> true ``` -------------------------------- ### ArgumentError: Complex Scope Fix Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example demonstrating how to fix ArgumentError by resolving the scope to IDs first before applying the update. ```ruby # Instead of: Profile.joins(:users).languages.set_all!(:verified) # Do this: ids = Profile.joins(:users).ids Profile.where(id: ids).languages.set_all!(:verified) ``` -------------------------------- ### RuntimeError: Flag Already Defined Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example demonstrating how attempting to define a flag twice on the same model column raises a RuntimeError. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish] flag :languages, [:french] # RuntimeError raised end ``` -------------------------------- ### View User Preferences Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Demonstrates how to check individual preference flags and retrieve all set preferences. ```ruby user = User.find(1) user.preferences.email_notifications? #=> true user.preferences.dark_mode? #=> false user.preferences.to_a #=> [:email_notifications, :beta_features] ``` -------------------------------- ### Instance Scope Methods Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Shows how to use instance scope methods like `set_all!` on a relation. ```ruby scope = Profile.where(id: [1, 2, 3]) scope.languages.set_all!(:chinese) #=> Updates only records 1, 2, 3 ``` -------------------------------- ### NoMethodError: Undefined Flag Predicate Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example demonstrating calling a predicate method for a key that does not exist in the flag definition, resulting in a NoMethodError. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end profile = Profile.first profile.languages.french? # NoMethodError # :french is not in the languages definition ``` -------------------------------- ### ActiveRecord::RecordInvalid Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example showing how ActiveRecord::RecordInvalid is raised when set!(key) or unset!(key) calls save!() and validation fails. ```ruby class Profile < ActiveRecord::Base validates :name, presence: true flag :languages, [:english, :spanish] end profile = Profile.new(languages: [:english]) profile.languages.set!(:spanish) # Raises: ActiveRecord::RecordInvalid # Because profile is not saved yet and name is blank ``` -------------------------------- ### ArgumentError: Complex Scope with set_all! or unset_all! Examples Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Examples of calls to set_all! or unset_all! on scoped relations that include clauses other than WHERE, which raise an ArgumentError. ```ruby # All of these raise ArgumentError: Profile.joins(:users).languages.set_all!(:verified) Profile.where(active: true).group(:country).languages.unset_all!(:beta) Profile.order(:name).languages.set_all!(:flag) Profile.limit(10).languages.unset_all!(:old_feature) ``` -------------------------------- ### ActiveRecord::RecordInvalid Workaround with validate: false Example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/errors.md Example showing the workaround for ActiveRecord::RecordInvalid by using set!(key, validate: false) to skip validation. ```ruby profile.languages.set!(:spanish, validate: false) ``` -------------------------------- ### Update User Preferences (With Save) Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Shows how to set or unset single preferences and how to assign a new array of preferences, followed by saving the user object. ```ruby # Set a single preference user.preferences.set!(:dark_mode) # Unset a single preference user.preferences.unset!(:marketing_emails) # Set multiple at once user.preferences = [:dark_mode, :compact_layout] user.save ``` -------------------------------- ### Combining with Joins Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Illustrates how scope methods can be used with joins. ```ruby Profile.joins(:users).where_languages(:english) User.joins(:profile).where(profile: Profile.where_languages(:english)) ``` -------------------------------- ### Convert Integer to Keys Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Illustrates how to convert an integer representation to a list of Active Flag keys and vice versa. ```ruby # What does integer 7 mean? Profile.languages.to_array(7) #=> [:english, :spanish, :chinese] # What's the integer for these keys? Profile.languages.to_i([:english, :french]) #=> 9 ``` -------------------------------- ### Model Definition and Instance Methods Source: https://github.com/kenn/active_flag/blob/master/README.md Example of defining a model with ActiveFlag and demonstrating instance method usage for checking, setting, and unsetting flags, as well as direct assignment. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese, :french, :japanese] end # {:english=>1, :spanish=>2, :chinese=>4, :french=>8, :japanese=>16 } # Instance methods profile.languages #=> # profile.languages.english? #=> true profile.languages.set?(:english) #=> true profile.languages.unset?(:english) #=> false profile.languages.set(:spanish) profile.languages.unset(:japanese) profile.languages.raw #=> 3 profile.languages.to_a #=> [:english, :spanish] profile.languages = [:spanish, :japanese] # Direct assignment that works with forms ``` -------------------------------- ### Internationalization Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Example of configuring and using internationalization for flag human-readable names. ```yaml # config/locales/ja.yml ja: active_flag: profile: languages: english: 英語 spanish: スペイン語 ``` -------------------------------- ### Definition Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Example of defining multiple flag columns in a Rails model. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese, :french, :japanese] flag :preferences, [:notifications, :newsletter, :beta] end ``` -------------------------------- ### Feature Workflow Source: https://github.com/kenn/active_flag/blob/master/_autodocs/recipes.md Demonstrates the lifecycle of a feature using status flags, from development to archiving. ```ruby # 1. In Development feature = Feature.find(:new_ui) # feature.status = [:in_development] # 2. Ready for QA feature.status.set!(:in_qa) feature.status.unset!(:in_development) # 3. Deployed to staging feature.status.set!(:staged) # 4. Canary deployment feature.status.set!(:canary) # Feature.where_status(:canary).each { |f| enable_for_beta_users(f) } # 5. Full release feature.status.set!(:released) feature.status.unset!(:canary) # 6. Years later, deprecation feature.status.set!(:deprecated) # Notify users # 7. Archive feature.status.set!(:archived) feature.status.unset!(:deprecated) ``` -------------------------------- ### Internationalization Usage Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Example of how to retrieve human-readable flag names after setting the locale. ```ruby I18n.locale = :ja profile.languages.to_human #=> ["英語", "スペイン語"] Profile.languages.humans #=> {:english=>"英語", ...} ``` -------------------------------- ### Forms Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Example of using ActiveFlag with simple_form_for to render checkboxes for flag selection. ```erb <%= simple_form_for(@profile) do |f| %> <%= f.input :languages, as: :check_boxes, collection: Profile.languages.pairs %> <% end %> ``` -------------------------------- ### Definition Source: https://github.com/kenn/active_flag/blob/master/_autodocs/README.md Example of defining a flag column in a Rails model using ActiveFlag. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end # Profile.languages returns an ActiveFlag::Definition Profile.languages.keys #=> [:english, :spanish, :chinese] Profile.languages.maps #=> {:english=>1, :spanish=>2, :chinese=>4} ``` -------------------------------- ### Instance Setter with Various Inputs Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/activeflag-module.md Shows the flexibility of the instance setter in accepting different types of input for flag keys. ```ruby profile.languages = [:spanish, :japanese] profile.languages.spanish? #=> true ``` -------------------------------- ### i18n Language Mappings Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Examples showing how to access language mappings for internationalization. ```ruby Profile.languages.humans #=> {"English"=>"english", ...} Profile.languages.pairs #=> {:english=>"English", ...} ``` -------------------------------- ### Form Usage with pairs Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/definition.md Examples of using the `pairs` method with FormBuilder and SimpleForm. ```erb <%= form_for(@profile) do |f| %> <%= f.collection_check_boxes :languages, Profile.languages.pairs do |b| %> <%= b.label { b.check_box + b.text } %> <% end %> <% end %> <%= simple_form_for(@profile) do |f| %> <%= f.input :languages, as: :check_boxes, collection: Profile.languages.pairs %> <% end %> ``` -------------------------------- ### Performance Comparison Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Illustrates the performance benefit of ActiveFlag's bitwise SQL operations over multiple boolean columns. ```ruby # Instead of: # Profile.where(english: true).where(spanish: true) # ActiveFlag generates: Profile.where_all_languages(:english, :spanish) # SELECT * FROM profiles WHERE languages & 3 = 3 ``` -------------------------------- ### set? method example Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/value.md Checks if a specific flag key is currently set. ```ruby profile.languages.set?(:english) #=> true profile.languages.set?(:french) #=> false ``` -------------------------------- ### Basic Definition and Usage Source: https://github.com/kenn/active_flag/blob/master/_autodocs/api-reference/overview.md Demonstrates defining flags in a model, creating records with flags, reading flag status, and updating flags in-memory and with save. ```ruby class Profile < ActiveRecord::Base flag :languages, [:english, :spanish, :chinese] end # Create profile = Profile.create(languages: [:english, :spanish]) # Read profile.languages.english? #=> true profile.languages.to_a #=> [:english, :spanish] profile.languages.to_human #=> ["English", "Spanish"] profile.languages.raw #=> 3 # Update (in-memory) profile.languages.set(:chinese) profile.save # Update (with save) profile.languages.set!(:french) profile.languages.unset!(:english) ``` -------------------------------- ### Definition Class - Initialization Source: https://github.com/kenn/active_flag/blob/master/_autodocs/architecture.md The Ruby code snippet shows the initialization of the `Definition` class, including storing keys, creating a mapping of keys to bit masks, and freezing the attributes. ```ruby class Definition attr_reader :keys, :maps, :column def initialize(column, keys, klass) @column = column @keys = keys.freeze # Maps each key to a power of 2: {:english=>1, :spanish=>2, ...} @maps = keys.map.with_index { |key, i| [key, 2**i] }.to_h.freeze @klass = klass @scope = klass end end ```