Class: Cyc::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/cyc/client.rb

Overview

Author

Aleksander Pohl ([email protected])

License

MIT/X11 License

This class is the implementation of a Cyc client.

Example:

cyc = Cyc::Client.new
cyc.genls? :Dog, :Animal # checks if Dog generalizes to Animal
#=> true
cyc.genls? :Animal, :Dog # checks if Animal generalizes to Dog
#=> nil

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Client

Creates new Client. Usage:

Cyc::Client.new [options = {}]

options:

  • :host = localhost server address

  • :port = 3601 server port

  • :debug = false initial debug flag

  • :cache = false initial cache enabled flag or external cache instance

  • :timeout = 0.2 connection timeout in seconds

  • :url (String): cyc://host:port overrides :host, :port

  • :driver (Class) = Cyc::Connection::Socket client connection driver class

  • :thread_safe = true set to true if you want to share client between threads

Example:

Cyc::Client.new
Cyc::Client.new :host => 'cyc.example', :port => 3661, :debug => true
Cyc::Client.new :debug => true, :url => 'cyc://localhost/3661',
  :timeout => 1.5, :driver => Cyc::Connection::SynchronyDriver

Thread safe client:

Cyc::Client.new :thread_safe => true


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
# File 'lib/cyc/client.rb', line 78

def initialize(options={})
  @pid = Process.pid
  unless Hash === options
    raise ArgumentError.new("The Client.new(host,port) API is no longer supported.")
  end
  @host = options[:host] || "localhost"
  @port = (options[:port] || 3601).to_i
  if url = options[:url]
    url = URI.parse(url)
    @host = url.host || @host
    @port = url.port || @port
  end
  @timeout = (options[:timeout] || 0.2).to_f
  @driver = options[:driver] || Connection.driver
  @debug = !!options[:debug]
  cache = options[:cache]
  @cache_enabled = !!cache
  @cache = cache.respond_to?(:cached_value) ? cache : Cache.new
  @thread_safe = !!options[:thread_safe]

  if @thread_safe
    self.extend ThreadSafeClientExtension
  else
    @conn = nil
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

This hook allows for direct call on the Client class, that are translated into corresponding calls for Cyc server.

E.g. if users initializes the client and calls some Ruby method

cyc = Cyc::Client.new
cyc.genls? :Dog, :Animal

He/She returns a parsed answer from the server:

=> true

Since dashes are not allowed in Ruby method names they are replaced with underscores:

cyc.min_genls :Dog

is translated into:

(min-genls #$Dog)

As you see the Ruby symbols are translated into Cyc terms (not Cyc symbols!).

It is also possible to nest the calls to build more complex calls:

cyc.with_any_mt do |cyc|
  cyc.min_genls :Dog
end

is translated into:

(with-any-mt (min-genls #$Dog))


272
273
274
275
276
# File 'lib/cyc/client.rb', line 272

def method_missing(name,*args,&block)
  builder = Builder.new
  builder.send(name,*args,&block)
  talk(builder.to_cyc)
end

Instance Attribute Details

#cacheObject

The cache instance of a client. You may call clear on it to clear cache’s content, You may copy it’s reference to the other client instances.



35
36
37
# File 'lib/cyc/client.rb', line 35

def cache
  @cache
end

#cache_enabledObject

If set to true, results of the queries are cached. This is turned off by default, since there is a functional-languages assumption, that the result for the same query is always the same, but this might not be true in case of Cyc (however highly probable). The cache is used only in the talk call (and calls based on it – i.e. direct Cyc calls, e.g. cyc.genls :Dog).



30
31
32
# File 'lib/cyc/client.rb', line 30

def cache_enabled
  @cache_enabled
end

#conn=(value) ⇒ Object

The connection object - direct usage is discouraged. Use connection() call instead.



52
53
54
# File 'lib/cyc/client.rb', line 52

def conn=(value)
  @conn = value
end

#debugObject

If set to true, all communication with the server is logged to standard output



22
23
24
# File 'lib/cyc/client.rb', line 22

def debug
  @debug
end

#driverObject (readonly)

The driver the client uses to connect to the server.



44
45
46
# File 'lib/cyc/client.rb', line 44

def driver
  @driver
end

#hostObject (readonly)

The host the client connects to.



38
39
40
# File 'lib/cyc/client.rb', line 38

def host
  @host
end

#portObject (readonly)

The port the client connects to.



41
42
43
# File 'lib/cyc/client.rb', line 41

def port
  @port
end

#thread_safeObject (readonly) Also known as: thread_safe?

true if the client is thread safe.



47
48
49
# File 'lib/cyc/client.rb', line 47

def thread_safe
  @thread_safe
end

Instance Method Details

#check_parenthesis(message) ⇒ Object

Scans the message to find out if the parenthesis are matched. Raises UnbalancedClosingParenthesis exception if there is a not matched closing parenthesis. The message of the exception contains the string with the unmatched parenthesis highlighted. Raises UnbalancedOpeningParenthesis exception if there is a not matched opening parenthesis.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/cyc/client.rb', line 182

def check_parenthesis(message)
  count = 0
  message.scan(/[()]/) do |char|
    count += (char == "(" ?  1 : -1)
    if count < 0
      # this *is* thread safe
      position = $~.offset(0)[0]
      raise UnbalancedClosingParenthesis.
        new((position > 1 ? message[0...position] : "") +
          "<error>)</error>" + message[position+1..-1])
    end
  end
  raise UnbalancedOpeningParenthesis.new(count) if count > 0
end

#closeObject

Closes connection with the server.



149
150
151
152
153
154
# File 'lib/cyc/client.rb', line 149

def close
  conn.write("(api-quit)") if connected?
rescue Errno::ECONNRESET
ensure
  self.conn = nil
end

#connected?Boolean

Returns true if the client is connected with the server.

Returns:

  • (Boolean)


106
107
108
# File 'lib/cyc/client.rb', line 106

def connected?
  (conn=self.conn) && conn.connected? && @pid == Process.pid || false
end

#raw_talk(message, options = {}) ⇒ Object

Sends the message to the Cyc server and returns the raw answer (i.e. not parsed).



171
172
173
174
# File 'lib/cyc/client.rb', line 171

def raw_talk(message, options={})
  send_message(message)
  receive_raw_answer(options)
end

#receive_answer(options = {}) ⇒ Object

Receives and parses an answer for a message from the Cyc server.



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/cyc/client.rb', line 207

def receive_answer(options={})
  receive_raw_answer do |answer, last_message|
    begin
      result = Parser.new.parse answer, options[:stack]
    rescue ContinueParsing => ex
      current_result = result = ex.stack
      while current_result.size == 100 do
        send_message("(subseq #{last_message} #{result.size} " +
                     "#{result.size + 100})")
        current_result = receive_answer(options) || []
        result.concat(current_result)
      end
    end
    return result
  end
end

#receive_raw_answer(options = {}) ⇒ Object

Receives raw answer from server. If a block is given the answer is yield to the block, otherwise the answer is returned.



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/cyc/client.rb', line 226

def receive_raw_answer(options={})
  status, answer, last_message = connection{|c| c.read}
  puts "Recv: #{last_message} -> #{status} #{answer}" if @debug
  if status == 200
    if block_given?
      yield answer, last_message
    else
      return answer
    end
  else
    raise CycError.new(answer.sub(/^"/,"").sub(/"$/,"") + "\n" + last_message)
  end
end

#send_message(message) ⇒ Object

Sends a raw message to the Cyc server. The user is responsible for receiving the answer by calling receive_answer or receive_raw_answer.



200
201
202
203
204
# File 'lib/cyc/client.rb', line 200

def send_message(message)
  check_parenthesis(message)
  puts "Send: #{message}" if @debug
  connection{|c| c.write(message)}
end

#talk(message, options = {}) ⇒ Object

Sends the messsage to the Cyc server and returns a parsed answer.



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/cyc/client.rb', line 157

def talk(message, options={})
  if @cache_enabled
    @cache.cached_value(message) do
      send_message(message)
      receive_answer(options)
    end
  else
    send_message(message)
    receive_answer(options)
  end
end