fig_fig 0.2.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.
@@ -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: