From 1b36ba02a1073c1ea95ac6c26f336390b9f48f25 Mon Sep 17 00:00:00 2001 From: Brandon Keepers Date: Tue, 1 Aug 2023 12:57:00 -0400 Subject: [PATCH] Load environment, add list subcommand --- lib/flipper/cli.rb | 24 ++++--------------- lib/flipper/cli/command.rb | 46 +++++++++++++++++++++++++++++++++++- lib/flipper/cli/list.rb | 13 ++++++++++ lib/flipper/cli/toggle.rb | 42 +++++++------------------------- spec/fixtures/environment.rb | 1 + spec/flipper/cli_spec.rb | 39 +++++++++++++++++++++++++----- 6 files changed, 106 insertions(+), 59 deletions(-) create mode 100644 lib/flipper/cli/list.rb create mode 100644 spec/fixtures/environment.rb diff --git a/lib/flipper/cli.rb b/lib/flipper/cli.rb index be6bb43d4..3d3f64c77 100644 --- a/lib/flipper/cli.rb +++ b/lib/flipper/cli.rb @@ -2,6 +2,7 @@ require 'flipper/cli/command' require 'flipper/cli/enable' require 'flipper/cli/disable' +require 'flipper/cli/list' require 'flipper/cli/help' module Flipper @@ -11,29 +12,14 @@ def self.run(argv = ARGV) end class Base < Command - # Path to the local Rails application's environment configuration. - # Requiring this loads the application's configuration and classes. - RAILS_ENVIRONMENT_RB = File.expand_path("config/environment") - def initialize(**args) # Program is always flipper, no matter how it's invoked super program_name: 'flipper', **args - subcommand('enable', Flipper::CLI::Enable) - subcommand('disable', Flipper::CLI::Disable) - subcommand('help', Flipper::CLI::Help) - end - - def spawn(subcommand) - # Initialize flipper before calling any subcommands - initialize_flipper! - super - end - - private - - def initialize_flipper! - # require RAILS_ENVIRONMENT_RB + subcommand 'enable', Flipper::CLI::Enable + subcommand 'disable', Flipper::CLI::Disable + subcommand 'list', Flipper::CLI::List + subcommand 'help', Flipper::CLI::Help end end end diff --git a/lib/flipper/cli/command.rb b/lib/flipper/cli/command.rb index 2e3b9021e..e1503dcde 100644 --- a/lib/flipper/cli/command.rb +++ b/lib/flipper/cli/command.rb @@ -3,6 +3,9 @@ module Flipper module CLI class Command < OptionParser + # Path to the local Rails application's environment configuration. + DEFAULT_REQUIRE = "./config/environment" + attr_reader :options, :subcommands, :parent def initialize(parent: nil, options: parent&.options || {}, program_name: nil) @@ -12,6 +15,12 @@ def initialize(parent: nil, options: parent&.options || {}, program_name: nil) @program_name = program_name @parent = parent + options[:require] ||= ENV.fetch("FLIPPER_REQUIRE", DEFAULT_REQUIRE) + + on_tail('-r path', "The path to load your application. Default: #{options[:require]}") do |path| + options[:require] = path + end + # Options available on all commands on_tail('-h', '--help', 'Print help message') do puts help @@ -43,7 +52,12 @@ def subcommand(name, command) @subcommands[name] = command end - private + def load_environment! + require File.expand_path(options[:require]) + rescue LoadError => e + warn e.message + exit 1 + end # Internal: Initialize a subcommand with state from the current command def spawn(subcommand) @@ -52,6 +66,36 @@ def spawn(subcommand) program_name: "#{program_name} #{subcommand}" ) end + + private + + def feature_summary(feature) + summary = case feature.state + when :on + "fully enabled" + when :off + "disabled" + else + "enabled for " + feature.enabled_gates.map do |gate| + case gate.name + when :actor + pluralize feature.actors_value.size, 'actor', 'actors' + when :group + pluralize feature.groups_value.size, 'group', 'groups' + when :percentage_of_actors + "#{feature.percentage_of_actors_value}% of actors" + when :percentage_of_time + "#{feature.percentage_of_time_value}% of actors" + end + end.join(', ') + end + + "#{feature.name.to_s.inspect} is #{summary}" + end + + def pluralize(count, singular, plural) + "#{count} #{count == 1 ? singular : plural}" + end end end end diff --git a/lib/flipper/cli/list.rb b/lib/flipper/cli/list.rb new file mode 100644 index 000000000..c3a4310b5 --- /dev/null +++ b/lib/flipper/cli/list.rb @@ -0,0 +1,13 @@ +module Flipper + module CLI + class List < Command + def call + load_environment! + + Flipper.features.each do |feature| + puts feature_summary(feature) + end + end + end + end +end diff --git a/lib/flipper/cli/toggle.rb b/lib/flipper/cli/toggle.rb index 3c0e1baf4..91b8ab234 100644 --- a/lib/flipper/cli/toggle.rb +++ b/lib/flipper/cli/toggle.rb @@ -4,6 +4,8 @@ class Toggle < Command def initialize(**args) super + self.banner = "Usage: #{program_name} [options] " + @values = [] on('-a id', '--actor=id', "#{action} for an actor") do |id| @@ -24,44 +26,18 @@ def action raise NotImplementedError end - def call(feature_name) - feature = Flipper.feature(feature_name) + def call(feature) + load_environment! - if @values.empty? - feature.send(action) - else - @values.each { |value| feature.send(action, value) } - end + f = Flipper.feature(feature) - puts feature_summary(feature) - end - - def feature_summary(feature) - summary = case feature.state - when :on - "fully enabled" - when :off - "disabled" + if @values.empty? + f.send(action) else - "enabled for " + feature.enabled_gates.map do |gate| - case gate.name - when :actor - pluralize feature.actors_value.size, 'actor', 'actors' - when :group - pluralize feature.groups_value.size, 'group', 'groups' - when :percentage_of_actors - "#{feature.percentage_of_actors_value}% of actors" - when :percentage_of_time - "#{feature.percentage_of_time_value}% of actors" - end - end.join(', ') + @values.each { |value| f.send(action, value) } end - "#{feature.name.to_s.inspect} is #{summary}" - end - - def pluralize(count, singular, plural) - "#{count} #{count == 1 ? singular : plural}" + puts feature_summary(f) end end end diff --git a/spec/fixtures/environment.rb b/spec/fixtures/environment.rb new file mode 100644 index 000000000..0bb57b15c --- /dev/null +++ b/spec/fixtures/environment.rb @@ -0,0 +1 @@ +# Placeholder for config/environment.rb diff --git a/spec/flipper/cli_spec.rb b/spec/flipper/cli_spec.rb index 20047e409..83b6f9d7b 100644 --- a/spec/flipper/cli_spec.rb +++ b/spec/flipper/cli_spec.rb @@ -1,9 +1,19 @@ -require 'flipper/cli' +require "flipper/cli" RSpec.describe Flipper::CLI do - subject(:argv) { self.class.parent_groups.map {|g| g.metadata[:description_args] }.reverse.flatten.drop(1) } + # Infer the command from the description + subject(:argv) do + descriptions = self.class.parent_groups.map {|g| g.metadata[:description_args] }.reverse.flatten.drop(1) + descriptions.map { |arg| arg.split }.flatten + end + subject { run argv } + before do + ENV["FLIPPER_REQUIRE"] = "./spec/fixtures/environment" + end + + describe "enable" do describe "feature" do it do @@ -12,10 +22,10 @@ end end - describe %w(-a User;1 feature) do + describe "-a User;1 feature" do it do expect(subject).to have_attributes(status: 0, stdout: /"feature" is enabled for 1 actor/) - expect(Flipper).to be_enabled(:feature, Flipper::Actor.new('User;1')) + expect(Flipper).to be_enabled(:feature, Flipper::Actor.new("User;1")) end end end @@ -31,13 +41,30 @@ end end - ['-h', '--help', 'help'].each do |arg| + describe "list" do + before do + Flipper.enable :foo + Flipper.disable :bar + end + + it "lists features" do + expect(subject).to have_attributes(status: 0, stdout: /foo.*fully enabled/) + expect(subject).to have_attributes(status: 0, stdout: /bar.*disabled/) + end + end + + ["-h", "--help", "help"].each do |arg| describe arg do it { should have_attributes(status: 0, stdout: /Usage: flipper/) } end end - describe 'nope' do + describe "help enable" do + + it { should have_attributes(status: 0, stdout: /Usage: flipper enable \[options\] /) } + end + + describe "nope" do it { should have_attributes(status: 1, stderr: /Unknown command: nope/) } end