viking-sequel 3.10.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/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,332 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
context "Migration classes" do
|
4
|
+
before do
|
5
|
+
Sequel::Migration.descendants.clear
|
6
|
+
end
|
7
|
+
|
8
|
+
specify "should be registred in Migration.descendants" do
|
9
|
+
@class = Class.new(Sequel::Migration)
|
10
|
+
|
11
|
+
Sequel::Migration.descendants.should == [@class]
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "should be registered in the right order" do
|
15
|
+
@c1 = Class.new(Sequel::Migration)
|
16
|
+
@c2 = Class.new(Sequel::Migration)
|
17
|
+
@c3 = Class.new(Sequel::Migration)
|
18
|
+
|
19
|
+
Sequel::Migration.descendants.should == [@c1, @c2, @c3]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Migration#apply" do
|
24
|
+
before do
|
25
|
+
@c = Class.new do
|
26
|
+
define_method(:one) {|x| [1111, x]}
|
27
|
+
define_method(:two) {|x| [2222, x]}
|
28
|
+
end
|
29
|
+
@db = @c.new
|
30
|
+
|
31
|
+
@migration = Class.new(Sequel::Migration) do
|
32
|
+
define_method(:up) {one(3333)}
|
33
|
+
define_method(:down) {two(4444)}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "should raise for an invalid direction" do
|
38
|
+
proc {@migration.apply(@db, :hahaha)}.should raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should apply the up direction correctly" do
|
42
|
+
@migration.apply(@db, :up).should == [1111, 3333]
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "should apply the down direction correctly" do
|
46
|
+
@migration.apply(@db, :down).should == [2222, 4444]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
MIGRATION_001 = %[
|
51
|
+
class CreateSessions < Sequel::Migration
|
52
|
+
def up
|
53
|
+
create(1111)
|
54
|
+
end
|
55
|
+
|
56
|
+
def down
|
57
|
+
drop(1111)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
]
|
61
|
+
|
62
|
+
MIGRATION_002 = %[
|
63
|
+
class CreateNodes < Sequel::Migration
|
64
|
+
def up
|
65
|
+
create(2222)
|
66
|
+
end
|
67
|
+
|
68
|
+
def down
|
69
|
+
drop(2222)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
]
|
73
|
+
|
74
|
+
MIGRATION_003 = %[
|
75
|
+
class CreateUsers < Sequel::Migration
|
76
|
+
def up
|
77
|
+
create(3333)
|
78
|
+
end
|
79
|
+
|
80
|
+
def down
|
81
|
+
drop(3333)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
]
|
85
|
+
|
86
|
+
MIGRATION_005 = %[
|
87
|
+
class CreateAttributes < Sequel::Migration
|
88
|
+
def up
|
89
|
+
create(5555)
|
90
|
+
end
|
91
|
+
|
92
|
+
def down
|
93
|
+
drop(5555)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
]
|
97
|
+
|
98
|
+
ALT_MIGRATION_001 = %[
|
99
|
+
class CreateAltBasic < Sequel::Migration
|
100
|
+
def up
|
101
|
+
create(11111)
|
102
|
+
end
|
103
|
+
|
104
|
+
def down
|
105
|
+
drop(11111)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
]
|
109
|
+
|
110
|
+
ALT_MIGRATION_003 = %[
|
111
|
+
class CreateAltAdvanced < Sequel::Migration
|
112
|
+
def up
|
113
|
+
create(33333)
|
114
|
+
end
|
115
|
+
|
116
|
+
def down
|
117
|
+
drop(33333)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
]
|
121
|
+
|
122
|
+
context "Sequel::Migrator" do
|
123
|
+
before do
|
124
|
+
dbc = Class.new(MockDatabase) do
|
125
|
+
attr_reader :creates, :drops, :tables_created, :columns_created, :versions
|
126
|
+
def initialize(*args)
|
127
|
+
super
|
128
|
+
@creates = []
|
129
|
+
@drops = []
|
130
|
+
@tables_created = []
|
131
|
+
@columns_created = []
|
132
|
+
@versions = {}
|
133
|
+
end
|
134
|
+
|
135
|
+
def create(x); @creates << x; end
|
136
|
+
def drop(x); @drops << x; end
|
137
|
+
|
138
|
+
def create_table(name, opts={}, &block)
|
139
|
+
super
|
140
|
+
@columns_created << / \(?(\w+) integer\)?\z/.match(sqls.last)[1].to_sym
|
141
|
+
@tables_created << name
|
142
|
+
end
|
143
|
+
|
144
|
+
def dataset(opts={})
|
145
|
+
ds = super
|
146
|
+
ds.extend(Module.new do
|
147
|
+
def columns; db.columns_created end
|
148
|
+
def insert(h); db.versions.merge!(h); super(h) end
|
149
|
+
def update(h); db.versions.merge!(h); super(h) end
|
150
|
+
def fetch_rows(sql); db.execute(sql); yield(db.versions) unless db.versions.empty? end
|
151
|
+
end)
|
152
|
+
ds
|
153
|
+
end
|
154
|
+
|
155
|
+
def table_exists?(name)
|
156
|
+
@tables_created.include?(name)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
@db = dbc.new
|
160
|
+
|
161
|
+
@dirname = "migrate_#{$$}"
|
162
|
+
Dir.mkdir(@dirname)
|
163
|
+
File.open("#{@dirname}/001_create_sessions.rb", 'w') {|f| f << MIGRATION_001}
|
164
|
+
File.open("#{@dirname}/002_create_nodes.rb", 'w') {|f| f << MIGRATION_002}
|
165
|
+
File.open("#{@dirname}/003_create_users.rb", 'w') {|f| f << MIGRATION_003}
|
166
|
+
File.open("#{@dirname}/005_5_create_attributes.rb", 'w') {|f| f << MIGRATION_005}
|
167
|
+
|
168
|
+
@alt_dirname = "migrate_alt_#{$$}"
|
169
|
+
Dir.mkdir(@alt_dirname)
|
170
|
+
File.open("#{@alt_dirname}/001_create_alt_basic.rb", 'w') {|f| f << ALT_MIGRATION_001}
|
171
|
+
File.open("#{@alt_dirname}/003_create_alt_advanced.rb", 'w') {|f| f << ALT_MIGRATION_003}
|
172
|
+
end
|
173
|
+
|
174
|
+
after do
|
175
|
+
Object.send(:remove_const, "CreateSessions") if Object.const_defined?("CreateSessions")
|
176
|
+
Object.send(:remove_const, "CreateNodes") if Object.const_defined?("CreateNodes")
|
177
|
+
Object.send(:remove_const, "CreateUsers") if Object.const_defined?("CreateUsers")
|
178
|
+
Object.send(:remove_const, "CreateAttributes") if Object.const_defined?("CreateAttributes")
|
179
|
+
Object.send(:remove_const, "CreateAltBasic") if Object.const_defined?("CreateAltBasic")
|
180
|
+
Object.send(:remove_const, "CreateAltAdvanced") if Object.const_defined?("CreateAltAdvanced")
|
181
|
+
|
182
|
+
File.delete("#{@dirname}/001_create_sessions.rb")
|
183
|
+
File.delete("#{@dirname}/002_create_nodes.rb")
|
184
|
+
File.delete("#{@dirname}/003_create_users.rb")
|
185
|
+
File.delete("#{@dirname}/005_5_create_attributes.rb")
|
186
|
+
Dir.rmdir(@dirname)
|
187
|
+
File.delete("#{@alt_dirname}/001_create_alt_basic.rb")
|
188
|
+
File.delete("#{@alt_dirname}/003_create_alt_advanced.rb")
|
189
|
+
Dir.rmdir(@alt_dirname)
|
190
|
+
end
|
191
|
+
|
192
|
+
specify "#migration_files should return the list of files for a specified version range" do
|
193
|
+
Sequel::Migrator.migration_files(@dirname, 1..1).map{|f| File.basename(f)}.should == ['001_create_sessions.rb']
|
194
|
+
Sequel::Migrator.migration_files(@dirname, 1..3).map{|f| File.basename(f)}.should == ['001_create_sessions.rb', '002_create_nodes.rb', '003_create_users.rb']
|
195
|
+
Sequel::Migrator.migration_files(@dirname, 3..6).map{|f| File.basename(f)}.should == ['003_create_users.rb', '005_5_create_attributes.rb']
|
196
|
+
Sequel::Migrator.migration_files(@dirname, 7..8).map{|f| File.basename(f)}.should == []
|
197
|
+
Sequel::Migrator.migration_files(@alt_dirname, 1..1).map{|f| File.basename(f)}.should == ['001_create_alt_basic.rb']
|
198
|
+
Sequel::Migrator.migration_files(@alt_dirname, 1..3).map{|f| File.basename(f)}.should == ['001_create_alt_basic.rb','003_create_alt_advanced.rb']
|
199
|
+
end
|
200
|
+
|
201
|
+
specify "#latest_migration_version should return the latest version available" do
|
202
|
+
Sequel::Migrator.latest_migration_version(@dirname).should == 5
|
203
|
+
Sequel::Migrator.latest_migration_version(@alt_dirname).should == 3
|
204
|
+
end
|
205
|
+
|
206
|
+
specify "#migration_classes should load the migration classes for the specified range for the up direction" do
|
207
|
+
Sequel::Migrator.migration_classes(@dirname, 3, 0, :up).should == [CreateSessions, CreateNodes, CreateUsers]
|
208
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 3, 0, :up).should == [CreateAltBasic, CreateAltAdvanced]
|
209
|
+
end
|
210
|
+
|
211
|
+
specify "#migration_classes should load the migration classes for the specified range for the down direction" do
|
212
|
+
Sequel::Migrator.migration_classes(@dirname, 0, 5, :down).should == [CreateAttributes, CreateUsers, CreateNodes, CreateSessions]
|
213
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 0, 3, :down).should == [CreateAltAdvanced, CreateAltBasic]
|
214
|
+
end
|
215
|
+
|
216
|
+
specify "#migration_classes should start from current + 1 for the up direction" do
|
217
|
+
Sequel::Migrator.migration_classes(@dirname, 3, 1, :up).should == [CreateNodes, CreateUsers]
|
218
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 3, 2, :up).should == [CreateAltAdvanced]
|
219
|
+
end
|
220
|
+
|
221
|
+
specify "#migration_classes should end on current + 1 for the down direction" do
|
222
|
+
Sequel::Migrator.migration_classes(@dirname, 2, 5, :down).should == [CreateAttributes, CreateUsers]
|
223
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 2, 4, :down).should == [CreateAltAdvanced]
|
224
|
+
end
|
225
|
+
|
226
|
+
specify "#schema_info_dataset should automatically create the schema_info table" do
|
227
|
+
@db.table_exists?(:schema_info).should be_false
|
228
|
+
Sequel::Migrator.schema_info_dataset(@db)
|
229
|
+
@db.table_exists?(:schema_info).should be_true
|
230
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)"]
|
231
|
+
end
|
232
|
+
|
233
|
+
specify "should automatically create new APP_version column in schema_info" do
|
234
|
+
@db.table_exists?(:schema_info).should be_false
|
235
|
+
Sequel::Migrator.schema_info_dataset(@db, :column => :alt_version)
|
236
|
+
@db.table_exists?(:schema_info).should be_true
|
237
|
+
@db.sqls.should == ["CREATE TABLE schema_info (alt_version integer)"]
|
238
|
+
end
|
239
|
+
|
240
|
+
specify "should automatically create new APP_version column in schema_info" do
|
241
|
+
@db.table_exists?(:alt_table).should be_false
|
242
|
+
Sequel::Migrator.schema_info_dataset(@db, :table => :alt_table)
|
243
|
+
@db.table_exists?(:alt_table).should be_true
|
244
|
+
Sequel::Migrator.schema_info_dataset(@db, :table => :alt_table, :column=>:alt_version)
|
245
|
+
@db.sqls.should == ["CREATE TABLE alt_table (version integer)",
|
246
|
+
"ALTER TABLE alt_table ADD COLUMN alt_version integer"]
|
247
|
+
end
|
248
|
+
|
249
|
+
specify "should return a dataset for the correct table" do
|
250
|
+
Sequel::Migrator.schema_info_dataset(@db).first_source_alias.should == :schema_info
|
251
|
+
Sequel::Migrator.schema_info_dataset(@db, :table=>:blah).first_source_alias.should == :blah
|
252
|
+
end
|
253
|
+
|
254
|
+
specify "should assume a migration version of 0 if no migration information exists in the database" do
|
255
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
256
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)", "SELECT * FROM schema_info LIMIT 1"]
|
257
|
+
end
|
258
|
+
specify "should use the migration version stored in the database" do
|
259
|
+
Sequel::Migrator.schema_info_dataset(@db).insert(:version => 4321)
|
260
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 4321
|
261
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)", "INSERT INTO schema_info (version) VALUES (4321)", "SELECT * FROM schema_info LIMIT 1"]
|
262
|
+
end
|
263
|
+
|
264
|
+
specify "should set the migration version stored in the database" do
|
265
|
+
Sequel::Migrator.set_current_migration_version(@db, 6666)
|
266
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 6666
|
267
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
268
|
+
"SELECT * FROM schema_info LIMIT 1",
|
269
|
+
"INSERT INTO schema_info (version) VALUES (6666)",
|
270
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
271
|
+
end
|
272
|
+
|
273
|
+
specify "should apply migrations correctly in the up direction" do
|
274
|
+
Sequel::Migrator.apply(@db, @dirname, 3, 2)
|
275
|
+
@db.creates.should == [3333]
|
276
|
+
|
277
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 3
|
278
|
+
|
279
|
+
Sequel::Migrator.apply(@db, @dirname, 5)
|
280
|
+
@db.creates.should == [3333, 5555]
|
281
|
+
|
282
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
283
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
284
|
+
"SELECT * FROM schema_info LIMIT 1",
|
285
|
+
"INSERT INTO schema_info (version) VALUES (3)",
|
286
|
+
"SELECT * FROM schema_info LIMIT 1",
|
287
|
+
"SELECT * FROM schema_info LIMIT 1",
|
288
|
+
"SELECT * FROM schema_info LIMIT 1",
|
289
|
+
"UPDATE schema_info SET version = 5",
|
290
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
291
|
+
end
|
292
|
+
|
293
|
+
specify "should apply migrations correctly in the down direction" do
|
294
|
+
Sequel::Migrator.apply(@db, @dirname, 1, 5)
|
295
|
+
@db.drops.should == [5555, 3333, 2222]
|
296
|
+
|
297
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 1
|
298
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
299
|
+
"SELECT * FROM schema_info LIMIT 1",
|
300
|
+
"INSERT INTO schema_info (version) VALUES (1)",
|
301
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
302
|
+
end
|
303
|
+
|
304
|
+
specify "should apply migrations up to the latest version if no target is given" do
|
305
|
+
Sequel::Migrator.apply(@db, @dirname)
|
306
|
+
@db.creates.should == [1111, 2222, 3333, 5555]
|
307
|
+
|
308
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
309
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
310
|
+
"SELECT * FROM schema_info LIMIT 1",
|
311
|
+
"SELECT * FROM schema_info LIMIT 1",
|
312
|
+
"INSERT INTO schema_info (version) VALUES (5)",
|
313
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
314
|
+
end
|
315
|
+
|
316
|
+
specify "should apply migrations down to 0 version correctly" do
|
317
|
+
Sequel::Migrator.apply(@db, @dirname, 0, 5)
|
318
|
+
@db.drops.should == [5555, 3333, 2222, 1111]
|
319
|
+
|
320
|
+
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
321
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
322
|
+
"SELECT * FROM schema_info LIMIT 1",
|
323
|
+
"INSERT INTO schema_info (version) VALUES (0)",
|
324
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
325
|
+
end
|
326
|
+
|
327
|
+
specify "should return the target version" do
|
328
|
+
Sequel::Migrator.apply(@db, @dirname, 3, 2).should == 3
|
329
|
+
Sequel::Migrator.apply(@db, @dirname, 0).should == 0
|
330
|
+
Sequel::Migrator.apply(@db, @dirname).should == 5
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
if (begin
|
4
|
+
require 'tzinfo'
|
5
|
+
true
|
6
|
+
rescue LoadError
|
7
|
+
end)
|
8
|
+
|
9
|
+
Sequel.extension :named_timezones
|
10
|
+
Sequel.datetime_class = Time
|
11
|
+
|
12
|
+
describe "Sequel named_timezones extension" do
|
13
|
+
before do
|
14
|
+
@tz_in = TZInfo::Timezone.get('America/Los_Angeles')
|
15
|
+
@tz_out = TZInfo::Timezone.get('America/New_York')
|
16
|
+
@db = MockDatabase.new
|
17
|
+
@dt = DateTime.civil(2009,6,1,10,20,30,0)
|
18
|
+
Sequel.application_timezone = 'America/Los_Angeles'
|
19
|
+
Sequel.database_timezone = 'America/New_York'
|
20
|
+
Sequel.datetime_class = DateTime
|
21
|
+
end
|
22
|
+
after do
|
23
|
+
Sequel.default_timezone = nil
|
24
|
+
Sequel.datetime_class = Time
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should convert string arguments to *_timezone= to TZInfo::Timezone instances" do
|
28
|
+
Sequel.application_timezone.should == @tz_in
|
29
|
+
Sequel.database_timezone.should == @tz_out
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should accept TZInfo::Timezone instances in *_timezone=" do
|
33
|
+
Sequel.application_timezone = @tz_in
|
34
|
+
Sequel.database_timezone = @tz_out
|
35
|
+
Sequel.application_timezone.should == @tz_in
|
36
|
+
Sequel.database_timezone.should == @tz_out
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should convert datetimes going into the database to named database_timezone" do
|
40
|
+
ds = @db[:a]
|
41
|
+
def ds.supports_timestamp_timezones?; true; end
|
42
|
+
def ds.supports_timestamp_usecs?; false; end
|
43
|
+
ds.insert([@dt, DateTime.civil(2009,6,1,3,20,30,Rational(-7, 24)), DateTime.civil(2009,6,1,6,20,30,Rational(-1, 6))])
|
44
|
+
@db.sqls.should == ["INSERT INTO a VALUES ('2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400')"]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should convert datetimes coming out of the database from database_timezone to application_timezone" do
|
48
|
+
dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30-0400')
|
49
|
+
dt.should == @dt
|
50
|
+
dt.offset.should == Rational(-7, 24)
|
51
|
+
|
52
|
+
dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30+0000')
|
53
|
+
dt.should == @dt
|
54
|
+
dt.offset.should == Rational(-7, 24)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
|
58
|
+
dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
|
59
|
+
dt.should == @dt
|
60
|
+
dt.offset.should == Rational(-7, 24)
|
61
|
+
|
62
|
+
dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30')
|
63
|
+
dt.should == @dt + Rational(1, 6)
|
64
|
+
dt.offset.should == Rational(-7, 24)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should work with the thread_local_timezones extension" do
|
68
|
+
[Thread.new{Sequel.thread_application_timezone = 'America/New_York'; sleep 0.03; Sequel.application_timezone.should == @tz_out},
|
69
|
+
Thread.new{sleep 0.01; Sequel.thread_application_timezone = 'America/Los_Angeles'; sleep 0.01; Sequel.application_timezone.should == @tz_in}].each{|x| x.join}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,396 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "NestedAttributes plugin" do
|
4
|
+
before do
|
5
|
+
mods = @mods = []
|
6
|
+
i = 0
|
7
|
+
gi = lambda{i}
|
8
|
+
ii = lambda{i+=1}
|
9
|
+
ds_mod = Module.new do
|
10
|
+
def empty?; false; end
|
11
|
+
define_method(:insert) do |h|
|
12
|
+
x = ii.call
|
13
|
+
mods << [:i, first_source, h, x]
|
14
|
+
x
|
15
|
+
end
|
16
|
+
define_method(:insert_select) do |h|
|
17
|
+
x = ii.call
|
18
|
+
mods << [:is, first_source, h, x]
|
19
|
+
h.merge(:id=>x)
|
20
|
+
end
|
21
|
+
define_method(:update) do |h|
|
22
|
+
mods << [:u, first_source, h, literal(opts[:where])]
|
23
|
+
1
|
24
|
+
end
|
25
|
+
define_method(:delete) do
|
26
|
+
mods << [:d, first_source, literal(opts[:where])]
|
27
|
+
1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
db = Sequel::Database.new({})
|
31
|
+
db.meta_def(:dataset) do |*a|
|
32
|
+
x = super(*a)
|
33
|
+
x.extend(ds_mod)
|
34
|
+
x
|
35
|
+
end
|
36
|
+
@c = Class.new(Sequel::Model(db))
|
37
|
+
@c.plugin :nested_attributes
|
38
|
+
@Artist = Class.new(@c).set_dataset(:artists)
|
39
|
+
@Album = Class.new(@c).set_dataset(:albums)
|
40
|
+
@Tag = Class.new(@c).set_dataset(:tags)
|
41
|
+
[@Artist, @Album, @Tag].each do |m|
|
42
|
+
m.dataset.extend(ds_mod)
|
43
|
+
end
|
44
|
+
@Artist.columns :id, :name
|
45
|
+
@Album.columns :id, :name, :artist_id
|
46
|
+
@Tag.columns :id, :name
|
47
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
48
|
+
@Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id
|
49
|
+
@Album.many_to_one :artist, :class=>@Artist
|
50
|
+
@Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
|
51
|
+
@Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
|
52
|
+
@Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should support creating new many_to_one objects" do
|
56
|
+
a = @Album.new({:name=>'Al', :artist_attributes=>{:name=>'Ar'}})
|
57
|
+
@mods.should == []
|
58
|
+
a.save
|
59
|
+
@mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should support creating new one_to_one objects" do
|
63
|
+
a = @Artist.new(:name=>'Ar')
|
64
|
+
a.id = 1
|
65
|
+
a.first_album_attributes = {:name=>'Al'}
|
66
|
+
@mods.should == []
|
67
|
+
a.save
|
68
|
+
@mods.should == [[:is, :artists, {:name=>"Ar", :id=>1}, 1], [:is, :albums, {:name=>"Al"}, 2], [:u, :albums, {:artist_id=>nil}, "((artist_id = 1) AND (id != 2))"], [:u, :albums, {:name=>"Al", :artist_id=>1}, "(id = 2)"]]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should support creating new one_to_many objects" do
|
72
|
+
a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
|
73
|
+
@mods.should == []
|
74
|
+
a.save
|
75
|
+
@mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should support creating new many_to_many objects" do
|
79
|
+
a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
|
80
|
+
@mods.should == []
|
81
|
+
a.save
|
82
|
+
@mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3]]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should add new objects to the cached association array as soon as the *_attributes= method is called" do
|
86
|
+
a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al', :tags_attributes=>[{:name=>'T'}]}]})
|
87
|
+
a.albums.should == [@Album.new(:name=>'Al')]
|
88
|
+
a.albums.first.tags.should == [@Tag.new(:name=>'T')]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should support updating many_to_one objects" do
|
92
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
93
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
94
|
+
al.associations[:artist] = ar
|
95
|
+
al.set(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
|
96
|
+
@mods.should == []
|
97
|
+
al.save
|
98
|
+
@mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should support updating one_to_one objects" do
|
102
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
103
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
104
|
+
ar.associations[:first_album] = al
|
105
|
+
ar.set(:first_album_attributes=>{:id=>10, :name=>'Al2'})
|
106
|
+
@mods.should == []
|
107
|
+
ar.save
|
108
|
+
@mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should support updating one_to_many objects" do
|
112
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
113
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
114
|
+
ar.associations[:albums] = [al]
|
115
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2'}])
|
116
|
+
@mods.should == []
|
117
|
+
ar.save
|
118
|
+
@mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should support updating many_to_many objects" do
|
122
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
123
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
124
|
+
a.associations[:tags] = [t]
|
125
|
+
a.set(:tags_attributes=>[{:id=>20, :name=>'T2'}])
|
126
|
+
@mods.should == []
|
127
|
+
a.save
|
128
|
+
@mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :tags, {:name=>"T2"}, '(id = 20)']]
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should support removing many_to_one objects" do
|
132
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
133
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
134
|
+
al.associations[:artist] = ar
|
135
|
+
al.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
|
136
|
+
@mods.should == []
|
137
|
+
al.save
|
138
|
+
@mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)']]
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should support removing one_to_one objects" do
|
142
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
143
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
144
|
+
ar.associations[:first_album] = al
|
145
|
+
ar.set(:first_album_attributes=>{:id=>10, :_remove=>'t'})
|
146
|
+
@mods.should == []
|
147
|
+
ar.save
|
148
|
+
@mods.should == [[:u, :albums, {:artist_id=>nil}, "(artist_id = 20)"], [:u, :artists, {:name=>"Ar"}, "(id = 20)"]]
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should support removing one_to_many objects" do
|
153
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
154
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
155
|
+
ar.associations[:albums] = [al]
|
156
|
+
ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
|
157
|
+
@mods.should == []
|
158
|
+
ar.save
|
159
|
+
@mods.should == [[:u, :albums, {:name=>"Al", :artist_id=>nil}, '(id = 10)'], [:u, :artists, {:name=>"Ar"}, '(id = 20)']]
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should support removing many_to_many objects" do
|
163
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
164
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
165
|
+
a.associations[:tags] = [t]
|
166
|
+
a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
|
167
|
+
@mods.should == []
|
168
|
+
a.save
|
169
|
+
@mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)']]
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should support destroying many_to_one objects" do
|
173
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
174
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
175
|
+
al.associations[:artist] = ar
|
176
|
+
al.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
|
177
|
+
@mods.should == []
|
178
|
+
al.save
|
179
|
+
@mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)'], [:d, :artists, '(id = 20)']]
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should support destroying one_to_one objects" do
|
183
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
184
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
185
|
+
ar.associations[:first_album] = al
|
186
|
+
ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
|
187
|
+
@mods.should == []
|
188
|
+
ar.save
|
189
|
+
@mods.should == [[:u, :albums, {:artist_id=>nil}, "(artist_id = 20)"], [:u, :artists, {:name=>"Ar"}, "(id = 20)"], [:d, :albums, "(id = 10)"]]
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should support destroying one_to_many objects" do
|
193
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
194
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
195
|
+
ar.associations[:albums] = [al]
|
196
|
+
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
197
|
+
@mods.should == []
|
198
|
+
ar.save
|
199
|
+
@mods.should == [[:u, :albums, {:name=>"Al", :artist_id=>nil}, '(id = 10)'], [:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:d, :albums, '(id = 10)']]
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should support destroying many_to_many objects" do
|
203
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
204
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
205
|
+
a.associations[:tags] = [t]
|
206
|
+
a.set(:tags_attributes=>[{:id=>20, :_delete=>true}])
|
207
|
+
@mods.should == []
|
208
|
+
a.save
|
209
|
+
@mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should support both string and symbol keys in nested attribute hashes" do
|
213
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
214
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
215
|
+
a.associations[:tags] = [t]
|
216
|
+
a.set('tags_attributes'=>[{'id'=>20, '_delete'=>true}])
|
217
|
+
@mods.should == []
|
218
|
+
a.save
|
219
|
+
@mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should support using a hash instead of an array for to_many nested attributes" do
|
223
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
224
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
225
|
+
a.associations[:tags] = [t]
|
226
|
+
a.set('tags_attributes'=>{'1'=>{'id'=>20, '_delete'=>true}})
|
227
|
+
@mods.should == []
|
228
|
+
a.save
|
229
|
+
@mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should only allow destroying associated objects if :destroy option is used in the nested_attributes call" do
|
233
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
234
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
235
|
+
a.associations[:artist] = ar
|
236
|
+
@Album.nested_attributes :artist
|
237
|
+
proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should raise_error(Sequel::Error)
|
238
|
+
@Album.nested_attributes :artist, :destroy=>true
|
239
|
+
proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error(Sequel::Error)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should only allow removing associated objects if :remove option is used in the nested_attributes call" do
|
243
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
244
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
245
|
+
a.associations[:artist] = ar
|
246
|
+
@Album.nested_attributes :artist
|
247
|
+
proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should raise_error(Sequel::Error)
|
248
|
+
@Album.nested_attributes :artist, :remove=>true
|
249
|
+
proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error(Sequel::Error)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should raise an Error if a primary key is given in a nested attribute hash, but no matching associated object exists" do
|
253
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
254
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
255
|
+
ar.associations[:albums] = [al]
|
256
|
+
proc{ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])}.should raise_error(Sequel::Error)
|
257
|
+
proc{ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should not raise an Error if an unmatched primary key is given, if the :strict=>false option is used" do
|
261
|
+
@Artist.nested_attributes :albums, :strict=>false
|
262
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
263
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
264
|
+
ar.associations[:albums] = [al]
|
265
|
+
ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
|
266
|
+
@mods.should == []
|
267
|
+
ar.save
|
268
|
+
@mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)']]
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should not save if nested attribute is not valid and should include nested attribute validation errors in the main object's validation errors" do
|
272
|
+
@Artist.class_eval do
|
273
|
+
def validate
|
274
|
+
super
|
275
|
+
errors.add(:name, 'cannot be Ar') if name == 'Ar'
|
276
|
+
end
|
277
|
+
end
|
278
|
+
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
279
|
+
@mods.should == []
|
280
|
+
proc{a.save}.should raise_error(Sequel::ValidationFailed)
|
281
|
+
a.errors.full_messages.should == ['artist name cannot be Ar']
|
282
|
+
@mods.should == []
|
283
|
+
# Should preserve attributes
|
284
|
+
a.artist.name.should == 'Ar'
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should not attempt to validate nested attributes if the :validate=>false association option is used" do
|
288
|
+
@Album.many_to_one :artist, :class=>@Artist, :validate=>false
|
289
|
+
@Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
|
290
|
+
@Artist.class_eval do
|
291
|
+
def validate
|
292
|
+
super
|
293
|
+
errors.add(:name, 'cannot be Ar') if name == 'Ar'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
297
|
+
@mods.should == []
|
298
|
+
a.save
|
299
|
+
@mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should not attempt to validate nested attributes if the :validate=>false option is passed to save" do
|
303
|
+
@Artist.class_eval do
|
304
|
+
def validate
|
305
|
+
super
|
306
|
+
errors.add(:name, 'cannot be Ar') if name == 'Ar'
|
307
|
+
end
|
308
|
+
end
|
309
|
+
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
310
|
+
@mods.should == []
|
311
|
+
a.save(:validate=>false)
|
312
|
+
@mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should not accept nested attributes unless explicitly specified" do
|
316
|
+
@Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
|
317
|
+
proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::Error)
|
318
|
+
@mods.should == []
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should save when save_changes or update is called if nested attribute associated objects changed but there are no changes to the main object" do
|
322
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
323
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
324
|
+
al.associations[:artist] = ar
|
325
|
+
al.update(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
|
326
|
+
@mods.should == [[:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should have a :limit option limiting the amount of entries" do
|
330
|
+
@Album.nested_attributes :tags, :limit=>2
|
331
|
+
arr = [{:name=>'T'}]
|
332
|
+
proc{@Album.new({:name=>'Al', :tags_attributes=>arr*3})}.should raise_error(Sequel::Error)
|
333
|
+
a = @Album.new({:name=>'Al', :tags_attributes=>arr*2})
|
334
|
+
@mods.should == []
|
335
|
+
a.save
|
336
|
+
@mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should accept a block that each hash gets passed to determine if it should be processed" do
|
340
|
+
@Album.nested_attributes(:tags){|h| h[:name].empty?}
|
341
|
+
a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}, {:name=>''}, {:name=>'T2'}]})
|
342
|
+
@mods.should == []
|
343
|
+
a.save
|
344
|
+
@mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T2"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should return objects created/modified in the internal methods" do
|
348
|
+
@Album.nested_attributes :tags, :remove=>true, :strict=>false
|
349
|
+
objs = []
|
350
|
+
@Album.class_eval do
|
351
|
+
define_method(:nested_attributes_create){|*a| objs << [super(*a), :create]}
|
352
|
+
define_method(:nested_attributes_remove){|*a| objs << [super(*a), :remove]}
|
353
|
+
define_method(:nested_attributes_update){|*a| objs << [super(*a), :update]}
|
354
|
+
end
|
355
|
+
a = @Album.new(:name=>'Al')
|
356
|
+
a.associations[:tags] = [@Tag.load(:id=>6, :name=>'A'), @Tag.load(:id=>7, :name=>'A2')]
|
357
|
+
a.tags_attributes = [{:id=>6, :name=>'T'}, {:id=>7, :name=>'T2', :_remove=>true}, {:name=>'T3'}, {:id=>8, :name=>'T4'}, {:id=>9, :name=>'T5', :_remove=>true}]
|
358
|
+
objs.should == [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create], [nil, :update], [nil, :remove]]
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should raise an error if updating modifies the associated objects keys" do
|
362
|
+
@Artist.columns :id, :name, :artist_id
|
363
|
+
@Album.columns :id, :name, :artist_id
|
364
|
+
@Tag.columns :id, :name, :tag_id
|
365
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :primary_key=>:artist_id
|
366
|
+
@Album.many_to_one :artist, :class=>@Artist, :primary_key=>:artist_id
|
367
|
+
@Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at, :right_primary_key=>:tag_id
|
368
|
+
@Artist.nested_attributes :albums, :destroy=>true, :remove=>true
|
369
|
+
@Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
|
370
|
+
|
371
|
+
al = @Album.load(:id=>10, :name=>'Al', :artist_id=>25)
|
372
|
+
ar = @Artist.load(:id=>20, :name=>'Ar', :artist_id=>25)
|
373
|
+
t = @Tag.load(:id=>30, :name=>'T', :tag_id=>15)
|
374
|
+
al.associations[:artist] = ar
|
375
|
+
al.associations[:tags] = [t]
|
376
|
+
ar.associations[:albums] = [al]
|
377
|
+
proc{ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :artist_id=>'3'}])}.should raise_error(Sequel::Error)
|
378
|
+
proc{al.set(:artist_attributes=>{:id=>20, :name=>'Ar2', :artist_id=>'3'})}.should raise_error(Sequel::Error)
|
379
|
+
proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :tag_id=>'3'}])}.should raise_error(Sequel::Error)
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should accept a :fields option and only allow modification of those fields" do
|
383
|
+
@Tag.columns :id, :name, :number
|
384
|
+
@Album.nested_attributes :tags, :destroy=>true, :remove=>true, :fields=>[:name]
|
385
|
+
|
386
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
387
|
+
t = @Tag.load(:id=>30, :name=>'T', :number=>10)
|
388
|
+
al.associations[:tags] = [t]
|
389
|
+
al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
|
390
|
+
@mods.should == []
|
391
|
+
al.save
|
392
|
+
@mods.should == [[:u, :albums, {:name=>'Al'}, '(id = 10)'], [:u, :tags, {:name=>'T2'}, '(id = 30)'], [:is, :tags, {:name=>"T3"}, 1], [:i, :at, {:album_id=>10, :tag_id=>1}, 2]]
|
393
|
+
proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
394
|
+
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
395
|
+
end
|
396
|
+
end
|