Module: Stud
- Extended by:
- Stud
- Included in:
- Stud
- Defined in:
- lib/stud/secret.rb,
lib/stud/try.rb,
lib/stud/pool.rb,
lib/stud/task.rb,
lib/stud/trap.rb,
lib/stud/with.rb,
lib/stud/buffer.rb,
lib/stud/interval.rb,
lib/stud/temporary.rb
Overview
A class for holding a secret. The main goal is to prevent the common mistake of accidentally logging or printing passwords or other secrets.
See <github.com/jordansissel/software-patterns/blob/master/dont-log-secrets/ruby/> for a discussion of why this implementation is useful.
Defined Under Namespace
Modules: Buffer, Temporary, With Classes: Pool, Secret, Task, Try
Constant Summary collapse
- TRY =
class Stud::Try
Try.new
- STUD_STOP_REQUESTED =
:stud_stop_requested
Class Method Summary collapse
-
.interval(time, opts = {}, &block) ⇒ Object
This implementation tries to keep clock more accurately.
-
.simulate_signal(signal) ⇒ Object
Simulate a signal.
-
.stop!(target = Thread.current) ⇒ Object
stop! instructs interval to stop and exit its execution loop before going to sleep between block executions.
-
.stop?(target = Thread.current) ⇒ Boolean
(also: interrupted?)
stop? returns true if stop! has been called.
-
.stoppable_sleep(duration, stop_condition_interval = 1.0, &stop_condition_block) ⇒ Numeric
stoppable_sleep will try to sleep for the given duration seconds (which may be any number, including a Float with fractional seconds).
-
.trap(signal, &block) ⇒ Object
Bind a block to be called when a certain signal is received.
-
.untrap(signal, id) ⇒ Object
Remove a previously set signal trap.
Instance Method Summary collapse
-
#interval(time, opts = {}, &block) ⇒ Object
def interval.
-
#try(enumerable = Stud::Try::FOREVER, exceptions = Try::DEFAULT_CATCHABLE_EXCEPTIONS, &block) ⇒ Object
A simple try method for the common case.
Class Method Details
.interval(time, opts = {}, &block) ⇒ Object
This implementation tries to keep clock more accurately. Prior implementations still permitted skew, where as this one will attempt to correct for skew.
The execution patterns of this method should be that the start time of ‘block.call’ should always be at time T*interval
12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/stud/interval.rb', line 12 def self.interval(time, opts = {}, &block) start = Time.now while true if opts[:sleep_then_run] start = sleep_for_interval(time, start) break if stop? block.call else block.call start = sleep_for_interval(time, start) break if stop? end end # loop forever end |
.simulate_signal(signal) ⇒ Object
Simulate a signal. This lets you force an interrupt without sending a signal to yourself.
44 45 46 47 |
# File 'lib/stud/trap.rb', line 44 def self.simulate_signal(signal) #puts "Simulate: #{signal} w/ #{@traps[signal].count} callbacks" @traps[signal].each(&:call) end |
.stop!(target = Thread.current) ⇒ Object
stop! instructs interval to stop and exit its execution loop before going to sleep between block executions. NOW the tricky part is: this is typically an operation that will be called from another thread than the thread running the interval loop in which case the target parameter must be set to the Thread object which is running the interval loop. Note that the stop logic is compatible with Stud::Task so if interval is run inside a Stud::Task, calling Stud::Task#stop! will stop the interval the same way as calling stop! on the interval itself.
41 42 43 44 45 46 |
# File 'lib/stud/interval.rb', line 41 def self.stop!(target = Thread.current) # setting/getting threalocal var is thread safe in JRuby target[STUD_STOP_REQUESTED] = true target.wakeup nil end |
.stop?(target = Thread.current) ⇒ Boolean Also known as: interrupted?
stop? returns true if stop! has been called
51 52 53 54 |
# File 'lib/stud/interval.rb', line 51 def self.stop?(target = Thread.current) # setting/getting threalocal var is thread safe in JRuby target[STUD_STOP_REQUESTED] end |
.stoppable_sleep(duration, stop_condition_interval = 1.0, &stop_condition_block) ⇒ Numeric
stoppable_sleep will try to sleep for the given duration seconds (which may be any number, including a Float with fractional seconds). an optional stop_condition_block can be supplied to verify for sleep interruption if the block returns a truthy value. if not block is supplied it will check for the Stud.stop? condition. this check will be performed at 1s interval by default or you can supply a different stop_condition_interval.
note that to achieve this, stoppable_sleep will actually perform a series of incremental sleeps but will try accurately spend the requested duration period in the overall stoppable_sleep method call. in other words this means that the duration supplied will be accurate for the time spent in the stoppable_sleep method not the actual total time spent in the underlying multiple sleep calls.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/stud/interval.rb', line 76 def self.stoppable_sleep(duration, stop_condition_interval = 1.0, &stop_condition_block) sleep_start = Time.now # default to using Stud.stop? as the condition block stop_condition_block ||= lambda { stop? } while (remaining_duration = (duration - (Time.now - sleep_start))) >= stop_condition_interval # sleep X if there is more than X remaining to sleep in relation to the loop start time sleep(stop_condition_interval) return(Time.now - sleep_start) if stop_condition_block.call end # here we know we have less than 1s reminding to sleep, sleep(remaining_duration) if remaining_duration > 0.0 Time.now - sleep_start end |
.trap(signal, &block) ⇒ Object
Bind a block to be called when a certain signal is received.
Same arguments to Signal::trap.
The behavior of this method is different than Signal::trap because multiple handlers can request notification for the same signal.
For example, this is valid:
Stud.trap("INT") { puts "Hello" }
Stud.trap("INT") { puts "World" }
When SIGINT is received, both callbacks will be invoked, in order.
This helps avoid the situation where a library traps a signal outside of your control.
If something has already used Signal::trap, that callback will be saved and scheduled the same way as any other Stud::trap.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/stud/trap.rb', line 21 def self.trap(signal, &block) @traps ||= Hash.new { |h,k| h[k] = [] } if !@traps.include?(signal) # First trap call for this signal, tell ruby to invoke us. previous_trap = Signal::trap(signal) { simulate_signal(signal) } # If there was a previous trap (via Kernel#trap) set, make sure we remember it. if previous_trap.is_a?(Proc) # MRI's default traps are "DEFAULT" string # JRuby's default traps are Procs with a source_location of "(internal") if RUBY_ENGINE != "jruby" || previous_trap.source_location.first != "(internal)" @traps[signal] << previous_trap end end end @traps[signal] << block return block.object_id end |
.untrap(signal, id) ⇒ Object
Remove a previously set signal trap.
‘signal’ is the name of the signal (“INT”, etc) ‘id’ is the value returned by a previous Stud.trap() call
53 54 55 56 57 58 59 60 61 |
# File 'lib/stud/trap.rb', line 53 def self.untrap(signal, id) @traps[signal].delete_if { |block| block.object_id == id } # Restore the default handler if there are no custom traps anymore. if @traps[signal].empty? @traps.delete(signal) Signal::trap(signal, "DEFAULT") end end |
Instance Method Details
#interval(time, opts = {}, &block) ⇒ Object
def interval
27 28 29 |
# File 'lib/stud/interval.rb', line 27 def interval(time, opts = {}, &block) Stud.interval(time, opts, &block) end |
#try(enumerable = Stud::Try::FOREVER, exceptions = Try::DEFAULT_CATCHABLE_EXCEPTIONS, &block) ⇒ Object
A simple try method for the common case.
122 123 124 |
# File 'lib/stud/try.rb', line 122 def try(enumerable=Stud::Try::FOREVER, exceptions=Try::DEFAULT_CATCHABLE_EXCEPTIONS, &block) return TRY.try(enumerable, exceptions, &block) end |