anyway_config 0.4.0 → 0.5.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +38 -0
  4. data/.travis.yml +18 -25
  5. data/CHANGELOG.md +21 -0
  6. data/Gemfile +11 -3
  7. data/LICENSE.txt +1 -1
  8. data/README.md +82 -47
  9. data/Rakefile +18 -5
  10. data/anyway_config.gemspec +15 -11
  11. data/config/cool.yml +5 -0
  12. data/gemfiles/rails42.gemfile +1 -2
  13. data/gemfiles/rails5.gemfile +6 -0
  14. data/gemfiles/railsmaster.gemfile +6 -0
  15. data/lib/anyway.rb +3 -5
  16. data/lib/anyway/config.rb +52 -31
  17. data/lib/anyway/env.rb +60 -61
  18. data/lib/anyway/ext/class.rb +18 -0
  19. data/lib/anyway/ext/deep_dup.rb +34 -0
  20. data/lib/anyway/ext/hash.rb +34 -0
  21. data/lib/anyway/rails/config.rb +16 -7
  22. data/lib/anyway/version.rb +5 -3
  23. data/lib/anyway_config.rb +3 -0
  24. data/spec/anyway.yml +1 -1
  25. data/spec/config_spec.rb +30 -19
  26. data/spec/config_spec_norails.rb +59 -16
  27. data/spec/dummy/config.ru +0 -4
  28. data/spec/dummy/config/application.rb +3 -12
  29. data/spec/dummy/config/cool.yml +1 -1
  30. data/spec/dummy/config/environments/test.rb +0 -37
  31. data/spec/dummy/config/routes.rb +0 -54
  32. data/spec/dummy/config/secrets.yml +0 -1
  33. data/spec/env_spec.rb +18 -14
  34. data/spec/ext/deep_dup_spec.rb +38 -0
  35. data/spec/spec_helper.rb +21 -9
  36. data/spec/spec_norails_helper.rb +18 -5
  37. data/spec/support/cool_config.rb +9 -3
  38. data/spec/support/test_config.rb +14 -3
  39. metadata +26 -80
  40. data/gemfiles/rails32.gemfile +0 -7
  41. data/gemfiles/rails40.gemfile +0 -7
  42. data/gemfiles/rails41.gemfile +0 -7
  43. data/spec/dummy.yml +0 -0
  44. data/spec/dummy/README.rdoc +0 -28
  45. data/spec/dummy/Rakefile +0 -6
  46. data/spec/dummy/app/assets/images/.keep +0 -0
  47. data/spec/dummy/app/assets/javascripts/application.js +0 -13
  48. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  49. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  50. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  51. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  52. data/spec/dummy/app/mailers/.keep +0 -0
  53. data/spec/dummy/app/models/.keep +0 -0
  54. data/spec/dummy/app/models/concerns/.keep +0 -0
  55. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  56. data/spec/dummy/bin/bundle +0 -3
  57. data/spec/dummy/bin/rails +0 -4
  58. data/spec/dummy/bin/rake +0 -4
  59. data/spec/dummy/config/environments/development.rb +0 -37
  60. data/spec/dummy/config/environments/production.rb +0 -83
  61. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  62. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  63. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  64. data/spec/dummy/config/initializers/inflections.rb +0 -16
  65. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  66. data/spec/dummy/config/initializers/session_store.rb +0 -3
  67. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  68. data/spec/dummy/config/locales/en.yml +0 -23
  69. data/spec/dummy/db/migrate/20140730133818_add_testos.rb +0 -11
  70. data/spec/dummy/db/migrate/20140731162044_add_column_to_testos.rb +0 -5
  71. data/spec/dummy/db/schema.rb +0 -22
  72. data/spec/dummy/lib/assets/.keep +0 -0
  73. data/spec/dummy/log/.keep +0 -0
  74. data/spec/dummy/public/404.html +0 -67
  75. data/spec/dummy/public/422.html +0 -67
  76. data/spec/dummy/public/500.html +0 -66
  77. data/spec/dummy/public/favicon.ico +0 -0
data/config/cool.yml ADDED
@@ -0,0 +1,5 @@
1
+ host: "test.host"
2
+ user:
3
+ name: "root"
4
+ password: "root"
5
+ port: <%= ENV['ANYWAY_COOL_PORT'] || 9292 %>
@@ -1,7 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'sqlite3', platform: :mri
4
- gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
3
+ gem 'sqlite3'
5
4
  gem 'rails', '~> 4.2.0'
6
5
 
7
6
  gemspec path: '..'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sqlite3'
4
+ gem 'rails', '~> 5.0'
5
+
6
+ gemspec path: '..'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'arel', github: 'rails/arel'
4
+ gem 'rails', github: 'rails/rails'
5
+
6
+ gemspec path: '..'
data/lib/anyway.rb CHANGED
@@ -1,9 +1,7 @@
1
- require 'active_support/core_ext/hash'
2
- require 'active_support/core_ext/string'
3
- require 'active_support/core_ext/object'
1
+ # frozen_string_literal: true
4
2
 
5
- require "anyway/version"
6
- module Anyway
3
+ module Anyway # :nodoc:
4
+ require "anyway/version"
7
5
  require "anyway/config"
8
6
  require "anyway/rails/config" if defined?(Rails)
9
7
  require "anyway/env"
data/lib/anyway/config.rb CHANGED
@@ -1,36 +1,48 @@
1
- module Anyway
1
+ # frozen_string_literal: true
2
+
3
+ require 'anyway/ext/class'
4
+ require 'anyway/ext/deep_dup'
5
+ require 'anyway/ext/hash'
6
+
7
+ module Anyway # :nodoc:
8
+ using Anyway::Ext::Class
9
+ using Anyway::Ext::DeepDup
10
+ using Anyway::Ext::Hash
11
+
12
+ # Base config class
13
+ # Provides `attr_config` method to describe
14
+ # configuration parameters and set defaults
2
15
  class Config
3
16
  class << self
4
17
  attr_reader :defaults, :config_attributes
5
18
 
6
- def attr_config(*args,hargs)
7
- @defaults = hargs.dup.with_indifferent_access
8
- @config_attributes = args+hargs.keys
9
- attr_accessor *@config_attributes
19
+ def attr_config(*args, **hargs)
20
+ @defaults = hargs.deep_dup
21
+ defaults.stringify_keys!
22
+ @config_attributes = args + defaults.keys
23
+ attr_accessor(*@config_attributes)
10
24
  end
11
25
 
12
26
  def config_name(val = nil)
13
27
  return (@config_name = val.to_s) unless val.nil?
14
- @config_name ||= extract_name
28
+ @config_name = underscore_name unless defined?(@config_name)
29
+ @config_name
15
30
  end
16
31
 
17
32
  # Load config as Hash by any name
18
33
  #
19
34
  # Example:
20
- #
21
- # my_config = Anyway::Config.for(:my_app)
22
- # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
35
+ #
36
+ # my_config = Anyway::Config.for(:my_app)
37
+ # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
23
38
  def for(name)
24
- self.new(name,false).load_from_sources
39
+ new(name, false).load_from_sources
25
40
  end
26
-
27
- private
28
- def extract_name
29
- name[/^(\w+)/].underscore
30
- end
31
41
  end
32
42
 
33
- def initialize(config_name=nil, do_load=true)
43
+ attr_reader :config_name
44
+
45
+ def initialize(config_name = nil, do_load = true)
34
46
  @config_name = config_name || self.class.config_name
35
47
  load if do_load
36
48
  end
@@ -39,7 +51,7 @@ module Anyway
39
51
  clear
40
52
  load
41
53
  self
42
- end
54
+ end
43
55
 
44
56
  def clear
45
57
  self.class.config_attributes.each do |attr|
@@ -49,37 +61,46 @@ module Anyway
49
61
  end
50
62
 
51
63
  def load
52
- config = load_from_sources self.class.defaults.deep_dup
53
- config.each do |key, val|
64
+ config = load_from_sources((self.class.defaults || {}).deep_dup)
65
+ config.each do |key, val|
54
66
  set_value(key, val)
55
67
  end
56
68
  end
57
69
 
58
- def load_from_sources(config={}.with_indifferent_access)
70
+ def load_from_sources(config = {})
71
+ # Handle anonymous configs
72
+ return config unless config_name
59
73
  load_from_file(config)
60
74
  load_from_env(config)
61
75
  end
62
76
 
63
77
  def load_from_file(config)
64
- config_path = (Anyway.env.send(@config_name)||{}).delete(:conf)
65
- if config_path and File.file?(config_path)
66
- require 'yaml'
67
- config.deep_merge! (YAML.load_file(config_path) || {})
78
+ config_path = Anyway.env.fetch(config_name).delete('conf') ||
79
+ "./config/#{config_name}.yml"
80
+ if config_path && File.file?(config_path)
81
+ config.deep_merge!(parse_yml(config_path) || {})
68
82
  end
69
83
  config
70
84
  end
71
-
85
+
72
86
  def load_from_env(config)
73
- config.deep_merge! (Anyway.env.send(@config_name) || {})
87
+ config.deep_merge!(Anyway.env.fetch(config_name))
74
88
  config
75
89
  end
76
90
 
77
91
  private
78
92
 
79
- # safe way to assing config value
80
- # checks that key exists in config
81
93
  def set_value(key, val)
82
- send("#{key}=", val) if self.class.config_attributes.include?(key.to_sym)
83
- end
94
+ send("#{key}=", val) if respond_to?(key)
95
+ end
96
+
97
+ def parse_yml(path)
98
+ require 'yaml'
99
+ if defined?(ERB)
100
+ YAML.safe_load(ERB.new(File.read(path)).result)
101
+ else
102
+ YAML.load_file(path)
103
+ end
104
+ end
84
105
  end
85
- end
106
+ end
data/lib/anyway/env.rb CHANGED
@@ -1,87 +1,86 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Anyway
4
+ # Parses environment variables and provides
5
+ # method-like access
2
6
  class Env
3
-
4
7
  # Regexp to detect array values
5
8
  # Array value is a values that contains at least one comma
6
- # and doesn't start/end with quote
7
-
9
+ # and doesn't start/end with quote
8
10
  ARRAY_RXP = /\A[^'"].*\s*,\s*.*[^'"]\z/
9
11
 
10
12
  def initialize
11
13
  @data = {}
12
- load
13
- end
14
-
15
- def reload
16
- clear
17
- load
18
- self
19
14
  end
20
15
 
21
16
  def clear
22
17
  @data.clear
23
- self
24
18
  end
25
19
 
26
- def method_missing(meth, *args, &block)
27
- meth = meth.to_s.gsub(/\_/,'')
28
- if args.empty? and @data.key?(meth)
29
- @data[meth]
30
- end
20
+ def fetch(config_name)
21
+ @data[config_name] ||= parse_env(config_name)
31
22
  end
32
23
 
33
24
  private
34
- def load
35
- ENV.each_pair do |key, val|
36
- if config_key?(key)
37
- mod, path = extract_module_path(key)
38
- set_by_path(get_hash(@data, mod), path, serialize_val(val))
39
- end
25
+
26
+ def parse_env(config_name)
27
+ config_env_name = config_name.to_s.delete("_")
28
+ config_env_name.upcase!
29
+ data = {}
30
+ ENV.each_pair do |key, val|
31
+ if key.start_with?(config_env_name)
32
+ _mod, path = extract_module_path(key)
33
+ set_by_path(data, path, serialize_val(val))
40
34
  end
41
35
  end
42
-
43
- def config_key?(key)
44
- key =~ /^[A-Z\d]+\_[A-Z\d\_]+/
45
- end
36
+ data
37
+ end
46
38
 
47
- def extract_module_path(key)
48
- _, mod, path = key.split(/^([^\_]+)/)
49
- path.sub!(/^[\_]+/,'')
50
- [mod.downcase, path.downcase]
51
- end
39
+ def config_key?(key)
40
+ key =~ /^[A-Z\d]+\_[A-Z\d\_]+/
41
+ end
52
42
 
53
- def set_by_path(to, path, val)
54
- parts = path.split("__")
43
+ def extract_module_path(key)
44
+ _, mod, path = key.split(/^([^\_]+)/)
45
+ path.sub!(/^[\_]+/, '')
46
+ [mod.downcase, path.downcase]
47
+ end
55
48
 
56
- while parts.length > 1
57
- to = get_hash(to, parts.shift)
58
- end
59
- to[parts.first] = val
60
- end
49
+ def set_by_path(to, path, val)
50
+ parts = path.split("__")
61
51
 
62
- def get_hash(from, name)
63
- (from[name] ||= {}.with_indifferent_access)
64
- end
52
+ to = get_hash(to, parts.shift) while parts.length > 1
65
53
 
66
- def serialize_val(value)
67
- case value
68
- when ARRAY_RXP
69
- value.split(/\s*,\s*/).map(&method(:serialize_val))
70
- when /\A(true|t|yes|y)\z/i
71
- true
72
- when /\A(false|f|no|n)\z/i
73
- false
74
- when /\A(nil|null)\z/i
75
- nil
76
- when /\A\d+\z/
77
- value.to_i
78
- when /\A\d*\.\d+\z/
79
- value.to_f
80
- when /\A['"].*['"]\z/
81
- value.gsub(/(\A['"]|['"]\z)/,'')
82
- else
83
- value
84
- end
54
+ to[parts.first] = val
55
+ end
56
+
57
+ def get_hash(from, name)
58
+ (from[name] ||= {})
59
+ end
60
+
61
+ # rubocop:disable Metrics/MethodLength
62
+ # rubocop:disable Metrics/CyclomaticComplexity
63
+ def serialize_val(value)
64
+ case value
65
+ when ARRAY_RXP
66
+ value.split(/\s*,\s*/).map(&method(:serialize_val))
67
+ when /\A(true|t|yes|y)\z/i
68
+ true
69
+ when /\A(false|f|no|n)\z/i
70
+ false
71
+ when /\A(nil|null)\z/i
72
+ nil
73
+ when /\A\d+\z/
74
+ value.to_i
75
+ when /\A\d*\.\d+\z/
76
+ value.to_f
77
+ when /\A['"].*['"]\z/
78
+ value.gsub(/(\A['"]|['"]\z)/, '')
79
+ else
80
+ value
85
81
  end
82
+ end
83
+ # rubocop:enable Metrics/MethodLength
84
+ # rubocop:enable Metrics/CyclomaticComplexity
86
85
  end
87
- end
86
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Ext
5
+ # Extend String through refinements
6
+ module Class
7
+ refine ::Class do
8
+ def underscore_name
9
+ return unless name
10
+ word = name[/^(\w+)/]
11
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
12
+ word.downcase!
13
+ word
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Ext
5
+ # Extend Object through refinements
6
+ module DeepDup
7
+ refine ::Hash do
8
+ # Based on ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_dup
9
+ def deep_dup
10
+ each_with_object(dup) do |(key, value), hash|
11
+ hash[key] = if value.is_a?(::Hash) || value.is_a?(::Array)
12
+ value.deep_dup
13
+ else
14
+ value
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ refine ::Array do
21
+ # From ActiveSupport http://api.rubyonrails.org/classes/Array.html#method-i-deep_dup
22
+ def deep_dup
23
+ map do |value|
24
+ if value.is_a?(::Hash) || value.is_a?(::Array)
25
+ value.deep_dup
26
+ else
27
+ value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Ext
5
+ # Extend Hash through refinements
6
+ module Hash
7
+ refine ::Hash do
8
+ # From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_merge
9
+ def deep_merge!(other_hash)
10
+ other_hash.each_pair do |current_key, other_value|
11
+ this_value = self[current_key]
12
+
13
+ if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
14
+ this_value.deep_merge!(other_value)
15
+ this_value
16
+ else
17
+ self[current_key] = other_value
18
+ end
19
+ end
20
+
21
+ self
22
+ end
23
+
24
+ def stringify_keys!
25
+ keys.each do |key|
26
+ value = delete(key)
27
+ value.stringify_keys! if value.is_a?(::Hash)
28
+ self[key.to_s] = value
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,25 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Anyway
2
- class Config
3
- def load_from_sources(config={}.with_indifferent_access)
4
+ class Config # :nodoc:
5
+ class << self
6
+ def defaults
7
+ return unless @defaults
8
+ @defaults_wia ||= @defaults.with_indifferent_access
9
+ end
10
+ end
11
+
12
+ def load_from_sources(config = {})
13
+ config = config.with_indifferent_access
4
14
  load_from_file(config)
5
15
  load_from_secrets(config)
6
16
  load_from_env(config)
7
17
  end
8
18
 
9
19
  def load_from_file(config)
10
- config_path = Rails.root.join("config","#{@config_name}.yml")
20
+ config_path = Rails.root.join("config", "#{@config_name}.yml")
11
21
  if File.file? config_path
12
- require 'yaml'
13
- config.deep_merge! (YAML.load_file(config_path)[Rails.env] || {})
22
+ config.deep_merge!(parse_yml(config_path)[Rails.env] || {})
14
23
  end
15
24
  config
16
25
  end
17
26
 
18
27
  def load_from_secrets(config)
19
28
  if Rails.application.respond_to?(:secrets)
20
- config.deep_merge! (Rails.application.secrets.send(@config_name)||{})
29
+ config.deep_merge!(Rails.application.secrets.send(@config_name) || {})
21
30
  end
22
31
  config
23
32
  end
24
33
  end
25
- end
34
+ end