-
-
Notifications
You must be signed in to change notification settings - Fork 25
Upgrading To Chamber 3.0
Making breaking changes in a library is never easy. I know there are a ton of users out there that love Chamber and love it exactly the way it is. We have not had an intentional breaking change since Chamber was released as a 2.0 over nine years ago. Back then the number one hit song was "Wrecking Ball" by Miley Cyrus.
So I hope you all will understand that the breaking changes are done with good reason and bear with me while I try to get you all through this with as little pain as possible.
dig!
is available as of chamber 2.14.1
(which was also the version that
enabled deprecation warnings). If you get rid of all of the warnings
(preferably by converting to using dig!
, you will be able to upgrade to
Chamber 3.0 with no problems.
Hashie has been both the reason for the extra power that has allowed Chamber users to access their settings however they'd like, and also the bane of my existance in terms of maintenance.
First, you should go read Richard Schneeman's great blog post "Hashie Considered Harmful" for some in-depth rationales on why Hashie is problematic regardless of what it is being used for.
Additionally Hashie's own creator said that Hashie was never intended for production use. It was to be used primarily for prototyping. (Note: I know this is true however I failed to find the quote. If someone can provide it to me, I'd be grateful.)
Even moreso than all of the above is the amount of indeterminism that arose in
the code from having a thing that was "kind-of-a-Hash-but-not-really". There
were corner cases and branches that only existed because the object that was
being used was a Hashie::Mash
instead of a proper Hash
.
The removal of hashie
required additional constraints on how users could
access their settings. This ended up being a significant reduction in internal
logic in its own right. There were three places in the code where we were
needing to do method_missing
to catch attempts to access settings rather than
call a legitimate method on the object. In almost all cases, reducing
metaprogramming is a Good Thing² and this was no exception.
In Chamber 2.x you could access your settings like so:
Chamber.env.smtp.username # => "my_username_example"
In Chamber 3.x you would access that same setting like so:
Chamber.dig!('smtp', 'username') # => "my_username_example"
or you can use symbols if you would prefer:
Chamber.dig!(:smtp, :username) # => "my_username_example"
In Chamber 2.x you could either access your settings like so:
Chamber.env['smtp']['username'] # => "my_username_example"
or like so:
Chamber.env[:smtp][:username] # => "my_username_example"
In Chamber 3.x, you must use strings to access settings with bracket syntax:
Note: You can now call []
directly on the Chamber
constant (ie
Chamber['smtp']['username']
)
If you really want to use symbols, you should use the dig
syntax:
Chamber.dig!(:smtp, :username) # => "my_username_example"
In Chamber 2.x, you could utilize predicate methods.
In Chamber 3.x, the user experience defaults the user to one where you simply cannot access configuration values that do not exist. Therefore these predicate matchers are of very limited use. They were also a feature that few Chamber users used. As such, they have been removed from Chamber 3.x.
If you absolutely require the knowledge of whether a key exists, you should use
Ruby's built-in hash methods like has_key?
.
Ever since Chamber 1.0 it has had the ability to be able to push or pull settings from cloud environments such as Heroku and CircleCI. This worked somewhat well at first, but became a maintenance burden.
- Testing Difficulties
Automated tests for the cloud providers frequently failed due to changes in the APIs or issues with rotating test credentials.
- Lack of Use
This integration was rarely used ever since Chamber added support for better alternatives (I will describe in a second) for this process.
- Code Complexity
There was a surprisingly large amount of code that existed to support these integrations.
- Maintenance Burden
For a feature that was rarely used, it had quite a lot of people submitting issues about it. Additionally, if I wanted to support another service, I would have had to write custom code to integrate with that services API. Considering my limited time, this wasn't going to work.
Fortunately Chamber has a much better alternative to this. If you send your private key to your cloud service of choice, when Chamber loads, it will automatically decrypt the settings in your YAML files.
What does this mean? A single push when you start your project and, unless you rotate your Chamber key, you never have to sync your settings ever again (as long as you push your YAML files to the service).
I will do my best to keep that page up-to-date with new commands for new services. Please submit a pull request if one is missing.
I don't technically consider removing older Ruby versions as a breaking change however I'm adding it here for completeness.
Ruby 1.9.3 is, at this point over eleven years old. If some of you are on that
version of Ruby, then 2.13.x
will likely serve you well for the foreseeable
future. However we've got to move on at some point and the benefits this
brought to the codebase made it worth the change to me.
Although I always touted Chamber as a "zero monkeypatching" library, that wasn't
technically true. If the version of Ruby you were using didn't have
transform_keys
defined on the Hash
class, it would inject that method onto
it. A very minor patch to be sure, but still, I loathe monkeypatching and think
it's bad in all its forms. Because Ruby 2.5.5 introduced a native
transform_keys
method, I can remove the monkeypatch.
Ruby 2.0 introduced refinements and even though they have their own problems, they are a good solution to allow Rubyists to inject code into core classes without it affecting the rest of the world.
Since I was able to upgrade to Ruby 2.0+, I was able to also inject some methods
that mimiced some of the things that Hashie
gave me, without needing to use
the gem.
While not a huge factor, Ruby 2.1+ allowed me to use some improved syntax in the code which simplified a lot of things. The most notable of these was keyword arguments. Previously I was passing around options hashes which is not ideal.
In Chamber 2.x, with this data:
development:
smtp_enabled: false
test:
smtp_enabled: false
production:
smtp_enabled: true
You could do:
Chamber.env['my_nonexistent_key'] # => nil
In Chamber 3.x, this will fail with a KeyError
. Everything about Chamber
3.x's changes steers the user into helping make sure that their configuration is
as they expect it. One could just as easily have put somthing like:
Chamber.env['smpt_enabled'] # => nil
Note that the above is misspelled. Since this is a boolean, it works fine in
development and test since nil
and false
behave almost identically, however
in production you would have a bug. You would be receiving nil
when you were
expecting true
.
In Chamber 3.x, that same line would throw a KeyError
. Alerting you to the
fact that the thing you expected to be there, was not.
This also pushes you to make sure that the various namespace combinations you have all result in a configuration data structure that is equivalent. This is also a Good Thing.
This will have almost zero impact for Chamber uses as I'm sure most of you are not doing this, but as it is a breaking change, I'm listing it here:
In Chamber 2.x, because Hashie was allowing for indifferent access, you could technically define keys as symbols in YAML like so:
:my_symbol_key: "my_value"
and Chamber would read it in and store :my_symbol_key
as the key in the hash.
Now that we're using plain Hash
objects, we want to be deterministic about how
keys are stored in the data structure so that, conversely, we know how we should
access the data in the structure when the user requests it.
In Chamber 3.x, the same structure above would have 'my_symbol_key'
as the key
for the value 'my_value'
.
If you attempt to load settings with keys that are anything other than
a String
, you will receive a Chamber::Errors::NonConformingKey
error.
Ruby 3.1 now defaults the Psych YAML parser to "safe" mode. By default, safe mode does not allow aliases or any objects other than:
TrueClass
FalseClass
NilClass
Integer
Float
String
Array
Hash
Because Chamber is not supposed to be parsing user-provided YAML (read: YOU SHOULD BE VETTING EVERY YAML FILE CHANGE THAT IS MADE TO YOUR CHAMBER SETTINGS) I am relaxing those constraints a little bit, but I do think it is prudent not to allow any and all classes to be loaded.
As of Chamber 3.0 only the above classes will be able to be loaded in your settings files with the addition of:
Regexp
Date
Time
YAML aliases are also still supported.
Symbols are a whole can of worms in Ruby and because of that and because we're removing Symbol access for the keys in Chamber 3.0 anyway, I've kept them restricted on load for values as well.
While the chamber
command line tool still relies on thor
, thor
is an
incredibly stable gem and has had minimal changes over the years.
The core library code does not depend on any other libraries for it to do what
it does and is therefore super resiliant and much more able to be installed
securely with the Rubygems HighSecurity
option enabled.
I wish that backwards incompatible changes never needed to happen, but that would mean we would all have to be perfect from day one. Unfortunately that is not me.
I do hope that I have made the upgrade path for you all as easy as possible (hopefully a few find and replaces at most).
I'm looking forward to brining you all more features in the future including Github Actions support, and automatic re-keying if you have to change your Chamber private key for some reason (or want to introduce additional namespaces into an already existing project).
❤️
Copyright ©2023
- Release News
- Gem Comparison
- 12-Factor App Rebuttal
- Environment Variable Problems
- Installation
- Basics
- Defining Settings
- Accessing Settings
- Verifying Settings
- Namespaces
- Environment Variables
- Integrations
- Encryption
- Advanced Usage
- Command Line Reference