empty_eye 0.4.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.
- data/.gitignore +4 -0
- data/CHANGELOG.md +43 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +109 -0
- data/Rakefile +17 -0
- data/empty_eye.gemspec +32 -0
- data/lib/empty_eye/active_record/base.rb +133 -0
- data/lib/empty_eye/active_record/connection_adapter.rb +46 -0
- data/lib/empty_eye/active_record/schema_dumper.rb +18 -0
- data/lib/empty_eye/associations/builder/shard_has_one.rb +20 -0
- data/lib/empty_eye/associations/shard_association_scope.rb +79 -0
- data/lib/empty_eye/associations/shard_has_one_association.rb +35 -0
- data/lib/empty_eye/errors.rb +5 -0
- data/lib/empty_eye/persistence.rb +50 -0
- data/lib/empty_eye/primary_view_extension.rb +137 -0
- data/lib/empty_eye/relation.rb +85 -0
- data/lib/empty_eye/shard.rb +130 -0
- data/lib/empty_eye/shard_association_reflection.rb +28 -0
- data/lib/empty_eye/version.rb +9 -0
- data/lib/empty_eye/view_extension.rb +115 -0
- data/lib/empty_eye/view_extension_collection.rb +222 -0
- data/lib/empty_eye.rb +30 -0
- data/spec/configuration_spec.rb +35 -0
- data/spec/mti_crud_spec.rb +130 -0
- data/spec/mti_to_sti_to_mti_crud_spec.rb +160 -0
- data/spec/spec_helper.rb +120 -0
- data/spec/sti_to_mti_crud_spec.rb +138 -0
- data/spec/validation_spec.rb +84 -0
- metadata +160 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
module EmptyEye
|
2
|
+
class ViewExtensionCollection
|
3
|
+
|
4
|
+
#a collection of all the view_extensions
|
5
|
+
#these are wranglers for the shards
|
6
|
+
#uses 'array' as a proxy
|
7
|
+
#performs array methods by passing things off in method missing
|
8
|
+
|
9
|
+
def initialize(parent)
|
10
|
+
@parent = parent
|
11
|
+
@array = []
|
12
|
+
end
|
13
|
+
|
14
|
+
#the proxy object for instances
|
15
|
+
def array
|
16
|
+
@array
|
17
|
+
end
|
18
|
+
|
19
|
+
#we want to see the proxy object not the class info
|
20
|
+
def inspect
|
21
|
+
array.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
#the class to which the instance belongs
|
25
|
+
def parent
|
26
|
+
@parent
|
27
|
+
end
|
28
|
+
|
29
|
+
def descend(klass)
|
30
|
+
@parent = klass
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
#add extensions based on association from parent
|
35
|
+
def association(assoc)
|
36
|
+
new_extension = ViewExtension.new(assoc)
|
37
|
+
reject! {|extension| new_extension.name == extension.name}
|
38
|
+
push(new_extension)
|
39
|
+
new_extension
|
40
|
+
end
|
41
|
+
|
42
|
+
#add primary extension; needs a table only
|
43
|
+
def primary_table(table)
|
44
|
+
push(@primary = PrimaryViewExtension.new(table, parent))
|
45
|
+
end
|
46
|
+
|
47
|
+
#generates view sql
|
48
|
+
def create_view_sql
|
49
|
+
#determine what shard will handle what columns
|
50
|
+
map_attribute_management
|
51
|
+
#start with primary table
|
52
|
+
query = primary_arel_table
|
53
|
+
|
54
|
+
#build select clause with correct table handling the appropriate columns
|
55
|
+
arel_columns.each do |arel_column|
|
56
|
+
query = query.project(arel_column)
|
57
|
+
end
|
58
|
+
|
59
|
+
#build joins
|
60
|
+
without_primary.each do |ext|
|
61
|
+
current = ext.arel_table
|
62
|
+
key = ext.foreign_key.to_sym
|
63
|
+
if ext.type_column
|
64
|
+
query = query.join(current).on(
|
65
|
+
primary.key.eq(current[key]), ext.type_column.eq(ext.type_value)
|
66
|
+
)
|
67
|
+
else
|
68
|
+
query = query.join(current).on(
|
69
|
+
primary.key.eq(current[key])
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#we dont need to keep this data
|
75
|
+
free_arel_columns
|
76
|
+
|
77
|
+
#STI condition if needed
|
78
|
+
if primary.sti_also?
|
79
|
+
query.where(primary.type_column.eq(primary.type_value))
|
80
|
+
end
|
81
|
+
|
82
|
+
#build veiw creation statement
|
83
|
+
"CREATE VIEW #{parent.table_name} AS\n#{query.to_sql}"
|
84
|
+
end
|
85
|
+
|
86
|
+
#takes the name of extension and a hash of intended updates from master instance
|
87
|
+
#returns a subset of hash with only values the extension handles
|
88
|
+
def delegate_map(name, hash)
|
89
|
+
keys = update_mapping[name] & hash.keys
|
90
|
+
keys.inject({}) do |res, col|
|
91
|
+
res[col] = hash[col] if hash[col]
|
92
|
+
res
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#in the end this will be an array of argument arrays
|
97
|
+
#[[:validates_presence_of, :name, {}]]
|
98
|
+
#parent will call the method and associated args inheriting validations
|
99
|
+
def validations
|
100
|
+
@validations ||= []
|
101
|
+
end
|
102
|
+
|
103
|
+
#the primary extension
|
104
|
+
def primary
|
105
|
+
@primary
|
106
|
+
end
|
107
|
+
|
108
|
+
#array of shard classes
|
109
|
+
def shards
|
110
|
+
map(&:shard)
|
111
|
+
end
|
112
|
+
|
113
|
+
#this object responds to array methods
|
114
|
+
def respond_to?(m)
|
115
|
+
super || array.respond_to?(m)
|
116
|
+
end
|
117
|
+
|
118
|
+
#delegate to the array proxy when the method is missing
|
119
|
+
def method_missing(m, *args, &block)
|
120
|
+
if respond_to?(m)
|
121
|
+
array.send(m, *args, &block)
|
122
|
+
else
|
123
|
+
super
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
#all of the arel columns mapped to the right arel tables
|
130
|
+
def arel_columns
|
131
|
+
@arel_columns ||= []
|
132
|
+
end
|
133
|
+
|
134
|
+
#we dont need to keep this data
|
135
|
+
def free_arel_columns
|
136
|
+
@arel_columns = nil
|
137
|
+
end
|
138
|
+
|
139
|
+
#tracks the attributes with the view extension that will handle it
|
140
|
+
def update_mapping
|
141
|
+
@update_mapping ||= {}
|
142
|
+
end
|
143
|
+
|
144
|
+
#generate a foreign_key if it is missing
|
145
|
+
def default_foreign_key
|
146
|
+
view_name = parent.table_name.singularize
|
147
|
+
"#{view_name}_id"
|
148
|
+
end
|
149
|
+
|
150
|
+
#the primary arel table
|
151
|
+
def primary_arel_table
|
152
|
+
primary.arel_table
|
153
|
+
end
|
154
|
+
|
155
|
+
#all the tables
|
156
|
+
def tables
|
157
|
+
map(&:table)
|
158
|
+
end
|
159
|
+
|
160
|
+
#map the columns to the extension that will handle it
|
161
|
+
def map_attribute_management
|
162
|
+
#clear out what we know
|
163
|
+
arel_columns.clear
|
164
|
+
#use this to track and remove dupes
|
165
|
+
tracker = {}
|
166
|
+
each do |ext|
|
167
|
+
#mimic the parent's associations through primary shard
|
168
|
+
primary.have_one(ext)
|
169
|
+
ext.columns.each do |col|
|
170
|
+
column = col.to_sym
|
171
|
+
#skip if we already have this column
|
172
|
+
next if tracker[column]
|
173
|
+
#set to true so we wont do again
|
174
|
+
tracker[column] = true
|
175
|
+
#add the column based on the extension's arel_table
|
176
|
+
arel_columns << ext.arel_table[column]
|
177
|
+
#later we need to know how to update thing correctly
|
178
|
+
update_mapping[ext.name] = update_mapping[ext.name].to_a << col
|
179
|
+
#delegate the setter for column to shard of extension through primary shard
|
180
|
+
primary.delegate_to(column, ext) unless ext.primary
|
181
|
+
#mti class must inherit validations
|
182
|
+
add_validations(column, ext)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
#tried a cleaner solution but it wouldnt work
|
188
|
+
#here i am stealing the arguments needed from the shards
|
189
|
+
#to call the same validation on the master class (parent)
|
190
|
+
def add_validations(column, ext)
|
191
|
+
return unless ext.shard._validators[column].present?
|
192
|
+
#primary either has no validations or they have already been inherited
|
193
|
+
return if ext.primary
|
194
|
+
rtn = ext.shard._validators[column].each do |validator|
|
195
|
+
meth = case validator.class.to_s
|
196
|
+
when /presence/i then :validates_presence_of
|
197
|
+
when /acceptance/i then :validates_acceptance_of
|
198
|
+
when /numericality/i then :validates_numericality_of
|
199
|
+
when /length/i then :validates_length_of
|
200
|
+
when /inclusion/i then :validates_inclusion_of
|
201
|
+
when /format/i then :validates_format_of
|
202
|
+
when /exclusion/i then :validates_exclusion_of
|
203
|
+
when /confirmation/i then :validates_confirmation_of
|
204
|
+
when /uniqueness/i then :validates_uniqueness_of
|
205
|
+
else nil
|
206
|
+
end
|
207
|
+
if meth
|
208
|
+
args = []
|
209
|
+
args << meth
|
210
|
+
args << column
|
211
|
+
args << validator.options
|
212
|
+
validations << args
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
#return a list of extensions without primary
|
218
|
+
def without_primary
|
219
|
+
array.select {|ext| ext != primary}
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/empty_eye.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "arel"
|
3
|
+
|
4
|
+
require "empty_eye/version"
|
5
|
+
|
6
|
+
require "empty_eye/persistence"
|
7
|
+
require "empty_eye/relation"
|
8
|
+
require "empty_eye/errors"
|
9
|
+
require "empty_eye/view_extension"
|
10
|
+
require "empty_eye/primary_view_extension"
|
11
|
+
require "empty_eye/view_extension_collection"
|
12
|
+
require "empty_eye/shard"
|
13
|
+
require "empty_eye/associations/builder/shard_has_one"
|
14
|
+
require "empty_eye/associations/shard_has_one_association"
|
15
|
+
require "empty_eye/associations/shard_association_scope"
|
16
|
+
require "empty_eye/shard_association_reflection"
|
17
|
+
|
18
|
+
require "empty_eye/active_record/base"
|
19
|
+
require "empty_eye/active_record/schema_dumper"
|
20
|
+
require "empty_eye/active_record/connection_adapter"
|
21
|
+
|
22
|
+
module EmptyEye
|
23
|
+
# Your code goes here...
|
24
|
+
end
|
25
|
+
|
26
|
+
::ActiveRecord::Base.send :include, EmptyEye::Persistence
|
27
|
+
::ActiveRecord::Base.send :include, EmptyEye::Relation
|
28
|
+
::ActiveRecord::Associations::Builder::HasOne.valid_options += [:except, :only]
|
29
|
+
::ActiveRecord::Associations::Builder::BelongsTo.valid_options += [:except, :only]
|
30
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
class SmallMechanic < ActiveRecord::Base
|
5
|
+
mti_class :mechanics_core do |t|
|
6
|
+
has_one :garage, :foreign_key => :mechanic_id, :except => 'specialty'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TinyMechanic < ActiveRecord::Base
|
11
|
+
mti_class :mechanics_core do |t|
|
12
|
+
has_one :garage, :foreign_key => :mechanic_id, :only => 'specialty'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ActiveRecord::Base do
|
17
|
+
|
18
|
+
describe "MTI class configuration" do
|
19
|
+
it "should exclude columns with except option" do
|
20
|
+
mechanic_columns = Mechanic.column_names
|
21
|
+
small_mechanic_columns = SmallMechanic.column_names
|
22
|
+
delta = mechanic_columns - small_mechanic_columns
|
23
|
+
delta.should eq(["specialty"])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "MTI class configuration" do
|
28
|
+
it "should restrict columns with only option" do
|
29
|
+
garage_columns = Garage.column_names
|
30
|
+
tiny_mechanic_columns = TinyMechanic.column_names
|
31
|
+
intersection = garage_columns & tiny_mechanic_columns
|
32
|
+
intersection.should eq(["id", "specialty"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
|
5
|
+
describe ActiveRecord::Base do
|
6
|
+
before(:each) do
|
7
|
+
exec_sql "truncate bars_core"
|
8
|
+
exec_sql "truncate businesses"
|
9
|
+
|
10
|
+
@bar = Bar.create(
|
11
|
+
:music_genre => "Latin", :best_nights => "Tuesdays", :dress_code => "casual", # bar_core attributes
|
12
|
+
:address => "1904 Easy Kaley Orlando, FL 32806", :name => 'Chicos', :phone => '123456789' #business attributes
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "create" do
|
17
|
+
it "should create a mti class correctly" do
|
18
|
+
@bar.music_genre.should eq("Latin")
|
19
|
+
@bar.best_nights.should eq("Tuesdays")
|
20
|
+
@bar.dress_code.should eq("casual")
|
21
|
+
@bar.address.should eq("1904 Easy Kaley Orlando, FL 32806")
|
22
|
+
@bar.name.should eq("Chicos")
|
23
|
+
@bar.phone.should eq("123456789")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should create associations correctly" do
|
27
|
+
@bar.business.class.should eq(Business)
|
28
|
+
|
29
|
+
@bar.business.address.should eq("1904 Easy Kaley Orlando, FL 32806")
|
30
|
+
@bar.business.name.should eq("Chicos")
|
31
|
+
@bar.business.phone.should eq("123456789")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "read" do
|
36
|
+
it "should find a mti class correctly" do
|
37
|
+
@found_bar = Bar.find_by_id(@bar.id)
|
38
|
+
@bar.should eq(@found_bar)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "update" do
|
43
|
+
it "should update a mti class correctly with update_attributes" do
|
44
|
+
@bar.phone.should eq("123456789")
|
45
|
+
@bar.update_attributes(:phone => '987654321') #attribute from business
|
46
|
+
@bar.reload
|
47
|
+
@bar.phone.should eq("987654321")
|
48
|
+
@bar.business.phone.should eq("987654321")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should update a mti class correctly with assignment" do
|
52
|
+
@bar.phone.should eq("123456789")
|
53
|
+
@bar.phone = '987654321' #attribute from business
|
54
|
+
@bar.save
|
55
|
+
@bar.reload
|
56
|
+
@bar.phone.should eq("987654321")
|
57
|
+
@bar.business.phone.should eq("987654321")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should update a mti class correctly with Class.update" do
|
61
|
+
Bar.update(@bar.id, :name => 'Betos')
|
62
|
+
@bar.reload
|
63
|
+
@bar.name.should eq('Betos')
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should update a mti class correctly with Class.update_all" do
|
67
|
+
rtn = Bar.update_all(:name => 'Betos')
|
68
|
+
@bar.reload
|
69
|
+
rtn.should eq(1)
|
70
|
+
@bar.name.should eq('Betos')
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not update a mti class incorrectly with Class.update_all" do
|
74
|
+
rtn = Bar.update_all({:name => 'Betos'}, ["id = ?", @bar.id + 1]) #choose the wrong one
|
75
|
+
@bar.reload
|
76
|
+
rtn.should eq(0)
|
77
|
+
@bar.name.should eq('Chicos')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "delete" do
|
82
|
+
it "should destroy a mti class correctly" do
|
83
|
+
@business = @bar.business
|
84
|
+
@bar.destroy
|
85
|
+
@bar.destroyed?.should eq(true)
|
86
|
+
Bar.find_by_id(@bar.id).should eq(nil)
|
87
|
+
Business.find_by_id(@business.id).should eq(nil)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should destroy_all mti class correctly" do
|
91
|
+
Bar.count.should eq(1)
|
92
|
+
Business.count.should eq(1)
|
93
|
+
Bar.destroy_all
|
94
|
+
Bar.count.should eq(0)
|
95
|
+
Business.count.should eq(0)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should not destroy_all mti class incorrectly" do
|
99
|
+
Bar.count.should eq(1)
|
100
|
+
Business.count.should eq(1)
|
101
|
+
Bar.destroy_all(:id => @bar.id + 1) #choose the wrong one
|
102
|
+
Bar.count.should eq(1)
|
103
|
+
Business.count.should eq(1)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should delete a mti class correctly" do
|
107
|
+
@bar.delete
|
108
|
+
@bar.destroyed?.should eq(true)
|
109
|
+
Bar.find_by_id(@bar.id).should eq(nil)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should delete_all mti class correctly" do
|
113
|
+
Bar.count.should eq(1)
|
114
|
+
Business.count.should eq(1)
|
115
|
+
rtn = Bar.delete_all
|
116
|
+
rtn.should eq(1)
|
117
|
+
Bar.count.should eq(0)
|
118
|
+
Business.count.should eq(0)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should not delete_all mti class incorrectly" do
|
122
|
+
Bar.count.should eq(1)
|
123
|
+
Business.count.should eq(1)
|
124
|
+
rtn = Bar.delete_all(:id => @bar.id + 1) #choose the wrong one
|
125
|
+
rtn.should eq(0)
|
126
|
+
Bar.count.should eq(1)
|
127
|
+
Business.count.should eq(1)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
|
5
|
+
describe ActiveRecord::Base do
|
6
|
+
before(:each) do
|
7
|
+
exec_sql "truncate eating_venues_core"
|
8
|
+
exec_sql "truncate restaurants"
|
9
|
+
exec_sql "truncate businesses"
|
10
|
+
|
11
|
+
@venue = EatingVenue.create(
|
12
|
+
:api_venue_id => 'abcdefg', :latitude => '122.11111', :longitude => '-81,11111', # eating venue attributes
|
13
|
+
:kids_area => false, :wifi => true, :food_genre => "mexican", # restaurant attributes
|
14
|
+
:address => "1904 Easy Kaley Orlando, FL 32806", :name => 'Chicos', :phone => '123456789' #business attributes
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "create" do
|
19
|
+
it "should create a mti to sti to mti class correctly" do
|
20
|
+
@venue.api_venue_id.should eq('abcdefg')
|
21
|
+
@venue.latitude.should eq('122.11111')
|
22
|
+
@venue.longitude.should eq('-81,11111')
|
23
|
+
@venue.kids_area.should eq(false)
|
24
|
+
@venue.wifi.should eq(true)
|
25
|
+
@venue.food_genre.should eq("mexican")
|
26
|
+
@venue.address.should eq("1904 Easy Kaley Orlando, FL 32806")
|
27
|
+
@venue.name.should eq("Chicos")
|
28
|
+
@venue.phone.should eq("123456789")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should create mti to sti to mti associations correctly" do
|
32
|
+
@venue.mexican_restaurant.class.should eq(MexicanRestaurant)
|
33
|
+
|
34
|
+
@venue.mexican_restaurant.kids_area.should eq(false)
|
35
|
+
@venue.mexican_restaurant.wifi.should eq(true)
|
36
|
+
@venue.mexican_restaurant.food_genre.should eq("mexican")
|
37
|
+
@venue.mexican_restaurant.address.should eq("1904 Easy Kaley Orlando, FL 32806")
|
38
|
+
@venue.mexican_restaurant.name.should eq("Chicos")
|
39
|
+
@venue.mexican_restaurant.phone.should eq("123456789")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "read" do
|
44
|
+
it "should find a mti to sti to mti class correctly" do
|
45
|
+
@found_venue = EatingVenue.find_by_id(@venue.id)
|
46
|
+
@venue.should eq(@found_venue)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "update" do
|
51
|
+
it "should update a mti to sti to mti class correctly with update_attributes" do
|
52
|
+
@venue.phone.should eq("123456789")
|
53
|
+
@venue.wifi.should eq(true)
|
54
|
+
@venue.update_attributes(:phone => '987654321', :wifi => false) #attribute from business
|
55
|
+
@venue.reload
|
56
|
+
@venue.phone.should eq("987654321")
|
57
|
+
@venue.wifi.should eq(false)
|
58
|
+
@venue.mexican_restaurant.phone.should eq("987654321")
|
59
|
+
@venue.mexican_restaurant.wifi.should eq(false)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should update a mti to sti to mti class correctly with assignment" do
|
63
|
+
@venue.phone.should eq("123456789")
|
64
|
+
@venue.wifi.should eq(true)
|
65
|
+
@venue.wifi = false
|
66
|
+
@venue.phone = '987654321' #attribute from business
|
67
|
+
@venue.save
|
68
|
+
@venue.reload
|
69
|
+
@venue.wifi.should eq(false)
|
70
|
+
@venue.phone.should eq("987654321")
|
71
|
+
@venue.mexican_restaurant.phone.should eq("987654321")
|
72
|
+
@venue.mexican_restaurant.wifi.should eq(false)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should update a mti to sti to mti class correctly with Class.update" do
|
76
|
+
EatingVenue.update(@venue.id, :name => 'Betos', :food_genre => 'Italian')
|
77
|
+
@venue.reload
|
78
|
+
@venue.name.should eq('Betos')
|
79
|
+
@venue.food_genre.should eq('Italian')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should update a mti class correctly with Class.update_all" do
|
83
|
+
rtn = EatingVenue.update_all(:name => 'Betos', :food_genre => 'Italian')
|
84
|
+
@venue.reload
|
85
|
+
rtn.should eq(1)
|
86
|
+
@venue.name.should eq('Betos')
|
87
|
+
@venue.food_genre.should eq('Italian')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not update a mti to sti to mti class incorrectly with Class.update_all" do
|
91
|
+
rtn = EatingVenue.update_all({:name => 'Betos', :food_genre => 'Italian'}, ["id = ?", @venue.id + 1]) #choose the wrong one
|
92
|
+
@venue.reload
|
93
|
+
rtn.should eq(0)
|
94
|
+
@venue.name.should eq('Chicos')
|
95
|
+
@venue.food_genre.should eq('mexican')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "delete" do
|
100
|
+
it "should destroy a mti to sti to mti class correctly" do
|
101
|
+
@restaurant = @venue.mexican_restaurant
|
102
|
+
@venue.destroy
|
103
|
+
@venue.destroyed?.should eq(true)
|
104
|
+
EatingVenue.find_by_id(@venue.id).should eq(nil)
|
105
|
+
@restaurant.business.should eq(nil)
|
106
|
+
MexicanRestaurant.find_by_id(@restaurant.id).should eq(nil)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should destroy_all mti to sti to mti class correctly" do
|
110
|
+
EatingVenue.count.should eq(1)
|
111
|
+
Business.count.should eq(1)
|
112
|
+
MexicanRestaurant.count.should eq(1)
|
113
|
+
EatingVenue.destroy_all
|
114
|
+
EatingVenue.count.should eq(0)
|
115
|
+
Business.count.should eq(0)
|
116
|
+
MexicanRestaurant.count.should eq(0)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not destroy_all mti to sti to mti class incorrectly" do
|
120
|
+
EatingVenue.count.should eq(1)
|
121
|
+
Business.count.should eq(1)
|
122
|
+
EatingVenue.destroy_all(:id => @venue.id + 1) #choose the wrong one
|
123
|
+
EatingVenue.count.should eq(1)
|
124
|
+
Business.count.should eq(1)
|
125
|
+
MexicanRestaurant.count.should eq(1)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should delete a mti to sti to mti class correctly" do
|
129
|
+
Business.count.should eq(1)
|
130
|
+
MexicanRestaurant.count.should eq(1)
|
131
|
+
@venue.delete
|
132
|
+
@venue.destroyed?.should eq(true)
|
133
|
+
EatingVenue.find_by_id(@venue.id).should eq(nil)
|
134
|
+
Business.count.should eq(0)
|
135
|
+
MexicanRestaurant.count.should eq(0)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should delete_all mti to sti to mti class correctly" do
|
139
|
+
EatingVenue.count.should eq(1)
|
140
|
+
Business.count.should eq(1)
|
141
|
+
MexicanRestaurant.count.should eq(1)
|
142
|
+
rtn = EatingVenue.delete_all
|
143
|
+
rtn.should eq(1)
|
144
|
+
EatingVenue.count.should eq(0)
|
145
|
+
Business.count.should eq(0)
|
146
|
+
MexicanRestaurant.count.should eq(0)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should not delete_all mti to sti to mti class incorrectly" do
|
150
|
+
EatingVenue.count.should eq(1)
|
151
|
+
Business.count.should eq(1)
|
152
|
+
MexicanRestaurant.count.should eq(1)
|
153
|
+
rtn = EatingVenue.delete_all(:id => @venue.id + 1) #choose the wrong one
|
154
|
+
rtn.should eq(0)
|
155
|
+
EatingVenue.count.should eq(1)
|
156
|
+
Business.count.should eq(1)
|
157
|
+
MexicanRestaurant.count.should eq(1)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'empty_eye'
|
5
|
+
|
6
|
+
# RSpec.configure do |config|
|
7
|
+
# # some (optional) config here
|
8
|
+
# end
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
:adapter => "mysql2",
|
12
|
+
:database => "empty_eye_test"
|
13
|
+
)
|
14
|
+
|
15
|
+
def exec_sql(sql)
|
16
|
+
ActiveRecord::Base.connection.execute sql
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Migration.create_table :restaurants, :force => true do |t|
|
20
|
+
t.string :type
|
21
|
+
t.boolean :kids_area
|
22
|
+
t.boolean :wifi
|
23
|
+
t.integer :eating_venue_id
|
24
|
+
t.string :food_genre
|
25
|
+
t.datetime :created_at
|
26
|
+
t.datetime :updated_at
|
27
|
+
t.datetime :deleted_at
|
28
|
+
end
|
29
|
+
|
30
|
+
ActiveRecord::Migration.create_table :bars_core, :force => true do |t|
|
31
|
+
t.string :music_genre
|
32
|
+
t.string :best_nights
|
33
|
+
t.string :dress_code
|
34
|
+
t.datetime :created_at
|
35
|
+
t.datetime :updated_at
|
36
|
+
t.datetime :deleted_at
|
37
|
+
end
|
38
|
+
|
39
|
+
ActiveRecord::Migration.create_table :businesses, :force => true do |t|
|
40
|
+
t.integer :biz_id
|
41
|
+
t.string :biz_type
|
42
|
+
t.string :name
|
43
|
+
t.string :address
|
44
|
+
t.string :phone
|
45
|
+
end
|
46
|
+
|
47
|
+
ActiveRecord::Migration.create_table :eating_venues_core, :force => true do |t|
|
48
|
+
t.string :api_venue_id
|
49
|
+
t.string :latitude
|
50
|
+
t.string :longitude
|
51
|
+
end
|
52
|
+
|
53
|
+
ActiveRecord::Migration.create_table :eating_venues_core, :force => true do |t|
|
54
|
+
t.string :api_venue_id
|
55
|
+
t.string :latitude
|
56
|
+
t.string :longitude
|
57
|
+
end
|
58
|
+
|
59
|
+
ActiveRecord::Migration.create_table :garages, :force => true do |t|
|
60
|
+
t.boolean :privately_owned
|
61
|
+
t.integer :max_wait_days
|
62
|
+
t.string :specialty
|
63
|
+
t.string :email
|
64
|
+
t.integer :mechanic_id
|
65
|
+
end
|
66
|
+
|
67
|
+
ActiveRecord::Migration.create_table :mechanics_core, :force => true do |t|
|
68
|
+
t.string :name
|
69
|
+
end
|
70
|
+
|
71
|
+
class Business < ActiveRecord::Base
|
72
|
+
belongs_to :biz, :polymorphic => true
|
73
|
+
end
|
74
|
+
|
75
|
+
class Restaurant < ActiveRecord::Base
|
76
|
+
belongs_to :foursquare_venue
|
77
|
+
end
|
78
|
+
|
79
|
+
class MexicanRestaurant < Restaurant
|
80
|
+
mti_class do |t|
|
81
|
+
has_one :business, :as => :biz
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Bar < ActiveRecord::Base
|
86
|
+
mti_class do |t|
|
87
|
+
has_one :business, :as => :biz
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class EatingVenue < ActiveRecord::Base
|
92
|
+
mti_class do |t|
|
93
|
+
has_one :mexican_restaurant
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Garage < ActiveRecord::Base
|
98
|
+
belongs_to :mechanic, :foreign_key => :mechanic_id
|
99
|
+
|
100
|
+
validates_presence_of :privately_owned
|
101
|
+
validates_numericality_of :max_wait_days
|
102
|
+
validates_length_of :email, :minimum => 7
|
103
|
+
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
104
|
+
validates_uniqueness_of :email
|
105
|
+
validates_inclusion_of :specialty, :in => %w{foreign domestic antique something_crazy}
|
106
|
+
validates_exclusion_of :specialty, :in => %{ something_crazy }
|
107
|
+
end
|
108
|
+
|
109
|
+
class Mechanic < ActiveRecord::Base
|
110
|
+
mti_class :mechanics_core do |t|
|
111
|
+
has_one :garage, :foreign_key => :mechanic_id
|
112
|
+
end
|
113
|
+
|
114
|
+
validates_presence_of :name
|
115
|
+
validates_uniqueness_of :name
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|