figaro 0.7.0 → 1.0.0.rc1

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