fig_fig 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![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`)
|
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:
|