site_search 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/Gemfile +10 -0
- data/MIT-LICENSE +20 -0
- data/README.md +107 -0
- data/Rakefile +29 -0
- data/lib/generators/site_search/USAGE +3 -0
- data/lib/generators/site_search/google_custom_search_generator.rb +12 -0
- data/lib/generators/site_search/templates/google_custom_search.yml +25 -0
- data/lib/generators/site_search/templates/load_site_search_config.rb +1 -0
- data/lib/site_search/exceptions.rb +30 -0
- data/lib/site_search/google_search.rb +105 -0
- data/lib/site_search/result_item.rb +12 -0
- data/lib/site_search.rb +3 -0
- metadata +78 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2011 Gareth Allen
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# SiteSearch
|
|
2
|
+
|
|
3
|
+
This gem provides a simple and extremely flexible way to include site-wide searching in Rack based web applications, such as Ruby on Rails.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the latest stable release:
|
|
8
|
+
|
|
9
|
+
[sudo] gem install site_search
|
|
10
|
+
|
|
11
|
+
In Rails, add it to your Gemfile:
|
|
12
|
+
|
|
13
|
+
``` ruby
|
|
14
|
+
gem 'site_search'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Getting Started
|
|
18
|
+
|
|
19
|
+
Start off by generating the required config and initializer files:
|
|
20
|
+
|
|
21
|
+
Rails 3
|
|
22
|
+
|
|
23
|
+
rails generate site_search:google_custom_search
|
|
24
|
+
|
|
25
|
+
Rails 2.3
|
|
26
|
+
|
|
27
|
+
ruby script/generate site_search:google_custom_search
|
|
28
|
+
|
|
29
|
+
this should give you files in:
|
|
30
|
+
|
|
31
|
+
config/initializers/load_site_search_config.rb
|
|
32
|
+
config/google_custom_search.yml
|
|
33
|
+
|
|
34
|
+
## Configuring SiteSearch
|
|
35
|
+
|
|
36
|
+
SiteSearch currently only supports Google Custom Search and allows for multiple search engines to be configured and utilized.
|
|
37
|
+
|
|
38
|
+
Edit the google_custom_search.yml file in config/
|
|
39
|
+
|
|
40
|
+
``` yaml
|
|
41
|
+
production:
|
|
42
|
+
api_key: "YOUR-API-KEY"
|
|
43
|
+
search_engines:
|
|
44
|
+
your_search_engine:
|
|
45
|
+
cx: "YOUR-CX-RECORD"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
An initializer file is required to load the config settings, if you're using Rails, one is created for you:
|
|
49
|
+
|
|
50
|
+
## Using SiteSearch
|
|
51
|
+
|
|
52
|
+
In your controller create an instance of the GoogleSearch class by passing the name of the search engine and the search arguments:
|
|
53
|
+
|
|
54
|
+
``` ruby
|
|
55
|
+
@search = SiteSearch::GoogleSearch.new(:your_search_engine, params[:query_string])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
You can then access the results in the view like so:
|
|
59
|
+
|
|
60
|
+
``` ruby
|
|
61
|
+
<% @search.results.each do |result| %>
|
|
62
|
+
<%= results.title %>
|
|
63
|
+
<%= result.text %>
|
|
64
|
+
<%= result.url %>
|
|
65
|
+
<% end %>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
You can also access the raw document that is received from the Google Custom Search API.
|
|
69
|
+
|
|
70
|
+
``` ruby
|
|
71
|
+
@search.raw
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The GoogleSearch class also accepts a optional parameters, for example the alt parameter for specifying the format
|
|
75
|
+
of the response, can either be json(default) or atom.
|
|
76
|
+
|
|
77
|
+
``` ruby
|
|
78
|
+
@search = SiteSearch::GoogleSearch.new(:your_search_engine, params[:query_string], {:alt=>'atom'})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Contributing to SiteSearch
|
|
82
|
+
|
|
83
|
+
Contributions are appreciated and pull requests are very welcome. Before submitting a pull request,
|
|
84
|
+
please make sure that your changes are well tested.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
Copyright (c) 20011 Gareth Allen
|
|
89
|
+
|
|
90
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
91
|
+
a copy of this software and associated documentation files (the
|
|
92
|
+
"Software"), to deal in the Software without restriction, including
|
|
93
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
94
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
95
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
96
|
+
the following conditions:
|
|
97
|
+
|
|
98
|
+
The above copyright notice and this permission notice shall be
|
|
99
|
+
included in all copies or substantial portions of the Software.
|
|
100
|
+
|
|
101
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
102
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
103
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
104
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
105
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
106
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
107
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
begin
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require 'rake'
|
|
10
|
+
require 'rake/rdoctask'
|
|
11
|
+
|
|
12
|
+
require 'rake/testtask'
|
|
13
|
+
|
|
14
|
+
Rake::TestTask.new(:test) do |t|
|
|
15
|
+
t.libs << 'lib'
|
|
16
|
+
t.libs << 'test'
|
|
17
|
+
t.pattern = 'test/**/*_test.rb'
|
|
18
|
+
t.verbose = false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
task :default => :test
|
|
22
|
+
|
|
23
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
24
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
25
|
+
rdoc.title = 'SiteSearch'
|
|
26
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
27
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
29
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module SiteSearch
|
|
2
|
+
module Generators
|
|
3
|
+
class GoogleCustomSearchGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
5
|
+
|
|
6
|
+
def generate_google_custom_search
|
|
7
|
+
copy_file "google_custom_search.yml", "config/google_custom_search.yml"
|
|
8
|
+
copy_file "load_site_search_config.rb", "config/initializers/load_site_search_config.rb"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Put your Google Custom Search API key and any search engines you have set up.
|
|
2
|
+
|
|
3
|
+
development: &defaults
|
|
4
|
+
api_key: "YOUR-API-KEY"
|
|
5
|
+
search_engines:
|
|
6
|
+
my_search_engine:
|
|
7
|
+
cx: "CX-RECORD-FOR-MY-SEARCH-ENGINE"
|
|
8
|
+
#cref: "{optional}-YOUR-SEARCH-ENGINE-URL"
|
|
9
|
+
#lr: "{optional}-LANGUAGE-RESTRICTION"
|
|
10
|
+
|
|
11
|
+
# The following options can be supplied or overridden in the options hash
|
|
12
|
+
#num: "{optional}-NUM-OF-RESULTS-1-10(*default)"
|
|
13
|
+
#safe: "{optional}-high/medium/off(*default)"
|
|
14
|
+
#start: "{optional}start-index-between-1(*default)-101"
|
|
15
|
+
#filter: "{optional}-0(duplicates-off)/1(duplicated-on-*default)"
|
|
16
|
+
#alt: "{optional}-json(*default)/atom"
|
|
17
|
+
|
|
18
|
+
production:
|
|
19
|
+
<<: *defaults
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
<<: *defaults
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
GOOGLE_SEARCH_CONFIG = YAML.load_file("#{Rails.root}/config/google_custom_search.yml")[Rails.env]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module SiteSearch
|
|
2
|
+
# A general SiteSearch exception
|
|
3
|
+
class Error < StandardError; end
|
|
4
|
+
|
|
5
|
+
# The search engine could not be located due to an invalid cx record
|
|
6
|
+
class InvalidSearchRequest < Error; end
|
|
7
|
+
|
|
8
|
+
# The search engine has not been configured in the config yaml file
|
|
9
|
+
class UndefinedSearchEngine < Error; end
|
|
10
|
+
|
|
11
|
+
# The search engine has restricted access, likely cause is exceeding the Google Custom Search request limit
|
|
12
|
+
class ForbiddenAccessToSearchEngine < Error; end
|
|
13
|
+
|
|
14
|
+
# You can rescue from this error in the controller to display the error message provided or the default message
|
|
15
|
+
class NoResults < Error
|
|
16
|
+
attr_reader :query
|
|
17
|
+
attr_writer :default_message
|
|
18
|
+
|
|
19
|
+
def initialize(message = nil, query = nil)
|
|
20
|
+
@message = message
|
|
21
|
+
@query = query
|
|
22
|
+
@default_message = "No results for '#{@query}'"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
@message || @default_message
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module SiteSearch
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'net/https'
|
|
4
|
+
|
|
5
|
+
class GoogleSearch
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'rexml/document'
|
|
8
|
+
attr_reader :results, :total, :start_index, :raw
|
|
9
|
+
|
|
10
|
+
alias_method :raw?, :raw
|
|
11
|
+
|
|
12
|
+
def initialize(search_engine, query, options={})
|
|
13
|
+
raise NoResults.new if query.blank?
|
|
14
|
+
@query = escape_html(query).gsub(" ", '+')
|
|
15
|
+
@results = []
|
|
16
|
+
doc = get_document(set_uri(options, search_engine.to_s))
|
|
17
|
+
@raw = doc.body
|
|
18
|
+
case options[:alt]
|
|
19
|
+
when 'atom' then parse_atom(doc)
|
|
20
|
+
else parse_json(doc)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def escape_html(str)
|
|
26
|
+
str.gsub(/<\/?[^>]*>/, "")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def set_uri(options, search_engine)
|
|
30
|
+
raise(UndefinedSearchEngine, "'#{search_engine}' is undefined for #{self.class}") if !GOOGLE_SEARCH_CONFIG['search_engines'].has_key? search_engine
|
|
31
|
+
|
|
32
|
+
key = GOOGLE_SEARCH_CONFIG['api_key']
|
|
33
|
+
cx = GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['cx']
|
|
34
|
+
|
|
35
|
+
params = "key=#{key}&cx=#{cx}&q=#{@query}"
|
|
36
|
+
|
|
37
|
+
options_hash = {}
|
|
38
|
+
|
|
39
|
+
options_hash.store('cref', GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['cref'])
|
|
40
|
+
options_hash.store('lr', GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['lr'])
|
|
41
|
+
options_hash.store('num', options[:num] || GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['num'])
|
|
42
|
+
options_hash.store('safe', options[:safe] || GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['safe'])
|
|
43
|
+
options_hash.store('start', options[:start] || GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['start'])
|
|
44
|
+
options_hash.store('filter', options[:filter] || GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['filter'])
|
|
45
|
+
options_hash.store('alt', options[:alt] || GOOGLE_SEARCH_CONFIG['search_engines'][search_engine]['alt'])
|
|
46
|
+
|
|
47
|
+
options_hash.each do |k, v|
|
|
48
|
+
params = params + "&#{k}=#{v}" if v.present?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return URI.parse("https://www.googleapis.com/customsearch/v1?#{params}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def get_document(uri)
|
|
55
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
56
|
+
http.use_ssl = true
|
|
57
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
58
|
+
return http.request(Net::HTTP::Get.new(uri.request_uri))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse_json(doc)
|
|
62
|
+
json = JSON.parse(doc.body)
|
|
63
|
+
case doc
|
|
64
|
+
when Net::HTTPSuccess
|
|
65
|
+
then
|
|
66
|
+
json['queries']['request'].each do |r|
|
|
67
|
+
@total =r['totalResults']
|
|
68
|
+
@start_index = r['startIndex']
|
|
69
|
+
end
|
|
70
|
+
raise NoResults.new(nil, @query) if @total.to_i < 1
|
|
71
|
+
items = json['items']
|
|
72
|
+
items.each do |item|
|
|
73
|
+
@results << ResultItem.new(item)
|
|
74
|
+
end
|
|
75
|
+
when Net::HTTPForbidden then raise ForbiddenAccessToSearchEngine, json['error']['message']
|
|
76
|
+
else raise InvalidSearchRequest, json['error']['message']
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def parse_atom(doc)
|
|
81
|
+
atom = REXML::Document.new(doc.body)
|
|
82
|
+
case doc
|
|
83
|
+
when Net::HTTPSuccess
|
|
84
|
+
then
|
|
85
|
+
@total = atom.elements["//opensearch:totalResults"].text
|
|
86
|
+
raise NoResults.new(nil, @query) if @total.to_i < 1
|
|
87
|
+
@start_index = atom.elements["//opensearch:startIndex"].text
|
|
88
|
+
REXML::XPath.each(atom, "//entry").each do |el|
|
|
89
|
+
item = {}
|
|
90
|
+
item.store('link', el.elements["link"].attributes["href"])
|
|
91
|
+
item.store('displayLink', el.elements["link"].attributes["title"])
|
|
92
|
+
item.store('htmlSnippet', el.elements["summary"].text)
|
|
93
|
+
if item['htmlSnippet'].is_a? String
|
|
94
|
+
item.store('snippet', escape_html(item['htmlSnippet']))
|
|
95
|
+
end
|
|
96
|
+
@results << ResultItem.new(item)
|
|
97
|
+
end
|
|
98
|
+
when Net::HTTPForbidden then raise ForbiddenAccessToSearchEngine, atom.elements["//internalReason"].text
|
|
99
|
+
else raise InvalidSearchRequest, atom.elements["//internalReason"].text
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class ResultItem
|
|
2
|
+
attr_reader :url, :text, :title, :html
|
|
3
|
+
|
|
4
|
+
# Creates attributes that can easily be accessed from the view
|
|
5
|
+
def initialize(item)
|
|
6
|
+
@url = item['link']
|
|
7
|
+
@text = item['snippet']
|
|
8
|
+
@title = item['displayLink']
|
|
9
|
+
@html = item['htmlSnippet']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
data/lib/site_search.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: site_search
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 29
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.0.1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Gareth Allen
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2011-08-16 00:00:00 +01:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies: []
|
|
21
|
+
|
|
22
|
+
description: SiteSearch allows for multiple Google Custom Search engines to be configured, and provides JSON and Atom formats
|
|
23
|
+
email: gaz.allen12@gmail.co.uk
|
|
24
|
+
executables: []
|
|
25
|
+
|
|
26
|
+
extensions: []
|
|
27
|
+
|
|
28
|
+
extra_rdoc_files: []
|
|
29
|
+
|
|
30
|
+
files:
|
|
31
|
+
- lib/generators/site_search/google_custom_search_generator.rb
|
|
32
|
+
- lib/generators/site_search/templates/google_custom_search.yml
|
|
33
|
+
- lib/generators/site_search/templates/load_site_search_config.rb
|
|
34
|
+
- lib/generators/site_search/USAGE
|
|
35
|
+
- lib/site_search/exceptions.rb
|
|
36
|
+
- lib/site_search/google_search.rb
|
|
37
|
+
- lib/site_search/result_item.rb
|
|
38
|
+
- lib/site_search.rb
|
|
39
|
+
- MIT-LICENSE
|
|
40
|
+
- Rakefile
|
|
41
|
+
- Gemfile
|
|
42
|
+
- README.md
|
|
43
|
+
has_rdoc: true
|
|
44
|
+
homepage: http://github.com/garetha/site_search
|
|
45
|
+
licenses: []
|
|
46
|
+
|
|
47
|
+
post_install_message:
|
|
48
|
+
rdoc_options: []
|
|
49
|
+
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
none: false
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
hash: 3
|
|
58
|
+
segments:
|
|
59
|
+
- 0
|
|
60
|
+
version: "0"
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
none: false
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
hash: 3
|
|
67
|
+
segments:
|
|
68
|
+
- 0
|
|
69
|
+
version: "0"
|
|
70
|
+
requirements: []
|
|
71
|
+
|
|
72
|
+
rubyforge_project:
|
|
73
|
+
rubygems_version: 1.3.7
|
|
74
|
+
signing_key:
|
|
75
|
+
specification_version: 3
|
|
76
|
+
summary: Provides site searching capabilities to rack applications using the Google Custom Search API
|
|
77
|
+
test_files: []
|
|
78
|
+
|