acts_as_suggest 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +1 -0
  3. data/lib/acts_as_suggest.rb +129 -0
  4. metadata +59 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDM1YWQ1YWFlOWE0OTM3NzE4MjlkODIyN2UyOTgwNmM1NTQyMjc4Zg==
5
+ data.tar.gz: !binary |-
6
+ MTFkMTJhMDE3NDZhOGYwYmNmMTM1NTU1Y2Q1YmU0NGY0ZmMxMDlmYg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDJmZDNmOGY1NDcxZWZlMGZjZDY3MjIxNjJhY2VmNTNhNjI4ZmMyYjFkNDRi
10
+ OWJmMTQ5OTczMmRkNzFiYTY0OWUyMTFlOWFkMWQzYTA0Y2ZhMmQzMGI5MGVj
11
+ YTk1Y2I3ZjQxMTI3OWRmMjhlMTVmMWVhMzExYWNhNDNlZmQ2MDA=
12
+ data.tar.gz: !binary |-
13
+ M2NmMTNkMzFiZTQ5OWZlNDQ4NmRiOTRjMDczZTg4MzliMDY2YjY3ZjBjMzBk
14
+ YWU4YzE5Njg0ZWIyZTQwMzg1MTJlZjJhZjRjMjY5ODYyOWY5MzU1MmIzZWY2
15
+ NWMzMTBhNDQ0MTY0NTZhNWViMDU5MDE1NzRmNjU2NWI4MmVkNWU=
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,129 @@
1
+ Gem.loaded_specs['acts_as_suggest'].dependencies.each do |d| require d.name end
2
+
3
+ require 'text'
4
+
5
+ module ActiveRecord #:nodoc:
6
+ module Acts #:nodoc:
7
+ module Suggest#:nodoc:
8
+
9
+ def self.included(base) #:nodoc:
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ module ClassMethods #:nodoc:
14
+ def acts_as_suggest
15
+ extend ActiveRecord::Acts::Suggest::SingletonMethods
16
+ end
17
+ end
18
+
19
+ # When searching for the word "honnolullu", Google will promptly suggest "Did you mean: honolulu". This small module provides a +suggest+ method
20
+ # which enables developers to add this functionality to any model, basing the suggestion on the existing values in the table.
21
+ # Just place <tt>acts_as_suggest</tt> in a given model to mixin the method +suggest+.
22
+ #
23
+ # Example:
24
+ # class Person < ActiveRecord::Base
25
+ # acts_as_suggest
26
+ # end
27
+ #
28
+ # and then to retrieve suggestions:
29
+ #
30
+ # MyModel.suggest(:field_name, searched_value, optional_treshold)
31
+ # or
32
+ # MyModel.suggest([:field1, :field2, ...], searched_value, optional_treshold)
33
+ #
34
+ # The field_name(s) specify in what columns we need to look for the suggested/existing values.
35
+ # The searched_value is the supposedly misspelled string for which we want to retrieve corrections.
36
+ # The optional_treshold defines the tolerance level in determining the Levenshtein distance between the searched string and existing values in the database.
37
+ # If omitted, this value is calculated based on the length of the string.
38
+ module SingletonMethods
39
+ # Output:
40
+ # * If the value of +word+ exists for the specified column(s) => Records are returned (equivalent to a find(:all, :conditions => '...')
41
+ # * If the value doesn't exist in the table, but there are similar existing ones in the specified field(s) => An array of possible intended values is returned
42
+ # * If the value doesn't exist in the table and there are not enough similar strings stored => [] is returned
43
+ #
44
+ # Examples:
45
+ # Person.suggest(:city, 'Rome') #=> [#<Post:0x556fcd4 @attributes={"city"=>"Rome", "name"=>"Antonio", "id"=>"1","country"=>"Italy"}>]
46
+ # Person.suggest(:city, 'Rom') #=> ["Rome", "Roma"]
47
+ # Person.suggest([:city, :country], 'Romai'] #=> ["Rome", "Roma", "Romania"]
48
+ # Person.suggest(:city, 'Vancovvver', 1) #=> []
49
+ def suggest(fields, word, treshold = nil)
50
+ similar_results = []
51
+ # Define treshold if not explicitly specified
52
+ unless treshold
53
+ if word.size <= 4
54
+ treshold = 1
55
+ else
56
+ # Longer words should have more tolerance
57
+ treshold = word.size/3
58
+ end
59
+ end
60
+
61
+ # Checks if an array of fields is passed
62
+ if fields.kind_of?(Array)
63
+ conditions = ""
64
+ # Hash that will contain the values for the matching symbol keys
65
+ param_hash = {}
66
+ # Builds the conditions for the find method
67
+ # and fills the hash for the named parameters
68
+ fields.each_with_index do |field, i|
69
+ param_hash[field] = word
70
+ if fields.size > 1 && i < fields.size - 1
71
+ conditions += "#{field} = :#{field} OR "
72
+ else
73
+ conditions += "#{field} = :#{field}"
74
+ end
75
+ end
76
+ # Search multiple fields through named bind variables
77
+ # (for safety against tainted data)
78
+ search_results = self.find(:all, :conditions => [conditions, param_hash])
79
+ else
80
+ # Only one field to search in
81
+ search_results = self.find(:all, :conditions => ["#{fields} = ?", word])
82
+ end
83
+
84
+ # Checks if +word+ exist in the requested field(s)
85
+ if search_results.empty?
86
+ # Retrieves list of all existing values in the table
87
+ all_results = self.find(:all)
88
+ # Checks if the table is empty
89
+ unless all_results.empty?
90
+ all_results.each do |record|
91
+ if fields.kind_of?(Array)
92
+ # Adds all the strings that are similar to the one passed as a parameter (searching in the specified fields)
93
+ fields.each {|field| similar_results << record.send(field).to_s if record.send(field).to_s.similar?(word, treshold)}
94
+ else
95
+ # Adds all the strings that are similar to the one passed as a parameter (searching the single field specified only)
96
+ similar_results << record.send(fields).to_s if record.send(fields).to_s.similar?(word, treshold)
97
+ end
98
+ end
99
+ end
100
+ # Remove multiple entries of the same string from the results
101
+ return similar_results.uniq
102
+ else
103
+ # The value exists in the table,
104
+ # the corrisponding records are therefore returned in an array
105
+ return search_results
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end
114
+ end
115
+
116
+ class String
117
+ # Levenshtein distance, used to determine the minimum number
118
+ # of changes needed to modify a string into another one.
119
+ # It uses the Text gem for its UTF-8 enabled comparisons.
120
+ def distance(other)
121
+ Text::Levenshtein::distance(self, other)
122
+ end
123
+
124
+ # Determines if two strings are similar based
125
+ # on a provided treshold.
126
+ def similar?(other, threshold = 2)
127
+ self.distance(other) <= threshold ? true : false
128
+ end
129
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_suggest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Antonio Cangiano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: text
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ description: A simple word suggestion
28
+ email: info@antoniocangiano.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - lib/acts_as_suggest.rb
35
+ homepage: http://rubygems.org/gems/acts_as_suggest
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.2.2
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: acts as sugges
59
+ test_files: []