### Install Superfeature Gem Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Instructions for installing the Superfeature gem using Bundler and generating its initial configuration files. ```bash bundle add superfeature rails generate superfeature:install ``` -------------------------------- ### Price Comparisons in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code illustrates how to compare `Price` objects with each other and with numerical values. It shows examples of greater than, equal to, and less than comparisons, demonstrating that `Price` objects can be compared directly with integers and other `Price` objects. ```ruby Price(100) > Price(50) # => true Price(100) == 100 # => true Price(100) < 200 # => true ``` -------------------------------- ### Building a Complete Pricing Table Source: https://context7.com/rubymonolith/superfeature/llms.txt Illustrates how to integrate various Superfeature components, including plans, features, pricing, and discounts, to construct a comprehensive pricing page. It shows a controller example that sets up base plans, a collection of plans, and applies promotional discounts. ```ruby # Controller class PricingController < ApplicationController def index base_plan = Plans::Free.new(User.new) @plans = Superfeature::Plan::Collection.new(base_plan).to_a @promo = Promotion.new(name: "Launch Special", percent_off: 20) if params[:promo] end end ``` -------------------------------- ### Custom Discount Sources with to_discount Source: https://context7.com/rubymonolith/superfeature/llms.txt Explains how to create custom discount objects by implementing the `to_discount` method. This method should return a `Discount` object, allowing any object (like database records or custom classes) to be used as a discount source. Examples include using a `Coupon` model and a `Promotion` class. ```ruby class Coupon < ApplicationRecord # columns: code, percent_off, fixed_off def to_discount if percent_off.present? Superfeature::Discount::Percent.new(percent_off) else Superfeature::Discount::Fixed.new(fixed_off) end end end class Promotion attr_reader :name, :percent_off def initialize(name:, percent_off:) @name = name @percent_off = percent_off end def to_discount Superfeature::Discount::Percent.new(@percent_off) end end # Usage coupon = Coupon.find_by(code: "SAVE20") # percent_off: 20 price = Price(100).apply_discount(coupon) price.amount # => 80.0 promo = Promotion.new(name: "Launch Special", percent_off: 15) price = Price(100).apply_discount(promo) price.amount # => 85.0 ``` -------------------------------- ### Defining a New Plan in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code defines a new `Enterprise` plan. It sets the plan's name, price, and description, and shows how to override or conditionally enable/disable features inherited from a base class. It also includes commented-out examples for linking to adjacent plans. ```ruby module Plans class Enterprise < Base def name = "Enterprise" def price = 0 def description = "Description for Enterprise plan" # Override features from Base to enable them # def priority_support = super.enable # # Conditionally enable/disable based on a boolean: # def dark_mode = super.enable(user.premium?) # def legacy_feature = super.disable(user.migrated?) # Link to adjacent plans for navigation # def next = plan NextPlan # def previous = plan PreviousPlan end end ``` -------------------------------- ### Install Superfeature Generator (Bash) Source: https://context7.com/rubymonolith/superfeature/llms.txt This command installs the Superfeature gem and sets up the initial plan structure within a Rails application. It creates essential files for defining base plans, features, and configuration. ```bash # Install Superfeature and create initial plan structure rails generate superfeature:install # Creates: # - app/plans/base.rb # - app/plans/features/base.rb # - app/plans/free.rb # - app/plans/paid.rb # - config/initializers/plans.rb ``` -------------------------------- ### Working with Plan Collections in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md The `Superfeature::Plan::Collection` class in Ruby allows for navigation and enumeration of plans. It supports creating a collection from a starting plan, finding specific plans by symbol or class, slicing multiple plans, and iterating through all plans using `Enumerable`. ```ruby # Create a collection starting from any plan collection = Superfeature::Plan::Collection.new(Plans::Free.new(current_user)) # Find a specific plan by symbol key collection.find(:paid) # => Paid plan instance # Find a specific plan by class collection.find(Plans::Paid) # => Paid plan instance # Get multiple plans with slice collection.slice(:free, :paid) # => Array of matching plans collection.slice(Plans::Free, Plans::Paid) # => Also works with classes # Iterate through all plans (includes Enumerable) collection.each do |plan| puts "#{plan.name}: $#{plan.price}" end collection.to_a # All plans as an array ``` -------------------------------- ### Preventing Inheritance with `exclusively` in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md The `exclusively` keyword in Ruby is used to ensure a method applies only to the exact class it's defined in, not to subclasses. This example shows how `exclusively def badge` in `Plans::Pro` is not inherited by `Plans::Enterprise`. ```ruby module Plans class Pro < Basic # Only Pro gets this badge, not Enterprise which inherits from Pro exclusively def badge = "Most Popular" end end module Plans class Enterprise < Pro # badge returns nil here, not "Most Popular" end end ``` -------------------------------- ### Apply Discounts Using Strings (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Shows how to apply discounts to prices by parsing natural language discount strings, such as percentages or fixed dollar amounts. ```ruby price = Price(100) price.apply_discount("20%").amount # => 80.0 price.apply_discount("$15").amount # => 85.0 price.apply_discount(10).amount # => 90.0 (numeric = dollars off) ``` -------------------------------- ### Controller Action to Prepare Pricing Data (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md A controller action can be used to set up the necessary data for a pricing page. This involves creating a collection of available plans and potentially a promotion object. The plans are instantiated with a user object, and the promotion is created with its specific details. ```ruby class PricingController < ApplicationController def index @plans = Superfeature::Plan::Collection.new(Plans::Free.new(User.new)).to_a @promo = Promotion.new(name: "Launch Special", percent_off: 20) end end ``` -------------------------------- ### Apply Discounts to Prices in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Demonstrates various ways to apply discounts to a `Price` object, including fixed amounts, percentages, and setting a target price. ```ruby price = Price(100) # Fixed dollar amount off price.discount_fixed(20).amount # => 80.0 # Percentage off (25 = 25%) price.discount_percent(25).amount # => 75.0 # Set a target price directly price.discount_to(79).amount # => 79.0 ``` -------------------------------- ### Define a Basic Plan in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Demonstrates how to create a simple plan by inheriting from `Superfeature::Plan` and defining its name and description. ```ruby module Plans class Free < Superfeature::Plan def name = "Free" def description = "Get started for free" end end ``` -------------------------------- ### Create and Format Prices in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Shows how to create `Price` objects using `BigDecimal` internally and how to format them to different string representations. ```ruby price = Superfeature::Price.new(49.99) price.amount # => BigDecimal("49.99") price.to_f # => 49.99 price.to_i # => 49 price = Price(29) price.to_formatted_s # => "29.00" price.to_formatted_s(decimals: 0) # => "29" ``` -------------------------------- ### Linking Plans with Navigation (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Demonstrates how to define navigation links between plans using `next` and `previous` methods to create a plan hierarchy. ```ruby module Plans class Free < Superfeature::Plan def next = plan Pro end class Pro < Free def previous = plan Free def next = plan Enterprise end class Enterprise < Pro def previous = plan Pro end end ``` -------------------------------- ### Price Queries in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code demonstrates query methods available for `Price` objects. It shows how to check if a price is zero (`zero?`), free (`free?`), positive (`positive?`), or paid (`paid?`), providing convenient ways to evaluate price conditions. ```ruby Price(0).zero? # => true Price(0).free? # => true (alias) Price(100).positive? # => true Price(100).paid? # => true (alias) ``` -------------------------------- ### Creating a Promotion Object for Discounts (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md A `Promotion` class can be created to represent special offers. This class can hold details like the promotion name and percentage off. By implementing the `to_discount` method, a `Promotion` object can be used seamlessly with the `apply_discount` method, returning a `Superfeature::Discount::Percent` object. ```ruby class Promotion attr_reader :name, :percent_off def initialize(name:, percent_off:) @name = name @percent_off = percent_off end def to_discount Superfeature::Discount::Percent.new(@percent_off) end end ``` -------------------------------- ### Wiring Plan Chain in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code demonstrates how to link plans sequentially by updating the `next` and `previous` methods. It shows the necessary modifications in `paid.rb` to point to `Enterprise` as the next plan, and in `enterprise.rb` to point back to `Paid` as the previous plan. ```ruby # In paid.rb def next = plan Enterprise # In enterprise.rb def previous = plan Paid ``` -------------------------------- ### Creating Prices in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code illustrates various ways to create `Price` objects. It shows the use of a convenience method `Price(49.99)`, the standard constructor `Price.new(49.99)`, and Rails core extensions like `discounted_by`, `to_price`, and `"$49.99".to_price`. It also mentions opting in for core extensions outside of Rails. ```ruby price = Price(49.99) # convenience method price = Price.new(49.99) # standard constructor # In Rails, you can also use core extensions: 100.discounted_by(20.percent_off) # => Price(80) 100.discounted_by(20) # => Price(80) 100.to_price # => Price(100) "$49.99".to_price # => Price(49.99) # Outside of Rails, opt-in with `require "superfeature/core_ext"`. ``` -------------------------------- ### Assign Prices to Plans (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Illustrates how to assign `Price` objects to different plans, such as Free, Pro, and Enterprise. ```ruby module Plans class Free < Superfeature::Plan def price = Price(0) end class Pro < Free def price = Price(29) end class Enterprise < Pro def price = Price(99) end end ``` -------------------------------- ### Implementing Custom Discount Sources via `to_discount` (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Any object can act as a discount source if it implements the `to_discount` method. This method should return a `Superfeature::Discount` object. This allows for integration with custom discount logic, such as database records representing coupons. ```ruby class Coupon < ApplicationRecord def to_discount Superfeature::Discount::Percent.new(percent_off) end end coupon = Coupon.find_by(code: "SAVE20") price = Price(100).apply_discount(coupon) price.amount # => 80.0 ``` -------------------------------- ### Check Feature Status in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Illustrates how to instantiate a plan and check the enabled/disabled status of its features using the `.enabled?` method. ```ruby plan = Plans::Free.new(current_user) plan.priority_support.enabled? # => false plan.api_access.enabled? # => true ``` -------------------------------- ### Plan Navigation with Plan::Collection (Ruby) Source: https://context7.com/rubymonolith/superfeature/llms.txt Explains how to use `Superfeature::Plan::Collection` in Ruby for navigating and discovering plans. It covers finding specific plans by key or class, retrieving multiple plans, and iterating through all available plans to build pricing tables or understand upgrade paths. ```ruby user = current_user collection = Superfeature::Plan::Collection.new(Plans::Free.new(user)) # Find a specific plan by symbol key pro_plan = collection.find(:pro) pro_plan.name # => "Pro" # Find by class enterprise = collection.find(Plans::Enterprise) # Get multiple plans plans = collection.slice(:free, :pro, :enterprise) # Iterate through all plans (via Enumerable) collection.each do |plan| puts "#{plan.name}: $#{plan.price.to_i}/mo" end # Output: # Free: $0/mo # Pro: $29/mo # Enterprise: $99/mo ``` -------------------------------- ### Accessing Discount Information After Application (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md After applying a discount to a price, you can access various details about the discount applied. This includes the discounted amount, whether a discount was applied, the original amount, and specific details about the discount itself like fixed value, percentage, and formatted string representations. ```ruby price = Price(100).apply_discount("25%") price.amount # => 75.0 price.discounted? # => true price.original.amount # => 100.0 price.discount.fixed # => 25.0 (dollars saved this step) price.discount.percent # => 25.0 (percent of original this step) price.discount.to_fixed_s # => "25.00" price.discount.to_percent_s # => "25%" price.discount.to_formatted_s # => "25%" (natural format) ``` -------------------------------- ### Render Pricing Table with Promotions (ERB) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This ERB template renders a dynamic pricing table. It displays original and discounted prices when a promotion is active, shows a 'Free' label for zero-price plans, and includes a feature list with checkmarks. It depends on the `@promo` and `@plans` instance variables. ```erb

Pricing

<% if @promo %>
<%= @promo.name %>: Save <%= @promo.percent_off %>% on all plans!
<% end %>
<% @plans.each do |plan| %>

<%= plan.name %>

<% price = plan.price %> <% if @promo && price.positive? %> <% discounted = price.apply_discount(@promo) %>

$<%= price.to_formatted_s(decimals: 0) %> $<%= discounted.to_formatted_s(decimals: 0) %> Save <%= discounted.discount.to_percent_s %>

<% else %>

<% if price.free? %> Free <% else %> $<%= price.to_formatted_s(decimals: 0) %>/mo <% end %>

<% end %> <%= link_to "Choose #{plan.name}", subscribe_path(plan: plan.key), class: "button" %>
<% end %>
``` -------------------------------- ### Apply Discounts to Prices in Ruby Source: https://context7.com/rubymonolith/superfeature/llms.txt Shows how to apply discounts to a Price object using percentages, fixed amounts, or target prices. Operations return new Price objects, and discounts can be chained. The `apply_discount` method accepts various formats including strings and Discount objects. ```ruby include Superfeature::Pricing price = Price(100) # Percentage discount (25 = 25% off) discounted = price.discount_percent(25) discounted.amount # => 75.0 # Fixed dollar discount discounted = price.discount_fixed(20) discounted.amount # => 80.0 # Set to a specific target price discounted = price.discount_to(79) discounted.amount # => 79.0 # String format parsing price.apply_discount("20%").amount # => 80.0 price.apply_discount("$15").amount # => 85.0 price.apply_discount(10).amount # => 90.0 (numeric = dollars off) # Using Discount objects summer_sale = Discount::Percent.new(20) loyalty = Discount::Fixed.new(10) Price(100).apply_discount(summer_sale).amount # => 80.0 Price(100).apply_discount(loyalty).amount # => 90.0 # Chaining multiple discounts final = Price(100) .apply_discount("20%") # 100 -> 80 .apply_discount("$10") # 80 -> 70 final.amount # => 70.0 ``` -------------------------------- ### Creating and Applying Reusable Discount Objects (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Reusable discount logic can be encapsulated in `Discount` objects, such as `Discount::Percent` or `Discount::Fixed`. These objects can then be passed to `apply_discount` for consistent discount application. Multiple discount objects can be applied simultaneously. ```ruby include Superfeature summer_sale = Discount::Percent.new(20) loyalty = Discount::Fixed.new(10) Price(100).apply_discount(summer_sale).amount # => 80.0 Price(100).apply_discount(loyalty).amount # => 90.0 Price(100).apply_discount( Discount::Fixed.new(10), # $10 off first Discount::Percent.new(20) # then 20% off ).amount # => 72.0 (100 - 10 = 90, then 90 * 0.8 = 72) ``` -------------------------------- ### Plan Inheritance and Feature Overriding (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Explains how plans can inherit from each other and how to override features, such as enabling a previously disabled feature or increasing limits. ```ruby module Plans class Pro < Free def name = "Pro" def description = "For professionals" # Enable what was disabled in Free def priority_support = super.enable # Increase limits def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 100) end end ``` -------------------------------- ### Chaining Discounts and Price Itemization Source: https://context7.com/rubymonolith/superfeature/llms.txt Demonstrates how to chain multiple discounts (percentage and fixed amount) and access the itemization of price changes. The itemization provides a breakdown from the original price to the final price, including details of each discount applied. It also shows how to format this breakdown into a receipt-style text using the `inspector` method. ```ruby include Superfeature::Pricing final = Price(100).apply_discount("20%").apply_discount("$10") # Access via Price#itemization itemization = final.itemization itemization.original # => Price(100) itemization.final # => Price(70) itemization.count # => 3 # Enumerable - iterate from original to final itemization.each do |price| if price.discounted? puts "#{price.discount.to_formatted_s} off -> #{price.to_s}" else puts "Original: #{price.to_s}" end end # Output: # Original: 100 # 20% off -> 80 # $10 off -> 70 # Use Enumerable methods itemization.map(&:to_s) # => ["100", "80", "70"] itemization.select(&:discounted?) # => [Price(80), Price(70)] # Receipt-style formatting puts final.inspector # Output: # Original 100.00 # 20% off -20.00 # -------- # Subtotal 80.00 # 10.00 off -10.00 # -------- # FINAL 70.00 # Quick debugging with pp pp final ``` -------------------------------- ### Define Free and Paid Plan with Linked List (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md These Ruby classes define 'Free' and 'Paid' plans, inheriting from a base plan. They specify plan names, prices, and descriptions. Crucially, they use `next` and `previous` methods to create a linked list, enabling plan traversal within `Superfeature::Plan::Collection`. The 'Paid' plan overrides features from the base plan. ```ruby module Plans class Free < Base def name = "Free" def price = 0 def description = "Get started for free" def next = plan Paid end end module Plans class Paid < Free def name = "Paid" def price = 9.99 def description = "Full access to all features" # Override features from Base to enable them def priority_support = super.enable def next = nil def previous = plan Free end end ``` -------------------------------- ### Price Conversions in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code demonstrates various conversion methods for `Price` objects. It shows how to convert a price to a Float (`to_f`), Integer (`to_i`), BigDecimal (`to_d`), a display-friendly string (`to_s`), and a string with consistent decimals (`to_formatted_s`). ```ruby price.to_f # => 49.99 (Float) price.to_i # => 49 (Integer) price.to_d # => BigDecimal("49.99") price.to_s # => "49" or "49.99" (display-friendly, omits .00) price.to_formatted_s(decimals: 2) # => "49.99" (consistent decimals) ``` -------------------------------- ### Add Plan Column to Users Table (SQL) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This SQL command adds a 'plan' column to the 'users' table. It's defined as a string with a default value of 'free', intended to store the user's current plan identifier. This is a prerequisite for associating users with specific plans. ```sql add_column :users, :plan, :string, default: "free" ``` -------------------------------- ### Read Discount Information in Ruby Source: https://context7.com/rubymonolith/superfeature/llms.txt Explains how to access discount details after applying them, including the amount saved in the last step and cumulative savings. It also shows how to traverse the chain of previous price modifications. ```ruby include Superfeature::Pricing # Single discount price = Price(100).apply_discount("25%") price.amount # => 75.0 price.discounted? # => true price.original.amount # => 100.0 price.discount.fixed # => 25.0 (dollars saved this step) price.discount.percent # => 25.0 (percent of original this step) price.discount.to_fixed_s # => "25.00" price.discount.to_percent_s # => "25%" price.discount.to_formatted_s # => "25%" (natural format from source) price.discount.source # => Discount::Percent object # Chained discounts - cumulative savings final = Price(100).apply_discount("20%").apply_discount("$10") # 100 -> 80 -> 70 final.discount.fixed # => 10.0 (last step only) final.discount.percent # => 10.0 (last step as % of original) final.savings.fixed # => 30.0 (total saved from original) final.savings.percent # => 30.0 (total % off original) final.savings.to_fixed_s # => "30.00" final.savings.to_percent_s # => "30%" # Walking the price chain final.previous.amount # => 80.0 (immediate parent) final.previous.previous.amount # => 100.0 (one more step back) final.original.amount # => 100.0 (walks all the way back) ``` -------------------------------- ### Define Plan with Features (Ruby) Source: https://context7.com/rubymonolith/superfeature/llms.txt Demonstrates how to define a base plan in Ruby using Superfeature::Plan. It showcases the `feature` macro for defining boolean flags, hard limits, soft limits, and unlimited quotas, associating them with user data and business logic. ```ruby module Plans class Base < Superfeature::Plan attr_reader :user def initialize(user) @user = user end # Boolean features - simple on/off flags feature def priority_support = disable("Priority support") feature def api_access = disable("API access") # Hard limit - strict maximum that cannot be exceeded feature def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 5) # Soft limit - warning boundary before hard cutoff feature def storage_gb = soft_limit("Storage", quantity: user.storage_used_gb, soft_limit: 100, hard_limit: 150 ) # Unlimited - no restrictions feature def team_members = unlimited("Team members", quantity: user.team_members_count) end end ``` -------------------------------- ### Define Features in a Plan (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Shows how to define features within a plan using the `feature` method, enabling or disabling them with descriptive names. ```ruby module Plans class Free < Superfeature::Plan feature def priority_support = disable("Priority support") feature def api_access = enable("API access") end end ``` -------------------------------- ### Price Math Operations in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code showcases basic arithmetic operations that can be performed on `Price` objects. It includes addition, subtraction, multiplication, and division, demonstrating that these operations work seamlessly with integers as well. ```ruby Price(100) + 20 # => Price(120) Price(100) - 20 # => Price(80) Price(100) * 2 # => Price(200) Price(100) / 4 # => Price(25) 10 + Price(5) # => Price(15) ``` -------------------------------- ### Checking Plan Limits in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code shows how to check various limits and boolean features associated with a user's plan. It includes checking for exceeded hard limits like API calls, accessing current usage, maximum allowed, and remaining quantities, as well as checking boolean features like `priority_support`. ```ruby plan = current_user.plan # Hard limits if plan.api_calls.exceeded? render "api_limit_reached" end puts plan.api_calls.quantity # current usage puts plan.api_calls.maximum # max allowed puts plan.api_calls.remaining # how many left # Boolean features plan.priority_support.enabled? # => false plan.priority_support.disabled? # => true ``` -------------------------------- ### Core Extensions for Numeric and String Source: https://context7.com/rubymonolith/superfeature/llms.txt Details the opt-in core extensions that add pricing-related methods to Ruby's built-in `Numeric` and `String` classes. These extensions allow for easy conversion to `Price` objects and application of discounts directly on numbers and strings. ```ruby # Opt-in to core extensions require "superfeature/core_ext" # Numeric extensions 100.to_price # => Price(100) 100.discounted_by(20) # => Price(80) (fixed $20 off) 100.discounted_by(20.percent_off) # => Price(80) (20% off) 100.discounted_by("25%") # => Price(75) # String extensions "49.99".to_price # => Price(49.99) "$100".to_price # => Price(100) # Combining extensions 99.discounted_by(10.percent_off).round_up(9) # => Price(99) ``` -------------------------------- ### Create and Manipulate Prices in Ruby Source: https://context7.com/rubymonolith/superfeature/llms.txt Demonstrates how to create Price objects using different constructors and perform various conversions, queries, comparisons, and mathematical operations. Prices are immutable and use BigDecimal internally for precision. ```ruby include Superfeature::Pricing # Create prices price = Price(49.99) price = Price.new(49.99) # Conversions price.amount # => BigDecimal("49.99") price.to_f # => 49.99 price.to_i # => 49 price.to_d # => BigDecimal("49.99") price.to_s # => "49.99" (display-friendly) price.to_formatted_s(decimals: 2) # => "49.99" # Queries Price(0).zero? # => true Price(0).free? # => true (alias) Price(100).positive? # => true Price(100).paid? # => true (alias) # Comparisons Price(100) > Price(50) # => true Price(100) == 100 # => true Price(100) < 200 # => true # Math operations (returns new Price) Price(100) + 20 # => Price(120) Price(100) - 20 # => Price(80) Price(100) * 2 # => Price(200) Price(100) / 4 # => Price(25) 10 + Price(5) # => Price(15) (via coercion) # Range clamping (default: 0..) Price(-10).amount # => 0 (clamped to minimum 0) Price(-10, range: nil).amount # => -10 (no clamping) Price(50, range: 10..100).amount # => 50 (within range) ``` -------------------------------- ### Implement User Plan Association (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code adds a `plan` method to the `User` model. It uses `Superfeature::Plan::Collection` to find the correct plan object based on the `plan_key` stored in the database. It handles cases where the plan key might be missing or invalid, defaulting to 'free'. ```ruby class User < ApplicationRecord def plan @plan ||= Superfeature::Plan::Collection.new(Plans::Free.new(self)).find(plan_key) end def plan_key self[:plan]&.to_sym || :free end end ``` -------------------------------- ### Display Pricing Plans and Promotions (ERB) Source: https://context7.com/rubymonolith/superfeature/llms.txt This ERB template renders pricing information for different plans, including promotional discounts. It iterates through plans and features, displaying prices and feature availability. Dependencies include the `@promo` object for promotions and an array of `@plans`. ```erb

Pricing

<% if @promo %>
<%= @promo.name %>: Save <%= @promo.percent_off %>% on all plans!
<% end %>
<% @plans.each do |plan| %>

<%= plan.name %>

<% price = plan.price %> <% if @promo && price.paid? %> <% discounted = price.apply_discount(@promo) %>

$<%= price.to_formatted_s(decimals: 0) %> $<%= discounted.to_formatted_s(decimals: 0) %> Save <%= discounted.discount.to_percent_s %>

<% else %>

<% if price.free? %> Free <% else %> $<%= price.to_formatted_s(decimals: 0) %>/mo <% end %>

<% end %> <%= link_to "Choose #{plan.name}", subscribe_path(plan: plan.key), class: "button" %>
<% end %>
``` -------------------------------- ### Generating a New Plan with Rails Generator Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This bash command uses the `rails generate` command to create a new plan, specifically named 'Enterprise', within the Superfeature framework. It generates the necessary Ruby file (`app/plans/enterprise.rb`) with a base structure for defining plan attributes and features. ```bash $ rails generate superfeature:plan Enterprise ``` -------------------------------- ### Generate New Plan with Superfeature (Bash) Source: https://context7.com/rubymonolith/superfeature/llms.txt This command generates a new plan file for a specified plan name (e.g., 'Enterprise') using the Superfeature generator. It creates a new Ruby file within the `app/plans/` directory for the custom plan. ```bash # Generate a new plan rails generate superfeature:plan Enterprise # Creates: # - app/plans/enterprise.rb ``` -------------------------------- ### Define Feature Limits in Ruby Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Demonstrates defining features as limits, using `hard_limit` and `soft_limit` to specify quantities and thresholds. ```ruby module Plans class Free < Superfeature::Plan feature def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 5) feature def storage_gb = soft_limit("Storage", quantity: user.storage_gb, soft_limit: 1, hard_limit: 2) end end ``` -------------------------------- ### Plan Inheritance and Feature Overrides (Ruby) Source: https://context7.com/rubymonolith/superfeature/llms.txt Illustrates plan inheritance and feature overrides in Ruby using Superfeature. Higher-tier plans can inherit from lower tiers, enabling disabled features using `super.enable` or redefining limits. This allows for tiered feature access and pricing. ```ruby module Plans class Free < Superfeature::Plan def name = "Free" def description = "Get started for free" def price = Price(0) def next = plan Pro end class Pro < Free def name = "Pro" def description = "For professionals" def price = Price(29) # Enable what was disabled in Free def priority_support = super.enable def api_access = super.enable # Increase limits def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 100) def previous = plan Free def next = plan Enterprise end class Enterprise < Pro def name = "Enterprise" def description = "For large teams" def price = Price(99) # Unlimited projects for enterprise def projects = unlimited("Projects", quantity: user.projects_count) def previous = plan Pro end end ``` -------------------------------- ### Defining Pricing Plans with Features (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md The `Superfeature::Plan` base class allows for the definition of different pricing tiers. Plans can specify features like project limits, API access, and support levels, which can be enabled or disabled. Features can be defined using methods like `hard_limit`, `unlimited`, and `enable`/`disable`. ```ruby module Plans class Base < Superfeature::Plan attr_reader :user def initialize(user) @user = user end feature def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 3) feature def api_access = disable("API access") feature def priority_support = disable("Priority support") end class Free < Base def name = "Free" def price = Price(0) def next = plan Pro end class Pro < Free def name = "Pro" def price = Price(29) def projects = hard_limit("Projects", quantity: user.projects_count, maximum: 100) def api_access = super.enable def previous = plan Free def next = plan Enterprise end class Enterprise < Pro def name = "Enterprise" def price = Price(99) def projects = unlimited("Projects", quantity: user.projects_count) def api_access = super.enable def priority_support = super.enable def previous = plan Pro end end ``` -------------------------------- ### Check Feature Limit Status (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Shows how to check if a feature limit has been exceeded or if a warning threshold has been reached, and how to determine remaining capacity. ```ruby plan.projects.exceeded? # => true if over 5 plan.projects.remaining # => how many left plan.storage_gb.warning? # => true if between soft and hard limit ``` -------------------------------- ### Calculating Cumulative Savings with Chained Discounts (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md When multiple discounts are applied sequentially, the `savings` attribute provides the total discount accumulated from the original price. Individual discount details (`fixed`, `percent`) only reflect the most recent discount applied. ```ruby price = Price(100).apply_discount("20%").apply_discount("$10") # 100 -> 80 -> 70 price.discount.fixed # => 10.0 (last step only) price.discount.percent # => 10.0 (last step as % of original) price.savings.fixed # => 30.0 (total saved from original) price.savings.percent # => 30.0 (total % off original) price.savings.to_fixed_s # => "30.00" price.savings.to_percent_s # => "30%" ``` -------------------------------- ### Check Features in Application Code (Ruby) Source: https://context7.com/rubymonolith/superfeature/llms.txt Shows how to check feature status in Ruby application code (controllers and views) using Superfeature's plan objects. It demonstrates methods like `enabled?`, `disabled?`, `exceeded?`, and `remaining` for both boolean features and limits. ```ruby # In a controller class DashboardController < ApplicationController def show plan = Plans::Pro.new(current_user) # Boolean feature checks if plan.priority_support.enabled? @support_queue = :priority end if plan.api_access.disabled? redirect_to upgrade_path, alert: "API access requires Pro plan" return end # Limit checks if plan.projects.exceeded? flash[:warning] = "You've reached your project limit (#{plan.projects.maximum})" end @projects_remaining = plan.projects.remaining # => 95 (if 5 used of 100) @storage_warning = plan.storage_gb.exceeded? # true if over soft_limit end end # In a view <% if current_plan.api_access.enabled? %>
<%= render "api_key_form" %>
<% else %>

Upgrade to Pro to access the API

<% end %> # Feature type checks plan.projects.hard_limit? # => true plan.projects.soft_limit? # => false plan.projects.unlimited? # => false plan.api_access.boolean? # => true ``` -------------------------------- ### Chaining Price Operations and Traversing the Chain (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md Multiple pricing operations like discounts and rounding can be chained together. Each operation returns a new `Price` object, maintaining a reference to the previous state. This allows for easy traversal back through the chain using the `previous` attribute. ```ruby Price(100) .discount_percent(20) # => Price(80) .discount_fixed(10) # => Price(70) .round_up(9) # => Price(79) final = Price(100).discount_percent(20).round_up(9) final.amount # => 89 final.previous.amount # => 80 final.original.amount # => 100 final.discounted? # => true ``` -------------------------------- ### Round Prices to Specific Endings in Ruby Source: https://context7.com/rubymonolith/superfeature/llms.txt Demonstrates how to round Price objects to specific marketing-friendly endings, such as $X.99 or $X9. Includes options for rounding to the nearest, rounding up, or rounding down. ```ruby include Superfeature::Pricing price = Price(50) # Round to nearest ending price.round(9) # => Price(49) (nearest ending in 9) price.round_up(9) # => Price(59) (round up to ending in 9) price.round_down(9) # => Price(49) (round down to ending in 9) # Decimal endings (e.g., $X.99) Price(2.50).round(0.99) # => Price(2.99) Price(2.50).round_up(0.99) # => Price(2.99) Price(2.50).round_down(0.99) # => Price(1.99) # Larger endings ($99, $199, $299...) Price(150).round_up(99) # => Price(199) Price(250).round_down(99) # => Price(199) ``` -------------------------------- ### Define Base Plan Features with Limits (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code defines a base plan class using Superfeature. It sets up various feature types: boolean flags (priority_support, phone_support), hard limits (api_calls), soft limits with overages (storage_gb), and unlimited features (projects). It requires a `user` object for quantity-based features and uses helper methods for feature creation. ```ruby module Plans class Base < Superfeature::Plan attr_reader :user def initialize(user) @user = user end # Boolean features - simple on/off flags feature def priority_support = disable("Priority support", group: "Support") feature def phone_support = disable("Phone support", group: "Support") # Hard limits - strict maximum that cannot be exceeded feature def api_calls = hard_limit("API calls", group: "Limits", quantity: user.api_calls_count, maximum: 1000) # Soft limits - has a soft and hard boundary for overages feature def storage_gb = soft_limit("Storage", group: "Limits", quantity: user.storage_used_gb, soft_limit: 100, hard_limit: 150) # Unlimited - no restrictions feature def projects = unlimited("Projects", group: "Limits", quantity: user.projects_count) protected def feature(name, **options) Features::Base.new(name, **options) end end end ``` -------------------------------- ### Check Feature Access in Controller (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby controller action demonstrates how to check if a user has a specific feature enabled using their plan. If the 'moderation' feature is enabled, it renders a 'moderation' view; otherwise, it redirects the user to an upgrade path. It relies on a `current_plan` helper method. ```ruby class ModerationController < ApplicationController def show if current_plan.moderation.enabled? render "moderation" else redirect_to upgrade_path end end private def current_plan @current_plan ||= current_user.plan end helper_method :current_plan end ``` -------------------------------- ### Rounding Prices to Specific Endings (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md The `Price` object supports rounding to specific numerical endings. Methods like `round`, `round_up`, and `round_down` allow you to adjust the price to the nearest, next highest, or next lowest value ending in a specified digit or decimal. ```ruby price = Price(50) price.round(9) # => Price(49) - nearest ending in 9 price.round_up(9) # => Price(59) - round up to ending in 9 price.round_down(9) # => Price(49) - round down to ending in 9 price.round(0.99) # => Price(49.99) - nearest ending in .99 ``` -------------------------------- ### 422 Error Message (Plain Text) Source: https://github.com/rubymonolith/superfeature/blob/main/spec/dummy/public/422.html This is the user-facing plain text message displayed for a 422 Unprocessable Entity error. It informs the user that their requested change was rejected and suggests checking logs if they are the application owner. No specific dependencies are mentioned. ```text The change you wanted was rejected. =================================== Maybe you tried to change something you didn't have access to. If you are the application owner check the logs for more information. ``` -------------------------------- ### Define Feature Base Class with Name and Group (Ruby) Source: https://github.com/rubymonolith/superfeature/blob/main/README.md This Ruby code defines a base feature class that extends `Superfeature::Feature`. It adds `name` and `group` attributes for display purposes, which are initialized during object creation. This class serves as a foundation for more specific feature definitions. ```ruby module Plans module Features class Base < Superfeature::Feature attr_reader :name, :group def initialize(name = nil, group: nil, **) super(**) @name = name @group = group end end end end ```