rack-flags 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/rack-flags/admin_app.rb +120 -0
 - data/lib/rack-flags/config.rb +41 -0
 - data/lib/rack-flags/cookie_codec.rb +66 -0
 - data/lib/rack-flags/rack_middleware.rb +19 -0
 - data/lib/rack-flags/reader.rb +61 -0
 - data/lib/rack-flags/version.rb +3 -0
 - data/lib/rack-flags.rb +15 -0
 - data/resources/admin_app/index.html.erb +35 -0
 - data/resources/admin_app/style.css +35 -0
 - data/spec/acceptance/administering_feature_flags_spec.rb +93 -0
 - data/spec/acceptance/reading_feature_flags_spec.rb +73 -0
 - data/spec/acceptance/spec_helper.rb +39 -0
 - data/spec/acceptance/support/reader_app.rb +24 -0
 - data/spec/spec_helper.rb +11 -0
 - data/spec/unit/admin_app_spec.rb +143 -0
 - data/spec/unit/config_spec.rb +65 -0
 - data/spec/unit/cookie_codec_spec.rb +63 -0
 - data/spec/unit/middleware_spec.rb +87 -0
 - data/spec/unit/rack_flags_module_methods_spec.rb +18 -0
 - data/spec/unit/reader_spec.rb +100 -0
 - data/spec/unit/spec_helper.rb +1 -0
 - metadata +214 -0
 
| 
         @@ -0,0 +1,120 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'erb'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FullFlagPresenter
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(full_flag)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @full_flag = full_flag
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def default
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @full_flag.default ? 'On' : 'Off'
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def name
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @full_flag.name
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def description
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @full_flag.description
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def checked_attribute_for(state)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  state == selected_state ? 'checked' : ''
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def selected_state
         
     | 
| 
      
 29 
     | 
    
         
            +
                    case @full_flag.override
         
     | 
| 
      
 30 
     | 
    
         
            +
                    when nil then :default
         
     | 
| 
      
 31 
     | 
    
         
            +
                    when true then :on
         
     | 
| 
      
 32 
     | 
    
         
            +
                    else :off
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              class AdminApp
         
     | 
| 
      
 39 
     | 
    
         
            +
                def call(env)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  request = Rack::Request.new(env)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  if request.path_info.chomp("/").empty?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    handle_root(request)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  else
         
     | 
| 
      
 45 
     | 
    
         
            +
                    handle_non_root(request)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def handle_root(request)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    case 
         
     | 
| 
      
 53 
     | 
    
         
            +
                    when request.get? then handle_root_get(request)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    when request.post? then handle_root_post(request)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    else
         
     | 
| 
      
 56 
     | 
    
         
            +
                      not_allowed
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def handle_root_get(request)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    reader = RackFlags.for_env(request.env)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    template = ERB.new(resource('index.html.erb'))
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    flag_presenters = reader.full_flags.map{ |flag| FullFlagPresenter.new(flag) }
         
     | 
| 
      
 67 
     | 
    
         
            +
                    view_model = OpenStruct.new( 
         
     | 
| 
      
 68 
     | 
    
         
            +
                      :css_href => "#{request.path}/style.css",
         
     | 
| 
      
 69 
     | 
    
         
            +
                      :flags => flag_presenters
         
     | 
| 
      
 70 
     | 
    
         
            +
                    )
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    [
         
     | 
| 
      
 73 
     | 
    
         
            +
                      200, 
         
     | 
| 
      
 74 
     | 
    
         
            +
                      {'Content-Type'=>'text/html'}, 
         
     | 
| 
      
 75 
     | 
    
         
            +
                      [template.result( view_model.instance_eval{ binding } )]
         
     | 
| 
      
 76 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  def handle_root_post(request)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    overrides = request.POST.inject({}) do |overrides, (flag_name, form_param_flag_state)|
         
     | 
| 
      
 81 
     | 
    
         
            +
                      overrides[flag_name.downcase.to_sym] = flag_value_for(form_param_flag_state)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      overrides
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    cookie = CookieCodec.new.generate_cookie_from(overrides)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    response = Rack::Response.new
         
     | 
| 
      
 88 
     | 
    
         
            +
                    response.redirect(request.script_name, 303)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    response.set_cookie(CookieCodec::COOKIE_KEY, cookie)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    response.finish
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  def handle_non_root(request)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    Rack::File.new( RackFlags.path_for_resource('admin_app') ).call(request.env)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  def not_allowed
         
     | 
| 
      
 99 
     | 
    
         
            +
                    [405, {}, ['405 - METHOD NOT ALLOWED']]
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  def flag_value_for(form_param_flag_state)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    flag_states = {
         
     | 
| 
      
 104 
     | 
    
         
            +
                        on: true,
         
     | 
| 
      
 105 
     | 
    
         
            +
                        off: false,
         
     | 
| 
      
 106 
     | 
    
         
            +
                        default: nil
         
     | 
| 
      
 107 
     | 
    
         
            +
                    }
         
     | 
| 
      
 108 
     | 
    
         
            +
                    flag_states[form_param_flag_state.to_sym]
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  def resource_root
         
     | 
| 
      
 112 
     | 
    
         
            +
                    RackFlags.path_for_resource('admin_app')
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  def resource(filename)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    File.read(RackFlags.path_for_resource(File.join('admin_app',filename)))
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :flags
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                def self.load( yaml_path )
         
     | 
| 
      
 9 
     | 
    
         
            +
                  flags = YAML.load( File.read( yaml_path ) )
         
     | 
| 
      
 10 
     | 
    
         
            +
                  new( flags )
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(flag_config)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  flag_config ||= {}
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  @flags = flag_config.map do |flag_name, flag_details| 
         
     | 
| 
      
 17 
     | 
    
         
            +
                    base_flag_from_config_entry(flag_name,flag_details)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                private
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def base_flag_from_config_entry( name, details )
         
     | 
| 
      
 24 
     | 
    
         
            +
                  symbolized_details = symbolize_keys( details )
         
     | 
| 
      
 25 
     | 
    
         
            +
                  BaseFlag.new( 
         
     | 
| 
      
 26 
     | 
    
         
            +
                               name.to_sym,
         
     | 
| 
      
 27 
     | 
    
         
            +
                               symbolized_details.fetch( :description ,""),
         
     | 
| 
      
 28 
     | 
    
         
            +
                               symbolized_details.fetch( :default ,false)
         
     | 
| 
      
 29 
     | 
    
         
            +
                              )
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def symbolize_keys(hash)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  hash.inject({}) do |symbolized_hash, (key, value)|
         
     | 
| 
      
 34 
     | 
    
         
            +
                    symbolized_hash[key.to_sym] = value
         
     | 
| 
      
 35 
     | 
    
         
            +
                    symbolized_hash
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
                
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              class CookieCodec
         
     | 
| 
      
 4 
     | 
    
         
            +
                COOKIE_KEY='rack-flags'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                class Parser
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_reader :overrides
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.parse(cookie_value)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    parser = new 
         
     | 
| 
      
 11 
     | 
    
         
            +
                    parser.parse(cookie_value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    parser.overrides
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize()
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @overrides = {}
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def parse(raw_overrides)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    return if raw_overrides.nil?
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    raw_overrides.split(' ').each do |override|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      parse_override(override)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  BANG_DETECTOR = Regexp.compile(/^!(.+)/)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def parse_override(override)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if override_without_bang = override[BANG_DETECTOR,1]
         
     | 
| 
      
 33 
     | 
    
         
            +
                      add_override(override_without_bang,false)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    else
         
     | 
| 
      
 35 
     | 
    
         
            +
                      add_override(override,true)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def add_override( name, value )
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @overrides[name.to_sym] = value
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def overrides_from_env(env)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  req = Rack::Request.new(env)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  raw_overrides = req.cookies[COOKIE_KEY]
         
     | 
| 
      
 47 
     | 
    
         
            +
                  Parser.parse( raw_overrides )
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def generate_cookie_from(overrides)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  cookie_values = overrides.map {|flag_name, flag_value| cookie_value_for(flag_name, flag_value) }
         
     | 
| 
      
 52 
     | 
    
         
            +
                  cookie_values.compact.join(' ')
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                private
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def cookie_value_for(flag_name, flag_value)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    case flag_value
         
     | 
| 
      
 59 
     | 
    
         
            +
                    when true then flag_name
         
     | 
| 
      
 60 
     | 
    
         
            +
                    when false then "!#{flag_name}"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    else nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 2 
     | 
    
         
            +
              class RackMiddleware
         
     | 
| 
      
 3 
     | 
    
         
            +
                ENV_KEY = 'x-rack-flags.flag-reader'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize( app, args )
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @app = app
         
     | 
| 
      
 7 
     | 
    
         
            +
                  yaml_path = args.fetch( :yaml_path ){ raise ArgumentError.new( 'yaml_path must be provided' ) }
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @config = Config.load( yaml_path )
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def call( env )
         
     | 
| 
      
 12 
     | 
    
         
            +
                  overrides = CookieCodec.new.overrides_from_env( env )
         
     | 
| 
      
 13 
     | 
    
         
            +
                  reader = Reader.new( @config.flags, overrides )
         
     | 
| 
      
 14 
     | 
    
         
            +
                  env[ENV_KEY] = reader
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  @app.call(env)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              BaseFlag = Struct.new(:name,:description,:default)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              FullFlag = Struct.new(:base_flag, :override) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def_delegators :base_flag, :name, :description, :default
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              class Reader
         
     | 
| 
      
 14 
     | 
    
         
            +
                def self.blank_reader
         
     | 
| 
      
 15 
     | 
    
         
            +
                  new( [], {} )
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def initialize( base_flags, overrides )
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @base_flags = load_base_flags( base_flags )
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @overrides = overrides
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def base_flags
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @base_flags.values.inject({}) { |h,flag| h[flag.name] = flag.default; h }
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def full_flags
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @base_flags.values.map do |base_flag|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    FullFlag.new(base_flag, @overrides[base_flag.name.to_sym])
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def on?(flag_name)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  flag_name = flag_name.to_sym
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  return false unless base_flag_exists?( flag_name )
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  @overrides.fetch(flag_name) do
         
     | 
| 
      
 39 
     | 
    
         
            +
                    # fall back to defaults
         
     | 
| 
      
 40 
     | 
    
         
            +
                    fetch_base_flag(flag_name).default
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                private
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def load_base_flags( flags )
         
     | 
| 
      
 47 
     | 
    
         
            +
                  Hash[ *flags.map{ |f| [f.name.to_sym, f] }.flatten ]
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def base_flag_exists?( flag_name )
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @base_flags.has_key?( flag_name )
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def fetch_base_flag( flag_name )
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @base_flags.fetch( flag_name ) do
         
     | 
| 
      
 56 
     | 
    
         
            +
                    BaseFlag.new( nil, nil, false ) # if we couldn't find a flag return a Null flag
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rack-flags.rb
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rack-flags/reader'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rack-flags/cookie_codec'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rack-flags/config'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'rack-flags/rack_middleware'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'rack-flags/admin_app'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 8 
     | 
    
         
            +
              def self.for_env(env)
         
     | 
| 
      
 9 
     | 
    
         
            +
                env[RackMiddleware::ENV_KEY] || Reader.blank_reader
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def self.path_for_resource(subpath)
         
     | 
| 
      
 13 
     | 
    
         
            +
                File.expand_path( File.join( "../../resources", subpath ), __FILE__ )
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!DOCTYPE html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 3 
     | 
    
         
            +
              <head>
         
     | 
| 
      
 4 
     | 
    
         
            +
                <title>rack-flags admin</title>
         
     | 
| 
      
 5 
     | 
    
         
            +
                <link href="<%=css_href%>" rel="stylesheet" type="text/css">
         
     | 
| 
      
 6 
     | 
    
         
            +
              </head>
         
     | 
| 
      
 7 
     | 
    
         
            +
              <body>
         
     | 
| 
      
 8 
     | 
    
         
            +
                <h1>rack-flags admin</h1>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <form method="post">
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <%flags.each do |flag|%>
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <section data-flag-name="<%=flag.name%>">
         
     | 
| 
      
 12 
     | 
    
         
            +
                      <h3><%=flag.name%></h3>
         
     | 
| 
      
 13 
     | 
    
         
            +
                      <p><%=flag.description%></p>
         
     | 
| 
      
 14 
     | 
    
         
            +
                      <div>
         
     | 
| 
      
 15 
     | 
    
         
            +
                        <label class="default">
         
     | 
| 
      
 16 
     | 
    
         
            +
                          Default (<%=flag.default%>)
         
     | 
| 
      
 17 
     | 
    
         
            +
                          <input type="radio" name="<%=flag.name%>" value="default" <%=flag.checked_attribute_for(:default)%>/>
         
     | 
| 
      
 18 
     | 
    
         
            +
                        </label>
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                        <label class="on">
         
     | 
| 
      
 21 
     | 
    
         
            +
                          On
         
     | 
| 
      
 22 
     | 
    
         
            +
                          <input type="radio" name="<%=flag.name%>" value="on" <%=flag.checked_attribute_for(:on)%>/>
         
     | 
| 
      
 23 
     | 
    
         
            +
                        </label>
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                        <label class="off">
         
     | 
| 
      
 26 
     | 
    
         
            +
                          Off
         
     | 
| 
      
 27 
     | 
    
         
            +
                          <input type="radio" name="<%=flag.name%>" value="off" <%=flag.checked_attribute_for(:off)%>/>
         
     | 
| 
      
 28 
     | 
    
         
            +
                        </label>
         
     | 
| 
      
 29 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 30 
     | 
    
         
            +
                    </section>
         
     | 
| 
      
 31 
     | 
    
         
            +
                  <%end%>
         
     | 
| 
      
 32 
     | 
    
         
            +
                  <input type="submit" value="Update Flags"/>
         
     | 
| 
      
 33 
     | 
    
         
            +
                </form>
         
     | 
| 
      
 34 
     | 
    
         
            +
              </body>
         
     | 
| 
      
 35 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            body {
         
     | 
| 
      
 2 
     | 
    
         
            +
              font-family: Futura, "Trebuchet MS", Arial, sans-serif;
         
     | 
| 
      
 3 
     | 
    
         
            +
            }
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            section {
         
     | 
| 
      
 6 
     | 
    
         
            +
              margin: 20px 20px 40px 20px;
         
     | 
| 
      
 7 
     | 
    
         
            +
              padding-bottom: 10px;
         
     | 
| 
      
 8 
     | 
    
         
            +
              background-color: lightgray;
         
     | 
| 
      
 9 
     | 
    
         
            +
            }
         
     | 
| 
      
 10 
     | 
    
         
            +
            section>* {
         
     | 
| 
      
 11 
     | 
    
         
            +
              padding-left: 8px;
         
     | 
| 
      
 12 
     | 
    
         
            +
            }
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            section h3 {
         
     | 
| 
      
 15 
     | 
    
         
            +
              background-color: gray;
         
     | 
| 
      
 16 
     | 
    
         
            +
              color: white;
         
     | 
| 
      
 17 
     | 
    
         
            +
              font-size: 16pt;
         
     | 
| 
      
 18 
     | 
    
         
            +
              padding-top: 5px;
         
     | 
| 
      
 19 
     | 
    
         
            +
              padding-bottom: 5px;
         
     | 
| 
      
 20 
     | 
    
         
            +
              margin: 0;
         
     | 
| 
      
 21 
     | 
    
         
            +
            }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            section p {
         
     | 
| 
      
 24 
     | 
    
         
            +
              margin-top: 0;
         
     | 
| 
      
 25 
     | 
    
         
            +
              font-style: italic;
         
     | 
| 
      
 26 
     | 
    
         
            +
            }
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            input[type="submit"] {
         
     | 
| 
      
 29 
     | 
    
         
            +
              font-family: Futura, "Trebuchet MS", Arial, sans-serif;
         
     | 
| 
      
 30 
     | 
    
         
            +
              padding: 10px;
         
     | 
| 
      
 31 
     | 
    
         
            +
              margin-left: 20px;
         
     | 
| 
      
 32 
     | 
    
         
            +
              font-size: 20px;
         
     | 
| 
      
 33 
     | 
    
         
            +
              background-color: #eee;
         
     | 
| 
      
 34 
     | 
    
         
            +
              border: 1px solid;
         
     | 
| 
      
 35 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,93 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe 'displaying flags in admin app' do
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Capybara::DSL
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              let( :feature_flag_config ) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  on_by_default: {
         
     | 
| 
      
 9 
     | 
    
         
            +
                    'default' => true,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    'description' => 'this flag on by default'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  },
         
     | 
| 
      
 12 
     | 
    
         
            +
                  off_by_default: {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    'default' => false,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    'description' => 'this flag off by default'
         
     | 
| 
      
 15 
     | 
    
         
            +
                  }
         
     | 
| 
      
 16 
     | 
    
         
            +
                }
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 20 
     | 
    
         
            +
                ff_config_file_contains feature_flag_config
         
     | 
| 
      
 21 
     | 
    
         
            +
                Capybara.app = app
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              let( :app ) do
         
     | 
| 
      
 25 
     | 
    
         
            +
                yaml_path = ff_config_file_path
         
     | 
| 
      
 26 
     | 
    
         
            +
                Rack::Builder.new do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  use RackFlags::RackMiddleware, yaml_path: yaml_path
         
     | 
| 
      
 28 
     | 
    
         
            +
                  run RackFlags::AdminApp.new
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              let(:on_flag_section){ page.find('section[data-flag-name="on_by_default"]') }
         
     | 
| 
      
 33 
     | 
    
         
            +
              let(:off_flag_section){ page.find('section[data-flag-name="off_by_default"]') }
         
     | 
| 
      
 34 
     | 
    
         
            +
              let(:update_button){ page.find('input[type="submit"]') }
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              it 'successfully GETs the admin page' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                visit '/'
         
     | 
| 
      
 38 
     | 
    
         
            +
                status_code.should == 200
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              it 'renders the feature flag name, default and description' do
         
     | 
| 
      
 42 
     | 
    
         
            +
                visit '/'
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                on_flag_section.should_not be_nil
         
     | 
| 
      
 45 
     | 
    
         
            +
                on_flag_section.find('h3').text.should == 'on_by_default'
         
     | 
| 
      
 46 
     | 
    
         
            +
                on_flag_section.find('p').text.should == 'this flag on by default'
         
     | 
| 
      
 47 
     | 
    
         
            +
                on_flag_section.find('label.default').text.should include('Default (On)')
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                off_flag_section.should_not be_nil
         
     | 
| 
      
 50 
     | 
    
         
            +
                off_flag_section.find('h3').text.should == 'off_by_default'
         
     | 
| 
      
 51 
     | 
    
         
            +
                off_flag_section.find('p').text.should == 'this flag off by default'
         
     | 
| 
      
 52 
     | 
    
         
            +
                off_flag_section.find('label.default').text.should include('Default (Off)')
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              it 'selects the default option if there are no cookies present' do
         
     | 
| 
      
 56 
     | 
    
         
            +
                visit '/'
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                verify_flag_section( on_flag_section, :default )
         
     | 
| 
      
 59 
     | 
    
         
            +
                verify_flag_section( off_flag_section, :default )
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              it 'allows switching off a flag defaulted to on' do
         
     | 
| 
      
 63 
     | 
    
         
            +
                visit '/'
         
     | 
| 
      
 64 
     | 
    
         
            +
                
         
     | 
| 
      
 65 
     | 
    
         
            +
                on_flag_section.choose( 'Off' )
         
     | 
| 
      
 66 
     | 
    
         
            +
                update_button.click
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                verify_flag_section( on_flag_section, :off )
         
     | 
| 
      
 69 
     | 
    
         
            +
                verify_flag_section( off_flag_section, :default )
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              def verify_flag_section( section, expected_state )
         
     | 
| 
      
 73 
     | 
    
         
            +
                case expected_state.to_sym
         
     | 
| 
      
 74 
     | 
    
         
            +
                when :default
         
     | 
| 
      
 75 
     | 
    
         
            +
                  section.find('label.default input').should be_checked
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  section.find('label.on input').should_not be_checked
         
     | 
| 
      
 78 
     | 
    
         
            +
                  section.find('label.off input').should_not be_checked
         
     | 
| 
      
 79 
     | 
    
         
            +
                when :on
         
     | 
| 
      
 80 
     | 
    
         
            +
                  section.find('label.on input').should be_checked
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  section.find('label.default input').should_not be_checked
         
     | 
| 
      
 83 
     | 
    
         
            +
                  section.find('label.off input').should_not be_checked
         
     | 
| 
      
 84 
     | 
    
         
            +
                when :off
         
     | 
| 
      
 85 
     | 
    
         
            +
                  section.find('label.off input').should be_checked
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  section.find('label.default input').should_not be_checked
         
     | 
| 
      
 88 
     | 
    
         
            +
                  section.find('label.on input').should_not be_checked
         
     | 
| 
      
 89 
     | 
    
         
            +
                else 
         
     | 
| 
      
 90 
     | 
    
         
            +
                  raise "unrecognized state '#{expected_state}'"
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'support/reader_app'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe 'reading feature flags in an app' do
         
     | 
| 
      
 5 
     | 
    
         
            +
              include Rack::Test::Methods
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              let( :feature_flag_config ){ {} }
         
     | 
| 
      
 8 
     | 
    
         
            +
              let( :feature_flag_cookie ){ "" }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 11 
     | 
    
         
            +
                ff_config_file_contains feature_flag_config
         
     | 
| 
      
 12 
     | 
    
         
            +
                set_feature_flags_cookie feature_flag_cookie
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              let( :app ) do
         
     | 
| 
      
 16 
     | 
    
         
            +
                yaml_path = ff_config_file_path
         
     | 
| 
      
 17 
     | 
    
         
            +
                Rack::Builder.new do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  use RackFlags::RackMiddleware, yaml_path: yaml_path
         
     | 
| 
      
 19 
     | 
    
         
            +
                  run ReaderApp
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              context 'no base flags, no overrides' do
         
     | 
| 
      
 24 
     | 
    
         
            +
                it 'should interpret both foo and bar as off by default' do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  get '/'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  last_response.body.should == 'foo is off; bar is off'
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              context 'foo defined as a base flag, defaulted to true' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                let( :feature_flag_config ) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  {
         
     | 
| 
      
 33 
     | 
    
         
            +
                    foo: { default: true }
         
     | 
| 
      
 34 
     | 
    
         
            +
                  }
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                it 'should interpret foo as on and bar as off' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  get '/'
         
     | 
| 
      
 39 
     | 
    
         
            +
                  last_response.body.should == 'foo is on; bar is off'
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              context 'foo defaults to false, bar defaults to true' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                let( :feature_flag_config ) do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  {
         
     | 
| 
      
 46 
     | 
    
         
            +
                    foo: { default: false },
         
     | 
| 
      
 47 
     | 
    
         
            +
                    bar: { default: true }
         
     | 
| 
      
 48 
     | 
    
         
            +
                  }
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                it 'should interpret foo as off and bar as on' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  get '/'
         
     | 
| 
      
 53 
     | 
    
         
            +
                  last_response.body.should == 'foo is off; bar is on'
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              context 'foo and bar both default to true, bar is overridden to false' do
         
     | 
| 
      
 58 
     | 
    
         
            +
                let( :feature_flag_config ) do
         
     | 
| 
      
 59 
     | 
    
         
            +
                  {
         
     | 
| 
      
 60 
     | 
    
         
            +
                    foo: { default: true },
         
     | 
| 
      
 61 
     | 
    
         
            +
                    bar:  { default: true }
         
     | 
| 
      
 62 
     | 
    
         
            +
                  }
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                let( :feature_flag_cookie){ "!bar" }
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                it 'should interpret foo as on and bar as off' do
         
     | 
| 
      
 68 
     | 
    
         
            +
                  get '/'
         
     | 
| 
      
 69 
     | 
    
         
            +
                  last_response.body.should == 'foo is on; bar is off'
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative '../spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rack/test'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'capybara/rspec'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module ConfigFileHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def ff_config_file_path
         
     | 
| 
      
 8 
     | 
    
         
            +
                ff_yaml_file.path
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def ff_config_file_contains(config)
         
     | 
| 
      
 12 
     | 
    
         
            +
                ff_yaml_file.write( config.to_yaml )
         
     | 
| 
      
 13 
     | 
    
         
            +
                ff_yaml_file.flush
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def teardown_ff_config_file_if_exists
         
     | 
| 
      
 17 
     | 
    
         
            +
                @_ff_yaml_file.unlink if @_ff_yaml_file
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def ff_yaml_file
         
     | 
| 
      
 21 
     | 
    
         
            +
                @_ff_yaml_file ||= Tempfile.new('rack-flags acceptance test example config file')
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            module CookieHelper
         
     | 
| 
      
 26 
     | 
    
         
            +
              def set_feature_flags_cookie ff_cookie
         
     | 
| 
      
 27 
     | 
    
         
            +
                raw_cookie = "#{RackFlags::CookieCodec::COOKIE_KEY}=#{Rack::Utils.escape(ff_cookie)}"
         
     | 
| 
      
 28 
     | 
    
         
            +
                set_cookie(raw_cookie)
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 33 
     | 
    
         
            +
              include ConfigFileHelper
         
     | 
| 
      
 34 
     | 
    
         
            +
              include CookieHelper
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              config.after :each do
         
     | 
| 
      
 37 
     | 
    
         
            +
                teardown_ff_config_file_if_exists
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'sinatra'
         
     | 
| 
      
 2 
     | 
    
         
            +
            class ReaderApp < Sinatra::Base
         
     | 
| 
      
 3 
     | 
    
         
            +
              enable :raise_errors
         
     | 
| 
      
 4 
     | 
    
         
            +
              disable :show_exceptions 
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              get "/" do
         
     | 
| 
      
 7 
     | 
    
         
            +
                flags = RackFlags.for_env(env)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                output = []
         
     | 
| 
      
 10 
     | 
    
         
            +
                if flags.on?( :foo )
         
     | 
| 
      
 11 
     | 
    
         
            +
                  output << "foo is on"
         
     | 
| 
      
 12 
     | 
    
         
            +
                else
         
     | 
| 
      
 13 
     | 
    
         
            +
                  output << "foo is off"
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                if flags.on?( :bar )
         
     | 
| 
      
 17 
     | 
    
         
            +
                  output << "bar is on"
         
     | 
| 
      
 18 
     | 
    
         
            +
                else
         
     | 
| 
      
 19 
     | 
    
         
            +
                  output << "bar is off"
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                output.join("; ")
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    
| 
         @@ -0,0 +1,143 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              describe AdminApp do
         
     | 
| 
      
 6 
     | 
    
         
            +
                describe '#call' do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  let(:fake_env) { {} }
         
     | 
| 
      
 8 
     | 
    
         
            +
                  let(:admin_app) { AdminApp.new }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  context 'when the request is a get' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                    let(:fake_env) { {'REQUEST_METHOD' => 'GET'} }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    it 'returns a successful HTML response' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                      status, headers, body = admin_app.call(fake_env)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                      expect(status).to eq(200)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      expect(headers).to include({'Content-Type' => 'text/html'})
         
     | 
| 
      
 18 
     | 
    
         
            +
                      expect(body).to_not be_empty
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  context 'when the request is a post' do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    let(:fake_env) { {'REQUEST_METHOD' => 'POST', 'SCRIPT_NAME' => 'admin-app' } }
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    before do
         
     | 
| 
      
 27 
     | 
    
         
            +
                      any_instance_of(Rack::Request) do |rack_request|
         
     | 
| 
      
 28 
     | 
    
         
            +
                        stub(rack_request).POST { {} }
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    it 'returns a 303 redirect response' do
         
     | 
| 
      
 33 
     | 
    
         
            +
                      status, headers , body = admin_app.call(fake_env)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      expect(status).to eq(303)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      expect(headers).to include({'Location' => 'admin-app'})
         
     | 
| 
      
 37 
     | 
    
         
            +
                      expect(body).to be_empty
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    it 'sets the cookie using the post params' do
         
     | 
| 
      
 41 
     | 
    
         
            +
                      any_instance_of(Rack::Request) do |rack_request|
         
     | 
| 
      
 42 
     | 
    
         
            +
                        stub(rack_request).POST { {flag_1: 'on', flag_2: 'off', flag_3: 'default'} }
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                      stub.proxy(CookieCodec).new do |cookie_codec|
         
     | 
| 
      
 46 
     | 
    
         
            +
                        mock(cookie_codec).generate_cookie_from({flag_1: true, flag_2: false, flag_3: nil}) { 'flag_1 !flag2' }
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                      any_instance_of(Rack::Response) do |rack_response|
         
     | 
| 
      
 50 
     | 
    
         
            +
                        mock(rack_response).set_cookie(CookieCodec::COOKIE_KEY, 'flag_1 !flag2')
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                      admin_app.call(fake_env)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    it 'returns the finished response' do
         
     | 
| 
      
 57 
     | 
    
         
            +
                      stub.proxy(Rack::Response).new do |rack_response|
         
     | 
| 
      
 58 
     | 
    
         
            +
                        mock(rack_response).finish { 'finished rack response' }
         
     | 
| 
      
 59 
     | 
    
         
            +
                      end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                      response = admin_app.call(fake_env)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                      expect(response).to eq('finished rack response')
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  context 'when the request is something else' do
         
     | 
| 
      
 69 
     | 
    
         
            +
                    let(:fake_env) { {'REQUEST_METHOD' => 'OTHER'} }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    it 'returns a 405 method not allowed response' do
         
     | 
| 
      
 72 
     | 
    
         
            +
                      status, headers , body = admin_app.call(fake_env)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      expect(status).to eq(405)
         
     | 
| 
      
 75 
     | 
    
         
            +
                      expect(headers).to be_empty
         
     | 
| 
      
 76 
     | 
    
         
            +
                      expect(body).to eq('405 - METHOD NOT ALLOWED')
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
              describe FullFlagPresenter do
         
     | 
| 
      
 85 
     | 
    
         
            +
                describe '#default' do
         
     | 
| 
      
 86 
     | 
    
         
            +
                  let(:full_flag) { FullFlag.new(base_flag, nil) }
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  subject(:full_flag_presenter) { FullFlagPresenter.new(full_flag) }
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  context 'when the default is true' do
         
     | 
| 
      
 91 
     | 
    
         
            +
                    let(:base_flag) { BaseFlag.new('name', 'description', true) }
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    its(:default) { should == 'On' }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  context 'when the default is false' do
         
     | 
| 
      
 97 
     | 
    
         
            +
                    let(:base_flag) { BaseFlag.new('name', 'description', false) }
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    its(:default) { should == 'Off' }
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                describe '#checked_attributes_for' do
         
     | 
| 
      
 104 
     | 
    
         
            +
                  let(:full_flag) { FullFlag.new(mock(BaseFlag), override) }
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  subject(:full_flag_presenter) { FullFlagPresenter.new(full_flag) }
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  context 'when the default value is not overridden' do
         
     | 
| 
      
 109 
     | 
    
         
            +
                    let(:final_value) { nil }
         
     | 
| 
      
 110 
     | 
    
         
            +
                    let(:override) { nil }
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                    specify 'only its default checked state is "checked"' do
         
     | 
| 
      
 113 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:default)).to eq('checked')
         
     | 
| 
      
 114 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:on)).to eq('')
         
     | 
| 
      
 115 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:off)).to eq('')
         
     | 
| 
      
 116 
     | 
    
         
            +
                    end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  context 'when the default value is overridden to true' do
         
     | 
| 
      
 120 
     | 
    
         
            +
                    let(:final_value) { nil }
         
     | 
| 
      
 121 
     | 
    
         
            +
                    let(:override) { true }
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    specify 'only its on checked state is "checked"' do
         
     | 
| 
      
 124 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:default)).to eq('')
         
     | 
| 
      
 125 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:on)).to eq('checked')
         
     | 
| 
      
 126 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:off)).to eq('')
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  context 'when the default value is overridden to false' do
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let(:final_value) { nil }
         
     | 
| 
      
 132 
     | 
    
         
            +
                    let(:override) { false }
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    specify 'only its off checked state is "checked"' do
         
     | 
| 
      
 135 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:default)).to eq('')
         
     | 
| 
      
 136 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:on)).to eq('')
         
     | 
| 
      
 137 
     | 
    
         
            +
                      expect(subject.checked_attribute_for(:off)).to eq('checked')
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
         
     | 
| 
      
 5 
     | 
    
         
            +
              describe Config do
         
     | 
| 
      
 6 
     | 
    
         
            +
                let( :config_file ) { Tempfile.new('rack-flags-config-unit-test') }
         
     | 
| 
      
 7 
     | 
    
         
            +
                let( :yaml ) { raise NotImplementedError }
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                before :each do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  config_file.write( yaml )
         
     | 
| 
      
 12 
     | 
    
         
            +
                  config_file.flush
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                let(:config){ Config.load( config_file.path ) }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                after :each do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  config_file.unlink
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                context 'regular yaml' do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  let(:yaml) do
         
     | 
| 
      
 23 
     | 
    
         
            +
                    <<-EOS
         
     | 
| 
      
 24 
     | 
    
         
            +
                    foo: 
         
     | 
| 
      
 25 
     | 
    
         
            +
                      description: the description
         
     | 
| 
      
 26 
     | 
    
         
            +
                      default: true
         
     | 
| 
      
 27 
     | 
    
         
            +
                    bar:
         
     | 
| 
      
 28 
     | 
    
         
            +
                      description: another description
         
     | 
| 
      
 29 
     | 
    
         
            +
                      default: false
         
     | 
| 
      
 30 
     | 
    
         
            +
                    EOS
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  it 'loads a set of BaseFlags' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                    config.should have(2).flags
         
     | 
| 
      
 35 
     | 
    
         
            +
                    config.flags.map(&:name) .should =~ [:foo,:bar]
         
     | 
| 
      
 36 
     | 
    
         
            +
                    config.flags.map(&:description).should =~ ["the description","another description"]
         
     | 
| 
      
 37 
     | 
    
         
            +
                    config.flags.map(&:default).should =~ [true,false]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                context 'empty file' do
         
     | 
| 
      
 42 
     | 
    
         
            +
                  let(:yaml){ "" }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  it 'loads as an empty config' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    config.should have(0).flags
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                context 'symbolized yaml' do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  let :yaml do
         
     | 
| 
      
 50 
     | 
    
         
            +
                    <<-EOS
         
     | 
| 
      
 51 
     | 
    
         
            +
                      :foo: 
         
     | 
| 
      
 52 
     | 
    
         
            +
                        :default: true
         
     | 
| 
      
 53 
     | 
    
         
            +
                        :description: a description
         
     | 
| 
      
 54 
     | 
    
         
            +
                    EOS
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  subject(:flag){ config.flags.first } 
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  it { should_not be_nil }
         
     | 
| 
      
 60 
     | 
    
         
            +
                  its(:default) { should be_true }
         
     | 
| 
      
 61 
     | 
    
         
            +
                  its(:description) { should == "a description" }
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
         
     | 
| 
      
 5 
     | 
    
         
            +
              describe CookieCodec do
         
     | 
| 
      
 6 
     | 
    
         
            +
                let(:cookie_codec){ CookieCodec.new }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                describe '#overrides_from_env' do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  subject(:overrides){ cookie_codec.overrides_from_env(env) }
         
     | 
| 
      
 10 
     | 
    
         
            +
                  let(:env){ {'HTTP_COOKIE'=>cookie_header } }
         
     | 
| 
      
 11 
     | 
    
         
            +
                  let(:cookie_header){ "#{CookieCodec::COOKIE_KEY}=#{Rack::Utils.escape(ff_cookie)};foo=bar" }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  context 'no cookie' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                    let(:env){ {} }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    it{ should == {} }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  context 'empty cookie' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                    let(:ff_cookie){ '' }
         
     | 
| 
      
 20 
     | 
    
         
            +
                    it{ should == {} }
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  context 'simple cookie' do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    let(:ff_cookie){ 'foo bar' }
         
     | 
| 
      
 25 
     | 
    
         
            +
                    it{ should == {foo: true, bar: true} }
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  context 'cookie with positives and negatives' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                    let(:ff_cookie){ 'yes !no yes-sir !na-huh' }
         
     | 
| 
      
 30 
     | 
    
         
            +
                    it{ should == {
         
     | 
| 
      
 31 
     | 
    
         
            +
                      :yes => true, 
         
     | 
| 
      
 32 
     | 
    
         
            +
                      :'yes-sir' => true,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      :no => false,
         
     | 
| 
      
 34 
     | 
    
         
            +
                      :'na-huh' => false
         
     | 
| 
      
 35 
     | 
    
         
            +
                    } }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                describe '#generate_cookie_from' do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  it 'returns a simple cookie' do
         
     | 
| 
      
 41 
     | 
    
         
            +
                    admin_app_overrides = {flag_1: true}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    cookie = cookie_codec.generate_cookie_from(admin_app_overrides)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    expect(cookie).to eq('flag_1')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  it 'returns a cookie with positive and negative values' do
         
     | 
| 
      
 48 
     | 
    
         
            +
                    admin_app_overrides = {flag_1: true, flag_2: false}
         
     | 
| 
      
 49 
     | 
    
         
            +
                    cookie = cookie_codec.generate_cookie_from(admin_app_overrides)
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    expect(cookie).to eq('flag_1 !flag_2')
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  it 'returns a cookie with default values' do
         
     | 
| 
      
 55 
     | 
    
         
            +
                    admin_app_overrides = {flag_1: true, flag_2: false, flag_3: nil}
         
     | 
| 
      
 56 
     | 
    
         
            +
                    cookie = cookie_codec.generate_cookie_from(admin_app_overrides)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    expect(cookie).to eq('flag_1 !flag_2')
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              describe RackMiddleware do
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def mock_out_config_loading
         
     | 
| 
      
 8 
     | 
    
         
            +
                  stub(Config).load(anything){ OpenStruct.new( flags: {} ) }
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                it 'raise an exception if no yaml path is provided' do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  lambda{
         
     | 
| 
      
 13 
     | 
    
         
            +
                    RackMiddleware.new( :fake_app, {} )
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }.should raise_error( ArgumentError, 'yaml_path must be provided' )
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                it 'loads the config from the specified yaml file' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  mock(Config).load('some/config/path')
         
     | 
| 
      
 19 
     | 
    
         
            +
                  RackMiddleware.new( :fake_app, yaml_path: 'some/config/path' )
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                describe '#call' do
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def create_middleware( fake_app = false)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    fake_app ||= Proc.new {}
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    RackMiddleware.new( fake_app, yaml_path: 'blah' )
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  it 'creates a Reader using the config flags when called' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                    stub(Config).load(anything){ OpenStruct.new( flags: 'fake flags from config' ) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                    mock(Reader).new( 'fake flags from config', anything )
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    middleware = create_middleware()
         
     | 
| 
      
 35 
     | 
    
         
            +
                    middleware.call( {} )
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  it 'adds the reader to the env' do
         
     | 
| 
      
 39 
     | 
    
         
            +
                    mock_out_config_loading
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    mock(Reader).new(anything,anything){ 'fake derived flags' }
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    middleware = create_middleware()
         
     | 
| 
      
 44 
     | 
    
         
            +
                    fake_env = {}
         
     | 
| 
      
 45 
     | 
    
         
            +
                    middleware.call( fake_env )
         
     | 
| 
      
 46 
     | 
    
         
            +
                    fake_env[RackMiddleware::ENV_KEY].should == 'fake derived flags'
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  it 'reads overrides from cookies' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                    mock_out_config_loading
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    fake_env = { fake: 'env' }
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    fake_cookie_codec = mock( Object.new )
         
     | 
| 
      
 56 
     | 
    
         
            +
                    mock(CookieCodec).new{ fake_cookie_codec }
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    fake_cookie_codec.overrides_from_env( fake_env )
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    create_middleware().call( fake_env )
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  it 'passes the overrides into the reader' do
         
     | 
| 
      
 64 
     | 
    
         
            +
                    mock_out_config_loading
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    mock(CookieCodec).new{ stub!.overrides_from_env{'fake overrides'} }
         
     | 
| 
      
 67 
     | 
    
         
            +
                    mock(Reader).new( anything, 'fake overrides' )
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    create_middleware().call( {} )
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  it 'passes through to downstream app' do
         
     | 
| 
      
 73 
     | 
    
         
            +
                    mock_out_config_loading
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    fake_app ||= Proc.new do
         
     | 
| 
      
 76 
     | 
    
         
            +
                      "downstream app response"
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    middleware = create_middleware( fake_app )
         
     | 
| 
      
 80 
     | 
    
         
            +
                    middleware_response = middleware.call( {} )
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    middleware_response.should == "downstream app response"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe '.for_env' do
         
     | 
| 
      
 5 
     | 
    
         
            +
                it 'should return the flags which the middleware stuffed in the env' do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  fake_env = {RackMiddleware::ENV_KEY => 'fake flags from env'}
         
     | 
| 
      
 7 
     | 
    
         
            +
                  RackFlags.for_env(fake_env).should == 'fake flags from env'
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                it 'returns a generic empty reader if none is in the env' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  fake_env = {}
         
     | 
| 
      
 12 
     | 
    
         
            +
                  reader = RackFlags.for_env(fake_env)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  reader.should_not be_nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                  reader.should respond_to(:on?)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,100 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RackFlags
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              describe Reader do
         
     | 
| 
      
 6 
     | 
    
         
            +
                def derived_flags_final_values(derived_flags)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  derived_flags.map { |flag| flag.final_value }
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def full_flags_overrides(derived_flags)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  derived_flags.map { |flag| flag.override }
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                let( :base_flags ) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  [
         
     | 
| 
      
 16 
     | 
    
         
            +
                    BaseFlag.new( :usually_on, 'a flag', true ),
         
     | 
| 
      
 17 
     | 
    
         
            +
                    BaseFlag.new( :usually_off, 'another flag', false )
         
     | 
| 
      
 18 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                let( :overrides ){ {} }
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                subject( :reader ){ Reader.new( base_flags, overrides ) }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                shared_examples 'full flags that mimic the base flags' do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  it 'has a name, description, and default for each base flag' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    full_flags = subject.full_flags
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    expect(full_flags.length).to eq(base_flags.length)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    expect(full_flags[0].name).to eq(:usually_on)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    expect(full_flags[0].description).to eq('a flag')
         
     | 
| 
      
 32 
     | 
    
         
            +
                    expect(full_flags[0].default).to be_true
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    expect(full_flags[1].name).to eq(:usually_off)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    expect(full_flags[1].description).to eq('another flag')
         
     | 
| 
      
 36 
     | 
    
         
            +
                    expect(full_flags[1].default).to be_false
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                context 'no overrides' do
         
     | 
| 
      
 42 
     | 
    
         
            +
                  let( :overrides ){ {} }
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  its(:base_flags){ should == {usually_on: true, usually_off: false} }
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  specify 'on? is true for a flag which is on by default' do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    subject.on?( :usually_on ).should be_true
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  specify 'on? is false for a flag which is off by default' do
         
     | 
| 
      
 51 
     | 
    
         
            +
                    subject.on?( :usually_off ).should be_false
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  specify 'on? is false for unknown flags' do
         
     | 
| 
      
 55 
     | 
    
         
            +
                    subject.on?( :unknown_flag ).should be_false
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  it_behaves_like 'full flags that mimic the base flags'
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  it 'has derived flags with nil override values' do
         
     | 
| 
      
 61 
     | 
    
         
            +
                    overrides = full_flags_overrides(subject.full_flags)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    expect(overrides).to eq([nil, nil])
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                context 'overridden to false' do
         
     | 
| 
      
 68 
     | 
    
         
            +
                  let( :overrides ){ {usually_on: false} }
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  specify 'on? is false' do
         
     | 
| 
      
 71 
     | 
    
         
            +
                    subject.on?( :usually_on ).should be_false
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  it_behaves_like 'full flags that mimic the base flags'
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  it 'has full flags with one override value' do
         
     | 
| 
      
 77 
     | 
    
         
            +
                    overrides = full_flags_overrides(subject.full_flags)
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    expect(overrides).to eq([false, nil])
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                context 'overridding a flag which has no base' do
         
     | 
| 
      
 84 
     | 
    
         
            +
                  let( :overrides ){ {no_base: true} }
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  specify 'on? is false' do
         
     | 
| 
      
 87 
     | 
    
         
            +
                    subject.on?( :no_base ).should be_false
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  it_behaves_like 'full flags that mimic the base flags'
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  it 'has full flags with no override values' do
         
     | 
| 
      
 93 
     | 
    
         
            +
                    overrides = full_flags_overrides(subject.full_flags)
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    expect(overrides).to eq([nil, nil])
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative '../spec_helper'
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,214 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: rack-flags
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.2
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
      
 6 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 7 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Pete Hodgson
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Ryan Oglesby
         
     | 
| 
      
 10 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 11 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 12 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 13 
     | 
    
         
            +
            date: 2013-07-14 00:00:00.000000000 Z
         
     | 
| 
      
 14 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 15 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 16 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 17 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 18 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 19 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 20 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 21 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 22 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 23 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 24 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 25 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 26 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 27 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 28 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 29 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 30 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 31 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 32 
     | 
    
         
            +
              name: rack
         
     | 
| 
      
 33 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 34 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 35 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 36 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 37 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 38 
     | 
    
         
            +
                    version: '1.4'
         
     | 
| 
      
 39 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 40 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 41 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 42 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 43 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 44 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 45 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 46 
     | 
    
         
            +
                    version: '1.4'
         
     | 
| 
      
 47 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 48 
     | 
    
         
            +
              name: sinatra
         
     | 
| 
      
 49 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 50 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 55 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 56 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 57 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 59 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 60 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 61 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 62 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 63 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 64 
     | 
    
         
            +
              name: pry-debugger
         
     | 
| 
      
 65 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 66 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 67 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 68 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 69 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 70 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 71 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 72 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 73 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 74 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 75 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 76 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 77 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 78 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 79 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 80 
     | 
    
         
            +
              name: rspec-core
         
     | 
| 
      
 81 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 82 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 83 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 84 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 85 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 86 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 87 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 88 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 89 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 90 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 91 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 92 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 93 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 94 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 95 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 96 
     | 
    
         
            +
              name: rspec-expectations
         
     | 
| 
      
 97 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 98 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 99 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 100 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 101 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 102 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 103 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 104 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 105 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 106 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 107 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 108 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 109 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 110 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 111 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 112 
     | 
    
         
            +
              name: rr
         
     | 
| 
      
 113 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 114 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 115 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 116 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 117 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 118 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 119 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 120 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 121 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 122 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 123 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 124 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 125 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 126 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 127 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 128 
     | 
    
         
            +
              name: capybara
         
     | 
| 
      
 129 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 130 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 131 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 132 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 133 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 134 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 135 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 136 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 137 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 138 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 139 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 140 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 141 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 142 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 143 
     | 
    
         
            +
            description: This is a simple lightweight way to expose work-in-progress functionality
         
     | 
| 
      
 144 
     | 
    
         
            +
              to developers, testers or other internal users.
         
     | 
| 
      
 145 
     | 
    
         
            +
            email:
         
     | 
| 
      
 146 
     | 
    
         
            +
            - git@thepete.net
         
     | 
| 
      
 147 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 148 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 149 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 150 
     | 
    
         
            +
            files:
         
     | 
| 
      
 151 
     | 
    
         
            +
            - lib/rack-flags.rb
         
     | 
| 
      
 152 
     | 
    
         
            +
            - lib/rack-flags/admin_app.rb
         
     | 
| 
      
 153 
     | 
    
         
            +
            - lib/rack-flags/config.rb
         
     | 
| 
      
 154 
     | 
    
         
            +
            - lib/rack-flags/cookie_codec.rb
         
     | 
| 
      
 155 
     | 
    
         
            +
            - lib/rack-flags/rack_middleware.rb
         
     | 
| 
      
 156 
     | 
    
         
            +
            - lib/rack-flags/reader.rb
         
     | 
| 
      
 157 
     | 
    
         
            +
            - lib/rack-flags/version.rb
         
     | 
| 
      
 158 
     | 
    
         
            +
            - resources/admin_app/index.html.erb
         
     | 
| 
      
 159 
     | 
    
         
            +
            - resources/admin_app/style.css
         
     | 
| 
      
 160 
     | 
    
         
            +
            - spec/acceptance/administering_feature_flags_spec.rb
         
     | 
| 
      
 161 
     | 
    
         
            +
            - spec/acceptance/reading_feature_flags_spec.rb
         
     | 
| 
      
 162 
     | 
    
         
            +
            - spec/acceptance/spec_helper.rb
         
     | 
| 
      
 163 
     | 
    
         
            +
            - spec/acceptance/support/reader_app.rb
         
     | 
| 
      
 164 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 165 
     | 
    
         
            +
            - spec/unit/admin_app_spec.rb
         
     | 
| 
      
 166 
     | 
    
         
            +
            - spec/unit/config_spec.rb
         
     | 
| 
      
 167 
     | 
    
         
            +
            - spec/unit/cookie_codec_spec.rb
         
     | 
| 
      
 168 
     | 
    
         
            +
            - spec/unit/middleware_spec.rb
         
     | 
| 
      
 169 
     | 
    
         
            +
            - spec/unit/rack_flags_module_methods_spec.rb
         
     | 
| 
      
 170 
     | 
    
         
            +
            - spec/unit/reader_spec.rb
         
     | 
| 
      
 171 
     | 
    
         
            +
            - spec/unit/spec_helper.rb
         
     | 
| 
      
 172 
     | 
    
         
            +
            homepage: https://github.com/moredip/rack-flags
         
     | 
| 
      
 173 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 174 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 175 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 176 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 177 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 178 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 179 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 180 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 181 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 182 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 183 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 184 
     | 
    
         
            +
                  segments:
         
     | 
| 
      
 185 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 186 
     | 
    
         
            +
                  hash: -2280499656748688466
         
     | 
| 
      
 187 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 188 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 189 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 190 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 191 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 192 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 193 
     | 
    
         
            +
                  segments:
         
     | 
| 
      
 194 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 195 
     | 
    
         
            +
                  hash: -2280499656748688466
         
     | 
| 
      
 196 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 197 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 198 
     | 
    
         
            +
            rubygems_version: 1.8.25
         
     | 
| 
      
 199 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 200 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 201 
     | 
    
         
            +
            summary: Simple cookie-based feature flags using Rack.
         
     | 
| 
      
 202 
     | 
    
         
            +
            test_files:
         
     | 
| 
      
 203 
     | 
    
         
            +
            - spec/acceptance/administering_feature_flags_spec.rb
         
     | 
| 
      
 204 
     | 
    
         
            +
            - spec/acceptance/reading_feature_flags_spec.rb
         
     | 
| 
      
 205 
     | 
    
         
            +
            - spec/acceptance/spec_helper.rb
         
     | 
| 
      
 206 
     | 
    
         
            +
            - spec/acceptance/support/reader_app.rb
         
     | 
| 
      
 207 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 208 
     | 
    
         
            +
            - spec/unit/admin_app_spec.rb
         
     | 
| 
      
 209 
     | 
    
         
            +
            - spec/unit/config_spec.rb
         
     | 
| 
      
 210 
     | 
    
         
            +
            - spec/unit/cookie_codec_spec.rb
         
     | 
| 
      
 211 
     | 
    
         
            +
            - spec/unit/middleware_spec.rb
         
     | 
| 
      
 212 
     | 
    
         
            +
            - spec/unit/rack_flags_module_methods_spec.rb
         
     | 
| 
      
 213 
     | 
    
         
            +
            - spec/unit/reader_spec.rb
         
     | 
| 
      
 214 
     | 
    
         
            +
            - spec/unit/spec_helper.rb
         
     |