Ba665f86f90a1982af7b9c857418fcdb

Loop a block of code every set interval:

Every.twenty.milliseconds { #stuff }
Every.second { #stuff }
Every.two.minutes { #stuff }
Every.twenty.four.hours { #stuff }

You get the idea..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
module Every
  @interval, @stopped = 0.0, true
  @numwords = {"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5, "six"=>6, "seven"=>7, "eight"=>8, "nine"=>9,
                        "ten"=>10, "eleven"=>11, "twelve"=>12, "thirteen"=>13, "fourteen"=>14, "fifteen"=>15, "sixteen"=>16, "seventeen"=>17, "eighteen"=>18, "nineteen"=>19,
                        "twenty"=>20, "thirty"=>30, "forty"=>40, "fifty"=>50}
  protected #todo: figure out a better repeat_every function and make accessable only to the Every module
    def time_block
      start_time = Time.now
      yield if block_given?
      return Time.now - start_time
    end
    def repeat_every(seconds)
      while !@stopped do
        sleep( seconds - time_block { yield if block_given? } ) # again ignoring time > seconds
      end
    end
    
  public    
    def break
      @stopped, @interval = true, 0.01
    end

    def method_missing(meth, *args, &block)
      meth = meth.to_s.downcase
      
      if meth == "millisecond"
        @stopped, @interval = false, 0.01
        repeat_every(@interval) { block.call(self) if block }
      elsif meth == "second"
        @stopped, @interval = false, 1
        repeat_every(@interval) { block.call(self) if block }
      elsif meth == "minute"
        @stopped, @interval = false, 60
        repeat_every(@interval) { block.call(self) if block }
      elsif meth == "hour"
        @stopped, @interval = false, 3600
        repeat_every(@interval) { block.call(self) if block }
      elsif meth == "milliseconds"
        @stopped, @interval = false, @interval / 60
        repeat_every(@interval) { block.call(self) if block }
        @stopped, @interval = true, 0
      elsif meth == ("seconds" || "minutes" || "hours")
        @stopped = false
        repeat_every(@interval) { block.call(self) if block }
        @stopped, @interval = true, 0
      elsif meth == "stopped?"
        @stopped
      elsif meth == "interval"
        @interval
      elsif @numwords[meth]
        @interval += @numwords[meth]
        self
      else
        @stopped, @interval = true, 0
        raise("\n  Method missin': \"#{meth}\"\n  You prolly fucked up... srsly wtf?\n")
      end
    end

    module_function :method_missing, :break, :time_block, :repeat_every
end

Refactorings

No refactoring yet !

F22c48808ddcb56564889bb39f755717

elliottcable

June 15, 2008, June 15, 2008 06:17, permalink

No rating. Login to rate!

Make sure to read here about the threadsafety and strict clocking I worked in: http://pastie.org/215285

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# Used to repeat a block at a given interval, in seconds.
#   Repeat::Every 30 do
#     puts "I will be printed every thirty seconds"
#   end
# Can be told to not abide slow iterations, and continue on according to
# schedule regardless of the state of block-execute iterations:
#   Repeat::Every 4 do
#     sleep 5; puts "I completed after the next block was initiated"
#   end
# Finally, some convenience methods are proffered for working with the seconds:
#   Repeat::Every 2.hours do; end
#   Repeat::Every 3.days do; end
#   Repeat::Every 140.milliseconds do; end
module Repeat
  StretchMarks = { # In seconds
    :millisecond  =>        0.001,
    :centisecond  =>        0.01,
    :second       =>        1.0,
    :minute       =>       59.83617,
    :hour         =>     3590.1702,
    :day          =>   86_164.09,
    :week         =>  603_148.63
  }
  
  class <<self
    def Every period, opts = {}, &action
      begin
        while true
          act = Thread.new &action
          hold = Thread.new { sleep period }
          
          # Strict timekeeping: Takes :strict => tru in the opts, which
          # causes it to ignore slow blocks. Otherwise, it will continue to
          # wait for slow blocks to evaluate before initiating the next block,
          # even if they exceed the allowed iteration time.
          unless opts[:strict]
            [act, hold].map {|t| t.join }
          else
            act.run
            hold.join
          end
        end
      rescue Interrupt
        puts "\n" # Cleanly exit the loop.
      end
    end
  end
  
  module Every
    class <<self
      # Unfortunately, we can't do a metaprogrammy bit of magic here, due to the
      # fact that blocks in 1.8 can't take other blocks as arguments. Hence, you
      # have to do some lambda magic like this to use the singualar syntax:
      #   Repeat::Every.minute(lambda do
      #     puts 'whee'
      #   end)
      ::Repeat::StretchMarks.each do |stretch, period|
      
        define_method stretch do |proc|
          ::Repeat::Every 1.send(stretch.to_s + 's'), &proc
        end
      
      end
    end
  end
  
  class ::Fixnum
    ::Repeat::StretchMarks.each do |stretch, period|
      
      define_method(stretch.to_s + 's') do
        period * self
      end
      
    end
  end
  
end

Your refactoring





Format Copy from initial code

or Cancel