diff --git a/lib/wx/core/config.rb b/lib/wx/core/config.rb index c13fc989..9117ee9b 100644 --- a/lib/wx/core/config.rb +++ b/lib/wx/core/config.rb @@ -18,7 +18,7 @@ module Interface def method_missing(sym, *args, &block) unless block_given? || args.size>1 setter = false - key = sym.to_s.sub(/=\z/) { |s| setter = true; '' } + key = sym.to_s.sub(/=\z/) { |_| setter = true; '' } if (!setter && args.empty?) || (!has_group?(key) && setter && args.size==1) if setter return set(key, args.shift) @@ -142,8 +142,8 @@ def read(path_str, output=nil) when ::TrueClass == output || ::FalseClass == output || output == true || output == false val.is_a?(Integer) ? val != 0 : !!val else - raise ArgumentError, "Unknown coercion type #{output.is_a?(::Class) ? output : output.class}" if output - val + raise ArgumentError, "Unknown coercion type #{output.is_a?(::Class) ? output : output.class}" unless output.nil? || output.is_a?(::Proc) + output ? output.call(val) : val end end end @@ -330,17 +330,17 @@ module Interface def each_entry(&block) if block_given? - @data.select { |_,v| !v.is_a?(::Hash) }.each(&block) + data.select { |_,v| !v.is_a?(::Hash) }.each(&block) else - ::Enumerator.new { |y| @data.each_pair { |k,v| y << [k,v] if !v.is_a?(::Hash) } } + ::Enumerator.new { |y| data.each_pair { |k,v| y << [k, expand(v)] unless v.is_a?(::Hash) } } end end def each_group(&block) if block_given? - @data.select { |_,g| g.is_a?(::Hash) }.each { |k,g| block.call(k, Group.new(self, self.path.dup.push(k), g)) } + data.select { |_,g| g.is_a?(::Hash) }.each { |k,_| block.call(k, Group.new(self, self.path.dup.push(k))) } else - ::Enumerator.new { |y| @data.each_pair { |k,g| y << [k,Group.new(self, self.path.dup.push(k), g)] if g.is_a?(::Hash) } } + ::Enumerator.new { |y| data.each_pair { |k,g| y << [k,Group.new(self, self.path.dup.push(k))] if g.is_a?(::Hash) } } end end @@ -365,11 +365,9 @@ def has_entry?(path_str) return false if segments.empty? entry = segments.pop group_data = if segments.empty? - @data + data else - unless abs || root? - segments = self.path + segments - end + segments = self.path + segments unless abs || root? get_group_at(segments) end !!(group_data && group_data.has_key?(entry) && !group_data[entry].is_a?(::Hash)) @@ -378,40 +376,39 @@ def has_entry?(path_str) def has_group?(path_str) segments, abs = get_path(path_str) return root? if segments.empty? - unless abs || root? - segments = self.path + segments - end + segments = self.path + segments unless abs || root? !!get_group_at(segments) end def get(key) key = key.to_s raise ArgumentError, 'No paths allowed' if key.index(ConfigBase::SEPARATOR) - elem = @data[key] + elem = data[key] if elem.is_a?(::Hash) - Group.new(self, self.path.dup.push(key), elem) + Group.new(self, self.path.dup.push(key)) else - elem + expand(elem) end end def set(key, val) key = key.to_s raise ArgumentError, 'No paths allowed' if key.index(ConfigBase::SEPARATOR) - exist = @data.has_key?(key) - elem = exist ? @data[key] : nil + hsh = data + exist = hsh.has_key?(key) + elem = exist ? hsh[key] : nil if val.nil? - @data.delete(key) if exist + hsh.delete(key) if exist nil elsif val.is_a?(::Hash) raise ArgumentError, 'Cannot change existing value entry to group.' if exist && !elem.is_a?(::Hash) - elem = @data[key] = {} unless elem - group = Group.new(self, self.path.dup.push(key), elem) + hsh[key] = {} unless elem + group = Group.new(self, self.path.dup.push(key)) val.each_pair { |k, v| group.set(k, v) } group else raise ArgumentError, 'Cannot change existing group to value entry.' if exist && elem.is_a?(::Hash) - @data[key] = sanitize_value(val) + hsh[key] = val end end @@ -420,23 +417,21 @@ def delete(path_str) return nil if segments.empty? last = segments.pop group_data = if segments.empty? - @data + data else - unless abs || root? - segments = self.path + segments - end + segments = self.path + segments unless abs || root? get_group_at(segments, create_missing_groups: false) end - raise ArgumentError, "Unable to resolve path #{segments+[last]}" unless group_data - group_data.delete(last) + group_data ? group_data.delete(last) : nil end def rename(old_key, new_key) old_key = old_key.to_s new_key = new_key.to_s raise ArgumentError, 'No paths allowed' if old_key.index(ConfigBase::SEPARATOR) || new_key.index(ConfigBase::SEPARATOR) - if @data.has_key?(old_key) && !@data.has_key?(new_key) - @data[new_key] = @data.delete(old_key) + hsh = data + if hsh.has_key?(old_key) && !hsh.has_key?(new_key) + hsh[new_key] = hsh.delete(old_key) true else false @@ -448,19 +443,19 @@ def read(path_str, output=nil) return nil if segments.empty? last = segments.pop group_data = if segments.empty? - @data + segments = self.path.dup unless abs || root? + data else - unless abs || root? - segments = self.path + segments - end - get_group_at(segments, create_missing_groups: true) + segments = self.path + segments unless abs || root? + get_group_at(segments) end - raise ArgumentError, "Unable to resolve path #{segments+[last]}" unless group_data - val = group_data[last] + val = group_data ? group_data[last] : nil if val.is_a?(::Hash) raise TypeError, "Cannot convert group" unless output.nil? - Group.new(self, segments.dup.push(last), val) + Group.new(self, segments.dup.push(last)) else + val = expand(val) + return val unless val && output case when ::String == output || ::String === output val.to_s @@ -471,8 +466,8 @@ def read(path_str, output=nil) when ::TrueClass == output || ::FalseClass == output || output == true || output == false val.is_a?(::Integer) ? val != 0 : !!val else - raise ArgumentError, "Unknown coercion type #{output.is_a?(::Class) ? output : output.class}" if output - val + raise ArgumentError, "Unknown coercion type #{output.is_a?(::Class) ? output : output.class}" unless output.nil? || output.is_a?(::Proc) + output ? output.call(val) : val end end end @@ -483,11 +478,10 @@ def write(path_str, val) return false if segments.empty? last = segments.pop group_data = if segments.empty? - @data + segments = self.path.dup unless abs || root? + data else - unless abs || root? - segments = self.path + segments - end + segments = self.path + segments unless abs || root? get_group_at(segments, create_missing_groups: true) end raise ArgumentError, "Unable to resolve path #{segments+[last]}" unless group_data @@ -498,13 +492,13 @@ def write(path_str, val) nil elsif val.is_a?(::Hash) raise ArgumentError, 'Cannot change existing value entry to group.' if exist && !elem.is_a?(::Hash) - elem = group_data[last] = {} unless elem - group = Group.new(self, segments.dup.push(last), elem) + group_data[last] = {} unless elem + group = Group.new(self, segments.dup.push(last)) val.each_pair { |k, v| group.set(k, v) } group else raise ArgumentError, 'Cannot change existing group to value entry.' if exist && elem.is_a?(::Hash) - group_data[last] = sanitize_value(val) + group_data[last] = val end end alias :[]= :write @@ -514,7 +508,7 @@ def to_s end def to_h - @data + data end def get_path(path_str) @@ -526,21 +520,33 @@ def get_path(path_str) end protected :get_path - def sanitize_value(val) - case val - when TrueClass, FalseClass, Numeric, String - val - else - if val.respond_to?(:to_int) - val.to_int - elsif val.respond_to?(:to_f) - val.to_f - else - val.to_s + EXPAND_RE = if Wx::PLATFORM == 'WXMSW' + /(\\)?([$][{(]?|%)(\w+)([})%])?/ + else + /(\\)?([$][{(]?)(\w+)([})])?/ + end + private_constant :EXPAND_RE + + def expand(val) + if root.expanding_env_vars? && ::String === val + val.gsub(EXPAND_RE) do |s| + if $1.nil? && + (($2[0] == '$' && + ($2.size == 1 && $4.nil?) || + ($2[1] == '(' && $4 == ')') || + ($2[1] == '{' && $4 == '}'))|| + ($2[0] == '%' && $4 == '%')) && + ENV[$3] + $1 ? $1+ENV[$3] : ENV[$3] + else + $1 ? s[1,s.size] : s + end end + else + val end end - protected :sanitize_value + protected :expand end @@ -550,12 +556,16 @@ class Group include Interface - def initialize(parent, path, data) + def initialize(parent, path) @parent = parent @path = path.freeze - @data = data end + def data + self.root.__send__(:get_group_at, @path, create_missing_groups: true, is_pruned: true) + end + protected :data + def root? false end @@ -573,7 +583,7 @@ def parent end def get_group_at(segments, create_missing_groups: false) - root.__send__(:get_group_at, segments) + root.__send__(:get_group_at, segments, create_missing_groups: create_missing_groups) end protected :get_group_at @@ -582,10 +592,16 @@ def get_group_at(segments, create_missing_groups: false) include Interface def initialize(hash = nil) + @expand_env_vars = true @data = {} replace(hash) if hash end + def data + @data + end + protected :data + def root? true end @@ -614,19 +630,31 @@ def replace(hash) self end - def get_group_at(segments, create_missing_groups: false) - # prune segments (process relative segments) - segments = segments.inject([]) do |lst, seg| - case seg - when '..' - lst.pop # remove previous - # forget .. - when '.' - # forget - else - lst << seg + def is_expanding_env_vars + @expand_env_vars + end + alias :expanding_env_vars? :is_expanding_env_vars + + def set_expand_env_vars(flag) + @expand_env_vars = !!flag + end + alias :expand_env_vars :set_expand_env_vars + + def get_group_at(segments, create_missing_groups: false, is_pruned: false) + unless is_pruned + # prune segments (process relative segments) + segments = segments.inject([]) do |lst, seg| + case seg + when '..' + lst.pop # remove previous + # forget .. + when '.' + # forget + else + lst << seg + end + lst end - lst end # find group matching segments segments.inject(@data) do |hsh, seg| diff --git a/lib/wx/doc/config.rb b/lib/wx/doc/config.rb index fa6961b5..ddb5eaa7 100644 --- a/lib/wx/doc/config.rb +++ b/lib/wx/doc/config.rb @@ -113,7 +113,7 @@ def rename(old_key, new_key) end # By default returns un-coerced value. # Raises exception if incompatible coercion is specified. # @param [String] path_str - # @param [Class,nil] output output type to convert to + # @param [Class,Proc,nil] output output type (or converter proc) to convert to (with) # @return [Boolean,String,Integer,Float,Wx::Config::Group,nil] value entry value def read(path_str, output=nil) end @@ -186,6 +186,16 @@ def clear; end # @return [self] def replace(hash) end + # Returns true if we are expanding environment variables in string values, false otherwise. + # @return [Boolean] + def is_expanding_env_vars; end + alias :expanding_env_vars? :is_expanding_env_vars + + # Determine whether we wish to expand environment variables in string values. + # @param [Boolean] flag enables expanding environment variables if true, disables otherwise + def set_expand_env_vars(flag) end + alias :expand_env_vars :set_expand_env_vars + end # Configuration class for wxRuby which stores it's settings in a (possibly nested) Hash. @@ -225,6 +235,16 @@ def clear; end # @return [self] def replace(hash) end + # Returns true if we are expanding environment variables in string values, false otherwise. + # @return [Boolean] + def is_expanding_env_vars; end + alias :expanding_env_vars? :is_expanding_env_vars + + # Determine whether we wish to expand environment variables in string values. + # @param [Boolean] flag enables expanding environment variables if true, disables otherwise + def set_expand_env_vars(flag) end + alias :expand_env_vars :set_expand_env_vars + end end diff --git a/lib/wx/doc/extra/14_config.md b/lib/wx/doc/extra/14_config.md new file mode 100644 index 00000000..64ec5280 --- /dev/null +++ b/lib/wx/doc/extra/14_config.md @@ -0,0 +1,102 @@ + + +# 14. Configuration support + +## Introduction + +wxRuby3 fully supports the wxWidgets config classes providing a Ruby-fied interface. + +The config classes provide a way to store some application configuration information providing features +that make them very useful for storing all kinds of small to medium volumes of hierarchically-organized, +heterogeneous data. +In wxWidgets these were especially designed for storing application configuration information and intended to be +mostly limited to that. That meant the information to be stored was intended to be: + +* Typed, i.e. strings, booleans or numbers for the moment. You cannot store binary data, for example. +* Small. For instance, it is not recommended to use the Windows registry (which is the default storage medium on + that platform) for amounts of data more than a couple of kilobytes. +* Not performance critical, neither from speed nor from a memory consumption point of view. + +As you will see wxRuby3 extends the support in this area and provides means forego a lot of these restrictions. + +The config classes also are intended to abstract away a lot platform differences. In this area wxRuby3 extends the +support also. + +## Default configuration support + +When the default, global, config instance is used (by using {Wx::ConfigBase.get} with default argument) this will be +a platform specific instance. On Windows platforms a Windows registry based implementation is used and on other +platforms a text format configuration file. + +wxRuby3 provides a single wrapper class for these with {Wx::ConfigWx}. This is an abstract class that cannot be +instantiated in Ruby which provides a common, Ruby-fied interface supported by all config classes in wxRuby3. + +While wxWidgets does a decent job of abstracting platform differences it is in no way perfect in this area. With the +text format configuration files for example the stored values loose all type information since everything is stored +as strings. This also differs from the registry based implementation where some type information is not lost but some +(like boolean types) is. +This is not a problem when accessing information for which the structure and types are exactly known as the config +classes offer type specific readers (as well as writers) which coerce values to their expected types but may offer +nasty surprises when more reflectively accessing data of which the exact typing and structure is not known. + +In Ruby where we more or less expect to have common API-s that can return or accept any type of object needing to be +type specific is awkward. wxRuby3 works around this as much as possible for the {Wx::ConfigWx} wrapper class but also +provides an alternative config class integrated with the wxWidgets framework that does not suffer from these restrictions. + +## Enhanced Ruby configuration support + +Instead of the default, platform specific, config classes it is also possible to use a custom wxRuby3 extension providing +a config class which is implemented in pure Ruby and integrated in the wxWidgets configuration framework. +To use an instance of this class as the global config instance the {Wx::ConfigBase.create} should be called at application +initialization time with it's `:use_hash_config` keyword argument set to `true` (and possibly, to be sure, it's +`forced_create` argument set to `true` also). Alternatively a {Wx::Config} (or derivative) instance could be explicitly +instantiated in code and assigned as global instance with {Wx::ConfigBase.set}. + +This would create an instance of {Wx::Config} and install that as the global config instance (if no other instance was +yet installed or, overruling that condition, if `forced_create` was set to `true`). + +As the keyword argument indicates {Wx::Config} is a Ruby `Hash` based config class implementation. + +Value objects are stored Ruby-style as-is into it's internal hash table (maintaining full typing) and are also retrieved +as-is by default (to maintain compatibility with the {Wx::ConfigWx} wrapper type coercion options are provided). +Grouping is based of nested `Hash` instances. + +Because of the `Hash` based implementation and lack of (the need for) type coercion the {Wx::Config} class does have **any** +restrictions of the type of data stored. The only possible type restrictions to enforce may come from usage contexts: + +* In case of value entries shared with wxWidgets framework code (like for example entries save by the persistence +framework; see [here](15_persistence.md)) value types should be restricted to those supported by the wxWidget platform +specific classes and correspond to what the framework code expects. +* In case of the need to save/restore the configuration data using a mechanism that imposes type restrictions these +should be applied. + +With {Wx::Config} it would be perfectly alright to store arrays or any kind of arbitrary object (only be aware that `Hash` +instances will always be expected to provide configuration structure by default) as long as these do not conflict with +expectations of framework code or storage mechanisms. + +With the standard Ruby YAML and JSON serialization support this also provides improved platform independent configuration +persistence options with full typing maintainability. + +## Differences between default and enhanced configuration support + +The major difference is, as described above, the absence of type restrictions in the enhanced Ruby config class {Wx::Config}. + +Another difference is that {Wx::Config} will not automatically create missing groups or entries on reading. This will only +happen when writing configuration values. + +A last difference is that the default support is by default backed up by persistent storage (windows registry or file) and +the wxRuby enhanced support only provides in-memory storage (`Hash` instance) by default. + +Persisting configuration data from {Wx::Config} will require coding customized storage and retrieval operations (which is +trivial using standard YAML or JSON support). + +## Differences between wxWidgets config interface and wxRuby + +In wxRuby there is no option to provide a default value argument when reading values. The reasoning is that Ruby itself +provides more than enough options to elegantly provide for defaults with statement options like `var ||= default` or +`var = get('something') || default`. + +As a consequence wxRuby also does not support recording defaults on read operations (and also does not provide the +corresponding option setter/getter in the interface). diff --git a/lib/wx/version.rb b/lib/wx/version.rb index 96052235..d7833251 100644 --- a/lib/wx/version.rb +++ b/lib/wx/version.rb @@ -3,5 +3,5 @@ # This software is released under the MIT license. module Wx - WXRUBY_VERSION = '0.9.3' + WXRUBY_VERSION = '0.9.4' end diff --git a/rakelib/lib/director/config_base.rb b/rakelib/lib/director/config_base.rb index 70780b09..df1cca6b 100644 --- a/rakelib/lib/director/config_base.rb +++ b/rakelib/lib/director/config_base.rb @@ -330,7 +330,7 @@ def setup if (argc < 1 || argc > 1) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } wxString name = RSTR_TO_WXSTR(argv[0]); wxConfigPathChanger path(cfg, name); @@ -350,7 +350,7 @@ def setup if (argc < 1 || argc > 1) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } wxString key = RSTR_TO_WXSTR(argv[0]); VALUE rc = Qfalse; @@ -372,7 +372,7 @@ def setup if (argc < 2 || argc > 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 2)", argc); } wxString key = RSTR_TO_WXSTR(argv[0]); wxString newKey = RSTR_TO_WXSTR(argv[1]); @@ -395,7 +395,7 @@ def setup if (argc < 0 || argc > 0) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 0)", argc); } wxString key; long index = 0; @@ -420,7 +420,7 @@ def setup if (argc < 0 || argc > 0) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 0)", argc); } wxString key; long index = 0; @@ -445,7 +445,7 @@ def setup if (argc < 0 || argc > 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } bool recurse = argc>0 ? (argv[0] != Qfalse && argv[0] != Qnil) : false; size_t n = cfg->GetNumberOfEntries(recurse); @@ -459,7 +459,7 @@ def setup if (argc < 0 || argc > 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } bool recurse = argc>0 ? (argv[0] != Qfalse && argv[0] != Qnil) : false; size_t n = cfg->GetNumberOfGroups(recurse); @@ -486,7 +486,7 @@ def setup if (argc < 1 || argc > 1) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } wxString path = RSTR_TO_WXSTR(argv[0]); return cfg->HasGroup(path) ? Qtrue : Qfalse; @@ -499,7 +499,7 @@ def setup if (argc < 0 || argc > 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } return WXSTR_TO_RSTR(cfg->GetPath()); } @@ -511,10 +511,36 @@ def setup if (argc < 0 || argc > 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } return cfg->DeleteAll() ? Qtrue : Qfalse; } + + static VALUE config_wx_is_expanding_env_vars(int argc, VALUE *argv, VALUE self) + { + wxConfigBase *cfg; + Data_Get_Struct(self, wxConfigBase, cfg); + + if (argc != 0) + { + rb_raise(rb_eArgError, "No arguments expected"); + } + return cfg->IsExpandingEnvVars() ? Qtrue : Qfalse; + } + + static VALUE config_wx_set_expand_env_vars(int argc, VALUE *argv, VALUE self) + { + wxConfigBase *cfg; + Data_Get_Struct(self, wxConfigBase, cfg); + + if (argc < 1 || argc > 1) + { + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); + } + bool expand = (argv[0] != Qfalse && argv[0] != Qnil); + cfg->SetExpandEnvVars(expand); + return Qnil; + } __HEREDOC spec.add_wrapper_code <<~__HEREDOC SWIGINTERN void @@ -563,6 +589,10 @@ def setup rb_define_method(g_cConfigWx, "rename", VALUEFUNC(config_wx_rename), -1); rb_define_method(g_cConfigWx, "path", VALUEFUNC(config_wx_path), -1); rb_define_method(g_cConfigWx, "clear", VALUEFUNC(config_wx_clear), -1); + rb_define_method(g_cConfigWx, "is_expanding_env_vars", VALUEFUNC(config_wx_is_expanding_env_vars), -1); + rb_define_alias(g_cConfigWx, "expanding_env_vars?", "is_expanding_env_vars"); + rb_define_method(g_cConfigWx, "set_expand_env_vars", VALUEFUNC(config_wx_set_expand_env_vars), -1); + rb_define_alias(g_cConfigWx, "expand_env_vars=", "set_expand_env_vars"); g_cConfig = rb_define_class_under(mWxCore, "Config", g_cConfigBase); rb_define_alloc_func(g_cConfig, config_allocate); diff --git a/tests/test_config.rb b/tests/test_config.rb index 155ed817..0d2308b2 100644 --- a/tests/test_config.rb +++ b/tests/test_config.rb @@ -132,6 +132,10 @@ def run_config_tests(cfg) cfg['/Group1/Group1_2'] = { 'Float' => 0.3330 } assert_equal_cfg(0.3330, cfg['/Group1/Group1_2'].get('Float')) + assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float').to_f) + assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float', Float)) + assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float', ->(v) { v.to_f })) + cfg.replace(DEMO_CONFIG) # reset end @@ -192,6 +196,47 @@ def run_auto_accessor_tests(cfg) cfg.Group1.Group1_2 = { 'Float' => 0.3330 } assert_equal_cfg(0.3330, cfg.Group1.Group1_2.get('Float')) + + cfg.replace(DEMO_CONFIG) # reset + end + + def run_env_var_tests(cfg) + # by default expansion is on + + # add a number of entries for env var in new group 'Environment' + cfg['/Environment/HOME'] = '$HOME' + cfg['Environment'].USER = Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}' + cfg['/Environment/PATH'] = '$(PATH)' + + assert_equal(ENV['HOME'], cfg.Environment['HOME']) + assert_equal(ENV[Wx::PLATFORM == 'WXMSW' ? 'USERNAME' : 'USER'], cfg['/Environment/USER']) + assert_equal(ENV['PATH'], cfg.Environment.PATH) + + # test escaping + cfg['/Environment/Escaped_HOME'] = '\$HOME' + cfg['/Environment/Escaped_HOME2'] = '\\$HOME' + cfg['/Environment/Escaped_HOME3'] = '\\\$HOME' + + assert_equal('$HOME', cfg.Environment['Escaped_HOME']) + assert_equal('$HOME', cfg.Environment['Escaped_HOME2']) + assert_equal('\$HOME', cfg.Environment['Escaped_HOME3']) + + cfg['/Environment/NONSENSE'] = '${NonExistingLongNonsenseVariable}' + + assert_equal('${NonExistingLongNonsenseVariable}', cfg.Environment['NONSENSE']) + + cfg['/Environment/MULTIPLE'] = "$HOME / #{Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}'}" + + assert_equal("#{ENV['HOME']} / #{Wx::PLATFORM == 'WXMSW' ? ENV['USERNAME'] : ENV['USER']}", cfg.Environment['MULTIPLE']) + + # disable env var expansion + cfg.expand_env_vars = false + begin + assert_equal('$HOME', cfg.Environment['HOME']) + ensure + # re-enable + cfg.set_expand_env_vars(true) + end end def test_basic @@ -199,6 +244,7 @@ def test_basic run_config_tests(cfg) run_auto_accessor_tests(cfg) + run_env_var_tests(cfg) end def test_global @@ -214,6 +260,7 @@ def test_global run_config_tests(cfg) run_auto_accessor_tests(cfg) + run_env_var_tests(cfg) cfg_old = Wx::ConfigBase.set(nil) @@ -234,6 +281,7 @@ def test_default_wx run_config_tests(cfg) run_auto_accessor_tests(cfg) + run_env_var_tests(cfg) assert_true(cfg.clear) # cleanup end