app_config_for 0.0.4.1 → 0.0.6.1

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