app_config_for 0.0.4.1 → 0.0.6.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 228991a28438bec90c4aa9f06b33850a5ee4034b01a7ec87a33c90fa77ace37d
4
- data.tar.gz: eed01754e58c59c3ea8d58c23e809f8d9c3ae1b6fe5a3b24958a1a173f01a87c
3
+ metadata.gz: 7f85a6a8ca8b5be3a0fcbccc544519908b2f257760199a6f49c06a3100824692
4
+ data.tar.gz: 6de1a37f3f917450a3214fd4ba98e70d5ce2eed0485933470cc2e072b1f89e0e
5
5
  SHA512:
6
- metadata.gz: d05a93814a9b8162890151e89f2e83e99f29c58478e371aeef49c63c29713a01d1c86ab3b4a28ee320d2eb639dd51879d107dd820c9f173c3bae958c626396b9
7
- data.tar.gz: b9dc3d8224b2e0c05167da273faf7df940c1397022d22768af8cce85101de32993a56cc2e13bca5b2d8cb656e7885251a962486856388ad1871be9163784816e
6
+ metadata.gz: 4d3810ce467896ea6d8165a8a313066c2d2d8947c07e39843bdc7886ad072e6eef29ea309d2505a67b902b2d3b0a14fdde8025a0c00c6d26092583b6fe505cd5
7
+ data.tar.gz: 3b6c650d56e7624e673d4a840d24970e9428744478c7ac9399f398bdd6fc39ac92ad1470cb6209bae26e17c2eebfa0c200be30d8823990c79257348508b0c573
data/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  ## [Alpha Release]
2
2
 
3
- ## [0.0.1] - 2022-02-18
3
+ ## [0.0.1] - 2022-04-07
4
4
 
5
5
  - Initial release
6
+
7
+ ## [0.0.2] - 2022-04-12
8
+
9
+ - Added prefix inheritance.
10
+ - Initial inclusion vs extension support.
11
+ - Refactored some internal behaviors.
12
+
13
+ ## [0.0.3] - 2022-04-12
14
+
15
+ - More internal refactoring and miscellaneous bug fixes.
16
+
17
+ ## [0.0.4] - 2022-04-14
18
+
19
+ - Reduced minimal ActiveSupport version requirements to 5.0.
20
+ - Added a legacy support shim for older versions of ActiveSupport.
21
+ - Multiple configuration directory support.
22
+ - Single configuration name override support.
23
+ - Automatically prep gems consumers with the ability to ship with a default configuration.
24
+
25
+ ## [0.0.4.1] - 2022-04-15
26
+
27
+ - Reduced minimal Ruby version requirements to 2.3.6
28
+ - Bug fix: active_support/configuration_file was still being requested on older ActiveSupport installations.
29
+
30
+ ## [0.0.5] - 2022-04-25
31
+
32
+ - Full Yard documentation.
33
+ - Multiple config directories for file searching can be added at one time.
34
+ - Small updates to initializations.
35
+ - Started specs.
36
+ - Fixed bug in progenitor_prefixes_of that prevented rails and rack prefixes from being used.
37
+ - additional_config_directories duped by default to prevent accidental changes.
38
+ - add_config_directory now converts its argument to a string via #to_s.
39
+
40
+ ## [0.0.6] - 2022-04-25
41
+
42
+ - Reading and setting configuration values can now be done directly on the extending class/module.
43
+ - Documentation updates.
44
+ - Fallback configuration support.
45
+ - Requesting configuration for another object that can supply it's own config_files will use those files instead of locally determining them.
46
+
47
+ ## [0.0.6.1] - 2022-04-25
48
+ - Fixed issue when initializing env_prefixes in a namespaced module/class.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # AppConfigFor
1
+ # AppConfigFor [![Gem Version](https://badge.fury.io/rb/app_config_for.svg)](https://badge.fury.io/rb/app_config_for)
2
2
 
3
3
  Ruby gem providing Rails::Application#config_for style capabilities for non-rails applications, gems, and rails engines.
4
4
 
@@ -20,54 +20,52 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
  Presume a typical rails database config at ./config/database.yml
23
- One environment variable ('MY_APP_ENV', 'RAILS_ENV', or 'RACK_ENV') is set to 'development'
23
+ One environment variable ('MY_APP_ENV', 'RAILS_ENV', or 'RACK_ENV') is set to 'development' or all are non existent.
24
24
 
25
- #### ./config/my_app.yml
25
+ #### ./config/sample_app.yml
26
26
  ```yml
27
27
  default: &default
28
- site: <%= ENV.fetch("MY_APP_SITE", 'www.slackware.com') %>
29
- password: Slackware#1!
28
+ site: <%= ENV.fetch("MY_APP_SITE", 'www.slackware.com') %>
29
+ password: Slackware#1!
30
30
 
31
31
  development:
32
- <<: *default
33
- username: Linux
32
+ <<: *default
33
+ username: Linux
34
34
 
35
35
  test:
36
- <<: *default
37
- username: TestingWith
36
+ <<: *default
37
+ username: TestingWith
38
38
 
39
39
  production:
40
- <<: *default
41
- username: DefinitelyUsing
40
+ <<: *default
41
+ username: DefinitelyUsing
42
42
 
43
43
  shared:
44
- color: 'Blue'
44
+ color: 'Blue'
45
45
  ```
46
46
 
47
- #### ./my_class.rb
47
+ #### sample_application.rb
48
48
  ```ruby
49
49
  require 'app_config_for'
50
50
 
51
- module MyApp
52
-
51
+ module Sample
52
+ class App
53
53
  extend AppConfigFor
54
-
55
- class MyClass
56
- def info
57
- puts "Curent environment is #{MyApp.env}"
58
-
59
- puts "Remote Host: #{MyApp.configured.site}"
60
-
61
- # Can access same configuration in other ways
62
- puts "Username: MyApp.config_for(:my_app)[:username]"
63
- puts "Password: MyApp.config_for(MyApp).username"
64
-
65
- # Access a different config
66
- if MyApp.config_file?(:database)
67
- puts "Rails database config: MyApp.config_for(:database)"
68
- end
69
- end
54
+ def info
55
+ puts "Current environment is #{App.env}"
56
+
57
+ puts "Remote Host: #{App.configured.site}"
58
+
59
+ # Can access same configuration in other ways
60
+ puts "Username: self.class.config_for(:app)[:username]"
61
+ puts "Password: App.config_for(App).username"
62
+
63
+ # Access a different config
64
+ if App.config_file?(:database)
65
+ puts "Rails database config: App.config_for(:database)"
66
+ end
70
67
  end
68
+ end
71
69
  end
72
70
  ```
73
71
 
@@ -1,9 +1,15 @@
1
1
  module AppConfigFor
2
+ # Base class for all errors generated by AppConfigFor
2
3
  class Error < StandardError; end
3
4
 
5
+ # Raised when the configuration could not be found.
4
6
  class ConfigNotFound < Error
5
7
 
6
- attr_reader :locations_searched, :original_exception
8
+ # The full path of every place the configuration file was looked for.
9
+ attr_reader :locations_searched
10
+ # The underlying SystemCallError that signaled the missing file.
11
+ # Most commonly this will be +Errno::ENOENT+.
12
+ attr_reader :original_exception
7
13
 
8
14
  def initialize(locations, original_exception)
9
15
  @locations_searched = Array(locations).map { |x| Pathname(x).expand_path }
@@ -12,9 +18,14 @@ module AppConfigFor
12
18
  end
13
19
  end
14
20
 
21
+ # Raised when there was an issue parsing the configuration file.
15
22
  class LoadError < Error
16
23
 
17
- attr_reader :file, :original_exception
24
+ # The file that was being parsed.
25
+ attr_reader :file
26
+ # The original exception that signaled the parsing problme.
27
+ # Most commonly this will be a +Psych::SyntaxError+
28
+ attr_reader :original_exception
18
29
 
19
30
  def initialize(file, original_exception)
20
31
  @file = Pathname(file).expand_path
@@ -23,13 +34,17 @@ module AppConfigFor
23
34
  end
24
35
  end
25
36
 
37
+ # Raised when an attempting to utilize an unrecognized inheritance style.
26
38
  class InvalidEnvInheritanceStyle < Error
27
39
 
28
- attr_reader :attempted, :valid
40
+ # The inheritance style that was attempted to be used.
41
+ attr_reader :attempted
42
+ # List of valid styles at the time of the attempt.
43
+ attr_reader :valid
29
44
 
30
45
  def initialize(attempted)
31
46
  @attempted = attempted
32
- @valid = EnvPrefixInheritanceStyles.dup
47
+ @valid = EnvInheritanceStyles.dup
33
48
  super "Invalid inheritance style #{@attempted.inspect}. Please use one of the following: #{@valid.map(&:inspect).join(', ')}"
34
49
  end
35
50
 
@@ -1,16 +1,20 @@
1
1
  module AppConfigFor
2
2
 
3
+ # Current version of this gem with comparable values.
4
+ # @return [Gem::Version]
3
5
  def self.gem_version
4
6
  Gem::Version.new(VERSION::STRING)
5
7
  end
6
8
 
9
+ # The rendition
7
10
  module VERSION
8
- MAJOR = 0
9
- MINOR = 0
10
- TINY = 4
11
- PRE = 1
11
+ MAJOR = 0 # A field-grade officer
12
+ MINOR = 0 # When the semitones show up as intervals between the 2nd and 3rd degrees
13
+ TINY = 6 # The number of people who use antidisestablishmentarianism in everyday conversation
14
+ PRE = 1 # Ante not auntie
12
15
 
13
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
16
+ # String form of the version (duh). Are you seriously reading this? I guess it is slightly more interesting that Moby-Dick.
17
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
14
18
  end
15
19
 
16
20
  end
@@ -9,9 +9,19 @@ if ActiveSupport.gem_version < Gem::Version.new('6.1.0')
9
9
  require "erb"
10
10
  require "yaml"
11
11
 
12
+ # @note This legacy support is only included if the existing ActiveSupport version is below 6.1.0.
13
+ # If your application is using any version of ActiveSupport below 6.1.4 it is *strongly* suggested you upgrade your application due to security and bug fixes.
14
+ # This backwards compatability is only enough to support AppConfigFor and is not to be considered a full backport of existing features.
15
+ # This support will be removed in the future.
12
16
  module ActiveSupport
17
+
18
+ # @note This legacy support is only included if the existing ActiveSupport version is below 6.1.0.
19
+ # If your application is using any version of ActiveSupport below 6.1.4 it is *strongly* suggested you upgrade your application due to security and bug fixes.
20
+ # This backwards compatability is only enough to support AppConfigFor and is not to be considered a full backport of existing features.
21
+ # This support will be removed in the future.
13
22
  class EnvironmentInquirer < StringInquirer
14
23
 
24
+ # Default environments
15
25
  Environments = %w(development test production)
16
26
 
17
27
  def initialize(env)
@@ -22,6 +32,10 @@ if ActiveSupport.gem_version < Gem::Version.new('6.1.0')
22
32
  Environments.each { |e| define_method("#{e}?") { instance_variable_get("@#{e}") }}
23
33
  end
24
34
 
35
+ # @note This legacy support is only included if the existing ActiveSupport version is below 6.1.0.
36
+ # If your application is using any version of ActiveSupport below 6.1.4 it is *strongly* suggested you upgrade your application due to security and bug fixes.
37
+ # This backwards compatability is only enough to support AppConfigFor and is not to be considered a full backport of existing features.
38
+ # This support will be removed in the future.
25
39
  class ConfigurationFile
26
40
  def initialize(file_name)
27
41
  @file_name = file_name
@@ -29,10 +43,12 @@ if ActiveSupport.gem_version < Gem::Version.new('6.1.0')
29
43
  warn(file_name + ' contains invisible non-breaking spaces.') if @config.match?("\u00A0")
30
44
  end
31
45
 
46
+ # Quick and dirty parse
32
47
  def self.parse(file_name)
33
48
  new(file_name).parse
34
49
  end
35
50
 
51
+ # Quick and dirty parse
36
52
  def parse
37
53
  YAML.load(ERB.new(@config).result) || {}
38
54
  rescue Psych::SyntaxError => e
@@ -3,6 +3,9 @@ require_relative "gem_version"
3
3
 
4
4
  module AppConfigFor
5
5
 
6
+ # Current version of this gem.
7
+ # @return [Gem::Version]
8
+ # @see gem_version
6
9
  def self.version
7
10
  gem_version
8
11
  end
@@ -16,55 +16,251 @@ require 'active_support/core_ext/string/inflections'
16
16
  require 'active_support/core_ext/hash/indifferent_access'
17
17
  require 'active_support/ordered_options'
18
18
  require 'active_support/core_ext/object/try'
19
-
19
+ require 'active_support/backtrace_cleaner'
20
+
21
+ # {<img src="https://badge.fury.io/rb/app_config_for.svg" alt="Gem Version" />}[https://badge.fury.io/rb/app_config_for]
22
+ #
23
+ # Ruby gem providing Rails::Application#config_for style capabilities for non-rails applications, gems, and rails engines.
24
+ # It respects RAILS_ENV and RACK_ENV while providing additional capabilities beyond Rails::Application#config_for.
25
+ #
26
+ # = Usage
27
+ # Typical usage will be done by extension but inclusion is also supported.
28
+ #
29
+ # Presume a typical rails database config at ./config/database.yml
30
+ #
31
+ # One environment variable ('MY_APP_ENV', 'RAILS_ENV', or 'RACK_ENV') is set to 'development' or all are non existent.
32
+ #
33
+ # ==== ./config/sample_app.yml
34
+ # default: &default
35
+ # site: <%= ENV.fetch("MY_APP_SITE", 'www.slackware.com') %>
36
+ # password: Slackware#1!
37
+ #
38
+ # development:
39
+ # <<: *default
40
+ # username: Linux
41
+ #
42
+ # test:
43
+ # <<: *default
44
+ # username: TestingWith
45
+ #
46
+ # production:
47
+ # <<: *default
48
+ # username: DefinitelyUsing
49
+ #
50
+ # shared:
51
+ # color: 'Blue'
52
+ #
53
+ # === sample_application.rb
54
+ # require 'app_config_for'
55
+ #
56
+ # module Sample
57
+ # class App
58
+ # extend AppConfigFor
59
+ # def info
60
+ # puts "Current environment is #{App.env}"
61
+ #
62
+ # # Access the configuration in various ways depending on need/preference.
63
+ # puts "Remote Host: #{App.site}"
64
+ # puts "Username: #{App.configured.username}"
65
+ # puts "Password: #{App.config_for(App).password}"
66
+ # puts "Domain: #{self.class.config_for(:app)[:domain]}"
67
+ #
68
+ # # Access a different config
69
+ # if App.config_file?(:database)
70
+ # adapter_name = App.config_for(:database).adapter
71
+ # puts "Rails is using the #{adapter_name} adapter."
72
+ # end
73
+ # end
74
+ # end
75
+ # end
76
+ #
20
77
  module AppConfigFor
21
-
22
- EnvPrefixInheritanceStyles = %i(none namespace class namespace_class class_namespace)
23
-
78
+ # Types of hierarchical traversal used to determine the runtime environment.
79
+ # * +:none+ - No inheritance active
80
+ # * +:namespace+ - Inheritance by lexical namespace
81
+ # * +:class+ - Inheritance by class hierarchy
82
+ # * +:namespace_class+ - Namespace inheritance combined with class inheritance
83
+ # * +:class_namespace+ - Class inheritance combined with namespace inheritance
84
+ EnvInheritanceStyles = [:none, :namespace, :class, :namespace_class, :class_namespace]
85
+
86
+ # AppConfigFor can be included instead of extended. If this occurs, instances of the class will have their
87
+ # own list of prefixes. The default class prefix will be automatically added to the list.
24
88
  def initialize(*args)
25
89
  add_env_prefix
26
90
  super
27
91
  end
28
92
 
29
- def add_env_prefix(prefix = nil, at_beginning = true)
30
- env_prefixes(false, false).send(at_beginning ? :unshift : :push, AppConfigFor.prefix_from(prefix || self)).uniq!
93
+ # Add multiple additional directories to be used when searching for the config file.
94
+ # Any duplicates will be ignored.
95
+ # @param additional_directories [Array<#to_s>] additional directories to add
96
+ # @param at_beginning [Boolean] where to insert the new directories with respect to existing prefixes
97
+ # * +true+ - Add to the beginning of the list.
98
+ # * +false+ - Add to the end of the list.
99
+ # @return [Array<Pathname>] updated array of additional config directories
100
+ # @see #additional_config_directories Additional config directories
101
+ # @see #config_directories All config directories
102
+ def add_config_directories(*additional_directories, at_beginning: true)
103
+ additional_directories = additional_directories.flatten.map { |d| Pathname.new(d.to_s) }
104
+ directories = additional_config_directories(false).send(at_beginning ? :unshift : :push, additional_directories)
105
+ directories.flatten!
106
+ directories.uniq!
107
+ directories.dup
108
+ end
109
+
110
+ # Add an single additional directory to be used when searching for the config file.
111
+ # Any duplicates will be ignored.
112
+ # @param additional_directory [#to_s] additional directory to add
113
+ # @param at_beginning [Boolean] where to insert the new directory with respect to existing prefixes
114
+ # * +true+ - Add to the beginning of the list.
115
+ # * +false+ - Add to the end of the list.
116
+ # @return [Array<Pathname>] updated array of additional config directories
117
+ # @see #additional_config_directories Additional config directories
118
+ # @see #config_directories All config directories
119
+ def add_config_directory(additional_directory, at_beginning = true)
120
+ add_config_directories additional_directory, at_beginning: at_beginning
121
+ end
122
+
123
+ # Add an additional base name to be used when locating the config file.
124
+ # @param config_name [Object] Object to extract a config name from.
125
+ # @param at_beginning [Boolean] where to insert the new config name with respect to existing names.
126
+ # * +true+ - Add to the beginning of the list.
127
+ # * +false+ - Add to the end of the list.
128
+ # @return [Array<Object>] current config names
129
+ # @see yml_name_from How the name of the yml file is determined
130
+ def add_config_name(config_name, at_beginning = true)
131
+ add_config_names config_name, at_beginning: at_beginning
132
+ end
133
+
134
+ # Add multiple additional base names to be used when locating the config file.
135
+ # @param config_names [Array<Object>] Array of objects to extract a config name from.
136
+ # @param at_beginning [Boolean] where to insert the new config names with respect to existing names.
137
+ # * +true+ - Add to the beginning of the list.
138
+ # * +false+ - Add to the end of the list.
139
+ # @return [Array<Object>] current config names
140
+ # @see yml_name_from How the name of the yml file is determined
141
+ def add_config_names(*config_names, at_beginning: true)
142
+ names = config_names(false).send(at_beginning ? :unshift : :push, config_names)
143
+ names.flatten!
144
+ names.uniq!
145
+ names.dup
31
146
  end
32
147
 
33
- def add_config_directory(new_directory)
34
- (additional_config_directories << Pathname.new(new_directory).expand_path).uniq!
148
+ # Add an additional environmental prefix to be used when determining current environment.
149
+ # @param prefix [Symbol, Object] Prefix to add.
150
+ # +nil+ is treated as +self+
151
+ # Non symbols are converted via {.prefix_from AppConfigFor.prefix_from}.
152
+ # @param at_beginning [Boolean] where to insert the new prefix with respect to existing prefixes
153
+ # * +true+ - Add to the beginning of the list.
154
+ # * +false+ - Add to the end of the list.
155
+ # @return [Array<Symbol>] Current prefixes (without inheritance)
156
+ # @see #env_prefixes Current prefixes
157
+ def add_env_prefix(prefix = nil, at_beginning = true)
158
+ env_prefixes(false, false).send(at_beginning ? :unshift : :push, AppConfigFor.prefix_from(prefix || self)).uniq!
159
+ env_prefixes(false)
35
160
  end
36
161
 
37
- def additional_config_directories
162
+ # Directories to be checked in addition to the defaults when searching for the config file.
163
+ # @param dup [Boolean] Return a duplicated array to prevent accidental side effects
164
+ # @return [Array<Pathname>]
165
+ # @see #add_config_directory Adding config directories
166
+ # @see #config_directories All config directories
167
+ def additional_config_directories(dup = true)
38
168
  @additional_config_directories ||= []
169
+ dup ? @additional_config_directories.dup : @additional_config_directories
39
170
  end
40
171
 
172
+ # Clear all additional config directories and set to the directory given.
173
+ # @param directory [#to_s] additional directory to use
174
+ # @return [Array<Pathname>] updated array of additional config directories
175
+ # @see #additional_config_directories Additional config directories
176
+ # @see #config_directories All config directories
177
+ def config_directory=(directory)
178
+ additional_config_directories(false).clear
179
+ add_config_directory(directory)
180
+ end
181
+ alias_method :config_directories=, :config_directory=
182
+
183
+ # All directories that will be used when searching for the config file. Search order is as follows:
184
+ # 1. Rails configuration directories if Rails is present.
185
+ # 2. Engine configuration directories if extended by an engine.
186
+ # 3. Additional configuration directories.
187
+ # 4. ./config within the current working directory.
188
+ # All paths are expanded at time of call.
189
+ # @return [Array<Pathname>] directories in the order they will be searched.
190
+ # @see #add_config_directory Adding config directories
41
191
  def config_directories
42
192
  directories = ['Rails'.safe_constantize&.application&.paths, try(:paths)].compact.map { |root| root["config"].existent.first }.compact
43
- directories.map! { |directory| Pathname.new(directory).expand_path }
44
193
  directories.concat additional_config_directories
45
194
  directories.push(Pathname.getwd + 'config')
46
- directories.uniq
195
+ directories.map { |directory| Pathname.new(directory).expand_path }.uniq
47
196
  end
48
197
 
49
- def config_file(name = nil)
198
+ # Configuration file that will be used.
199
+ # This is the first file from {#config_files} that exists or +nil+ if none exists.
200
+ # @param name [Symbol, Object] Name of the config to load.
201
+ # Conversion to a file name will occur using {.yml_name_from AppConfigFor.yml_name_from}.
202
+ # If name is +nil+ {#config_names} will be used.
203
+ # @param fallback [Symbol, Object] If not +nil+, attempt to load a fallback configuration if the requested one cannot be found.
204
+ # @return [Pathname, nil]
205
+ def config_file(name = nil, fallback = nil)
50
206
  unless name.is_a?(Pathname)
51
207
  config_files(name).find(&:exist?)
52
208
  else
53
209
  name.exist? ? name.expand_path : nil
54
- end
210
+ end.yield_self { |file| file || fallback && config_file(fallback) }
55
211
  end
56
212
 
213
+ # The list of potential config files that will be searched for and the order in which they will be searched.
214
+ # @param name [Symbol, Object] Name of the config to load.
215
+ # Conversion to a file name will occur using {.yml_name_from AppConfigFor.yml_name_from}.
216
+ # If name is +nil+, {#config_names} will be used.
217
+ # If name is object that responds to +config_files+, it will be called instead.
218
+ # @return [Array<Pathname>]
57
219
  def config_files(name = nil)
58
- name = AppConfigFor.yml_name_from(name || config_name)
59
- config_directories.map { |directory| directory + name }
220
+ if name.respond_to?(:config_files) && name != self
221
+ name.config_files
222
+ else
223
+ names = (name && name != self && Array(name) || config_names).map { |name| AppConfigFor.yml_name_from(name) }
224
+ config_directories.map { |directory| names.map { |name| directory + name } }.flatten
225
+ end
60
226
  end
61
227
 
62
- def config_file?(name = nil)
63
- !config_file(name).blank?
228
+ # Does a config file exit?
229
+ # @param name [Symbol, Object] Name of the config to load.
230
+ # Conversion to a file name will occur using {.yml_name_from AppConfigFor.yml_name_from}.
231
+ # If name is +nil+ {#config_names} will be used.
232
+ # @param fallback [Symbol, Object] If not +nil+, attempt to load a fallback configuration if the requested one cannot be found.
233
+ # @return [Boolean]
234
+ def config_file?(name = nil, fallback = nil)
235
+ !config_file(name, fallback).blank?
64
236
  end
65
237
 
66
- def config_for(name, env: nil)
67
- config, shared = config_options(name).fetch_values((env || self.env).to_sym, :shared) {nil}
238
+ # Configuration settings for the current environment.
239
+ # Shared sections in the yml config file are automatically merged into the returned configuration.
240
+ # @param name [Symbol, Object] Name of the config to load.
241
+ # Conversion to a file name will occur using {.yml_name_from AppConfigFor.yml_name_from}.
242
+ # If name is +nil+ {#config_names} will be used.
243
+ # @param env [Symbol, String] name of environment to use. +nil+ will use the current environment settings from {#env}
244
+ # @param fallback [Symbol, Object] If not +nil+, attempt to load a fallback configuration if the requested one cannot be found.
245
+ # @return [ActiveSupport::OrderedOptions]
246
+ # @raise ConfigNotFound - No configuration file could be located.
247
+ # @raise LoadError - A configuration file was found but could not be properly read.
248
+ # @see yml_name_from How the name of the yml file is determined
249
+ # @see #env The current runtime environment
250
+ # @example
251
+ # config_for(:my_app) # Load my_app.yml and extract the section relative to the current environment.
252
+ # config_for(:my_app).log_level # Get the configured logging level from my_app.yml for the current environment.
253
+ # config_for("MyApp", env: 'test') # Load my_app.yml and extract the 'test' section.
254
+ #
255
+ # module Other
256
+ # class App
257
+ # end
258
+ # end
259
+ # # Load other_app.yml and extract the 'production' section.
260
+ # # Notice that Other::App does not need to extend AppConfigFor
261
+ # config_for(Other::App, env: :production)
262
+ def config_for(name, env: nil, fallback: nil)
263
+ config, shared = config_options(name, fallback).fetch_values((env || self.env).to_sym, :shared) { nil }
68
264
  config ||= shared
69
265
 
70
266
  if config.is_a?(Hash)
@@ -75,43 +271,201 @@ module AppConfigFor
75
271
  config
76
272
  end
77
273
 
78
- def config_name
79
- @config_name ||= self
274
+ # Clear all config names and set to the name given.
275
+ # Set the base name of the config file to use.
276
+ # @param new_config_name [Object] Any object. Actual name will be determined using {.yml_name_from AppConfigFor.yml_name_for}
277
+ # @return [Array<Object>] current config names
278
+ # @see yml_name_from How the name of the yml file is determined
279
+ def config_name=(new_config_name)
280
+ config_names(false).clear
281
+ add_config_names(new_config_name)
80
282
  end
283
+ alias_method :config_names=, :config_name=
81
284
 
82
- def config_name=(new_config_name)
83
- @config_name = new_config_name
285
+ # Base names of the configuration file.
286
+ # Defaults to: +[self]+
287
+ def config_names(dup = true)
288
+ @config_names ||= [self]
289
+ dup ? @config_names.dup : @config_names
84
290
  end
85
291
 
86
- def config_options(name = nil)
87
- file = name.is_a?(Pathname) ? name : config_file(name)
88
- ActiveSupport::ConfigurationFile.parse(file.to_s).deep_symbolize_keys
292
+ # Configuration for all environments parsed from the {#config_file}.
293
+ # @param name [Symbol, Object] Name of the config to load.
294
+ # Conversion to a file name will occur using {.yml_name_from AppConfigFor.yml_name_from}.
295
+ # If name is +nil+ {#config_names} will be used.
296
+ # @param fallback [Symbol, Object] If not +nil+, attempt to load a fallback configuration if the requested one cannot be found.
297
+ # @return [Hash]
298
+ # @raise ConfigNotFound - No configuration file could be located.
299
+ # @raise LoadError - A configuration file was found but could not be properly read.
300
+ def config_options(name = nil, fallback = nil)
301
+ file = config_file(name, fallback).to_s
302
+ ActiveSupport::ConfigurationFile.parse(file).deep_symbolize_keys
89
303
  rescue SystemCallError => exception
90
- raise ConfigNotFound.new(name.is_a?(Pathname) ? name : config_files(name), exception)
304
+ locations = name.is_a?(Pathname) ? Array(name) : config_files(name)
305
+ locations += config_files(fallback) if fallback
306
+ raise ConfigNotFound.new(locations, exception)
91
307
  rescue => exception
92
308
  raise file ? LoadError.new(file, exception) : exception
93
309
  end
94
310
 
95
- def configured(reload = false, env: nil)
96
- @configured = config_for(nil, env: env) if reload || @configured.nil?
311
+ # Convenience method for {config_for}(+self+). Caches the result for faster access.
312
+ # @param reload [Boolean] Update the cached config by rereading the configuration file.
313
+ # @return [ActiveSupport::OrderedOptions]
314
+ # @raise ConfigNotFound - No configuration file could be located.
315
+ # @raise LoadError - A configuration file was found but could not be properly read.
316
+ # @example
317
+ # module Sample
318
+ # class App
319
+ # extend AppConfigFor
320
+ # @@logger = Logger.new($stdout, level: configured.level)
321
+ # end
322
+ # end
323
+ # Sample::App.configured.url # Get the configured url from my_app.yml for the current environment
324
+ # @see #method_missing Accessing configuration values directly from the extending class/module
325
+ def configured(reload = false)
326
+ if reload || !@configured
327
+ # @disable_local_missing = true # Disable local method missing to prevent recursion
328
+ @configured = config_for(nil, env: env(reload))
329
+ # @disable_local_missing = false # Reenable local method missing since no exception occurred.
330
+ end
97
331
  @configured
98
332
  end
99
333
 
334
+ # Convenience method for {configured}(+true+).
335
+ # @return [ActiveSupport::OrderedOptions]
336
+ # @raise ConfigNotFound - No configuration file could be located.
337
+ # @raise LoadError - A configuration file was found but could not be properly read.
338
+ # @example
339
+ # module Sample
340
+ # class App
341
+ # extend AppConfigFor
342
+ # mattr_accessor :logger, default: Logger.new($stdout)
343
+ # logger.level = configured.log_level
344
+ # end
345
+ # end
346
+ # # Switch to production
347
+ # ENV['SAMPLE_APP_ENV'] = 'production'
348
+ # # Update the log level with the production values
349
+ # Sample::App.logger.level = Sample::App.configured!.log_level
350
+ def configured!
351
+ configured(true)
352
+ end
353
+
354
+ # Check for the existence of a configuration setting. Handles exceptions and recursion.
355
+ # @param key [#to_s] Key to check for
356
+ # @return [Boolean]
357
+ # * +true+ - Configuration has the key
358
+ # * +false+ - If one of the following:
359
+ # 1. Configuration does not have the key
360
+ # 2. Called recursively while retrieving the configuration
361
+ # 3. An exception is raised while retrieving the configuration
362
+ # @note This is primarily used internally during {#respond_to_missing?} and {#method_missing} calls.
363
+ def configured?(key)
364
+ if @disable_local_missing
365
+ false
366
+ else
367
+ @disable_local_missing = true
368
+ begin
369
+ configured.has_key?(key.to_s.to_sym)
370
+ rescue Exception # One of the few times you ever want to catch this exception and not reraise it.
371
+ false
372
+ ensure
373
+ @disable_local_missing = false
374
+ end
375
+ end
376
+ end
377
+
378
+ # Returns the current runtime environment. Caches the result.
379
+ # @param reload [Boolean] Update the cached env by requerying the environment
380
+ # @return [ActiveSupport::EnvironmentInquirer]
381
+ # @example
382
+ # module Sample
383
+ # extend AppConfigFor
384
+ # end
385
+ # Sample.env # => 'development'
386
+ # Sample.env.development? # => true
387
+ # Sample.env.production? # => false
100
388
  def env(reload = false)
101
- @env = ActiveSupport::EnvironmentInquirer.new(AppConfigFor.env_name(env_prefixes)) if reload || @env.nil?
389
+ @env = ActiveSupport::EnvironmentInquirer.new(env_name) if reload || @env.nil?
102
390
  @env
103
391
  end
104
392
 
105
- def env_prefix_inheritance
106
- @env_prefix_inheritance ||= :namespace
393
+ # Convenience method for {env}(+true+).
394
+ # @return [ActiveSupport::EnvironmentInquirer]
395
+ # @example
396
+ # module Sample
397
+ # extend AppConfigFor
398
+ # end
399
+ # Sample.env # => 'development'
400
+ # Sample.env.development? # => true
401
+ # Sample.env.production? # => false
402
+ # # Switch to production
403
+ # ENV['SAMPLE_APP_ENV'] = 'production'
404
+ # Sample.env.production? # => false
405
+ # Sample.env!.production? # => true
406
+ def env!
407
+ env(true)
408
+ end
409
+
410
+ # Set the runtime environment (without affecting environment variables)
411
+ # @param environment [#to_s]
412
+ # @return [ActiveSupport::EnvironmentInquirer]
413
+ # @example
414
+ # ENV['SAMPLE_ENV'] = 'test'
415
+ # module Sample
416
+ # extend AppConfigFor
417
+ # end
418
+ # Sample.env # => 'test'
419
+ # Sample.env = 'development'
420
+ # Sample.env # => 'development'
421
+ # ENV['SAMPLE_ENV'] # => 'test'
422
+ def env=(environment)
423
+ @env = ActiveSupport::EnvironmentInquirer.new(environment.to_s)
424
+ end
425
+
426
+ # Current runtime environment inheritance style. Defaults to +:namespace+.
427
+ # @return [Symbol]
428
+ def env_inheritance
429
+ @env_inheritance ||= :namespace
430
+ end
431
+
432
+ # Set the runtime environment inheritance.
433
+ # @param style [#to_s] New inheritance style
434
+ # @return [Symbol]
435
+ # @raise InvalidEnvInheritanceStyle - Attempt to set a style that is not one of the {EnvInheritanceStyles}.
436
+ # @see .verified_style! Valid inheritance styles
437
+ def env_inheritance=(style)
438
+ @env_inheritance = AppConfigFor.verified_style!(style)
107
439
  end
108
440
 
109
- def env_prefix_inheritance=(style)
110
- @env_prefix_inheritance = AppConfigFor.verified_style!(style)
441
+ # The name of the current runtime environment for this object.
442
+ #
443
+ # Convenience method for {.env_name AppConfigFor.env_name}({#env_prefixes env_prefixes})
444
+ #
445
+ # If no value can be found, the default is 'development'.
446
+ # @return [String] current runtime environment.
447
+ # @see #env_prefixes Environment variable prefixes
448
+ def env_name
449
+ AppConfigFor.env_name(env_prefixes)
111
450
  end
112
451
 
452
+ # Prefixes used to determine the environment name.
453
+ #
454
+ # A prefix of :some_app will will cause AppConfigFor to react to the environment variable +'SOME_APP_ENV'+
455
+ # The order of the prefixes will be the order in which AppConfigFor searches the environment variables.
456
+ # A prefix for +self+ is automatically added at the time of extension/inclusion of AppConfigFor.
457
+ #
458
+ # @param all [Boolean] Combine current prefixes with inherited prefixes.
459
+ # @param dup [Boolean] Return a duplicate of the internal array to prevent accidental modification.
460
+ # @return [Array<Symbol>] Environment prefixes for this object.
461
+ # @see add_env_prefix Adding a prefix
462
+ # @see remove_env_prefix Removing a prefix
463
+ # @see env_name Current runtime environment
113
464
  def env_prefixes(all = true, dup = true)
114
- @env_prefixes ||= []
465
+ unless @env_prefixes
466
+ @env_prefixes = []
467
+ add_env_prefix
468
+ end
115
469
  if all
116
470
  @env_prefixes + AppConfigFor.progenitor_prefixes_of(self)
117
471
  else
@@ -119,6 +473,48 @@ module AppConfigFor
119
473
  end
120
474
  end
121
475
 
476
+ # Allow access to configuration getters and setters directly from the extending class/module.
477
+ # @example
478
+ # class Sample
479
+ # extend AppConfigFor
480
+ # end
481
+ #
482
+ # # Presuming config/sample.yml contains a configuration for 'log_level' and 'status' but no other keys.
483
+ # Sample.log_level # => :production
484
+ # Sample.log_level = :debug
485
+ # Sample.log_level # => :debug
486
+ #
487
+ # # You are allowed to set the value prior reading it should the need should arise.
488
+ # Sample.status = 'active'
489
+ # Sample.status # => 'active'
490
+ #
491
+ # # However, you cannot invent new keys with these methods.
492
+ # Sample.something_else # => NoMethodError(undefined method `something_else' for Sample)
493
+ # Sample.something_else = 1 # => NoMethodError(undefined method `something_else=' for Sample)
494
+ # @note Values can be written or read prior to the loading of the configuration presuming the configuration can load without error.
495
+ def method_missing(name, *args, &block)
496
+ if configured?(name.to_s.split('=').first.to_sym)
497
+ configured.send(name, *args, &block)
498
+ else
499
+ begin
500
+ super
501
+ rescue Exception => e
502
+ # Remove the call to super from the backtrace to make it more apparent where the failure occurred,
503
+ super_line = Regexp.new("#{__FILE__}:#{__LINE__ - 3}")
504
+ e.set_backtrace(ActiveSupport::BacktraceCleaner.new.tap { |bc| bc.add_silencer { |line| line =~ super_line } }.clean(e.backtrace))
505
+ raise e
506
+ end
507
+ end
508
+ end
509
+
510
+ # Remove an environmental prefix from the existing list.
511
+ # @param prefix [Symbol, Object] Prefix to remove.
512
+ # +nil+ is treated as +self+
513
+ # Non symbols are converted via {.prefix_from AppConfigFor.prefix_from}.
514
+ # @param all [Boolean] Remove this prefix throughout the entire inheritance chain.
515
+ # +USE WITH CAUTION:+ When +true+ this will affect other consumers of AppConfigFor by altering their env prefix values.
516
+ # @return [Array<Symbol>] Current prefixes (without inheritance)
517
+ # @see #env_prefixes Current prefixes
122
518
  def remove_env_prefix(prefix, all = false)
123
519
  if all
124
520
  remove_env_prefix(prefix)
@@ -126,24 +522,70 @@ module AppConfigFor
126
522
  else
127
523
  env_prefixes(false, false).delete(AppConfigFor.prefix_from(prefix))
128
524
  end
525
+ env_prefixes(all)
129
526
  end
130
527
 
528
+ # Return true if the missing method is a configuration getter or setter.
529
+ # @see #method_missing Accessing configuration values directly from the extending class/module
530
+ def respond_to_missing?(name, *args)
531
+ configured?(name.to_s.split('=').first.to_sym) || super
532
+ end
533
+
131
534
  class << self
132
535
 
536
+ # Add an additional environmental prefix to be used when determining current environment.
537
+ # @param prefix [Symbol, Object] Prefix to add.
538
+ # Non symbols are converted via {.prefix_from}.
539
+ # @param at_beginning [Boolean] where to insert the new prefix with respect to existing prefixes.
540
+ # * +true+ - Add to the beginning of the list.
541
+ # * +false+ - Add to the end of the list.
542
+ # @return [Array<Symbol>] Current prefixes (without inheritance)
543
+ # @see env_prefixes Current prefixes
544
+ # @note Prefixes added here will affect all consumers of AppConfigFor. For targeted changes see: {#add_env_prefix}
133
545
  def add_env_prefix(prefix, at_beginning = true)
134
546
  env_prefixes(false, false).send(at_beginning ? :unshift : :push, prefix_from(prefix)).uniq!
547
+ env_prefixes(false)
135
548
  end
136
549
 
550
+ # The name of the current runtime environment. This is value of the first non blank environment variable.
551
+ # If no value can be found, the default is 'development'.
552
+ # Prefixes like +:some_app+, +:rails+, and +:rack+ convert to +'SOME_APP_ENV'+, +'RAILS_ENV'+, and +'RACK_ENV'+ respectively.
553
+ # @param prefixes [Array<#to_s>] List of prefixes of environment variables to check.
554
+ # @return [String] current runtime environment.
555
+ # @see env_prefixes Current prefixes
137
556
  def env_name(prefixes = env_prefixes)
138
- prefixes.inject(nil) { |current_env, name| current_env || ENV["#{name.to_s.upcase}_ENV"].presence } || 'development'
557
+ Array(prefixes).inject(nil) { |current_env, name| current_env || ENV["#{name.to_s.upcase}_ENV"].presence } || 'development'
139
558
  end
140
559
 
141
- def env_prefixes(_all = true, dup = true)
142
- # all is ignored as we are at the end of the chain
560
+ # Prefixes used to determine the environment name.
561
+ #
562
+ # A prefix of :some_app will will cause AppConfigFor to react to the environment variable +'SOME_APP_ENV'+
563
+ # The order of the prefixes will be the order in which AppConfigFor searches the environment variables.
564
+ #
565
+ # @param _ Ignored. Unlike {#env_prefixes}, the first parameter is ignored as there is no inheritance at this point.
566
+ # @param dup[Boolean] Return a duplicate of the internal array to prevent accidental modification.
567
+ # @return [Array<Symbol>] Defaults to +[:rails, :rack]+
568
+ # @see env_name Current runtime environment
569
+ def env_prefixes(_ = true, dup = true)
143
570
  @env_prefixes ||= [:rails, :rack]
144
571
  dup ? @env_prefixes.dup : @env_prefixes
145
572
  end
146
573
 
574
+ # Lexical namespace of an object.
575
+ # Strings are considered to hold the #name of a Class or Module.
576
+ # Anything not a String, Class, or Module will return the namespace of the class of the object.
577
+ # @param object [Module, Class, String, Object]
578
+ # @return [Module, Class, nil] +nil+ is returned if there is no surrounding namespace.
579
+ # @example
580
+ # module Some
581
+ # class App
582
+ # end
583
+ # end
584
+ #
585
+ # namespace_of(Some::App) # => Some
586
+ # namespace_of('Some::App') # => Some
587
+ # namespace_of(Some::App.new) # => Some
588
+ # namespace_of(Some) # => nil
147
589
  def namespace_of(object)
148
590
  case object
149
591
  when String
@@ -155,11 +597,42 @@ module AppConfigFor
155
597
  end.deconstantize.safe_constantize
156
598
  end
157
599
 
158
- # Not used internally, this is a convenience method to study what progenitors are used during namespace dives
600
+ # Array of all hierarchical lexical namespaces of an object. Uses {.namespace_of}
601
+ # @param object [Module, Class, String, Object]
602
+ # @return [Array<Module, Class>]
603
+ # @example
604
+ # module Some
605
+ # class App
606
+ # class Connection
607
+ # end
608
+ # end
609
+ # end
610
+ #
611
+ # namespaces_of(Some::App::Connection) # => [Some::App, Some]
612
+ # namespaces_of(Some) # => []
159
613
  def namespaces_of(object)
160
614
  (object = [namespace_of(object)]).each { |x| x && object << namespace_of(x) }[0..-2]
161
615
  end
162
616
 
617
+ # Parent of an object.
618
+ # While similar to inheritance it provides a meaningful value for strings and other objects.
619
+ # Classes return super classes.
620
+ # Strings are treated as a name of a class and an attempt is made to locate that class (not the superclass of the named class).
621
+ # All other objects return the class of the object.
622
+ # @param object [Class, String, Object]
623
+ # @return [Class, nil] +nil+ is returned if a string is given that is not the name of a class.
624
+ # @example
625
+ # module Some
626
+ # class Base
627
+ # end
628
+ # class App < Base
629
+ # end
630
+ # end
631
+ #
632
+ # parent_of(Some::App) # => Some::Base
633
+ # parent_of(Some::App.new) # => Some::App
634
+ # parent_of('Some::App') # => Some::App
635
+ # parent_of('wtf') # => nil
163
636
  def parent_of(object)
164
637
  case object
165
638
  when String
@@ -171,11 +644,49 @@ module AppConfigFor
171
644
  end
172
645
  end
173
646
 
174
- # Not used internally, this is a convenience method to study what progenitors are used during class dives
647
+ # List of all hierarchical parents of an object. Uses {.parents_of}
648
+ # @param object [Class, String, Object]
649
+ # @return [Array<Class>]
650
+ #
651
+ # @example
652
+ # module Some
653
+ # class Base
654
+ # end
655
+ # class App < Base
656
+ # end
657
+ # end
658
+ #
659
+ # parents_of(Some::App) # => [Some::Base, Object, BasicObject]
660
+ # parents_of(Some::App.new) # => [Some::App, Some::Base, Object, BasicObject]
661
+ # parents_of('Some::App') # => [Some::App, Some::Base, Object, BasicObject]
662
+ # parents_of('wtf') # => []
175
663
  def parents_of(object)
176
664
  (object = [parent_of(object)]).each { |x| x && object << parent_of(x) }[0..-2]
177
665
  end
178
666
 
667
+ # Converts an object to a prefix symbol.
668
+ # Non symbols are converted to underscored symbols with '/' characters changed to underscores.
669
+ # Conversion by object type is as follows:
670
+ # * Symbol -> symbol
671
+ # * Module -> module.name
672
+ # * Class -> class.name
673
+ # * String -> string
674
+ # * Pathname -> pathname.basename (Without an extension)
675
+ # * other -> other.class.name
676
+ # @param object [Symbol, Module, Class, String, Pathname, Object] object to convert to a prefix
677
+ # @return [Symbol]
678
+ # @example
679
+ # module Some
680
+ # class App
681
+ # end
682
+ # end
683
+ #
684
+ # # All of the following return :some_app
685
+ # prefix_from(Some::App)
686
+ # prefix_from('Some::App')
687
+ # prefix_from(Some::App.new)
688
+ # prefix_from(:some_app)
689
+ # prefix_from(Pathname.new('/foo/bar/some_app.yml'))
179
690
  def prefix_from(object)
180
691
  if object.is_a?(Symbol)
181
692
  object
@@ -186,26 +697,51 @@ module AppConfigFor
186
697
  when String
187
698
  object
188
699
  when Pathname
189
- object.basename.to_s
700
+ object.basename('.*').to_s
190
701
  else
191
702
  object.class.name
192
703
  end.underscore.gsub('/','_').to_sym
193
704
  end
194
705
  end
195
706
 
707
+ # The first env_prefix aware namespace/parent of an object.
708
+ # Search is dependant on the inheritance style given.
709
+ # @param object [Object] Object to retrieve the progenitor of.
710
+ # @param style [#to_s] Type of hierarchical traversal.
711
+ # @return [Object, nil] +nil+ is returned if there is no progenitor.
712
+ # @raise InvalidEnvInheritanceStyle - Attempt to use a style that is not one of the {EnvInheritanceStyles}.
713
+ # @see .verified_style! Valid inheritance styles
196
714
  def progenitor_of(object, style = nil)
197
715
  style = verified_style!(style, object)
198
- command = {namespace: :namespace_of, class: :parent_of}[style] # Todo, deal with the other styles by doing nothing and not crashing or something.
716
+ command = {namespace: :namespace_of, class: :parent_of}[style.to_s.split('_').first.to_sym]
199
717
  object && command && send(command, object).yield_self { |n| n && (n.respond_to?(:env_prefixes) ? n : progenitor_of(n)) }
200
718
  end
201
719
 
720
+ # Extract the env_prefixes from the progenitor of the given object.
721
+ # @param object [Object] Object to retrieve the {.progenitor_of} and subsequently the {#env_prefixes}.
722
+ # @param style [#to_s] Type of hierarchical traversal.
723
+ # @param all [Boolean] Return inherited prefixes.
724
+ # If there is no progenitor of the object and all is +true+ then {.env_prefixes AppConfigFor.env_prefixes} will be returned.
725
+ # @return [Array<Symbol>] Environment prefixes for this object.
726
+ # @raise InvalidEnvInheritanceStyle - Attempt to use a style that is not one of the {EnvInheritanceStyles}.
727
+ # @see .env_prefixes Default prefixes
728
+ # @see .verified_style! Valid inheritance styles
202
729
  def progenitor_prefixes_of(object, style = nil, all = true)
203
- Array(progenitor_of(object, style)&.env_prefixes(all))
730
+ Array((progenitor_of(object, style) || all && AppConfigFor).try(:env_prefixes, all))
204
731
  end
205
732
 
206
- def progenitors_of(object, style = nil, terminate = true)
733
+ # List of hierarchical progenitors of an object.
734
+ # Hierarchical precedence is controlled by the style.
735
+ # @param object [Object] Object to get the progenitors from
736
+ # @param style [#to_s] Type of hierarchical traversal.
737
+ # @param unique [Boolean] Remove duplicate progenitors.
738
+ # @return [Array<Object>]
739
+ # @raise InvalidEnvInheritanceStyle - Attempt to use a style that is not one of the {EnvInheritanceStyles}.
740
+ # @see progenitor_of Progenitor of an object
741
+ # @see .verified_style! Valid inheritance styles
742
+ def progenitors_of(object, style = nil, unique = true)
207
743
  style = verified_style!(style, object)
208
- terminate = terminate && style != :none
744
+ unique = unique && style != :none
209
745
  if object && style != :none
210
746
  styles = style.to_s.split('_')
211
747
  if styles.size > 1
@@ -215,19 +751,59 @@ module AppConfigFor
215
751
  end
216
752
  else
217
753
  []
218
- end.yield_self { |result| terminate ? result.reverse.uniq.reverse + [self] : result }
754
+ end.yield_self { |result| unique ? result.reverse.uniq.reverse + [self] : result }
219
755
  end
220
756
 
221
- def remove_env_prefix(prefix, all = false)
222
- env_prefixes(all, false).delete(prefix_from(prefix))
757
+ # Remove an environmental prefix from the existing list.
758
+ # @param prefix [Symbol, Object] Prefix to remove.
759
+ # Non symbols are converted via {.prefix_from AppConfigFor.prefix_from}.
760
+ # @param _ Ignored. Unlike {#remove_env_prefix}, the first parameter is ignored as there is no inheritance at this point.
761
+ # @return [Array<Symbol>] Current prefixes (without inheritance)
762
+ # @note Prefixes removed here will affect all consumers of AppConfigFor. For targeted changes see: {#remove_env_prefix}
763
+ def remove_env_prefix(prefix, _ = false)
764
+ env_prefixes(false, false).delete(prefix_from(prefix))
765
+ env_prefixes(false)
223
766
  end
224
767
 
225
- def verified_style!(style, object = nil)
226
- style ||= object.respond_to?(:env_prefix_inheritance) ? object.send(:env_prefix_inheritance) : :namespace
768
+ # Verifies the inheritance style. If style is nil, the object, if given, will be queried for its env_inheritance.
769
+ # Otherwise the style will default to +:namespace:+
770
+ # @param style [#to_s] Inheritance style to verify.
771
+ # @param object [Object] Object to query for env_inheritance if style is nil.
772
+ # return [Symbol] A valid inheritance style.
773
+ # @raise InvalidEnvInheritanceStyle - An invalid inheritance style was received.
774
+ def verified_style!(style = nil, object = nil)
775
+ style ||= object.respond_to?(:env_inheritance) && object.env_inheritance || :namespace
227
776
  style = style.try(:to_sym) || style.to_s.to_sym
228
- EnvPrefixInheritanceStyles.include?(style) ? style : raise(InvalidEnvInheritanceStyle.new(style))
777
+ EnvInheritanceStyles.include?(style) ? style : raise(InvalidEnvInheritanceStyle.new(style))
229
778
  end
230
779
 
780
+ # Determine the name of the yml file from the object given. No pathing is assumed.
781
+ # Anything not a Pathname is converted to an underscored string with '/' characters changed to underscores and a '.yml' extension
782
+ # @param object [Object] Object to determine a yml name from.
783
+ #
784
+ # Determination by object type is as follows:
785
+ # * Pathname -> pathname
786
+ # * Module -> module.name
787
+ # * Class -> class.name
788
+ # * String -> string
789
+ # * Symbol -> symbol.to_s
790
+ # * other -> other.class.name
791
+ # @return [String, Pathname]
792
+ # @example
793
+ # module Some
794
+ # class App
795
+ # end
796
+ # end
797
+ #
798
+ # # All of the following return 'some_app.yml'
799
+ # yml_name_from(Some::App)
800
+ # yml_name_from(Some::App.new)
801
+ # yml_name_from(:some_app)
802
+ # yml_name_from('Some/App')
803
+ # yml_name_from('Some::App')
804
+ #
805
+ # # Pathnames receive no conversion
806
+ # yml_name_from(Pathname.new('not/a/yml_file.txt')) # => #<Pathname:not/a/yml_file.txt>
231
807
  def yml_name_from(object)
232
808
  if object.is_a?(Pathname)
233
809
  object
@@ -235,7 +811,9 @@ module AppConfigFor
235
811
  case object
236
812
  when Module
237
813
  object.name
238
- when String, Symbol
814
+ when String
815
+ object
816
+ when Symbol
239
817
  object.to_s
240
818
  else
241
819
  object.class.name
@@ -245,8 +823,8 @@ module AppConfigFor
245
823
 
246
824
  private
247
825
 
248
- def prep_base(base)
249
- base.add_env_prefix
826
+ # Add the config directory from the gem installation if this is a gem.
827
+ def add_gem_directory(base)
250
828
  gem = Gem.loaded_specs[base.name.underscore]
251
829
  base.add_config_directory(gem.gem_dir + '/config') if gem
252
830
  end
@@ -254,14 +832,13 @@ module AppConfigFor
254
832
  def extended(base)
255
833
  # Todo: Add the ability to check the default environments directly from base if the methods don't yet exist.
256
834
  # ie: base.development? is the same as base.env.development?
257
- prep_base(base)
835
+ add_gem_directory(base)
258
836
  end
259
837
 
260
838
  def included(base)
261
- prep_base(base)
839
+ add_gem_directory(base)
262
840
  end
263
841
 
264
842
  end
265
843
 
266
- end
267
-
844
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_config_for
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4.1
4
+ version: 0.0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Hall
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-15 00:00:00.000000000 Z
11
+ date: 2022-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -87,8 +87,8 @@ licenses:
87
87
  - MIT
88
88
  metadata:
89
89
  homepage_uri: https://github.com/ChapterHouse/app_config_for
90
- source_code_uri: https://github.com/ChapterHouse/app_config_for/tree/v0.0.4.1
91
- changelog_uri: https://github.com/ChapterHouse/app_config_for/blob/v0.0.4.1/CHANGELOG.md
90
+ source_code_uri: https://github.com/ChapterHouse/app_config_for/tree/v0.0.6.1
91
+ changelog_uri: https://github.com/ChapterHouse/app_config_for/blob/v0.0.6.1/CHANGELOG.md
92
92
  post_install_message:
93
93
  rdoc_options: []
94
94
  require_paths: