feature 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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