seielit-figaro 1.1.2
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 +6 -0
- data/.rspec +3 -0
- data/.travis.yml +30 -0
- data/CHANGELOG.md +122 -0
- data/CONTRIBUTING.md +48 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +350 -0
- data/Rakefile +6 -0
- data/bin/figaro +5 -0
- data/gemfiles/rails41.gemfile +12 -0
- data/gemfiles/rails42.gemfile +12 -0
- data/gemfiles/rails50.gemfile +13 -0
- data/gemfiles/rails51.gemfile +13 -0
- data/gemfiles/rails52.gemfile +14 -0
- data/gemfiles/rails60.gemfile +14 -0
- data/lib/figaro.rb +33 -0
- data/lib/figaro/application.rb +91 -0
- data/lib/figaro/cli.rb +42 -0
- data/lib/figaro/cli/heroku_set.rb +33 -0
- data/lib/figaro/cli/install.rb +32 -0
- data/lib/figaro/cli/install/application.yml +11 -0
- data/lib/figaro/cli/task.rb +33 -0
- data/lib/figaro/env.rb +45 -0
- data/lib/figaro/error.rb +17 -0
- data/lib/figaro/rails.rb +7 -0
- data/lib/figaro/rails/application.rb +21 -0
- data/lib/figaro/rails/railtie.rb +10 -0
- data/lib/figaro/rails/tasks.rake +6 -0
- data/lib/figaro/settings.rb +124 -0
- data/seielit-figaro.gemspec +23 -0
- data/spec/figaro/application_spec.rb +262 -0
- data/spec/figaro/cli/heroku_set_spec.rb +67 -0
- data/spec/figaro/cli/install_spec.rb +49 -0
- data/spec/figaro/env_spec.rb +195 -0
- data/spec/figaro/rails/application_spec.rb +47 -0
- data/spec/figaro/settings_spec.rb +109 -0
- data/spec/figaro_spec.rb +106 -0
- data/spec/rails_spec.rb +50 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/aruba.rb +19 -0
- data/spec/support/bin/heroku +5 -0
- data/spec/support/command_helpers.rb +17 -0
- data/spec/support/command_interceptor.rb +33 -0
- data/spec/support/reset.rb +13 -0
- metadata +145 -0
data/lib/figaro/rails.rb
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# at require-time, we need Rails to be defined to initialize the railtie
|
|
2
|
+
# and set the default adapter to the Rails::Application adapter
|
|
3
|
+
if defined?(Rails)
|
|
4
|
+
require "figaro/rails/application"
|
|
5
|
+
require "figaro/rails/railtie"
|
|
6
|
+
Figaro.adapter = Figaro::Rails::Application
|
|
7
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Figaro
|
|
2
|
+
module Rails
|
|
3
|
+
class Application < Figaro::Application
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def default_path
|
|
7
|
+
rails_not_initialized! unless ::Rails.root
|
|
8
|
+
|
|
9
|
+
::Rails.root.join("config", "application.yml")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def default_environment
|
|
13
|
+
::Rails.env
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def rails_not_initialized!
|
|
17
|
+
raise RailsNotInitialized
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bigdecimal'
|
|
4
|
+
|
|
5
|
+
module Figaro
|
|
6
|
+
#
|
|
7
|
+
# This class should be extended in your own application to provide a higher
|
|
8
|
+
# level API for use in your application.
|
|
9
|
+
#
|
|
10
|
+
# Your app shouldn't use ENV, Figaro.env not Settings#[]. It also shouldn't
|
|
11
|
+
# fiddle with data conversion in settings.
|
|
12
|
+
#
|
|
13
|
+
# class Settings < Figaro::Settings
|
|
14
|
+
# requires :per_page # makes it fail on startup
|
|
15
|
+
# requires :port,
|
|
16
|
+
# :int
|
|
17
|
+
#
|
|
18
|
+
# def per_page
|
|
19
|
+
# self[:per_page].int
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
class Settings
|
|
24
|
+
attr_reader :namespace
|
|
25
|
+
|
|
26
|
+
NAMESPACE_SEPARATOR = '__'.freeze
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
def [](key)
|
|
30
|
+
new(key)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def requires(key, type = nil)
|
|
34
|
+
setting = self[key]
|
|
35
|
+
if type
|
|
36
|
+
setting.as type,
|
|
37
|
+
raises: true
|
|
38
|
+
else
|
|
39
|
+
setting.value || raise(MissingKey, key)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
extend ClassMethods
|
|
44
|
+
|
|
45
|
+
def initialize(namespace)
|
|
46
|
+
@namespace = [namespace]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def [](key)
|
|
50
|
+
self.class.new(namespace + [key])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def key
|
|
54
|
+
ns = namespace || []
|
|
55
|
+
ns.join NAMESPACE_SEPARATOR
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def value
|
|
59
|
+
env.send key
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_str
|
|
63
|
+
value
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def to_s
|
|
67
|
+
value
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def inspect
|
|
71
|
+
"Setting: #{key} => #{value.inspect}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
module DataTypes
|
|
75
|
+
FALSY_VALUES = %w[no off false disabled].freeze
|
|
76
|
+
|
|
77
|
+
NULLABLE_TYPES = {
|
|
78
|
+
int: method(:Integer),
|
|
79
|
+
float: method(:Float),
|
|
80
|
+
decimal: method(:BigDecimal),
|
|
81
|
+
string: ->(itself) { itself || raise(ArgumentError) }
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
84
|
+
class InvalidKey < Error
|
|
85
|
+
def initialize(setting, type)
|
|
86
|
+
super("Invalid configuration key: can't convert #{setting.inspect} to #{type}")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def as(type, raises: false)
|
|
91
|
+
case type
|
|
92
|
+
when :bool
|
|
93
|
+
bool
|
|
94
|
+
else
|
|
95
|
+
conversion = NULLABLE_TYPES[type]
|
|
96
|
+
conversion.call value
|
|
97
|
+
end
|
|
98
|
+
rescue ArgumentError, TypeError
|
|
99
|
+
raise InvalidKey.new(self, type) if raises
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def bool
|
|
103
|
+
!FALSY_VALUES.include? value&.downcase
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
NULLABLE_TYPES.each do |method, _conversion|
|
|
107
|
+
define_method method do
|
|
108
|
+
as method
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
define_method "#{method}!" do
|
|
112
|
+
as method,
|
|
113
|
+
raises: true
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
include DataTypes
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
def env
|
|
121
|
+
Figaro.env
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |gem|
|
|
4
|
+
gem.name = "seielit-figaro"
|
|
5
|
+
gem.version = "1.1.2"
|
|
6
|
+
|
|
7
|
+
gem.author = "Steve Richert"
|
|
8
|
+
gem.email = "steve.richert@gmail.com"
|
|
9
|
+
gem.summary = "Simple Rails app configuration"
|
|
10
|
+
gem.description = "Simple, Heroku-friendly Rails app configuration using ENV and a single YAML file (fork of https://github.com/laserlemon/figaro)"
|
|
11
|
+
gem.homepage = "https://github.com/seielit/figaro"
|
|
12
|
+
gem.license = "MIT"
|
|
13
|
+
|
|
14
|
+
gem.add_dependency "thor", "~> 0.14"
|
|
15
|
+
|
|
16
|
+
gem.add_development_dependency "bundler"
|
|
17
|
+
gem.add_development_dependency "rake", "~> 10.4"
|
|
18
|
+
|
|
19
|
+
gem.files = `git ls-files`.split($\)
|
|
20
|
+
gem.test_files = gem.files.grep(/^spec/)
|
|
21
|
+
|
|
22
|
+
gem.executables << "figaro"
|
|
23
|
+
end
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
require "tempfile"
|
|
2
|
+
|
|
3
|
+
module Figaro
|
|
4
|
+
describe Application do
|
|
5
|
+
before do
|
|
6
|
+
allow_any_instance_of(Application).to receive(:default_path) { "/path/to/app/config/application.yml" }
|
|
7
|
+
allow_any_instance_of(Application).to receive(:default_environment) { "development" }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "#path" do
|
|
11
|
+
it "uses the default" do
|
|
12
|
+
application = Application.new
|
|
13
|
+
|
|
14
|
+
expect(application.path).to eq("/path/to/app/config/application.yml")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "is configurable via initialization" do
|
|
18
|
+
application = Application.new(path: "/app/env.yml")
|
|
19
|
+
|
|
20
|
+
expect(application.path).to eq("/app/env.yml")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "is configurable via setter" do
|
|
24
|
+
application = Application.new
|
|
25
|
+
application.path = "/app/env.yml"
|
|
26
|
+
|
|
27
|
+
expect(application.path).to eq("/app/env.yml")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "casts to string" do
|
|
31
|
+
application = Application.new(path: Pathname.new("/app/env.yml"))
|
|
32
|
+
|
|
33
|
+
expect(application.path).to eq("/app/env.yml")
|
|
34
|
+
expect(application.environment).not_to be_a(Pathname)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "follows a changing default" do
|
|
38
|
+
application = Application.new
|
|
39
|
+
|
|
40
|
+
expect {
|
|
41
|
+
allow(application).to receive(:default_path) { "/app/env.yml" }
|
|
42
|
+
}.to change {
|
|
43
|
+
application.path
|
|
44
|
+
}.from("/path/to/app/config/application.yml").to("/app/env.yml")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "#environment" do
|
|
49
|
+
it "uses the default" do
|
|
50
|
+
application = Application.new
|
|
51
|
+
|
|
52
|
+
expect(application.environment).to eq("development")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "is configurable via initialization" do
|
|
56
|
+
application = Application.new(environment: "test")
|
|
57
|
+
|
|
58
|
+
expect(application.environment).to eq("test")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "is configurable via setter" do
|
|
62
|
+
application = Application.new
|
|
63
|
+
application.environment = "test"
|
|
64
|
+
|
|
65
|
+
expect(application.environment).to eq("test")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "casts to string" do
|
|
69
|
+
application = Application.new(environment: :test)
|
|
70
|
+
|
|
71
|
+
expect(application.environment).to eq("test")
|
|
72
|
+
expect(application.environment).not_to be_a(Symbol)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "respects nil" do
|
|
76
|
+
application = Application.new(environment: nil)
|
|
77
|
+
|
|
78
|
+
expect(application.environment).to eq(nil)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "follows a changing default" do
|
|
82
|
+
application = Application.new
|
|
83
|
+
|
|
84
|
+
expect {
|
|
85
|
+
allow(application).to receive(:default_environment) { "test" }
|
|
86
|
+
}.to change {
|
|
87
|
+
application.environment
|
|
88
|
+
}.from("development").to("test")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe "#configuration" do
|
|
93
|
+
def yaml_to_path(yaml)
|
|
94
|
+
Tempfile.open("figaro") do |file|
|
|
95
|
+
file.write(yaml)
|
|
96
|
+
file.path
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "loads values from YAML" do
|
|
101
|
+
application = Application.new(path: yaml_to_path(<<-YAML))
|
|
102
|
+
foo: bar
|
|
103
|
+
YAML
|
|
104
|
+
|
|
105
|
+
expect(application.configuration).to eq("foo" => "bar")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "merges environment-specific values" do
|
|
109
|
+
application = Application.new(path: yaml_to_path(<<-YAML), environment: "test")
|
|
110
|
+
foo: bar
|
|
111
|
+
test:
|
|
112
|
+
foo: baz
|
|
113
|
+
YAML
|
|
114
|
+
|
|
115
|
+
expect(application.configuration).to eq("foo" => "baz")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "drops unused environment-specific values" do
|
|
119
|
+
application = Application.new(path: yaml_to_path(<<-YAML), environment: "test")
|
|
120
|
+
foo: bar
|
|
121
|
+
test:
|
|
122
|
+
foo: baz
|
|
123
|
+
production:
|
|
124
|
+
foo: bad
|
|
125
|
+
YAML
|
|
126
|
+
|
|
127
|
+
expect(application.configuration).to eq("foo" => "baz")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "is empty when no YAML file is present" do
|
|
131
|
+
application = Application.new(path: "/path/to/nowhere")
|
|
132
|
+
|
|
133
|
+
expect(application.configuration).to eq({})
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "is empty when the YAML file is blank" do
|
|
137
|
+
application = Application.new(path: yaml_to_path(""))
|
|
138
|
+
|
|
139
|
+
expect(application.configuration).to eq({})
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "is empty when the YAML file contains only comments" do
|
|
143
|
+
application = Application.new(path: yaml_to_path(<<-YAML))
|
|
144
|
+
# Comment
|
|
145
|
+
YAML
|
|
146
|
+
|
|
147
|
+
expect(application.configuration).to eq({})
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "processes ERB" do
|
|
151
|
+
application = Application.new(path: yaml_to_path(<<-YAML))
|
|
152
|
+
foo: <%= "bar".upcase %>
|
|
153
|
+
YAML
|
|
154
|
+
|
|
155
|
+
expect(application.configuration).to eq("foo" => "BAR")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "handles an empty environment block" do
|
|
159
|
+
application = Application.new(path: yaml_to_path("development:"))
|
|
160
|
+
|
|
161
|
+
expect {
|
|
162
|
+
application.configuration
|
|
163
|
+
}.not_to raise_error
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "follows a changing default path" do
|
|
167
|
+
path_1 = yaml_to_path("foo: bar")
|
|
168
|
+
path_2 = yaml_to_path("foo: baz")
|
|
169
|
+
|
|
170
|
+
application = Application.new
|
|
171
|
+
allow(application).to receive(:default_path) { path_1 }
|
|
172
|
+
|
|
173
|
+
expect {
|
|
174
|
+
allow(application).to receive(:default_path) { path_2 }
|
|
175
|
+
}.to change {
|
|
176
|
+
application.configuration
|
|
177
|
+
}.from("foo" => "bar").to("foo" => "baz")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "follows a changing default environment" do
|
|
181
|
+
application = Application.new(path: yaml_to_path(<<-YAML))
|
|
182
|
+
foo: bar
|
|
183
|
+
test:
|
|
184
|
+
foo: baz
|
|
185
|
+
YAML
|
|
186
|
+
allow(application).to receive(:default_environment) { "development" }
|
|
187
|
+
|
|
188
|
+
expect {
|
|
189
|
+
allow(application).to receive(:default_environment) { "test" }
|
|
190
|
+
}.to change {
|
|
191
|
+
application.configuration
|
|
192
|
+
}.from("foo" => "bar").to("foo" => "baz")
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
describe "#load" do
|
|
197
|
+
let!(:application) { Application.new }
|
|
198
|
+
|
|
199
|
+
before do
|
|
200
|
+
allow(application).to receive(:configuration) { { "foo" => "bar" } }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it "merges values into ENV" do
|
|
204
|
+
expect {
|
|
205
|
+
application.load
|
|
206
|
+
}.to change {
|
|
207
|
+
::ENV["foo"]
|
|
208
|
+
}.from(nil).to("bar")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "skips keys (and warns) that have already been set externally" do
|
|
212
|
+
::ENV["foo"] = "baz"
|
|
213
|
+
|
|
214
|
+
expect(application).to receive(:warn)
|
|
215
|
+
|
|
216
|
+
expect {
|
|
217
|
+
application.load
|
|
218
|
+
}.not_to change {
|
|
219
|
+
::ENV["foo"]
|
|
220
|
+
}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it "sets keys that have already been set internally" do
|
|
224
|
+
application.load
|
|
225
|
+
|
|
226
|
+
allow(application).to receive(:configuration) { { "foo" => "baz" } }
|
|
227
|
+
|
|
228
|
+
expect {
|
|
229
|
+
application.load
|
|
230
|
+
}.to change {
|
|
231
|
+
::ENV["foo"]
|
|
232
|
+
}.from("bar").to("baz")
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "warns when a key isn't a string" do
|
|
236
|
+
allow(application).to receive(:configuration) { { foo: "bar" } }
|
|
237
|
+
|
|
238
|
+
expect(application).to receive(:warn)
|
|
239
|
+
|
|
240
|
+
application.load
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it "warns when a value isn't a string" do
|
|
244
|
+
allow(application).to receive(:configuration) { { "foo" => ["bar"] } }
|
|
245
|
+
|
|
246
|
+
expect(application).to receive(:warn)
|
|
247
|
+
|
|
248
|
+
application.load
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "allows nil values" do
|
|
252
|
+
allow(application).to receive(:configuration) { { "foo" => nil } }
|
|
253
|
+
|
|
254
|
+
expect {
|
|
255
|
+
application.load
|
|
256
|
+
}.not_to change {
|
|
257
|
+
::ENV["foo"]
|
|
258
|
+
}
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|