alchemy-api-rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +19 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +80 -0
  5. data/Rakefile +11 -0
  6. data/alchemy-api-rb.gemspec +24 -0
  7. data/lib/alchemy-api.rb +47 -0
  8. data/lib/alchemy-api/author_extraction.rb +21 -0
  9. data/lib/alchemy-api/base.rb +78 -0
  10. data/lib/alchemy-api/concept_tagging.rb +16 -0
  11. data/lib/alchemy-api/config.rb +34 -0
  12. data/lib/alchemy-api/entity_extraction.rb +15 -0
  13. data/lib/alchemy-api/keyword_extraction.rb +15 -0
  14. data/lib/alchemy-api/language_detection.rb +17 -0
  15. data/lib/alchemy-api/relation_extraction.rb +16 -0
  16. data/lib/alchemy-api/sentiment_analysis.rb +16 -0
  17. data/lib/alchemy-api/text_categorization.rb +21 -0
  18. data/lib/alchemy-api/text_extraction.rb +19 -0
  19. data/lib/alchemy-api/version.rb +3 -0
  20. data/spec/alchemy_api_spec.rb +70 -0
  21. data/spec/author_extraction_spec.rb +43 -0
  22. data/spec/base_spec.rb +20 -0
  23. data/spec/concept_tagging_spec.rb +44 -0
  24. data/spec/entity_extraction_spec.rb +44 -0
  25. data/spec/keyword_extraction_spec.rb +50 -0
  26. data/spec/language_detection_spec.rb +43 -0
  27. data/spec/relation_extraction_spec.rb +44 -0
  28. data/spec/sentiment_analysis_spec.rb +43 -0
  29. data/spec/spec_helper.rb +13 -0
  30. data/spec/text_categorization_spec.rb +43 -0
  31. data/spec/text_extraction_spec.rb +32 -0
  32. data/spec/vcr_cassettes/author_basic_html_json_search.yml +46 -0
  33. data/spec/vcr_cassettes/author_basic_url_json_search.yml +47 -0
  34. data/spec/vcr_cassettes/category_basic_html_json_search.yml +43 -0
  35. data/spec/vcr_cassettes/category_basic_text_json_search.yml +46 -0
  36. data/spec/vcr_cassettes/category_basic_url_json_search.yml +46 -0
  37. data/spec/vcr_cassettes/concept_basic_html_json_search.yml +54 -0
  38. data/spec/vcr_cassettes/concept_basic_text_json_search.yml +55 -0
  39. data/spec/vcr_cassettes/concept_basic_url_json_search.yml +75 -0
  40. data/spec/vcr_cassettes/entity_basic_html_json_search.yml +61 -0
  41. data/spec/vcr_cassettes/entity_basic_text_json_search.yml +60 -0
  42. data/spec/vcr_cassettes/entity_basic_url_json_search.yml +71 -0
  43. data/spec/vcr_cassettes/keyword_basic_html_json_search.yml +48 -0
  44. data/spec/vcr_cassettes/keyword_basic_text_json_search.yml +48 -0
  45. data/spec/vcr_cassettes/keyword_basic_url_json_search.yml +67 -0
  46. data/spec/vcr_cassettes/language_basic_html_json_search.yml +49 -0
  47. data/spec/vcr_cassettes/language_basic_text_json_search.yml +49 -0
  48. data/spec/vcr_cassettes/language_basic_url_json_search.yml +49 -0
  49. data/spec/vcr_cassettes/relation_basic_html_json_search.yml +53 -0
  50. data/spec/vcr_cassettes/relation_basic_text_json_search.yml +53 -0
  51. data/spec/vcr_cassettes/relation_basic_url_json_search.yml +58 -0
  52. data/spec/vcr_cassettes/sentiment_basic_html_json_search.yml +48 -0
  53. data/spec/vcr_cassettes/sentiment_basic_text_json_search.yml +48 -0
  54. data/spec/vcr_cassettes/sentiment_basic_url_json_search.yml +48 -0
  55. data/spec/vcr_cassettes/text_basic_html_json_search.yml +47 -0
  56. data/spec/vcr_cassettes/text_basic_url_json_search.yml +47 -0
  57. metadata +196 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
19
+ .keys
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in alchemy-api-rb.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 John Allen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # AlchemyAPI
2
+
3
+ This is a Ruby Client Library that provides access to the Alchemy text mining API. The initial version targets the following API's:
4
+
5
+ * Keyword Extraction
6
+ * Text Extraction
7
+ * Entity Extraction
8
+ * Sentiment Analysis
9
+ * Concept Tagging
10
+ * Text Categorization
11
+ * Language Detection
12
+ * Author Extraction
13
+
14
+ Not yet implemented API's:
15
+
16
+ * Content Scraping
17
+ * Microformats
18
+ * RSS / ATOM
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'alchemy-api-rb'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install alchemy-api-rb
33
+
34
+ ## Usage
35
+
36
+ Set your API Key in an initializer or something similar:
37
+
38
+ AlchemyAPI.key = "xxxxxxxxxxxxxxxxxx"
39
+
40
+ You may set the key along with other config settings:
41
+
42
+ AlchemyAPI.configure do |config|
43
+ config.key = "xxxxxxxxxxxxxxxxxx"
44
+ config.output_mode = :xml # not yet supported
45
+ end
46
+
47
+ Request keyword extraction for a string of text:
48
+
49
+ results = AlchemyAPI.search(:keyword_extraction, :text => "hello world")
50
+
51
+ or
52
+
53
+ results = AlchemyAPI::KeywordExtraction.search(:url => "http://www.alchemyapi.com/")
54
+
55
+ or
56
+
57
+ results = AlchemyAPI::KeywordExtraction.search(:html => "<html><body>lorem ipsum</body></html>")
58
+
59
+ Results for keyword extraction are returned as a array of keywords:
60
+
61
+ [
62
+ {
63
+ "text" => "lorem ipsum",
64
+ "relevance" => "0.993164"
65
+ }
66
+ ]
67
+
68
+ ## TODO
69
+
70
+ 1. Add missing Alchmey API search modes
71
+ 2. Add the ability to search all supported API's in a single request?
72
+ 3. Add support for raw output (JSON, XML and RDF)?
73
+
74
+ ## Contributing
75
+
76
+ 1. Fork it
77
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
78
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
79
+ 4. Push to the branch (`git push origin my-new-feature`)
80
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ task :default => [:test]
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs.push "lib"
9
+ t.test_files = FileList['spec/**/*_spec.rb']
10
+ t.verbose = true
11
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/alchemy-api/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["John Allen"]
6
+ gem.email = ["john.allen@technekes.com"]
7
+ gem.description = %q{Provides access to the Alchemy text mining API - http://www.alchemyapi.com/}
8
+ gem.summary = %q{This is a ruby client library that provides access to the Alechemy text mining API. The initial version targets the keyword extraction api specificaly, with other implementations to follow.}
9
+ gem.homepage = "https://github.com/technekes/alchemy-api-rb"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "alchemy-api-rb"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = AlchemyAPI::VERSION
17
+
18
+ gem.add_dependency 'faraday', '~> 0.8.1'
19
+
20
+ gem.add_development_dependency 'rake', '~> 0.9.2'
21
+ gem.add_development_dependency 'turn', '~> 0.9.5'
22
+ gem.add_development_dependency 'vcr', '~> 2.2.0'
23
+ gem.add_development_dependency 'fakeweb', '~> 1.3.0'
24
+ end
@@ -0,0 +1,47 @@
1
+ require "alchemy-api/version"
2
+ require "alchemy-api/config"
3
+ require "alchemy-api/base"
4
+ require "alchemy-api/keyword_extraction"
5
+ require "alchemy-api/text_extraction"
6
+ require "alchemy-api/entity_extraction"
7
+ require "alchemy-api/sentiment_analysis"
8
+ require "alchemy-api/relation_extraction"
9
+ require "alchemy-api/concept_tagging"
10
+ require "alchemy-api/text_categorization"
11
+ require "alchemy-api/language_detection"
12
+ require "alchemy-api/author_extraction"
13
+
14
+ module AlchemyAPI
15
+ BASE_URL = "http://access.alchemyapi.com/calls/"
16
+
17
+ def self.config
18
+ Config
19
+ end
20
+
21
+ def self.configure
22
+ block_given? ? yield(Config) : Config
23
+ end
24
+
25
+ def self.key
26
+ Config.apikey
27
+ end
28
+
29
+ def self.key=(value)
30
+ Config.apikey = value
31
+ end
32
+
33
+ def self.search(mode, opts)
34
+ klass = Config.modes[mode]
35
+
36
+ raise InvalidAPIKey.new unless Config.apikey
37
+ raise InvalidSearchMode.new unless klass
38
+
39
+ klass.new.search(opts)
40
+ end
41
+
42
+ class UnknownError < StandardError; end
43
+ class MissingOptionsError < StandardError; end
44
+ class InvalidAPIKey < StandardError; end
45
+ class InvalidSearchMode < StandardError; end
46
+ class InvalidOutputMode < StandardError; end
47
+ end
@@ -0,0 +1,21 @@
1
+ module AlchemyAPI
2
+ class AuthorExtraction < Base
3
+ Config.add_mode :author_extraction, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetAuthor"
7
+ end
8
+
9
+ private
10
+
11
+ def supported_search_types
12
+ [:html, :url]
13
+ end
14
+
15
+ def indexer
16
+ nil
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,78 @@
1
+ require 'json/ext'
2
+ require 'faraday'
3
+
4
+ module AlchemyAPI
5
+ class Base
6
+ attr_accessor :options, :response
7
+
8
+ def search(opts)
9
+ check_options(opts)
10
+
11
+ @response = connection.post(path, construct_body)
12
+
13
+ parsed_response
14
+ end
15
+
16
+ def parsed_response
17
+ case Config.output_mode
18
+ when :json
19
+ parsed = JSON.parse(@response.body)
20
+ indexer ? parsed[indexer] : parsed
21
+ when :xml
22
+ when :rdf
23
+ raise NotImplementedError.new
24
+ end
25
+ end
26
+
27
+ def merged_options(opts)
28
+ opts.merge(Config.default_options)
29
+ end
30
+
31
+ private
32
+
33
+ def check_options(opts)
34
+ @options = opts
35
+
36
+ raise MissingOptionsError.new unless options && options.keys
37
+
38
+ unless supported_search_types.include?(mode)
39
+ raise UnsupportedSearchMode.new
40
+ end
41
+ end
42
+
43
+ def connection
44
+ @connection ||= Faraday.new(url: BASE_URL)
45
+ end
46
+
47
+ def supported_search_types
48
+ [:text, :url, :html]
49
+ end
50
+
51
+ def mode
52
+ [:text, :url, :html].each do |type|
53
+ return type if options.keys && options.keys.include?(type)
54
+ end
55
+
56
+ raise MissingOptionsError.new
57
+ end
58
+
59
+ def method_prefix
60
+ case mode
61
+ when :text then "Text"
62
+ when :url then "URL"
63
+ when :html then "HTML"
64
+ end
65
+ end
66
+
67
+ def path
68
+ "#{mode}/#{web_method}"
69
+ end
70
+
71
+ def construct_body
72
+ body = { apikey: Config.apikey }.merge!(merged_options(options))
73
+
74
+ body.map { |e| e.join('=') }.join('&')
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,16 @@
1
+ module AlchemyAPI
2
+ class ConceptTagging < Base
3
+ Config.add_mode :concept_tagging, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetRankedConcepts"
7
+ end
8
+
9
+ private
10
+
11
+ def indexer
12
+ "concepts"
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,34 @@
1
+ module AlchemyAPI
2
+ class Config
3
+ class << self
4
+ attr_accessor :apikey
5
+ attr_accessor :modes
6
+ end
7
+
8
+ def self.add_mode(key, klass)
9
+ @modes = {} unless @modes
10
+
11
+ @modes[key] = klass
12
+ end
13
+
14
+ def self.default_options
15
+ {
16
+ :outputMode => output_mode
17
+ }
18
+ end
19
+
20
+ def self.output_mode
21
+ @output_mode || :json
22
+ end
23
+
24
+ def self.output_mode=(value)
25
+ raise InvalidOutputMode.new unless valid_output_modes.include?(value.to_s)
26
+
27
+ @output_mode = value
28
+ end
29
+
30
+ def self.valid_output_modes
31
+ ["xml", "json", "rdf", "rel-tag", "rel-tag-raw"]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ module AlchemyAPI
2
+ class EntityExtraction < Base
3
+ Config.add_mode :entity_extraction, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetRankedNamedEntities"
7
+ end
8
+
9
+ private
10
+
11
+ def indexer
12
+ "entities"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module AlchemyAPI
2
+ class KeywordExtraction < Base
3
+ Config.add_mode :keyword_extraction, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetRankedKeywords"
7
+ end
8
+
9
+ private
10
+
11
+ def indexer
12
+ "keywords"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module AlchemyAPI
2
+ class LanguageDetection < Base
3
+ Config.add_mode :language_detection, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetLanguage"
7
+ end
8
+
9
+ private
10
+
11
+ def indexer
12
+ nil
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -0,0 +1,16 @@
1
+ module AlchemyAPI
2
+ class RelationExtraction < Base
3
+ Config.add_mode :relation_extraction, self
4
+
5
+ def web_method
6
+ "#{method_prefix}GetRelations"
7
+ end
8
+
9
+ private
10
+
11
+ def indexer
12
+ "relations"
13
+ end
14
+ end
15
+ end
16
+