radiant-sphinx_search-extension 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitmodules ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Sphinx Search
2
+
3
+ Adds ThinkingSphinx (<http://ts.freelancing-gods.com>) support to Radiant,
4
+ including a paginated search results page.
5
+
6
+ ## Installation
7
+
8
+ First you'll need to install Sphinx, available via your preferred package
9
+ manager or from <http://sphinxsearch.com>.
10
+
11
+ Installing the extension itself as a gem is preferred. Add to your
12
+ `config/environment.rb:`
13
+
14
+ config.gem 'radiant-sphinx_search-extension'
15
+
16
+ After the gem is configured, run `rake db:migrate:extensions` and `rake
17
+ radiant:extensions:update_all`. You should at least read the Thinking Sphinx
18
+ quickstart guide, but these commands should be enough to get you started:
19
+
20
+ rake ts:in
21
+ rake ts:start
22
+
23
+ ## Indexing
24
+
25
+ SphinxSearch indexes your pages on title and content (parts.) Sphinx
26
+ attributes are created for `status_id`, `updated_at`, and `virtual`.
27
+
28
+ ## Building a Search Page
29
+
30
+ SphinxSearch adds a new Page subclass, SearchPage. This page serves as both
31
+ the search form and the search results display.
32
+
33
+ There are a number of Radius tags available to the Search page, most of which
34
+ are customizable to some extent. You can find the full documentation for these
35
+ tags and their options in the tag reference, but a simple search page might
36
+ look like this:
37
+
38
+ <r:search>
39
+ <r:form />
40
+ <p>Your search for <strong><r:query /></strong> returned <r:count />.</p>
41
+ <r:results paginated="true">
42
+ <r:each>
43
+ <h3><r:link /></h3>
44
+ <p><r:excerpt /></p>
45
+ </r:each>
46
+ <r:pagination />
47
+ </r:results>
48
+ </r:search>
49
+
50
+ ## NoMethodError
51
+
52
+ If you're using some versions of Radiant (generally <= 0.9.1) or certain
53
+ 3rd-party extensions, you may see this error:
54
+
55
+ NoMethodError
56
+
57
+ You have a nil object when you didn't expect it!
58
+ You might have expected an instance of Array.
59
+ The error occurred while evaluating nil.<<
60
+
61
+ This is a load-order problem and it means another extension created a Page
62
+ subclass before SphinxSearch had a chance to extend the base class. The
63
+ easiest way around this is to simply load SphinxSearch first:
64
+
65
+ config.extensions = [:sphinx_search, :all]
66
+
67
+ ------------------------------------------------------------------------------
68
+
69
+ Copyright (c) 2008 Digital Pulp, Inc.
70
+
71
+ Permission is hereby granted, free of charge, to any person obtaining a copy
72
+ of this software and associated documentation files (the "Software"), to deal
73
+ in the Software without restriction, including without limitation the rights
74
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
75
+ copies of the Software, and to permit persons to whom the Software is
76
+ furnished to do so, subject to the following conditions:
77
+
78
+ The above copyright notice and this permission notice shall be included in
79
+ all copies or substantial portions of the Software.
80
+
81
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
84
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
86
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
87
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,134 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "radiant-sphinx_search-extension"
5
+ gem.summary = %Q{Sphinx Search Extension for Radiant CMS}
6
+ gem.description = %Q{Adds fulltext search capability to Radiant content via Thinking Sphinx.}
7
+ gem.email = "git@digitalpulp.com"
8
+ gem.homepage = "http://ext.radiantcms.org/extensions/15-sphinx-search"
9
+ gem.authors = ["Josh French", "Kunal Shah", "Justin Blecher"]
10
+ gem.add_dependency 'radiant'
11
+ gem.add_dependency 'thinking-sphinx', '>= 1.3.3'
12
+ end
13
+ rescue LoadError
14
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package page-factory as a gem."
15
+ end
16
+
17
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
18
+ # Check to see if the rspec plugin is installed first and require
19
+ # it if it is. If not, use the gem version.
20
+
21
+ # Determine where the RSpec plugin is by loading the boot
22
+ unless defined? RADIANT_ROOT
23
+ ENV["RAILS_ENV"] = "test"
24
+ case
25
+ when ENV["RADIANT_ENV_FILE"]
26
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
27
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
28
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
29
+ else
30
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
31
+ end
32
+ end
33
+
34
+ require 'rake'
35
+ require 'rake/rdoctask'
36
+ require 'rake/testtask'
37
+
38
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
39
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
40
+ require 'spec/rake/spectask'
41
+ # require 'spec/translator'
42
+
43
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
44
+ Object.send(:remove_const, :RADIANT_ROOT)
45
+
46
+ extension_root = File.expand_path(File.dirname(__FILE__))
47
+
48
+ task :default => :spec
49
+ task :stats => "spec:statsetup"
50
+
51
+ desc "Run all specs in spec directory"
52
+ Spec::Rake::SpecTask.new(:spec) do |t|
53
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
54
+ t.spec_files = FileList['spec/**/*_spec.rb']
55
+ end
56
+
57
+ namespace :spec do
58
+ desc "Run all specs in spec directory with RCov"
59
+ Spec::Rake::SpecTask.new(:rcov) do |t|
60
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
61
+ t.spec_files = FileList['spec/**/*_spec.rb']
62
+ t.rcov = true
63
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
64
+ end
65
+
66
+ desc "Print Specdoc for all specs"
67
+ Spec::Rake::SpecTask.new(:doc) do |t|
68
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
69
+ t.spec_files = FileList['spec/**/*_spec.rb']
70
+ end
71
+
72
+ [:models, :controllers, :views, :helpers].each do |sub|
73
+ desc "Run the specs under spec/#{sub}"
74
+ Spec::Rake::SpecTask.new(sub) do |t|
75
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
76
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
77
+ end
78
+ end
79
+
80
+ # Hopefully no one has written their extensions in pre-0.9 style
81
+ # desc "Translate specs from pre-0.9 to 0.9 style"
82
+ # task :translate do
83
+ # translator = ::Spec::Translator.new
84
+ # dir = RAILS_ROOT + '/spec'
85
+ # translator.translate(dir, dir)
86
+ # end
87
+
88
+ # Setup specs for stats
89
+ task :statsetup do
90
+ require 'code_statistics'
91
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
92
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
93
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
94
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
95
+ ::CodeStatistics::TEST_TYPES << "Model specs"
96
+ ::CodeStatistics::TEST_TYPES << "View specs"
97
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
98
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
99
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
100
+ end
101
+
102
+ namespace :db do
103
+ namespace :fixtures do
104
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
105
+ task :load => :environment do
106
+ require 'active_record/fixtures'
107
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
108
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
109
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ desc 'Generate documentation for the sphinx_search extension.'
117
+ Rake::RDocTask.new(:rdoc) do |rdoc|
118
+ rdoc.rdoc_dir = 'rdoc'
119
+ rdoc.title = 'SphinxSearchExtension'
120
+ rdoc.options << '--line-numbers' << '--inline-source'
121
+ rdoc.rdoc_files.include('README')
122
+ rdoc.rdoc_files.include('lib/**/*.rb')
123
+ end
124
+
125
+ # For extensions that are in transition
126
+ desc 'Test the sphinx_search extension.'
127
+ Rake::TestTask.new(:test) do |t|
128
+ t.libs << 'lib'
129
+ t.pattern = 'test/**/*_test.rb'
130
+ t.verbose = true
131
+ end
132
+
133
+ # Load any custom rakefiles for extension
134
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.1
@@ -0,0 +1,8 @@
1
+ class SearchPage < Page
2
+ include SphinxSearch::RadiusTags
3
+
4
+ def cache?
5
+ false
6
+ end
7
+
8
+ end
@@ -0,0 +1,15 @@
1
+ - content_for :page_css do
2
+ :plain
3
+ #page-search-status .BoxFix { padding: 4px 0; }
4
+
5
+ %tr#page-search-status
6
+ %th.label Search
7
+ %td.field
8
+ .BoxFix
9
+ %ul.Inputs.HorizList
10
+ %li
11
+ = radio_button :page, :searchable, true, :class => 'RadioInput'
12
+ %label{:for => 'page_searchable_true' } Searchable
13
+ %li
14
+ = radio_button :page, :searchable, false, :class => 'RadioInput'
15
+ %label{:for => 'page_searchable_false' } Invisible to Search
@@ -0,0 +1,9 @@
1
+ class AddDeltaToPages < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :pages, :delta, :boolean, :default => false
4
+ end
5
+
6
+ def self.down
7
+ remove_column :pages, :delta
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ # Nothing to see here, move along.
@@ -0,0 +1,6 @@
1
+ module SphinxSearch
2
+ mattr_accessor :param_name, :content_length, :hidden_classes
3
+ self.param_name = 'q'
4
+ self.content_length = 8.kilobytes
5
+ self.hidden_classes = %w(SearchPage JavascriptPage StylesheetPage)
6
+ end
@@ -0,0 +1,16 @@
1
+ module SphinxSearch
2
+ class LinkRenderer < Radiant::Pagination::LinkRenderer
3
+ def initialize(url_stem, query)
4
+ @url_stem, @query = url_stem, query
5
+ end
6
+
7
+ def page_link(page, text, attributes = {})
8
+ linkclass = %{ class="#{attributes[:class]}"} if attributes[:class]
9
+ linkrel = %{ rel="#{attributes[:rel]}"} if attributes[:rel]
10
+ page_param_name = WillPaginate::ViewHelpers.pagination_options[:param_name]
11
+ search_param_name = SphinxSearch.param_name || 'q'
12
+ %Q{<a href="#{@url_stem}?#{search_param_name}=#{@query}&#{page_param_name}=#{page}"#{linkrel}#{linkclass}>#{text}</a>}
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module SphinxSearch
2
+ module PageContextExtensions
3
+ def self.included(base)
4
+ base.alias_method_chain :set_process_variables, :sphinx
5
+ end
6
+
7
+ def set_process_variables_with_sphinx(result)
8
+ quacks_like_page = lambda do |object|
9
+ [:request, :response, :request=, :response=].all? { |method| object.respond_to?(method) }
10
+ end
11
+ set_process_variables_without_sphinx(result) if quacks_like_page.call(result)
12
+ end
13
+ private :set_process_variables_with_sphinx
14
+
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require 'thinking_sphinx'
2
+
3
+ module SphinxSearch
4
+ module PageExtensions
5
+
6
+ def self.included(base)
7
+ base.define_index do
8
+ set_property :delta => true, :group_concat_max_len => SphinxSearch.content_length || 8.kilobytes
9
+ set_property :field_weights => { 'title' => 100 }
10
+ indexes title, parts.content
11
+ has updated_at, status_id, virtual
12
+ end
13
+
14
+ base.extend ClassMethods
15
+ end
16
+
17
+ module ClassMethods
18
+ def searchable(search=true)
19
+ search ? SphinxSearch.hidden_classes.delete(self.name) : SphinxSearch.hidden_classes.push(self.name)
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,169 @@
1
+ module SphinxSearch
2
+ module RadiusTags
3
+ include Radiant::Taggable
4
+ include ActionView::Helpers::TextHelper
5
+
6
+ desc %{
7
+ Namespace for all search tags.
8
+ }
9
+ tag 'search' do |tag|
10
+ param = (SphinxSearch.param_name || 'q').to_sym
11
+ tag.locals.query = tag.globals.page.request[param]
12
+ tag.expand
13
+ end
14
+
15
+ desc %{
16
+ Renders a basic search form. Takes optional @id@ and @class@ values if
17
+ you need to target the form with specific CSS or JS; also takes an optional
18
+ @value@ tag as the label on the input. If for some reason you don't want
19
+ to use the default search term paramater @q@, you can override this by
20
+ defining @SphinxSearch.param_name@ in @/config/initializers/sphinx_search.rb@.
21
+
22
+ *Usage:*
23
+
24
+ <pre><code><r:search:form [id="form-id"] [class="form-class"] [value="Go"] /></code></pre>
25
+ }
26
+ tag 'search:form' do |tag|
27
+ form_id = tag.attr['id'] || 'search-form'
28
+ form_class = tag.attr['class'] || 'search-form'
29
+ form_value = tag.attr['value'] || 'Search'
30
+ form_input = SphinxSearch.param_name || 'q'
31
+ return <<-HTML
32
+ <form action="#{tag.globals.page.url}" method="get" id="#{form_id}" class="#{form_class}">
33
+ <input type="text" name="#{form_input}" value="#{tag.locals.query}">
34
+ <input type="submit" value="#{form_value}">
35
+ </form>
36
+ HTML
37
+ end
38
+
39
+ desc %{
40
+ Namespace for search results. Expands if a query was passed and at least
41
+ one result was found. Takes a @paginated@ attribute and all the standard
42
+ pagination attributes.
43
+
44
+ *Usage:*
45
+
46
+ <pre><code><r:search:results [paginated="false|true"] [per_page="..."] [...other pagination attributes]>...</r:search:results></code></pre>
47
+ }
48
+ tag 'search:results' do |tag|
49
+ options = thinking_sphinx_options(tag)
50
+ paging = pagination_find_options(tag)
51
+ if paging
52
+ options.merge!(paging)
53
+ tag.locals.pagination_opts = will_paginate_options(tag)
54
+ end
55
+ tag.globals.results ||= ThinkingSphinx.search(tag.locals.query, options)
56
+ tag.expand if tag.globals.results.any? and not tag.locals.query.blank?
57
+ end
58
+
59
+ desc %{
60
+ Expands if a query was run but no results were returned.
61
+ }
62
+ tag 'search:no_results' do |tag|
63
+ tag.expand if tag.globals.results.blank? and not tag.locals.query.blank?
64
+ end
65
+
66
+ desc %{
67
+ Displays the total (unpaginated) number of search results, in the format
68
+ *X results.* If you would like a different label than "result", pass
69
+ the optional @label@ attribute. The label will be pluralized as necessary.
70
+
71
+ *Usage:*
72
+
73
+ <pre><code><r:search:results:count [label="..."] /></code></pre>
74
+ }
75
+ tag 'search:results:count' do |tag|
76
+ label = tag.attr['label'] || 'result'
77
+ pluralize tag.globals.results.total_entries, label
78
+ end
79
+
80
+ desc %{
81
+ Returns the current page of search results.
82
+ }
83
+ tag 'search:results:current_page' do |tag|
84
+ tag.globals.results.current_page
85
+ end
86
+
87
+ desc %{
88
+ Returns the total number of pages of search results.
89
+ }
90
+ tag 'search:results:total_pages' do |tag|
91
+ tag.globals.results.total_pages
92
+ end
93
+
94
+ desc %{
95
+ Displays the original search term, sanitized for display.
96
+ }
97
+ tag 'search:query' do |tag|
98
+ ActionController::Base.helpers.strip_tags tag.locals.query
99
+ end
100
+
101
+ desc %{
102
+ Iterates over each search result. Sets @tag.locals.page@ to the current result.
103
+
104
+ *Usage:*
105
+
106
+ <pre><code><r:search:results:each>...</r:search:results:each></code></pre>
107
+ }
108
+ tag 'search:results:each' do |tag|
109
+ tag.globals.results.collect do |result|
110
+ tag.locals.page = result
111
+ tag.expand
112
+ end.join("\n")
113
+ end
114
+
115
+ desc %{
116
+ Returns the associated excerpt for each search result. If you want to
117
+ take the excerpt from the page title or just one specific page part, use
118
+ the optional @for@ attribute.
119
+
120
+ *Usage:*
121
+
122
+ <pre><code><r:search:results:each:excerpt [for="title|part_name"]/></code></pre>
123
+ }
124
+ tag 'search:results:each:excerpt' do |tag|
125
+ content = case tag.attr['for']
126
+ when 'title' : tag.locals.page.title
127
+ when nil : tag.locals.page.parts.map(&:content).join(' ')
128
+ else tag.locals.page.part(tag.attr['for']).try(:content) || ''
129
+ end
130
+ tag.globals.results.excerpt_for(content, tag.locals.page.class)
131
+ end
132
+
133
+ desc %{
134
+ Renders pagination links for the results.
135
+
136
+ *Usage:*
137
+ <pre><code><r:search:results paginated="true" [pagination options]>...<r:search:results:pagination /></r:search:results></code></pre>
138
+ }
139
+ tag 'search:results:pagination' do |tag|
140
+ if tag.globals.results
141
+ will_paginate(tag.globals.results, tag.locals.pagination_opts)
142
+ end
143
+ end
144
+
145
+ desc %{
146
+ Renders if no a query was run without a term, e.g. someone hit the search
147
+ button without entering anything.
148
+ }
149
+ tag 'search:empty_query' do |tag|
150
+ tag.expand if tag.locals.query.try(:empty?)
151
+ end
152
+
153
+ private
154
+
155
+ def will_paginate_options(tag)
156
+ options = super
157
+ options[:renderer] &&= SphinxSearch::LinkRenderer.new(tag.globals.page.url, tag.locals.query)
158
+ options
159
+ end
160
+
161
+ def thinking_sphinx_options(tag)
162
+ {
163
+ :with => { :status_id => 100, :virtual => false },
164
+ :without => { :class_crc => SphinxSearch.hidden_classes.map(&:to_crc32) },
165
+ :retry_stale => true
166
+ }
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,67 @@
1
+ require 'thinking_sphinx/tasks'
2
+
3
+ namespace :radiant do
4
+ namespace :extensions do
5
+ namespace :sphinx_search do
6
+
7
+ desc "Runs the migration of the Sphinx Search extension"
8
+ task :migrate => :environment do
9
+ require 'radiant/extension_migrator'
10
+ if ENV["VERSION"]
11
+ SphinxSearchExtension.migrator.migrate(ENV["VERSION"].to_i)
12
+ else
13
+ SphinxSearchExtension.migrator.migrate
14
+ end
15
+ end
16
+
17
+ desc "Turns off indexing for subsequent tasks"
18
+ task :disable_deltas do
19
+ ThinkingSphinx.deltas_enabled = false
20
+ end
21
+
22
+ desc "Copies public assets of the Sphinx Search to the instance public/ directory."
23
+ task :update => :environment do
24
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
25
+ puts "Copying assets from SphinxSearchExtension"
26
+ Dir[SphinxSearchExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
27
+ path = file.sub(SphinxSearchExtension.root, '')
28
+ directory = File.dirname(path)
29
+ mkdir_p RAILS_ROOT + directory, :verbose => false
30
+ cp file, RAILS_ROOT + path, :verbose => false
31
+ end
32
+ unless SphinxSearchExtension.root.starts_with? RAILS_ROOT # don't need to copy vendored tasks
33
+ puts "Copying rake tasks from SphinxSearchExtension"
34
+ local_tasks_path = File.join(RAILS_ROOT, %w(lib tasks))
35
+ mkdir_p local_tasks_path, :verbose => false
36
+ Dir[File.join SphinxSearchExtension.root, %w(lib tasks *.rake)].each do |file|
37
+ cp file, local_tasks_path, :verbose => false
38
+ end
39
+ end
40
+ end
41
+
42
+ desc "Copies the sphinx.yml config to the project root"
43
+ task :config => :environment do
44
+ config_temp = File.join(SphinxSearchExtension.root, %w(lib templates sphinx.yml))
45
+ config_dest = File.join(RAILS_ROOT, %w(config sphinx.yml))
46
+ cp(config_temp, config_dest, :verbose => false) unless File.exists?(config_dest)
47
+
48
+ init_temp = File.join(SphinxSearchExtension.root, %w(lib templates initializer.rb))
49
+ init_dest = File.join(RAILS_ROOT, %w(config initializers sphinx_search.rb))
50
+ cp(init_temp, init_dest, :verbose => false) unless File.exists?(init_dest)
51
+ end
52
+ task :update => :config
53
+
54
+ desc 'Run specs with coverage'
55
+ task :coverage do
56
+ Spec::Rake::SpecTask.new('radiant:extensions:sphinx_search:coverage') do |t|
57
+ t.spec_opts = ['--format profile', '--loadby mtime', '--reverse']
58
+ t.spec_files = FileList['vendor/extensions/sphinx_search/spec/**/*_spec.rb']
59
+ t.rcov = true
60
+ t.rcov_opts = ['--exclude', 'gems,spec,/usr/lib/ruby,config,vendor/radiant', '--include-file', 'vendor/extensions/sphinx_search/app,vendor/extensions/sphinx_search/lib', '--sort', 'coverage']
61
+ t.rcov_dir = 'coverage'
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,10 @@
1
+ # The parameter passed in your search URLs.
2
+ SphinxSearch.param_name = 'q'
3
+
4
+ # Max length of content to index, per page. Bump this up if you have more than
5
+ # 8k of text per page (as calculated by combining all page parts.)
6
+ SphinxSearch.content_length = 8.kilobytes
7
+
8
+ # Don't include these page subclasses in the search results. This can also be
9
+ # accessed by declaring `self.searchable [true|false]` in your Page subclasses.
10
+ SphinxSearch.hidden_classes = %w(SearchPage JavascriptPage StylesheetPage)
@@ -0,0 +1,6 @@
1
+ development: &dev
2
+ port: 9312
3
+ html_strip: true
4
+
5
+ production:
6
+ <<: *dev
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{radiant-sphinx_search-extension}
8
+ s.version = "0.9.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Josh French", "Kunal Shah", "Justin Blecher"]
12
+ s.date = %q{2010-08-08}
13
+ s.description = %q{Adds fulltext search capability to Radiant content via Thinking Sphinx.}
14
+ s.email = %q{git@digitalpulp.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitmodules",
20
+ "README.md",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "app/models/search_page.rb",
24
+ "app/views/admin/pages/_search_toggle.haml",
25
+ "db/migrate/20100808155751_add_delta_to_pages.rb",
26
+ "lib/radiant-sphinx_search-extension.rb",
27
+ "lib/sphinx_search.rb",
28
+ "lib/sphinx_search/link_renderer.rb",
29
+ "lib/sphinx_search/page_context_extensions.rb",
30
+ "lib/sphinx_search/page_extensions.rb",
31
+ "lib/sphinx_search/radius_tags.rb",
32
+ "lib/tasks/sphinx_search_extension_tasks.rake",
33
+ "lib/templates/initializer.rb",
34
+ "lib/templates/sphinx.yml",
35
+ "radiant-sphinx_search-extension.gemspec",
36
+ "spec/datasets/search_pages_dataset.rb",
37
+ "spec/lib/search_radius_tags_spec.rb",
38
+ "spec/models/page_spec.rb",
39
+ "spec/spec.opts",
40
+ "spec/spec_helper.rb",
41
+ "sphinx_search_extension.rb"
42
+ ]
43
+ s.homepage = %q{http://ext.radiantcms.org/extensions/15-sphinx-search}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.6}
47
+ s.summary = %q{Sphinx Search Extension for Radiant CMS}
48
+ s.test_files = [
49
+ "spec/datasets/search_pages_dataset.rb",
50
+ "spec/lib/search_radius_tags_spec.rb",
51
+ "spec/models/page_spec.rb",
52
+ "spec/spec_helper.rb"
53
+ ]
54
+
55
+ if s.respond_to? :specification_version then
56
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
60
+ s.add_runtime_dependency(%q<radiant>, [">= 0"])
61
+ s.add_runtime_dependency(%q<thinking-sphinx>, [">= 1.3.3"])
62
+ else
63
+ s.add_dependency(%q<radiant>, [">= 0"])
64
+ s.add_dependency(%q<thinking-sphinx>, [">= 1.3.3"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<radiant>, [">= 0"])
68
+ s.add_dependency(%q<thinking-sphinx>, [">= 1.3.3"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,13 @@
1
+ class SearchPagesDataset < Dataset::Base
2
+ uses :home_page
3
+
4
+ def load
5
+ create_page 'searchable', :slug => 'searchable', :parent_id => page_id(:home),
6
+ :description => 'a searchable page' do
7
+ create_page_part "body", :content => "Hello world!", :id => 1
8
+ create_page_part "extended", :content => "sweet harmonious biscuits", :id => 2
9
+ end
10
+
11
+ create_page 'search_results', :slug => 'search_results', :parent_id => page_id(:home), :class_name => "SearchPage"
12
+ end
13
+ end
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class Array
4
+ alias_method :total_entries, :size # make arrays quack like TS result sets
5
+ end
6
+
7
+ describe SphinxSearch::RadiusTags do
8
+ dataset :search_pages
9
+
10
+ before do
11
+ @page = pages(:search_results)
12
+ @request = ActionController::TestRequest.new
13
+ @request.params[:q] = 'query'
14
+ ActionController::TestRequest.stub!(:new).and_return(@request)
15
+ ThinkingSphinx.stub!(:search).and_return([pages(:searchable)])
16
+ end
17
+
18
+ describe 'r:search:form' do
19
+ it "should render a form input" do
20
+ form = @page.should render('<r:search:form id="test" class="test" value="go" />').matching(/action="#{@page.url}"/)
21
+ end
22
+ end
23
+
24
+ describe "r:search:results" do
25
+ it "should expand if there are results" do
26
+ @page.should render('<r:search:results>hi</r:search:results>').as('hi')
27
+ end
28
+
29
+ it "should not expand if no query was run" do
30
+ @request.params[:q] = ''
31
+ @page.should render('<r:search:results>hi</r:search:results>').as('')
32
+ end
33
+ end
34
+
35
+ describe "r:search:results:each" do
36
+ it "should iterate over results" do
37
+ @page.should render('<r:search:results:each><r:title /></r:search:results:each>').as(pages(:searchable).title)
38
+ end
39
+ end
40
+
41
+ describe "r:search:results:count" do
42
+ it "should return result count" do
43
+ @page.should render('<r:search:results:count />').as("1 result")
44
+ end
45
+
46
+ it "should pluralize any label" do
47
+ ThinkingSphinx.stub!(:search).and_return([pages(:searchable), pages(:search_results)])
48
+ @page.should render('<r:search:results:count label="item" />').as("2 items")
49
+ end
50
+ end
51
+
52
+ describe "r:search:query" do
53
+ it "should sanitize query" do
54
+ @request.params[:q] = '<script>query'
55
+ @page.should render('<r:search:query />').as('query')
56
+ end
57
+ end
58
+
59
+ describe "r:search:empty_query" do
60
+ it "should render if query was blank" do
61
+ @request.params[:q] = ''
62
+ @page.should render('<r:search:empty_query>empty</r:search:empty_query>').as('empty')
63
+ end
64
+
65
+ end
66
+
67
+ describe "r:search:no_results" do
68
+ it "should render if results were empty" do
69
+ ThinkingSphinx.stub!(:search).and_return([])
70
+ @page.should render('<r:search:no_results>none</r:search:no_results>').as('none')
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Page do
4
+ describe "Page.searchable" do
5
+ it "should add itself to the hidden classes array" do
6
+ SphinxSearch.hidden_classes = []
7
+ FileNotFoundPage.searchable false
8
+ SphinxSearch.hidden_classes.should include('FileNotFoundPage')
9
+ end
10
+
11
+ it "should remove itself from the hidden classes array" do
12
+ SphinxSearch.hidden_classes = %w(FileNotFoundPage)
13
+ FileNotFoundPage.searchable true
14
+ SphinxSearch.hidden_classes.should_not include("FileNotFoundPage")
15
+ end
16
+ end
17
+
18
+ describe "#thinking_sphinx_options" do
19
+ it "should description" do
20
+ SphinxSearch.hidden_classes = %w(FileNotFoundPage)
21
+ opts = SearchPage.new.send :thinking_sphinx_options, nil
22
+ opts[:without][:class_crc].should include('FileNotFoundPage'.to_crc32)
23
+ end
24
+ end
25
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -0,0 +1,37 @@
1
+ unless defined? RADIANT_ROOT
2
+ ENV["RAILS_ENV"] = "test"
3
+ case
4
+ when ENV["RADIANT_ENV_FILE"]
5
+ require ENV["RADIANT_ENV_FILE"]
6
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
7
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
8
+ else
9
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
10
+ end
11
+ end
12
+ require "#{RADIANT_ROOT}/spec/spec_helper"
13
+
14
+ Dataset::Resolver.default << (File.dirname(__FILE__) + "/datasets")
15
+
16
+ if File.directory?(File.dirname(__FILE__) + "/matchers")
17
+ Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
18
+ end
19
+
20
+ Spec::Runner.configure do |config|
21
+ SphinxSearch.hidden_classes ||= %w(SearchPage JavascriptPage StylesheetPage)
22
+ # config.use_transactional_fixtures = true
23
+ # config.use_instantiated_fixtures = false
24
+ # config.fixture_path = RAILS_ROOT + '/spec/fixtures'
25
+
26
+ # You can declare fixtures for each behaviour like this:
27
+ # describe "...." do
28
+ # fixtures :table_a, :table_b
29
+ #
30
+ # Alternatively, if you prefer to declare them only once, you can
31
+ # do so here, like so ...
32
+ #
33
+ # config.global_fixtures = :table_a, :table_b
34
+ #
35
+ # If you declare global fixtures, be aware that they will be declared
36
+ # for all of your examples, even those that don't use them.
37
+ end
@@ -0,0 +1,14 @@
1
+ class SphinxSearchExtension < Radiant::Extension
2
+ version YAML::load_file(File.join(File.dirname(__FILE__), 'VERSION'))
3
+ description "Search Pages with ThinkingSphinx"
4
+ url "http://digitalpulp.com"
5
+
6
+ def activate
7
+ Page.send(:include, SphinxSearch::PageExtensions)
8
+ PageContext.send(:include, SphinxSearch::PageContextExtensions)
9
+ end
10
+
11
+ def deactivate
12
+ end
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radiant-sphinx_search-extension
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 9
8
+ - 1
9
+ version: 0.9.1
10
+ platform: ruby
11
+ authors:
12
+ - Josh French
13
+ - Kunal Shah
14
+ - Justin Blecher
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-08 00:00:00 -04:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: radiant
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: thinking-sphinx
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 3
44
+ - 3
45
+ version: 1.3.3
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ description: Adds fulltext search capability to Radiant content via Thinking Sphinx.
49
+ email: git@digitalpulp.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - README.md
56
+ files:
57
+ - .gitmodules
58
+ - README.md
59
+ - Rakefile
60
+ - VERSION
61
+ - app/models/search_page.rb
62
+ - app/views/admin/pages/_search_toggle.haml
63
+ - db/migrate/20100808155751_add_delta_to_pages.rb
64
+ - lib/radiant-sphinx_search-extension.rb
65
+ - lib/sphinx_search.rb
66
+ - lib/sphinx_search/link_renderer.rb
67
+ - lib/sphinx_search/page_context_extensions.rb
68
+ - lib/sphinx_search/page_extensions.rb
69
+ - lib/sphinx_search/radius_tags.rb
70
+ - lib/tasks/sphinx_search_extension_tasks.rake
71
+ - lib/templates/initializer.rb
72
+ - lib/templates/sphinx.yml
73
+ - radiant-sphinx_search-extension.gemspec
74
+ - spec/datasets/search_pages_dataset.rb
75
+ - spec/lib/search_radius_tags_spec.rb
76
+ - spec/models/page_spec.rb
77
+ - spec/spec.opts
78
+ - spec/spec_helper.rb
79
+ - sphinx_search_extension.rb
80
+ has_rdoc: true
81
+ homepage: http://ext.radiantcms.org/extensions/15-sphinx-search
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options:
86
+ - --charset=UTF-8
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project:
106
+ rubygems_version: 1.3.6
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Sphinx Search Extension for Radiant CMS
110
+ test_files:
111
+ - spec/datasets/search_pages_dataset.rb
112
+ - spec/lib/search_radius_tags_spec.rb
113
+ - spec/models/page_spec.rb
114
+ - spec/spec_helper.rb