Class: Magick::AdminUI::FeaturesController

Inherits:
ActionController::Base
  • Object
show all
Includes:
ActionController::RequestForgeryProtection
Defined in:
app/controllers/magick/adminui/features_controller.rb

Constant Summary collapse

FORM_UNMANAGED_TARGETING_KEYS =

Targeting rules the edit form has no fields for. They are carried over from the current state on every submit so a form save can never silently destroy them. (:variants is preserved by replace_targeting itself.)

%i[
  group excluded_groups ip_address excluded_ip_addresses
  date_range custom_attributes complex_conditions
].freeze

Instance Method Summary collapse

Instance Method Details

#available_rolesObject



36
37
38
# File 'app/controllers/magick/adminui/features_controller.rb', line 36

def available_roles
  Magick::AdminUI.config.available_roles || []
end

#available_tagsObject



40
41
42
# File 'app/controllers/magick/adminui/features_controller.rb', line 40

def available_tags
  Magick::AdminUI.config.tags
end

#disableObject



103
104
105
106
# File 'app/controllers/magick/adminui/features_controller.rb', line 103

def disable
  @feature.disable
  redirect_to magick_admin_ui.features_path, notice: 'Feature disabled'
end

#disable_for_roleObject



123
124
125
126
127
128
129
130
131
# File 'app/controllers/magick/adminui/features_controller.rb', line 123

def disable_for_role
  role = params[:role]
  if role.present?
    @feature.disable_for_role(role)
    redirect_to magick_admin_ui.feature_path(@feature.name), notice: "Feature disabled for role: #{role}"
  else
    redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Role is required'
  end
end

#editObject



73
74
# File 'app/controllers/magick/adminui/features_controller.rb', line 73

def edit
end

#enableObject



98
99
100
101
# File 'app/controllers/magick/adminui/features_controller.rb', line 98

def enable
  @feature.enable
  redirect_to magick_admin_ui.features_path, notice: 'Feature enabled'
end

#enable_for_roleObject



113
114
115
116
117
118
119
120
121
# File 'app/controllers/magick/adminui/features_controller.rb', line 113

def enable_for_role
  role = params[:role]
  if role.present?
    @feature.enable_for_role(role)
    redirect_to magick_admin_ui.feature_path(@feature.name), notice: "Feature enabled for role: #{role}"
  else
    redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Role is required'
  end
end

#enable_for_userObject



108
109
110
111
# File 'app/controllers/magick/adminui/features_controller.rb', line 108

def enable_for_user
  @feature.enable_for_user(params[:user_id])
  redirect_to magick_admin_ui.feature_path(@feature.name), notice: 'Feature enabled for user'
end

#indexObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'app/controllers/magick/adminui/features_controller.rb', line 48

def index
  @features = Magick.features.values

  # Filter by group if provided
  if params[:group].present?
    @features = @features.select { |f| f.group == params[:group] }
  end

  # Filter by search query (name or description)
  if params[:search].present?
    search_term = params[:search].downcase
    @features = @features.select do |f|
      f.name.downcase.include?(search_term) ||
      (f.display_name && f.display_name.downcase.include?(search_term)) ||
      (f.description && f.description.downcase.include?(search_term))
    end
  end

  # Get all available groups for filter dropdown
  @available_groups = Magick.features.values.map(&:group).compact.uniq.sort
end

#magick_admin_uiObject



32
33
34
# File 'app/controllers/magick/adminui/features_controller.rb', line 32

def magick_admin_ui
  Magick::AdminUI::Engine.routes.url_helpers
end

#partially_enabled?(feature) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
# File 'app/controllers/magick/adminui/features_controller.rb', line 44

def partially_enabled?(feature)
  (feature.targeting || {}).any?
end

#showObject



70
71
# File 'app/controllers/magick/adminui/features_controller.rb', line 70

def show
end

#updateObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/controllers/magick/adminui/features_controller.rb', line 76

def update
  # Update group if provided
  if params.key?(:group)
    @feature.set_group(params[:group])
  end

  if @feature.type == :boolean
    # For boolean features, checkbox sends 'true' when checked, nothing when unchecked
    # Rails form helpers handle this - if checkbox is unchecked, params[:value] will be nil
    value = params[:value] == 'true'
    @feature.set_value(value)
  elsif params[:value].present?
    # For string/number features, convert to appropriate type
    value = params[:value]
    if @feature.type == :number
      value = value.include?('.') ? value.to_f : value.to_i
    end
    @feature.set_value(value)
  end
  redirect_to magick_admin_ui.feature_path(@feature.name), notice: 'Feature updated successfully'
end

#update_targetingObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'app/controllers/magick/adminui/features_controller.rb', line 133

def update_targeting
  targeting_params = params[:targeting] || {}
  unless hash_like?(targeting_params)
    redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Invalid targeting payload.'
    return
  end

  # Ensure we're using the registered feature instance
  feature_name = @feature.name.to_s
  @feature = Magick.features[feature_name] if Magick.features.key?(feature_name)

  # One declarative write: one audit entry + one version per submit.
  @feature.replace_targeting(desired_targeting_from_form(targeting_params))
  @feature.reload

  redirect_to magick_admin_ui.feature_path(@feature.name), notice: 'Targeting updated successfully'
rescue Magick::InvalidTargetingError => e
  redirect_to magick_admin_ui.feature_path(@feature.name), alert: "Invalid targeting: #{e.message}"
rescue StandardError => e
  Rails.logger.error "Magick: Error updating targeting for #{@feature.name}: #{e.class}: #{e.message}\n#{e.backtrace.first(5).join("\n")}" if defined?(Rails)
  redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Could not update targeting — see server logs for details.'
end

#update_variantsObject



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'app/controllers/magick/adminui/features_controller.rb', line 156

def update_variants
  variants_data = []

  if params[:variants].present? && !hash_like?(params[:variants])
    redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Invalid variants payload.'
    return
  end

  if params[:variants].present?
    params[:variants].each do |_index, variant_params|
      next if variant_params[:name].blank?

      variants_data << {
        name: variant_params[:name].strip,
        value: variant_params[:value].to_s.strip,
        weight: (variant_params[:weight].presence || 0).to_f
      }
    end
  end

  if variants_data.any?
    @feature.set_variants(variants_data)
  else
    # Clear variants if all removed
    @feature.instance_variable_get(:@targeting)&.delete(:variants)
    @feature.save_targeting
  end

  redirect_to magick_admin_ui.feature_path(@feature.name), notice: 'Variants updated successfully'
rescue StandardError => e
  Rails.logger.error "Magick: Error updating variants for #{@feature.name}: #{e.class}: #{e.message}" if defined?(Rails)
  redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Could not update variants — see server logs for details.'
end