Class: Nrepl::Repl

Inherits:
Object
  • Object
show all
Defined in:
lib/nrepl/repl.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port) ⇒ Repl

Returns a new instance of Repl.



55
56
57
58
59
# File 'lib/nrepl/repl.rb', line 55

def initialize(port)
  @port = port
  @session = nil
  @debug = false
end

Instance Attribute Details

#debugObject

Returns the value of attribute debug.



9
10
11
# File 'lib/nrepl/repl.rb', line 9

def debug
  @debug
end

#sessionObject

Returns the value of attribute session.



8
9
10
# File 'lib/nrepl/repl.rb', line 8

def session
  @session
end

Class Method Details

.connect(port) ⇒ Nrepl::Repl

Connects to an already running nrepl server

Parameters:

  • port (Fixnum)

    The port the server is running on

Returns:



16
17
18
# File 'lib/nrepl/repl.rb', line 16

def self.connect(port)
  new(port)
end

.start(port = nil) ⇒ Nrepl::Repl

Starts a new clojure process at the current working directory

Parameters:

  • port (Fixnum, nil) (defaults to: nil)

    An optional port number, otherwise leiningen will pick the port

Returns:



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
# File 'lib/nrepl/repl.rb', line 26

def self.start(port=nil)
  read, write = IO.pipe
  
  pid = fork do
    read.close
    Process.setsid
    STDOUT.reopen write
    STDERR.reopen write
    STDIN.reopen  "/dev/null", "r"
    exec "bash -c 'lein repl :headless :port #{port}'"
  end
  
  write.close
  line = read.gets
  case line
  when /Address already in use/ ;
    # no-op, just connect to existing repl
  when /nREPL server started on port (\d+)/
    port = $~[1].to_i
  else
    raise "Unknown error launching repl: #{line}"
    Process.kill(pid)
    Process.waitpid(pid)
  end
  read.close

  new(port)
end

Instance Method Details

#clone_session(session = nil) ⇒ String

Clones the bindings for the provided session and sets the new session to current

Parameters:

  • session (String, nil) (defaults to: nil)

    The optional prototype session to clone from

Returns:

  • (String)

    the new session id



80
81
82
83
84
# File 'lib/nrepl/repl.rb', line 80

def clone_session(session=nil)
  response = send(op:"clone", session:session).first
  @session = response["new-session"]
  @session
end

#close_session(session = nil) ⇒ Object

Closes the provided sessions

Parameters:

  • session (String, nil) (defaults to: nil)

    An optional session identifier, otherwise the current session will be closed



91
92
93
# File 'lib/nrepl/repl.rb', line 91

def close_session(session=nil)
  result = send(op:"close", session:session)
end

#debug!Object



61
62
63
64
# File 'lib/nrepl/repl.rb', line 61

def debug!
  @debug = true
  self
end

#eval(code, &block) ⇒ Object

Evaluates code on the repl, returning an array of responses

Parameters:

  • code (String)

    The code to run



108
109
110
# File 'lib/nrepl/repl.rb', line 108

def eval(code, &block)
  result = send(op:"eval", code:code, &block)
end

#get_socket(tries = 3) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/nrepl/repl.rb', line 150

def get_socket(tries = 3)
  retriable on: [Errno::ECONNREFUSED], tries: tries, interval: 3 do
    TCPSocket.new("localhost", @port)
  end
rescue Errno::ECONNREFUSED
  nil
end

#list_sessions<String>

Returns a list of sessions known to the repl

Returns:

  • (<String>)

    An array of session ids



99
100
101
102
# File 'lib/nrepl/repl.rb', line 99

def list_sessions
  response = send(op:"ls-sessions").first
  response["sessions"]
end

#running?Boolean

Returns true if we can get a socket connection to this repl

Returns:

  • (Boolean)

    true if running, false if not



70
71
72
# File 'lib/nrepl/repl.rb', line 70

def running?
  !!get_socket(0)
end

#send(command) ⇒ Object

Sends a raw command to the repl, returning an array of responses



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/nrepl/repl.rb', line 115

def send(command)
  command = command.reverse_merge(session: @session).delete_blank
  
  sock = get_socket
  
  sock.print command.bencode
  
  puts ">>> #{command.inspect}" if @debug
  
  responses = []
  done = false
  
  begin
    until done
      retriable timeout: 0.2, tries: 100 do
        raw = sock.recv(100000) #TODO: figure out better way to ensure we've drained the receive buffer besides large magic number
        decoded = raw.bdecode
      
        puts "<<< #{decoded}" if @debug
        responses << decoded
        yield decoded if block_given?
      
        status = responses.last["status"]
        done = status.include?("done") || status.include?("error")
      end
    end
  rescue BEncode::DecodeError
    # TODO
  end
  
  responses
end