minidusen 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.ruby-version +2 -0
- data/.travis.yml +30 -0
- data/LICENSE +22 -0
- data/README.md +209 -0
- data/Rakefile +42 -0
- data/doc/filtered_index_view.cropped.png +0 -0
- data/gemfiles/Gemfile.3.2.mysql2 +10 -0
- data/gemfiles/Gemfile.3.2.mysql2.lock +59 -0
- data/gemfiles/Gemfile.4.2.mysql2 +9 -0
- data/gemfiles/Gemfile.4.2.mysql2.lock +63 -0
- data/gemfiles/Gemfile.4.2.pg +9 -0
- data/gemfiles/Gemfile.4.2.pg.lock +63 -0
- data/gemfiles/Gemfile.5.0.mysql2 +9 -0
- data/gemfiles/Gemfile.5.0.mysql2.lock +60 -0
- data/gemfiles/Gemfile.5.0.pg +9 -0
- data/gemfiles/Gemfile.5.0.pg.lock +60 -0
- data/lib/minidusen.rb +12 -0
- data/lib/minidusen/active_record_ext.rb +39 -0
- data/lib/minidusen/filter.rb +31 -0
- data/lib/minidusen/parser.rb +35 -0
- data/lib/minidusen/query.rb +54 -0
- data/lib/minidusen/syntax.rb +54 -0
- data/lib/minidusen/token.rb +25 -0
- data/lib/minidusen/util.rb +55 -0
- data/lib/minidusen/version.rb +3 -0
- data/minidusen.gemspec +23 -0
- data/spec/minidusen/active_record_ext_spec.rb +45 -0
- data/spec/minidusen/filter_spec.rb +196 -0
- data/spec/minidusen/parser_spec.rb +22 -0
- data/spec/minidusen/query_spec.rb +18 -0
- data/spec/minidusen/util_spec.rb +3 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/database.rb +50 -0
- data/spec/support/database.sample.yml +10 -0
- data/spec/support/database.travis.yml +9 -0
- data/spec/support/models.rb +81 -0
- metadata +123 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Minidusen
|
2
|
+
class Token
|
3
|
+
|
4
|
+
attr_reader :field, :value, :exclude
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@value = options.fetch(:value)
|
8
|
+
@exclude = options.fetch(:exclude)
|
9
|
+
@field = options.fetch(:field).to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
value
|
14
|
+
end
|
15
|
+
|
16
|
+
def text?
|
17
|
+
field == 'text'
|
18
|
+
end
|
19
|
+
|
20
|
+
def exclude?
|
21
|
+
exclude
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Minidusen
|
2
|
+
module Util
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def postgres?(scope)
|
6
|
+
adapter_name = scope.connection.class.name
|
7
|
+
adapter_name =~ /postgres/i
|
8
|
+
end
|
9
|
+
|
10
|
+
def like_expression(phrase)
|
11
|
+
"%#{escape_for_like_query(phrase)}%"
|
12
|
+
end
|
13
|
+
|
14
|
+
def ilike_operator(scope)
|
15
|
+
if postgres?(scope)
|
16
|
+
'ILIKE'
|
17
|
+
else
|
18
|
+
'LIKE'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def regexp_operator(scope)
|
23
|
+
if postgres?(scope)
|
24
|
+
'~'
|
25
|
+
else
|
26
|
+
'REGEXP'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def escape_with_backslash(phrase, characters)
|
31
|
+
characters << '\\'
|
32
|
+
pattern = /[#{characters.collect(&Regexp.method(:quote)).join('')}]/
|
33
|
+
# debugger
|
34
|
+
phrase.gsub(pattern) do |match|
|
35
|
+
"\\#{match}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def escape_for_like_query(phrase)
|
40
|
+
# phrase.gsub("%", "\\%").gsub("_", "\\_")
|
41
|
+
escape_with_backslash(phrase, ['%', '_'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def qualify_column_name(model, column_name)
|
45
|
+
column_name = column_name.to_s
|
46
|
+
unless column_name.include?('.')
|
47
|
+
quoted_table_name = model.connection.quote_table_name(model.table_name)
|
48
|
+
quoted_column_name = model.connection.quote_column_name(column_name)
|
49
|
+
column_name = "#{quoted_table_name}.#{quoted_column_name}"
|
50
|
+
end
|
51
|
+
column_name
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/minidusen.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "minidusen/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'minidusen'
|
6
|
+
s.version = Minidusen::VERSION
|
7
|
+
s.authors = ["Henning Koch"]
|
8
|
+
s.email = 'henning.koch@makandra.de'
|
9
|
+
s.homepage = 'https://github.com/makandra/minidusen'
|
10
|
+
s.summary = 'Low-tech search for ActiveRecord with MySQL or PostgreSQL'
|
11
|
+
s.description = s.summary
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n").reject { |path| File.lstat(path).symlink? }
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").reject { |path| File.lstat(path).symlink? }
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency('activesupport', '>=3.2')
|
20
|
+
s.add_dependency('activerecord', '>=3.2')
|
21
|
+
s.add_dependency('edge_rider', '>=0.2.5')
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
describe ActiveRecord::Base do
|
2
|
+
|
3
|
+
describe '.where_like' do
|
4
|
+
|
5
|
+
it 'matches a record if a word appears in any of the given columns' do
|
6
|
+
match1 = User.create!(:name => 'word', :city => 'XXXX')
|
7
|
+
match2 = User.create!(:name => 'XXXX', :city => 'word')
|
8
|
+
no_match = User.create!(:name => 'XXXX', :city => 'XXXX')
|
9
|
+
User.where_like([:name, :city] => 'word').to_a.should =~ [match1, match2]
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'matches a record if it contains all the given words' do
|
13
|
+
match1 = User.create!(:city => 'word1 word2')
|
14
|
+
match2 = User.create!(:city => 'word2 word1')
|
15
|
+
no_match = User.create!(:city => 'word1')
|
16
|
+
User.where_like(:city => ['word1', 'word2']).to_a.should =~ [match1, match2]
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'with :negate option' do
|
20
|
+
|
21
|
+
it 'rejects a record if a word appears in any of the given columns' do
|
22
|
+
no_match1 = User.create!(:name => 'word', :city => 'XXXX')
|
23
|
+
no_match2 = User.create!(:name => 'XXXX', :city => 'word')
|
24
|
+
match = User.create!(:name => 'XXXX', :city => 'XXXX')
|
25
|
+
User.where_like({ [:name, :city] => 'word' }, :negate => true).to_a.should =~ [match]
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'rejects a record if it matches at least one of the given words' do
|
29
|
+
no_match1 = User.create!(:city => 'word1')
|
30
|
+
no_match2 = User.create!(:city => 'word2')
|
31
|
+
match = User.create!(:city => 'word3')
|
32
|
+
User.where_like({ :city => ['word1', 'word2'] }, :negate => true).to_a.should =~ [match]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "doesn't match NULL values" do
|
36
|
+
no_match = User.create!(:city => nil)
|
37
|
+
match = User.create!(:city => 'word3')
|
38
|
+
User.where_like({ :city => ['word1'] }, :negate => true).to_a.should =~ [match]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
describe Minidusen::Filter do
|
2
|
+
|
3
|
+
let :user_filter do
|
4
|
+
UserFilter.new
|
5
|
+
end
|
6
|
+
|
7
|
+
let :recipe_filter do
|
8
|
+
RecipeFilter.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#filter' do
|
12
|
+
|
13
|
+
it 'should find records by given words' do
|
14
|
+
match = User.create!(:name => 'Abraham')
|
15
|
+
no_match = User.create!(:name => 'Elizabath')
|
16
|
+
user_filter.filter(User, 'Abraham').to_a.should == [match]
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should make a case-insensitive search' do
|
20
|
+
match = User.create!(:name => 'Abraham')
|
21
|
+
no_match = User.create!(:name => 'Elizabath')
|
22
|
+
user_filter.filter(User, 'aBrAhAm').to_a.should == [match]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not find stale text after fields were updated (bugfix)' do
|
26
|
+
match = User.create!(:name => 'Abraham')
|
27
|
+
no_match = User.create!(:name => 'Elizabath')
|
28
|
+
match.update_attributes!(:name => 'Johnny')
|
29
|
+
user_filter.filter(User, 'Abraham').to_a.should be_empty
|
30
|
+
user_filter.filter(User, 'Johnny').to_a.should == [match]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should AND multiple words' do
|
34
|
+
match = User.create!(:name => 'Abraham Lincoln')
|
35
|
+
no_match = User.create!(:name => 'Abraham')
|
36
|
+
user_filter.filter(User, 'Abraham Lincoln').to_a.should == [match]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should find records by phrases' do
|
40
|
+
match = User.create!(:name => 'Abraham Lincoln')
|
41
|
+
no_match = User.create!(:name => 'Abraham John Lincoln')
|
42
|
+
user_filter.filter(User, '"Abraham Lincoln"').to_a.should == [match]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should find records by qualified fields' do
|
46
|
+
match = User.create!(:name => 'foo@bar.com', :email => 'foo@bar.com')
|
47
|
+
no_match = User.create!(:name => 'foo@bar.com', :email => 'bam@baz.com')
|
48
|
+
user_filter.filter(User, 'email:foo@bar.com').to_a.should == [match]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should find no records if a nonexistent qualifier is used' do
|
52
|
+
User.create!(:name => 'someuser', :email => 'foo@bar.com')
|
53
|
+
user_filter.filter(User, 'nonexistent_qualifier:someuser email:foo@bar.com').to_a.should == []
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should allow phrases as values for qualified field queries' do
|
57
|
+
match = User.create!(:name => 'Foo Bar', :city => 'Foo Bar')
|
58
|
+
no_match = User.create!(:name => 'Foo Bar', :city => 'Bar Foo')
|
59
|
+
user_filter.filter(User, 'city:"Foo Bar"').to_a.should == [match]
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should allow to mix multiple types of tokens in a single query' do
|
63
|
+
match = User.create!(:name => 'Abraham', :city => 'Foohausen')
|
64
|
+
no_match = User.create!(:name => 'Abraham', :city => 'Barhausen')
|
65
|
+
user_filter.filter(User, 'Foo city:Foohausen').to_a.should == [match]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should not find records from another model' do
|
69
|
+
match = User.create!(:name => 'Abraham')
|
70
|
+
Recipe.create!(:name => 'Abraham')
|
71
|
+
user_filter.filter(User, 'Abraham').to_a.should == [match]
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should find words where one letter is separated from other letters by a period' do
|
75
|
+
match = User.create!(:name => 'E.ONNNEN')
|
76
|
+
user_filter.filter(User, 'E.ONNNEN').to_a.should == [match]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should find words where one letter is separated from other letters by a semicolon' do
|
80
|
+
match = User.create!(:name => 'E;ONNNEN')
|
81
|
+
user_filter.filter(User, 'E;ONNNEN')
|
82
|
+
user_filter.filter(User, 'E;ONNNEN').to_a.should == [match]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should distinguish between "Baden" and "Baden-Baden" (bugfix)' do
|
86
|
+
match = User.create!(:city => 'Baden-Baden')
|
87
|
+
no_match = User.create!(:city => 'Baden')
|
88
|
+
user_filter.filter(User, 'Baden-Baden').to_a.should == [match]
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should handle umlauts and special characters' do
|
92
|
+
match = User.create!(:city => 'púlvérìsätëûr')
|
93
|
+
user_filter.filter(User, 'púlvérìsätëûr').to_a.should == [match]
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with excludes' do
|
97
|
+
|
98
|
+
it 'should exclude words with prefix - (minus)' do
|
99
|
+
match = User.create!(:name => 'Sunny Flower')
|
100
|
+
no_match = User.create!(:name => 'Sunny Power')
|
101
|
+
no_match2 = User.create!(:name => 'Absolutly no match')
|
102
|
+
user_filter.filter(User, 'Sunny -Power').to_a.should == [match]
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should exclude phrases with prefix - (minus)' do
|
106
|
+
match = User.create!(:name => 'Buch Tastatur Schreibtisch')
|
107
|
+
no_match = User.create!(:name => 'Buch Schreibtisch Tastatur')
|
108
|
+
no_match2 = User.create!(:name => 'Absolutly no match')
|
109
|
+
user_filter.filter(User, 'Buch -"Schreibtisch Tastatur"').to_a.should == [match]
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should exclude qualified fields with prefix - (minus)' do
|
113
|
+
match = User.create!(:name => 'Abraham', :city => 'Foohausen')
|
114
|
+
no_match = User.create!(:name => 'Abraham', :city => 'Barhausen')
|
115
|
+
no_match2 = User.create!(:name => 'Absolutly no match')
|
116
|
+
user_filter.filter(User, 'Abraham -city:Barhausen').to_a.should == [match]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should work if the query only contains excluded words' do
|
120
|
+
match = User.create!(:name => 'Sunny Flower')
|
121
|
+
no_match = User.create!(:name => 'Sunny Power')
|
122
|
+
user_filter.filter(User, '-Power').to_a.should == [match]
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should work if the query only contains excluded phrases' do
|
126
|
+
match = User.create!(:name => 'Buch Tastatur Schreibtisch')
|
127
|
+
no_match = User.create!(:name => 'Buch Schreibtisch Tastatur')
|
128
|
+
user_filter.filter(User, '-"Schreibtisch Tastatur"').to_a.should == [match]
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should work if the query only contains excluded qualified fields' do
|
132
|
+
match = User.create!(:name => 'Abraham', :city => 'Foohausen')
|
133
|
+
no_match = User.create!(:name => 'Abraham', :city => 'Barhausen')
|
134
|
+
user_filter.filter(User, '-city:Barhausen').to_a.should == [match]
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'respects an existing scope chain when there are only excluded tokens (bugfix)' do
|
138
|
+
match = User.create!(:name => 'Abraham', :city => 'Foohausen')
|
139
|
+
no_match = User.create!(:name => 'Abraham', :city => 'Barhausen')
|
140
|
+
also_no_match = User.create!(:name => 'Bebraham', :city => 'Foohausen')
|
141
|
+
user_scope = User.scoped(:conditions => { :name => 'Abraham' })
|
142
|
+
user_filter.filter(user_scope, '-Barhausen').to_a.should == [match]
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should work if there are fields contained in the search that are NULL' do
|
146
|
+
match = User.create!(:name => 'Sunny Flower', :city => nil, :email => nil)
|
147
|
+
no_match = User.create!(:name => 'Sunny Power', :city => nil, :email => nil)
|
148
|
+
no_match2 = User.create!(:name => 'Absolutly no match')
|
149
|
+
user_filter.filter(User, 'Sunny -Power').to_a.should == [match]
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should work if search_by contains a join (bugfix)' do
|
153
|
+
category1 = Recipe::Category.create!(:name => 'Rice')
|
154
|
+
category2 = Recipe::Category.create!(:name => 'Barbecue')
|
155
|
+
match = Recipe.create!(:name => 'Martini Chicken', :category => category1)
|
156
|
+
no_match = Recipe.create!(:name => 'Barbecue Chicken', :category => category2)
|
157
|
+
recipe_filter.filter(Recipe, 'Chicken -category:Barbecue').to_a.should == [match]
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should work when search_by uses SQL-Regexes which need to be "and"ed together by syntax#build_exclude_scope (bugfix)' do
|
161
|
+
match = User.create!(:name => 'Sunny Flower', :city => "Flower")
|
162
|
+
no_match = User.create!(:name => 'Sunny Power', :city => "Power")
|
163
|
+
user_filter.filter(User, '-name_and_city_regex:Power').to_a.should == [match]
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'when the given query is blank' do
|
169
|
+
|
170
|
+
it 'returns all records' do
|
171
|
+
match = User.create!
|
172
|
+
user_filter.filter(User, '').scoped.to_a.should == [match]
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'respects an existing scope chain' do
|
176
|
+
match = User.create!(:name => 'Abraham')
|
177
|
+
no_match = User.create!(:name => 'Elizabath')
|
178
|
+
scope = User.scoped(:conditions => { :name => 'Abraham' })
|
179
|
+
user_filter.filter(scope, '').scoped.to_a.should == [match]
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#minidusen_syntax' do
|
187
|
+
|
188
|
+
it "should return the model's syntax definition" do
|
189
|
+
syntax = UserFilter.send(:minidusen_syntax)
|
190
|
+
syntax.should be_a(Minidusen::Syntax)
|
191
|
+
syntax.fields.keys.should =~ ['text', 'email', 'city', 'role', 'name_and_city_regex']
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
describe Minidusen::Parser do
|
2
|
+
|
3
|
+
describe '.parse' do
|
4
|
+
|
5
|
+
it 'should parse field tokens first, because they usually give maximum filtering at little cost' do
|
6
|
+
query = Minidusen::Parser.parse('word1 field1:field1-value word2 field2:field2-value')
|
7
|
+
query.collect(&:value).should == ['field1-value', 'field2-value', 'word1', 'word2']
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should not consider the dash to be a word boundary' do
|
11
|
+
query = Minidusen::Parser.parse('Baden-Baden')
|
12
|
+
query.collect(&:value).should == ['Baden-Baden']
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should parse umlauts and accents' do
|
16
|
+
query = Minidusen::Parser.parse('field:åöÙÔøüéíÁ "ÄüÊçñÆ ððÿáÒÉ" pulvérisateur pędzić')
|
17
|
+
query.collect(&:value).should == ['åöÙÔøüéíÁ', 'ÄüÊçñÆ ððÿáÒÉ', 'pulvérisateur', 'pędzić']
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
describe Minidusen::Query do
|
2
|
+
|
3
|
+
describe '#condensed' do
|
4
|
+
|
5
|
+
it 'should return a version of the query where all text tokens have been collapsed into a single token with an Array value' do
|
6
|
+
query = Minidusen::Parser.parse('field:value foo bar baz')
|
7
|
+
query.tokens.size.should == 4
|
8
|
+
condensed_query = query.condensed
|
9
|
+
condensed_query.tokens.size.should == 2
|
10
|
+
condensed_query[0].field.should == 'field'
|
11
|
+
condensed_query[0].value.should == 'value'
|
12
|
+
condensed_query[1].field.should == 'text'
|
13
|
+
condensed_query[1].value.should == ['foo', 'bar', 'baz']
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), "/../../lib" )
|
2
|
+
|
3
|
+
require 'minidusen'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
ActiveRecord::Base.default_timezone = :local
|
7
|
+
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/*.rb"].sort.each {|f| require f}
|
9
|
+
Dir["#{File.dirname(__FILE__)}/shared_examples/*.rb"].sort.each {|f| require f}
|
10
|
+
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
|
14
|
+
config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
|
15
|
+
|
16
|
+
config.around do |example|
|
17
|
+
if example.metadata.fetch(:rollback, true)
|
18
|
+
ActiveRecord::Base.transaction do
|
19
|
+
begin
|
20
|
+
example.run
|
21
|
+
ensure
|
22
|
+
raise ActiveRecord::Rollback
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
example.run
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
database_config_file = ENV['TRAVIS'] ? 'database.travis.yml' : 'database.yml'
|
4
|
+
database_config_file = File.join(File.dirname(__FILE__), database_config_file)
|
5
|
+
File.exists?(database_config_file) or raise "Missing database configuration file: #{database_config_file}"
|
6
|
+
|
7
|
+
database_config = YAML.load_file(database_config_file)
|
8
|
+
|
9
|
+
connection_config = {}
|
10
|
+
|
11
|
+
case ENV['BUNDLE_GEMFILE']
|
12
|
+
when /pg/, /postgres/
|
13
|
+
connection_config = database_config['postgresql'].merge(adapter: 'postgresql')
|
14
|
+
when /mysql2/
|
15
|
+
connection_config = database_config['mysql'].merge(adapter: 'mysql2', encoding: 'utf8')
|
16
|
+
else
|
17
|
+
raise "Unknown database type in Gemfile suffix: #{ENV['BUNDLE_GEMFILE']}"
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveRecord::Base.establish_connection(connection_config)
|
21
|
+
|
22
|
+
|
23
|
+
connection = ::ActiveRecord::Base.connection
|
24
|
+
connection.tables.each do |table|
|
25
|
+
connection.drop_table table
|
26
|
+
end
|
27
|
+
|
28
|
+
ActiveRecord::Migration.class_eval do
|
29
|
+
|
30
|
+
create_table :users do |t|
|
31
|
+
t.string :name
|
32
|
+
t.string :email
|
33
|
+
t.string :city
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table :recipes do |t|
|
37
|
+
t.string :name
|
38
|
+
t.integer :category_id
|
39
|
+
end
|
40
|
+
|
41
|
+
create_table :recipe_ingredients do |t|
|
42
|
+
t.string :name
|
43
|
+
t.integer :recipe_id
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :recipe_categories do |t|
|
47
|
+
t.string :name
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|