acts_as_tokenizable 0.7.0 → 0.9.0
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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.hound.yml +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +16 -0
- data/.ruby-style.yml +7 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README.rdoc +7 -2
- data/Rakefile +4 -50
- data/acts_as_tokenizable.gemspec +25 -50
- data/lib/acts_as_tokenizable.rb +15 -5
- data/lib/acts_as_tokenizable/acts_as_tokenizable.rb +42 -23
- data/lib/acts_as_tokenizable/string_utils.rb +26 -18
- data/lib/acts_as_tokenizable/version.rb +3 -0
- data/lib/tasks/acts_as_tokenizable.rake +27 -29
- data/spec/acts_as_tokenizable_spec.rb +72 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/string_utils_spec.rb +82 -0
- data/spec/support_helper.rb +56 -0
- metadata +136 -27
- data/README +0 -0
- data/VERSION +0 -1
- data/init.rb +0 -1
- data/test/helper.rb +0 -10
- data/test/test_acts_as_tokenizable.rb +0 -7
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 17eaf4f8283785561ba5703894d935e0c54a527d
|
4
|
+
data.tar.gz: a19c66a85adf99293addc2bb3c3214cfc6544c1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db4f0327800efe794ff813630cb24df22c7852fcdb674851034cca68bb73790de70bad6fc84f5ddbcb57091679879628ce3a66a5b3e19ff727ac4bf634def1f4
|
7
|
+
data.tar.gz: 4f2443a43d80f329a32ad65f4b50a0aaea794fe9671a556654c416176cdc1182eb132bc8767e8a9abbe15a9c1e41e324df7af6b976908650b457dea2f874689f
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.hound.yml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-05-16 07:44:12 +0200 using RuboCop version 0.39.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 2
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 22
|
12
|
+
|
13
|
+
# Offense count: 2
|
14
|
+
# Configuration parameters: CountComments.
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Max: 17
|
data/.ruby-style.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.3
|
4
|
+
cache: bundler
|
5
|
+
script: bundle exec rake
|
6
|
+
addons:
|
7
|
+
code_climate:
|
8
|
+
repo_token:
|
9
|
+
secure: EEQ24se1YhmTSlpHI47yrlAQi1s36WOj9Sk9WDecpKjZFYJ8gSyS0nVhUAPi4rG84KyJARUd0uGNHBOhJ/ZH2YN3Ge4kxzJZEbSKQ9F65MW0VZelcL5DtIsdEoiuZ6BZ1fGM9RYnPnk4jwHn8JeZ/XyVPmSvhmShmwwcWNjY5hqGw2KWHqOvgiw9fWNn/9r1iq+pUQAjqrsjjeih5fC5nrh570mt1sY51ogFbnfI6vO5JpqLcK6v1PO4gejYharfE4zqMiBwWf4HWduyQNUVC/4pKa8ohwh8yQoInieLcweLosOTRPulBuE9Hu3EXSv4JRbNhBtREjRva8YfG7epSlQddlyc5LITwZgpw2nLDLxkGjXH0JZ9g02y5uorCiwcU3amVtrxusqpYmq6zl7o6LxNnPhCbWrMdRveXmTPpS/rL5G8DAKS7/VRylXvy4nrPbbpvwrNicyRJeFrgXREA0KXaYAOTGL4C4yrhY0LPxjbwGzL6goD2U6dxja0SDovyWPbiXIlDxPX4tn09LxdzPMdHBqfFb/oqJiLrn0vp7kn736VrvhMfU3BweLEjlCXbSmLy+h5l8d118EpZOs/BnOdqjqtAb5eztiyBHZaaJG1H6cAxCST40yCx7nhAeEti5Zt/EVyBbUyZFcJiNbvQv5jbbaX7LA4nI4Y4pmduQA=
|
data/Gemfile
ADDED
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
= acts_as_tokenizable
|
2
2
|
|
3
|
-
|
3
|
+
Add this gem to enable a simple analizer on fields suitable for searching.
|
4
|
+
|
5
|
+
{<img src="https://travis-ci.org/fjuan/acts_as_tokenizable.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/fjuan/acts_as_tokenizable]
|
6
|
+
{<img src="https://codeclimate.com/github/fjuan/acts_as_tokenizable/badges/gpa.svg" />}[https://codeclimate.com/github/fjuan/acts_as_tokenizable]
|
7
|
+
{<img src="https://gemnasium.com/badges/github.com/fjuan/acts_as_tokenizable.svg" alt="Dependency Status" />}[https://gemnasium.com/github.com/fjuan/acts_as_tokenizable]
|
8
|
+
{<img src="https://coveralls.io/repos/github/fjuan/acts_as_tokenizable/badge.svg?branch=master" alt="Coverage Status" />}[https://coveralls.io/github/fjuan/acts_as_tokenizable?branch=master]
|
4
9
|
|
5
10
|
== Note on Patches/Pull Requests
|
6
|
-
|
11
|
+
|
7
12
|
* Fork the project.
|
8
13
|
* Make your feature addition or bug fix.
|
9
14
|
* Add tests for it. This is important so I don't break it in a
|
data/Rakefile
CHANGED
@@ -1,52 +1,6 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'bundler/gem_tasks'
|
3
3
|
|
4
|
-
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "acts_as_tokenizable"
|
8
|
-
gem.summary = "Acts as tokenizable"
|
9
|
-
gem.description = "Make ActiveRecord models easily searchable via tokens."
|
10
|
-
gem.email = "github@splendeo.es"
|
11
|
-
gem.homepage = "http://github.com/splendeo/acts_as_tokenizable"
|
12
|
-
gem.authors = ["Enrique Garcia Cota", "Francisco de Juan"]
|
13
|
-
gem.add_dependency "babosa", "~> 0.3.7"
|
14
|
-
end
|
15
|
-
Jeweler::GemcutterTasks.new
|
16
|
-
rescue LoadError
|
17
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
-
end
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
19
5
|
|
20
|
-
|
21
|
-
Rake::TestTask.new(:test) do |test|
|
22
|
-
test.libs << 'lib' << 'test'
|
23
|
-
test.pattern = 'test/**/test_*.rb'
|
24
|
-
test.verbose = true
|
25
|
-
end
|
26
|
-
|
27
|
-
begin
|
28
|
-
require 'rcov/rcovtask'
|
29
|
-
Rcov::RcovTask.new do |test|
|
30
|
-
test.libs << 'test'
|
31
|
-
test.pattern = 'test/**/test_*.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
34
|
-
rescue LoadError
|
35
|
-
task :rcov do
|
36
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
task :test => :check_dependencies
|
41
|
-
|
42
|
-
task :default => :test
|
43
|
-
|
44
|
-
require 'rake/rdoctask'
|
45
|
-
Rake::RDocTask.new do |rdoc|
|
46
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
-
|
48
|
-
rdoc.rdoc_dir = 'rdoc'
|
49
|
-
rdoc.title = "acts_as_tokenizable #{version}"
|
50
|
-
rdoc.rdoc_files.include('README*')
|
51
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
-
end
|
6
|
+
task default: :spec
|
data/acts_as_tokenizable.gemspec
CHANGED
@@ -1,56 +1,31 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'acts_as_tokenizable/version'
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.authors = ['Enrique Garcia Cota', 'Francisco Juan']
|
8
|
+
gem.email = 'francisco.juan@gmail.com'
|
9
|
+
gem.description = 'Make ActiveRecord models easily searchable via tokens.'
|
10
|
+
gem.summary = 'Acts as tokenizable'
|
11
|
+
gem.homepage = 'https://github.com/fjuan/acts_as_tokenizable'
|
12
|
+
gem.license = 'MIT'
|
13
|
+
gem.extra_rdoc_files = ['README.rdoc']
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
"LICENSE",
|
17
|
-
"README",
|
18
|
-
"README.rdoc"
|
19
|
-
]
|
20
|
-
s.files = [
|
21
|
-
"LICENSE",
|
22
|
-
"README",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"acts_as_tokenizable.gemspec",
|
27
|
-
"init.rb",
|
28
|
-
"lib/acts_as_tokenizable.rb",
|
29
|
-
"lib/acts_as_tokenizable/acts_as_tokenizable.rb",
|
30
|
-
"lib/acts_as_tokenizable/string_utils.rb",
|
31
|
-
"lib/tasks/acts_as_tokenizable.rake",
|
32
|
-
"test/helper.rb",
|
33
|
-
"test/test_acts_as_tokenizable.rb"
|
34
|
-
]
|
35
|
-
s.homepage = %q{http://github.com/splendeo/acts_as_tokenizable}
|
36
|
-
s.require_paths = [%q{lib}]
|
37
|
-
s.rubygems_version = %q{1.8.6}
|
38
|
-
s.summary = %q{Acts as tokenizable}
|
39
|
-
s.test_files = [
|
40
|
-
"test/helper.rb",
|
41
|
-
"test/test_acts_as_tokenizable.rb"
|
42
|
-
]
|
15
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = 'acts_as_tokenizable'
|
19
|
+
gem.require_paths = ['lib']
|
20
|
+
gem.version = ActsAsTokenizable::VERSION
|
43
21
|
|
44
|
-
|
45
|
-
|
22
|
+
gem.add_runtime_dependency 'activerecord'
|
23
|
+
gem.add_runtime_dependency 'babosa'
|
46
24
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
s.add_dependency(%q<babosa>, ["~> 0.3.7"])
|
54
|
-
end
|
25
|
+
gem.add_development_dependency 'rake'
|
26
|
+
gem.add_development_dependency 'rspec'
|
27
|
+
gem.add_development_dependency 'sqlite3'
|
28
|
+
gem.add_development_dependency 'coveralls'
|
29
|
+
gem.add_development_dependency 'byebug'
|
30
|
+
gem.add_development_dependency 'pry-byebug'
|
55
31
|
end
|
56
|
-
|
data/lib/acts_as_tokenizable.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'active_record'
|
1
4
|
require 'acts_as_tokenizable/acts_as_tokenizable'
|
5
|
+
require 'acts_as_tokenizable/string_utils'
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
module ActsAsTokenizable
|
8
|
+
if defined?(Rails::Railtie)
|
9
|
+
class Railtie < Rails::Railtie
|
10
|
+
initializer 'acts_as_tokenizable.insert_into_active_record' do
|
11
|
+
ActiveSupport.on_load :active_record do
|
12
|
+
ActiveRecord::Base.send(:include, ActsAsTokenizable)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
elsif defined?(ActiveRecord)
|
17
|
+
ActiveRecord::Base.send(:include, ActsAsTokenizable)
|
8
18
|
end
|
9
19
|
end
|
@@ -1,45 +1,64 @@
|
|
1
|
-
require 'acts_as_tokenizable/string_utils'
|
2
|
-
|
3
1
|
module ActsAsTokenizable
|
4
|
-
|
5
|
-
|
6
|
-
# override for more complex token generation
|
7
|
-
def to_token
|
8
|
-
raise NoMethodError.new("You must redefine to_token in your model. Example: self.name.to_token()")
|
9
|
-
end
|
10
|
-
|
11
|
-
#makes self.<token_field_name>=self.to_token
|
12
|
-
def tokenize
|
13
|
-
self.send("#{self.class.token_field_name}=", self.to_token)
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval { extend ClassMethods }
|
14
4
|
end
|
15
5
|
|
16
6
|
module ClassMethods
|
17
7
|
attr_accessor :token_field_name
|
18
8
|
|
19
|
-
|
20
|
-
|
21
|
-
|
9
|
+
def acts_as_tokenizable(field_name = :token)
|
10
|
+
include InstanceMethods
|
11
|
+
include TokenizedBy
|
12
|
+
|
13
|
+
before_save :tokenize
|
14
|
+
|
15
|
+
self.token_field_name = field_name
|
16
|
+
end
|
17
|
+
|
18
|
+
# search_token parameter is used by tokenized_by. This function allows for
|
19
|
+
# preparation before tokenized_by function is invoked. Usually this means
|
20
|
+
# removing stop words, replacing words.
|
22
21
|
# By default it tokenizes each word and removes duplicates.
|
23
22
|
def prepare_search_token(search_token)
|
24
23
|
StringUtils.words_to_token(search_token)
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
module InstanceMethods
|
28
|
+
# default to_token method. needs to have a "name" property on the object.
|
29
|
+
# override for more complex token generation
|
30
|
+
def to_token
|
31
|
+
raise(
|
32
|
+
NoMethodError,
|
33
|
+
'You must define to_token in your model. Example: self.name.to_token()'
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# makes self.<token_field_name>=self.to_token
|
38
|
+
def tokenize
|
39
|
+
send("#{self.class.token_field_name}=", to_token)
|
40
|
+
end
|
41
|
+
|
42
|
+
def tokenize!
|
43
|
+
update_column(self.class.token_field_name, to_token)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module TokenizedBy
|
48
|
+
extend ActiveSupport::Concern
|
31
49
|
|
32
|
-
|
50
|
+
included do
|
51
|
+
scope :tokenized_by, lambda { |search_token|
|
33
52
|
search_strings = []
|
34
53
|
search_values = []
|
35
|
-
StringUtils.words(
|
36
|
-
if w[0,1] == '-'
|
54
|
+
StringUtils.words(search_token).each do |w|
|
55
|
+
if w[0, 1] == '-'
|
37
56
|
search_strings.push("#{table_name}.#{token_field_name} NOT LIKE ?")
|
38
|
-
search_values.push("%#{w[1,w.length]}%")
|
39
57
|
else
|
40
58
|
search_strings.push("#{table_name}.#{token_field_name} LIKE ?")
|
41
|
-
search_values.push("%#{w}%")
|
42
59
|
end
|
60
|
+
tokenized_word = StringUtils.to_token(w)
|
61
|
+
search_values.push("%#{tokenized_word}%")
|
43
62
|
end
|
44
63
|
where([search_strings.join(' AND '), *search_values])
|
45
64
|
}
|
@@ -1,33 +1,35 @@
|
|
1
1
|
require 'babosa' # make sure that babosa is loaded
|
2
2
|
|
3
3
|
module ActsAsTokenizable
|
4
|
-
|
5
4
|
module StringUtils
|
6
|
-
|
7
|
-
#returns true if numeric, false, otherwise
|
5
|
+
# returns true if numeric, false, otherwise
|
8
6
|
def self.numeric?(str)
|
9
|
-
true if Float(str)
|
7
|
+
true if Float(str)
|
8
|
+
rescue
|
10
9
|
false
|
11
10
|
end
|
12
11
|
|
13
|
-
#returns an array of strings containing the words on this string.
|
12
|
+
# returns an array of strings containing the words on this string. Removes
|
13
|
+
# spaces, strange chars, etc
|
14
14
|
def self.words(str)
|
15
|
-
str.
|
15
|
+
str.split(/[\s|\.|,]+/)
|
16
16
|
end
|
17
17
|
|
18
|
-
#removes certain words from a string.
|
18
|
+
# removes certain words from a string.
|
19
19
|
# As a side-effect, all word-separators are converted to the separator char
|
20
20
|
def self.remove_words(str, words_array, separator = ' ')
|
21
21
|
(words(str) - words_array).join separator
|
22
22
|
end
|
23
23
|
|
24
|
-
# replaces certain words on a string.
|
24
|
+
# replaces certain words on a string.
|
25
25
|
# As a side-effect, all word-separators are converted to the separator char
|
26
26
|
def self.replace_words(str, replacements, separator = ' ')
|
27
27
|
replaced_words = words(str)
|
28
|
-
replacements.each do |candidates,replacement|
|
28
|
+
replacements.each do |candidates, replacement|
|
29
29
|
candidates.each do |candidate|
|
30
|
-
replaced_words=replaced_words.collect
|
30
|
+
replaced_words = replaced_words.collect do |w|
|
31
|
+
w == candidate ? replacement : w
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
replaced_words.join separator
|
@@ -44,17 +46,23 @@ module ActsAsTokenizable
|
|
44
46
|
str.split(/(\d+)/).map { |v| v =~ /\d/ ? v.to_i : v }
|
45
47
|
end
|
46
48
|
|
47
|
-
#convert into something that can be used as an indexation key
|
48
|
-
def self.to_token(str, max_length=255)
|
49
|
+
# convert into something that can be used as an indexation key
|
50
|
+
def self.to_token(str, max_length = 255)
|
49
51
|
# to_slug and normalize are provided by the 'babosa' gem
|
50
|
-
|
51
|
-
str = str.
|
52
|
-
|
52
|
+
# remove all non-alphanumeric but hyphen (-)
|
53
|
+
str = str.to_slug.normalize.strip.downcase.gsub(/[\s|\.|,]+/, '')
|
54
|
+
# remove duplicates, except on pure numbers
|
55
|
+
str = str.squeeze unless numeric?(str)
|
56
|
+
str[0..(max_length - 1)]
|
53
57
|
end
|
54
58
|
|
55
|
-
#tokenizes each word individually
|
56
|
-
def self.words_to_token(str, max_length=255, separator = ' ')
|
57
|
-
words(str)
|
59
|
+
# tokenizes each word individually and joins the word with the separator
|
60
|
+
def self.words_to_token(str, max_length = 255, separator = ' ')
|
61
|
+
words(str)
|
62
|
+
.collect { |w| to_token(w) }
|
63
|
+
.uniq
|
64
|
+
.join(separator)
|
65
|
+
.slice(0, max_length)
|
58
66
|
end
|
59
67
|
end
|
60
68
|
end
|
@@ -1,54 +1,52 @@
|
|
1
1
|
namespace :tokens do
|
2
|
-
desc
|
3
|
-
task :
|
2
|
+
desc 'Generates the token for objects without tokens.'
|
3
|
+
task generate: :environment do
|
4
4
|
tokenize_models
|
5
5
|
end
|
6
6
|
|
7
|
-
desc
|
8
|
-
task :
|
7
|
+
desc 'Re-builds the token for all objects.'
|
8
|
+
task regenerate: :environment do
|
9
9
|
tokenize_models(true)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
def array_of_active_record_models
|
14
|
-
|
15
|
-
|
16
|
-
models_with_token = ActiveRecord::Base.send(:subclasses).select{|m| m.respond_to?(:tokenized_by)}
|
14
|
+
Rails.application.eager_load!
|
15
|
+
ActiveRecord::Base.descendants.select { |m| m.respond_to? :tokenized_by }
|
17
16
|
end
|
18
|
-
|
17
|
+
|
19
18
|
def tokenize_records(records)
|
20
19
|
total_count = records.size
|
21
|
-
|
20
|
+
|
22
21
|
count = 0
|
23
|
-
|
22
|
+
|
24
23
|
records.each do |record|
|
25
|
-
record.tokenize
|
26
|
-
record.save false #this saves without checking validations
|
24
|
+
record.tokenize!
|
27
25
|
count += 1
|
28
26
|
print "\r#{count}/#{total_count}"
|
29
|
-
|
27
|
+
# launch garbage collection each 1000 registers
|
28
|
+
GC.start if count % 1000 == 0
|
30
29
|
end
|
31
|
-
puts
|
30
|
+
puts ''
|
32
31
|
end
|
33
|
-
|
32
|
+
|
34
33
|
def tokenize_models(regenerate = false)
|
35
|
-
start = Time.
|
36
|
-
puts
|
37
|
-
puts
|
38
|
-
|
34
|
+
start = Time.current
|
35
|
+
puts 'Start token generation'
|
36
|
+
puts '++++++++++++++++++++++++++++++++'
|
37
|
+
|
39
38
|
array_of_active_record_models.each do |model|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if records_without_token.
|
39
|
+
puts "Generating new tokens for #{model.name.pluralize}"
|
40
|
+
field_name = model.token_field_name
|
41
|
+
sql_conds = "#{field_name} IS NULL OR #{field_name} = ''" unless regenerate
|
42
|
+
|
43
|
+
records_without_token = model.all(conditions: sql_conds)
|
44
|
+
if !records_without_token.empty?
|
46
45
|
tokenize_records(records_without_token)
|
47
46
|
else
|
48
|
-
puts
|
49
|
-
puts
|
47
|
+
puts 'There are no records without token'
|
48
|
+
puts '++++++++++++++++++++++++++++++++'
|
50
49
|
end
|
51
50
|
end
|
52
|
-
puts "Elapsed time
|
53
|
-
|
51
|
+
puts "Elapsed time #{(Time.current - start).seconds} seconds"
|
54
52
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support_helper'
|
3
|
+
|
4
|
+
describe ActiveRecord do
|
5
|
+
describe 'classes with acts_as_tokenizable' do
|
6
|
+
it 'respond to `tokenized_by`' do
|
7
|
+
expect(Friend.methods).to include(:tokenized_by)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'raises an error if `to_token` method is not defined' do
|
11
|
+
expect { Enemy.to_token }.to raise_error(NoMethodError)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'instances' do
|
15
|
+
it 'include to_token and tokenize methods' do
|
16
|
+
expect(Friend.instance_methods)
|
17
|
+
.to include(:tokenize, :tokenize!, :to_token)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'update the token field after creating an object' do
|
21
|
+
friend = Friend.new name: 'John', email: 'john@example.com', age: 30
|
22
|
+
expect(friend.token).to be_nil
|
23
|
+
friend.save
|
24
|
+
expect(friend.token).to eq('john 30')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allow different token field name' do
|
28
|
+
malmo = City.create name: 'Malmö'
|
29
|
+
expect(malmo.tokenized_name).to eq('malmo')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'scope tokenized_by' do
|
34
|
+
before do
|
35
|
+
Friend.delete_all
|
36
|
+
|
37
|
+
Friend.create name: 'Tomás', age: 31
|
38
|
+
Friend.create name: 'Rafa', age: 25
|
39
|
+
Friend.create name: 'Matthias', age: 35
|
40
|
+
Friend.create name: 'Mamá', age: 30
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'finds records with tokenized string' do
|
44
|
+
expect(Friend.tokenized_by('as').count).to eq(2)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'finds records with tokenized with number' do
|
48
|
+
expect(Friend.tokenized_by('3').count).to eq(3)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'finds records with duplicated characters' do
|
52
|
+
expect(Friend.tokenized_by('mathias').count).to eq(1)
|
53
|
+
expect(Friend.tokenized_by('matthias').count).to eq(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'excludes records with negative token' do
|
57
|
+
expect(Friend.tokenized_by('-To').count).to eq(3)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'combines tokens' do
|
61
|
+
expect(Friend.tokenized_by('-To 5').count).to eq(2)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'classes without acts_as_tokenizable' do
|
67
|
+
it 'does not include to_token and tokenize methods' do
|
68
|
+
expect(Pet.instance_methods)
|
69
|
+
.to_not include(:tokenize, :tokenize!, :to_token)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ActsAsTokenizable
|
4
|
+
describe StringUtils do
|
5
|
+
describe '.numeric?' do
|
6
|
+
it 'returns true with a float' do
|
7
|
+
expect(described_class.numeric?('1.2')).to be_truthy
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns true with an integer' do
|
11
|
+
expect(described_class.numeric?('1')).to be_truthy
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns true with a negative number' do
|
15
|
+
expect(described_class.numeric?('-11')).to be_truthy
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns false with a string' do
|
19
|
+
expect(described_class.numeric?('1a')).to be_falsy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.words' do
|
24
|
+
it 'splits a string into by word separators' do
|
25
|
+
example = 'una mamá española aterrizó en Götemborg-int, Suecia.'
|
26
|
+
|
27
|
+
expect(described_class.words(example))
|
28
|
+
.to match_array %w(una mamá española aterrizó en Götemborg-int Suecia)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '.remove_words' do
|
33
|
+
it 'returns a string without the specified list of words' do
|
34
|
+
sentence = 'a b c d e f g'
|
35
|
+
words_to_remove = %w(c e f)
|
36
|
+
expect(described_class.remove_words(sentence, words_to_remove, ' '))
|
37
|
+
.to eq('a b d g')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'remove multiples occurences of a word' do
|
41
|
+
sentence = 'a b a d a f g'
|
42
|
+
words_to_remove = %w(a f)
|
43
|
+
expect(described_class.remove_words(sentence, words_to_remove, ' '))
|
44
|
+
.to eq('b d g')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.to_token' do
|
49
|
+
it 'transforms tildes and letter modifications' do
|
50
|
+
examples = {
|
51
|
+
'mamá' => 'mama',
|
52
|
+
'éxtasis' => 'extasis',
|
53
|
+
'maría' => 'maria',
|
54
|
+
'camión' => 'camion',
|
55
|
+
'Úrsula' => 'ursula',
|
56
|
+
'Umeå' => 'umea',
|
57
|
+
'Gävle' => 'gavle',
|
58
|
+
'Malmö' => 'malmo',
|
59
|
+
'terraza' => 'teraza',
|
60
|
+
'España' => 'espana'
|
61
|
+
}
|
62
|
+
|
63
|
+
examples.each do |string, expected_token|
|
64
|
+
expect(described_class.to_token(string)).to eq(expected_token)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'removes duplicate characters' do
|
69
|
+
expect(described_class.to_token('terraza')).to eq('teraza')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '.words_to_token' do
|
74
|
+
it 'converts a string into something that can be used as an index key' do
|
75
|
+
example = 'una mamá española aterrizó en Götemborg-int, Suecia.'
|
76
|
+
|
77
|
+
expect(described_class.words_to_token(example))
|
78
|
+
.to eq('una mama espanola aterizo en gotemborg-int suecia')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Pet < ActiveRecord::Base
|
2
|
+
end
|
3
|
+
|
4
|
+
class Enemy < ActiveRecord::Base
|
5
|
+
acts_as_tokenizable :token
|
6
|
+
end
|
7
|
+
|
8
|
+
class Friend < ActiveRecord::Base
|
9
|
+
acts_as_tokenizable :token
|
10
|
+
|
11
|
+
def to_token
|
12
|
+
string_to_token = [name, age].join(' ')
|
13
|
+
ActsAsTokenizable::StringUtils.words_to_token(string_to_token)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class City < ActiveRecord::Base
|
18
|
+
acts_as_tokenizable :tokenized_name
|
19
|
+
|
20
|
+
def to_token
|
21
|
+
ActsAsTokenizable::StringUtils.words_to_token(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Base.establish_connection(
|
26
|
+
adapter: 'sqlite3',
|
27
|
+
database: ':memory:'
|
28
|
+
)
|
29
|
+
|
30
|
+
ActiveRecord::Base.logger
|
31
|
+
ActiveRecord::Schema.define do
|
32
|
+
self.verbose = false
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveRecord::Schema.define(version: 1) do
|
36
|
+
create_table :pets do |t|
|
37
|
+
t.column :name, :string
|
38
|
+
t.column :owner, :string
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
ActiveRecord::Schema.define(version: 2) do
|
43
|
+
create_table :friends do |t|
|
44
|
+
t.column :name, :string
|
45
|
+
t.column :email, :string
|
46
|
+
t.column :age, :integer
|
47
|
+
t.column :token, :text
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ActiveRecord::Schema.define(version: 3) do
|
52
|
+
create_table :cities do |t|
|
53
|
+
t.column :name, :string
|
54
|
+
t.column :tokenized_name, :text
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,74 +1,183 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_tokenizable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.9.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Enrique Garcia Cota
|
9
|
-
- Francisco
|
8
|
+
- Francisco Juan
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2016-06-26 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
15
28
|
- !ruby/object:Gem::Dependency
|
16
29
|
name: babosa
|
17
|
-
requirement:
|
18
|
-
none: false
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
19
31
|
requirements:
|
20
|
-
- -
|
32
|
+
- - ">="
|
21
33
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0
|
34
|
+
version: '0'
|
23
35
|
type: :runtime
|
24
36
|
prerelease: false
|
25
|
-
version_requirements:
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: sqlite3
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: coveralls
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: byebug
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: pry-byebug
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
26
126
|
description: Make ActiveRecord models easily searchable via tokens.
|
27
|
-
email:
|
127
|
+
email: francisco.juan@gmail.com
|
28
128
|
executables: []
|
29
129
|
extensions: []
|
30
130
|
extra_rdoc_files:
|
31
|
-
- LICENSE
|
32
|
-
- README
|
33
131
|
- README.rdoc
|
34
132
|
files:
|
133
|
+
- ".coveralls.yml"
|
134
|
+
- ".gitignore"
|
135
|
+
- ".hound.yml"
|
136
|
+
- ".rspec"
|
137
|
+
- ".rubocop.yml"
|
138
|
+
- ".rubocop_todo.yml"
|
139
|
+
- ".ruby-style.yml"
|
140
|
+
- ".travis.yml"
|
141
|
+
- Gemfile
|
35
142
|
- LICENSE
|
36
|
-
- README
|
37
143
|
- README.rdoc
|
38
144
|
- Rakefile
|
39
|
-
- VERSION
|
40
145
|
- acts_as_tokenizable.gemspec
|
41
|
-
- init.rb
|
42
146
|
- lib/acts_as_tokenizable.rb
|
43
147
|
- lib/acts_as_tokenizable/acts_as_tokenizable.rb
|
44
148
|
- lib/acts_as_tokenizable/string_utils.rb
|
149
|
+
- lib/acts_as_tokenizable/version.rb
|
45
150
|
- lib/tasks/acts_as_tokenizable.rake
|
46
|
-
-
|
47
|
-
-
|
48
|
-
|
49
|
-
|
151
|
+
- spec/acts_as_tokenizable_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
- spec/string_utils_spec.rb
|
154
|
+
- spec/support_helper.rb
|
155
|
+
homepage: https://github.com/fjuan/acts_as_tokenizable
|
156
|
+
licenses:
|
157
|
+
- MIT
|
158
|
+
metadata: {}
|
50
159
|
post_install_message:
|
51
160
|
rdoc_options: []
|
52
161
|
require_paths:
|
53
162
|
- lib
|
54
163
|
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
164
|
requirements:
|
57
|
-
- -
|
165
|
+
- - ">="
|
58
166
|
- !ruby/object:Gem::Version
|
59
167
|
version: '0'
|
60
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
-
none: false
|
62
169
|
requirements:
|
63
|
-
- -
|
170
|
+
- - ">="
|
64
171
|
- !ruby/object:Gem::Version
|
65
172
|
version: '0'
|
66
173
|
requirements: []
|
67
174
|
rubyforge_project:
|
68
|
-
rubygems_version:
|
175
|
+
rubygems_version: 2.6.4
|
69
176
|
signing_key:
|
70
|
-
specification_version:
|
177
|
+
specification_version: 4
|
71
178
|
summary: Acts as tokenizable
|
72
179
|
test_files:
|
73
|
-
-
|
74
|
-
-
|
180
|
+
- spec/acts_as_tokenizable_spec.rb
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
- spec/string_utils_spec.rb
|
183
|
+
- spec/support_helper.rb
|
data/README
DELETED
File without changes
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.7.0
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'acts_as_tokenizable'
|
data/test/helper.rb
DELETED