dbview_cti 0.1.3 → 0.1.4
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/.travis.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile +7 -0
- data/lib/db_view_cti/connection_adapters/schema_statements.rb +5 -0
- data/lib/db_view_cti/model/collection_delegator.rb +1 -1
- data/lib/db_view_cti/model/cti.rb +59 -13
- data/lib/db_view_cti/model/model_delegator.rb +0 -17
- data/lib/db_view_cti/sql_generation/migration/base.rb +4 -0
- data/lib/db_view_cti/sql_generation/migration/postgresql.rb +4 -0
- data/lib/db_view_cti/version.rb +1 -1
- data/spec/dummy-rails-3/app/models/vehicle.rb +4 -1
- data/spec/dummy-rails-3/db/migrate/20140408013710_check_view_exists.rb +8 -0
- data/spec/models/space_shuttle_spec.rb +36 -0
- metadata +5 -4
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -34,3 +34,10 @@ group :test, :development do
|
|
34
34
|
gem "activerecord-postgresql-adapter", :platforms => [:ruby, :mswin, :mingw]
|
35
35
|
gem "activerecord-jdbcpostgresql-adapter", :platforms => [:jruby]
|
36
36
|
end
|
37
|
+
|
38
|
+
# for rubinius testing in Travis (cf. travis docs)
|
39
|
+
platforms :rbx do
|
40
|
+
gem 'racc'
|
41
|
+
gem 'rubysl', '~> 2.0'
|
42
|
+
gem 'psych'
|
43
|
+
end
|
@@ -14,6 +14,11 @@ module DBViewCTI
|
|
14
14
|
cti_execute_sql(generator.drop_view_sql)
|
15
15
|
end
|
16
16
|
|
17
|
+
def cti_view_exists?(class_name)
|
18
|
+
generator = DBViewCTI::SQLGeneration::Migration::Factory.generator(class_name)
|
19
|
+
cti_execute_sql(generator.view_exists_sql)[0]['count'].to_i > 0
|
20
|
+
end
|
21
|
+
|
17
22
|
# use with block in up/down methods
|
18
23
|
def cti_recreate_views_after_change_to(class_name, options = {})
|
19
24
|
klass = class_name.constantize
|
@@ -62,7 +62,7 @@ module DBViewCTI
|
|
62
62
|
after_save :cti_save_associations
|
63
63
|
|
64
64
|
# validations
|
65
|
-
validate :cti_validate_associations
|
65
|
+
validate :cti_validate_associations, :cti_no_disable => true
|
66
66
|
attr_accessor :cti_disable_validations
|
67
67
|
end
|
68
68
|
|
@@ -164,6 +164,31 @@ module DBViewCTI
|
|
164
164
|
result
|
165
165
|
end
|
166
166
|
|
167
|
+
# redefine validate to always add :unless proc so we can disable the validations for an object
|
168
|
+
# by setting the cti_disable_validations accessor to true
|
169
|
+
def validate(*args, &block)
|
170
|
+
# we specifically don't want to disable balidations belonging to associations. Based on the naming
|
171
|
+
# rails uses, we return immediately in such cases (there must be a cleaner way to do this...)
|
172
|
+
return super if args.first && args.first.to_s =~ /^validate_associated_records_for_/
|
173
|
+
# rest of implementation insipred by the validate implementation in rails
|
174
|
+
options = args.extract_options!.dup
|
175
|
+
return super if options[:cti_no_disable]
|
176
|
+
if options.key?(:unless)
|
177
|
+
options[:unless] = Array(options[:unless])
|
178
|
+
options[:unless].unshift( cti_validation_unless_proc )
|
179
|
+
else
|
180
|
+
options[:unless] = cti_validation_unless_proc
|
181
|
+
end
|
182
|
+
args << options
|
183
|
+
return super(*args, &block)
|
184
|
+
end
|
185
|
+
|
186
|
+
def cti_validation_unless_proc
|
187
|
+
@cti_validation_unless_proc ||= Proc.new do |object|
|
188
|
+
object.respond_to?(:cti_disable_validations) && object.cti_disable_validations
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
167
192
|
# redefine association class methods
|
168
193
|
[:has_many, :has_and_belongs_to_many, :has_one].each do |name|
|
169
194
|
self.class_eval <<-eos, __FILE__, __LINE__+1
|
@@ -196,11 +221,9 @@ module DBViewCTI
|
|
196
221
|
[:has_many, :has_one].each do |association_type|
|
197
222
|
@cti_associations[association_type].each do |association|
|
198
223
|
next if @cti_redefined_remote_associations[association_type].include?( association )
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
if remote_associations.include?( remote_association )
|
203
|
-
cti_redefine_remote_belongs_to_association(remote_class, remote_association)
|
224
|
+
if cti_reciprocal_association_present_for?( association, :belongs_to )
|
225
|
+
remote_class = cti_association_name_to_class_name( association ).constantize
|
226
|
+
cti_redefine_remote_belongs_to_association(remote_class, cti_class_name_to_association_name.to_sym)
|
204
227
|
@cti_redefined_remote_associations[association_type] << association
|
205
228
|
end
|
206
229
|
end
|
@@ -209,21 +232,44 @@ module DBViewCTI
|
|
209
232
|
[:has_many, :has_and_belongs_to_many].each do |association_type|
|
210
233
|
@cti_associations[association_type].each do |association|
|
211
234
|
next if @cti_redefined_remote_associations[association_type].include?( association )
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
if remote_associations.include?( remote_association )
|
216
|
-
cti_redefine_remote_to_many_association(remote_class, remote_association)
|
235
|
+
if cti_reciprocal_association_present_for?( association, association_type, true )
|
236
|
+
remote_class = cti_association_name_to_class_name( association ).constantize
|
237
|
+
cti_redefine_remote_to_many_association(remote_class, cti_class_name_to_association_name( true ).to_sym)
|
217
238
|
@cti_redefined_remote_associations[association_type] << association
|
218
239
|
end
|
219
240
|
end
|
220
241
|
end
|
221
242
|
end
|
243
|
+
|
244
|
+
# Check if a reciprocal association of type 'type' is present for the given association.
|
245
|
+
# (example: check if a has_many association has a corresponding belongs_to in the remote class).
|
246
|
+
# Plural indicates wether the remote association we're looking for is plural (i.e. has_many :space_ships)
|
247
|
+
# or singular (i.e. belongs_to :space_ship).
|
248
|
+
# Normally, the method checks if the remote association refers to this class, but it is possible to
|
249
|
+
# pass in 'class_name' to check different classes
|
250
|
+
def cti_reciprocal_association_present_for?(association, type, plural = false, class_name = nil)
|
251
|
+
remote_class = cti_association_name_to_class_name( association ).constantize
|
252
|
+
remote_associations = remote_class.reflect_on_all_associations( type ).map(&:name)
|
253
|
+
remote_associations.include?( cti_class_name_to_association_name(plural, class_name).to_sym )
|
254
|
+
end
|
255
|
+
|
256
|
+
# converts e.g. SpaceShip to :space_ship (for plural == false), or :space_ships (for plural true)
|
257
|
+
def cti_class_name_to_association_name(plural = false, class_name = nil)
|
258
|
+
class_name ||= self.name
|
259
|
+
association_name = class_name.underscore
|
260
|
+
association_name = association_name.pluralize if plural
|
261
|
+
association_name
|
262
|
+
end
|
263
|
+
|
264
|
+
# converts e.g. :space_ships to SpaceShip
|
265
|
+
def cti_association_name_to_class_name(association_name)
|
266
|
+
association_name.to_s.camelize.singularize
|
267
|
+
end
|
222
268
|
|
223
269
|
def cti_redefine_remote_belongs_to_association(remote_class, remote_association)
|
224
270
|
remote_class.class_eval <<-eos, __FILE__, __LINE__+1
|
225
271
|
def #{remote_association}=(object, *args, &block)
|
226
|
-
super( object.convert_to
|
272
|
+
super( object.try(:convert_to, '#{self.name}'), *args, &block )
|
227
273
|
end
|
228
274
|
eos
|
229
275
|
end
|
@@ -231,7 +277,7 @@ module DBViewCTI
|
|
231
277
|
def cti_redefine_remote_to_many_association(remote_class, remote_association)
|
232
278
|
remote_class.class_eval <<-eos, __FILE__, __LINE__+1
|
233
279
|
def #{remote_association}=(objects, *args, &block)
|
234
|
-
super( objects.map { |o| o.convert_to
|
280
|
+
super( objects.map { |o| o.try(:convert_to, '#{self.name}') }, *args, &block)
|
235
281
|
end
|
236
282
|
def #{remote_association}(*args, &block)
|
237
283
|
collection = super
|
@@ -42,26 +42,9 @@ module DBViewCTI
|
|
42
42
|
|
43
43
|
private
|
44
44
|
|
45
|
-
module DisableValidator
|
46
|
-
def validate_each(record, *args)
|
47
|
-
return if record.respond_to?(:cti_disable_validations) && record.cti_disable_validations
|
48
|
-
super
|
49
|
-
end
|
50
|
-
|
51
|
-
if Rails::VERSION::MAJOR == 3
|
52
|
-
def validate(record, *args)
|
53
|
-
return if record.respond_to?(:cti_disable_validations) && record.cti_disable_validations
|
54
|
-
super
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
45
|
def disable_validations(object = nil)
|
60
46
|
object ||= @cti_converted_object
|
61
47
|
object.cti_disable_validations = true
|
62
|
-
object._validators.values.flatten.each do |validator|
|
63
|
-
validator.extend( DisableValidator )
|
64
|
-
end
|
65
48
|
end
|
66
49
|
|
67
50
|
module ForcePersistedState
|
@@ -57,6 +57,10 @@ module DBViewCTI
|
|
57
57
|
"DROP VIEW #{@view_name};"
|
58
58
|
end
|
59
59
|
|
60
|
+
def view_exists_sql
|
61
|
+
raise NotImplementedError, "DBViewCTI: view_exists_sql not implemented for this adapter."
|
62
|
+
end
|
63
|
+
|
60
64
|
def create_trigger_sql
|
61
65
|
# to be implemented by derived classes
|
62
66
|
raise NotImplementedError, "DBViewCTI: create_trigger_sql not implemented for this adapter."
|
@@ -87,6 +87,10 @@ module DBViewCTI
|
|
87
87
|
insert_trigger_func + insert_trigger
|
88
88
|
end
|
89
89
|
|
90
|
+
def view_exists_sql
|
91
|
+
"SELECT count(*) FROM pg_views where viewname='#{@view_name}';"
|
92
|
+
end
|
93
|
+
|
90
94
|
def drop_trigger_sql
|
91
95
|
query = <<-eos
|
92
96
|
DROP TRIGGER IF EXISTS #{@trigger_name} ON #{@view_name};
|
data/lib/db_view_cti/version.rb
CHANGED
@@ -91,6 +91,12 @@ describe SpaceShuttle do
|
|
91
91
|
launch.space_ship = @shuttle.convert_to(:vehicle)
|
92
92
|
launch.save!
|
93
93
|
}.to change(Launch, :count).by(1)
|
94
|
+
launch = Launch.new(:date => Date.today)
|
95
|
+
# also test nil assignment
|
96
|
+
expect {
|
97
|
+
launch.space_ship = nil
|
98
|
+
launch.save!
|
99
|
+
}.to change(Launch, :count).by(1)
|
94
100
|
end
|
95
101
|
|
96
102
|
it "supports accepts_nested_attributes for has_many associations defined in ascendant classes" do
|
@@ -184,6 +190,18 @@ describe SpaceShuttle do
|
|
184
190
|
expect {
|
185
191
|
experiment.space_ships.delete(@shuttle)
|
186
192
|
}.to change(ExperimentSpaceShipPerformance, :count).by(-1)
|
193
|
+
# make sure nil assignments raise activerecord exceptions and not exceptions in dbview_cti code
|
194
|
+
expect {
|
195
|
+
experiment.space_ships = [ nil ]
|
196
|
+
experiment.save!
|
197
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
198
|
+
expect {
|
199
|
+
experiment.space_ships << nil
|
200
|
+
experiment.save!
|
201
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
202
|
+
expect {
|
203
|
+
experiment.space_ships.delete(nil)
|
204
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
187
205
|
end
|
188
206
|
|
189
207
|
it "supports accepts_nested_attributes for has_many :through associations defined in ascendant classes" do
|
@@ -252,6 +270,12 @@ describe SpaceShuttle do
|
|
252
270
|
captain.space_ship = @shuttle.convert_to(:vehicle)
|
253
271
|
captain.save!
|
254
272
|
}.to change(Captain, :count).by(1)
|
273
|
+
# also test nil assignment
|
274
|
+
captain = Captain.new(:name => 'Armstrong')
|
275
|
+
expect {
|
276
|
+
captain.space_ship = nil
|
277
|
+
captain.save!
|
278
|
+
}.to change(Captain, :count).by(1)
|
255
279
|
end
|
256
280
|
|
257
281
|
it "supports accepts_nested_attributes for has_one associations defined in ascendant classes" do
|
@@ -341,6 +365,18 @@ describe SpaceShuttle do
|
|
341
365
|
ActiveRecord::Base.connection().execute(query)[0]['count'].to_i.should eq 1
|
342
366
|
astronaut.space_ships.destroy(@shuttle)
|
343
367
|
ActiveRecord::Base.connection().execute(query)[0]['count'].to_i.should be_zero
|
368
|
+
# make sure nil assignments raise activerecord exceptions and not exceptions in dbview_cti code
|
369
|
+
expect {
|
370
|
+
astronaut.space_ships = [nil]
|
371
|
+
astronaut.save!
|
372
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
373
|
+
expect {
|
374
|
+
astronaut.space_ships << nil
|
375
|
+
astronaut.save!
|
376
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
377
|
+
expect {
|
378
|
+
astronaut.space_ships.destroy(nil)
|
379
|
+
}.to raise_error(ActiveRecord::AssociationTypeMismatch)
|
344
380
|
end
|
345
381
|
|
346
382
|
it "supports accepts_nested_attributes for has_and_belongs_to_many associations defined in ascendant classes" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbview_cti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -148,6 +148,7 @@ files:
|
|
148
148
|
- spec/dummy-rails-3/db/migrate/20131022020655_create_habtm_join_table.rb
|
149
149
|
- spec/dummy-rails-3/db/migrate/20131022030659_create_experiments.rb
|
150
150
|
- spec/dummy-rails-3/db/migrate/20131022030720_create_experiment_space_ship_performances.rb
|
151
|
+
- spec/dummy-rails-3/db/migrate/20140408013710_check_view_exists.rb
|
151
152
|
- spec/dummy-rails-3/db/schema.rb
|
152
153
|
- spec/dummy-rails-3/lib/assets/.gitkeep
|
153
154
|
- spec/dummy-rails-3/log/.gitkeep
|
@@ -214,7 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
214
215
|
version: '0'
|
215
216
|
segments:
|
216
217
|
- 0
|
217
|
-
hash:
|
218
|
+
hash: 1297181597632204944
|
218
219
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
220
|
none: false
|
220
221
|
requirements:
|
@@ -223,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
223
224
|
version: '0'
|
224
225
|
segments:
|
225
226
|
- 0
|
226
|
-
hash:
|
227
|
+
hash: 1297181597632204944
|
227
228
|
requirements: []
|
228
229
|
rubyforge_project:
|
229
230
|
rubygems_version: 1.8.25
|