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.
- checksums.yaml +7 -0
- data/.gitignore +51 -0
- data/.rubocop.yml +86 -0
- data/.rubocop_todo.yml +25 -0
- data/LICENSE +21 -0
- data/README.md +174 -0
- data/circle.yml +15 -0
- data/fig_fig.gemspec +24 -0
- data/lib/fig_fig.rb +165 -0
- data/lib/fig_fig/version.rb +3 -0
- data/spec/app_config/configuration_container_spec.rb +32 -0
- data/spec/app_config_spec.rb +263 -0
- data/spec/simplecov_custom_profile.rb +4 -0
- data/spec/spec_helper.rb +4 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
@@ -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
|
data/.rubocop.yml
ADDED
@@ -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
|
data/.rubocop_todo.yml
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
# FigFig
|
2
|
+
Ruby Application Configurator with some validation support
|
3
|
+
|
4
|
+
[](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`)
|
data/circle.yml
ADDED
@@ -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
|
data/fig_fig.gemspec
ADDED
@@ -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
|
data/lib/fig_fig.rb
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
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:
|