circuit_switch is a gem for 'difficult' application; for example, few tests, too many meta-programming codes, low aggregation classes and few deploys.
This switch helps make changes easier and deploy safely.
You can deploy and check with a short code like following if the change is good or not, and when a problem is found, you can undo it without releasing it.
if CircuitSwitch.report.open?
# new codes
else
# old codes
endYou can also specify conditions to release testing features.
CircuitSwitch.run(if: -> { current_user.testing_new_feature? }) do
# testing feature codes
endCircuitSwitch depends on ActiveRecord and ActiveJob.
Add this line to your application's Gemfile and run bundle install:
gem 'circuit_switch'Run generator to create initializer and modify config/initizlizers/circuit_switch.rb:
rails generate circuit_switch:install
Generate a migration for ActiveRecord.
This table saves named key, circuit caller, called count, limit count and so on.
rails generate circuit_switch:migration circuit_switch
rails db:migrate
When you want to deploy and undo it if something unexpected happens, use CircuitSwitch.run.
CircuitSwitch.run do
# experimental codes
endrun calls received proc, and when conditions are met, closes it's circuit.
To switch circuit opening and closing, some conditions can be set. By default, the circuit is always opened.
You can also set limit_count to close circuit when reached the specified count. Default limit_count is 10. To change this default value, modify circuit_switches.run_limit_count default value in the migration file.
run receives optional arguments.
key: [String] Named key to find switch instead of caller
If no key passed, use caller.if: [Boolean, Proc] Call proc whenifis truthy (default: true)close_if: [Boolean, Proc] Call proc whenclose_ifis falsy (default: false)close_if_reach_limit: [Boolean] Stop calling proc when run count reaches limit (default: false)limit_count: [Integer] Limit count. Userun_limit_countdefault value if it's nil (default: nil)
Can't be set 0 whenclose_if_reach_limitis trueinitially_closed: [Boolean] Create switch with terminated mode (default: false)
To close the circuit at specific date or when called 1000 times, code goes like:
CircuitSwitch.run(close_if: -> { Date.today >= some_day }, limit_count: 1_000) do
# testing codes
endTo run other codes when circuit closes, run? is available.
circuit_open = CircuitSwitch.run { ... }.run?
unless circuit_open
# other codes
endCircuitSwitch.run.run? has syntax sugar. open? doesn't receive proc.
if CircuitSwitch.open?
# new codes
else
# old codes
endWhen you just want to report, set your reporter to initializer and then call CircuitSwitch.report.
CircuitSwitch.report(if: some_condition)report just reports the line of code is called. It doesn't receive proc. It's useful for refactoring or removing dead codes.
Same as run, some conditions can be set. By default, reporting is stopped when reached the specified count. The default count is 10. To change this default value, modify circuit_switches.report_limit_count default value in the migration file.
report receives optional arguments.
key: [String] Named key to find switch instead of caller
If no key passed, use caller.if: [Boolean, Proc] Report whenifis truthy (default: true)stop_report_if: [Boolean, Proc] Report whenclose_ifis falsy (default: false)stop_report_if_reach_limit: [Boolean] Stop reporting when reported count reaches limit (default: true)limit_count: [Integer] Limit count. Usereport_limit_countdefault value if it's nil (default: nil)
Can't be set 0 whenstop_report_if_reach_limitis true
To know about report is executed or not, you can get through report?.
Of course you can chain report and run or open?.
Reporter reports a short message by default like Watching process is called for 5th. Report until for 10th..
When your reporting tool knows about it's caller and backtrace, this is enough (e.g. Bugsnag).
When your reporting tool just reports plain message (e.g. Slack), you can set with_backtrace true to initializer. Then report has a long message with backtrace like:
Watching process is called for 5th. Report until for 10th.
called_path: /app/services/greetings_service:21 block in validate
/app/services/greetings_service:20 validate
/app/services/greetings_service:5 call
/app/controllers/greetings_controller.rb:93 create
To test, FactoryBot will look like this;
FactoryBot.define do
factory :circuit_switch, class: 'CircuitSwitch::CircuitSwitch' do
sequence(:key) { |n| "/path/to/file:#{n}" }
sequence(:caller) { |n| "/path/to/file:#{n}" }
due_date { Date.tomorrow }
trait :initially_closed do
run_is_terminated { true }
end
end
endWhen find a problem and you want to terminate running or reporting right now, execute a task with it's caller.
You can specify either key or caller.
rake circuit_switch:terminate_to_run[your_key]
rake circuit_switch:terminate_to_report[/app/services/greetings_service:21 block in validate]
In case of not Rails applications, add following to your Rakefile:
require 'circuit_switch/tasks'Too many circuits make codes messed up. We want to remove circuits after several days, but already have too many TODO things enough to remember them.
Let's forget it! Set due_date_notifier to initializer and then call CircuitSwitch::DueDateNotifier job daily. It will notify the list of currently registered switches.
By default, due_date is 10 days after today. To modify, set due_date to initializer.
Under development :)
Bug reports and pull requests are welcome on GitHub at https://github.com/makicamel/circuit_switch. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the CircuitSwitch project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.