kasabi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,3 @@
1
+ == Version 0.0.1
2
+
3
+ * Initial project structure and code
@@ -0,0 +1,55 @@
1
+ KASABI.RB
2
+ ---------
3
+
4
+ Kasabi.rb provides a lightweight Ruby client library for interacting with the
5
+ Kasabi API
6
+
7
+ [http://kasabi.com][0]
8
+
9
+ AUTHOR
10
+ ------
11
+
12
+ Leigh Dodds (leigh@kasabi.com)
13
+
14
+ INSTALLATION
15
+ ------------
16
+
17
+ Kasabi.rb is packaged as a Ruby Gem and can be installed as follows:
18
+
19
+ sudo gem install kasabi
20
+
21
+ The source for the project is maintained in github at:
22
+
23
+ http://github.com/kasabi/kasabi.rb
24
+
25
+ USAGE
26
+ -----
27
+
28
+ require 'rubygems'
29
+ require 'kasabi'
30
+
31
+ client = Kasabi::Sparql::Client.new("http://api.kasabi.com/api/example-api", ENV["KASABI_API_KEY"])
32
+ results = client.query(...)
33
+
34
+ See the examples directory for example scripts.
35
+
36
+ LICENSE
37
+ -------
38
+
39
+ Copyright 2011 Talis Systems Ltd
40
+
41
+ Licensed under the Apache License, Version 2.0 (the "License");
42
+ you may not use this file except in compliance with the License.
43
+
44
+ You may obtain a copy of the License at
45
+
46
+ http://www.apache.org/licenses/LICENSE-2.0
47
+
48
+ Unless required by applicable law or agreed to in writing,
49
+ software distributed under the License is distributed on an "AS IS" BASIS,
50
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
51
+
52
+ See the License for the specific language governing permissions and limitations
53
+ under the License.
54
+
55
+ [0]: [http://kasabi.com]
@@ -0,0 +1,76 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ require 'rake/clean'
6
+
7
+ NAME = "kasabi"
8
+ VER = "0.0.1"
9
+
10
+ RDOC_OPTS = ['--quiet', '--title', 'Kasabi Ruby Client Documentation']
11
+
12
+ PKG_FILES = %w( README.md Rakefile CHANGES ) +
13
+ Dir.glob("{bin,tests,etc,lib}/**/*")
14
+
15
+ CLEAN.include ['*.gem', 'pkg']
16
+ SPEC =
17
+ Gem::Specification.new do |s|
18
+ s.name = NAME
19
+ s.version = VER
20
+ s.platform = Gem::Platform::RUBY
21
+ s.required_ruby_version = ">= 1.8.5"
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = ["CHANGES"]
24
+ s.rdoc_options = RDOC_OPTS
25
+ s.summary = "Ruby Client for Kasabi"
26
+ s.description = s.summary
27
+ s.author = "Leigh Dodds"
28
+ s.email = 'leigh@kasabi.com'
29
+ s.homepage = 'http://github.com/kasabi/kasabi.rb'
30
+ s.rubyforge_project = 'kasabi'
31
+ s.files = PKG_FILES
32
+ s.require_path = "lib"
33
+ s.bindir = "bin"
34
+ #s.executables = ["..."]
35
+ s.test_file = "tests/ts_kasabi.rb"
36
+ s.add_dependency("httpclient", ">= 2.1.3.1")
37
+ s.add_dependency("json", ">= 1.1.3")
38
+ s.add_dependency("mocha", ">= 0.9.5")
39
+ s.add_dependency("mime-types", ">= 1.16")
40
+ #FIXME versions
41
+ s.add_dependency("linkeddata")
42
+ end
43
+
44
+ Rake::GemPackageTask.new(SPEC) do |pkg|
45
+ pkg.need_tar = true
46
+ end
47
+
48
+ Rake::RDocTask.new do |rdoc|
49
+ rdoc.rdoc_dir = 'doc/rdoc'
50
+ rdoc.options += RDOC_OPTS
51
+ rdoc.rdoc_files.include("CHANGES", "lib/**/*.rb")
52
+ #rdoc.main = "README"
53
+ end
54
+
55
+ #desc "Publish rdoc output to rubyforge"
56
+ #task "publish-docs" => ["rdoc"] do
57
+ # rubyforge_path = "/var/www/gforge-projects/#{NAME}/"
58
+ # sh "scp -r doc/* " +
59
+ # "#{ENV["RUBYFORGE_USER"]}@rubyforge.org:#{rubyforge_path}",
60
+ # :verbose => true
61
+ #end
62
+
63
+ Rake::TestTask.new do |test|
64
+ test.test_files = FileList['tests/*/tc_*.rb']
65
+ end
66
+
67
+ desc "Install from a locally built copy of the gem"
68
+ task :install do
69
+ sh %{rake package}
70
+ sh %{sudo gem install pkg/#{NAME}-#{VER}}
71
+ end
72
+
73
+ desc "Uninstall the gem"
74
+ task :uninstall => [:clean] do
75
+ sh %{sudo gem uninstall #{NAME}}
76
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'httpclient'
3
+ require 'json'
4
+ require 'linkeddata'
5
+
6
+ require 'kasabi/api/base_client'
7
+ require 'kasabi/api/lookup'
8
+ require 'kasabi/api/sparql'
9
+ require 'kasabi/api/search'
10
+ require 'kasabi/api/facet'
11
+ require 'kasabi/api/augment'
12
+ require 'kasabi/api/reconcile'
@@ -0,0 +1,40 @@
1
+ module Kasabi
2
+
3
+ module Augment
4
+
5
+ #Client for working with Kasabi Augmentation APIs
6
+ class Client < BaseClient
7
+
8
+ #Initialize the client to work with a specific endpoint
9
+ #
10
+ # The _options_ hash can contain the following values:
11
+ # * *:apikey*: required. apikey authorized to use the API
12
+ # * *:client*: HTTPClient object instance
13
+ def initialize(endpoint, options={})
14
+ super(endpoint, options)
15
+ end
16
+
17
+ # Augment an RSS feed that can be retrieved from the specified URL, against data in this store
18
+ #
19
+ # uri:: the URL for the RSS 1.0 feed
20
+ def augment_uri(uri)
21
+ response = @client.get(@endpoint, {:apikey=>@apikey, "data-uri" => uri})
22
+ validate_response(response)
23
+
24
+ return response.content
25
+ end
26
+
27
+ # Augment data using POSTing it to the API
28
+ #
29
+ # Currently this is limited to RSS 1.0 feeds
30
+ #
31
+ # data:: a String containing the data to augment
32
+ def augment(data, content_type="application/rss+xml")
33
+ response = @client.post("#{@endpoint}?apikey=#{@apikey}", data, {"Content-Type" => "application/rss+xml"})
34
+ validate_response(response)
35
+ return response.content
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ module Kasabi
2
+
3
+ #Base class for API clients
4
+ class BaseClient
5
+
6
+ attr_reader :endpoint
7
+ attr_reader :client
8
+ attr_reader :apikey
9
+
10
+ #Initialize the client to work with a specific endpoint
11
+ #
12
+ # The _options_ hash can contain the following values:
13
+ # * *:apikey*: required. apikey authorized to use the API
14
+ # * *:client*: HTTPClient object instance
15
+ def initialize(endpoint, options={})
16
+ @endpoint = endpoint
17
+ @client = options[:client] || HTTPClient.new()
18
+ @apikey = options[:apikey] || nil
19
+ end
20
+
21
+ def validate_response(response)
22
+ if response.status != 200
23
+ raise "Unable to perform request. Status: #{response.status}. Message: #{response.content}"
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,91 @@
1
+ module Kasabi
2
+
3
+ #This module organizes the classes related to the Facet service
4
+ module Search
5
+
6
+ module Facet
7
+
8
+ #A term returned in a facet search result
9
+ #
10
+ #A term has a number of hits, a reference to a search uri that can
11
+ #be used to find all of those hits, and a value
12
+ class Term
13
+ attr_reader :hits
14
+ attr_reader :search_uri
15
+ attr_reader :value
16
+
17
+ def initialize(hits, search_uri, value)
18
+ @hits = hits
19
+ @search_uri = search_uri
20
+ @value = value
21
+ end
22
+
23
+ end
24
+
25
+ #The results of a facetted search
26
+ class Results
27
+
28
+ #The query used to generate the facet results, as echoed in the response
29
+ attr_reader :query
30
+
31
+ #The fields used to generate the results
32
+ attr_reader :fields
33
+
34
+ #An array of Term objects
35
+ attr_reader :facets
36
+
37
+ def initialize(query, fields, facets=Hash.new)
38
+ @query = query
39
+ @fields = fields
40
+ @facets = facets
41
+ end
42
+
43
+ #Parses the XML format from a successful API response to generate
44
+ #a Results object instance
45
+ def Results.parse(data)
46
+ doc = REXML::Document.new(data)
47
+ root = doc.root
48
+ head = root.elements[1]
49
+
50
+ query = ""
51
+ fields = ""
52
+ queryEl = head.get_elements("query")[0]
53
+ if queryEl != nil
54
+ query = queryEl.text
55
+ end
56
+ fieldsEl = head.get_elements("fields")[0]
57
+ if fieldsEl != nil
58
+ fields = fieldsEl.text
59
+ end
60
+
61
+ results = Results.new(query, fields)
62
+
63
+ fields = root.get_elements("fields")[0]
64
+ if fields == nil
65
+ raise "No fields in document!"
66
+ end
67
+
68
+ fields.get_elements("field").each do |field|
69
+ field_name = field.attribute("name").value
70
+ results.facets[field_name] = Array.new
71
+
72
+ field.get_elements("term").each do |term|
73
+ term = Term.new(term.attribute("number").value.to_i,
74
+ term.attribute("search-uri").value,
75
+ term.text() )
76
+
77
+ results.facets[field_name] << term
78
+ end
79
+
80
+ end
81
+
82
+ return results
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,25 @@
1
+ module Kasabi
2
+
3
+ module Lookup
4
+
5
+ class Client < BaseClient
6
+
7
+ #Initialize the client to work with a specific endpoint
8
+ #
9
+ # The _options_ hash can contain the following values:
10
+ # * *:apikey*: required. apikey authorized to use the API
11
+ # * *:client*: HTTPClient object instance
12
+ def initialize(endpoint, options={})
13
+ super(endpoint, options)
14
+ end
15
+
16
+ def lookup(uri)
17
+ response = @client.get(@endpoint, {:about => uri, :apikey=>@apikey, :output=>"json"} )
18
+ validate_response(response)
19
+ return JSON.parse( response.content )
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,124 @@
1
+ module Kasabi
2
+
3
+ module Reconcile
4
+
5
+ class Client < BaseClient
6
+
7
+ #Initialize the client to work with a specific endpoint
8
+ #
9
+ # The _options_ hash can contain the following values:
10
+ # * *:apikey*: required. apikey authorized to use the API
11
+ # * *:client*: HTTPClient object instance
12
+ def initialize(endpoint, options={})
13
+ super(endpoint, options)
14
+ end
15
+
16
+ #Simple reconciliation request
17
+ def reconcile_label(label, &block)
18
+ return reconcile( Client.make_query(label), &block )
19
+ end
20
+
21
+ #Full reconciliation request, allows specifying of additional parameters
22
+ #
23
+ #Returns the array of results from the reconciliation request
24
+ #
25
+ #Accepts a block to support iteration through the results. Method will yield
26
+ #each result and its index.
27
+ def reconcile(query, &block)
28
+ response = @client.get( @endpoint, {"query" => query.to_json, :apikey=>@apikey } )
29
+ validate_response(response)
30
+ results = JSON.parse( response.content )
31
+ if results["result"] && block_given?
32
+ results["result"].each_with_index do |r, i|
33
+ yield r, i
34
+ end
35
+ end
36
+ return results["result"]
37
+ end
38
+
39
+ #Perform a number of reconciliation queries
40
+ #Submits a single request with a number of queries
41
+ #
42
+ #Accepts a block to support iteration through the results. Method will yield
43
+ #each result, its index, and the query
44
+ def reconcile_all(queries, &block)
45
+ #TODO add batching
46
+ #TODO make parallel?
47
+ json = {}
48
+ queries.each_with_index do |query, i|
49
+ json["q#{i}"] = query
50
+ end
51
+ response = @client.get( @endpoint, {"queries" => json.to_json, :apikey=>@apikey } )
52
+ validate_response(response)
53
+ results = JSON.parse( response.content )
54
+ if block_given?
55
+ queries.each_with_index do |query, i|
56
+ if results["q#{i}"]
57
+ yield results["q#{i}"]["result"], i, query
58
+ end
59
+ end
60
+ end
61
+ return results
62
+ end
63
+
64
+ #Make an array of reconciliation queries using a standard set of options for limiting,
65
+ #type matching and property filtering
66
+ #
67
+ # label:: text to reconcile on
68
+ # limit:: limit number of results, default is 3
69
+ # type_strict:: how to perform type matching, legal values are :any (default), :all, :should
70
+ # type:: string identifier of type, or array of string identifiers
71
+ # properties:: property filters, see make_property_filter
72
+ def Client.make_queries(labels, limit=3, type_strict=:any, type=nil, properties=nil)
73
+ queries = []
74
+ labels.each do |label|
75
+ queries << Client.make_query(label, limit, type_strict, type, properties)
76
+ end
77
+ return queries
78
+ end
79
+
80
+ #Make a reconciliation query
81
+ #
82
+ # label:: text to reconcile on
83
+ # limit:: limit number of results, default is 3
84
+ # type_strict:: how to perform type matching, legal values are :any (default), :all, :should
85
+ # type:: string identifier of type, or array of string identifiers
86
+ # properties:: property filters, see make_property_filter
87
+ def Client.make_query(label, limit=3, type_strict=:any, type=nil, properties=nil)
88
+ query = Hash.new
89
+ query[:query] = label
90
+ query[:limit] = limit
91
+ query[:type_strict] = type_strict
92
+
93
+ query[:type] = type if type != nil
94
+ query[:properties] = properties if properties != nil
95
+
96
+ return query
97
+ end
98
+
99
+ #Construct a property filter
100
+ #
101
+ #A property name or identifier must be specified. Both are legal but it is up to the
102
+ #service to decide which one it uses. Some services may have restrictions on whether
103
+ #they support names or identifiers.
104
+ #
105
+ # value:: a single value, or an array of string or number or object literal, e.g., "Japan"
106
+ # name:: string, property name, e.g., "country"
107
+ # id:: string, property ID, e.g., "/people/person/nationality" in the Freebase ID space
108
+ def Client.make_property_filter(value, name=nil, id=nil)
109
+ if name == nil and id == nil
110
+ raise "Must specify at least a property name or property identifier"
111
+ end
112
+
113
+ filter = Hash.new
114
+ filter[:v] = value
115
+ filter[:p] = name if name != nil
116
+ filter[:pid] = id if id != nil
117
+
118
+ return filter
119
+ end
120
+
121
+
122
+ end
123
+ end
124
+ end