class Urbit::Ship

Attributes

channels[R]
config[R]
logged_in[RW]

Public Class Methods

finalize(channels) click to toggle source
# File lib/urbit/ship.rb, line 20
def self.finalize(channels)
  proc { channels.each { |c| c.close } }
end
new(config: Config.new) click to toggle source
# File lib/urbit/ship.rb, line 12
def initialize(config: Config.new)
  @auth_cookie = nil
  @channels    = []
  @config      = config
  @graphs      = []
  @logged_in   = false
end

Public Instance Methods

graph(resource:) click to toggle source
# File lib/urbit/ship.rb, line 32
def graph(resource:)
  self.graphs.find {|g| g.resource == resource}
end
graph_names() click to toggle source

A helper method to just print out the descriptive names of all the ship's graphs.

# File lib/urbit/ship.rb, line 59
def graph_names
  self.graphs.collect {|g| g.resource}
end
graphs(flush_cache: false) click to toggle source

Answers a collection of all the top-level graphs on this ship. This collection is cached and will need to be invalidated to discover new graphs.

# File lib/urbit/ship.rb, line 40
def graphs(flush_cache: false)
  @graphs = [] if flush_cache
  if @graphs.empty?
    if self.logged_in?
      r = self.scry(app: 'graph-store', path: '/keys')
      if r[:body]
        body = JSON.parse r[:body]
        body["graph-update"]["keys"].each do |k|
          @graphs << Graph.new(ship: self, graph_name: k["name"], host_ship_name: k["ship"])
        end
      end
    end
  end
  @graphs
end
logged_in?() click to toggle source
# File lib/urbit/ship.rb, line 24
def logged_in?
  logged_in
end
login() click to toggle source
# File lib/urbit/ship.rb, line 63
def login
  return self if logged_in?

  ensure_connections_closed
  response = Faraday.post(login_url, "password=#{config.code}")
  parse_cookie(response)
  self
end
name() click to toggle source
# File lib/urbit/ship.rb, line 72
def name
  config.name
end
open_channels() click to toggle source
# File lib/urbit/ship.rb, line 101
def open_channels
  @channels.select {|c| c.open?}
end
pat_p() click to toggle source
# File lib/urbit/ship.rb, line 97
def pat_p
  config.name
end
poke(app:, mark:, message:) click to toggle source

Poke an app with a message using a mark.

Returns a Channel which has been created and opened and will begin

to get back a stream of facts via its Receiver.
# File lib/urbit/ship.rb, line 111
def poke(app:, mark:, message:)
  (self.add_channel).poke(app: app, mark: mark, message: message)
end
remove_graph(graph:) click to toggle source
# File lib/urbit/ship.rb, line 76
def remove_graph(graph:)
  delete_json = %Q({
    "delete": {
      "resource": {
        "ship": "#{self.name}",
        "name": "#{graph.name}"
      }
    }
  })

  spider = self.spider(mark_in: 'graph-view-action', mark_out: 'json', thread: 'graph-delete', data: delete_json, args: ["NO_RESPONSE"])
  if (retcode = (200 == spider[:status]))
    self.graphs.delete graph
  end
  retcode
end
scry(app:, path:, mark: 'json') click to toggle source
# File lib/urbit/ship.rb, line 115
def scry(app:, path:, mark: 'json')
  self.login
  mark = ".#{mark}" unless mark.empty?
  scry_url = "#{self.config.api_base_url}/~/scry/#{app}#{path}#{mark}"

  response = Faraday.get(scry_url) do |req|
    req.headers['Accept'] = 'application/json'
    req.headers['Cookie'] = self.cookie
  end

  {status: response.status, code: response.reason_phrase, body: response.body}
end
spider(mark_in:, mark_out:, thread:, data:, args: []) click to toggle source
# File lib/urbit/ship.rb, line 128
def spider(mark_in:, mark_out:, thread:, data:, args: [])
  self.login
  url = "#{self.config.api_base_url}/spider/#{mark_in}/#{thread}/#{mark_out}.json"

  # TODO: This is a huge hack due to the fact that certain spider operations are known to
  #       not return when they should. Instead I just set the timeout low and catch the
  #       error and act like everything is ok.
  if args.include?("NO_RESPONSE")
    conn = Faraday::Connection.new()
    conn.options.timeout = 1
    conn.options.open_timeout = 1

    begin
      response = conn.post(url) do |req|
        req.headers['Accept'] = 'application/json'
        req.headers['Cookie'] = self.cookie
        req.body = data
      end
    rescue Faraday::TimeoutError
      return {status: 200, code: "ok", body: "null"}
    end
  end

  response = Faraday.post(url) do |req|
    req.headers['Accept'] = 'application/json'
    req.headers['Cookie'] = self.cookie
    req.body = data
  end

  {status: response.status, code: response.reason_phrase, body: response.body}
end
subscribe(app:, path:) click to toggle source

Subscribe to an app at a path.

Returns a Channel which has been created and opened and will begin

to get back a stream of facts via its Receiver.
# File lib/urbit/ship.rb, line 166
def subscribe(app:, path:)
  (self.add_channel).subscribe(app: app, path: path)
end
to_h() click to toggle source
# File lib/urbit/ship.rb, line 170
def to_h
  {name: "#{self.pat_p}", host: "#{self.config.host}", port: "#{self.config.port}"}
end
to_s() click to toggle source
# File lib/urbit/ship.rb, line 174
def to_s
  "a Ship(#{self.to_h})"
end
untilded_name() click to toggle source
# File lib/urbit/ship.rb, line 93
def untilded_name
  name.gsub('~', '')
end

Private Instance Methods

add_channel() click to toggle source
# File lib/urbit/ship.rb, line 180
def add_channel
  self.login
  self.channels << (c = Channel.new(ship: self, name: self.make_channel_name))
  c
end
ensure_connections_closed() click to toggle source
# File lib/urbit/ship.rb, line 190
def ensure_connections_closed
  # Make sure all our created channels are closed by the GC
  ObjectSpace.define_finalizer( self, self.class.finalize(channels) )
end
login_url() click to toggle source
# File lib/urbit/ship.rb, line 195
def login_url
  "#{config.api_base_url}/~/login"
end
make_channel_name() click to toggle source
# File lib/urbit/ship.rb, line 186
def make_channel_name
  "Channel-#{self.open_channels.count}"
end