daqing_figaro 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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