Created
January 29, 2014 18:04
-
-
Save tkareine/8693458 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# A toy program demonstrating consuming REST API and quering and | |
# inserting documents to MongoDB database with Ruby and its standard | |
# library without extra dependencies. | |
# | |
# Usage: | |
# | |
# $ mongod # leave it running | |
# | |
# $ which mongo # the program uses this tool to access MongoDB | |
# /usr/local/bin/mongo | |
# | |
# $ ./define.rb groggy | |
# New term, searching definition from DuckDuckGo... | |
# groggy: weak and unsteady on the feet or in action. | |
# | |
# $ ./define.rb groggy | |
# groggy: weak and unsteady on the feet or in action. | |
require 'cgi' | |
require 'net/http' | |
require 'json' | |
module CLI | |
class << self | |
def exit_with_usage | |
warn "Usage: #{File.basename($0)} term\n\nRequires mongod to be running locally, accessible without authentication." | |
abort | |
end | |
def parse(argv) | |
exit_with_usage if argv.empty? | |
argv.first | |
end | |
def print_definition(definition) | |
puts "#{definition['term']}: #{definition['description']}\n\n" | |
end | |
def print_searching_web | |
warn "New term, searching definition from DuckDuckGo..." | |
end | |
def print_not_found(term) | |
warn "Term not found: #{term}" | |
end | |
end | |
end | |
module HTTP | |
def self.get_json(uri) | |
response = Net::HTTP.get_response(uri) | |
if response.is_a?(Net::HTTPSuccess) | |
JSON.parse(response.body) | |
else | |
fail "Get failed for #{uri}:\n#{response.code} #{response.message}\n#{response.body}" | |
end | |
rescue JSON::ParserError => e | |
fail "Invalid response body, not JSON: #{e}" | |
end | |
end | |
module Mongo | |
class << self | |
def query(db, cmd) | |
result = nil | |
connect(db) do |input, output| | |
input.puts(cmd) | |
input.close | |
result = output.read | |
end | |
result | |
end | |
def insert(db, *cmds) | |
connect(db) do |input| | |
cmds.each { |cmd| input.puts(cmd) } | |
end | |
end | |
private | |
def connect(db, &block) | |
cmd = "mongo --quiet #{db}" | |
status = piped_spawn(cmd, &block) | |
fail "Mongo command failed with exit status #{status.exitstatus}: #{cmd}" if status.exitstatus != 0 | |
end | |
def piped_spawn(cmd) | |
out_rd, out_wr = IO.pipe | |
in_rd, in_wr = IO.pipe | |
pid = spawn(cmd, in: in_rd, out: out_wr) | |
in_rd.close | |
out_wr.close | |
yield in_wr, out_rd | |
ensure_closed(in_wr, out_rd) | |
_, status = Process.waitpid2(pid) | |
status | |
ensure | |
ensure_closed(in_rd, in_wr, out_rd, out_wr) | |
end | |
def ensure_closed(*ios) | |
ios.each { |io| io.close unless io.closed? } | |
end | |
end | |
end | |
module DuckDuckGo | |
NOT_FOUND = {} | |
class << self | |
def define(term) | |
query = CGI.escape("define #{term}") | |
uri = URI("http://api.duckduckgo.com/?q=#{query}&format=json") | |
result = HTTP.get_json(uri) | |
parse_definition(result) | |
end | |
private | |
def parse_definition(response) | |
definition = response["Definition"] | |
if definition && !definition.empty? | |
term, description = definition.split(/\s+definition:\s+/, 2) | |
{'term' => term, 'description' => description} | |
else | |
NOT_FOUND | |
end | |
end | |
end | |
end | |
module Definitions | |
DB_NAME = 'definitions' | |
class << self | |
def find(term) | |
mongo_query = <<-END | |
var regex = new RegExp(RegExp.escape(#{term.to_json})) | |
var result = db.definitions.find({term: regex}, {_id: 0, term: 1, description: 1}).toArray() | |
printjson(result) | |
END | |
result = Mongo.query(DB_NAME, mongo_query) | |
JSON.parse(result) | |
end | |
def add(definition) | |
mongo_insert = %|db.definitions.insert(#{definition.to_json})| | |
Mongo.insert(DB_NAME, mongo_insert) | |
end | |
end | |
end | |
term = CLI.parse(ARGV) | |
found = Definitions.find(term) | |
unless found.empty? | |
found.each { |definition| CLI.print_definition(definition) } | |
exit | |
end | |
CLI.print_searching_web | |
new_definition = DuckDuckGo.define(term) | |
if new_definition.empty? | |
CLI.print_not_found(term) | |
abort | |
end | |
Definitions.add(new_definition) | |
CLI.print_definition(new_definition) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment