Class: OpalHotReloader::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/opal_hot_reloader/server.rb

Overview

Most of this lifted from github.com/saward/Rubame

Constant Summary collapse

PROGRAM =
'opal-hot-reloader'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Server

Returns a new instance of Server.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/opal_hot_reloader/server.rb', line 13

def initialize(options)
  Socket.do_not_reverse_lookup
  @hostname = '0.0.0.0'
  @port = options[:port]
  setup_directories(options)

  @reading = []
  @writing = []

  @clients = {} # Socket as key, and Client as value

  @socket = TCPServer.new(@hostname, @port)
  @reading.push @socket
end

Instance Attribute Details

#directoriesObject (readonly)

Returns the value of attribute directories.



12
13
14
# File 'lib/opal_hot_reloader/server.rb', line 12

def directories
  @directories
end

Instance Method Details

#acceptObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/opal_hot_reloader/server.rb', line 45

def accept
  socket = @socket.accept_nonblock
  @reading.push socket
  handshake = WebSocket::Handshake::Server.new
  client = Client.new(socket, handshake, self)

  while line = socket.gets
    client.handshake << line
    break if client.handshake.finished?
  end
  if client.handshake.valid?
    @clients[socket] = client
    client.write handshake.to_s
    client.opened = true
    return client
  else
    close(client)
  end
  return nil
end

#close(client) ⇒ Object



146
147
148
149
150
151
152
153
154
# File 'lib/opal_hot_reloader/server.rb', line 146

def close(client)
  @reading.delete client.socket
  @clients.delete client.socket
  begin
    client.socket.close
  rescue
  end
  client.closed = true
end

#loopObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/opal_hot_reloader/server.rb', line 95

def loop
  listener = Listen.to(*@directories, only: %r{\.(rb|s?[ac]ss)$}) do |modified, added, removed|
    modified.each { |modified_file| send_updated_file(modified_file) }
    puts "modified absolute path: #{modified}"
    puts "added absolute path: #{added}"
    puts "removed absolute path: #{removed}"
  end
  listener.start

  puts "#{PROGRAM}: starting..."
  while (!$quit)
    run do |client|
      client.onopen do
        puts "#{PROGRAM}:  client open"
      end
      client.onmessage do |mess|
        puts "PROGRAM:  message received: #{mess}"
      end
      client.onclose do
        puts "#{PROGRAM}:  client closed"
      end
    end
    sleep 0.2
  end
end

#read(client) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/opal_hot_reloader/server.rb', line 121

def read(client)

  pairs = client.socket.recvfrom(2000)
  messages = []

  if pairs[0].length == 0
    close(client)
  else
    client.frame << pairs[0]

    while f = client.frame.next
      if (f.type == :close)
        close(client)
        return messages
      else
        messages.push f
      end
    end
        
  end

  return messages

end

#run(time = 0, &blk) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/opal_hot_reloader/server.rb', line 156

def run(time = 0, &blk)
  readable, writable = IO.select(@reading, @writing, nil, 0)

  if readable
    readable.each do |socket|
      client = @clients[socket]
      if socket == @socket
        client = accept
      else
        msg = read(client)
        client.messaged = msg
      end

      blk.call(client) if client and blk
    end
  end

  # Check for lazy send items
  timer_start = Time.now
  time_passed = 0
  begin
    @clients.each do |s, c|
      c.send_some_lazy(5)
    end
    time_passed = Time.now - timer_start
  end while time_passed < time
end

#send_updated_file(modified_file) ⇒ Object



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
# File 'lib/opal_hot_reloader/server.rb', line 67

def send_updated_file(modified_file)
  if modified_file =~ /\.rb$/
    file_contents = File.read(modified_file)
    update = {
      type: 'ruby',
      filename: modified_file,
      source_code: file_contents
    }.to_json
  end
  if modified_file =~ /\.s?[ac]ss$/
    # TODO: Switch from hard-wired path assumptions to using SASS/sprockets config
    relative_path = Pathname.new(modified_file).relative_path_from(Pathname.new(Dir.pwd))
    url = relative_path.to_s
      .sub('public/','')
      .sub('/sass/','/')
      .sub(/\.s[ac]ss/, '.css')
    update = {
      type: 'css',
      filename: modified_file,
      url: url
    }.to_json
  end
  if update
    @clients.each { |socket, client| client.send(update) }
  end
end

#setup_directories(options) ⇒ Object

adds known directories automatically if they exist

  • rails js app/assets/javascripts app/assets/stylesheets

  • reactrb rails defaults app/views/components

  • you tell me and I’ll add them



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/opal_hot_reloader/server.rb', line 32

def setup_directories(options)
  @directories = options[:directories] || []
  [
    'app/assets/javascripts',
    'app/assets/stylesheets',
    'app/views/components'
  ].each { |known_dir|
    if !@directories.include?(known_dir) && File.exists?(known_dir)
      @directories << known_dir
    end
  }
end

#stopObject



184
185
186
# File 'lib/opal_hot_reloader/server.rb', line 184

def stop
  @socket.close
end