feature 0.2.1 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.3.0 (2011-11-23)
2
+
3
+ * assume all features as inactive that are not active, also unknown ones
4
+ * make yaml parsing in yaml repository more strict
5
+
1
6
  ## 0.2.1 (2011-11-22)
2
7
 
3
8
  * refactored gemspec
data/README.md CHANGED
@@ -5,6 +5,10 @@ Feature is a [feature toggle](http://martinfowler.com/bliki/FeatureToggle.html)
5
5
  The feature toggle functionality has to be configured by feature repositories. A feature repository simply provides lists of active and inctive features.
6
6
  With this approach Feature is higly configurable and not bound to a specific kind of configuration.
7
7
 
8
+ ## CI status
9
+
10
+ [![Travis-CI Build Status](https://secure.travis-ci.org/mgsnova/feature.png)](https://secure.travis-ci.org/mgsnova/feature)
11
+
8
12
  ## Installation
9
13
 
10
14
  gem install feature
@@ -1,12 +1,9 @@
1
1
  module Feature
2
2
  module Repository
3
- ##
4
3
  # Abstract class for subclassing and building repository classes which
5
- # provide lists of active and inactive features
4
+ # provide lists of active features
6
5
  #
7
6
  class AbstractRepository
8
-
9
- ##
10
7
  # Constructor
11
8
  #
12
9
  # Should be overridden in derived class to initialize repository with all data needed
@@ -15,23 +12,13 @@ module Feature
15
12
  raise "abstract class #{self.class.name}!"
16
13
  end
17
14
 
18
- ##
19
15
  # Returns list of active features
20
16
  #
21
- # @return Array<Symbol>
17
+ # @return [Array<Symbol>] list of active features
22
18
  #
23
19
  def active_features
24
20
  raise "#{__method__} has to be overridden in derived class"
25
21
  end
26
-
27
- ##
28
- # Returns list of inactive features
29
- #
30
- # @return Array<Symbol>
31
- #
32
- def inactive_features
33
- raise "#{__method__} has to be overridden in derived class"
34
- end
35
22
  end
36
23
  end
37
24
  end
@@ -1,86 +1,55 @@
1
1
  module Feature
2
2
  module Repository
3
- ##
4
- # SimpleRepository for active and inactive feature list
5
- # Simply add features to both lists, not config or data sources required
3
+ # SimpleRepository for active feature list
4
+ # Simply add features to that should be active, no config or data sources required
6
5
  #
7
6
  class SimpleRepository < AbstractRepository
8
- ##
9
7
  # Constructor
10
8
  #
11
9
  def initialize
12
10
  @active_features = []
13
- @inactive_features = []
14
11
  end
15
12
 
16
- ##
17
13
  # Returns list of active features
18
14
  #
19
- # @return Array<Symbol>
15
+ # @return [Array<Symbol>] list of active features
20
16
  #
21
17
  def active_features
22
18
  @active_features.dup
23
19
  end
24
20
 
25
- ##
26
- # Returns list of inactive features
21
+ # Add an active feature to repository
27
22
  #
28
- # @return Array<Symbol>
29
- #
30
- def inactive_features
31
- @inactive_features.dup
32
- end
33
-
34
- ##
35
- # Add an active feature
36
- #
37
- # @param Sybmol, feature
23
+ # @param [Symbol] feature the feature to be added
38
24
  #
39
25
  def add_active_feature(feature)
40
- check_feature_is_symbol(feature)
41
- check_feature_already_in_lists(feature)
26
+ check_feature_is_not_symbol(feature)
27
+ check_feature_already_in_list(feature)
42
28
  @active_features << feature
43
29
  end
44
30
 
45
- ##
46
- # Add an inactive feature
47
- #
48
- # @param Sybmol, feature
49
- #
50
- def add_inactive_feature(feature)
51
- check_feature_is_symbol(feature)
52
- check_feature_already_in_lists(feature)
53
- @inactive_features << feature
54
- end
55
-
56
- ##
57
- # Checks if the given feature is a symbol and raises and raises an exception if so
31
+ # Checks if the given feature is a not symbol and raises an exception if so
58
32
  #
59
- # @param Sybmol, feature
33
+ # @param [Sybmol] feature the feature to be checked
60
34
  #
61
- def check_feature_is_symbol(feature)
35
+ def check_feature_is_not_symbol(feature)
62
36
  if !feature.is_a?(Symbol)
63
37
  raise ArgumentError, "given feature #{feature} is not a symbol"
64
38
  end
65
39
  end
66
- private :check_feature_is_symbol
40
+ private :check_feature_is_not_symbol
67
41
 
68
- ##
69
- # Checks if given feature is already added to list of active or inactive features
42
+ # Checks if given feature is already added to list of active features
70
43
  # and raises an exception if so
71
44
  #
72
- # @param Symbol, feature
45
+ # @param [Symbol] feature the feature to be checked
73
46
  #
74
- def check_feature_already_in_lists(feature)
47
+ def check_feature_already_in_list(feature)
75
48
  if @active_features.include?(feature)
76
49
  raise ArgumentError, "feature :#{feature} already added to list of active features"
77
50
  end
78
-
79
- if @inactive_features.include?(feature)
80
- raise ArgumentError, "feature :#{feature} already added to list of inactive features"
81
- end
82
51
  end
83
- private :check_feature_already_in_lists
52
+ private :check_feature_already_in_list
84
53
  end
85
54
  end
86
55
  end
@@ -1,78 +1,50 @@
1
1
  module Feature
2
2
  module Repository
3
- ##
4
- # YamlRepository for active and inactive feature list
3
+ # YamlRepository for active and inactive features
5
4
  # The yaml config file should look like this:
6
5
  #
7
- # features:
8
- # an_active_feature: true
9
- # an_inactive_feature: false
6
+ # features:
7
+ # an_active_feature: true
8
+ # an_inactive_feature: false
10
9
  #
11
10
  class YamlRepository < AbstractRepository
12
11
  require 'yaml'
13
12
 
14
- ##
15
13
  # Constructor
16
14
  #
17
- # @param String, yaml_file_name
15
+ # @param [String] yaml_file_name the yaml config filename
18
16
  #
19
17
  def initialize(yaml_file_name)
20
18
  @yaml_file_name = yaml_file_name
21
19
  end
22
20
 
23
- ##
24
21
  # Returns list of active features
25
22
  #
26
- # @return Array<Symbol>
23
+ # @return [Array<Symbol>] list of active features
27
24
  #
28
25
  def active_features
29
- get_active_features_from_file
30
- end
31
-
32
- ##
33
- # Return a list of active features read from the config file
34
- #
35
- # @return Array<Symbol>
36
- #
37
- def get_active_features_from_file
38
26
  features_hash = read_and_parse_file_data
39
27
  features = features_hash.keys.select { |feature_key| features_hash[feature_key] }
40
28
  features.sort.map(&:to_sym)
41
29
  end
42
- private :get_active_features_from_file
43
-
44
- ##
45
- # Returns list of inactive features
46
- #
47
- # @return Array<Symbol>
48
- #
49
- def inactive_features
50
- get_inactive_features_from_file
51
- end
52
-
53
- ##
54
- # Return a list of inactive features read from the config file
55
- #
56
- # @return Array<Symbol>
57
- #
58
- def get_inactive_features_from_file
59
- features_hash = read_and_parse_file_data
60
- features = features_hash.keys.select { |feature_key| !features_hash[feature_key] }
61
- features.sort.map(&:to_sym)
62
- end
63
- private :get_inactive_features_from_file
64
30
 
65
- ##
66
31
  # Read and parses the feature config file
67
32
  #
68
- # @return Hash (with :feature => true/false meaning active/inactive)
33
+ # @return [Hash] Hash containing :feature => true/false entries for representing active/inactive features
69
34
  #
70
35
  def read_and_parse_file_data
71
36
  raw_data = File.read(@yaml_file_name)
72
37
  data = YAML.load(raw_data)
38
+
73
39
  if !data.is_a?(Hash) or !data.has_key?('features')
74
40
  raise ArgumentError, "content of #{@yaml_file_name} does not contain proper config"
75
41
  end
42
+
43
+ invalid_value = data['features'].values.detect { |value| ![true, false].include?(value) }
44
+ if invalid_value
45
+ raise ArgumentError, "#{invalid_value} is not allowed value in config, use true/false"
46
+ end
47
+
76
48
  data['features']
77
49
  end
78
50
  private :read_and_parse_file_data
@@ -1,4 +1,5 @@
1
1
  module Feature
2
+ # Module for holding feature repositories
2
3
  module Repository
3
4
  require 'feature/repository/abstract_repository'
4
5
  require 'feature/repository/simple_repository'
data/lib/feature.rb CHANGED
@@ -1,21 +1,20 @@
1
- ##
2
1
  # Feature module provides all methods
3
- # - to set a feature repository
4
- # - to check if a feature (represented by a symbol) is acitve or inactive
5
- # - for conditional block execution with or without a feature
6
- # - to refresh the feature lists (request them from repository)
2
+ # - to set a feature repository
3
+ # - to check if a feature (represented by a symbol) is active or inactive
4
+ # - for conditional block execution with or without a feature
5
+ # - to refresh the feature lists (request them from repository)
6
+ #
7
+ # @note all features not active will be handled has inactive
7
8
  #
8
9
  module Feature
9
10
  require 'feature/repository'
10
11
 
11
12
  @repository = nil
12
13
  @active_features = nil
13
- @inactive_features = nil
14
14
 
15
- ##
16
15
  # Set the feature repository
17
16
  #
18
- # @param Feature::Repository::AbstractRepository, repository
17
+ # @param [Feature::Repository::AbstractRepository] repository the repository to get the features from
19
18
  #
20
19
  def self.set_repository(repository)
21
20
  if !repository.is_a?(Feature::Repository::AbstractRepository)
@@ -26,19 +25,17 @@ module Feature
26
25
  refresh!
27
26
  end
28
27
 
29
- ##
30
- # Obtains list of active and inactive features from repository
28
+ # Obtains list of active features from repository
31
29
  #
32
30
  def self.refresh!
33
31
  @active_features = @repository.active_features
34
- @inactive_features = @repository.inactive_features
35
32
  end
36
33
 
37
34
  ##
38
35
  # Requests if feature is active
39
36
  #
40
- # @param Symbol, feature
41
- # @return Boolean
37
+ # @param [Symbol] feature
38
+ # @return [Boolean]
42
39
  #
43
40
  def self.active?(feature)
44
41
  if !@repository
@@ -48,23 +45,19 @@ module Feature
48
45
  @active_features.include?(feature)
49
46
  end
50
47
 
51
- ##
52
- # Requests if feature is inactive
48
+ # Requests if feature is inactive (or unknown)
53
49
  #
54
- # @param Symbol, feature
55
- # @return Boolean
50
+ # @param [Symbol] feature
51
+ # @return [Boolean]
56
52
  #
57
53
  def self.inactive?(feature)
58
- if !@repository
59
- raise "Feature is missing Repository for obtaining feature lists"
60
- end
61
-
62
- @inactive_features.include?(feature)
54
+ !self.active?(feature)
63
55
  end
64
56
 
65
- ##
66
57
  # Execute the given block if feature is active
67
58
  #
59
+ # @param [Symbol] feature
60
+ #
68
61
  def self.with(feature)
69
62
  if !block_given?
70
63
  raise ArgumentError, "no block given to #{__method__}"
@@ -75,9 +68,10 @@ module Feature
75
68
  end
76
69
  end
77
70
 
78
- ##
79
71
  # Execute the given block if feature is inactive
80
72
  #
73
+ # @param [Symbol] feature
74
+ #
81
75
  def self.without(feature)
82
76
  if !block_given?
83
77
  raise ArgumentError, "no block given to #{__method__}"
@@ -55,11 +55,6 @@ describe Feature do
55
55
  @repository.add_active_feature(:feature_a)
56
56
  Feature.active?(:feature_a).should be_false
57
57
  end
58
-
59
- it "should get inactive features from repository once" do
60
- @repository.add_inactive_feature(:feature_a)
61
- Feature.inactive?(:feature_a).should be_false
62
- end
63
58
  end
64
59
 
65
60
  context "refresh features" do
@@ -73,19 +68,12 @@ describe Feature do
73
68
  Feature.refresh!
74
69
  Feature.active?(:feature_a).should be_true
75
70
  end
76
-
77
- it "should refresh inactive feature lists from repository" do
78
- @repository.add_inactive_feature(:feature_a)
79
- Feature.refresh!
80
- Feature.inactive?(:feature_a).should be_true
81
- end
82
71
  end
83
72
 
84
73
  context "request features" do
85
74
  before(:each) do
86
75
  repository = Feature::Repository::SimpleRepository.new
87
76
  repository.add_active_feature :feature_active
88
- repository.add_inactive_feature :feature_inactive
89
77
  Feature.set_repository repository
90
78
  end
91
79
 
@@ -11,10 +11,6 @@ describe Feature::Repository::SimpleRepository do
11
11
  @repository.active_features.should == []
12
12
  end
13
13
 
14
- it "should have no inactive features after initialization" do
15
- @repository.inactive_features.should == []
16
- end
17
-
18
14
  it "should add an active feature" do
19
15
  @repository.add_active_feature :feature_a
20
16
  @repository.active_features.should == [:feature_a]
@@ -32,36 +28,4 @@ describe Feature::Repository::SimpleRepository do
32
28
  @repository.add_active_feature :feature_a
33
29
  end.should raise_error(ArgumentError, "feature :feature_a already added to list of active features")
34
30
  end
35
-
36
- it "should raise an exception when adding a active feature already added as inactive" do
37
- @repository.add_inactive_feature :feature_a
38
- lambda do
39
- @repository.add_active_feature :feature_a
40
- end.should raise_error(ArgumentError, "feature :feature_a already added to list of inactive features")
41
- end
42
-
43
- it "should add an inactive feature" do
44
- @repository.add_inactive_feature :feature_a
45
- @repository.inactive_features.should == [:feature_a]
46
- end
47
-
48
- it "should raise an exception when adding not a symbol as inactive feature" do
49
- lambda do
50
- @repository.add_inactive_feature 'feature_a'
51
- end.should raise_error(ArgumentError, "given feature feature_a is not a symbol")
52
- end
53
-
54
- it "should raise an exception when adding a inactive feature already added as inactive" do
55
- @repository.add_inactive_feature :feature_a
56
- lambda do
57
- @repository.add_inactive_feature :feature_a
58
- end.should raise_error(ArgumentError, "feature :feature_a already added to list of inactive features")
59
- end
60
-
61
- it "should raise an exception when adding a inactive feature already added as active" do
62
- @repository.add_active_feature :feature_a
63
- lambda do
64
- @repository.add_inactive_feature :feature_a
65
- end.should raise_error(ArgumentError, "feature :feature_a already added to list of active features")
66
- end
67
31
  end
@@ -28,10 +28,6 @@ EOF
28
28
  @repo.active_features.should == [:feature_a_active, :feature_b_active]
29
29
  end
30
30
 
31
- it "should read inactive features from a config file" do
32
- @repo.inactive_features.should == [:feature_c_inactive, :feature_d_inactive]
33
- end
34
-
35
31
  context "re-read config file" do
36
32
  before(:each) do
37
33
  fp = File.new(@filename, 'w')
@@ -46,10 +42,6 @@ EOF
46
42
  it "should read active features new on each request" do
47
43
  @repo.active_features.should == [:feature_a_active]
48
44
  end
49
-
50
- it "should read inactive features new on each request" do
51
- @repo.inactive_features.should == [:feature_c_inactive]
52
- end
53
45
  end
54
46
  end
55
47
 
@@ -71,4 +63,19 @@ EOF
71
63
  repo.active_features
72
64
  end.should raise_error(ArgumentError, "content of #{@filename} does not contain proper config")
73
65
  end
66
+
67
+ it "should raise exception on not true/false value in config" do
68
+ @filename = Tempfile.new(['feature_config', '.yaml']).path
69
+ fp = File.new(@filename, 'w')
70
+ fp.write <<"EOF";
71
+ features:
72
+ invalid_feature: neither_true_or_false
73
+ EOF
74
+ fp.close
75
+
76
+ repo = YamlRepository.new(@filename)
77
+ lambda do
78
+ repo.active_features
79
+ end.should raise_error(ArgumentError, "neither_true_or_false is not allowed value in config, use true/false")
80
+ end
74
81
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feature
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Markus Gerdes
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-22 00:00:00 +01:00
18
+ date: 2011-11-23 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21