dbview_cti 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/README.md +23 -2
- data/lib/db_view_cti/model/cti.rb +82 -44
- data/lib/db_view_cti/model/extensions.rb +1 -1
- data/lib/db_view_cti/model/model_delegator.rb +48 -0
- data/lib/db_view_cti/version.rb +1 -1
- data/lib/dbview_cti.rb +1 -0
- data/spec/dummy-rails-3/app/models/category.rb +5 -0
- data/spec/dummy-rails-3/app/models/launch.rb +2 -0
- data/spec/dummy-rails-3/app/models/space_ship.rb +9 -0
- data/spec/dummy-rails-3/db/migrate/20130816022112_create_categories.rb +9 -0
- data/spec/dummy-rails-3/db/migrate/20130816022212_create_space_ships.rb +3 -0
- data/spec/dummy-rails-3/db/schema.rb +8 -0
- data/spec/models/space_shuttle_spec.rb +214 -1
- metadata +7 -4
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 0.1.2 (27/10/2013)
|
2
|
+
|
3
|
+
* Fundamental change to how associations are handled in order to solve problems with build
|
4
|
+
* Added support for accepts_nested_attributes_for
|
5
|
+
* Validation errors on associated models are now correctly handled
|
6
|
+
|
1
7
|
## 0.1.1 (23/10/2013)
|
2
8
|
|
3
9
|
* Fixed handling of the remote part of associations (i.e. the belongs_to part when a cti-class has e.g. a has_many association)
|
data/README.md
CHANGED
@@ -175,8 +175,29 @@ end
|
|
175
175
|
|
176
176
|
## Associations
|
177
177
|
|
178
|
-
Associations (`has_many`, `has_one`, etc.) work and are inherited as you would expect.
|
179
|
-
|
178
|
+
Associations (`has_many`, `has_one`, etc.) work and are inherited as you would expect. There are two caveats:
|
179
|
+
|
180
|
+
* In the base class, you have to call `cti_base_class` before defining any associations:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class Vehicle < ActiveRecord::Base
|
184
|
+
# call cti_base_class first...
|
185
|
+
cti_base_class
|
186
|
+
|
187
|
+
# ...before defining any associations
|
188
|
+
has_many :parts
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
* In Rails 4 it might be necessary to explicitly specify the join table when using `has_and_belongs_to_many`:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class SpaceShip < Vehicle
|
196
|
+
cti_derived_class
|
197
|
+
|
198
|
+
has_and_belongs_to_many :astronauts, :join_table => 'astronauts_space_ships'
|
199
|
+
end
|
200
|
+
```
|
180
201
|
|
181
202
|
## API
|
182
203
|
|
@@ -31,6 +31,7 @@ module DBViewCTI
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def convert_to(type)
|
34
|
+
return nil unless persisted?
|
34
35
|
type_string = type.to_s
|
35
36
|
type_string = type_string.camelize if type.is_a?(Symbol)
|
36
37
|
return self if type_string == self.class.name
|
@@ -48,26 +49,73 @@ module DBViewCTI
|
|
48
49
|
type_string.constantize.find(id.to_i)
|
49
50
|
end
|
50
51
|
|
51
|
-
# change destroy and delete methods to operate on most specialized
|
52
|
+
# change destroy and delete methods to operate on most specialized object
|
52
53
|
included do
|
53
|
-
alias_method_chain :destroy, :
|
54
|
-
alias_method_chain :delete, :
|
54
|
+
alias_method_chain :destroy, :cti
|
55
|
+
alias_method_chain :delete, :cti
|
55
56
|
# destroy! seems te be defined in Rails 4
|
56
|
-
alias_method_chain :destroy!, :
|
57
|
+
alias_method_chain :destroy!, :cti if self.method_defined?(:destroy!)
|
58
|
+
|
59
|
+
# for associations:
|
60
|
+
alias_method_chain :association, :cti
|
61
|
+
validate :cti_validate_associations
|
62
|
+
|
63
|
+
# save callbacks (necessary for saving associations)
|
64
|
+
after_save :cti_save_associations
|
57
65
|
end
|
58
66
|
|
59
|
-
def
|
60
|
-
specialize.
|
67
|
+
def destroy_with_cti
|
68
|
+
specialize.destroy_without_cti
|
61
69
|
end
|
62
70
|
|
63
|
-
def
|
64
|
-
specialize.
|
71
|
+
def destroy_with_cti!
|
72
|
+
specialize.destroy_without_cti!
|
65
73
|
end
|
66
74
|
|
67
|
-
def
|
68
|
-
specialize.
|
75
|
+
def delete_with_cti
|
76
|
+
specialize.delete_without_cti
|
77
|
+
end
|
78
|
+
|
79
|
+
def cti_validate_associations
|
80
|
+
return_value = true
|
81
|
+
self.class.cti_association_proxies.each_key do |proxy_name|
|
82
|
+
proxy = instance_variable_get(proxy_name)
|
83
|
+
if proxy && !proxy.valid?
|
84
|
+
errors.messages.merge!(proxy.errors.messages)
|
85
|
+
return_value = false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
return_value
|
89
|
+
end
|
90
|
+
|
91
|
+
def cti_save_associations
|
92
|
+
self.class.cti_association_proxies.each_key do |proxy_name|
|
93
|
+
proxy = instance_variable_get(proxy_name)
|
94
|
+
proxy.save if proxy
|
95
|
+
end
|
96
|
+
true
|
69
97
|
end
|
70
98
|
|
99
|
+
def association_with_cti(*args)
|
100
|
+
return association_without_cti(*args) unless args.length == 1
|
101
|
+
association_name = args[0]
|
102
|
+
proxy = cti_association_proxy(association_name)
|
103
|
+
proxy ||= self
|
104
|
+
proxy.association_without_cti(association_name)
|
105
|
+
end
|
106
|
+
|
107
|
+
def cti_association_proxy(association_name)
|
108
|
+
return nil if self.class.reflect_on_all_associations(:belongs_to).map(&:name).include?(association_name.to_sym)
|
109
|
+
proxy_name = self.class.cti_association_proxy_name(association_name)
|
110
|
+
proxy = instance_variable_get(proxy_name)
|
111
|
+
if !proxy && !self.class.cti_has_association?(association_name)
|
112
|
+
instance_variable_set(proxy_name,
|
113
|
+
ModelDelegator.new(self, self.class.cti_association_proxies[proxy_name]))
|
114
|
+
proxy = instance_variable_get(proxy_name)
|
115
|
+
end
|
116
|
+
proxy
|
117
|
+
end
|
118
|
+
|
71
119
|
module ClassMethods
|
72
120
|
|
73
121
|
def cti_base_class?
|
@@ -78,7 +126,7 @@ module DBViewCTI
|
|
78
126
|
!!@cti_derived_class
|
79
127
|
end
|
80
128
|
|
81
|
-
attr_accessor :cti_descendants, :cti_ascendants
|
129
|
+
attr_accessor :cti_descendants, :cti_ascendants, :cti_association_proxies
|
82
130
|
|
83
131
|
# registers a derived class and its descendants in the current class
|
84
132
|
# class_name: name of derived class (the one calling cti_register_descendants on this class)
|
@@ -114,7 +162,7 @@ module DBViewCTI
|
|
114
162
|
result
|
115
163
|
end
|
116
164
|
|
117
|
-
# redefine association class methods
|
165
|
+
# redefine association class methods
|
118
166
|
[:has_many, :has_and_belongs_to_many, :has_one].each do |name|
|
119
167
|
self.class_eval <<-eos, __FILE__, __LINE__+1
|
120
168
|
def #{name}(*args, &block)
|
@@ -125,45 +173,20 @@ module DBViewCTI
|
|
125
173
|
eos
|
126
174
|
end
|
127
175
|
|
128
|
-
def
|
129
|
-
#
|
176
|
+
def cti_create_association_proxies
|
177
|
+
# create hash with proxy and class names. The proxies themselves will be created
|
178
|
+
# by the 'association' instance method when the association is used for the first time.
|
179
|
+
@cti_association_proxies ||= {}
|
130
180
|
@cti_ascendants.each do |ascendant|
|
131
|
-
[:has_many, :has_and_belongs_to_many].each do |association_type|
|
181
|
+
[:has_many, :has_and_belongs_to_many, :has_one].each do |association_type|
|
132
182
|
ascendant.constantize.cti_associations[association_type].each do |association|
|
133
|
-
|
183
|
+
proxy_name = cti_association_proxy_name(association)
|
184
|
+
@cti_association_proxies[proxy_name] = ascendant
|
134
185
|
end
|
135
186
|
end
|
136
|
-
ascendant.constantize.cti_associations[:has_one].each do |association|
|
137
|
-
cti_redefine_has_one(ascendant, association)
|
138
|
-
end
|
139
187
|
end
|
140
188
|
end
|
141
189
|
|
142
|
-
# redefine has_many and has_and_belongs_to_many association
|
143
|
-
def cti_redefine_to_many(class_name, association)
|
144
|
-
plural = association.to_s
|
145
|
-
singular = association.to_s.singularize
|
146
|
-
[ plural, "#{plural}=", "#{singular}_ids", "#{singular}_ids=" ].each do |name|
|
147
|
-
cti_redefine_single_association(name, class_name)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# redefine has_many and has_and_belongs_to_many association
|
152
|
-
def cti_redefine_has_one(class_name, association)
|
153
|
-
singular = association
|
154
|
-
[ association, "#{association}=", "build_#{association}", "create_#{association}", "create_#{association}!" ].each do |name|
|
155
|
-
cti_redefine_single_association(name, class_name)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def cti_redefine_single_association(name, class_name)
|
160
|
-
self.class_eval <<-eos, __FILE__, __LINE__+1
|
161
|
-
def #{name}(*args, &block)
|
162
|
-
self.convert_to('#{class_name}').send('#{name}', *args, &block)
|
163
|
-
end
|
164
|
-
eos
|
165
|
-
end
|
166
|
-
|
167
190
|
# fix the 'remote' (i.e. belongs_to) part of any has_one of has_many association in this class
|
168
191
|
def cti_redefine_remote_associations
|
169
192
|
cti_initialize_cti_associations
|
@@ -214,12 +237,26 @@ module DBViewCTI
|
|
214
237
|
end
|
215
238
|
eos
|
216
239
|
end
|
240
|
+
|
241
|
+
def cti_association_proxy_name(association)
|
242
|
+
"@cti_#{association}_association_proxy"
|
243
|
+
end
|
217
244
|
|
218
245
|
def cti_associations
|
219
246
|
cti_initialize_cti_associations
|
220
247
|
@cti_associations
|
221
248
|
end
|
222
249
|
|
250
|
+
def cti_has_association?(association_name)
|
251
|
+
if !@cti_all_associations
|
252
|
+
@cti_all_associations = @cti_associations.keys.inject([]) do |result, key|
|
253
|
+
result += @cti_associations[key]
|
254
|
+
result
|
255
|
+
end
|
256
|
+
end
|
257
|
+
@cti_all_associations.include?(association_name.to_sym)
|
258
|
+
end
|
259
|
+
|
223
260
|
include DBViewCTI::SQLGeneration::Model
|
224
261
|
|
225
262
|
# this method is only used in testing. It returns the number of rows present in the real database
|
@@ -236,6 +273,7 @@ module DBViewCTI
|
|
236
273
|
@cti_associations[name] ||= []
|
237
274
|
@cti_redefined_remote_associations[name] ||= []
|
238
275
|
end
|
276
|
+
@cti_association_proxies ||= {}
|
239
277
|
end
|
240
278
|
|
241
279
|
end
|
@@ -18,7 +18,7 @@ module DBViewCTI
|
|
18
18
|
@cti_derived_class = true
|
19
19
|
self.table_name = DBViewCTI::Names.view_name(self)
|
20
20
|
self.superclass.cti_register_descendants(self.name)
|
21
|
-
|
21
|
+
cti_create_association_proxies
|
22
22
|
cti_redefine_remote_associations
|
23
23
|
# call redefine_remote_associations on superclass to deal with associations
|
24
24
|
# that were defined after the call to cti_derived_class or cti_base_class
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module DBViewCTI
|
4
|
+
module Model
|
5
|
+
|
6
|
+
class ModelDelegator < SimpleDelegator
|
7
|
+
|
8
|
+
attr_reader :cti_target_class
|
9
|
+
|
10
|
+
def initialize(object, target_class)
|
11
|
+
@cti_object = object
|
12
|
+
@cti_converted_object = object.convert_to(target_class)
|
13
|
+
if !@cti_converted_object
|
14
|
+
@cti_converted_object = object.becomes(target_class.constantize)
|
15
|
+
@cti_is_new = true
|
16
|
+
end
|
17
|
+
@cti_target_class = target_class
|
18
|
+
super( @cti_converted_object )
|
19
|
+
end
|
20
|
+
|
21
|
+
def cti_is_new?
|
22
|
+
@cti_is_new
|
23
|
+
end
|
24
|
+
|
25
|
+
def save(*args, &block)
|
26
|
+
return super unless cti_is_new?
|
27
|
+
# special case for new objects, we need som hackish id-juggling
|
28
|
+
old_id = @cti_object.id
|
29
|
+
new_id = @cti_object.convert_to( @cti_target_class ).id
|
30
|
+
# since @cti_converted_object was created using 'becomes', @cti_object.id changes
|
31
|
+
# as well in the following statement. So we saved it in old_id and restore it after the
|
32
|
+
# call to save (i.e. super)
|
33
|
+
@cti_object.reload # only needed in rails 4
|
34
|
+
self.id = new_id
|
35
|
+
self.created_at = @cti_object.created_at # only needed in rails 4
|
36
|
+
self.updated_at = @cti_object.updated_at # only needed in rails 4
|
37
|
+
retval = !!super
|
38
|
+
@cti_is_new = false
|
39
|
+
@cti_object.id = old_id
|
40
|
+
@cti_converted_object = @cti_object.convert_to( @cti_target_class )
|
41
|
+
__setobj__(@cti_converted_object)
|
42
|
+
retval
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/db_view_cti/version.rb
CHANGED
data/lib/dbview_cti.rb
CHANGED
@@ -2,9 +2,18 @@ class SpaceShip < Vehicle
|
|
2
2
|
attr_accessible :single_use, :reliability unless Rails::VERSION::MAJOR > 3
|
3
3
|
cti_derived_class
|
4
4
|
|
5
|
+
belongs_to :category
|
6
|
+
|
5
7
|
has_many :launches
|
8
|
+
accepts_nested_attributes_for :launches
|
9
|
+
|
6
10
|
has_one :captain
|
11
|
+
accepts_nested_attributes_for :captain
|
12
|
+
|
7
13
|
has_and_belongs_to_many :astronauts, :join_table => 'astronauts_space_ships'
|
14
|
+
accepts_nested_attributes_for :astronauts
|
15
|
+
|
8
16
|
has_many :experiment_space_ship_performances
|
9
17
|
has_many :experiments, :through => :experiment_space_ship_performances
|
18
|
+
accepts_nested_attributes_for :experiments
|
10
19
|
end
|
@@ -2,12 +2,15 @@ class CreateSpaceShips < ActiveRecord::Migration
|
|
2
2
|
def change
|
3
3
|
create_table :space_ships do |t|
|
4
4
|
t.references :vehicle
|
5
|
+
|
6
|
+
t.references :category
|
5
7
|
t.boolean :single_use
|
6
8
|
|
7
9
|
t.timestamps
|
8
10
|
end
|
9
11
|
|
10
12
|
add_foreign_key(:space_ships, :vehicles)
|
13
|
+
add_foreign_key(:space_ships, :categories)
|
11
14
|
cti_create_view('SpaceShip')
|
12
15
|
end
|
13
16
|
end
|
@@ -39,6 +39,12 @@ ActiveRecord::Schema.define(:version => 20131022030720) do
|
|
39
39
|
t.datetime "updated_at", :null => false
|
40
40
|
end
|
41
41
|
|
42
|
+
create_table "categories", :force => true do |t|
|
43
|
+
t.string "name"
|
44
|
+
t.datetime "created_at", :null => false
|
45
|
+
t.datetime "updated_at", :null => false
|
46
|
+
end
|
47
|
+
|
42
48
|
create_table "experiment_space_ship_performances", :force => true do |t|
|
43
49
|
t.integer "experiment_id"
|
44
50
|
t.integer "space_ship_id"
|
@@ -84,6 +90,7 @@ ActiveRecord::Schema.define(:version => 20131022030720) do
|
|
84
90
|
|
85
91
|
create_table "space_ships", :force => true do |t|
|
86
92
|
t.integer "vehicle_id"
|
93
|
+
t.integer "category_id"
|
87
94
|
t.boolean "single_use"
|
88
95
|
t.datetime "created_at", :null => false
|
89
96
|
t.datetime "updated_at", :null => false
|
@@ -122,6 +129,7 @@ ActiveRecord::Schema.define(:version => 20131022030720) do
|
|
122
129
|
|
123
130
|
add_foreign_key "rocket_engines", "space_ships", :name => "rocket_engines_space_ship_id_fk"
|
124
131
|
|
132
|
+
add_foreign_key "space_ships", "categories", :name => "space_ships_category_id_fk"
|
125
133
|
add_foreign_key "space_ships", "vehicles", :name => "space_ships_vehicle_id_fk"
|
126
134
|
|
127
135
|
add_foreign_key "space_shuttles", "space_ships", :name => "space_shuttles_space_ship_id_fk"
|
@@ -44,6 +44,39 @@ describe SpaceShuttle do
|
|
44
44
|
launch3.save!
|
45
45
|
@shuttle.launch_ids = [ launch1.id, launch3.id ]
|
46
46
|
@shuttle.launch_ids.sort.should eq [ launch1.id, launch3.id ]
|
47
|
+
# test build functionality for adding to existing collection
|
48
|
+
expect {
|
49
|
+
@shuttle.launches.build(:date => Date.yesterday)
|
50
|
+
@shuttle.save!
|
51
|
+
}.to change(Launch, :count).by(1)
|
52
|
+
@shuttle.launches.order(:id).last.date.should eq Date.yesterday
|
53
|
+
# test build functionality for new collection
|
54
|
+
@shuttle.launches.clear
|
55
|
+
@shuttle.launch_ids.should eq []
|
56
|
+
expect {
|
57
|
+
@shuttle.name = 'Shuttle'
|
58
|
+
@shuttle.launches.build(:date => Date.yesterday)
|
59
|
+
@shuttle.save!
|
60
|
+
}.to change(Launch, :count).by(1)
|
61
|
+
@shuttle.name.should eq 'Shuttle'
|
62
|
+
# test build functionality for new collection and new object
|
63
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
64
|
+
expect {
|
65
|
+
expect {
|
66
|
+
shuttle.name = 'Shuttle'
|
67
|
+
shuttle.launches.build(:date => Date.yesterday)
|
68
|
+
shuttle.launches.build(:date => Date.tomorrow)
|
69
|
+
shuttle.save!
|
70
|
+
}.to change(Launch, :count).by(2)
|
71
|
+
}.to change(SpaceShuttle, :count).by(1)
|
72
|
+
shuttle.reload
|
73
|
+
shuttle.name.should eq 'Shuttle'
|
74
|
+
# test adding onto existing collection
|
75
|
+
expect {
|
76
|
+
shuttle.launches.build(:date => Date.today)
|
77
|
+
shuttle.save!
|
78
|
+
}.to change(Launch, :count).by(1)
|
79
|
+
shuttle.launches.order(:id).last.date.should eq Date.today
|
47
80
|
end
|
48
81
|
|
49
82
|
it "supports assignment on the 'remote' side of a has_many association" do
|
@@ -59,6 +92,27 @@ describe SpaceShuttle do
|
|
59
92
|
launch.save!
|
60
93
|
}.to change(Launch, :count).by(1)
|
61
94
|
end
|
95
|
+
|
96
|
+
it "supports accepts_nested_attributes for has_many associations defined in ascendant classes" do
|
97
|
+
expect {
|
98
|
+
@shuttle.launches_attributes = [
|
99
|
+
{:date => Date.today }, {:date => Date.yesterday }
|
100
|
+
]
|
101
|
+
@shuttle.save!
|
102
|
+
}.to change(Launch, :count).by(2)
|
103
|
+
@shuttle.launches.order(:date).map(&:date).should eq [ Date.yesterday, Date.today ]
|
104
|
+
# do the same for a new object
|
105
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
106
|
+
expect {
|
107
|
+
expect {
|
108
|
+
shuttle.launches_attributes = [
|
109
|
+
{:date => Date.today }, {:date => Date.yesterday }
|
110
|
+
]
|
111
|
+
shuttle.save!
|
112
|
+
}.to change(Launch, :count).by(2)
|
113
|
+
}.to change(SpaceShuttle, :count).by(1)
|
114
|
+
shuttle.launches.order(:date).map(&:date).should eq [ Date.yesterday, Date.today ]
|
115
|
+
end
|
62
116
|
|
63
117
|
it "can use has_many :through associations defined in ascendant classes" do
|
64
118
|
experiment1 = Experiment.new(:name => 'Zero-gravity')
|
@@ -77,6 +131,41 @@ describe SpaceShuttle do
|
|
77
131
|
@shuttle.experiment_ids = [ experiment1.id, experiment3.id ]
|
78
132
|
@shuttle.experiment_ids.sort.should eq [ experiment1.id, experiment3.id ]
|
79
133
|
Experiment.last.space_ships.first.specialize.id.should eq @shuttle.id
|
134
|
+
# test build functionality for adding to existing collection
|
135
|
+
expect {
|
136
|
+
@shuttle.experiments.build(:name => 'Superconductivity')
|
137
|
+
@shuttle.save!
|
138
|
+
}.to change(Experiment, :count).by(1)
|
139
|
+
@shuttle.experiments.order(:id).last.name.should eq 'Superconductivity'
|
140
|
+
# test build functionality for new collection
|
141
|
+
@shuttle.experiments.clear
|
142
|
+
@shuttle.experiment_ids.should eq []
|
143
|
+
expect {
|
144
|
+
@shuttle.name = 'Shuttle'
|
145
|
+
@shuttle.experiments.build(:name => 'Failed experiment')
|
146
|
+
@shuttle.save!
|
147
|
+
}.to change(Experiment, :count).by(1)
|
148
|
+
@shuttle.experiments.first.name.should eq 'Failed experiment'
|
149
|
+
@shuttle.name.should eq 'Shuttle'
|
150
|
+
# test build functionality for new collection and new object
|
151
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
152
|
+
expect {
|
153
|
+
expect {
|
154
|
+
shuttle.name = 'Shuttle'
|
155
|
+
shuttle.experiments.build(:name => 'Exp1')
|
156
|
+
shuttle.experiments.build(:name => 'Exp2')
|
157
|
+
shuttle.save!
|
158
|
+
}.to change(Experiment, :count).by(2)
|
159
|
+
}.to change(SpaceShuttle, :count).by(1)
|
160
|
+
shuttle.reload
|
161
|
+
shuttle.name.should eq 'Shuttle'
|
162
|
+
shuttle.experiments.order(:name).map(&:name).should eq ['Exp1', 'Exp2']
|
163
|
+
# test adding onto existing collection (new object)
|
164
|
+
expect {
|
165
|
+
shuttle.experiments.build(:name => 'Exp3')
|
166
|
+
shuttle.save!
|
167
|
+
}.to change(Experiment, :count).by(1)
|
168
|
+
shuttle.experiments.order(:name).map(&:name).should eq ['Exp1', 'Exp2', 'Exp3']
|
80
169
|
end
|
81
170
|
|
82
171
|
it "supports operations on the 'remote' side of a has_many :through association" do
|
@@ -97,6 +186,27 @@ describe SpaceShuttle do
|
|
97
186
|
}.to change(ExperimentSpaceShipPerformance, :count).by(-1)
|
98
187
|
end
|
99
188
|
|
189
|
+
it "supports accepts_nested_attributes for has_many :through associations defined in ascendant classes" do
|
190
|
+
expect {
|
191
|
+
@shuttle.experiments_attributes = [
|
192
|
+
{:name => 'Exp1'}, {:name => 'Exp2'}
|
193
|
+
]
|
194
|
+
@shuttle.save!
|
195
|
+
}.to change(Experiment, :count).by(2)
|
196
|
+
@shuttle.experiments.order(:name).map(&:name).should eq [ 'Exp1', 'Exp2' ]
|
197
|
+
# do the same for a new object
|
198
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
199
|
+
expect {
|
200
|
+
expect {
|
201
|
+
shuttle.experiments_attributes = [
|
202
|
+
{:name => 'Exp1'}, {:name => 'Exp2'}
|
203
|
+
]
|
204
|
+
shuttle.save!
|
205
|
+
}.to change(Experiment, :count).by(2)
|
206
|
+
}.to change(SpaceShuttle, :count).by(1)
|
207
|
+
shuttle.experiments.order(:name).map(&:name).should eq [ 'Exp1', 'Exp2' ]
|
208
|
+
end
|
209
|
+
|
100
210
|
it "can use has_one associations defined in ascendant classes" do
|
101
211
|
captain = Captain.new(:name => 'Armstrong')
|
102
212
|
expect {
|
@@ -110,11 +220,24 @@ describe SpaceShuttle do
|
|
110
220
|
@shuttle.create_captain(:name => 'Glenn')
|
111
221
|
}.to change(Captain, :count).by(1)
|
112
222
|
@shuttle.captain.space_ship_id.should eq @shuttle.convert_to(:space_ship).id
|
223
|
+
Captain.all.map(&:destroy)
|
224
|
+
# test build for existing object
|
113
225
|
expect {
|
114
226
|
cap = @shuttle.build_captain(:name => 'Aldrinn')
|
115
|
-
|
227
|
+
@shuttle.save!
|
116
228
|
}.to change(Captain, :count).by(1)
|
117
229
|
@shuttle.captain.space_ship_id.should eq @shuttle.convert_to(:space_ship).id
|
230
|
+
# test build for new object
|
231
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
232
|
+
expect {
|
233
|
+
expect {
|
234
|
+
shuttle.name = 'Shuttle'
|
235
|
+
cap = shuttle.build_captain(:name => 'Aldrinn')
|
236
|
+
shuttle.save!
|
237
|
+
}.to change(Captain, :count).by(1)
|
238
|
+
}.to change(SpaceShuttle, :count).by(1)
|
239
|
+
shuttle.name.should eq 'Shuttle'
|
240
|
+
shuttle.captain.space_ship_id.should eq shuttle.convert_to(:space_ship).id
|
118
241
|
end
|
119
242
|
|
120
243
|
it "supports operations on the 'remote' side of a has_one association" do
|
@@ -131,6 +254,23 @@ describe SpaceShuttle do
|
|
131
254
|
}.to change(Captain, :count).by(1)
|
132
255
|
end
|
133
256
|
|
257
|
+
it "supports accepts_nested_attributes for has_one associations defined in ascendant classes" do
|
258
|
+
expect {
|
259
|
+
@shuttle.captain_attributes = {:name => 'Haddock'}
|
260
|
+
@shuttle.save!
|
261
|
+
}.to change(Captain, :count).by(1)
|
262
|
+
@shuttle.captain.name.should eq 'Haddock'
|
263
|
+
# do the same for a new object
|
264
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
265
|
+
expect {
|
266
|
+
expect {
|
267
|
+
shuttle.captain_attributes = {:name => 'Haddock'}
|
268
|
+
shuttle.save!
|
269
|
+
}.to change(Captain, :count).by(1)
|
270
|
+
}.to change(SpaceShuttle, :count).by(1)
|
271
|
+
shuttle.captain.name.should eq 'Haddock'
|
272
|
+
end
|
273
|
+
|
134
274
|
it "can use has_and_belongs_to_many associations defined in ascendant classes" do
|
135
275
|
astronaut1 = Astronaut.new(:name => 'Armstrong')
|
136
276
|
astronaut2 = Astronaut.new(:name => 'Glenn')
|
@@ -148,6 +288,41 @@ describe SpaceShuttle do
|
|
148
288
|
astronaut3.save!
|
149
289
|
@shuttle.astronaut_ids = [ astronaut1.id, astronaut3.id ]
|
150
290
|
@shuttle.astronaut_ids.sort.should eq [ astronaut1.id, astronaut3.id ]
|
291
|
+
# test build functionality for adding to existing collection
|
292
|
+
expect {
|
293
|
+
@shuttle.astronauts.build(:name => 'astro1')
|
294
|
+
@shuttle.save!
|
295
|
+
}.to change(Astronaut, :count).by(1)
|
296
|
+
@shuttle.astronauts.order(:id).last.name.should eq 'astro1'
|
297
|
+
# test build functionality for new collection
|
298
|
+
@shuttle.astronauts.clear
|
299
|
+
@shuttle.astronaut_ids.should eq []
|
300
|
+
expect {
|
301
|
+
@shuttle.name = 'Shuttle'
|
302
|
+
@shuttle.astronauts.build(:name => 'astro2')
|
303
|
+
@shuttle.save!
|
304
|
+
}.to change(Astronaut, :count).by(1)
|
305
|
+
@shuttle.astronauts.first.name.should eq 'astro2'
|
306
|
+
@shuttle.name.should eq 'Shuttle'
|
307
|
+
# test build functionality for new collection and new object
|
308
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
309
|
+
expect {
|
310
|
+
expect {
|
311
|
+
shuttle.name = 'Shuttle'
|
312
|
+
shuttle.astronauts.build(:name => 'Astro1')
|
313
|
+
shuttle.astronauts.build(:name => 'Astro2')
|
314
|
+
shuttle.save!
|
315
|
+
}.to change(Astronaut, :count).by(2)
|
316
|
+
}.to change(SpaceShuttle, :count).by(1)
|
317
|
+
shuttle.reload
|
318
|
+
shuttle.name.should eq 'Shuttle'
|
319
|
+
shuttle.astronauts.order(:name).map(&:name).should eq ['Astro1', 'Astro2']
|
320
|
+
# test adding onto existing collection (new object)
|
321
|
+
expect {
|
322
|
+
shuttle.astronauts.build(:name => 'Astro3')
|
323
|
+
shuttle.save!
|
324
|
+
}.to change(Astronaut, :count).by(1)
|
325
|
+
shuttle.astronauts.order(:name).map(&:name).should eq ['Astro1', 'Astro2', 'Astro3']
|
151
326
|
end
|
152
327
|
|
153
328
|
it "supports operations on the 'remote' side of a has_and_belongs_to_many association" do
|
@@ -167,7 +342,45 @@ describe SpaceShuttle do
|
|
167
342
|
astronaut.space_ships.destroy(@shuttle)
|
168
343
|
ActiveRecord::Base.connection().execute(query)[0]['count'].to_i.should be_zero
|
169
344
|
end
|
345
|
+
|
346
|
+
it "supports accepts_nested_attributes for has_and_belongs_to_many associations defined in ascendant classes" do
|
347
|
+
expect {
|
348
|
+
@shuttle.astronauts_attributes = [
|
349
|
+
{:name => 'Astro1'}, {:name => 'Astro2'}
|
350
|
+
]
|
351
|
+
@shuttle.save!
|
352
|
+
}.to change(Astronaut, :count).by(2)
|
353
|
+
@shuttle.astronauts.order(:name).map(&:name).should eq [ 'Astro1', 'Astro2' ]
|
354
|
+
# do the same for a new object
|
355
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
356
|
+
expect {
|
357
|
+
expect {
|
358
|
+
shuttle.astronauts_attributes = [
|
359
|
+
{:name => 'Astro1'}, {:name => 'Astro2'}
|
360
|
+
]
|
361
|
+
shuttle.save!
|
362
|
+
}.to change(Astronaut, :count).by(2)
|
363
|
+
}.to change(SpaceShuttle, :count).by(1)
|
364
|
+
shuttle.astronauts.order(:name).map(&:name).should eq [ 'Astro1', 'Astro2' ]
|
365
|
+
end
|
366
|
+
|
367
|
+
it "doesn't choke on belongs_to associations" do
|
368
|
+
@shuttle.category # should not rais exception
|
369
|
+
end
|
170
370
|
|
371
|
+
it "doesn't save in case of validation errors in associations defined in ascendant classes" do
|
372
|
+
expect {
|
373
|
+
@shuttle.launches.build
|
374
|
+
@shuttle.save!
|
375
|
+
}.to raise_exception(ActiveRecord::RecordInvalid)
|
376
|
+
# same with new object
|
377
|
+
shuttle = SpaceShuttle.new(:name => 'Endeavour', :reliability => 100)
|
378
|
+
expect {
|
379
|
+
shuttle.launches.build
|
380
|
+
shuttle.save!
|
381
|
+
}.to raise_exception(ActiveRecord::RecordInvalid)
|
382
|
+
end
|
383
|
+
|
171
384
|
end
|
172
385
|
|
173
386
|
end
|
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.2
|
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: 2013-10-
|
12
|
+
date: 2013-10-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- lib/db_view_cti/model/collection_delegator.rb
|
82
82
|
- lib/db_view_cti/model/cti.rb
|
83
83
|
- lib/db_view_cti/model/extensions.rb
|
84
|
+
- lib/db_view_cti/model/model_delegator.rb
|
84
85
|
- lib/db_view_cti/names.rb
|
85
86
|
- lib/db_view_cti/railtie.rb
|
86
87
|
- lib/db_view_cti/schema_dumper.rb
|
@@ -104,6 +105,7 @@ files:
|
|
104
105
|
- spec/dummy-rails-3/app/models/astronaut.rb
|
105
106
|
- spec/dummy-rails-3/app/models/captain.rb
|
106
107
|
- spec/dummy-rails-3/app/models/car.rb
|
108
|
+
- spec/dummy-rails-3/app/models/category.rb
|
107
109
|
- spec/dummy-rails-3/app/models/experiment.rb
|
108
110
|
- spec/dummy-rails-3/app/models/experiment_space_ship_performance.rb
|
109
111
|
- spec/dummy-rails-3/app/models/launch.rb
|
@@ -135,6 +137,7 @@ files:
|
|
135
137
|
- spec/dummy-rails-3/db/migrate/20130713133605_create_motor_vehicles.rb
|
136
138
|
- spec/dummy-rails-3/db/migrate/20130713133705_create_cars.rb
|
137
139
|
- spec/dummy-rails-3/db/migrate/20130713153631_create_motor_cycles.rb
|
140
|
+
- spec/dummy-rails-3/db/migrate/20130816022112_create_categories.rb
|
138
141
|
- spec/dummy-rails-3/db/migrate/20130816022212_create_space_ships.rb
|
139
142
|
- spec/dummy-rails-3/db/migrate/20130817014120_create_space_shuttles.rb
|
140
143
|
- spec/dummy-rails-3/db/migrate/20130817024220_create_rocket_engines.rb
|
@@ -211,7 +214,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
211
214
|
version: '0'
|
212
215
|
segments:
|
213
216
|
- 0
|
214
|
-
hash:
|
217
|
+
hash: -748447197345162789
|
215
218
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
216
219
|
none: false
|
217
220
|
requirements:
|
@@ -220,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
223
|
version: '0'
|
221
224
|
segments:
|
222
225
|
- 0
|
223
|
-
hash:
|
226
|
+
hash: -748447197345162789
|
224
227
|
requirements: []
|
225
228
|
rubyforge_project:
|
226
229
|
rubygems_version: 1.8.25
|