circuit_switch 0.2.0 → 0.4.0

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: d036ceb2748b3432749f5a03d83d324b59d1acb340bb7d64cbe3ef86f5531c4f
4
- data.tar.gz: 32b8e3dbe02c1eaf8e25b40ab26f97036ecf33402bf974e6b71cb989db8c3178
3
+ metadata.gz: f1e87399b178858a0b41fce787342117deab9d4a1a29c7d9262d742be2633641
4
+ data.tar.gz: fdf0d9ec8723b02fea239de819f60e98b24de9904d034a40f549bcf90d895f26
5
5
  SHA512:
6
- metadata.gz: fa10c7a2196b37619dc94605efca82c8b3ce5b3182063004911f8aa28bef44f711e945adc73f38857e85a40a6f005b1139a7fac990e76e39a45080e481a93c3f
7
- data.tar.gz: 3619df1cd293ba93f271ca536bcb55f4152a20810e52fb7aba16cd44e11b6bd081645bc1804f4f01c47a49c056a1793e840de5715f4c54126b4bdda8887f1d0c
6
+ metadata.gz: 075c7f6dfeef30c3e0faecadf30969dfa6cd1f8fd2b5e4e6f5934f0e15913ec98f4f6b8a3cf9c414a17e7b920d17ee22a01abcea18c9cf8af4bfc510562c8aba
7
+ data.tar.gz: 6df8910491782a75b873952c26e0ac5ebdb27aadcf93727fc044e1b5dd0eeb6bfcfceac04c30417b8f86b6ac1d0e6515b342dd8d0616fc0dc9f78f680816a232
data/CHANGELOG.md CHANGED
@@ -1,3 +1,60 @@
1
+ ## 0.4.0
2
+
3
+ ### Breaking Changes
4
+
5
+ * Be able to choice to notify `CircuitSwitch::CalledNotification` or `String`.
6
+ Improve `config/initializers/circuit_switch.rb` like following.
7
+
8
+ ```diff
9
+ CircuitSwitch.configure do |config|
10
+ - config.reporter = ->(message) { Bugsnag.notify(message) }
11
+ + config.reporter = ->(message, error) { Bugsnag.notify(error) }
12
+ ```
13
+
14
+ ## 0.3.0
15
+
16
+ ### Breaking Changes
17
+
18
+ * Modify `key` to unique by default.
19
+ To migrate, run next.
20
+
21
+ ```
22
+ rails generate circuit_switch:migration circuit_switch make_key_unique
23
+ rails db:migrate
24
+ ```
25
+
26
+ ### Changes
27
+
28
+ * Fix to save switch when block for `CircuitSwitch.run` raises error.
29
+
30
+ ## 0.2.2
31
+
32
+ ### New features
33
+
34
+ * Add `key_column_name` to configuration for aliasing `circuit_switches.key`.
35
+
36
+ ### Changes
37
+
38
+ * Declare dependent on ActiveSupport instead of implicitly dependent.
39
+
40
+ ## 0.2.1
41
+
42
+ ### New features
43
+
44
+ * Add `initially_closed` option to `run` and `open?`.
45
+ * Add `key` argument for easy handling for human more than caller.
46
+ To migrate, run next.
47
+
48
+ ```
49
+ rails generate circuit_switch:migration circuit_switch add_key
50
+ rails db:migrate
51
+ ```
52
+
53
+ ### Changes
54
+
55
+ * Modify log level from warn to info when default value for `close_if_reach_limit` is used.
56
+ * Suppress warning that ivar is not initialized.
57
+
1
58
  ## 0.2.0
2
59
 
3
60
  ### Breaking Changes
@@ -6,7 +63,7 @@
6
63
 
7
64
  ## 0.1.2
8
65
 
9
- * Modify `CircuitSwitch.open?` recieves same arguments as `CircuitSwtich.run`
66
+ * Modify `CircuitSwitch.open?` receives same arguments as `CircuitSwitch.run`
10
67
 
11
68
  ## 0.1.1
12
69
 
data/README.md CHANGED
@@ -37,7 +37,7 @@ rails generate circuit_switch:install
37
37
  ```
38
38
 
39
39
  Generate a migration for ActiveRecord.
40
- This table saves circuit caller, called count, limit count and so on.
40
+ This table saves named key, circuit caller, called count, limit count and so on.
41
41
 
42
42
  ```
43
43
  rails generate circuit_switch:migration circuit_switch
@@ -56,21 +56,30 @@ CircuitSwitch.run do
56
56
  end
57
57
  ```
58
58
 
59
- `run` calls received proc, and when conditions are met, closes it's circuit.
60
- To switch circuit opening and closing, some conditions can be set. By default, the circuit is always opened.
61
- 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.
62
- `run` receives optional arguments.
59
+ `run` basically calls the received proc. But when a condition is met, it closes the circuit and does not evaluate the proc.
60
+ To switch circuit opening and closing, a set of options can be set. Without options, the circuit is always open.
61
+ You can set `close_if_reach_limit: true` so that the circuit is only open for 10 invocations. The constant 10 comes from the table definition we have arbitrarily chosen. In case you need a larger number, specify it in the `limit_count` option in combination with `close_if_reach_limit: true`, or alter default constraint on `circuit_switches.run_limit_count`.
63
62
 
64
- - `if`: [Boolean, Proc] Call proc when `if` is truthy (default: true)
65
- - `close_if`: [Boolean, Proc] Call proc when `close_if` is falsy (default: false)
66
- - `close_if_reach_limit`: [Boolean] Stop calling proc when run count reaches limit (default: false)
67
- - `limit_count`: [Integer] Limit count. Use `run_limit_count` default value if it's nil (default: nil)
68
- Can't be set 0 when `close_if_reach_limit` is true
63
+ - `key`: [String] The identifier to find record by. If `key` has not been passed, `circuit_switches.caller` is chosen as an alternative.
64
+ - `if`: [Boolean, Proc] Calls proc when the value of `if` is evaluated truthy (default: true)
65
+ - `close_if`: [Boolean, Proc] Calls proc when the value of `close_if` is evaluated falsy (default: false)
66
+ - `close_if_reach_limit`: [Boolean] Stops calling proc when `circuit_switches.run_count` has reached `circuit_switches.run_limit_count` (default: false)
67
+ - `limit_count`: [Integer] Mutates `circuit_switches.run_limit_count` whose value defined in schema is 10 by default. (default: nil)
68
+ Can't be set to 0 when `close_if_reach_limit` is true. This option is only relevant when `close_if_reach_limit` is set to true.
69
+ - `initially_closed`: [Boolean] Creates switch with terminated mode (default: false)
69
70
 
70
- To close the circuit at specific date or when called 1000 times, code goes like:
71
+ To close the circuit at a specific date, code goes like:
71
72
 
72
73
  ```ruby
73
- CircuitSwitch.run(close_if: -> { Date.today >= some_day }, limit_count: 1_000) do
74
+ CircuitSwitch.run(close_if: -> { Date.today >= some_day }) do
75
+ # testing codes
76
+ end
77
+ ```
78
+
79
+ Or when the code of concern has been called 1000 times:
80
+
81
+ ```ruby
82
+ CircuitSwitch.run(close_if_reach_limit: true, limit_count: 1_000) do
74
83
  # testing codes
75
84
  end
76
85
  ```
@@ -102,15 +111,15 @@ When you just want to report, set your `reporter` to initializer and then call `
102
111
  CircuitSwitch.report(if: some_condition)
103
112
  ```
104
113
 
105
- `report` just reports the line of code is called. It doesn't receive proc. It's useful for refactoring or removing dead codes.
106
- 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.
107
- `report` receives optional arguments.
114
+ `report` just reports which line of code is called. It doesn't receive proc. It's useful for refactoring or removing dead codes.
115
+ Same as `run`, a set of options can be set. By default, this method does not send reports more than 10 times. The constant 10 comes from the table definition we have arbitrarily chosen. In case you need a larger number, specify it in the `limit_count` option, or alter default constraint on `circuit_switches.report_limit_count`.
108
116
 
109
- - `if`: [Boolean, Proc] Report when `if` is truthy (default: true)
110
- - `stop_report_if`: [Boolean, Proc] Report when `close_if` is falsy (default: false)
111
- - `stop_report_if_reach_limit`: [Boolean] Stop reporting when reported count reaches limit (default: true)
112
- - `limit_count`: [Integer] Limit count. Use `report_limit_count` default value if it's nil (default: nil)
113
- Can't be set 0 when `stop_report_if_reach_limit` is true
117
+ - `key`: [String] The identifier to find record by. If `key` has not been passed, `circuit_switches.caller` is chosen as an alternative.
118
+ - `if`: [Boolean, Proc] Reports when the value of `if` is evaluated truthy (default: true)
119
+ - `stop_report_if`: [Boolean, Proc] Reports when the value of `stop_report_if` is evaluated falsy (default: false)
120
+ - `stop_report_if_reach_limit`: [Boolean] Stops reporting when `circuit_switches.report_count` has reached `circuit_switches.report_limit_count` (default: true)
121
+ - `limit_count`: [Integer] Mutates `circuit_switches.report_limit_count` whose value defined in schema is 10 by default. (default: nil)
122
+ Can't be set to 0 when `stop_report_if_reach_limit` is true.
114
123
 
115
124
  To know about report is executed or not, you can get through `report?`.
116
125
  Of course you can chain `report` and `run` or `open?`.
@@ -129,12 +138,31 @@ called_path: /app/services/greetings_service:21 block in validate
129
138
  /app/controllers/greetings_controller.rb:93 create
130
139
  ```
131
140
 
141
+ ## Test
142
+
143
+ To test, FactoryBot will look like this;
144
+
145
+ ```ruby
146
+ FactoryBot.define do
147
+ factory :circuit_switch, class: 'CircuitSwitch::CircuitSwitch' do
148
+ sequence(:key) { |n| "/path/to/file:#{n}" }
149
+ sequence(:caller) { |n| "/path/to/file:#{n}" }
150
+ due_date { Date.tomorrow }
151
+
152
+ trait :initially_closed do
153
+ run_is_terminated { true }
154
+ end
155
+ end
156
+ end
157
+ ```
158
+
132
159
  ## Task
133
160
 
134
- When find a problem and you want to terminate running or reporting right now, execute a task with it's caller.
161
+ When find a problem and you want to terminate running or reporting right now, execute a task with it's caller.
162
+ You can specify either key or caller.
135
163
 
136
164
  ```
137
- rake circuit_switch:terminate_to_run[/app/services/greetings_service:21 block in validate]
165
+ rake circuit_switch:terminate_to_run[your_key]
138
166
  rake circuit_switch:terminate_to_report[/app/services/greetings_service:21 block in validate]
139
167
  ```
140
168
 
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_dependency 'activejob'
27
27
  spec.add_dependency 'activerecord'
28
+ spec.add_dependency 'activesupport'
28
29
  spec.add_development_dependency 'byebug'
29
30
  spec.add_development_dependency 'sqlite3'
30
31
  spec.add_development_dependency 'test-unit'
@@ -0,0 +1,66 @@
1
+ require_relative 'core'
2
+
3
+ module CircuitSwitch
4
+ class Builder < Core
5
+ def initialize
6
+ super
7
+ @run = false
8
+ @reported = false
9
+ end
10
+
11
+ def assign_runner(
12
+ key: nil,
13
+ if: true,
14
+ close_if: false,
15
+ close_if_reach_limit: nil,
16
+ limit_count: nil,
17
+ initially_closed: false
18
+ )
19
+ @key = key
20
+ @run_if = binding.local_variable_get(:if)
21
+ @close_if = close_if
22
+ @close_if_reach_limit = close_if_reach_limit
23
+ @run_limit_count = limit_count
24
+ @initially_closed = initially_closed
25
+ end
26
+
27
+ def assign_reporter(
28
+ key: nil,
29
+ if: true,
30
+ stop_report_if: false,
31
+ stop_report_if_reach_limit: true,
32
+ limit_count: nil
33
+ )
34
+ @key = key
35
+ @report_if = binding.local_variable_get(:if)
36
+ @stop_report_if = stop_report_if
37
+ @stop_report_if_reach_limit = stop_report_if_reach_limit
38
+ @report_limit_count = limit_count
39
+ end
40
+
41
+ def run(key: nil, if: nil, close_if: nil, close_if_reach_limit: nil, limit_count: nil, initially_closed: nil, &block)
42
+ arguments = {
43
+ key: key,
44
+ if: binding.local_variable_get(:if),
45
+ close_if: close_if,
46
+ close_if_reach_limit: close_if_reach_limit,
47
+ limit_count: limit_count,
48
+ initially_closed: initially_closed,
49
+ }.select { |_, v| v }
50
+ assign_runner(**arguments)
51
+ execute_run(&block)
52
+ end
53
+
54
+ def report(key: nil, if: nil, stop_report_if: nil, stop_report_if_reach_limit: nil, limit_count: nil)
55
+ arguments = {
56
+ key: key,
57
+ if: binding.local_variable_get(:if),
58
+ stop_report_if: stop_report_if,
59
+ stop_report_if_reach_limit: stop_report_if_reach_limit,
60
+ limit_count: limit_count
61
+ }.select { |_, v| v }
62
+ assign_reporter(**arguments)
63
+ execute_report
64
+ end
65
+ end
66
+ end
@@ -25,6 +25,10 @@ module CircuitSwitch
25
25
  report_if.respond_to?(:call) ? report_if.call : !!report_if
26
26
  end
27
27
 
28
+ def key_column_name=(key)
29
+ ::CircuitSwitch::CircuitSwitch.alias_attribute :key, key
30
+ end
31
+
28
32
  def due_date
29
33
  @due_date ||= Date.today + 10
30
34
  end
@@ -1,54 +1,59 @@
1
+ require_relative 'notification'
2
+ require_relative 'workers/reporter'
3
+ require_relative 'workers/run_count_updater'
4
+
1
5
  module CircuitSwitch
2
6
  class Core
3
7
  delegate :config, to: ::CircuitSwitch
8
+ attr_reader :key, :run_if, :close_if, :close_if_reach_limit, :run_limit_count, :initially_closed,
9
+ :report_if, :stop_report_if, :stop_report_if_reach_limit, :report_limit_count
4
10
 
5
- def run(
6
- if: true,
7
- close_if: false,
8
- close_if_reach_limit: nil,
9
- limit_count: nil,
10
- &block
11
- )
12
- if close_if_reach_limit && limit_count == 0
11
+ def execute_run(&block)
12
+ if close_if_reach_limit && run_limit_count == 0
13
13
  raise CircuitSwitchError.new('Can\'t set limit_count to 0 when close_if_reach_limit is true')
14
14
  end
15
15
  if close_if_reach_limit.nil?
16
- Logger.new($stdout).warn('Default value for close_if_reach_limit is modified from true to false at ver 0.2.0.')
17
- close_if_reach_limit = false
16
+ Logger.new($stdout).info('Default value for close_if_reach_limit is modified from true to false at ver 0.2.0.')
17
+ @close_if_reach_limit = false
18
18
  end
19
- return self if evaluate(close_if) || !evaluate(binding.local_variable_get(:if))
20
- return self if close_if_reach_limit && switch.reached_run_limit?(limit_count)
19
+
20
+ return self if evaluate(close_if) || !evaluate(run_if)
21
+ return self if close_if_reach_limit && switch.reached_run_limit?(run_limit_count)
21
22
  return self if switch.run_is_terminated?
22
23
 
23
- yield
24
+ unless switch.new_record? && initially_closed
25
+ yield
26
+ @run = true
27
+ end
28
+ self
29
+ ensure
24
30
  RunCountUpdater.perform_later(
25
- limit_count: limit_count,
31
+ key: key,
32
+ limit_count: run_limit_count,
26
33
  called_path: called_path,
27
- reported: reported?
34
+ reported: reported?,
35
+ initially_closed: initially_closed
28
36
  )
29
- @run = true
30
- self
31
37
  end
32
38
 
33
- def report(
34
- if: true,
35
- stop_report_if: false,
36
- stop_report_if_reach_limit: true,
37
- limit_count: nil
38
- )
39
+ def execute_report
39
40
  if config.reporter.nil?
40
41
  raise CircuitSwitchError.new('Set config.reporter.')
41
42
  end
42
- if stop_report_if_reach_limit && limit_count == 0
43
+ if config.reporter.arity == 1
44
+ Logger.new($stdout).info('config.reporter now receives 2 arguments. Improve your `config/initialzers/circuit_switch.rb`.')
45
+ end
46
+ if stop_report_if_reach_limit && report_limit_count == 0
43
47
  raise CircuitSwitchError.new('Can\'t set limit_count to 0 when stop_report_if_reach_limit is true')
44
48
  end
45
49
  return self unless config.enable_report?
46
- return self if evaluate(stop_report_if) || !evaluate(binding.local_variable_get(:if))
50
+ return self if evaluate(stop_report_if) || !evaluate(report_if)
47
51
  return self if switch.report_is_terminated?
48
- return self if stop_report_if_reach_limit && switch.reached_report_limit?(limit_count)
52
+ return self if stop_report_if_reach_limit && switch.reached_report_limit?(report_limit_count)
49
53
 
50
54
  Reporter.perform_later(
51
- limit_count: limit_count,
55
+ key: key,
56
+ limit_count: report_limit_count,
52
57
  called_path: called_path,
53
58
  run: run?
54
59
  )
@@ -58,18 +63,24 @@ module CircuitSwitch
58
63
 
59
64
  # @return [Boolean]
60
65
  def run?
61
- !!@run
66
+ @run
62
67
  end
63
68
 
64
69
  # @return [Boolean]
65
70
  def reported?
66
- !!@reported
71
+ @reported
67
72
  end
68
73
 
69
74
  private
70
75
 
71
76
  def switch
72
- @switch ||= CircuitSwitch.find_or_initialize_by(caller: called_path)
77
+ return @switch if defined? @switch
78
+
79
+ if key
80
+ @switch = CircuitSwitch.find_or_initialize_by(key: key)
81
+ else
82
+ @switch = CircuitSwitch.find_or_initialize_by(caller: called_path)
83
+ end
73
84
  end
74
85
 
75
86
  def called_path
@@ -1,4 +1,4 @@
1
- require 'circuit_switch/stacktrace_modifier'
1
+ require_relative 'stacktrace_modifier'
2
2
 
3
3
  module CircuitSwitch
4
4
  class CircuitSwitchError < RuntimeError
@@ -1,5 +1,13 @@
1
+ require 'active_record'
2
+
1
3
  module CircuitSwitch
2
4
  class CircuitSwitch < ::ActiveRecord::Base
5
+ validates :key, uniqueness: true
6
+
7
+ after_initialize do |switch|
8
+ switch.key ||= switch.caller
9
+ end
10
+
3
11
  def assign(run_limit_count: nil, report_limit_count: nil)
4
12
  self.run_limit_count = run_limit_count if run_limit_count
5
13
  self.report_limit_count = report_limit_count if report_limit_count
@@ -22,16 +30,17 @@ module CircuitSwitch
22
30
  end
23
31
  end
24
32
 
25
- def increment_run_count
33
+ def increment_run_count!
26
34
  with_writable { update!(run_count: run_count + 1) }
27
35
  end
28
36
 
29
- def increment_report_count
37
+ def increment_report_count!
30
38
  with_writable { update!(report_count: report_count + 1) }
31
39
  end
32
40
 
33
41
  def message
34
- "Watching process is called for #{report_count}th. Report until for #{report_limit_count}th."
42
+ process = key == caller ? 'Watching process' : "Process for '#{key}'"
43
+ "#{process} is called for #{report_count}th. Report until for #{report_limit_count}th."
35
44
  end
36
45
 
37
46
  private
@@ -1,4 +1,4 @@
1
- require 'circuit_switch/configuration.rb'
1
+ require 'active_support/core_ext/module/delegation'
2
2
 
3
3
  module CircuitSwitch
4
4
  class StacktraceModifier
@@ -1,40 +1,40 @@
1
1
  namespace :circuit_switch do
2
2
  desc 'Update run_is_terminated to true to close circuit'
3
- task :terminate_to_run, ['caller'] => :environment do |_, arg|
4
- called_path = arg[:caller]
5
- puts "Start to update run_is_terminated of circuit_switch for '#{called_path}' to true."
3
+ task :terminate_to_run, ['key_or_caller'] => :environment do |_, arg|
4
+ key_or_caller = arg[:key_or_caller]
5
+ puts "Start to update run_is_terminated of circuit_switch for '#{key_or_caller}' to true."
6
6
  sleep(3)
7
7
 
8
- switch = CircuitSwitch::CircuitSwitch.find_by!(caller: called_path)
8
+ switch = CircuitSwitch::CircuitSwitch.find_by(key: key_or_caller) || CircuitSwitch::CircuitSwitch.find_by!(caller: key_or_caller)
9
9
  puts "circuit_switch is found. id: #{switch.id}."
10
10
 
11
11
  switch.update(run_is_terminated: true)
12
- puts "Updated run_is_terminated of circuit_switch for '#{called_path}' to true."
12
+ puts "Updated run_is_terminated of circuit_switch for '#{key_or_caller}' to true."
13
13
  end
14
14
 
15
15
  desc 'Update report_is_terminated to true to stop reporting'
16
- task :terminate_to_report, ['caller'] => :environment do |_, arg|
17
- called_path = arg[:caller]
18
- puts "Start to update report_is_terminated of circuit_switch for '#{called_path}' to true."
16
+ task :terminate_to_report, ['key_or_caller'] => :environment do |_, arg|
17
+ key_or_caller = arg[:key_or_caller]
18
+ puts "Start to update report_is_terminated of circuit_switch for '#{key_or_caller}' to true."
19
19
  sleep(3)
20
20
 
21
- switch = CircuitSwitch::CircuitSwitch.find_by!(caller: called_path)
21
+ switch = CircuitSwitch::CircuitSwitch.find_by(key: key_or_caller) || CircuitSwitch::CircuitSwitch.find_by!(caller: key_or_caller)
22
22
  puts "circuit_switch is found. id: #{switch.id}."
23
23
 
24
24
  switch.update(report_is_terminated: true)
25
- puts "Updated report_is_terminated of circuit_switch for '#{called_path}' to true."
25
+ puts "Updated report_is_terminated of circuit_switch for '#{key_or_caller}' to true."
26
26
  end
27
27
 
28
28
  desc 'Delete switch'
29
- task :delete_switch, ['caller'] => :environment do |_, arg|
30
- called_path = arg[:caller]
31
- puts "Start to delete circuit_switch for '#{called_path}'."
29
+ task :delete_switch, ['key_or_caller'] => :environment do |_, arg|
30
+ key_or_caller = arg[:key_or_caller]
31
+ puts "Start to delete circuit_switch for '#{key_or_caller}'."
32
32
  sleep(3)
33
33
 
34
- switch = CircuitSwitch::CircuitSwitch.find_by!(caller: called_path)
34
+ switch = CircuitSwitch::CircuitSwitch.find_by(key: key_or_caller) || CircuitSwitch::CircuitSwitch.find_by!(caller: key_or_caller)
35
35
  puts "circuit_switch is found. id: #{switch.id}."
36
36
 
37
37
  switch.destroy!
38
- puts "Successfully deleted circuit_switch for '#{called_path}'."
38
+ puts "Successfully deleted circuit_switch for '#{key_or_caller}'."
39
39
  end
40
40
  end
@@ -1,3 +1,3 @@
1
1
  module CircuitSwitch
2
- VERSION = '0.2.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -10,7 +10,7 @@ module CircuitSwitch
10
10
  circuit_switches = CircuitSwitch.where('due_date <= ?', Date.today).order(id: :asc)
11
11
  if circuit_switches.present?
12
12
  message = "Due date has come! Let's consider about removing switches and cleaning up code! :)\n" +
13
- circuit_switches.map { |switch| "id: #{switch.id}, caller: '#{switch.caller}' , created_at: #{switch.created_at}" }.join("\n")
13
+ circuit_switches.map { |switch| "id: #{switch.id}, key: '#{switch.key}', caller: '#{switch.caller}', created_at: #{switch.created_at}" }.join("\n")
14
14
  config.due_date_notifier.call(message)
15
15
  else
16
16
  switches_count = CircuitSwitch.all.size
@@ -1,21 +1,38 @@
1
+ require 'active_job'
2
+ require 'active_support/core_ext/module/delegation'
3
+
1
4
  module CircuitSwitch
2
5
  class Reporter < ::ActiveJob::Base
3
6
  delegate :config, to: ::CircuitSwitch
4
7
 
5
- def perform(limit_count:, called_path:, run:)
6
- circuit_switch =
7
- if run
8
- # Wait for RunCountUpdater saves circuit_switch
9
- sleep(3)
10
- CircuitSwitch.find_by!(caller: called_path)
11
- else
12
- CircuitSwitch.find_or_initialize_by(caller: called_path)
8
+ def perform(key:, limit_count:, called_path:, run:)
9
+ # Wait for RunCountUpdater saves circuit_switch
10
+ sleep(3) if run
11
+
12
+ first_raise = true
13
+ begin
14
+ circuit_switch = key ? CircuitSwitch.find_by(key: key) : CircuitSwitch.find_by(caller: called_path)
15
+ if run && circuit_switch.nil?
16
+ raise ActiveRecord::RecordNotFound.new('Couldn\'t find CircuitSwitch::CircuitSwitch')
17
+ end
18
+
19
+ circuit_switch ||= CircuitSwitch.new(key: key, caller: called_path)
20
+ circuit_switch.due_date ||= config.due_date
21
+ circuit_switch.assign(report_limit_count: limit_count).increment_report_count!
22
+ raise CalledNotification.new(circuit_switch.message)
23
+ rescue ActiveRecord::RecordInvalid => e
24
+ raise e unless first_raise
25
+
26
+ first_raise = false
27
+ sleep(2)
28
+ retry
29
+ rescue CalledNotification => notification
30
+ if config.reporter.arity == 1
31
+ config.reporter.call(notification.to_message(called_path: called_path))
32
+ else
33
+ config.reporter.call(notification.to_message(called_path: called_path), notification)
34
+ end
13
35
  end
14
- circuit_switch.due_date ||= config.due_date
15
- circuit_switch.assign(report_limit_count: limit_count).increment_report_count
16
- raise CalledNotification.new(circuit_switch.message)
17
- rescue CalledNotification => notification
18
- config.reporter.call(notification.to_message(called_path: called_path))
19
36
  end
20
37
  end
21
38
  end
@@ -1,18 +1,31 @@
1
+ require 'active_job'
2
+ require 'active_support/core_ext/module/delegation'
3
+
1
4
  module CircuitSwitch
2
5
  class RunCountUpdater < ::ActiveJob::Base
3
6
  delegate :config, to: ::CircuitSwitch
4
7
 
5
- def perform(limit_count:, called_path:, reported:)
6
- circuit_switch =
7
- if reported
8
- # Wait for Reporter saves circuit_switch
9
- sleep(3)
10
- CircuitSwitch.find_by!(caller: called_path)
11
- else
12
- CircuitSwitch.find_or_initialize_by(caller: called_path)
8
+ def perform(key:, limit_count:, called_path:, reported:, initially_closed:)
9
+ # Wait for Reporter saves circuit_switch
10
+ sleep(3) if reported
11
+
12
+ first_raise = true
13
+ begin
14
+ circuit_switch = key ? CircuitSwitch.find_by(key: key) : CircuitSwitch.find_by(caller: called_path)
15
+ if reported && circuit_switch.nil?
16
+ raise ActiveRecord::RecordNotFound.new('Couldn\'t find CircuitSwitch::CircuitSwitch')
13
17
  end
14
- circuit_switch.due_date ||= config.due_date
15
- circuit_switch.assign(run_limit_count: limit_count).increment_run_count
18
+
19
+ circuit_switch ||= CircuitSwitch.new(key: key, caller: called_path, run_is_terminated: initially_closed)
20
+ circuit_switch.due_date ||= config.due_date
21
+ circuit_switch.assign(run_limit_count: limit_count).increment_run_count!
22
+ rescue ActiveRecord::RecordInvalid => e
23
+ raise e unless first_raise
24
+
25
+ first_raise = false
26
+ sleep(2)
27
+ retry
28
+ end
16
29
  end
17
30
  end
18
31
  end
@@ -1,12 +1,9 @@
1
- require "circuit_switch/configuration.rb"
2
- require "circuit_switch/core"
3
- require "circuit_switch/notification"
4
- require "circuit_switch/orm/active_record/circuit_switch"
5
- require "circuit_switch/railtie" if defined?(Rails::Railtie)
6
- require "circuit_switch/version"
7
- require "circuit_switch/workers/due_date_notifier"
8
- require "circuit_switch/workers/reporter"
9
- require "circuit_switch/workers/run_count_updater"
1
+ require_relative 'circuit_switch/configuration'
2
+ require_relative 'circuit_switch/builder'
3
+ require_relative 'circuit_switch/orm/active_record/circuit_switch'
4
+ require_relative 'circuit_switch/railtie' if defined?(Rails::Railtie)
5
+ require_relative 'circuit_switch/version'
6
+ require_relative 'circuit_switch/workers/due_date_notifier'
10
7
 
11
8
  module CircuitSwitch
12
9
  class << self
@@ -18,73 +15,69 @@ module CircuitSwitch
18
15
  @config ||= Configuration.new
19
16
  end
20
17
 
18
+ # @param key [String] Named key to find switch instead of caller
21
19
  # @param if [Boolean, Proc] Call proc when `if` is truthy (default: true)
22
20
  # @param close_if [Boolean, Proc] Call proc when `close_if` is falsy (default: false)
23
21
  # @param close_if_reach_limit [Boolean] Stop calling proc when run count reaches limit (default: false)
24
22
  # @param limit_count [Integer] Limit count. Use `run_limit_count` default value if it's nil
25
23
  # Can't be set 0 when `close_if_reach_limit` is true (default: nil)
24
+ # @param initially_closed [Boolean] Create switch with terminated mode (default: false)
26
25
  # @param [Proc] block
27
- def run(
28
- if: true,
29
- close_if: false,
30
- close_if_reach_limit: nil,
31
- limit_count: nil,
32
- &block
33
- )
34
- Core.new.run(
26
+ def run(key: nil, if: nil, close_if: nil, close_if_reach_limit: nil, limit_count: nil, initially_closed: nil, &block)
27
+ arguments = {
28
+ key: key,
35
29
  if: binding.local_variable_get(:if),
36
30
  close_if: close_if,
37
31
  close_if_reach_limit: close_if_reach_limit,
38
32
  limit_count: limit_count,
39
- &block
40
- )
33
+ initially_closed: initially_closed,
34
+ }.select { |_, v| v }
35
+ Builder.new.run(**arguments, &block)
41
36
  end
42
37
 
38
+ # @param key [String] Named key to find switch instead of caller
43
39
  # @param if [Boolean, Proc] Report when `if` is truthy (default: true)
44
40
  # @param stop_report_if [Boolean, Proc] Report when `close_if` is falsy (default: false)
45
41
  # @param stop_report_if_reach_limit [Boolean] Stop reporting when reported count reaches limit (default: true)
46
42
  # @param limit_count [Integer] Limit count. Use `report_limit_count` default value if it's nil
47
43
  # Can't be set 0 when `stop_report_if_reach_limit` is true (default: nil)
48
- def report(
49
- if: true,
50
- stop_report_if: false,
51
- stop_report_if_reach_limit: true,
52
- limit_count: nil
53
- )
44
+ def report(key: nil, if: nil, stop_report_if: nil, stop_report_if_reach_limit: nil, limit_count: nil)
54
45
  if block_given?
55
46
  raise ArgumentError.new('CircuitSwitch.report doesn\'t receive block. Use CircuitSwitch.run if you want to pass block.')
56
47
  end
57
48
 
58
- Core.new.report(
49
+ arguments = {
50
+ key: key,
59
51
  if: binding.local_variable_get(:if),
60
52
  stop_report_if: stop_report_if,
61
53
  stop_report_if_reach_limit: stop_report_if_reach_limit,
62
54
  limit_count: limit_count
63
- )
55
+ }.select { |_, v| v }
56
+ Builder.new.report(**arguments)
64
57
  end
65
58
 
66
59
  # Syntax sugar for `CircuitSwitch.run`
60
+ # @param key [String] Named key to find switch instead of caller
67
61
  # @param if [Boolean, Proc] `CircuitSwitch.run` is runnable when `if` is truthy (default: true)
68
62
  # @param close_if [Boolean, Proc] `CircuitSwitch.run` is runnable when `close_if` is falsy (default: false)
69
63
  # @param close_if_reach_limit [Boolean] `CircuitSwitch.run` is NOT runnable when run count reaches limit (default: true)
70
64
  # @param limit_count [Integer] Limit count. Use `run_limit_count` default value if it's nil. Can't be set 0 (default: nil)
65
+ # @param initially_closed [Boolean] Create switch with terminated mode (default: false)
71
66
  # @return [Boolean]
72
- def open?(
73
- if: true,
74
- close_if: false,
75
- close_if_reach_limit: true,
76
- limit_count: nil
77
- )
67
+ def open?(key: nil, if: nil, close_if: nil, close_if_reach_limit: nil, limit_count: nil, initially_closed: nil)
78
68
  if block_given?
79
69
  raise ArgumentError.new('CircuitSwitch.open doesn\'t receive block. Use CircuitSwitch.run if you want to pass block.')
80
70
  end
81
71
 
82
- Core.new.run(
72
+ arguments = {
73
+ key: key,
83
74
  if: binding.local_variable_get(:if),
84
75
  close_if: close_if,
85
76
  close_if_reach_limit: close_if_reach_limit,
86
- limit_count: limit_count
87
- ) {}.run?
77
+ limit_count: limit_count,
78
+ initially_closed: initially_closed,
79
+ }.select { |_, v| v }
80
+ Builder.new.run(**arguments) {}.run?
88
81
  end
89
82
  end
90
83
  end
@@ -4,9 +4,19 @@ module CircuitSwitch
4
4
  class MigrationGenerator < ActiveRecord::Generators::Base
5
5
  desc 'Create a migration to manage circuit switches state'
6
6
  source_root File.expand_path('templates', __dir__)
7
+ argument :migration_type, required: false, type: :array, default: ['create'],
8
+ desc: 'Type of migration to create or add key column or make key unique. By default to create.',
9
+ banner: 'create or add_key'
7
10
 
8
11
  def generate_migration
9
- migration_template 'migration.rb.erb', 'db/migrate/create_circuit_switches.rb', migration_version: migration_version
12
+ case migration_type
13
+ when ['add_key']
14
+ migration_template 'add_key.rb.erb', 'db/migrate/add_key_to_circuit_switches.rb', migration_version: migration_version
15
+ when ['make_key_unique']
16
+ migration_template 'make_key_unique.rb.erb', 'db/migrate/make_key_unique_for_circuit_switches.rb', migration_version: migration_version
17
+ else
18
+ migration_template 'migration.rb.erb', 'db/migrate/create_circuit_switches.rb', migration_version: migration_version
19
+ end
10
20
  end
11
21
 
12
22
  def migration_version
@@ -0,0 +1,13 @@
1
+ class AddKeyToCircuitSwitches < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ add_column :circuit_switches, :key, :string, after: :id
4
+ CircuitSwitch::CircuitSwitch.all.each { |switch| switch.update_column(:key, switch.caller) }
5
+ change_column_null :circuit_switches, :key, false
6
+ add_index :circuit_switches, :key
7
+ end
8
+
9
+ def down
10
+ remove_index :circuit_switches, :key
11
+ remove_column :circuit_switches, :key
12
+ end
13
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  CircuitSwitch.configure do |config|
4
4
  # Specify proc to call your report tool: like;
5
- # config.reporter = -> (message) { Bugsnag.notify(message) }
5
+ # config.reporter = -> (message, error) { Bugsnag.notify(error) }
6
+ # config.reporter = -> (message, error) { Sentry::Rails.capture_message(message) }
6
7
  config.reporter = nil
7
8
 
8
9
  # Condition to report
@@ -16,6 +17,9 @@ CircuitSwitch.configure do |config|
16
17
  # Excluded paths to report
17
18
  # config.silent_paths = [CIRCUIT_SWITCH]
18
19
 
20
+ # Alias column name for circuit_switches.key through alias_attribute
21
+ # config.key_column_name = :key
22
+
19
23
  # Notifier to notify circuit_switch's due_date come and it's time to clean code!
20
24
  # Specify proc to call your report tool: like;
21
25
  # config.due_date_notifier = -> (message) { Slack::Web::Client.new.chat_postMessage(channel: '#your_channel', text: message) }
@@ -26,10 +30,10 @@ CircuitSwitch.configure do |config|
26
30
 
27
31
  # Option to contain error backtrace for report
28
32
  # You don't need backtrace when you report to some bug report tool.
29
- # You may be want backtrace when report to plain feed; e.g. Slack or email.
33
+ # You may want backtrace when reporting to a plain feed; e.g. Slack or email.
30
34
  # config.with_backtrace = false
31
35
 
32
- # Allowd backtrace paths to report
36
+ # Allowed backtrace paths to report
33
37
  # Specify with `with_backtrace` option.
34
38
  # Allowed all paths when set `[]`.
35
39
  # config.allowed_backtrace_paths = [Dir.pwd]
@@ -0,0 +1,11 @@
1
+ class MakeKeyUniqueForCircuitSwitches < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ remove_index :circuit_switches, :key
4
+ add_index :circuit_switches, :key, unique: true
5
+ end
6
+
7
+ def down
8
+ remove_index :circuit_switches, :key
9
+ add_index :circuit_switches, :key
10
+ end
11
+ end
@@ -1,6 +1,7 @@
1
1
  class CreateCircuitSwitches < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table :circuit_switches do |t|
4
+ t.string :key, null: false
4
5
  t.string :caller, null: false
5
6
  t.integer :run_count, default: 0, null: false
6
7
  t.integer :run_limit_count, default: 10, null: false
@@ -11,5 +12,7 @@ class CreateCircuitSwitches < ActiveRecord::Migration<%= migration_version %>
11
12
  t.date :due_date, null: false
12
13
  t.timestamps
13
14
  end
15
+
16
+ add_index :circuit_switches, [:key], unique: true
14
17
  end
15
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circuit_switch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - makicamel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-13 00:00:00.000000000 Z
11
+ date: 2021-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: byebug
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +128,7 @@ files:
114
128
  - bin/setup
115
129
  - circuit_switch.gemspec
116
130
  - lib/circuit_switch.rb
131
+ - lib/circuit_switch/builder.rb
117
132
  - lib/circuit_switch/configuration.rb
118
133
  - lib/circuit_switch/core.rb
119
134
  - lib/circuit_switch/notification.rb
@@ -128,7 +143,9 @@ files:
128
143
  - lib/circuit_switch/workers/run_count_updater.rb
129
144
  - lib/generators/circuit_switch/install_generator.rb
130
145
  - lib/generators/circuit_switch/migration_generator.rb
146
+ - lib/generators/circuit_switch/templates/add_key.rb.erb
131
147
  - lib/generators/circuit_switch/templates/initializer.rb
148
+ - lib/generators/circuit_switch/templates/make_key_unique.rb.erb
132
149
  - lib/generators/circuit_switch/templates/migration.rb.erb
133
150
  homepage: https://github.com/makicamel/circuit_switch
134
151
  licenses: