mongoid_search2 0.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +121 -0
  3. data/Rakefile +12 -0
  4. data/VERSION +1 -0
  5. data/lib/mongoid_search/log.rb +22 -0
  6. data/lib/mongoid_search/mongoid_search.rb +127 -0
  7. data/lib/mongoid_search/railtie.rb +10 -0
  8. data/lib/mongoid_search/stemmers.rb +26 -0
  9. data/lib/mongoid_search/util.rb +45 -0
  10. data/lib/mongoid_search.rb +4 -0
  11. data/spec/config/ignorelist.yml +2 -0
  12. data/spec/coverage/assets/0.4.5/app.js +66 -0
  13. data/spec/coverage/assets/0.4.5/fancybox/blank.gif +0 -0
  14. data/spec/coverage/assets/0.4.5/fancybox/fancy_close.png +0 -0
  15. data/spec/coverage/assets/0.4.5/fancybox/fancy_loading.png +0 -0
  16. data/spec/coverage/assets/0.4.5/fancybox/fancy_nav_left.png +0 -0
  17. data/spec/coverage/assets/0.4.5/fancybox/fancy_nav_right.png +0 -0
  18. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_e.png +0 -0
  19. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_n.png +0 -0
  20. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_ne.png +0 -0
  21. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_nw.png +0 -0
  22. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_s.png +0 -0
  23. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_se.png +0 -0
  24. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_sw.png +0 -0
  25. data/spec/coverage/assets/0.4.5/fancybox/fancy_shadow_w.png +0 -0
  26. data/spec/coverage/assets/0.4.5/fancybox/fancy_title_left.png +0 -0
  27. data/spec/coverage/assets/0.4.5/fancybox/fancy_title_main.png +0 -0
  28. data/spec/coverage/assets/0.4.5/fancybox/fancy_title_over.png +0 -0
  29. data/spec/coverage/assets/0.4.5/fancybox/fancy_title_right.png +0 -0
  30. data/spec/coverage/assets/0.4.5/fancybox/fancybox-x.png +0 -0
  31. data/spec/coverage/assets/0.4.5/fancybox/fancybox-y.png +0 -0
  32. data/spec/coverage/assets/0.4.5/fancybox/fancybox.png +0 -0
  33. data/spec/coverage/assets/0.4.5/fancybox/jquery.fancybox-1.3.1.css +363 -0
  34. data/spec/coverage/assets/0.4.5/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  35. data/spec/coverage/assets/0.4.5/favicon.png +0 -0
  36. data/spec/coverage/assets/0.4.5/jquery-1.4.2.min.js +155 -0
  37. data/spec/coverage/assets/0.4.5/jquery.dataTables.min.js +152 -0
  38. data/spec/coverage/assets/0.4.5/jquery.timeago.js +141 -0
  39. data/spec/coverage/assets/0.4.5/jquery.url.js +174 -0
  40. data/spec/coverage/assets/0.4.5/loading.gif +0 -0
  41. data/spec/coverage/assets/0.4.5/magnify.png +0 -0
  42. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  43. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  44. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  45. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  46. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  47. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  48. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  49. data/spec/coverage/assets/0.4.5/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  50. data/spec/coverage/assets/0.4.5/smoothness/images/ui-icons_222222_256x240.png +0 -0
  51. data/spec/coverage/assets/0.4.5/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  52. data/spec/coverage/assets/0.4.5/smoothness/images/ui-icons_454545_256x240.png +0 -0
  53. data/spec/coverage/assets/0.4.5/smoothness/images/ui-icons_888888_256x240.png +0 -0
  54. data/spec/coverage/assets/0.4.5/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  55. data/spec/coverage/assets/0.4.5/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  56. data/spec/coverage/assets/0.4.5/stylesheet.css +341 -0
  57. data/spec/coverage/assets/0.5.3/app.js +88 -0
  58. data/spec/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  59. data/spec/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  60. data/spec/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  61. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  62. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  63. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  64. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  65. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  66. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  67. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  68. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  69. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  70. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  71. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  72. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  73. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  74. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  75. data/spec/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  76. data/spec/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  77. data/spec/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  78. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
  79. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  80. data/spec/coverage/assets/0.5.3/favicon_green.png +0 -0
  81. data/spec/coverage/assets/0.5.3/favicon_red.png +0 -0
  82. data/spec/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  83. data/spec/coverage/assets/0.5.3/highlight.css +129 -0
  84. data/spec/coverage/assets/0.5.3/highlight.pack.js +1 -0
  85. data/spec/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
  86. data/spec/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
  87. data/spec/coverage/assets/0.5.3/jquery.timeago.js +141 -0
  88. data/spec/coverage/assets/0.5.3/jquery.url.js +174 -0
  89. data/spec/coverage/assets/0.5.3/loading.gif +0 -0
  90. data/spec/coverage/assets/0.5.3/magnify.png +0 -0
  91. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  92. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  93. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  94. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  95. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  96. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  97. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  98. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  99. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  100. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  101. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  102. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  103. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  104. data/spec/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  105. data/spec/coverage/assets/0.5.3/stylesheet.css +383 -0
  106. data/spec/coverage/index.html +702 -0
  107. data/spec/coverage/resultset.yml +631 -0
  108. data/spec/models/category.rb +6 -0
  109. data/spec/models/product.rb +13 -0
  110. data/spec/models/subproduct.rb +8 -0
  111. data/spec/models/tag.rb +6 -0
  112. data/spec/models/variant.rb +4 -0
  113. data/spec/mongoid_search_spec.rb +161 -0
  114. data/spec/spec_helper.rb +34 -0
  115. data/spec/util_spec.rb +62 -0
  116. metadata +343 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mauricio Zaffari
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,121 @@
1
+ Mongoid Search
2
+ ============
3
+
4
+ Mongoid Search is a simple full text search implementation for Mongoid ORM.
5
+
6
+ Installation
7
+ --------
8
+
9
+ In your Gemfile:
10
+
11
+ gem 'mongoid_search'
12
+
13
+ Then:
14
+
15
+ bundle install
16
+
17
+ Examples
18
+ --------
19
+
20
+ class Product
21
+ include Mongoid::Document
22
+ include Mongoid::Search
23
+ field :brand
24
+ field :name
25
+
26
+ references_many :tags
27
+ refereced_in :category
28
+
29
+ search_in :brand, :name, :tags => :name, :category => :name
30
+ end
31
+
32
+ class Tag
33
+ include Mongoid::Document
34
+ field :name
35
+
36
+ referenced_in :product
37
+ end
38
+
39
+ class Category
40
+ include Mongoid::Document
41
+ field :name
42
+
43
+ references_many :products
44
+ end
45
+
46
+ Now when you save a product, you get a _keywords field automatically:
47
+
48
+ p = Product.new :brand => "Apple", :name => "iPhone"
49
+ p.tags << Tag.new(:name => "Amazing")
50
+ p.tags << Tag.new(:name => "Awesome")
51
+ p.tags << Tag.new(:name => "Superb")
52
+ p.save
53
+ => true
54
+ p._keywords
55
+
56
+ Now you can run search, which will look in the _keywords field and return all matching results:
57
+
58
+ Product.search("apple iphone").size
59
+ => 1
60
+
61
+ Note that the search is case insensitive, and accept partial searching too:
62
+
63
+ Product.search("ipho").size
64
+ => 1
65
+
66
+ Assuming you have a category with multiple products you can now use the following
67
+ code to search for 'iphone' in products cheaper than $499
68
+
69
+ @category.products.where(:price.lt => 499).csearch('iphone').asc(:price)
70
+
71
+ In this case we have to use csearch, an alias for search, because since v2.0.0
72
+ Mongoid defines it's own Criteria.search method.
73
+
74
+
75
+ Options
76
+ -------
77
+
78
+ match:
79
+ _:any_ - match any occurrence
80
+ _:all_ - match all ocurrences
81
+ Default is _:any_.
82
+
83
+ search_in :brand, :name, { :tags => :name }, { :match => :any }
84
+
85
+ Product.search("apple motorola").size
86
+ => 1
87
+
88
+ search_in :brand, :name, { :tags => :name }, { :match => :all }
89
+
90
+ Product.search("apple motorola").size
91
+ => 0
92
+
93
+ allow_empty_search:
94
+ _true_ - match any occurrence
95
+ _false_ - match all ocurrences
96
+ Default is _false_.
97
+
98
+ search_in :brand, :name, { :tags => :name }, { :allow_empty_search => true }
99
+
100
+ Product.search("").size
101
+ => 1
102
+
103
+ ignore_list:
104
+ Pass in an ignore list location. Keywords in that list will be ignored.
105
+
106
+ search_in :brand, :name, { :tags => :name }, { :ignore_list => Rails.root.join("config", "ignorelist.yml") }
107
+
108
+ The list should look like:
109
+
110
+ ignorelist:
111
+ a, an, to, from, as
112
+
113
+ You can include how many keywords you like.
114
+
115
+ TODO
116
+ ----
117
+
118
+ * Strip html with sanitize (https://github.com/rgrove/sanitize)
119
+ * Rewrite and test relevant search
120
+ * Move all configurations to a configuration file. Maybe /config/mongoid_search.yml.
121
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require "bundler/gem_tasks"
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.rspec_opts = ["-c", "-f progress"]
9
+ spec.pattern = 'spec/**/*_spec.rb'
10
+ end
11
+
12
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
@@ -0,0 +1,22 @@
1
+ module MongoidSearch
2
+ class Log
3
+ cattr_accessor :silent
4
+
5
+ def self.log(message)
6
+ print message unless silent
7
+ end
8
+
9
+ def self.red(text)
10
+ log(colorize(text, 31))
11
+ end
12
+
13
+ def self.green(text)
14
+ log(colorize(text, 32))
15
+ end
16
+
17
+ private
18
+ def self.colorize(text, code)
19
+ "\033[#{code}m#{text}\033[0m"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,127 @@
1
+ require "mongoid_search/stemmers"
2
+
3
+ module Mongoid::Search
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ cattr_accessor :search_fields, :match, :allow_empty_search, :relevant_search, :stem_keywords, :stemmer_class, :ignore_list
8
+ end
9
+
10
+ def self.included(base)
11
+ @classes ||= []
12
+ @classes << base
13
+ end
14
+
15
+ def self.classes
16
+ @classes
17
+ end
18
+
19
+ module ClassMethods #:nodoc:
20
+ # Set a field or a number of fields as sources for search
21
+ def search_in(*args)
22
+ options = args.last.is_a?(Hash) && [:match, :allow_empty_search, :relevant_search, :stem_keywords, :ignore_list].include?(args.last.keys.first) ? args.pop : {}
23
+ self.match = [:any, :all].include?(options[:match]) ? options[:match] : :any
24
+ self.allow_empty_search = [true, false].include?(options[:allow_empty_search]) ? options[:allow_empty_search] : false
25
+ self.relevant_search = [true, false].include?(options[:relevant_search]) ? options[:relevant_search] : false
26
+ self.stemmer_class = options[:stemmer_class]
27
+ self.stem_keywords = !!stemmer_class || options[:stem_keywords]
28
+ self.stemmer_class ||= MongoidSearch::FastStemmer
29
+ self.ignore_list = YAML.load(File.open(options[:ignore_list]))["ignorelist"] if options[:ignore_list].present?
30
+ self.search_fields = (self.search_fields || []).concat args
31
+
32
+ field :_keywords, :type => Array
33
+ index :_keywords, :background => true
34
+
35
+ before_save :set_keywords
36
+ end
37
+
38
+ def search(query, options={})
39
+ if relevant_search
40
+ search_relevant(query, options)
41
+ else
42
+ search_without_relevance(query, options)
43
+ end
44
+ end
45
+
46
+ # Mongoid 2.0.0 introduces Criteria.seach so we need to provide
47
+ # alternate method
48
+ alias csearch search
49
+
50
+ def search_without_relevance(query, options={})
51
+ return criteria.all if query.blank? && allow_empty_search
52
+ criteria.send("#{(options[:match]||self.match).to_s}_in", :_keywords => MongoidSearch::Util.normalize_keywords(query, keyword_stemmer(options), ignore_list).map { |q| /#{q}/ })
53
+ end
54
+
55
+ def keyword_stemmer(options={})
56
+ stemmer_class.new(:language => options[:language])
57
+ end
58
+
59
+ def search_relevant(query, options={})
60
+ return criteria.all if query.blank? && allow_empty_search
61
+
62
+ keywords = MongoidSearch::Util.normalize_keywords(query, keyword_stemmer(options), ignore_list)
63
+
64
+ map = <<-EOS
65
+ function() {
66
+ var entries = 0
67
+ for(i in keywords)
68
+ for(j in this._keywords) {
69
+ if(this._keywords[j] == keywords[i])
70
+ entries++
71
+ }
72
+ if(entries > 0)
73
+ emit(this._id, entries)
74
+ }
75
+ EOS
76
+ reduce = <<-EOS
77
+ function(key, values) {
78
+ return(values[0])
79
+ }
80
+ EOS
81
+
82
+ #raise [self.class, self.inspect].inspect
83
+
84
+ kw_conditions = keywords.map do |kw|
85
+ {:_keywords => kw}
86
+ end
87
+
88
+ criteria = (criteria || self).any_of(*kw_conditions)
89
+
90
+ query = criteria.selector
91
+
92
+ options.delete(:limit)
93
+ options.delete(:skip)
94
+ options.merge! :scope => {:keywords => keywords}, :query => query
95
+
96
+ # res = collection.map_reduce(map, reduce, options)
97
+ # res.find.sort(['value', -1]) # Cursor
98
+ collection.map_reduce(map, reduce, options)
99
+ end
100
+
101
+ # Goes through all documents in the class that includes Mongoid::Search
102
+ # and indexes the keywords.
103
+ def index_keywords!
104
+ all.each { |d| d.index_keywords! ? MongoidSearch::Log.green(".") : MongoidSearch::Log.red("F") }
105
+ end
106
+ end
107
+
108
+ # Indexes the document keywords
109
+ def index_keywords!
110
+ update_attribute(:_keywords, set_keywords)
111
+ end
112
+
113
+ def keyword_language
114
+ super if defined?(super)
115
+ end
116
+
117
+ def keyword_stemmer
118
+ self.class.stemmer_class.new(:language => keyword_language) if stem_keywords
119
+ end
120
+
121
+ private
122
+ def set_keywords
123
+ self._keywords = self.search_fields.map do |field|
124
+ MongoidSearch::Util.keywords(self, field, keyword_stemmer, ignore_list)
125
+ end.flatten.reject{|k| k.nil? || k.empty?}.uniq.sort
126
+ end
127
+ end
@@ -0,0 +1,10 @@
1
+ require 'mongoid_search'
2
+ require 'rails'
3
+
4
+ module MongoidSearch
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ Dir[File.join(File.dirname(__FILE__), '../../tasks/*.rake')].each { |f| load f }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module MongoidSearch
2
+ class FastStemmer
3
+ def initialize(*args)
4
+ end
5
+
6
+ def call(word)
7
+ ::Stemmer.stem_word(word)
8
+ end
9
+ end
10
+
11
+ class LinguaStemmer
12
+ def initialize(*args, &block)
13
+ @stemmer = ::Lingua::Stemmer.new(*args, &block)
14
+ rescue Lingua::StemmerError => e
15
+ raise unless e.message.include?("not available")
16
+ end
17
+
18
+ def call(word)
19
+ if @stemmer
20
+ @stemmer.stem(word)
21
+ else
22
+ word
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ module MongoidSearch
2
+ module Util
3
+
4
+ def self.keywords(klass, field, stemmer, ignore_list)
5
+ if field.is_a?(Hash)
6
+ field.keys.map do |key|
7
+ attribute = klass.send(key)
8
+ unless attribute.blank?
9
+ method = field[key]
10
+ if attribute.is_a?(Array)
11
+ if method.is_a?(Array)
12
+ method.map {|m| attribute.map { |a| Util.normalize_keywords a.send(m), stemmer, ignore_list } }
13
+ else
14
+ attribute.map(&method).map { |t| Util.normalize_keywords t, stemmer, ignore_list }
15
+ end
16
+ else
17
+ Util.normalize_keywords(attribute.send(method), stemmer, ignore_list)
18
+ end
19
+ end
20
+ end
21
+ else
22
+ value = klass[field]
23
+ value = value.join(' ') if value.respond_to?(:join)
24
+ Util.normalize_keywords(value, stemmer, ignore_list) if value
25
+ end
26
+ end
27
+
28
+ def self.normalize_keywords(text, stemmer, ignore_list)
29
+ return [] if text.blank?
30
+ text = text.to_s.
31
+ mb_chars.
32
+ normalize(:kd).
33
+ downcase.
34
+ to_s.
35
+ gsub(/[._:;'"`,?|+={}()!@#%^&*<>~\$\-\\\/\[\]]/, ' '). # strip punctuation
36
+ gsub(/[^[:alnum:]\s]/,''). # strip accents
37
+ split(' ').
38
+ reject { |word| word.size < 2 }
39
+ text = text.reject { |word| ignore_list.include?(word) } unless ignore_list.blank?
40
+ text = text.map{ |word| stemmer.call(word) } if stemmer
41
+ text
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,4 @@
1
+ require 'mongoid_search/railtie' if defined?(Rails)
2
+ require 'mongoid_search/util'
3
+ require 'mongoid_search/log'
4
+ require 'mongoid_search/mongoid_search'
@@ -0,0 +1,2 @@
1
+ ignorelist:
2
+ amazing, awesome
@@ -0,0 +1,66 @@
1
+ $(document).ready(function() {
2
+ $('.file_list').dataTable({
3
+ "aaSorting": [[ 1, "asc" ]],
4
+ "bPaginate": false,
5
+ "bJQueryUI": true,
6
+ "aoColumns": [
7
+ null,
8
+ { "sType": "percent" },
9
+ null,
10
+ null,
11
+ null,
12
+ null
13
+ ]
14
+ });
15
+
16
+ $('.source_table tbody tr:odd').addClass('odd');
17
+ $('.source_table tbody tr:even').addClass('even');
18
+
19
+ $("a.src_link").fancybox({
20
+ 'hideOnContentClick': true
21
+ });
22
+
23
+ // Hide src files and file list container
24
+ $('.source_files').hide();
25
+ $('.file_list_container').hide();
26
+
27
+ // Add tabs based upon existing file_list_containers
28
+ $('.file_list_container h2').each(function(){
29
+ $('.group_tabs').append('<li><a href="#' + $(this).parent().attr('id') + '">' + $(this).html() + '</a></li>');
30
+ });
31
+
32
+ $('.group_tabs a').each( function() {
33
+ $(this).addClass($(this).attr('href').replace('#', ''));
34
+ });
35
+
36
+ $('.group_tabs a').live('focus', function() {
37
+ $(this).blur();
38
+ });
39
+
40
+ var favicon_path = $('link[rel="shortcut icon"]').attr('href')
41
+ $('.group_tabs a').live('click', function(){
42
+ if (!$(this).parent().hasClass('active')) {
43
+ $('.group_tabs a').parent().removeClass('active');
44
+ $(this).parent().addClass('active');
45
+ $('.file_list_container').hide();
46
+ $(".file_list_container" + $(this).attr('href')).show();
47
+ window.location.href = window.location.href.split('#')[0] + $(this).attr('href').replace('#', '#_');
48
+
49
+ // Force favicon reload - otherwise the location change containing anchor would drop the favicon...
50
+ // Works only on firefox, but still... - Anyone know a better solution to force favicon?
51
+ $('link[rel="shortcut icon"]').remove();
52
+ $('head').append('<link rel="shortcut icon" type="image/png" href="'+ favicon_path +'" />');
53
+ };
54
+ return false;
55
+ });
56
+
57
+ if (jQuery.url.attr('anchor')) {
58
+ $('.group_tabs a.'+jQuery.url.attr('anchor').replace('_', '')).click();
59
+ } else {
60
+ $('.group_tabs a:first').click();
61
+ };
62
+
63
+ $("abbr.timeago").timeago();
64
+ $('#loading').fadeOut();
65
+ $('#wrapper').show();
66
+ });