fig_fig 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 611cf023b0f3caff2f1efbb07477ea41a1037879
4
+ data.tar.gz: f779ec85398861b6530d1e52fdef5e6d60fd0240
5
+ SHA512:
6
+ metadata.gz: 6787d47f9d7eea144daee7652f673d991dac6a2b941a38332a68f33fa31744997da2df6f4b9095f2d2884a132645a7cd33e714c4b40829ca7524f0fb6f716d8b
7
+ data.tar.gz: 32c2d398102871747ee2549691e19b6e5e6b9ed08c182dd39cd8d4223f08760e60b59aede46fe5148d4b879d24b184857750b0baabfdff698a8a9b6d882e1cd6
@@ -0,0 +1,51 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+ .idea
@@ -0,0 +1,86 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Style/MutableConstant:
4
+ Enabled: false
5
+
6
+ Lint/UselessAssignment:
7
+ Enabled: true
8
+
9
+ Lint/UnusedMethodArgument:
10
+ Enabled: true
11
+
12
+ Lint/UnusedBlockArgument:
13
+ Enabled: true
14
+
15
+ Metrics/AbcSize:
16
+ Exclude:
17
+ - spec/**/*
18
+
19
+ Style/DotPosition:
20
+ Enabled: true
21
+ EnforcedStyle: trailing
22
+
23
+ Style/PercentLiteralDelimiters:
24
+ PreferredDelimiters:
25
+ '%': () # interpolated String
26
+ '%i': '[]' # Array of symbols
27
+ '%q': () # Single quoted string
28
+ '%Q': () # Double quoted string
29
+ '%r': '{}' # Regex
30
+ '%s': () # Symbol
31
+ '%w': '[]' # Array of strings
32
+ '%W': '[]' # Array of strings, interpolated
33
+ '%x': () # shell command
34
+
35
+ Style/StringLiterals:
36
+ EnforcedStyle: double_quotes
37
+
38
+ Style/CollectionMethods:
39
+ Enabled: true
40
+ # Mapping from undesired method to desired_method
41
+ # e.g. use `detect` over `find`:
42
+ PreferredMethods:
43
+ find: 'detect'
44
+ find_all: 'select'
45
+
46
+ Style/AlignHash:
47
+ EnforcedHashRocketStyle: table
48
+ EnforcedColonStyle: table
49
+
50
+ Style/AndOr:
51
+ # Whether `and` and `or` are banned only in conditionals (conditionals)
52
+ # or completely (always).
53
+ EnforcedStyle: conditionals # and/or are sometimes used for flow control.
54
+
55
+ Style/BlockDelimiters:
56
+ EnforcedStyle: semantic
57
+
58
+ Style/HashSyntax:
59
+ EnforcedStyle: ruby19_no_mixed_keys
60
+
61
+ Style/NumericLiterals:
62
+ MinDigits: 5
63
+
64
+ Style/SpaceBeforeFirstArg:
65
+ Enabled: false
66
+
67
+ # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
68
+ Style/TrailingCommaInLiteral:
69
+ EnforcedStyleForMultiline: comma
70
+
71
+ Style/SingleLineBlockParams:
72
+ Enabled: false
73
+
74
+ Style/EachWithObject:
75
+ Enabled: false
76
+
77
+ Style/Lambda:
78
+ Enabled: false
79
+
80
+ Metrics/LineLength:
81
+ Exclude:
82
+ - spec/mocks.rb
83
+
84
+ Metrics/MethodLength:
85
+ Exclude:
86
+ - spec/mocks.rb
@@ -0,0 +1,25 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-08-10 23:33:29 +0000 using RuboCop version 0.41.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 25
10
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
11
+ # URISchemes: http, https
12
+ Metrics/LineLength:
13
+ Max: 112
14
+
15
+ # Offense count: 1
16
+ # Configuration parameters: CountComments.
17
+ Metrics/MethodLength:
18
+ Max: 13
19
+
20
+ # Offense count: 2
21
+ Style/Documentation:
22
+ Exclude:
23
+ - 'spec/**/*'
24
+ - 'test/**/*'
25
+ - 'lib/app_config.rb'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Referly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,174 @@
1
+ # FigFig
2
+ Ruby Application Configurator with some validation support
3
+
4
+ [![CircleCI](https://circleci.com/gh/Referly/fig_fig.svg?style=svg)](https://circleci.com/gh/Referly/fig_fig)
5
+
6
+ ## Usage
7
+
8
+ Follow this example to perform basic configuration and validation of
9
+ parameter presence.
10
+
11
+ ```ruby
12
+ require "fig_fig"
13
+ FigFig.configure do |c|
14
+ c.parameter :foo
15
+ c.parameter :bar, required: true
16
+ end
17
+ FigFig.foo = "foodog"
18
+ FigFig.ready
19
+ #=> raises FigFig::MissingConfigurationError
20
+ FigFig.bar = "yum beer"
21
+ FigFig.ready
22
+ # => nil
23
+ ```
24
+
25
+ If you call `.configure` a second time it raises a ConfigurationAlreadyDefinedError.
26
+ To reset the configuration definition so that it can be redefined pass the `reset: true` option
27
+ to `.configure`. If you do supply the `reset: true` option, all existing parameter definitions
28
+ and any set values will be lost. This true regardless of the current lifecycle stage of the config.
29
+
30
+ Despite the restriction against calling `.configure` more than once, callbacks you register in the
31
+ block passed to `.configure` can modify any parameter value (unless the parameter is locked),
32
+ read any parameter value, and even register additional parameters (advanced).
33
+
34
+ ```ruby
35
+ require "fig_fig"
36
+ FigFig.configure do |c|
37
+ c.parameter :foo
38
+ end
39
+ FigFig.foo = "foo"
40
+ FigFig.ready
41
+ FigFig.foo
42
+ # => "foo"
43
+ FigFig.foo = bar
44
+ FigFig.foo
45
+ # => "bar"
46
+ FigFig.configure do |c|
47
+ c.parameter :baz
48
+ end
49
+ # => raises ConfigurationAlreadyDefinedError
50
+ FigFig.configure(reset: true) do |c|
51
+ c.parameter :baz
52
+ end
53
+ FigFig.foo = "foo"
54
+ # => raises NoMethodError
55
+ ```
56
+
57
+ You can immediately read values for parameters inside of the block passed to
58
+ `.configure`. However, you need to call `.ready` when you are done with your
59
+ configuration process and before your application can read values for
60
+ parameters from the configuration.
61
+
62
+ ```ruby
63
+ require "fig_fig"
64
+ FigFig.configure do |c|
65
+ c.parameter :foo
66
+ c.parameter = "foo"
67
+ puts c.parameter
68
+ # => "foo"
69
+ end
70
+ c.parameter
71
+ # => raises NoMethodError
72
+ c.ready
73
+ c.parameter
74
+ # => "foo"
75
+ ```
76
+
77
+ You can check validity of the configuration without invoking `.ready` by
78
+ calling `.valid?`
79
+
80
+
81
+ You can specify blocks that should be executed after validation is successful,
82
+ this is useful for late binding configuration parameters managed by FigFig
83
+ to other gems that use the conventional .configure approach.
84
+
85
+ (Note that after_validation callbacks are invoked in the order they are set.)
86
+
87
+ ```ruby
88
+ require "fig_fig"
89
+ FigFig.configure do |c|
90
+ c.parameter :is_a_poodle
91
+ c.parameter :doggyz, required: true
92
+ c.after_validation do |validated_fig_fig|
93
+ validated_fig_fig.is_a_poodle = validated_fig_fig.doggyz == "poodle"
94
+ end
95
+ end
96
+ FigFig.doggyz = "collie"
97
+ FigFig.ready
98
+ FigFig.is_a_poodle
99
+ # => false
100
+ FigFig.doggyz = "poodle"
101
+ FigFig.ready
102
+ FigFig.is_a_poodle
103
+ # => true
104
+ ```
105
+
106
+ You can control the mutability level of the parameters you define. The gem syntax
107
+ exposes this functionality using the `:lock` option. The possible values are:
108
+
109
+ - nil (the default) - the parameter can be changed at any point
110
+ - :on_set - the parameter once set cannot be subsequently modified
111
+ - :on_validation - the parameter is locked during the validation lifecycle event and
112
+ before the after_validation callbacks are invoked
113
+ - :on_ready - the parameter is locked during the `ready` lifecycle event, the intended
114
+ use case is when you want to prevent the application from modifying the configuration
115
+ outside of the configuration bootstrapping stage of your application
116
+
117
+ ```ruby
118
+ require "fig_fig"
119
+ FigFig.configure do |c|
120
+ c.parameter :foo
121
+ c.parameter :bar, lock: :on_set
122
+ c.parameter :doz, lock: :on_validation
123
+ c.parameter :diz, lock: :on_validation
124
+ c.parameter :eck, lock: :on_ready
125
+ c.parameter :exz, lock: :on_ready
126
+ end
127
+ c.foo = "foo"
128
+ c.bar = "bar"
129
+ c.doz = "doz"
130
+ c.eck = "eck"
131
+ c.foo = "foo2"
132
+ c.bar = "bar2"
133
+ # => raises FigFig::CannotModifyLockedParameterError
134
+ c.doz = "doz2"
135
+ c.eck = "eck2"
136
+ FigFig.valid?
137
+ c.foo = "foo3"
138
+ c.bar = "bar3"
139
+ # => raises FigFig::CannotModifyLockedParameterError
140
+ c.doz = "doz3"
141
+ # => raises FigFig::CannotModifyLockedParameterError
142
+ c.diz = "diz"
143
+ # => raises FigFig::CannotModifyLockedParameterError
144
+ c.diz
145
+ # => nil
146
+ c.eck = "eck3"
147
+ FigFig.ready
148
+ c.foo = "foo4"
149
+ c.bar = "bar4"
150
+ # => raises FigFig::CannotModifyLockedParameterError
151
+ c.doz = "doz4"
152
+ # => raises FigFig::CannotModifyLockedParameterError
153
+ c.diz = "diz2"
154
+ # => raises FigFig::CannotModifyLockedParameterError
155
+ c.diz
156
+ # => nil
157
+ c.eck = "eck4"
158
+ # => raises FigFig::CannotModifyLockedParameterError
159
+ c.exz = "exz"
160
+ # => raises FigFig::CannotModifyLockedParameterError
161
+ c.exz
162
+ # => nil
163
+ ```
164
+
165
+ ## Lifecycle events
166
+
167
+ - `validation` - when the valid? method is invoked (you can register callbacks to `#after_validation`)
168
+
169
+ - `final_validation` - you cannot interact with this internal only lifecycle event which performs a second
170
+ validation pass after the after_validation callbacks have been run to make sure the configuration is still valid.
171
+
172
+ - `ready` - when the FigFig instance has completed its bootstrapping process and is ready to be
173
+ used by your application. Late immutable parameters are locked at this point and can no longer be changed
174
+ via setters. (currently you cannot register callbacks to `before_ready` or `after_ready`)
@@ -0,0 +1,15 @@
1
+ machine:
2
+
3
+ timezone:
4
+ America/Los_Angeles # Set the timezone
5
+
6
+ # Version of ruby to use
7
+ ruby:
8
+ version:
9
+ 2.1.5
10
+
11
+ test:
12
+ override:
13
+ - bundle exec rubocop
14
+ - mkdir -p $CIRCLE_TEST_REPORTS/rspec
15
+ - bundle exec rspec --format RspecJunitFormatter --out $CIRCLE_TEST_REPORTS/rspec/rspec.xml spec --format progress
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
3
+ require "fig_fig/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fig_fig"
7
+ s.version = FigFig::VERSION
8
+ s.date = "2016-08-09"
9
+ s.summary = "Ruby Application Configurator"
10
+ s.description = "Configurator for Ruby Applications with some validation and sugars."
11
+ s.authors = ["Courtland Caldwell", "Blake Luchessi"]
12
+ s.email = "engineering@mattermark.com"
13
+ s.files = `git ls-files`.split("\n") - %w[Gemfile Gemfile.lock]
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.license = "MIT"
16
+ s.homepage =
17
+ "https://github.com/Referly/fig_fig"
18
+ s.add_development_dependency "rspec", "~> 3.2"
19
+ s.add_development_dependency "rb-readline", "~> 0.5", ">= 0.5.3"
20
+ s.add_development_dependency "byebug", "~> 3.5"
21
+ s.add_development_dependency "simplecov", "~> 0.10"
22
+ s.add_development_dependency "rubocop", "~> 0.31"
23
+ s.add_development_dependency "rspec_junit_formatter", "~> 0.2"
24
+ end
@@ -0,0 +1,165 @@
1
+ # Top level class of the FigTree gem
2
+ class FigFig
3
+ MissingConfigurationError = Class.new StandardError
4
+ DuplicateParameterDefinitionError = Class.new StandardError
5
+ CannotModifyLockedParameterError = Class.new StandardError
6
+ InvalidLockOptionError = Class.new StandardError
7
+ ConfigurationAlreadyDefinedError = Class.new StandardError
8
+
9
+ class << self
10
+ attr_accessor :configuration
11
+
12
+ # And we define a wrapper for the configuration block, that we'll use to set up
13
+ # our set of options
14
+ def configure(reset: nil)
15
+ self.reset if reset
16
+ raise ConfigurationAlreadyDefinedError if @configuration
17
+ @configuration = ConfigurationContainer.new
18
+ @configuration.configuring = true
19
+ yield configuration if block_given?
20
+ @configuration.configuring = false
21
+ end
22
+
23
+ def configuration
24
+ @configuration ||= ConfigurationContainer.new
25
+ end
26
+
27
+ def reset
28
+ @configuration = nil
29
+ end
30
+
31
+ def method_missing(method_name, *args, &blk)
32
+ return super unless configuration.respond_to? method_name
33
+ configuration.send method_name, *args, &blk
34
+ end
35
+
36
+ def respond_to_missing?(method, _include_private = false)
37
+ configuration.respond_to?(method) || super
38
+ end
39
+ end
40
+
41
+ # The class that encapsulates the current configuration definition and parameter values
42
+ class ConfigurationContainer
43
+ attr_accessor :parameters,
44
+ :after_validation_callbacks,
45
+ :configuring,
46
+ :validating,
47
+ :readied, # set to true during the ready lifecycle event
48
+ :validated # set to true during the validation lifecycle event
49
+
50
+ def initialize
51
+ @configuring = false
52
+ @validating = false
53
+ end
54
+
55
+ def parameter(name, options = {})
56
+ @parameters ||= []
57
+ raise DuplicateParameterDefinitionError if parameters.any? { |p| p.keys.first == name }
58
+ parameters << { name: name.to_s, options: options, value: nil, set: false }
59
+ end
60
+
61
+ def valid?
62
+ @validating = true
63
+ _missing_configuration if _invalid_parameters.any?
64
+ # Set validated to true to block changes to parameters with on_validation locks
65
+ # including those in after_validation callbacks
66
+ @validated = true
67
+ Array(@after_validation_callbacks).each do |callback|
68
+ callback.call self
69
+ end
70
+ # Below line is the final_validation lifecycle event
71
+ _missing_configuration if _invalid_parameters.any?
72
+ @validating = false
73
+ @validated
74
+ end
75
+
76
+ def validated
77
+ @validated ||= false
78
+ end
79
+
80
+ def ready
81
+ valid?
82
+ @readied = true
83
+ end
84
+
85
+ def readied
86
+ @readied ||= false
87
+ end
88
+
89
+ def after_validation(&blk)
90
+ @after_validation_callbacks ||= []
91
+ @after_validation_callbacks << blk
92
+ end
93
+
94
+ def method_missing(method_name, *args, &blk)
95
+ method_name_str = method_name.to_s
96
+ return super unless _dynamically_exposed_methods.include? method_name_str
97
+ if _dynamically_exposed_readers.include? method_name_str
98
+ _ghost_reader method_name_str, *args, &blk
99
+ elsif _dynamically_exposed_writers.include? method_name_str
100
+ _ghost_writer method_name_str, *args, &blk
101
+ end
102
+ end
103
+
104
+ def respond_to_missing?(method_name, _include_private = false)
105
+ _dynamically_exposed_methods.include?(method_name.to_s) || super
106
+ end
107
+
108
+ def locked?(parameter)
109
+ lock_option = parameter[:options].fetch(:lock, nil)
110
+ case lock_option
111
+ when nil
112
+ false
113
+ when :on_set
114
+ parameter[:set]
115
+ when :on_validation
116
+ validated
117
+ when :on_ready
118
+ readied
119
+ else
120
+ raise InvalidLockOptionError
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def _ghost_reader(method_name_str, *_args)
127
+ parameters.detect { |p| p[:name] == method_name_str }[:value]
128
+ end
129
+
130
+ def _ghost_writer(method_name_str, *args)
131
+ parameter = parameters.detect { |p| "#{p[:name]}=" == method_name_str }
132
+ raise CannotModifyLockedParameterError if locked? parameter
133
+ parameter[:value] = args.first
134
+ parameter[:set] = true
135
+ parameter[:value]
136
+ end
137
+
138
+ def _missing_configuration
139
+ raise MissingConfigurationError,
140
+ "All required configurations have not been set. Missing configurations: #{_invalid_parameter_names}"
141
+ end
142
+
143
+ def _dynamically_exposed_methods
144
+ _dynamically_exposed_readers | _dynamically_exposed_writers
145
+ end
146
+
147
+ def _dynamically_exposed_readers
148
+ (readied || configuring || validating) ? parameters.map { |p| p[:name] } : []
149
+ end
150
+
151
+ def _dynamically_exposed_writers
152
+ parameters.map { |p| "#{p[:name]}=" }
153
+ end
154
+
155
+ def _invalid_parameters
156
+ parameters.
157
+ select { |p| p[:options].fetch(:required, false) }.
158
+ select { |p| send(p[:name]).nil? }
159
+ end
160
+
161
+ def _invalid_parameter_names
162
+ _invalid_parameters.map { |p| p[:name] }.join(",")
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ class FigFig
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe FigFig::ConfigurationContainer do
4
+ describe "registering a new parameter" do
5
+ it "registers a setter" do
6
+ subject.parameter :foo
7
+
8
+ expect { subject.foo = "foo" }.not_to raise_error
9
+ end
10
+ end
11
+
12
+ describe "validated" do
13
+ it "defaults to false" do
14
+ expect(subject.validated).to be false
15
+ end
16
+ end
17
+
18
+ describe "private methods" do
19
+ describe "#_invalid_parameters" do
20
+ context "during validation" do
21
+ it "is the Array of parameters that are invalid" do
22
+ subject.parameter :foo
23
+ subject.parameter :bar, required: true
24
+ subject.foo = "foo"
25
+ subject.validating = true
26
+ expect(subject.send(:_invalid_parameters)).
27
+ to eq [{ name: "bar", options: { required: true }, value: nil, set: false }]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,263 @@
1
+ require "spec_helper"
2
+
3
+ describe FigFig do
4
+ before(:each) do
5
+ described_class.reset
6
+ end
7
+ describe "configuring AppConfig" do
8
+ describe ".configure" do
9
+ it "yields AppConfig to the given block" do
10
+ expect { |b| described_class.configure(&b) }.to yield_with_args FigFig::ConfigurationContainer
11
+ end
12
+
13
+ it "is where you should specify the configuration parameters" do
14
+ described_class.configure do |c|
15
+ c.parameter :fooz
16
+ c.parameter :doggyz, required: true
17
+ end
18
+
19
+ described_class.fooz = "yo fooz"
20
+ expect { described_class.ready }.to raise_error FigFig::MissingConfigurationError
21
+ described_class.doggyz = "pups"
22
+ expect { described_class.ready }.not_to raise_error
23
+
24
+ described_class.ready
25
+
26
+ expect(described_class.fooz).to eq "yo fooz"
27
+ expect(described_class.doggyz).to eq "pups"
28
+ end
29
+
30
+ it "is where you should specify after_validation callbacks" do
31
+ expect { |b|
32
+ described_class.configure do |c|
33
+ c.parameter :doggyz, required: true
34
+ c.after_validation(&b)
35
+ end
36
+ described_class.doggyz = "poodle"
37
+ described_class.ready
38
+ }.to yield_with_args FigFig::ConfigurationContainer
39
+ end
40
+
41
+ it "is possible to perform actions based on the latest state of the AppConfig" do
42
+ described_class.configure do |c|
43
+ c.parameter :is_a_poodle
44
+ c.parameter :doggyz, required: true
45
+ c.after_validation do |validated_app_config|
46
+ validated_app_config.is_a_poodle = validated_app_config.doggyz == "poodle"
47
+ end
48
+ end
49
+
50
+ described_class.doggyz = "collie"
51
+ described_class.ready
52
+
53
+ expect(described_class.is_a_poodle).to eq false
54
+
55
+ described_class.doggyz = "poodle"
56
+ described_class.ready
57
+ expect(described_class.is_a_poodle).to eq true
58
+ end
59
+
60
+ context "when .configure is called more than once" do
61
+ it "raises a ConfigurationAlreadyDefinedError" do
62
+ described_class.configure do |c|
63
+ c.parameter :cats
64
+ end
65
+
66
+ expect { described_class.configure }.
67
+ to raise_error FigFig::ConfigurationAlreadyDefinedError
68
+ end
69
+
70
+ context "when the reset: true option is supplied" do
71
+ it "resets the configuration" do
72
+ described_class.configure do |c|
73
+ c.parameter :cats
74
+ expect(c).to respond_to :cats, :cats=
75
+ end
76
+
77
+ described_class.configure(reset: true) do |c|
78
+ c.parameter :mice
79
+ expect(c).to respond_to :mice, :mice=
80
+ expect(c).not_to respond_to :cats, :cats=
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "marking a configuration parameter as required" do
87
+ it "accepts a :required option" do
88
+ described_class.configure do |c|
89
+ c.parameter :foo, required: true
90
+ end
91
+ end
92
+
93
+ it "requires the parameter for the AppConfig to be valid" do
94
+ described_class.configure do |c|
95
+ c.parameter :foo, required: true
96
+ end
97
+
98
+ expect { described_class.ready }.to raise_error FigFig::MissingConfigurationError
99
+
100
+ described_class.foo = :bar
101
+
102
+ expect { described_class.ready }.not_to raise_error
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ describe ".valid?" do
109
+ context "when a required parameter becomes nil during the invocation of an after_validation callback" do
110
+ it "raises AppConfig::MissingConfigurationError" do
111
+ described_class.configure do |c|
112
+ c.parameter :foo, required: true
113
+ c.after_validation do |config_after_validation|
114
+ config_after_validation.foo = nil
115
+ end
116
+ end
117
+ described_class.foo = "foo"
118
+ expect { described_class.valid? }.to raise_error FigFig::MissingConfigurationError
119
+ end
120
+ end
121
+ end
122
+
123
+ describe ".ready" do
124
+ it "triggers validation" do
125
+ described_class.configure do |c|
126
+ c.parameter :foo
127
+ end
128
+ expect(described_class.configuration).to receive :valid?
129
+ described_class.ready
130
+ end
131
+ end
132
+
133
+ describe "parameter locking" do
134
+ it "defaults to no lock" do
135
+ described_class.configure do |c|
136
+ c.parameter :foo
137
+ end
138
+ described_class.foo = "foo"
139
+ expect { described_class.foo = "foo" }.not_to raise_error
140
+ described_class.valid?
141
+ expect { described_class.foo = "foo" }.not_to raise_error
142
+ described_class.ready
143
+ expect { described_class.foo = "foo" }.not_to raise_error
144
+ end
145
+
146
+ context "when the :on_set lock is used" do
147
+ it "prevents mutation of the parameter once it has been set" do
148
+ described_class.configure do |c|
149
+ c.parameter :foo, lock: :on_set
150
+ end
151
+ described_class.foo = "foo"
152
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
153
+ described_class.valid?
154
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
155
+ described_class.ready
156
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
157
+ end
158
+ end
159
+
160
+ context "when the :on_validation lock is used" do
161
+ it "prevents mutation of the parameter after validation" do
162
+ described_class.configure do |c|
163
+ c.parameter :foo, lock: :on_validation
164
+ end
165
+ described_class.foo = "foo"
166
+ expect { described_class.foo = "foo" }.not_to raise_error
167
+ described_class.valid?
168
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
169
+ described_class.ready
170
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
171
+ end
172
+
173
+ it "prevents mutation of the parameter in after_validation callbacks" do
174
+ described_class.configure do |c|
175
+ c.parameter :foo, lock: :on_validation
176
+ c.after_validation do |config_after_validation|
177
+ expect { config_after_validation.foo = "foo" }.
178
+ to raise_error FigFig::CannotModifyLockedParameterError
179
+ end
180
+ end
181
+ described_class.foo = "foo"
182
+ expect { described_class.foo = "foo" }.not_to raise_error
183
+ described_class.valid?
184
+ end
185
+ end
186
+
187
+ context "when the :on_ready lock is used" do
188
+ it "prevents mutation of the parameter after the AppConfig is readied" do
189
+ described_class.configure do |c|
190
+ c.parameter :foo, lock: :on_ready
191
+ end
192
+ described_class.foo = "foo"
193
+ expect { described_class.foo = "foo" }.not_to raise_error
194
+ described_class.valid?
195
+ expect { described_class.foo = "foo" }.not_to raise_error
196
+ described_class.ready
197
+ expect { described_class.foo = "foo" }.to raise_error FigFig::CannotModifyLockedParameterError
198
+ end
199
+ end
200
+
201
+ context "when the specified lock option is unexpected" do
202
+ it "raises an InvalidLockOptionError" do
203
+ described_class.configure do |c|
204
+ c.parameter :foo, lock: :blahblah
205
+ end
206
+ expect { described_class.foo = "foo" }.to raise_error FigFig::InvalidLockOptionError
207
+ end
208
+ end
209
+ end
210
+
211
+ describe "lifecycle events" do
212
+ describe "ready" do
213
+ context "before the ready lifecycle event" do
214
+ it "prohibits reader access outside of the .configure block's scope" do
215
+ described_class.configure do |c|
216
+ c.parameter :foo
217
+ end
218
+ described_class.foo = "foo"
219
+ expect { described_class.foo }.to raise_error NoMethodError
220
+ expect(described_class).not_to respond_to :foo
221
+ described_class.ready
222
+ expect(described_class.foo).to eq "foo"
223
+ expect(described_class).to respond_to :foo
224
+ end
225
+
226
+ it "permits reader access outside of the .configure block's scope during validation" do
227
+ described_class.configure do |c|
228
+ c.parameter :foo
229
+ end
230
+ described_class.foo = "foo"
231
+
232
+ described_class.configuration.validating = true
233
+ expect(described_class.foo).to eq "foo"
234
+ expect(described_class).to respond_to :foo
235
+ end
236
+ end
237
+ end
238
+ end
239
+ describe "callbacks" do
240
+ context "when validation is successful" do
241
+ it "invokes the registered callbacks in the order they were registered" do
242
+ described_class.configure do |c|
243
+ c.parameter :is_a_poodle
244
+ c.parameter :doggyz, required: true
245
+ c.after_validation do |validated_app_config|
246
+ validated_app_config.is_a_poodle = validated_app_config.doggyz == "poodle"
247
+ end
248
+ c.after_validation do |validated_app_config|
249
+ validated_app_config.doggyz = "buy a new dog" if validated_app_config.is_a_poodle
250
+ end
251
+ end
252
+
253
+ described_class.doggyz = "poodle"
254
+ described_class.is_a_poodle = false
255
+
256
+ described_class.ready
257
+
258
+ expect(described_class.is_a_poodle).to be true
259
+ expect(described_class.doggyz).to eq "buy a new dog"
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,4 @@
1
+ require "simplecov"
2
+ SimpleCov.profiles.define "fig_fig" do
3
+ add_filter "/spec"
4
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "simplecov_custom_profile"
2
+ SimpleCov.start "fig_fig"
3
+ require "fig_fig"
4
+ require "byebug"
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fig_fig
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Courtland Caldwell
8
+ - Blake Luchessi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '3.2'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '3.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rb-readline
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '0.5'
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.5.3
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: '0.5'
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.5.3
48
+ - !ruby/object:Gem::Dependency
49
+ name: byebug
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.5'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.5'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.10'
76
+ - !ruby/object:Gem::Dependency
77
+ name: rubocop
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.31'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.31'
90
+ - !ruby/object:Gem::Dependency
91
+ name: rspec_junit_formatter
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '0.2'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '0.2'
104
+ description: Configurator for Ruby Applications with some validation and sugars.
105
+ email: engineering@mattermark.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - .gitignore
111
+ - .rubocop.yml
112
+ - .rubocop_todo.yml
113
+ - LICENSE
114
+ - README.md
115
+ - circle.yml
116
+ - fig_fig.gemspec
117
+ - lib/fig_fig.rb
118
+ - lib/fig_fig/version.rb
119
+ - spec/app_config/configuration_container_spec.rb
120
+ - spec/app_config_spec.rb
121
+ - spec/simplecov_custom_profile.rb
122
+ - spec/spec_helper.rb
123
+ homepage: https://github.com/Referly/fig_fig
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.4.2
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Ruby Application Configurator
147
+ test_files:
148
+ - spec/app_config/configuration_container_spec.rb
149
+ - spec/app_config_spec.rb
150
+ - spec/simplecov_custom_profile.rb
151
+ - spec/spec_helper.rb
152
+ has_rdoc: