flip_the_switch 0.3.0 → 0.4.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Example/.gitignore +2 -1
  4. data/Example/Colors-iOS/ViewControlleriOS.m +27 -0
  5. data/Example/Colors.xcodeproj/project.pbxproj +4 -0
  6. data/Example/Rakefile +1 -0
  7. data/Example/features.yml +3 -2
  8. data/FlipTheSwitch.podspec +1 -1
  9. data/README.md +13 -3
  10. data/bin/flip-the-switch +6 -1
  11. data/flip_the_switch.gemspec +1 -1
  12. data/lib/flip_the_switch/cli.rb +28 -11
  13. data/lib/flip_the_switch/errors.rb +9 -0
  14. data/lib/flip_the_switch/generator/category.rb +1 -0
  15. data/lib/flip_the_switch/generator/plist.rb +1 -0
  16. data/lib/flip_the_switch/generator/settings.rb +121 -0
  17. data/lib/flip_the_switch/generator.rb +1 -1
  18. data/lib/flip_the_switch/reader/defaults.rb +43 -0
  19. data/lib/flip_the_switch/reader/features.rb +53 -0
  20. data/lib/flip_the_switch/reader.rb +2 -1
  21. data/spec/flip_the_switch/cli_spec.rb +12 -5
  22. data/spec/flip_the_switch/generator/category_spec.rb +1 -1
  23. data/spec/flip_the_switch/generator/plist_spec.rb +1 -1
  24. data/spec/flip_the_switch/generator/settings_spec.rb +80 -0
  25. data/spec/flip_the_switch/reader/defaults_spec.rb +96 -0
  26. data/spec/flip_the_switch/reader/{yaml_spec.rb → features_spec.rb} +17 -4
  27. data/spec/resources/ExistingSettingsRoot.plist +33 -0
  28. data/spec/resources/ExpectedSettingsBaseRoot.plist +23 -0
  29. data/spec/resources/ExpectedSettingsFeatures.plist +29 -0
  30. data/spec/resources/ExpectedSettingsMergeExistingRoot.plist +47 -0
  31. data/spec/resources/real/features.yml +6 -2
  32. metadata +20 -6
  33. data/lib/flip_the_switch/reader/yaml.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 619f1abd162d81a6bbad84209c1cea55f2b27aa4
4
- data.tar.gz: 5fe65dd5282fa538e9d7fae127bed002ad3b5b01
3
+ metadata.gz: 1e9b8507bbc66a9479d3fe174c8a42fba3495200
4
+ data.tar.gz: 01f913063a72ab9a2bd670fe7600f0d4b16fc830
5
5
  SHA512:
6
- metadata.gz: a2e7f90c45df40258f8ff75ccae108e4ca54d57f382458b617453d8871eac376846172f232ab1a0a5c2d5832a3cb24d55c4bfa85bc5bb5da26082832f3981b4b
7
- data.tar.gz: 6d7949f226ffec6785cadf0c6008ec653b9d0f28ec266c61db17eb92815ae79e941927dfa11cd98c9ad14eaf91f3e62c312cf7fe34cade0db96a209a7ef2391a
6
+ metadata.gz: 8077a424aec23a34f08139b7213c8f17d8c650d980a3cea097c1d137609f08d5af256823eacf89b9fd67af9b5fd3255a6d7cf70e9a4a6d0a8e62a34bd7256144
7
+ data.tar.gz: a5a65e6aa1a43e8e22a4424a76efd73a90c99106c851ff5cfb2880ee4ea71529d7a159c352b57ebcfa02e34456c439661015f0fa86ebe9599f51dd5190489ce1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 0.4.0 / 2014-07-16
2
+
3
+ * [FEATURE] #17 Use environments for feature states
4
+ * [FEATURE] #16 Allow reading of CLI defaults from yml file
5
+ * [FEATURE] #14 Auto-create Settings.bundle from feature defaults
6
+
1
7
  # 0.3.0 / 2014-07-02
2
8
 
3
9
  * [FEATURE] #15 Auto-create category for features
data/Example/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  Colors-iOS/Features.plist
2
- Colors-iOS/FlipTheSwitch+Features.*
2
+ Colors-iOS/FlipTheSwitch+Features.*
3
+ Colors-iOS/Settings.bundle/
@@ -12,12 +12,39 @@
12
12
 
13
13
  @implementation ViewControlleriOS
14
14
 
15
+ #pragma mark - Lifecycle
16
+
17
+ - (void)dealloc
18
+ {
19
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
20
+ }
21
+
15
22
  #pragma mark - UIViewController Lifecycle
16
23
 
17
24
  - (void)viewDidLoad
18
25
  {
19
26
  [super viewDidLoad];
20
27
  [self setupView];
28
+ [self setupStateChangeNotifications];
29
+ }
30
+
31
+ - (void)setupStateChangeNotifications
32
+ {
33
+ [self setupNotification:UIApplicationWillEnterForegroundNotification];
34
+ [self setupNotification:UIApplicationDidBecomeActiveNotification];
35
+ }
36
+
37
+ - (void)setupNotification:(NSString *)notification
38
+ {
39
+ [[NSNotificationCenter defaultCenter] addObserver:self
40
+ selector:@selector(applicationStateChanged:)
41
+ name:notification
42
+ object:nil];
43
+ }
44
+
45
+ - (void)applicationStateChanged:(UIApplication *)application
46
+ {
47
+ [self setupView];
21
48
  }
22
49
 
23
50
  #pragma mark - Actions
@@ -8,6 +8,7 @@
8
8
 
9
9
  /* Begin PBXBuildFile section */
10
10
  35D6E35AEAC9473FAAAB5BE5 /* libPods-Colors-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F7C4D132FE24A06ACC52E5F /* libPods-Colors-iOS.a */; };
11
+ 5B13CA4919721395001D008A /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5B13CA4819721395001D008A /* Settings.bundle */; };
11
12
  5B20701A194237BF00D07667 /* Features.plist in Resources */ = {isa = PBXBuildFile; fileRef = F80321679CBE499FFA387688 /* Features.plist */; };
12
13
  5B909898193A235E004C12B3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B909897193A235E004C12B3 /* Foundation.framework */; };
13
14
  5B90989C193A235E004C12B3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B90989B193A235E004C12B3 /* UIKit.framework */; };
@@ -20,6 +21,7 @@
20
21
  /* End PBXBuildFile section */
21
22
 
22
23
  /* Begin PBXFileReference section */
24
+ 5B13CA4819721395001D008A /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
23
25
  5B909894193A235E004C12B3 /* Colors-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Colors-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
24
26
  5B909897193A235E004C12B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
25
27
  5B90989B193A235E004C12B3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
@@ -93,6 +95,7 @@
93
95
  F803243E8A66F29C3EA37B65 /* AppDelegateiOS.m */,
94
96
  F8032EBDB675B3B788B1297F /* FlipTheSwitch+Features.h */,
95
97
  F803287440254AB051D00DDB /* FlipTheSwitch+Features.m */,
98
+ 5B13CA4819721395001D008A /* Settings.bundle */,
96
99
  );
97
100
  path = "Colors-iOS";
98
101
  sourceTree = "<group>";
@@ -162,6 +165,7 @@
162
165
  isa = PBXResourcesBuildPhase;
163
166
  buildActionMask = 2147483647;
164
167
  files = (
168
+ 5B13CA4919721395001D008A /* Settings.bundle in Resources */,
165
169
  5B20701A194237BF00D07667 /* Features.plist in Resources */,
166
170
  5B9098B3193A235E004C12B3 /* Images.xcassets in Resources */,
167
171
  5B9098AB193A235E004C12B3 /* Main_iPhone.storyboard in Resources */,
data/Example/Rakefile CHANGED
@@ -19,5 +19,6 @@ end
19
19
  task :test => podfile_lock do
20
20
  sh 'bundle exec ../bin/flip-the-switch plist -o Colors-iOS'
21
21
  sh 'bundle exec ../bin/flip-the-switch category -o Colors-iOS'
22
+ sh 'bundle exec ../bin/flip-the-switch settings -o Colors-iOS'
22
23
  sh "xctool build -scheme #{workspace}-iOS -workspace #{workspace}.xcworkspace -sdk iphonesimulator OBJROOT=#{build} SHARED_PRECOMPS_DIR=#{build}"
23
24
  end
data/Example/features.yml CHANGED
@@ -1,2 +1,3 @@
1
- purple_color: Yes
2
- red_color: No
1
+ default:
2
+ purple_color: Yes
3
+ red_color: No
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'FlipTheSwitch'
3
- s.version = '0.3.0'
3
+ s.version = '0.4.0'
4
4
  s.summary = 'A simple library to help enabling/disabling features on iOS/Mac applications.'
5
5
  s.authors = {
6
6
  'Michael England' => 'mg.england@gmail.com',
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A feature switching/toggling/flipping library for ObjectiveC.
4
4
 
5
- # flip_the_switch [![Build Status](https://travis-ci.org/michaelengland/FlipTheSwitch.svg?branch=master)](https://travis-ci.org/michaelengland/FlipTheSwitch) [![Code Climate](https://codeclimate.com/github/michaelengland/FlipTheSwitch.png)](https://codeclimate.com/github/michaelengland/FlipTheSwitch) [![Code Climate](https://codeclimate.com/github/michaelengland/FlipTheSwitch/coverage.png)](https://codeclimate.com/github/michaelengland/FlipTheSwitch) [![Gem Version](https://badge.fury.io/rb/flip-the-switch.svg)](http://badge.fury.io/rb/flip-the-switch)
5
+ # flip_the_switch [![Build Status](https://travis-ci.org/michaelengland/FlipTheSwitch.svg?branch=master)](https://travis-ci.org/michaelengland/FlipTheSwitch) [![Code Climate](https://codeclimate.com/github/michaelengland/FlipTheSwitch.png)](https://codeclimate.com/github/michaelengland/FlipTheSwitch) [![Code Climate](https://codeclimate.com/github/michaelengland/FlipTheSwitch/coverage.png)](https://codeclimate.com/github/michaelengland/FlipTheSwitch) [![Gem Version](https://badge.fury.io/rb/flip_the_switch.svg)](http://badge.fury.io/rb/flip_the_switch)
6
6
 
7
7
  A gem command line tool for generating the `Features.plist` & `FlipTheSwitch+Features.{h,m}` categories to help with the corresponding Pod.
8
8
 
@@ -63,6 +63,7 @@ If you install the gem, you will be able to use the Command-Line-Interface.
63
63
  The CLI consists of 2 commands:
64
64
 
65
65
  - `plist` - creates a `Features.plist` file for enabled/disabled features like that mentioned above.
66
+ - `settings` - creates a `Settings.bundle` used by the OS settings. These can then be used to enable/disable the features at runtime.
66
67
  - `category` - creates `FlipTheSwitch+Features.{h,m}` files for features, thus giving compile-time checks for adding/removal of new features.
67
68
  e.g:
68
69
 
@@ -83,7 +84,16 @@ e.g:
83
84
  The features, along with their default enabled/disabled state, are read from a `features.yml` file. e.g.:
84
85
 
85
86
  ```yaml
86
- awesome_feature: Yes
87
+ default:
88
+ awesome_feature: Yes
89
+ ```
90
+
91
+ In order to avoid typing in the same options all the time, you can create a `.flip.yml` file for the default options, e.g.:
92
+
93
+ ```yaml
94
+ input: features
95
+ environment: development
96
+ category_output: Classes/Extensions
87
97
  ```
88
98
 
89
99
  For more information, run `flip-the-switch help`
@@ -92,7 +102,7 @@ For more information, run `flip-the-switch help`
92
102
 
93
103
  Add `pod 'FlipTheSwitch'` to your Podfile
94
104
 
95
- Add `gem 'flip-the-switch'` to your Gemfile
105
+ Add `gem 'flip_the_switch'` to your Gemfile
96
106
 
97
107
  ## Authors
98
108
 
data/bin/flip-the-switch CHANGED
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'flip_the_switch/cli'
3
3
 
4
- FlipTheSwitch::Cli.start
4
+ begin
5
+ FlipTheSwitch::Cli.start
6
+ rescue FlipTheSwitch::Error::Base => e
7
+ $stderr.puts e
8
+ exit(false)
9
+ end
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
4
4
  s.email = %w(mg.england@gmail.com rob@robsiwek.com)
5
5
  s.summary = 'A simple library to help enabling/disabling features on iOS/Mac applications.'
6
6
  s.homepage = 'https://github.com/michaelengland/FlipTheSwitch'
7
- s.version = '0.3.0'
7
+ s.version = '0.4.0'
8
8
  s.license = 'MIT'
9
9
 
10
10
  s.add_dependency 'activesupport', '~> 3.2'
@@ -3,22 +3,35 @@ require 'flip_the_switch'
3
3
 
4
4
  module FlipTheSwitch
5
5
  class Cli < Thor
6
- class_option :input, type: :string, aliases: '-i', default: Dir.pwd, desc: 'Location of the directory containing features.yml file to read'
7
- class_option :enabled, type: :array, aliases: '-e', default: [], desc: 'Extra features to be set as enabled'
8
- class_option :disabled, type: :array, aliases: '-d', default: [], desc: 'Extra features to be set as disabled'
6
+ private
7
+ def self.defaults
8
+ @defaults ||= Reader::Defaults.new.defaults
9
+ end
10
+
11
+ public
12
+ class_option :input, type: :string, aliases: '-i', default: defaults[:input], desc: 'Location of the directory containing features.yml file to read'
13
+ class_option :environment, type: :string, aliases: '-n', default: defaults[:environment], desc: 'Name of environment to read from in features.yml file'
14
+ class_option :enabled, type: :string, aliases: '-e', default: defaults[:enabled], desc: 'Extra features to be set as enabled'
15
+ class_option :disabled, type: :string, aliases: '-d', default: defaults[:disabled], desc: 'Extra features to be set as disabled'
9
16
 
10
17
  desc 'plist', 'Auto-generates a Features.plist file for enabled/disabled features'
11
- method_option :output, type: :string, aliases: '-o', default: Dir.pwd, desc: 'Location of the directory in which Features.plist file will be created'
18
+ method_option :output, type: :string, aliases: '-o', default: defaults[:plist_output], desc: 'Location of the directory in which Features.plist file will be created'
12
19
  def plist
13
20
  plist_generator.generate
14
21
  end
15
22
 
16
23
  desc 'category', 'Auto-generates .h & .m files for enabled/disabled features'
17
- method_option :output, type: :string, aliases: '-o', default: Dir.pwd, desc: 'Location of the directory in which FlipTheSwitch+Features.{h,m} files will be created'
24
+ method_option :output, type: :string, aliases: '-o', default: defaults[:category_output], desc: 'Location of the directory in which FlipTheSwitch+Features.{h,m} files will be created'
18
25
  def category
19
26
  category_generator.generate
20
27
  end
21
28
 
29
+ desc 'settings', 'Auto-generates settings.bundle files for enabling/disabling features from iOS settings menu'
30
+ method_option :output, type: :string, aliases: '-o', default: defaults[:settings_output], desc: 'Location of the directory in which FlipTheSwitch+Features.{h,m} files will be created'
31
+ def settings
32
+ settings_generator.generate
33
+ end
34
+
22
35
  private
23
36
 
24
37
  def plist_generator
@@ -29,26 +42,30 @@ module FlipTheSwitch
29
42
  Generator::Category.new(output, feature_states)
30
43
  end
31
44
 
45
+ def settings_generator
46
+ Generator::Settings.new(output, feature_states)
47
+ end
48
+
32
49
  def output
33
- options.output
50
+ options['output']
34
51
  end
35
52
 
36
53
  def feature_states
37
- yaml_reader.feature_states.
54
+ feature_reader.feature_states.
38
55
  merge(enabled_states).
39
56
  merge(disabled_states)
40
57
  end
41
58
 
42
- def yaml_reader
43
- Reader::Yaml.new(options.input)
59
+ def feature_reader
60
+ Reader::Features.new(options['input'], options['environment'])
44
61
  end
45
62
 
46
63
  def enabled_states
47
- states_for(options.enabled, true)
64
+ states_for(options['enabled'].split(','), true)
48
65
  end
49
66
 
50
67
  def disabled_states
51
- states_for(options.disabled, false)
68
+ states_for(options['disabled'].split(','), false)
52
69
  end
53
70
 
54
71
  def states_for(array, default)
@@ -7,6 +7,15 @@ module FlipTheSwitch
7
7
  end
8
8
 
9
9
  class InvalidFile < Base
10
+ def initialize(file)
11
+ super("Invalid file - #{file}")
12
+ end
13
+ end
14
+
15
+ class InvalidEnvironment < Base
16
+ def initialize(environment)
17
+ super("Invalid environment - #{environment}")
18
+ end
10
19
  end
11
20
  end
12
21
  end
@@ -1,3 +1,4 @@
1
+ require 'flip_the_switch/generator/base'
1
2
  require 'active_support/inflector'
2
3
  require 'erb'
3
4
 
@@ -1,3 +1,4 @@
1
+ require 'flip_the_switch/generator/base'
1
2
  require 'plist'
2
3
 
3
4
  module FlipTheSwitch
@@ -0,0 +1,121 @@
1
+ require 'flip_the_switch/generator/base'
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+ require 'plist'
4
+
5
+ module FlipTheSwitch
6
+ module Generator
7
+ class Settings < Base
8
+ def generate
9
+ create_settings_bundle_if_not_exists
10
+ read_settings
11
+ delete_existing_settings_if_exist
12
+ write_settings
13
+ end
14
+
15
+ private
16
+
17
+ def create_settings_bundle_if_not_exists
18
+ unless Dir.exists?(settings_bundle)
19
+ Dir.mkdir(settings_bundle)
20
+ end
21
+ end
22
+
23
+ def read_settings
24
+ current_plist
25
+ end
26
+
27
+ def delete_existing_settings_if_exist
28
+ delete_root_plist_if_exists
29
+ delete_features_plist_if_exists
30
+ end
31
+
32
+ def write_settings
33
+ write_root_plist
34
+ write_features_plist
35
+ end
36
+
37
+ def delete_root_plist_if_exists
38
+ delete_file(root_plist)
39
+ end
40
+
41
+ def delete_features_plist_if_exists
42
+ delete_file(features_plist)
43
+ end
44
+
45
+ def write_root_plist
46
+ ::Plist::Emit.save_plist(root, root_plist)
47
+ end
48
+
49
+ def write_features_plist
50
+ ::Plist::Emit.save_plist(features, features_plist)
51
+ end
52
+
53
+ def delete_file(file)
54
+ File.delete(file) if File.exists?(file)
55
+ end
56
+
57
+ def root
58
+ current_plist.with_indifferent_access.merge(PreferenceSpecifiers: root_preferences)
59
+ end
60
+
61
+ def root_preferences
62
+ existing_root_preferences.delete_if { |root_preference|
63
+ root_preference[:Title] == 'Features'
64
+ } + feature_root_preferences
65
+ end
66
+
67
+ def existing_root_preferences
68
+ current_plist.with_indifferent_access[:PreferenceSpecifiers] || []
69
+ end
70
+
71
+ def feature_root_preferences
72
+ [
73
+ {
74
+ Title: 'Features',
75
+ Type: 'PSGroupSpecifier'
76
+ },
77
+ {
78
+ File: 'Features',
79
+ Title: 'Features',
80
+ Type: 'PSChildPaneSpecifier'
81
+ }
82
+ ]
83
+ end
84
+
85
+ def features
86
+ {PreferenceSpecifiers: feature_toggles}
87
+ end
88
+
89
+ def feature_toggles
90
+ feature_states.map { |feature, state|
91
+ {
92
+ Type: 'PSToggleSwitchSpecifier',
93
+ Title: feature,
94
+ Key: "FTS_FEATURE_#{feature}",
95
+ DefaultValue: state
96
+ }
97
+ }
98
+ end
99
+
100
+ def current_plist
101
+ @current_plist ||= if File.exists?(root_plist)
102
+ ::Plist::parse_xml(root_plist)
103
+ else
104
+ {}
105
+ end
106
+ end
107
+
108
+ def root_plist
109
+ File.join(settings_bundle, 'Root.plist')
110
+ end
111
+
112
+ def features_plist
113
+ File.join(settings_bundle, 'Features.plist')
114
+ end
115
+
116
+ def settings_bundle
117
+ File.join(output, 'Settings.bundle')
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,3 +1,3 @@
1
- require 'flip_the_switch/generator/base'
2
1
  require 'flip_the_switch/generator/category'
3
2
  require 'flip_the_switch/generator/plist'
3
+ require 'flip_the_switch/generator/settings'
@@ -0,0 +1,43 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ module FlipTheSwitch
4
+ module Reader
5
+ class Defaults
6
+ def defaults
7
+ if valid_file?
8
+ base_defaults.merge(file_defaults)
9
+ else
10
+ raise Error::InvalidFile.new(defaults_file)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def valid_file?
17
+ file_defaults.is_a?(Hash)
18
+ end
19
+
20
+ def base_defaults
21
+ {
22
+ input: Dir.pwd,
23
+ environment: 'default',
24
+ enabled: '',
25
+ disabled: '',
26
+ category_output: Dir.pwd,
27
+ plist_output: Dir.pwd,
28
+ settings_output: Dir.pwd
29
+ }.with_indifferent_access
30
+ end
31
+
32
+ def file_defaults
33
+ @file_defaults ||= YAML.load_file(defaults_file)
34
+ rescue SystemCallError
35
+ {}
36
+ end
37
+
38
+ def defaults_file
39
+ '.flip.yml'
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ require 'yaml'
2
+
3
+ module FlipTheSwitch
4
+ module Reader
5
+ class Features
6
+ def initialize(input, environment)
7
+ @input = input
8
+ @environment = environment
9
+ end
10
+
11
+ def feature_states
12
+ if valid_file?
13
+ environment_states
14
+ else
15
+ raise Error::InvalidFile.new(input_file)
16
+ end
17
+ end
18
+
19
+ private
20
+ attr_reader :input, :environment
21
+
22
+ def valid_file?
23
+ file_states.is_a?(Hash) && file_states.all? { |environment, feature|
24
+ environment.is_a?(String) && valid_feature_hash?(feature)
25
+ }
26
+ end
27
+
28
+ def valid_feature_hash?(feature_hash)
29
+ feature_hash.is_a?(Hash) && feature_hash.all? { |feature, state|
30
+ feature.is_a?(String) && !!state == state
31
+ }
32
+ end
33
+
34
+ def environment_states
35
+ if file_states.has_key?(environment)
36
+ file_states[environment]
37
+ else
38
+ raise Error::InvalidEnvironment.new(environment)
39
+ end
40
+ end
41
+
42
+ def file_states
43
+ @file_states ||= YAML.load_file(input_file)
44
+ rescue SystemCallError => e
45
+ raise Error::UnreadableFile.new(e)
46
+ end
47
+
48
+ def input_file
49
+ File.join(input, 'features.yml')
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1 +1,2 @@
1
- require 'flip_the_switch/reader/yaml'
1
+ require 'flip_the_switch/reader/features'
2
+ require 'flip_the_switch/reader/defaults'
@@ -16,14 +16,14 @@ describe FlipTheSwitch::Cli do
16
16
  end
17
17
 
18
18
  shared_examples_for 'generator' do
19
- let(:yaml_reader) { double(FlipTheSwitch::Reader::Yaml, feature_states: {'something' => true}) }
19
+ let(:feature_reader) { double(FlipTheSwitch::Reader::Features, feature_states: {'something' => true}) }
20
20
  let(:generator) { double(generator_class) }
21
21
 
22
22
  context 'when no options given' do
23
23
  let(:options) { [] }
24
24
 
25
25
  before do
26
- FlipTheSwitch::Reader::Yaml.stub(:new).with(Dir.pwd).and_return(yaml_reader)
26
+ FlipTheSwitch::Reader::Features.stub(:new).with(Dir.pwd, 'default').and_return(feature_reader)
27
27
  generator_class.stub(:new).with(Dir.pwd, 'something' => true).and_return(generator)
28
28
  end
29
29
 
@@ -35,7 +35,7 @@ describe FlipTheSwitch::Cli do
35
35
 
36
36
  context 'when options given' do
37
37
  before do
38
- FlipTheSwitch::Reader::Yaml.stub(:new).with('input').and_return(yaml_reader)
38
+ FlipTheSwitch::Reader::Features.stub(:new).with('input', 'environment').and_return(feature_reader)
39
39
  generator_class.stub(:new).with('output', {
40
40
  'something' => true,
41
41
  'en' => true,
@@ -46,7 +46,7 @@ describe FlipTheSwitch::Cli do
46
46
  end
47
47
 
48
48
  context 'using full name' do
49
- let(:options) { %w(--input=input --output=output --enabled=en abled --disabled=dis appointing) }
49
+ let(:options) { %w(--input=input --environment=environment --output=output --enabled=en,abled --disabled=dis,appointing) }
50
50
 
51
51
  it 'generates using the options given' do
52
52
  expect(generator).to receive(:generate)
@@ -55,7 +55,7 @@ describe FlipTheSwitch::Cli do
55
55
  end
56
56
 
57
57
  context 'using aliases' do
58
- let(:options) { %w(-i=input -o=output -e=en abled -d=dis appointing) }
58
+ let(:options) { %w(-i=input -n=environment -o=output -e=en,abled -d=dis,appointing) }
59
59
 
60
60
  it 'generates using the options given' do
61
61
  expect(generator).to receive(:generate)
@@ -78,4 +78,11 @@ describe FlipTheSwitch::Cli do
78
78
 
79
79
  it_behaves_like 'generator'
80
80
  end
81
+
82
+ context 'when settings command called' do
83
+ let(:command) { 'settings' }
84
+ let(:generator_class) { FlipTheSwitch::Generator::Settings }
85
+
86
+ it_behaves_like 'generator'
87
+ end
81
88
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FlipTheSwitch::Generator::Category do
4
- subject(:category) { FlipTheSwitch::Generator::Category.new(output, feature_states) }
4
+ subject(:category) { described_class.new(output, feature_states) }
5
5
  let(:output) { 'spec/resources' }
6
6
  let(:feature_states) { {'first_feature' => true, 'second_feature' => false} }
7
7
  let(:expected_header_file) { File.read('spec/resources/expected_header.h') }
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FlipTheSwitch::Generator::Plist do
4
- subject(:plist) { FlipTheSwitch::Generator::Plist.new(output, feature_states) }
4
+ subject(:plist) { described_class.new(output, feature_states) }
5
5
  let(:output) { 'spec/resources' }
6
6
  let(:feature_states) { {enabled_feature: true, disabled_feature: false} }
7
7
  let(:output_file) { 'spec/resources/Features.plist' }
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe FlipTheSwitch::Generator::Settings do
5
+ subject(:settings) { described_class.new(output, feature_states) }
6
+ let(:output) { 'spec/resources' }
7
+ let(:settings_bundle) { File.join(output, 'Settings.bundle') }
8
+ let(:feature_states) { {'first_feature' => true, 'second_feature' => false} }
9
+ let(:root_output_file) { File.read(File.join(settings_bundle, 'Root.plist')) }
10
+ let(:features_output_file) { File.read(File.join(settings_bundle, 'Features.plist')) }
11
+ let(:features_output_file) { File.read(File.join(settings_bundle, 'Features.plist')) }
12
+ let(:expected_features_file) { File.read('spec/resources/ExpectedSettingsFeatures.plist')}
13
+
14
+ after do
15
+ delete_settings_bundle_if_exists
16
+ end
17
+
18
+ context 'when settings already exist' do
19
+ let(:expected_root_file) { File.read('spec/resources/ExpectedSettingsMergeExistingRoot.plist') }
20
+
21
+ before do
22
+ Dir.mkdir(settings_bundle)
23
+ File.write(File.join(settings_bundle, 'Root.plist'), File.read('spec/resources/ExistingSettingsRoot.plist'))
24
+ end
25
+
26
+ it 'adds child page link to existing settings' do
27
+ subject.generate
28
+
29
+ expect(root_output_file).to eql(expected_root_file)
30
+ end
31
+
32
+ it 'creates a features settings page' do
33
+ subject.generate
34
+
35
+ expect(features_output_file).to eql(expected_features_file)
36
+ end
37
+ end
38
+
39
+ context 'when settings already exist and have already got features' do
40
+ let(:expected_root_file) { File.read('spec/resources/ExpectedSettingsMergeExistingRoot.plist') }
41
+
42
+ before do
43
+ Dir.mkdir(settings_bundle)
44
+ File.write(File.join(settings_bundle, 'Root.plist'), File.read('spec/resources/ExpectedSettingsMergeExistingRoot.plist'))
45
+ File.write(File.join(settings_bundle, 'Features.plist'), File.read('spec/resources/ExpectedFeatures.plist'))
46
+ end
47
+
48
+ it 'creates settings with features child page link' do
49
+ subject.generate
50
+
51
+ expect(root_output_file).to eql(expected_root_file)
52
+ end
53
+
54
+ it 'creates a features settings page' do
55
+ subject.generate
56
+
57
+ expect(features_output_file).to eql(expected_features_file)
58
+ end
59
+ end
60
+
61
+ context 'when no settings already exist' do
62
+ let(:expected_root_file) { File.read('spec/resources/ExpectedSettingsBaseRoot.plist') }
63
+
64
+ it 'creates settings with features child page link' do
65
+ subject.generate
66
+
67
+ expect(root_output_file).to eql(expected_root_file)
68
+ end
69
+
70
+ it 'creates a features settings page' do
71
+ subject.generate
72
+
73
+ expect(features_output_file).to eql(expected_features_file)
74
+ end
75
+ end
76
+
77
+ def delete_settings_bundle_if_exists
78
+ FileUtils.rm_rf(settings_bundle) if Dir.exists?(settings_bundle)
79
+ end
80
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlipTheSwitch::Reader::Defaults do
4
+ subject(:reader) { described_class.new }
5
+ let(:defaults_file) { '.flip.yml' }
6
+
7
+ after do
8
+ File.delete(defaults_file) if File.exists?(defaults_file)
9
+ end
10
+
11
+ context 'when defaults file exists' do
12
+ context 'when valid' do
13
+ before do
14
+ File.write(defaults_file, YAML.dump({
15
+ input: 'input',
16
+ environment: 'environment',
17
+ enabled: 'enabled',
18
+ disabled: 'disabled',
19
+ category_output: 'category_output',
20
+ plist_output: 'plist_output',
21
+ settings_output: 'settings_output'
22
+ }))
23
+ end
24
+
25
+ it 'returns file input default' do
26
+ expect(subject.defaults[:input]).to eql('input')
27
+ end
28
+
29
+ it 'returns file environment default' do
30
+ expect(subject.defaults[:environment]).to eql('environment')
31
+ end
32
+
33
+ it 'returns file enabled default' do
34
+ expect(subject.defaults[:enabled]).to eql('enabled')
35
+ end
36
+
37
+ it 'returns file disabled default' do
38
+ expect(subject.defaults[:disabled]).to eql('disabled')
39
+ end
40
+
41
+ it 'returns file category output default' do
42
+ expect(subject.defaults[:category_output]).to eql('category_output')
43
+ end
44
+
45
+ it 'returns file plist output default' do
46
+ expect(subject.defaults[:plist_output]).to eql('plist_output')
47
+ end
48
+
49
+ it 'returns file settings output default' do
50
+ expect(subject.defaults[:settings_output]).to eql('settings_output')
51
+ end
52
+ end
53
+
54
+ context 'when invalid' do
55
+ before do
56
+ File.write(defaults_file, 'this is not what we want')
57
+ end
58
+
59
+ specify do
60
+ expect {
61
+ subject.defaults
62
+ }.to raise_error(FlipTheSwitch::Error::InvalidFile)
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'when defaults file does NOT exist' do
68
+ it 'returns base input default' do
69
+ expect(subject.defaults[:input]).to eql(Dir.pwd)
70
+ end
71
+
72
+ it 'returns base environment default' do
73
+ expect(subject.defaults[:environment]).to eql('default')
74
+ end
75
+
76
+ it 'returns base enabled default' do
77
+ expect(subject.defaults[:enabled]).to eql('')
78
+ end
79
+
80
+ it 'returns base disabled default' do
81
+ expect(subject.defaults[:disabled]).to eql('')
82
+ end
83
+
84
+ it 'returns base category output default' do
85
+ expect(subject.defaults[:category_output]).to eql(Dir.pwd)
86
+ end
87
+
88
+ it 'returns base plist output default' do
89
+ expect(subject.defaults[:plist_output]).to eql(Dir.pwd)
90
+ end
91
+
92
+ it 'returns base settings output default' do
93
+ expect(subject.defaults[:settings_output]).to eql(Dir.pwd)
94
+ end
95
+ end
96
+ end
@@ -1,13 +1,26 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe FlipTheSwitch::Reader::Yaml do
4
- subject(:reader) { described_class.new(input) }
3
+ describe FlipTheSwitch::Reader::Features do
4
+ subject(:reader) { described_class.new(input, environment) }
5
+ let(:environment) { 'beta' }
5
6
 
6
7
  context 'when given a real file' do
7
8
  let(:input) { 'spec/resources/real' }
8
9
 
9
- it 'reads the enabled/disabled states of the features' do
10
- expect(subject.feature_states).to eql('enabled_feature' => true, 'disabled_feature' => false)
10
+ context 'when given an valid environment' do
11
+ it 'reads the enabled/disabled states of the features for the environment' do
12
+ expect(subject.feature_states).to eql('enabled_feature' => true, 'disabled_feature' => false)
13
+ end
14
+ end
15
+
16
+ context 'when given an invalid environment' do
17
+ let(:environment) { 'invalid' }
18
+
19
+ specify do
20
+ expect {
21
+ subject.feature_states
22
+ }.to raise_error(FlipTheSwitch::Error::InvalidEnvironment)
23
+ end
11
24
  end
12
25
  end
13
26
 
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>PreferenceSpecifiers</key>
6
+ <array>
7
+ <dict>
8
+ <key>Type</key>
9
+ <string>PSSliderSpecifier</string>
10
+ <key>Key</key>
11
+ <string>SomeThing</string>
12
+ <key>DefaultValue</key>
13
+ <integer>0</integer>
14
+ <key>MinimumValue</key>
15
+ <integer>0</integer>
16
+ <key>MaximumValue</key>
17
+ <integer>0</integer>
18
+ </dict>
19
+ <dict>
20
+ <key>Type</key>
21
+ <string>PSToggleSwitchSpecifier</string>
22
+ <key>Title</key>
23
+ <string>On/Off</string>
24
+ <key>Key</key>
25
+ <string>whatever</string>
26
+ <key>DefaultValue</key>
27
+ <false/>
28
+ </dict>
29
+ </array>
30
+ <key>StringsTable</key>
31
+ <string>Root</string>
32
+ </dict>
33
+ </plist>
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>PreferenceSpecifiers</key>
6
+ <array>
7
+ <dict>
8
+ <key>Title</key>
9
+ <string>Features</string>
10
+ <key>Type</key>
11
+ <string>PSGroupSpecifier</string>
12
+ </dict>
13
+ <dict>
14
+ <key>File</key>
15
+ <string>Features</string>
16
+ <key>Title</key>
17
+ <string>Features</string>
18
+ <key>Type</key>
19
+ <string>PSChildPaneSpecifier</string>
20
+ </dict>
21
+ </array>
22
+ </dict>
23
+ </plist>
@@ -0,0 +1,29 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>PreferenceSpecifiers</key>
6
+ <array>
7
+ <dict>
8
+ <key>DefaultValue</key>
9
+ <true/>
10
+ <key>Key</key>
11
+ <string>FTS_FEATURE_first_feature</string>
12
+ <key>Title</key>
13
+ <string>first_feature</string>
14
+ <key>Type</key>
15
+ <string>PSToggleSwitchSpecifier</string>
16
+ </dict>
17
+ <dict>
18
+ <key>DefaultValue</key>
19
+ <false/>
20
+ <key>Key</key>
21
+ <string>FTS_FEATURE_second_feature</string>
22
+ <key>Title</key>
23
+ <string>second_feature</string>
24
+ <key>Type</key>
25
+ <string>PSToggleSwitchSpecifier</string>
26
+ </dict>
27
+ </array>
28
+ </dict>
29
+ </plist>
@@ -0,0 +1,47 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>PreferenceSpecifiers</key>
6
+ <array>
7
+ <dict>
8
+ <key>DefaultValue</key>
9
+ <integer>0</integer>
10
+ <key>Key</key>
11
+ <string>SomeThing</string>
12
+ <key>MaximumValue</key>
13
+ <integer>0</integer>
14
+ <key>MinimumValue</key>
15
+ <integer>0</integer>
16
+ <key>Type</key>
17
+ <string>PSSliderSpecifier</string>
18
+ </dict>
19
+ <dict>
20
+ <key>DefaultValue</key>
21
+ <false/>
22
+ <key>Key</key>
23
+ <string>whatever</string>
24
+ <key>Title</key>
25
+ <string>On/Off</string>
26
+ <key>Type</key>
27
+ <string>PSToggleSwitchSpecifier</string>
28
+ </dict>
29
+ <dict>
30
+ <key>Title</key>
31
+ <string>Features</string>
32
+ <key>Type</key>
33
+ <string>PSGroupSpecifier</string>
34
+ </dict>
35
+ <dict>
36
+ <key>File</key>
37
+ <string>Features</string>
38
+ <key>Title</key>
39
+ <string>Features</string>
40
+ <key>Type</key>
41
+ <string>PSChildPaneSpecifier</string>
42
+ </dict>
43
+ </array>
44
+ <key>StringsTable</key>
45
+ <string>Root</string>
46
+ </dict>
47
+ </plist>
@@ -1,2 +1,6 @@
1
- enabled_feature: Yes
2
- disabled_feature: No
1
+ defaults: &DEFAULT
2
+ enabled_feature: Yes
3
+ disabled_feature: Yes
4
+ beta:
5
+ <<: *DEFAULT
6
+ disabled_feature: No
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flip_the_switch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael England
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-01 00:00:00.000000000 Z
12
+ date: 2014-07-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -115,13 +115,21 @@ files:
115
115
  - lib/flip_the_switch/generator/header.h.erb
116
116
  - lib/flip_the_switch/generator/implementation.m.erb
117
117
  - lib/flip_the_switch/generator/plist.rb
118
+ - lib/flip_the_switch/generator/settings.rb
118
119
  - lib/flip_the_switch/reader.rb
119
- - lib/flip_the_switch/reader/yaml.rb
120
+ - lib/flip_the_switch/reader/defaults.rb
121
+ - lib/flip_the_switch/reader/features.rb
120
122
  - spec/flip_the_switch/cli_spec.rb
121
123
  - spec/flip_the_switch/generator/category_spec.rb
122
124
  - spec/flip_the_switch/generator/plist_spec.rb
123
- - spec/flip_the_switch/reader/yaml_spec.rb
125
+ - spec/flip_the_switch/generator/settings_spec.rb
126
+ - spec/flip_the_switch/reader/defaults_spec.rb
127
+ - spec/flip_the_switch/reader/features_spec.rb
128
+ - spec/resources/ExistingSettingsRoot.plist
124
129
  - spec/resources/ExpectedFeatures.plist
130
+ - spec/resources/ExpectedSettingsBaseRoot.plist
131
+ - spec/resources/ExpectedSettingsFeatures.plist
132
+ - spec/resources/ExpectedSettingsMergeExistingRoot.plist
125
133
  - spec/resources/expected_header.h
126
134
  - spec/resources/expected_implementation.m
127
135
  - spec/resources/invalid_layout/features.yml
@@ -148,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
156
  version: '0'
149
157
  requirements: []
150
158
  rubyforge_project:
151
- rubygems_version: 2.1.11
159
+ rubygems_version: 2.0.3
152
160
  signing_key:
153
161
  specification_version: 4
154
162
  summary: A simple library to help enabling/disabling features on iOS/Mac applications.
@@ -156,8 +164,14 @@ test_files:
156
164
  - spec/flip_the_switch/cli_spec.rb
157
165
  - spec/flip_the_switch/generator/category_spec.rb
158
166
  - spec/flip_the_switch/generator/plist_spec.rb
159
- - spec/flip_the_switch/reader/yaml_spec.rb
167
+ - spec/flip_the_switch/generator/settings_spec.rb
168
+ - spec/flip_the_switch/reader/defaults_spec.rb
169
+ - spec/flip_the_switch/reader/features_spec.rb
170
+ - spec/resources/ExistingSettingsRoot.plist
160
171
  - spec/resources/ExpectedFeatures.plist
172
+ - spec/resources/ExpectedSettingsBaseRoot.plist
173
+ - spec/resources/ExpectedSettingsFeatures.plist
174
+ - spec/resources/ExpectedSettingsMergeExistingRoot.plist
161
175
  - spec/resources/expected_header.h
162
176
  - spec/resources/expected_implementation.m
163
177
  - spec/resources/invalid_layout/features.yml
@@ -1,39 +0,0 @@
1
- require 'yaml'
2
-
3
- module FlipTheSwitch
4
- module Reader
5
- class Yaml
6
- def initialize(input)
7
- @input = input
8
- end
9
-
10
- def feature_states
11
- if valid_file?
12
- file_states
13
- else
14
- raise Error::InvalidFile.new
15
- end
16
- end
17
-
18
- private
19
- attr_reader :input
20
-
21
- def valid_file?
22
- file_states.is_a?(Hash) && file_states.all? { |feature, state|
23
- feature.is_a?(String) && !!state == state
24
- }
25
- end
26
-
27
- def file_states
28
- @file_states ||= YAML.load_file(input_file)
29
- rescue SystemCallError => e
30
- raise Error::UnreadableFile.new(e)
31
- end
32
-
33
- def input_file
34
- File.join(input, 'features.yml')
35
- end
36
- end
37
- end
38
- end
39
-