minidusen 0.7.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/.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
|