Class: Flipper::CLI
- Inherits:
-
OptionParser
- Object
- OptionParser
- Flipper::CLI
- Defined in:
- lib/flipper/cli.rb
Defined Under Namespace
Modules: ShellOutput Classes: Command
Constant Summary collapse
- DEFAULT_REQUIRE =
Path to the local Rails application’s environment configuration.
"./config/environment"
Instance Attribute Summary collapse
-
#shell ⇒ Object
Returns the value of attribute shell.
Class Method Summary collapse
Instance Method Summary collapse
- #colorize(text, colors) ⇒ Object
-
#command(name, &block) ⇒ Object
Helper method to define a new command.
- #feature_details(feature) ⇒ Object
- #feature_summary(features) ⇒ Object
- #indent(text, spaces) ⇒ Object
-
#initialize(stdout: $stdout, stderr: $stderr, shell: Bundler::Thor::Base.shell.new) ⇒ CLI
constructor
A new instance of CLI.
- #load_environment! ⇒ Object
- #pluralize(count, singular, plural) ⇒ Object
- #run(argv) ⇒ Object
- #ui ⇒ Object
Constructor Details
#initialize(stdout: $stdout, stderr: $stderr, shell: Bundler::Thor::Base.shell.new) ⇒ CLI
Returns a new instance of CLI.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/flipper/cli.rb', line 14 def initialize(stdout: $stdout, stderr: $stderr, shell: Bundler::Thor::Base.shell.new) super # Program is always flipper, no matter how it's invoked @program_name = 'flipper' @require = ENV.fetch("FLIPPER_REQUIRE", DEFAULT_REQUIRE) @commands = {} # Extend whatever shell to support output redirection @shell = shell.extend(ShellOutput) shell.redirect(stdout: stdout, stderr: stderr) %w[enable disable].each do |action| command action do |c| c. = "Usage: #{c.program_name} [options] <feature>" c.description = "#{action.to_s.capitalize} a feature" values = [] c.on('-a id', '--actor=id', "#{action} for an actor") do |id| values << Actor.new(id) end c.on('-g name', '--group=name', "#{action} for a group") do |name| values << Types::Group.new(name) end c.on('-p NUM', '--percentage-of-actors=NUM', Numeric, "#{action} for a percentage of actors") do |num| values << Types::PercentageOfActors.new(num) end c.on('-t NUM', '--percentage-of-time=NUM', Numeric, "#{action} for a percentage of time") do |num| values << Types::PercentageOfTime.new(num) end c.on('-x expressions', '--expression=NUM', "#{action} for the given expression") do |expression| begin values << Flipper::Expression.build(JSON.parse(expression)) rescue JSON::ParserError => e ui.error "JSON parse error #{e.}" ui.trace(e) exit 1 rescue ArgumentError => e ui.error "Invalid expression: #{e.}" ui.trace(e) exit 1 end end c.action do |feature| f = Flipper.feature(feature) if values.empty? f.send(action) else values.each { |value| f.send(action, value) } end ui.info feature_details(f) end end end command 'list' do |c| c.description = "List defined features" c.action do ui.info feature_summary(Flipper.features) end end command 'show' do |c| c.description = "Show a defined feature" c.action do |feature| ui.info feature_details(Flipper.feature(feature)) end end command 'help' do |c| c.load_environment = false c.action do |command = nil| ui.info command ? @commands[command].help : help end end on_tail('-r path', "The path to load your application. Default: #{@require}") do |path| @require = path end # Options available on all commands on_tail('-h', '--help', 'Print help message') do ui.info help exit end # Set help documentation self. = "Usage: #{program_name} [options] <command>" separator "" separator "Commands:" pad = @commands.keys.map(&:length).max + 2 @commands.each do |name, command| separator " #{name.to_s.ljust(pad, " ")} #{command.description}" if command.description end separator "" separator "Options:" end |
Instance Attribute Details
#shell ⇒ Object
Returns the value of attribute shell.
12 13 14 |
# File 'lib/flipper/cli.rb', line 12 def shell @shell end |
Class Method Details
.run(argv = ARGV) ⇒ Object
5 6 7 |
# File 'lib/flipper/cli.rb', line 5 def self.run(argv = ARGV) new.run(argv) end |
Instance Method Details
#colorize(text, colors) ⇒ Object
220 221 222 |
# File 'lib/flipper/cli.rb', line 220 def colorize(text, colors) ui.add_color(text, *colors) end |
#command(name, &block) ⇒ Object
Helper method to define a new command
138 139 140 141 |
# File 'lib/flipper/cli.rb', line 138 def command(name, &block) @commands[name] = Command.new(program_name: "#{program_name} #{name}") block.call(@commands[name]) end |
#feature_details(feature) ⇒ Object
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/flipper/cli.rb', line 184 def feature_details(feature) summary = case feature.state when :on colorize("⏺ enabled", [:GREEN]) when :off "⦸ disabled" else lines = feature.enabled_gates.map do |gate| case gate.name when :actor [ pluralize(feature.actors_value.size, 'actor', 'actors') ] + feature.actors_value.map { |actor| "- #{actor}" } when :group [ pluralize(feature.groups_value.size, 'group', 'groups') ] + feature.groups_value.map { |group| " - #{group}" } when :percentage_of_actors "#{feature.percentage_of_actors_value}% of actors" when :percentage_of_time "#{feature.percentage_of_time_value}% of time" when :expression json = indent(JSON.pretty_generate(feature.expression_value), 2) "the expression: \n#{colorize(json, [:MAGENTA])}" end end "#{colorize("◯ conditionally enabled", [:YELLOW])} for:\n" + indent(lines.flatten.join("\n"), 2) end "#{colorize(feature.key, [:BOLD, :WHITE])} is #{summary}" end |
#feature_summary(features) ⇒ Object
153 154 155 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 |
# File 'lib/flipper/cli.rb', line 153 def feature_summary(features) features = Array(features) padding = features.map { |f| f.key.to_s.length }.max features.map do |feature| summary = case feature.state when :on colorize("⏺ enabled", [:GREEN]) when :off "⦸ disabled" else "#{colorize("◯ enabled", [:YELLOW])} 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 time" when :expression "an expression" end end.join(', ') end colorize("%-#{padding}s" % feature.key, [:BOLD, :WHITE]) + " is #{summary}" end.join("\n") end |
#indent(text, spaces) ⇒ Object
230 231 232 |
# File 'lib/flipper/cli.rb', line 230 def indent(text, spaces) text.gsub(/^/, " " * spaces) end |
#load_environment! ⇒ Object
143 144 145 146 147 148 149 150 151 |
# File 'lib/flipper/cli.rb', line 143 def load_environment! ENV["FLIPPER_CLOUD_LOGGING_ENABLED"] ||= "false" require File.(@require) # Ensure all of flipper gets loaded if it hasn't already. require 'flipper' rescue LoadError => e ui.error e. exit 1 end |
#pluralize(count, singular, plural) ⇒ Object
216 217 218 |
# File 'lib/flipper/cli.rb', line 216 def pluralize(count, singular, plural) "#{count} #{count == 1 ? singular : plural}" end |
#run(argv) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/flipper/cli.rb', line 118 def run(argv) command, *args = order(argv) if @commands[command] load_environment! if @commands[command].load_environment @commands[command].run(args) else ui.info help if command ui.error "Unknown command: #{command}" exit 1 end end rescue OptionParser::InvalidOption => e ui.error e. exit 1 end |
#ui ⇒ Object
224 225 226 227 228 |
# File 'lib/flipper/cli.rb', line 224 def ui @ui ||= Bundler::UI::Shell.new.tap do |ui| ui.shell = shell end end |