dusen 0.2.2 → 0.3.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.
Files changed (59) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +3 -0
  3. data/README.md +176 -47
  4. data/documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext.csv +22 -0
  5. data/documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext_vs_like.xls +0 -0
  6. data/documents/fulltext_vs_like_benchmark/all_records_and_scope/like.csv +22 -0
  7. data/documents/fulltext_vs_like_benchmark/benchmark.rb +70 -0
  8. data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext.csv +22 -0
  9. data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.png +0 -0
  10. data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.xls +0 -0
  11. data/documents/fulltext_vs_like_benchmark/exact_number_of_records/like.csv +21 -0
  12. data/dusen.gemspec +1 -1
  13. data/lib/dusen/active_record/base_ext.rb +104 -0
  14. data/lib/dusen/active_record/search_text.rb +50 -0
  15. data/lib/dusen/description.rb +5 -4
  16. data/lib/dusen/query.rb +24 -4
  17. data/lib/dusen/railtie.rb +9 -0
  18. data/lib/dusen/syntax.rb +5 -0
  19. data/lib/dusen/tasks.rb +31 -0
  20. data/lib/dusen/token.rb +4 -0
  21. data/lib/dusen/util.rb +86 -1
  22. data/lib/dusen/version.rb +1 -1
  23. data/lib/dusen.rb +7 -1
  24. data/spec/rails-2.3/Gemfile +3 -1
  25. data/spec/rails-2.3/Rakefile +1 -1
  26. data/spec/rails-2.3/app_root/config/database.yml +4 -19
  27. data/spec/rails-2.3/app_root/config/environments/{in_memory.rb → test.rb} +0 -0
  28. data/spec/rails-2.3/spec/spec_helper.rb +7 -9
  29. data/spec/rails-3.0/Gemfile +3 -1
  30. data/spec/rails-3.0/Rakefile +1 -1
  31. data/spec/rails-3.0/app_root/config/database.yml +5 -3
  32. data/spec/rails-3.0/spec/spec_helper.rb +6 -7
  33. data/spec/rails-3.2/Gemfile +2 -1
  34. data/spec/rails-3.2/Rakefile +1 -1
  35. data/spec/rails-3.2/app_root/config/database.yml +5 -3
  36. data/spec/rails-3.2/spec/spec_helper.rb +3 -7
  37. data/spec/shared/app_root/app/models/recipe/category.rb +13 -0
  38. data/spec/shared/app_root/app/models/recipe/ingredient.rb +13 -0
  39. data/spec/shared/app_root/app/models/recipe.rb +14 -0
  40. data/spec/shared/app_root/app/models/user/with_fulltext.rb +35 -0
  41. data/spec/shared/app_root/app/models/user/without_fulltext.rb +34 -0
  42. data/spec/shared/app_root/config/database.sample.yml +6 -0
  43. data/spec/shared/app_root/db/migrate/001_create_search_text.rb +19 -0
  44. data/spec/shared/app_root/db/migrate/002_create_user_variants.rb +25 -0
  45. data/spec/shared/app_root/db/migrate/003_create_recipe_models.rb +23 -0
  46. data/spec/shared/spec/dusen/active_record/base_ext_spec.rb +138 -0
  47. data/spec/shared/spec/dusen/active_record/search_text_spec.rb +23 -0
  48. data/spec/shared/spec/dusen/parser_spec.rb +14 -0
  49. data/spec/shared/spec/dusen/query_spec.rb +20 -0
  50. data/spec/shared/spec/dusen/util_spec.rb +21 -0
  51. metadata +80 -46
  52. data/lib/dusen/active_record_ext.rb +0 -35
  53. data/spec/rails-2.3/app_root/config/environments/mysql.rb +0 -0
  54. data/spec/rails-2.3/app_root/config/environments/postgresql.rb +0 -0
  55. data/spec/rails-2.3/app_root/config/environments/sqlite.rb +0 -0
  56. data/spec/rails-2.3/app_root/config/environments/sqlite3.rb +0 -0
  57. data/spec/shared/app_root/app/models/user.rb +0 -22
  58. data/spec/shared/app_root/db/migrate/001_create_users.rb +0 -17
  59. data/spec/shared/dusen/active_record_spec.rb +0 -55
@@ -1,20 +1,21 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # This is the DSL to describe a Syntax.
3
4
  module Dusen
4
5
  class Description
5
6
 
6
7
  attr_reader :syntax
7
8
 
8
- def initialize
9
- @syntax = Syntax.new
9
+ def initialize(syntax)
10
+ @syntax = syntax
10
11
  end
11
12
 
12
13
  def search_by(field, &scoper)
13
14
  @syntax.learn_field(field, &scoper)
14
15
  end
15
16
 
16
- def self.read_syntax(&dsl)
17
- description = new
17
+ def self.parse_syntax(syntax, &dsl)
18
+ description = new(syntax)
18
19
  description.instance_eval(&dsl)
19
20
  description.syntax
20
21
  end
data/lib/dusen/query.rb CHANGED
@@ -5,20 +5,40 @@ module Dusen
5
5
 
6
6
  include Enumerable
7
7
 
8
- def initialize
9
- @tokens = []
8
+ attr_reader :tokens
9
+
10
+ def initialize(initial_tokens = [])
11
+ @tokens = initial_tokens
10
12
  end
11
13
 
12
14
  def <<(token)
13
- @tokens << token
15
+ tokens << token
16
+ end
17
+
18
+ def [](index)
19
+ tokens[index]
14
20
  end
15
21
 
22
+ #def +(tokens)
23
+ # tokens.each do |token|
24
+ # self << token
25
+ # end
26
+ #end
27
+
16
28
  def to_s
17
29
  collect(&:to_s).join(" + ")
18
30
  end
19
31
 
20
32
  def each(&block)
21
- @tokens.each(&block)
33
+ tokens.each(&block)
34
+ end
35
+
36
+ def condensed
37
+ texts = tokens.select(&:text?).collect(&:value)
38
+ field_tokens = tokens.reject(&:text?)
39
+ condensed_tokens = field_tokens
40
+ condensed_tokens << Token.new(texts) if texts.present?
41
+ self.class.new(condensed_tokens)
22
42
  end
23
43
 
24
44
  end
@@ -0,0 +1,9 @@
1
+ #module Dusen
2
+ # class Railtie < Rails::Railtie
3
+ # railtie_name :dusen
4
+ #
5
+ # rake_tasks do
6
+ # load "dusen/tasks.rb"
7
+ # end
8
+ # end
9
+ #end
data/lib/dusen/syntax.rb CHANGED
@@ -19,6 +19,7 @@ module Dusen
19
19
  def search(root_scope, query)
20
20
  scope = root_scope
21
21
  query = parse(query) if query.is_a?(String)
22
+ query = query.condensed
22
23
  query.each do |token|
23
24
  scoper = @scopers[token.field] || unknown_scoper
24
25
  scope = scoper.call(scope, token.value)
@@ -26,6 +27,10 @@ module Dusen
26
27
  scope
27
28
  end
28
29
 
30
+ def fields
31
+ @scopers
32
+ end
33
+
29
34
  def parse(query)
30
35
  Parser.parse(query)
31
36
  end
@@ -0,0 +1,31 @@
1
+ #namespace :dusen do
2
+ #
3
+ # desc 'Creates a MyISAM table "search_texts" for fast Dusen searches'
4
+ # task :create_search_text do
5
+ # generator = File.exists?('script/generate') ? 'script/generate' : 'rails generate'
6
+ # output = `#{generator} migration CreateSearchText`
7
+ # output =~ %r{(db/migrate/.+?\.rb)} or raise "Could not create migration: #{output}"
8
+ # path = $1
9
+ # File.open(path, 'w') do |file|
10
+ # file.write(
11
+ #"
12
+ #class CreateSearchText < ActiveRecord::Migration
13
+ # def up
14
+ # create_table :search_texts, :options => 'ENGINE=MyISAM' do |t|
15
+ # t.integer :model_id
16
+ # t.string :model_type
17
+ # t.text :words
18
+ # end
19
+ # add_index :search_texts, [:model_type, :model_id] # for deletion
20
+ # execute 'CREATE FULLTEXT INDEX fulltext_index_body ON search_texts (words)' # for search
21
+ # end
22
+ # def down
23
+ # drop_table :search_texts
24
+ # end
25
+ #end
26
+ #"
27
+ # )
28
+ # end
29
+ # end
30
+ #
31
+ #end
data/lib/dusen/token.rb CHANGED
@@ -19,5 +19,9 @@ module Dusen
19
19
  value
20
20
  end
21
21
 
22
+ def text?
23
+ field == 'text'
24
+ end
25
+
22
26
  end
23
27
  end
data/lib/dusen/util.rb CHANGED
@@ -8,8 +8,33 @@ module Dusen
8
8
  "%#{escape_for_like_query(phrase)}%"
9
9
  end
10
10
 
11
+ def escape_with_backslash(phrase, characters)
12
+ characters << '\\'
13
+ pattern = /[#{characters.collect(&Regexp.method(:quote)).join('')}]/
14
+ # debugger
15
+ phrase.gsub(pattern) do |match|
16
+ "\\#{match}"
17
+ end
18
+ end
19
+
11
20
  def escape_for_like_query(phrase)
12
- phrase.gsub("%", "\\%").gsub("_", "\\_")
21
+ # phrase.gsub("%", "\\%").gsub("_", "\\_")
22
+ escape_with_backslash(phrase, ['%', '_'])
23
+ end
24
+
25
+ def escape_for_boolean_fulltext_query(phrase)
26
+ escape_with_backslash(phrase, ['+', '-', '<', '>', '(', ')', '~', '*', '"'])
27
+ end
28
+
29
+ def boolean_fulltext_query(phrases)
30
+ phrases.collect do |word|
31
+ escaped_word = Dusen::Util.escape_for_boolean_fulltext_query(word)
32
+ if escaped_word =~ /\s/
33
+ %{+"#{escaped_word}"} # no prefixed wildcard possible for phrases
34
+ else
35
+ %{+#{escaped_word}*}
36
+ end
37
+ end.join(' ')
13
38
  end
14
39
 
15
40
  def qualify_column_name(model, column_name)
@@ -32,5 +57,65 @@ module Dusen
32
57
  end
33
58
  end
34
59
 
60
+ def select_scope_fields(scope, fields)
61
+ if scope.respond_to?(:select)
62
+ # Rails 3
63
+ scope.select(fields)
64
+ else
65
+ # Rails 2
66
+ scope.scoped(:select => fields)
67
+ end
68
+ end
69
+
70
+ def drop_all_tables
71
+ connection = ::ActiveRecord::Base.connection
72
+ connection.tables.each do |table|
73
+ connection.drop_table table
74
+ end
75
+ end
76
+
77
+ def migrate_test_database
78
+ print "\033[30m" # dark gray text
79
+ drop_all_tables
80
+ ::ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
81
+ print "\033[0m"
82
+ end
83
+
84
+ #def scope_to_sql(scope)
85
+ # query = if scope.respond_to?(:to_sql)
86
+ # scope.to_sql
87
+ # else
88
+ # scope.construct_finder_sql({})
89
+ # end
90
+ #end
91
+
92
+ def scope_to_sql(options = {})
93
+ if Rails.version < '3'
94
+ scope.construct_finder_sql(options)
95
+ else
96
+ scope.scoped(options).to_sql
97
+ end
98
+ end
99
+
100
+ def collect_column(scope, column_name, find_options = {})
101
+ distinct = find_options.delete(:distinct)
102
+ qualified_column_name = "`#{scope.table_name}`.`#{column_name}`"
103
+ select = distinct ? "DISTINCT #{qualified_column_name}" : qualified_column_name
104
+ query = if Rails.version.to_i < 3
105
+ scope.construct_finder_sql(find_options.merge(:select => select))
106
+ else
107
+ scope.scoped(find_options.merge(:select => select)).to_sql
108
+ end
109
+ raw_values = scope.connection.select_values(query)
110
+ column = scope.columns_hash[column_name.to_s] or raise "Could not retrieve column information: #{column_name}"
111
+ raw_values.collect { |value| column.type_cast(value) }
112
+ end
113
+
114
+ #def collect_ids(scope)
115
+ # scope = select_scope_fields(scope, "`#{scope.table_name}`.`id`")
116
+ # query = scope_to_sql(scope)
117
+ # ::ActiveRecord::Base.connection.select_values(query).collect(&:to_i)
118
+ #end
119
+
35
120
  end
36
121
  end
data/lib/dusen/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Dusen
4
- VERSION = '0.2.2'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/dusen.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'dusen/version'
3
4
  require 'dusen/util'
4
5
  require 'dusen/token'
5
6
  require 'dusen/description'
@@ -8,5 +9,10 @@ require 'dusen/query'
8
9
  require 'dusen/syntax'
9
10
 
10
11
  if defined?(ActiveRecord)
11
- require 'dusen/active_record_ext'
12
+ require 'dusen/active_record/base_ext'
13
+ require 'dusen/active_record/search_text'
12
14
  end
15
+
16
+ #if defined?(Rails::Railstie)
17
+ # require 'dusen/railtie'
18
+ #end
@@ -3,8 +3,10 @@ source :rubygems
3
3
  gem 'rails', '~>2.3'
4
4
  gem 'rspec', '~>1.3'
5
5
  gem 'rspec-rails', '~>1.3'
6
- gem 'sqlite3'
6
+ gem 'mysql2', '~>0.2.0'
7
7
  gem 'ruby-debug', :platforms => :ruby_18
8
8
  gem 'test-unit', '~>1.2', :platforms => :ruby_19
9
+ gem 'database_cleaner'
10
+ gem 'andand'
9
11
 
10
12
  gem 'dusen', :path => '../..'
@@ -7,5 +7,5 @@ task :default => :spec
7
7
  desc "Run all specs for a specific rails version"
8
8
  Spec::Rake::SpecTask.new() do |t|
9
9
  t.spec_opts = ['--options', "\"spec.opts\""]
10
- t.spec_files = defined?(SPEC) ? SPEC : FileList['**/*_spec.rb', '../shared/**/*_spec.rb']
10
+ t.spec_files = defined?(SPEC) ? SPEC : FileList['**/*_spec.rb', '../shared/spec/**/*_spec.rb']
11
11
  end
@@ -1,21 +1,6 @@
1
- in_memory:
2
- adapter: sqlite3
3
- database: ":memory:"
4
- verbosity: quiet
5
- sqlite:
6
- adapter: sqlite
7
- dbfile: plugin_test.sqlite.db
8
- sqlite3:
9
- adapter: sqlite3
10
- dbfile: plugin_test.sqlite3.db
11
- postgresql:
12
- adapter: postgresql
13
- username: postgres
14
- password: postgres
15
- database: plugin_test
16
- mysql:
17
- adapter: mysql
1
+ test:
2
+ adapter: mysql2
18
3
  host: localhost
19
4
  username: root
20
- password:
21
- database: plugin_test
5
+ password: junior
6
+ database: dusen_test
@@ -2,25 +2,23 @@
2
2
 
3
3
  $: << File.join(File.dirname(__FILE__), "/../../lib" )
4
4
 
5
- # Set the default environment to sqlite3's in_memory database
6
- ENV['RAILS_ENV'] = 'in_memory'
5
+ ENV['RAILS_ENV'] = 'test'
7
6
 
8
7
  # Load the Rails environment and testing framework
9
8
  require "#{File.dirname(__FILE__)}/../app_root/config/environment"
10
9
  require 'spec/rails'
11
-
12
- # Undo changes to RAILS_ENV
13
- silence_warnings {RAILS_ENV = ENV['RAILS_ENV']}
10
+ DatabaseCleaner.strategy = :truncation
14
11
 
15
12
  # Requires supporting files with custom matchers and macros, etc in ./support/ and its subdirectories.
16
13
  Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
17
14
 
18
15
  # Run the migrations
19
- print "\033[30m" # dark gray text
20
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
21
- print "\033[0m"
16
+ Dusen::Util.migrate_test_database
22
17
 
23
18
  Spec::Runner.configure do |config|
24
- config.use_transactional_fixtures = true
19
+ config.use_transactional_fixtures = false
25
20
  config.use_instantiated_fixtures = false
21
+ config.before(:each) do
22
+ DatabaseCleaner.clean
23
+ end
26
24
  end
@@ -3,7 +3,9 @@ source :rubygems
3
3
  gem 'rails', '~>3.0.17'
4
4
  gem 'rspec'
5
5
  gem 'rspec-rails'
6
- gem 'sqlite3'
6
+ gem 'mysql2', '~>0.2.0'
7
7
  gem 'ruby-debug', :platforms => :ruby_18
8
+ gem 'database_cleaner'
9
+ gem 'andand'
8
10
 
9
11
  gem 'dusen', :path => '../..'
@@ -6,6 +6,6 @@ task :default => :spec
6
6
 
7
7
  desc "Run all specs for a specific rails version"
8
8
  RSpec::Core::RakeTask.new(:spec) do |t|
9
- t.pattern = defined?(SPEC) ? SPEC : ['**/*_spec.rb', '../shared/**/*_spec.rb']
9
+ t.pattern = defined?(SPEC) ? SPEC : ['**/*_spec.rb', '../shared/spec/**/*_spec.rb']
10
10
  t.verbose = false
11
11
  end
@@ -1,4 +1,6 @@
1
1
  test:
2
- adapter: sqlite3
3
- database: db/gem_test.db
4
- verbosity: quiet
2
+ adapter: mysql2
3
+ host: localhost
4
+ username: root
5
+ password: junior
6
+ database: dusen_test
@@ -2,22 +2,21 @@
2
2
 
3
3
  $: << File.join(File.dirname(__FILE__), "/../../lib" )
4
4
 
5
- # Set the default environment to sqlite3's in_memory database
6
5
  ENV['RAILS_ENV'] = 'test'
7
6
  ENV['RAILS_ROOT'] = 'app_root'
8
7
 
9
8
  # Load the Rails environment and testing framework
10
9
  require "#{File.dirname(__FILE__)}/../app_root/config/environment"
11
10
  require 'rspec/rails'
12
-
13
- FileUtils.rm(Dir.glob("#{Rails.root}/db/*.db"), :force => true)
11
+ DatabaseCleaner.strategy = :truncation
14
12
 
15
13
  # Run the migrations
16
- print "\033[30m" # dark gray text
17
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
18
- print "\033[0m"
14
+ Dusen::Util.migrate_test_database
19
15
 
20
16
  RSpec.configure do |config|
21
- config.use_transactional_fixtures = true
17
+ config.use_transactional_fixtures = false
22
18
  config.use_instantiated_fixtures = false
19
+ config.before(:each) do
20
+ DatabaseCleaner.clean
21
+ end
23
22
  end
@@ -3,9 +3,10 @@ source :rubygems
3
3
  gem 'rails', '~>3.2.0'
4
4
  gem 'rspec'
5
5
  gem 'rspec-rails'
6
- gem 'sqlite3'
6
+ gem 'mysql2'
7
7
  gem 'rspec_candy'
8
8
  gem 'ruby-debug', :platforms => :ruby_18
9
9
  gem 'database_cleaner'
10
+ gem 'andand'
10
11
 
11
12
  gem 'dusen', :path => '../..'
@@ -6,6 +6,6 @@ task :default => :spec
6
6
 
7
7
  desc "Run all specs for a specific rails version"
8
8
  RSpec::Core::RakeTask.new(:spec) do |t|
9
- t.pattern = defined?(SPEC) ? SPEC : ['**/*_spec.rb', '../shared/**/*_spec.rb']
9
+ t.pattern = defined?(SPEC) ? SPEC : ['**/*_spec.rb', '../shared/spec/**/*_spec.rb']
10
10
  t.verbose = false
11
11
  end
@@ -1,4 +1,6 @@
1
1
  test:
2
- adapter: sqlite3
3
- database: db/gem_test.db
4
- verbosity: quiet
2
+ adapter: mysql2
3
+ host: localhost
4
+ username: root
5
+ password: junior
6
+ database: dusen_test
@@ -5,19 +5,15 @@ $: << File.join(File.dirname(__FILE__), "/../../lib" )
5
5
  ENV['RAILS_ENV'] = 'test'
6
6
  ENV['RAILS_ROOT'] = 'app_root'
7
7
 
8
- FileUtils.rm(Dir.glob("#{ENV['RAILS_ROOT']}/db/*.db"), :force => true)
9
-
10
8
  # Load the Rails environment and testing framework
11
9
  require "#{File.dirname(__FILE__)}/../app_root/config/environment"
12
10
  require 'rspec/rails'
13
- require 'database_cleaner'
14
-
15
11
  DatabaseCleaner.strategy = :truncation
16
12
 
13
+ # Dir["#{File.dirname(__FILE__)}/../../shared/spec/shared_examples"].each {|f| require f}
14
+
17
15
  # Run the migrations
18
- print "\033[30m" # dark gray text
19
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
20
- print "\033[0m"
16
+ Dusen::Util.migrate_test_database
21
17
 
22
18
  RSpec.configure do |config|
23
19
  config.use_transactional_fixtures = false
@@ -0,0 +1,13 @@
1
+ class Recipe::Category < ActiveRecord::Base
2
+
3
+ self.table_name = 'recipe_categories'
4
+
5
+ validates_presence_of :name
6
+
7
+ has_many :recipes, :inverse_of => :category
8
+
9
+ part_of_search_text_for do
10
+ recipes
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ class Recipe::Ingredient < ActiveRecord::Base
2
+
3
+ self.table_name = 'recipe_ingredients'
4
+
5
+ validates_presence_of :name
6
+
7
+ belongs_to :recipe, :inverse_of => :ingredients
8
+
9
+ part_of_search_text_for do
10
+ recipe
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ class Recipe < ActiveRecord::Base
2
+
3
+ validates_presence_of :name
4
+
5
+ has_many :ingredients, :class_name => 'Recipe::Ingredient', :inverse_of => :recipe
6
+ belongs_to :category, :class_name => 'Recipe::Category', :inverse_of => :recipes
7
+
8
+ search_syntax
9
+
10
+ search_text do
11
+ [name, category.andand.name, ingredients.collect(&:name)]
12
+ end
13
+
14
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module User
4
+ class WithFulltext < ActiveRecord::Base
5
+
6
+ self.table_name = 'users_with_fulltext'
7
+
8
+ search_syntax do
9
+
10
+ search_by :city do |scope, city|
11
+ scope.scoped(:conditions => { :city => city })
12
+ end
13
+
14
+ search_by :email do |scope, email|
15
+ scope.scoped(:conditions => { :email => email })
16
+ end
17
+
18
+ end
19
+
20
+ search_syntax do # multiple search_syntax directives are allowed
21
+
22
+ search_by :role do |scope, role|
23
+ scope.scoped(:conditions => { :role => role })
24
+ end
25
+
26
+ end
27
+
28
+ search_text do
29
+ [name, email, city]
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ module User
4
+ class WithoutFulltext < ActiveRecord::Base
5
+
6
+ self.table_name = 'users_without_fulltext'
7
+
8
+ search_syntax do
9
+
10
+ search_by :text do |scope, text|
11
+ scope.where_like([:name, :email, :city] => text)
12
+ end
13
+
14
+ search_by :city do |scope, city|
15
+ scope.scoped(:conditions => { :city => city })
16
+ end
17
+
18
+ search_by :email do |scope, email|
19
+ scope.scoped(:conditions => { :email => email })
20
+ end
21
+
22
+ end
23
+
24
+ search_syntax do # multiple search_syntax directives are allowed
25
+
26
+ search_by :role do |scope, role|
27
+ scope.scoped(:conditions => { :role => role })
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,6 @@
1
+ test:
2
+ adapter: mysql2
3
+ host: localhost
4
+ username: root
5
+ password:
6
+ database: dusen_test
@@ -0,0 +1,19 @@
1
+ class CreateSearchText < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :search_texts, :options => 'ENGINE=MyISAM' do |t|
5
+ t.integer :source_id
6
+ t.string :source_type
7
+ t.boolean :stale
8
+ t.text :words
9
+ end
10
+ add_index :search_texts, [:source_type, :source_id] # for updates
11
+ add_index :search_texts, [:source_type, :stale] # for refreshs
12
+ execute 'CREATE FULLTEXT INDEX fulltext_index_words ON search_texts (words)'
13
+ end
14
+
15
+ def self.down
16
+ drop_table :search_texts
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ class CreateUserVariants < ActiveRecord::Migration
4
+
5
+ def self.user_tables
6
+ [:users_with_fulltext, :users_without_fulltext]
7
+ end
8
+
9
+ def self.up
10
+ user_tables.each do |user_table|
11
+ create_table user_table do |t|
12
+ t.string :name
13
+ t.string :email
14
+ t.string :city
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.down
20
+ user_tables.each do |user_table|
21
+ drop_table user_table
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,23 @@
1
+ class CreateRecipeModels < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :recipes do |t|
5
+ t.string :name
6
+ t.integer :category_id
7
+ end
8
+ create_table :recipe_ingredients do |t|
9
+ t.string :name
10
+ t.integer :recipe_id
11
+ end
12
+ create_table :recipe_categories do |t|
13
+ t.string :name
14
+ end
15
+ end
16
+
17
+ def self.down
18
+ drop_table :recipes
19
+ drop_table :recipe_ingredients
20
+ drop_table :recipe_categories
21
+ end
22
+
23
+ end