File: abstract.rb

package info (click to toggle)
ruby-spring 2.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 428 kB
  • sloc: ruby: 3,373; sh: 9; makefile: 7
file content (117 lines) | stat: -rw-r--r-- 2,691 bytes parent folder | download | duplicates (3)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
require "set"
require "pathname"
require "mutex_m"

module Spring
  module Watcher
    # A user of a watcher can use IO.select to wait for changes:
    #
    #   watcher = MyWatcher.new(root, latency)
    #   IO.select([watcher]) # watcher is running in background
    #   watcher.stale? # => true
    class Abstract
      include Mutex_m

      attr_reader :files, :directories, :root, :latency

      def initialize(root, latency)
        super()

        @root        = File.realpath(root)
        @latency     = latency
        @files       = Set.new
        @directories = Set.new
        @stale       = false
        @listeners   = []

        @on_debug    = nil
      end

      def on_debug(&block)
        @on_debug = block
      end

      def debug
        @on_debug.call(yield) if @on_debug
      end

      def add(*items)
        debug { "watcher: add: #{items.inspect}" }

        items = items.flatten.map do |item|
          item = Pathname.new(item)

          if item.relative?
            Pathname.new("#{root}/#{item}")
          else
            item
          end
        end

        items = items.select do |item|
          if item.symlink?
            item.readlink.exist?.tap do |exists|
              if !exists
                debug { "add: ignoring dangling symlink: #{item.inspect} -> #{item.readlink.inspect}" }
              end
            end
          else
            item.exist?
          end
        end

        synchronize {
          items.each do |item|
            if item.directory?
              directories << item.realpath.to_s
            else
              begin
                files << item.realpath.to_s
              rescue Errno::ENOENT
                # Race condition. Ignore symlinks whose target was removed
                # since the check above, or are deeply chained.
                debug { "add: ignoring now-dangling symlink: #{item.inspect} -> #{item.readlink.inspect}" }
              end
            end
          end

          subjects_changed
        }
      end

      def stale?
        @stale
      end

      def on_stale(&block)
        debug { "added listener: #{block.inspect}" }
        @listeners << block
      end

      def mark_stale
        return if stale?
        @stale = true
        debug { "marked stale, calling listeners: listeners=#{@listeners.inspect}" }
        @listeners.each(&:call)
      end

      def restart
        debug { "restarting" }
        stop
        start
      end

      def start
        raise NotImplementedError
      end

      def stop
        raise NotImplementedError
      end

      def subjects_changed
        raise NotImplementedError
      end
    end
  end
end