kasabi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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