daqing_figaro 1.2.1

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,91 @@
1
+ require "erb"
2
+ require "yaml"
3
+
4
+ module Figaro
5
+ class Application
6
+ FIGARO_ENV_PREFIX = "_FIGARO_"
7
+
8
+ include Enumerable
9
+
10
+ def initialize(options = {})
11
+ @options = options.inject({}) { |m, (k, v)| m[k.to_sym] = v; m }
12
+ end
13
+
14
+ def path
15
+ @options.fetch(:path) { default_path }.to_s
16
+ end
17
+
18
+ def path=(path)
19
+ @options[:path] = path
20
+ end
21
+
22
+ def environment
23
+ environment = @options.fetch(:environment) { default_environment }
24
+ environment.nil? ? nil : environment.to_s
25
+ end
26
+
27
+ def environment=(environment)
28
+ @options[:environment] = environment
29
+ end
30
+
31
+ def configuration
32
+ global_configuration.merge(environment_configuration)
33
+ end
34
+
35
+ def load
36
+ each do |key, value|
37
+ skip?(key) ? key_skipped!(key) : set(key, value)
38
+ end
39
+ end
40
+
41
+ def each(&block)
42
+ configuration.each(&block)
43
+ end
44
+
45
+ private
46
+
47
+ def default_path
48
+ raise NotImplementedError
49
+ end
50
+
51
+ def default_environment
52
+ nil
53
+ end
54
+
55
+ def raw_configuration
56
+ (@parsed ||= Hash.new { |hash, path| hash[path] = parse(path) })[path]
57
+ end
58
+
59
+ def parse(path)
60
+ File.exist?(path) && YAML.load(ERB.new(File.read(path)).result) || {}
61
+ end
62
+
63
+ def global_configuration
64
+ raw_configuration.reject { |_, value| value.is_a?(Hash) }
65
+ end
66
+
67
+ def environment_configuration
68
+ raw_configuration[environment] || {}
69
+ end
70
+
71
+ def set(key, value)
72
+ non_string_configuration!(key) unless key.is_a?(String)
73
+ non_string_configuration!(value) unless value.is_a?(String) || value.nil?
74
+
75
+ ::ENV[key.to_s] = value.nil? ? nil : value.to_s
76
+ ::ENV[FIGARO_ENV_PREFIX + key.to_s] = value.nil? ? nil: value.to_s
77
+ end
78
+
79
+ def skip?(key)
80
+ ::ENV.key?(key.to_s) && !::ENV.key?(FIGARO_ENV_PREFIX + key.to_s)
81
+ end
82
+
83
+ def non_string_configuration!(value)
84
+ warn "WARNING: Use strings for Figaro configuration. #{value.inspect} was converted to #{value.to_s.inspect}."
85
+ end
86
+
87
+ def key_skipped!(key)
88
+ warn "WARNING: Skipping key #{key.inspect}. Already set in ENV."
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,33 @@
1
+ require "figaro/cli/task"
2
+
3
+ module Figaro
4
+ class CLI < Thor
5
+ class HerokuSet < Task
6
+ def run
7
+ system(env, command)
8
+ end
9
+
10
+ private
11
+
12
+ def command
13
+ "heroku config:set #{vars} #{for_app} #{for_remote}"
14
+ end
15
+
16
+ def for_app
17
+ options[:app] ? "--app=#{options[:app]}" : nil
18
+ end
19
+
20
+ def for_remote
21
+ options[:remote] ? "--remote=#{options[:remote]}" : nil
22
+ end
23
+
24
+ def vars
25
+ configuration.keys.map { |k| var(k) }.join(" ")
26
+ end
27
+
28
+ def var(key)
29
+ Gem.win_platform? ? %(#{key}="%#{key}%") : %(#{key}="$#{key}")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ # Add configuration values here, as shown below.
2
+ #
3
+ # pusher_app_id: "2954"
4
+ # pusher_key: 7381a978f7dd7f9a1117
5
+ # pusher_secret: abdc3b896a0ffb85d373
6
+ # stripe_api_key: sk_test_2J0l093xOyW72XUYJHE4Dv2r
7
+ # stripe_publishable_key: pk_test_ro9jV5SNwGb1yYlQfzG17LHK
8
+ #
9
+ # production:
10
+ # stripe_api_key: sk_live_EeHnL644i6zo4Iyq4v1KdV9H
11
+ # stripe_publishable_key: pk_live_9lcthxpSIHbGwmdO941O1XVU
@@ -0,0 +1,32 @@
1
+ require "thor/group"
2
+
3
+ module Figaro
4
+ class CLI < Thor
5
+ class Install < Thor::Group
6
+ include Thor::Actions
7
+
8
+ class_option "path",
9
+ aliases: ["-p"],
10
+ default: "config/application.yml",
11
+ desc: "Specify a configuration file path"
12
+
13
+ def self.source_root
14
+ File.expand_path("../install", __FILE__)
15
+ end
16
+
17
+ def create_configuration
18
+ copy_file("application.yml", options[:path])
19
+ end
20
+
21
+ def ignore_configuration
22
+ if File.exist?(".gitignore")
23
+ append_to_file(".gitignore", <<-EOF)
24
+
25
+ # Ignore application configuration
26
+ /#{options[:path]}
27
+ EOF
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ require "figaro/application"
2
+
3
+ module Figaro
4
+ class CLI < Thor
5
+ class Task
6
+ attr_reader :options
7
+
8
+ def self.run(options = {})
9
+ new(options).run
10
+ end
11
+
12
+ def initialize(options = {})
13
+ @options = options
14
+ end
15
+
16
+ private
17
+
18
+ def env
19
+ ENV.to_hash.update(configuration)
20
+ end
21
+
22
+ def configuration
23
+ application.configuration
24
+ end
25
+
26
+ def application
27
+ @application ||= Figaro::Application.new(options)
28
+ end
29
+
30
+ if defined? Bundler
31
+ def system(*)
32
+ Bundler.with_clean_env { super }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/figaro/cli.rb ADDED
@@ -0,0 +1,42 @@
1
+ require "thor"
2
+
3
+ module Figaro
4
+ class CLI < Thor
5
+ # figaro install
6
+
7
+ desc "install", "Install Figaro"
8
+
9
+ method_option "path",
10
+ aliases: ["-p"],
11
+ default: "config/application.yml",
12
+ desc: "Specify a configuration file path"
13
+
14
+ def install
15
+ require "figaro/cli/install"
16
+ Install.start
17
+ end
18
+
19
+ # figaro heroku:set
20
+
21
+ desc "heroku:set", "Send Figaro configuration to Heroku"
22
+
23
+ method_option "app",
24
+ aliases: ["-a"],
25
+ desc: "Specify a Heroku app"
26
+ method_option "environment",
27
+ aliases: ["-e"],
28
+ desc: "Specify an application environment"
29
+ method_option "path",
30
+ aliases: ["-p"],
31
+ default: "config/application.yml",
32
+ desc: "Specify a configuration file path"
33
+ method_option "remote",
34
+ aliases: ["-r"],
35
+ desc: "Specify a Heroku git remote"
36
+
37
+ define_method "heroku:set" do
38
+ require "figaro/cli/heroku_set"
39
+ HerokuSet.run(options)
40
+ end
41
+ end
42
+ end
data/lib/figaro/env.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Figaro
2
+ module ENV
3
+ extend self
4
+
5
+ def respond_to?(method, *)
6
+ key, punctuation = extract_key_from_method(method)
7
+
8
+ case punctuation
9
+ when "!" then has_key?(key) || super
10
+ when "?", nil then true
11
+ else super
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def method_missing(method, *)
18
+ key, punctuation = extract_key_from_method(method)
19
+
20
+ case punctuation
21
+ when "!" then send(key) || missing_key!(key)
22
+ when "?" then !!send(key)
23
+ when nil then get_value(key)
24
+ else super
25
+ end
26
+ end
27
+
28
+ def extract_key_from_method(method)
29
+ method.to_s.downcase.match(/^(.+?)([!?=])?$/).captures
30
+ end
31
+
32
+ def has_key?(key)
33
+ ::ENV.any? { |k, _| k.downcase == key }
34
+ end
35
+
36
+ def missing_key!(key)
37
+ raise MissingKey.new(key)
38
+ end
39
+
40
+ def get_value(key)
41
+ _, value = ::ENV.detect { |k, _| k.downcase == key }
42
+ value
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ module Figaro
2
+ class Error < StandardError; end
3
+
4
+ class RailsNotInitialized < Error; end
5
+
6
+ class MissingKey < Error
7
+ def initialize(key)
8
+ super("Missing required configuration key: #{key.inspect}")
9
+ end
10
+ end
11
+
12
+ class MissingKeys < Error
13
+ def initialize(keys)
14
+ super("Missing required configuration keys: #{keys.inspect}")
15
+ end
16
+ end
17
+ 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,9 @@
1
+ module Figaro
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ config.before_configuration do
5
+ Figaro.load
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ namespace :figaro do
2
+ desc "Configure Heroku according to application.yml"
3
+ task :heroku, [:app] => :environment do |_, args|
4
+ Figaro::Tasks::Heroku.new(args[:app]).invoke
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ begin
2
+ require "rails"
3
+ rescue LoadError
4
+ else
5
+ require "figaro/rails/application"
6
+ require "figaro/rails/railtie"
7
+
8
+ Figaro.adapter = Figaro::Rails::Application
9
+ end
data/lib/figaro.rb ADDED
@@ -0,0 +1,32 @@
1
+ require "figaro/error"
2
+ require "figaro/env"
3
+ require "figaro/application"
4
+
5
+ module Figaro
6
+ extend self
7
+
8
+ attr_writer :adapter, :application
9
+
10
+ def env
11
+ Figaro::ENV
12
+ end
13
+
14
+ def adapter
15
+ @adapter ||= Figaro::Application
16
+ end
17
+
18
+ def application
19
+ @application ||= adapter.new
20
+ end
21
+
22
+ def load
23
+ application.load
24
+ end
25
+
26
+ def require_keys(*keys)
27
+ missing_keys = keys.flatten - ::ENV.keys
28
+ raise MissingKeys.new(missing_keys) if missing_keys.any?
29
+ end
30
+ end
31
+
32
+ require "figaro/rails"
@@ -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