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
data/Rakefile CHANGED
@@ -1,17 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
- require "cucumber/rake/task"
4
3
 
5
4
  RSpec::Core::RakeTask.new(:spec)
6
- Cucumber::Rake::Task.new(:cucumber)
7
5
 
8
- task :default => [:spec, :cucumber]
9
-
10
- if ENV["COVERAGE"]
11
- Rake::Task[:default].enhance do
12
- require "simplecov"
13
- require "coveralls"
14
-
15
- Coveralls::SimpleCov::Formatter.new.format(SimpleCov.result)
16
- end
17
- end
6
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "figaro/cli"
4
+
5
+ Figaro::CLI.start
Binary file
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "figaro"
5
- gem.version = "0.7.0"
5
+ gem.version = "1.0.0.rc1"
6
6
 
7
7
  gem.author = "Steve Richert"
8
8
  gem.email = "steve.richert@gmail.com"
@@ -11,10 +11,14 @@ Gem::Specification.new do |gem|
11
11
  gem.homepage = "https://github.com/laserlemon/figaro"
12
12
  gem.license = "MIT"
13
13
 
14
- gem.add_dependency "bundler", "~> 1.0"
15
14
  gem.add_dependency "rails", ">= 3", "< 5"
15
+ gem.add_dependency "thor", "~> 0.14"
16
16
 
17
- gem.files = `git ls-files`.split($\)
18
- gem.test_files = gem.files.grep(/^(features|spec)/)
19
- gem.require_paths = ["lib"]
17
+ gem.add_development_dependency "bundler", "~> 1.5"
18
+ gem.add_development_dependency "rake", "~> 10.1"
19
+
20
+ gem.files = `git ls-files`.split($\)
21
+ gem.test_files = gem.files.grep(/^spec/)
22
+
23
+ gem.executables << "figaro"
20
24
  end
@@ -1,12 +1,11 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec :path => "../"
3
+ gemspec path: "../"
4
4
 
5
5
  gem "rails", "~> 3.0.2"
6
6
 
7
7
  group :test do
8
8
  gem "aruba", "~> 0.5"
9
- gem "cucumber", "~> 1.3"
10
- gem "rake", "~> 10.0"
11
- gem "rspec", "~> 2.13"
9
+ gem "rspec", "~> 2.14"
10
+ gem "sqlite3", "~> 1.3"
12
11
  end
@@ -1,12 +1,11 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec :path => "../"
3
+ gemspec path: "../"
4
4
 
5
5
  gem "rails", "~> 3.1.0"
6
6
 
7
7
  group :test do
8
8
  gem "aruba", "~> 0.5"
9
- gem "cucumber", "~> 1.3"
10
- gem "rake", "~> 10.0"
11
- gem "rspec", "~> 2.13"
9
+ gem "rspec", "~> 2.14"
10
+ gem "sqlite3", "~> 1.3"
12
11
  end
@@ -1,12 +1,11 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec :path => "../"
3
+ gemspec path: "../"
4
4
 
5
5
  gem "rails", "~> 3.2.0"
6
6
 
7
7
  group :test do
8
8
  gem "aruba", "~> 0.5"
9
- gem "cucumber", "~> 1.3"
10
- gem "rake", "~> 10.0"
11
- gem "rspec", "~> 2.13"
9
+ gem "rspec", "~> 2.14"
10
+ gem "sqlite3", "~> 1.3"
12
11
  end
@@ -1,15 +1,11 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec :path => "../"
3
+ gemspec path: "../"
4
4
 
5
5
  gem "rails", "~> 4.0.0"
6
6
 
7
7
  group :test do
8
8
  gem "aruba", "~> 0.5"
9
- gem "coveralls", "~> 0.6", :require => false
10
- gem "cucumber", "~> 1.3"
11
- gem "json", "~> 1.7.7"
12
- gem "rake", "~> 10.0"
13
- gem "rspec", "~> 2.13"
14
- gem "simplecov", "~> 0.7", :require => false
9
+ gem "rspec", "~> 2.14"
10
+ gem "sqlite3", "~> 1.3"
15
11
  end
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../"
4
+
5
+ gem "rails", "~> 4.1.0"
6
+
7
+ group :test do
8
+ gem "aruba", "~> 0.5"
9
+ gem "rspec", "~> 2.14"
10
+ gem "sqlite3", "~> 1.3"
11
+ end
@@ -1,45 +1,32 @@
1
- require "shellwords"
1
+ require "figaro/error"
2
2
  require "figaro/env"
3
- require "figaro/railtie"
4
- require "figaro/tasks"
3
+ require "figaro/application"
5
4
 
6
5
  module Figaro
7
6
  extend self
8
7
 
9
- def vars(custom_environment = nil)
10
- env(custom_environment).map { |key, value|
11
- "#{key}=#{Shellwords.escape(value)}"
12
- }.sort.join(" ")
13
- end
14
-
15
- def env(custom_environment = nil)
16
- environment = (custom_environment || self.environment).to_s
17
- Figaro::Env.from(stringify(flatten(raw).merge(raw.fetch(environment, {}))))
18
- end
8
+ attr_writer :adapter, :application
19
9
 
20
- def raw
21
- @raw ||= yaml && YAML.load(yaml) || {}
10
+ def env
11
+ Figaro::ENV
22
12
  end
23
13
 
24
- def yaml
25
- @yaml ||= File.exist?(path) ? ERB.new(File.read(path)).result : nil
14
+ def adapter
15
+ @adapter ||= Figaro::Application
26
16
  end
27
17
 
28
- def path
29
- @path ||= Rails.root.join("config", "application.yml")
18
+ def application
19
+ @application ||= adapter.new
30
20
  end
31
21
 
32
- def environment
33
- Rails.env
22
+ def load
23
+ application.load
34
24
  end
35
25
 
36
- private
37
-
38
- def flatten(hash)
39
- hash.reject { |_, v| Hash === v }
40
- end
41
-
42
- def stringify(hash)
43
- hash.inject({}) { |h, (k, v)| h[k.to_s] = v.nil? ? nil : v.to_s; h }
26
+ def require(*keys)
27
+ missing_keys = keys.flatten - ::ENV.keys
28
+ raise MissingKeys.new(missing_keys) if missing_keys.any?
44
29
  end
45
30
  end
31
+
32
+ require "figaro/rails"
@@ -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.fetch(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,24 @@
1
+ require "thor"
2
+
3
+ require "figaro/cli/heroku_set"
4
+
5
+ module Figaro
6
+ class CLI < Thor
7
+ desc "heroku:set", "Send Figaro configuration to Heroku"
8
+
9
+ method_option "app",
10
+ aliases: ["-a"],
11
+ desc: "Specify a Heroku app"
12
+ method_option "environment",
13
+ aliases: ["-e"],
14
+ desc: "Specify an application environment"
15
+ method_option "path",
16
+ aliases: ["-p"],
17
+ default: "config/application.yml",
18
+ desc: "Specify a configuration file path"
19
+
20
+ define_method "heroku:set" do
21
+ HerokuSet.run(options)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ require "figaro/cli/task"
2
+
3
+ module Figaro
4
+ class CLI < Thor
5
+ class HerokuSet < Task
6
+ def run
7
+ system(configuration, command)
8
+ end
9
+
10
+ private
11
+
12
+ def command
13
+ "heroku config:set #{vars} #{for_app}"
14
+ end
15
+
16
+ def for_app
17
+ options[:app] ? "--app=#{options[:app]}" : nil
18
+ end
19
+
20
+ def vars
21
+ configuration.keys.map { |k| var(k) }.join(" ")
22
+ end
23
+
24
+ def var(key)
25
+ Gem.win_platform? ? %(#{key}="%#{key}%") : %(#{key}="$#{key}")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
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 configuration
19
+ application.configuration
20
+ end
21
+
22
+ def application
23
+ @application ||= Figaro::Application.new(options)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,16 +1,45 @@
1
1
  module Figaro
2
- class Env < Hash
3
- def self.from(hash)
4
- new.replace(hash)
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
5
13
  end
6
14
 
15
+ private
16
+
7
17
  def method_missing(method, *)
8
- pair = ENV.detect { |k, _| k.upcase == method.to_s.upcase }
9
- pair ? pair[1] : super
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
10
26
  end
11
27
 
12
- def respond_to?(method, *)
13
- ENV.keys.any? { |k| k.upcase == method.to_s.upcase } || super
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("Missing required Figaro configuration key #{key.inspect}.")
38
+ end
39
+
40
+ def get_value(key)
41
+ _, value = ::ENV.detect { |k, _| k.downcase == key }
42
+ value
14
43
  end
15
44
  end
16
45
  end
@@ -0,0 +1,12 @@
1
+ module Figaro
2
+ class Error < StandardError; end
3
+
4
+ class RailsNotInitialized < Error; end
5
+ class MissingKey < Error; end
6
+
7
+ class MissingKeys < Error
8
+ def initialize(keys)
9
+ super("Missing required configuration keys: #{keys.inspect}")
10
+ end
11
+ end
12
+ 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