figaro 0.7.0 → 1.0.0.rc1

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.
Files changed (54) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +1 -1
  3. data/.travis.yml +23 -18
  4. data/CHANGELOG.md +98 -0
  5. data/CONTRIBUTING.md +48 -0
  6. data/Gemfile +4 -6
  7. data/{LICENSE → LICENSE.txt} +0 -0
  8. data/README.md +184 -61
  9. data/Rakefile +1 -12
  10. data/bin/figaro +5 -0
  11. data/doc/title.png +0 -0
  12. data/figaro.gemspec +9 -5
  13. data/gemfiles/rails30.gemfile +3 -4
  14. data/gemfiles/rails31.gemfile +3 -4
  15. data/gemfiles/rails32.gemfile +3 -4
  16. data/gemfiles/rails40.gemfile +3 -7
  17. data/gemfiles/rails41.gemfile +11 -0
  18. data/lib/figaro.rb +16 -29
  19. data/lib/figaro/application.rb +91 -0
  20. data/lib/figaro/cli.rb +24 -0
  21. data/lib/figaro/cli/heroku_set.rb +29 -0
  22. data/lib/figaro/cli/task.rb +27 -0
  23. data/lib/figaro/env.rb +36 -7
  24. data/lib/figaro/error.rb +12 -0
  25. data/lib/figaro/rails.rb +9 -0
  26. data/lib/figaro/rails/application.rb +21 -0
  27. data/lib/figaro/rails/railtie.rb +9 -0
  28. data/lib/figaro/{tasks.rake → rails/tasks.rake} +0 -0
  29. data/lib/generators/figaro/install/install_generator.rb +1 -1
  30. data/lib/generators/figaro/install/templates/application.yml +10 -6
  31. data/spec/figaro/application_spec.rb +258 -0
  32. data/spec/figaro/cli/heroku_set_spec.rb +62 -0
  33. data/spec/figaro/env_spec.rb +167 -35
  34. data/spec/figaro/rails/application_spec.rb +43 -0
  35. data/spec/figaro_spec.rb +74 -36
  36. data/spec/rails_spec.rb +66 -0
  37. data/spec/spec_helper.rb +6 -3
  38. data/spec/support/aruba.rb +19 -0
  39. data/spec/support/bin/heroku +5 -0
  40. data/spec/support/command_helpers.rb +17 -0
  41. data/spec/support/command_interceptor.rb +33 -0
  42. data/spec/support/random.rb +3 -0
  43. data/spec/support/reset.rb +13 -0
  44. metadata +88 -44
  45. data/features/rails.feature +0 -97
  46. data/features/step_definitions/bundler_steps.rb +0 -7
  47. data/features/step_definitions/common_steps.rb +0 -19
  48. data/features/step_definitions/rails_steps.rb +0 -12
  49. data/features/support/aruba.rb +0 -12
  50. data/features/support/env.rb +0 -8
  51. data/lib/figaro/railtie.rb +0 -16
  52. data/lib/figaro/tasks.rb +0 -28
  53. data/spec/figaro/tasks_spec.rb +0 -71
  54. data/spec/support/rake.rb +0 -11
@@ -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
@@ -1,6 +1,6 @@
1
1
  module Figaro
2
2
  module Generators
3
- class InstallGenerator < Rails::Generators::Base
3
+ class InstallGenerator < ::Rails::Generators::Base
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
 
6
6
  def create_configuration
@@ -1,7 +1,11 @@
1
- # Add application configuration variables here, as shown below.
1
+ # Add configuration values here, as shown below.
2
2
  #
3
- # PUSHER_APP_ID: "2954"
4
- # PUSHER_KEY: 7381a978f7dd7f9a1117
5
- # PUSHER_SECRET: abdc3b896a0ffb85d373
6
- # STRIPE_API_KEY: EdAvEPVEC3LuaTg5Q3z6WbDVqZlcBQ8Z
7
- # STRIPE_PUBLIC_KEY: pk_BRgD57O8fHja9HxduJUszhef6jCyS
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,258 @@
1
+ require "spec_helper"
2
+
3
+ require "tempfile"
4
+
5
+ module Figaro
6
+ describe Application do
7
+ before do
8
+ Application.any_instance.stub(
9
+ default_path: "/path/to/app/config/application.yml",
10
+ default_environment: "development"
11
+ )
12
+ end
13
+
14
+ describe "#path" do
15
+ it "uses the default" do
16
+ application = Application.new
17
+
18
+ expect(application.path).to eq("/path/to/app/config/application.yml")
19
+ end
20
+
21
+ it "is configurable via initialization" do
22
+ application = Application.new(path: "/app/env.yml")
23
+
24
+ expect(application.path).to eq("/app/env.yml")
25
+ end
26
+
27
+ it "is configurable via setter" do
28
+ application = Application.new
29
+ application.path = "/app/env.yml"
30
+
31
+ expect(application.path).to eq("/app/env.yml")
32
+ end
33
+
34
+ it "casts to string" do
35
+ application = Application.new(path: Pathname.new("/app/env.yml"))
36
+
37
+ expect(application.path).to eq("/app/env.yml")
38
+ expect(application.environment).not_to be_a(Pathname)
39
+ end
40
+
41
+ it "follows a changing default" do
42
+ application = Application.new
43
+
44
+ expect {
45
+ application.stub(default_path: "/app/env.yml")
46
+ }.to change {
47
+ application.path
48
+ }.from("/path/to/app/config/application.yml").to("/app/env.yml")
49
+ end
50
+ end
51
+
52
+ describe "#environment" do
53
+ it "uses the default" do
54
+ application = Application.new
55
+
56
+ expect(application.environment).to eq("development")
57
+ end
58
+
59
+ it "is configurable via initialization" do
60
+ application = Application.new(environment: "test")
61
+
62
+ expect(application.environment).to eq("test")
63
+ end
64
+
65
+ it "is configurable via setter" do
66
+ application = Application.new
67
+ application.environment = "test"
68
+
69
+ expect(application.environment).to eq("test")
70
+ end
71
+
72
+ it "casts to string" do
73
+ application = Application.new(environment: :test)
74
+
75
+ expect(application.environment).to eq("test")
76
+ expect(application.environment).not_to be_a(Symbol)
77
+ end
78
+
79
+ it "respects nil" do
80
+ application = Application.new(environment: nil)
81
+
82
+ expect(application.environment).to eq(nil)
83
+ end
84
+
85
+ it "follows a changing default" do
86
+ application = Application.new
87
+
88
+ expect {
89
+ application.stub(default_environment: "test")
90
+ }.to change {
91
+ application.environment
92
+ }.from("development").to("test")
93
+ end
94
+ end
95
+
96
+ describe "#configuration" do
97
+ def yaml_to_path(yaml)
98
+ Tempfile.open("figaro") do |file|
99
+ file.write(yaml)
100
+ file.path
101
+ end
102
+ end
103
+
104
+ it "loads values from YAML" do
105
+ application = Application.new(path: yaml_to_path(<<-YAML))
106
+ foo: bar
107
+ YAML
108
+
109
+ expect(application.configuration).to eq("foo" => "bar")
110
+ end
111
+
112
+ it "merges environment-specific values" do
113
+ application = Application.new(path: yaml_to_path(<<-YAML), environment: "test")
114
+ foo: bar
115
+ test:
116
+ foo: baz
117
+ YAML
118
+
119
+ expect(application.configuration).to eq("foo" => "baz")
120
+ end
121
+
122
+ it "drops unused environment-specific values" do
123
+ application = Application.new(path: yaml_to_path(<<-YAML), environment: "test")
124
+ foo: bar
125
+ test:
126
+ foo: baz
127
+ production:
128
+ foo: bad
129
+ YAML
130
+
131
+ expect(application.configuration).to eq("foo" => "baz")
132
+ end
133
+
134
+ it "is empty when no YAML file is present" do
135
+ application = Application.new(path: "/path/to/nowhere")
136
+
137
+ expect(application.configuration).to eq({})
138
+ end
139
+
140
+ it "is empty when the YAML file is blank" do
141
+ application = Application.new(path: yaml_to_path(""))
142
+
143
+ expect(application.configuration).to eq({})
144
+ end
145
+
146
+ it "is empty when the YAML file contains only comments" do
147
+ application = Application.new(path: yaml_to_path(<<-YAML))
148
+ # Comment
149
+ YAML
150
+
151
+ expect(application.configuration).to eq({})
152
+ end
153
+
154
+ it "processes ERB" do
155
+ application = Application.new(path: yaml_to_path(<<-YAML))
156
+ foo: <%= "bar".upcase %>
157
+ YAML
158
+
159
+ expect(application.configuration).to eq("foo" => "BAR")
160
+ end
161
+
162
+ it "follows a changing default path" do
163
+ path_1 = yaml_to_path("foo: bar")
164
+ path_2 = yaml_to_path("foo: baz")
165
+
166
+ application = Application.new
167
+ application.stub(default_path: path_1)
168
+
169
+ expect {
170
+ application.stub(default_path: path_2)
171
+ }.to change {
172
+ application.configuration
173
+ }.from("foo" => "bar").to("foo" => "baz")
174
+ end
175
+
176
+ it "follows a changing default environment" do
177
+ application = Application.new(path: yaml_to_path(<<-YAML))
178
+ foo: bar
179
+ test:
180
+ foo: baz
181
+ YAML
182
+ application.stub(default_environment: "development")
183
+
184
+ expect {
185
+ application.stub(default_environment: "test")
186
+ }.to change {
187
+ application.configuration
188
+ }.from("foo" => "bar").to("foo" => "baz")
189
+ end
190
+ end
191
+
192
+ describe "#load" do
193
+ let!(:application) { Application.new }
194
+
195
+ before do
196
+ application.stub(configuration: { "foo" => "bar" })
197
+ end
198
+
199
+ it "merges values into ENV" do
200
+ expect {
201
+ application.load
202
+ }.to change {
203
+ ::ENV["foo"]
204
+ }.from(nil).to("bar")
205
+ end
206
+
207
+ it "skips keys (and warns) that have already been set externally" do
208
+ ::ENV["foo"] = "baz"
209
+
210
+ expect(application).to receive(:warn)
211
+
212
+ expect {
213
+ application.load
214
+ }.not_to change {
215
+ ::ENV["foo"]
216
+ }
217
+ end
218
+
219
+ it "sets keys that have already been set internally" do
220
+ application.load
221
+
222
+ application.stub(configuration: { "foo" => "baz" })
223
+
224
+ expect {
225
+ application.load
226
+ }.to change {
227
+ ::ENV["foo"]
228
+ }.from("bar").to("baz")
229
+ end
230
+
231
+ it "warns when a key isn't a string" do
232
+ application.stub(configuration: { foo: "bar" })
233
+
234
+ expect(application).to receive(:warn)
235
+
236
+ application.load
237
+ end
238
+
239
+ it "warns when a value isn't a string" do
240
+ application.stub(configuration: { "foo" => ["bar"] })
241
+
242
+ expect(application).to receive(:warn)
243
+
244
+ application.load
245
+ end
246
+
247
+ it "allows nil values" do
248
+ application.stub(configuration: { "foo" => nil })
249
+
250
+ expect {
251
+ application.load
252
+ }.not_to change {
253
+ ::ENV["foo"]
254
+ }
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,62 @@
1
+ require "spec_helper"
2
+
3
+ require "figaro/cli"
4
+
5
+ describe "figaro heroku:set" do
6
+ before do
7
+ create_dir("example")
8
+ cd("example")
9
+ write_file("config/application.yml", "foo: bar")
10
+ end
11
+
12
+ it "sends Figaro configuration to Heroku" do
13
+ run_simple("figaro heroku:set")
14
+
15
+ command = commands.last
16
+ expect(command.name).to eq("heroku")
17
+ expect(command.args).to eq(["config:set", "foo=bar"])
18
+ end
19
+
20
+ it "respects path" do
21
+ write_file("env.yml", "foo: bar")
22
+
23
+ run_simple("figaro heroku:set -p env.yml")
24
+
25
+ command = commands.last
26
+ expect(command.name).to eq("heroku")
27
+ expect(command.args).to eq(["config:set", "foo=bar"])
28
+ end
29
+
30
+ it "respects environment" do
31
+ overwrite_file("config/application.yml", <<-EOF)
32
+ foo: bar
33
+ test:
34
+ foo: baz
35
+ EOF
36
+
37
+ run_simple("figaro heroku:set -e test")
38
+
39
+ command = commands.last
40
+ expect(command.name).to eq("heroku")
41
+ expect(command.args).to eq(["config:set", "foo=baz"])
42
+ end
43
+
44
+ it "targets a specific Heroku app" do
45
+ run_simple("figaro heroku:set -a foo-bar-app")
46
+
47
+ command = commands.last
48
+ expect(command.name).to eq("heroku")
49
+ expect(command.args.shift).to eq("config:set")
50
+ expect(command.args).to match_array(["foo=bar", "--app=foo-bar-app"])
51
+ end
52
+
53
+ it "handles values with special characters" do
54
+ overwrite_file("config/application.yml", "foo: bar baz")
55
+
56
+ run_simple("figaro heroku:set")
57
+
58
+ command = commands.last
59
+ expect(command.name).to eq("heroku")
60
+ expect(command.args).to eq(["config:set", "foo=bar baz"])
61
+ end
62
+ end
@@ -1,64 +1,196 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Figaro::Env do
4
- subject(:env) { Figaro::Env.new }
3
+ describe Figaro::ENV do
4
+ subject(:env) { Figaro::ENV }
5
5
 
6
6
  before do
7
- ENV["HELLO"] = "world"
8
- ENV["foo"] = "bar"
9
- end
10
-
11
- after do
12
- ENV.delete("HELLO")
13
- ENV.delete("foo")
7
+ ::ENV["HELLO"] = "world"
8
+ ::ENV["foo"] = "bar"
14
9
  end
15
10
 
16
11
  describe "#method_missing" do
17
- it "makes ENV values accessible as lowercase methods" do
18
- expect(env.hello).to eq("world")
19
- expect(env.foo).to eq("bar")
12
+ context "plain methods" do
13
+ it "makes ENV values accessible as lowercase methods" do
14
+ expect(env.hello).to eq("world")
15
+ expect(env.foo).to eq("bar")
16
+ end
17
+
18
+ it "makes ENV values accessible as uppercase methods" do
19
+ expect(env.HELLO).to eq("world")
20
+ expect(env.FOO).to eq("bar")
21
+ end
22
+
23
+ it "makes ENV values accessible as mixed-case methods" do
24
+ expect(env.Hello).to eq("world")
25
+ expect(env.fOO).to eq("bar")
26
+ end
27
+
28
+ it "returns nil if no ENV key matches" do
29
+ expect(env.goodbye).to eq(nil)
30
+ end
31
+
32
+ it "respects a stubbed plain method" do
33
+ env.stub(bar: "baz")
34
+ expect(env.bar).to eq("baz")
35
+ end
20
36
  end
21
37
 
22
- it "makes ENV values accessible as uppercase methods" do
23
- expect(env.HELLO).to eq("world")
24
- expect(env.FOO).to eq("bar")
38
+ context "bang methods" do
39
+ it "makes ENV values accessible as lowercase methods" do
40
+ expect(env.hello!).to eq("world")
41
+ expect(env.foo!).to eq("bar")
42
+ end
43
+
44
+ it "makes ENV values accessible as uppercase methods" do
45
+ expect(env.HELLO!).to eq("world")
46
+ expect(env.FOO!).to eq("bar")
47
+ end
48
+
49
+ it "makes ENV values accessible as mixed-case methods" do
50
+ expect(env.Hello!).to eq("world")
51
+ expect(env.fOO!).to eq("bar")
52
+ end
53
+
54
+ it "raises an error if no ENV key matches" do
55
+ expect { env.goodbye! }.to raise_error(Figaro::MissingKey)
56
+ end
57
+
58
+ it "respects a stubbed plain method" do
59
+ env.stub(bar: "baz")
60
+ expect { expect(env.bar!).to eq("baz") }.not_to raise_error
61
+ end
25
62
  end
26
63
 
27
- it "makes ENV values accessible as mixed-case methods" do
28
- expect(env.Hello).to eq("world")
29
- expect(env.fOO).to eq("bar")
64
+ context "boolean methods" do
65
+ it "returns true for accessible, lowercase methods" do
66
+ expect(env.hello?).to eq(true)
67
+ expect(env.foo?).to eq(true)
68
+ end
69
+
70
+ it "returns true for accessible, uppercase methods" do
71
+ expect(env.HELLO?).to eq(true)
72
+ expect(env.FOO?).to eq(true)
73
+ end
74
+
75
+ it "returns true for accessible, mixed-case methods" do
76
+ expect(env.Hello?).to eq(true)
77
+ expect(env.fOO?).to eq(true)
78
+ end
79
+
80
+ it "returns false if no ENV key matches" do
81
+ expect(env.goodbye?).to eq(false)
82
+ end
83
+
84
+ it "respects a stubbed plain method" do
85
+ env.stub(bar: "baz")
86
+ expect(env.bar?).to eq(true)
87
+ end
30
88
  end
31
89
 
32
- it "raises an error if no ENV key matches" do
33
- expect { env.goodbye }.to raise_error(NoMethodError)
90
+ context "setter methods" do
91
+ it "raises an error for accessible, lowercase methods" do
92
+ expect { env.hello = "world" }.to raise_error(NoMethodError)
93
+ expect { env.foo = "bar" }.to raise_error(NoMethodError)
94
+ end
95
+
96
+ it "raises an error for accessible, uppercase methods" do
97
+ expect { env.HELLO = "world" }.to raise_error(NoMethodError)
98
+ expect { env.FOO = "bar" }.to raise_error(NoMethodError)
99
+ end
100
+
101
+ it "raises an error for accessible, mixed-case methods" do
102
+ expect { env.Hello = "world" }.to raise_error(NoMethodError)
103
+ expect { env.fOO = "bar" }.to raise_error(NoMethodError)
104
+ end
105
+
106
+ it "raises an error if no ENV key matches" do
107
+ expect { env.goodbye = "world" }.to raise_error(NoMethodError)
108
+ end
34
109
  end
35
110
  end
36
111
 
37
112
  describe "#respond_to?" do
38
- context "when ENV has the key" do
39
- it "is true for a lowercase method" do
40
- expect(env.respond_to?(:hello)).to be_true
41
- expect(env.respond_to?(:foo)).to be_true
113
+ context "plain methods" do
114
+ it "returns true for accessible, lowercase methods" do
115
+ expect(env.respond_to?(:hello)).to eq(true)
116
+ expect(env.respond_to?(:foo)).to eq(true)
117
+ end
118
+
119
+ it "returns true for accessible uppercase methods" do
120
+ expect(env.respond_to?(:HELLO)).to eq(true)
121
+ expect(env.respond_to?(:FOO)).to eq(true)
122
+ end
123
+
124
+ it "returns true for accessible mixed-case methods" do
125
+ expect(env.respond_to?(:Hello)).to eq(true)
126
+ expect(env.respond_to?(:fOO)).to eq(true)
127
+ end
128
+
129
+ it "returns true if no ENV key matches" do
130
+ expect(env.respond_to?(:baz)).to eq(true)
131
+ end
132
+ end
133
+
134
+ context "bang methods" do
135
+ it "returns true for accessible, lowercase methods" do
136
+ expect(env.respond_to?(:hello!)).to eq(true)
137
+ expect(env.respond_to?(:foo!)).to eq(true)
42
138
  end
43
139
 
44
- it "is true for a uppercase method" do
45
- expect(env.respond_to?(:HELLO)).to be_true
46
- expect(env.respond_to?(:FOO)).to be_true
140
+ it "returns true for accessible uppercase methods" do
141
+ expect(env.respond_to?(:HELLO!)).to eq(true)
142
+ expect(env.respond_to?(:FOO!)).to eq(true)
47
143
  end
48
144
 
49
- it "is true for a mixed-case key" do
50
- expect(env.respond_to?(:Hello)).to be_true
51
- expect(env.respond_to?(:fOO)).to be_true
145
+ it "returns true for accessible mixed-case methods" do
146
+ expect(env.respond_to?(:Hello!)).to eq(true)
147
+ expect(env.respond_to?(:fOO!)).to eq(true)
148
+ end
149
+
150
+ it "returns false if no ENV key matches" do
151
+ expect(env.respond_to?(:baz!)).to eq(false)
52
152
  end
53
153
  end
54
154
 
55
- context "when ENV doesn't have the key" do
56
- it "is true if Hash responds to the method" do
57
- expect(env.respond_to?(:baz)).to be_false
155
+ context "boolean methods" do
156
+ it "returns true for accessible, lowercase methods" do
157
+ expect(env.respond_to?(:hello?)).to eq(true)
158
+ expect(env.respond_to?(:foo?)).to eq(true)
159
+ end
160
+
161
+ it "returns true for accessible uppercase methods" do
162
+ expect(env.respond_to?(:HELLO?)).to eq(true)
163
+ expect(env.respond_to?(:FOO?)).to eq(true)
164
+ end
165
+
166
+ it "returns true for accessible mixed-case methods" do
167
+ expect(env.respond_to?(:Hello?)).to eq(true)
168
+ expect(env.respond_to?(:fOO?)).to eq(true)
169
+ end
170
+
171
+ it "returns true if no ENV key matches" do
172
+ expect(env.respond_to?(:baz?)).to eq(true)
173
+ end
174
+ end
175
+
176
+ context "setter methods" do
177
+ it "returns false for accessible, lowercase methods" do
178
+ expect(env.respond_to?(:hello=)).to eq(false)
179
+ expect(env.respond_to?(:foo=)).to eq(false)
180
+ end
181
+
182
+ it "returns false for accessible uppercase methods" do
183
+ expect(env.respond_to?(:HELLO=)).to eq(false)
184
+ expect(env.respond_to?(:FOO=)).to eq(false)
185
+ end
186
+
187
+ it "returns false for accessible mixed-case methods" do
188
+ expect(env.respond_to?(:Hello=)).to eq(false)
189
+ expect(env.respond_to?(:fOO=)).to eq(false)
58
190
  end
59
191
 
60
- it "is false if Hash doesn't respond to the method" do
61
- expect(env.respond_to?(:[])).to be_true
192
+ it "returns false if no ENV key matches" do
193
+ expect(env.respond_to?(:baz=)).to eq(false)
62
194
  end
63
195
  end
64
196
  end