dbview_cti 0.1.1 → 0.1.2
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/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
|