Class: Dugway::Drops::FeaturesDrop

Inherits:
BaseDrop
  • Object
show all
Defined in:
lib/dugway/liquid/drops/features_drop.rb

Overview

Provides access to specific account feature flags within Liquid templates, mirroring the behavior of the storefront’s FeaturesDrop but reading configuration from the Dugway store configuration (e.g., dugway.json).

Feature flags can have a default status (enabled or disabled). This status, along with the list of exposed features, opt-ins, and opt-outs, should be defined under a top-level “features” key in the store configuration file, making it a peer to “store” and “customization”.

Example structure expected within the store configuration (e.g., dugway.json): {

"store": { ... },
"customization": { ... },
"features": {
  "definitions": {
    "theme_bnpl_messaging": "enabled_by_default",
    "theme_category_collages": "disabled_by_default"
  },
  "opt_ins": ["some_opted_in_feature"],
  "opt_outs": ["some_opted_out_feature"]
}

} The ‘FeaturesDrop` receives the top-level `features` hash as its `source` via the ThemeDrop.

Usage in Liquid:

{% if features.has_theme_bnpl_messaging %} ... {% endif %}
Note: Liquid might require the trailing '?' depending on context,
      but this implementation handles calls without it.

Instance Attribute Summary

Attributes inherited from BaseDrop

#params, #request, #source

Instance Method Summary collapse

Methods inherited from BaseDrop

#cart, #context=, #error, #errors, #initialize, #method_missing, #store, #theme

Constructor Details

This class inherits a constructor from Dugway::Drops::BaseDrop

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Dugway::Drops::BaseDrop

Instance Method Details

#before_method(method_or_key) ⇒ Object

Override BaseDrop’s ‘before_method` to intercept `has_…` calls directly. This is called by BaseDrop’s ‘method_missing` and allows us to intercept `has_…` calls before BaseDrop tries to resolve them against the source hash.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/dugway/liquid/drops/features_drop.rb', line 74

def before_method(method_or_key)
  method_str = method_or_key.to_s

  if method_str.start_with?('has_')
    # Extract feature name, removing 'has_' prefix and optional trailing '?'
    # to handle calls like `features.has_feature_name` or `features.has_feature_name?`
    feature_name = method_str.sub(/^has_/, '').chomp('?')

    # Check if this feature is defined
    # If the extracted name doesn't match a defined feature, it's not available.
    unless feature_definitions.key?(feature_name)
      return false
    end

    # If defined, calculate availability based on defaults and overrides.
    is_feature_available?(feature_name)
  else
    # If it's not a 'has_' method, delegate to BaseDrop's original logic.
    super(method_or_key)
  end
end

#feature_definitionsHash<String, String>

Memoized hash of feature definitions (name => default_status_string) read from the source (store configuration data).

Returns:

  • (Hash<String, String>)


39
40
41
# File 'lib/dugway/liquid/drops/features_drop.rb', line 39

def feature_definitions
  @feature_definitions ||= source&.fetch('definitions', {}) || {}
end

#liquid_method_missing(method_name) ⇒ Object

Keep liquid_method_missing for compatibility or explicit Liquid contexts, We keep liquid_method_missing defined as it’s the official Liquid hook, although our ‘before_method` override likely handles most cases in Liquid 3. This provides potential compatibility if Liquid internals change or call this directly.



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/dugway/liquid/drops/features_drop.rb', line 100

def liquid_method_missing(method_name)
  method_str = method_name.to_s

  if method_str.start_with?('has_')
    feature_name = method_str.sub(/^has_/, '').chomp('?')
    # Check definition and calculate availability, returning false if undefined.
    feature_definitions.key?(feature_name) ? is_feature_available?(feature_name) : false
  else
    # Fallback for non-'has_' methods.
    super
  end
end

#opt_insArray<String>

Returns a memoized list of features this account has explicitly opted into, filtered to only include features defined in the definitions.

Returns:

  • (Array<String>)

    List of opted-in feature names.



46
47
48
49
50
51
52
53
54
55
# File 'lib/dugway/liquid/drops/features_drop.rb', line 46

def opt_ins
  return @opt_ins if defined?(@opt_ins)

  source_opt_ins = source&.fetch('opt_ins', []) || []
  # Ensure source data is treated as a Set for efficient intersection
  source_set = source_opt_ins.respond_to?(:to_set) ? source_opt_ins.to_set : Set.new(Array(source_opt_ins))

  # Only keep opt-ins that correspond to defined features
  @opt_ins = (Set.new(feature_definitions.keys) & source_set).to_a
end

#opt_outsArray<String>

Returns a memoized list of features this account has explicitly opted *out of*, filtered to only include features defined in the definitions.

Returns:

  • (Array<String>)

    List of opted-out feature names.



60
61
62
63
64
65
66
67
68
69
# File 'lib/dugway/liquid/drops/features_drop.rb', line 60

def opt_outs
  return @opt_outs if defined?(@opt_outs)

  source_opt_outs = source&.fetch('opt_outs', []) || []
  # Ensure source data is treated as a Set for efficient intersection
  source_set = source_opt_outs.respond_to?(:to_set) ? source_opt_outs.to_set : Set.new(Array(source_opt_outs))

  # Only keep opt-outs that correspond to defined features
  @opt_outs = (Set.new(feature_definitions.keys) & source_set).to_a
end