Skip to content

Commit

Permalink
Merge 9d1e270 into 9aa94ce
Browse files Browse the repository at this point in the history
  • Loading branch information
fjuan committed Jun 26, 2016
2 parents 9aa94ce + 9d1e270 commit b12a212
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 54 deletions.
18 changes: 17 additions & 1 deletion lib/acts_as_tokenizable.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
$LOAD_PATH << File.dirname(__FILE__)

require 'acts_as_tokenizable/base'
require 'active_record'
require 'acts_as_tokenizable/acts_as_tokenizable'
require 'acts_as_tokenizable/string_utils'

module ActsAsTokenizable
if defined?(Rails::Railtie)
class Railtie < Rails::Railtie
initializer 'acts_as_tokenizable.insert_into_active_record' do
ActiveSupport.on_load :active_record do
ActiveRecord::Base.send(:include, ActsAsTokenizable)
end
end
end
elsif defined?(ActiveRecord)
ActiveRecord::Base.send(:include, ActsAsTokenizable)
end
end
63 changes: 38 additions & 25 deletions lib/acts_as_tokenizable/acts_as_tokenizable.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
require 'acts_as_tokenizable/string_utils'

module ActsAsTokenizable
# default to_token method. needs to have a "name" property on the object.
# override for more complex token generation
def to_token
raise(
NoMethodError,
'You must define to_token in your model. Example: self.name.to_token()'
)
end

# makes self.<token_field_name>=self.to_token
def tokenize
send("#{self.class.token_field_name}=", to_token)
end

def tokenize!
update_column(self.class.token_field_name, to_token)
def self.included(base)
base.class_eval { extend ClassMethods }
end

module ClassMethods
attr_accessor :token_field_name

def acts_as_tokenizable(field_name = :token)
include InstanceMethods
include TokenizedBy

before_save :tokenize

self.token_field_name = field_name
end

# search_token parameter is used by tokenized_by. This function allows for
# preparation before tokenized_by function is invoked. Usually this means
# removing stop words, replacing words.
Expand All @@ -31,21 +24,41 @@ def prepare_search_token(search_token)
end
end

def self.included(base)
base.class_eval do
extend ClassMethods
module InstanceMethods
# default to_token method. needs to have a "name" property on the object.
# override for more complex token generation
def to_token
raise(
NoMethodError,
'You must define to_token in your model. Example: self.name.to_token()'
)
end

# makes self.<token_field_name>=self.to_token
def tokenize
send("#{self.class.token_field_name}=", to_token)
end

def tokenize!
update_column(self.class.token_field_name, to_token)
end
end

module TokenizedBy
extend ActiveSupport::Concern

scope :tokenized_by, lambda {|search_token|
included do
scope :tokenized_by, lambda { |search_token|
search_strings = []
search_values = []
StringUtils.words(prepare_search_token(search_token)).each do |w|
StringUtils.words(search_token).each do |w|
if w[0, 1] == '-'
search_strings.push("#{table_name}.#{token_field_name} NOT LIKE ?")
search_values.push("%#{w[1, w.length]}%")
else
search_strings.push("#{table_name}.#{token_field_name} LIKE ?")
search_values.push("%#{w}%")
end
tokenized_word = StringUtils.to_token(w)
search_values.push("%#{tokenized_word}%")
end
where([search_strings.join(' AND '), *search_values])
}
Expand Down
12 changes: 0 additions & 12 deletions lib/acts_as_tokenizable/base.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/acts_as_tokenizable/string_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def self.alphanumerics(str)
def self.to_token(str, max_length = 255)
# to_slug and normalize are provided by the 'babosa' gem
# remove all non-alphanumeric but hyphen (-)
str = str.to_slug.normalize.strip.downcase.gsub(/[^\w|-]/, '')
str = str.to_slug.normalize.strip.downcase.gsub(/[\s|\.|,]+/, '')
# remove duplicates, except on pure numbers
str = str.squeeze unless numeric?(str)
str[0..(max_length - 1)]
Expand Down
2 changes: 1 addition & 1 deletion lib/acts_as_tokenizable/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ActsAsTokenizable
VERSION = '0.8.0'.freeze
VERSION = '0.9.0'.freeze
end
64 changes: 51 additions & 13 deletions spec/acts_as_tokenizable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,63 @@

describe ActiveRecord do
describe 'classes with acts_as_tokenizable' do
it 'does include to_token and tokenize methods' do
expect(Friend.instance_methods)
.to include(:tokenize, :tokenize!, :to_token)
it 'respond to `tokenized_by`' do
expect(Friend.methods).to include(:tokenized_by)
end

it 'updates the token field after creating an object' do
friend = Friend.new name: 'John', email: 'john@example.com'
expect(friend.token).to be_nil
friend.save
expect(friend.token).to eq('john')
it 'raises an error if `to_token` method is not defined' do
expect { Enemy.to_token }.to raise_error(NoMethodError)
end

it 'allows different token field name' do
malmo = City.create name: 'Malmö'
expect(malmo.tokenized_name).to eq('malmo')
describe 'instances' do
it 'include to_token and tokenize methods' do
expect(Friend.instance_methods)
.to include(:tokenize, :tokenize!, :to_token)
end

it 'update the token field after creating an object' do
friend = Friend.new name: 'John', email: 'john@example.com', age: 30
expect(friend.token).to be_nil
friend.save
expect(friend.token).to eq('john 30')
end

it 'allow different token field name' do
malmo = City.create name: 'Malmö'
expect(malmo.tokenized_name).to eq('malmo')
end
end

it 'raises an error if `to_token` method is not defined' do
expect { Enemy.to_token }.to raise_error(NoMethodError)
describe 'scope tokenized_by' do
before do
Friend.delete_all

Friend.create name: 'Tomás', age: 31
Friend.create name: 'Rafa', age: 25
Friend.create name: 'Matthias', age: 35
Friend.create name: 'Mamá', age: 30
end

it 'finds records with tokenized string' do
expect(Friend.tokenized_by('as').count).to eq(2)
end

it 'finds records with tokenized with number' do
expect(Friend.tokenized_by('3').count).to eq(3)
end

it 'finds records with duplicated characters' do
expect(Friend.tokenized_by('mathias').count).to eq(1)
expect(Friend.tokenized_by('matthias').count).to eq(1)
end

it 'excludes records with negative token' do
expect(Friend.tokenized_by('-To').count).to eq(3)
end

it 'combines tokens' do
expect(Friend.tokenized_by('-To 5').count).to eq(2)
end
end
end

Expand Down
3 changes: 2 additions & 1 deletion spec/support_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class Friend < ActiveRecord::Base
acts_as_tokenizable :token

def to_token
ActsAsTokenizable::StringUtils.words_to_token(name)
string_to_token = [name, age].join(' ')
ActsAsTokenizable::StringUtils.words_to_token(string_to_token)
end
end

Expand Down

0 comments on commit b12a212

Please sign in to comment.