sequel 4.13.0 → 4.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/doc/active_record.rdoc +4 -4
- data/doc/advanced_associations.rdoc +2 -2
- data/doc/association_basics.rdoc +11 -11
- data/doc/cheat_sheet.rdoc +7 -7
- data/doc/core_extensions.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/extensions.rdoc +1 -1
- data/doc/migration.rdoc +3 -3
- data/doc/model_hooks.rdoc +1 -1
- data/doc/opening_databases.rdoc +4 -0
- data/doc/postgresql.rdoc +2 -2
- data/doc/prepared_statements.rdoc +1 -1
- data/doc/querying.rdoc +31 -31
- data/doc/release_notes/4.13.0.txt +1 -1
- data/doc/release_notes/4.14.0.txt +68 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/sharding.rdoc +2 -2
- data/doc/sql.rdoc +1 -1
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +4 -0
- data/lib/sequel/adapters/mysql.rb +18 -18
- data/lib/sequel/adapters/mysql2.rb +7 -7
- data/lib/sequel/adapters/shared/mysql.rb +15 -5
- data/lib/sequel/adapters/shared/postgres.rb +71 -58
- data/lib/sequel/adapters/shared/sqlite.rb +2 -2
- data/lib/sequel/ast_transformer.rb +1 -1
- data/lib/sequel/connection_pool/sharded_single.rb +8 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +8 -8
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -0
- data/lib/sequel/database/schema_methods.rb +8 -7
- data/lib/sequel/database/transactions.rb +1 -2
- data/lib/sequel/dataset/actions.rb +4 -4
- data/lib/sequel/dataset/graph.rb +4 -0
- data/lib/sequel/dataset/query.rb +18 -18
- data/lib/sequel/dataset/sql.rb +3 -3
- data/lib/sequel/extensions/_pretty_table.rb +1 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +3 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -0
- data/lib/sequel/extensions/connection_validator.rb +1 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -0
- data/lib/sequel/extensions/current_datetime_timestamp.rb +1 -0
- data/lib/sequel/extensions/dataset_source_alias.rb +1 -0
- data/lib/sequel/extensions/date_arithmetic.rb +1 -0
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +1 -0
- data/lib/sequel/extensions/error_sql.rb +1 -0
- data/lib/sequel/extensions/eval_inspect.rb +1 -0
- data/lib/sequel/extensions/filter_having.rb +1 -0
- data/lib/sequel/extensions/from_block.rb +1 -0
- data/lib/sequel/extensions/graph_each.rb +1 -0
- data/lib/sequel/extensions/hash_aliases.rb +1 -0
- data/lib/sequel/extensions/looser_typecasting.rb +1 -0
- data/lib/sequel/extensions/meta_def.rb +1 -0
- data/lib/sequel/extensions/migration.rb +1 -0
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +1 -0
- data/lib/sequel/extensions/named_timezones.rb +1 -0
- data/lib/sequel/extensions/null_dataset.rb +1 -0
- data/lib/sequel/extensions/pagination.rb +1 -0
- data/lib/sequel/extensions/pg_array.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +1 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +1 -0
- data/lib/sequel/extensions/pg_json_ops.rb +1 -0
- data/lib/sequel/extensions/pg_loose_count.rb +1 -0
- data/lib/sequel/extensions/pg_range_ops.rb +1 -0
- data/lib/sequel/extensions/pg_row_ops.rb +1 -0
- data/lib/sequel/extensions/pg_static_cache_updater.rb +1 -0
- data/lib/sequel/extensions/pretty_table.rb +1 -0
- data/lib/sequel/extensions/query.rb +1 -0
- data/lib/sequel/extensions/query_literals.rb +1 -0
- data/lib/sequel/extensions/schema_caching.rb +1 -0
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/select_remove.rb +1 -0
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +1 -0
- data/lib/sequel/extensions/server_block.rb +1 -0
- data/lib/sequel/extensions/set_overrides.rb +1 -0
- data/lib/sequel/extensions/split_array_nil.rb +1 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -0
- data/lib/sequel/extensions/to_dot.rb +1 -0
- data/lib/sequel/model/associations.rb +5 -5
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/caching.rb +8 -3
- data/lib/sequel/plugins/class_table_inheritance.rb +20 -11
- data/lib/sequel/plugins/composition.rb +18 -18
- data/lib/sequel/plugins/json_serializer.rb +3 -3
- data/lib/sequel/plugins/lazy_attributes.rb +23 -9
- data/lib/sequel/plugins/many_through_many.rb +21 -21
- data/lib/sequel/plugins/nested_attributes.rb +7 -3
- data/lib/sequel/plugins/pg_row.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +13 -13
- data/lib/sequel/plugins/sharding.rb +6 -6
- data/lib/sequel/plugins/timestamps.rb +4 -4
- data/lib/sequel/plugins/touch.rb +7 -7
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +36 -36
- data/lib/sequel/plugins/validation_helpers.rb +3 -4
- data/lib/sequel/plugins/xml_serializer.rb +29 -29
- data/lib/sequel/sql.rb +22 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +10 -0
- data/spec/core/database_spec.rb +3 -4
- data/spec/core/dataset_spec.rb +16 -1
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/core/object_graph_spec.rb +5 -0
- data/spec/core/placeholder_literalizer_spec.rb +8 -0
- data/spec/extensions/caching_spec.rb +18 -0
- data/spec/extensions/class_table_inheritance_spec.rb +34 -0
- data/spec/extensions/many_through_many_spec.rb +4 -0
- data/spec/extensions/nested_attributes_spec.rb +59 -4
- data/spec/extensions/pg_array_associations_spec.rb +5 -0
- data/spec/extensions/single_table_inheritance_spec.rb +23 -1
- data/spec/integration/plugin_test.rb +17 -0
- data/spec/model/eager_loading_spec.rb +8 -0
- metadata +4 -2
@@ -12,50 +12,50 @@ module Sequel
|
|
12
12
|
# album = Album[1]
|
13
13
|
# puts album.to_xml
|
14
14
|
# # Output:
|
15
|
-
# <?xml version="1.0"?>
|
16
|
-
# <album>
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# </album>
|
15
|
+
# # <?xml version="1.0"?>
|
16
|
+
# # <album>
|
17
|
+
# # <id>1</id>
|
18
|
+
# # <name>RF</name>
|
19
|
+
# # <artist_id>2</artist_id>
|
20
|
+
# # </album>
|
21
21
|
#
|
22
22
|
# You can provide options to control the XML output:
|
23
23
|
#
|
24
24
|
# puts album.to_xml(:only=>:name)
|
25
25
|
# puts album.to_xml(:except=>[:id, :artist_id])
|
26
26
|
# # Output:
|
27
|
-
# <?xml version="1.0"?>
|
28
|
-
# <album>
|
29
|
-
#
|
30
|
-
# </album>
|
27
|
+
# # <?xml version="1.0"?>
|
28
|
+
# # <album>
|
29
|
+
# # <name>RF</name>
|
30
|
+
# # </album>
|
31
31
|
#
|
32
32
|
# album.to_xml(:include=>:artist)
|
33
33
|
# # Output:
|
34
|
-
# <?xml version="1.0"?>
|
35
|
-
# <album>
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# </album>
|
34
|
+
# # <?xml version="1.0"?>
|
35
|
+
# # <album>
|
36
|
+
# # <id>1</id>
|
37
|
+
# # <name>RF</name>
|
38
|
+
# # <artist_id>2</artist_id>
|
39
|
+
# # <artist>
|
40
|
+
# # <id>2</id>
|
41
|
+
# # <name>YJM</name>
|
42
|
+
# # </artist>
|
43
|
+
# # </album>
|
44
44
|
#
|
45
45
|
# You can use a hash value with <tt>:include</tt> to pass options
|
46
46
|
# to associations:
|
47
47
|
#
|
48
48
|
# album.to_xml(:include=>{:artist=>{:only=>:name}})
|
49
49
|
# # Output:
|
50
|
-
# <?xml version="1.0"?>
|
51
|
-
# <album>
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# </album>
|
50
|
+
# # <?xml version="1.0"?>
|
51
|
+
# # <album>
|
52
|
+
# # <id>1</id>
|
53
|
+
# # <name>RF</name>
|
54
|
+
# # <artist_id>2</artist_id>
|
55
|
+
# # <artist>
|
56
|
+
# # <name>YJM</name>
|
57
|
+
# # </artist>
|
58
|
+
# # </album>
|
59
59
|
#
|
60
60
|
# +to_xml+ also exists as a class and dataset method, both
|
61
61
|
# of which return all objects in the dataset:
|
data/lib/sequel/sql.rb
CHANGED
@@ -909,7 +909,7 @@ module Sequel
|
|
909
909
|
# Return a +StringExpression+ representing the concatenation of the receiver
|
910
910
|
# with the given argument.
|
911
911
|
#
|
912
|
-
# :x.sql_string + :y =>
|
912
|
+
# :x.sql_string + :y # => "x" || "y"
|
913
913
|
def +(ce)
|
914
914
|
StringExpression.new(:'||', self, ce)
|
915
915
|
end
|
@@ -984,14 +984,14 @@ module Sequel
|
|
984
984
|
# and converts it to a +BooleanExpression+. The operator and args
|
985
985
|
# used depends on the case of the right (2nd) argument:
|
986
986
|
#
|
987
|
-
#
|
988
|
-
#
|
989
|
-
#
|
990
|
-
#
|
991
|
-
#
|
992
|
-
#
|
993
|
-
#
|
994
|
-
#
|
987
|
+
# 0..10 :: left >= 0 AND left <= 10
|
988
|
+
# [1,2] :: left IN (1,2)
|
989
|
+
# nil :: left IS NULL
|
990
|
+
# true :: left IS TRUE
|
991
|
+
# false :: left IS FALSE
|
992
|
+
# /as/ :: left ~ 'as'
|
993
|
+
# :blah :: left = blah
|
994
|
+
# 'blah' :: left = 'blah'
|
995
995
|
#
|
996
996
|
# If multiple arguments are given, they are joined with the op given (AND
|
997
997
|
# by default, OR possible). If negate is set to true,
|
@@ -1022,7 +1022,7 @@ module Sequel
|
|
1022
1022
|
when Regexp
|
1023
1023
|
StringExpression.like(l, r)
|
1024
1024
|
when DelayedEvaluation
|
1025
|
-
Sequel.delay{from_value_pair(l, r.
|
1025
|
+
Sequel.delay{|ds| from_value_pair(l, r.call(ds))}
|
1026
1026
|
when Dataset::PlaceholderLiteralizer::Argument
|
1027
1027
|
r.transform{|v| from_value_pair(l, v)}
|
1028
1028
|
else
|
@@ -1220,7 +1220,18 @@ module Sequel
|
|
1220
1220
|
@callable = callable
|
1221
1221
|
end
|
1222
1222
|
|
1223
|
-
|
1223
|
+
# Call the underlying callable and return the result. If the
|
1224
|
+
# underlying callable only accepts a single argument, call it
|
1225
|
+
# with the given dataset.
|
1226
|
+
def call(ds)
|
1227
|
+
if @callable.respond_to?(:arity) && @callable.arity == 1
|
1228
|
+
@callable.call(ds)
|
1229
|
+
else
|
1230
|
+
@callable.call
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
to_s_method :delayed_evaluation_sql
|
1224
1235
|
end
|
1225
1236
|
|
1226
1237
|
# Represents an SQL function call.
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 4
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 14
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
@@ -1533,6 +1533,16 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
1533
1533
|
@d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE'
|
1534
1534
|
# Make sure if exists works
|
1535
1535
|
@d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
|
1536
|
+
|
1537
|
+
if @d.supports_trigger_conditions?
|
1538
|
+
@d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true, :when=> {:new__name => 'b'}).should == %q{CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW WHEN ("new"."name" = 'b') EXECUTE PROCEDURE tf()}
|
1539
|
+
@d.create_trigger(:test, :identity, :tf, :each_row=>true, :events => :update, :when=> {:new__name => 'b'})
|
1540
|
+
proc{@d[:test].filter(:name=>'a').update(:value=>nil)}.should_not raise_error
|
1541
|
+
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>nil}]
|
1542
|
+
proc{@d[:test].filter(:name=>'a').update(:name=>'b')}.should raise_error(Sequel::DatabaseError)
|
1543
|
+
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>nil}]
|
1544
|
+
@d.drop_trigger(:test, :identity)
|
1545
|
+
end
|
1536
1546
|
end
|
1537
1547
|
end
|
1538
1548
|
|
data/spec/core/database_spec.rb
CHANGED
@@ -720,16 +720,15 @@ shared_examples_for "Database#transaction" do
|
|
720
720
|
@db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'ROLLBACK']
|
721
721
|
end
|
722
722
|
|
723
|
-
specify "should
|
723
|
+
specify "should raise original exception if there is an exception raised when rolling back" do
|
724
724
|
ec = Class.new(StandardError)
|
725
725
|
meta_def(@db, :database_error_classes){[ec]}
|
726
726
|
meta_def(@db, :log_connection_execute){|c, sql| sql =~ /ROLLBACK/ ? raise(ec, 'bad') : super(c, sql)}
|
727
727
|
begin
|
728
728
|
@db.transaction{raise ArgumentError, 'asdf'}
|
729
|
-
rescue
|
729
|
+
rescue => e
|
730
730
|
end
|
731
|
-
e.
|
732
|
-
e.wrapped_exception.should be_a_kind_of(ec)
|
731
|
+
e.should be_a_kind_of(ArgumentError)
|
733
732
|
@db.sqls.should == ['BEGIN']
|
734
733
|
end
|
735
734
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -593,8 +593,18 @@ describe "Dataset#where" do
|
|
593
593
|
"SELECT * FROM test WHERE (((name < 'b') AND (table.id = 1)) OR is_active(blah, xx, x.y_z))"
|
594
594
|
end
|
595
595
|
|
596
|
-
specify "should
|
596
|
+
specify "should handle arbitrary objects" do
|
597
|
+
o = Object.new
|
598
|
+
def o.sql_literal(ds)
|
599
|
+
"foo"
|
600
|
+
end
|
601
|
+
@dataset.filter(o).sql.should == 'SELECT * FROM test WHERE foo'
|
602
|
+
end
|
603
|
+
|
604
|
+
specify "should raise an error if an numeric is used" do
|
597
605
|
proc{@dataset.filter(1)}.should raise_error(Sequel::Error)
|
606
|
+
proc{@dataset.filter(1.0)}.should raise_error(Sequel::Error)
|
607
|
+
proc{@dataset.filter(BigDecimal.new('1.0'))}.should raise_error(Sequel::Error)
|
598
608
|
end
|
599
609
|
|
600
610
|
specify "should raise an error if a NumericExpression or StringExpression is used" do
|
@@ -3707,6 +3717,11 @@ describe "Sequel::Dataset#qualify" do
|
|
3707
3717
|
ds.sql.should == 'SELECT t.* FROM t WHERE t.b'
|
3708
3718
|
end
|
3709
3719
|
|
3720
|
+
specify "should handle SQL::DelayedEvaluations that take dataset arguments" do
|
3721
|
+
ds = @ds.filter(Sequel.delay{|ds| ds.first_source}).qualify
|
3722
|
+
ds.sql.should == 'SELECT t.* FROM t WHERE t.t'
|
3723
|
+
end
|
3724
|
+
|
3710
3725
|
specify "should handle all other objects by returning them unchanged" do
|
3711
3726
|
@ds.select("a").filter{a(3)}.filter('blah').order(Sequel.lit('true')).group(Sequel.lit('a > ?', 1)).having(false).qualify.sql.should == "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
|
3712
3727
|
end
|
@@ -1116,6 +1116,12 @@ describe "Sequel.delay" do
|
|
1116
1116
|
@o._a.should == 2
|
1117
1117
|
end
|
1118
1118
|
|
1119
|
+
specify "should call the block with the current dataset if it accepts one argument" do
|
1120
|
+
ds = Sequel.mock[:b].where(Sequel.delay{|ds| ds.first_source})
|
1121
|
+
ds.sql.should == "SELECT * FROM b WHERE b"
|
1122
|
+
ds.from(:c).sql.should == "SELECT * FROM c WHERE c"
|
1123
|
+
end
|
1124
|
+
|
1119
1125
|
specify "should have the condition specifier handling respect delayed evaluations" do
|
1120
1126
|
ds = Sequel.mock[:b].where(:a=>Sequel.delay{@o.b})
|
1121
1127
|
ds.sql.should == "SELECT * FROM b WHERE (a IS NULL)"
|
@@ -1125,6 +1131,12 @@ describe "Sequel.delay" do
|
|
1125
1131
|
ds.sql.should == "SELECT * FROM b WHERE (a IN (1, 2))"
|
1126
1132
|
end
|
1127
1133
|
|
1134
|
+
specify "should have the condition specifier handling call block with the current dataset if it accepts one argument" do
|
1135
|
+
ds = Sequel.mock[:b].where(:a=>Sequel.delay{|ds| ds.first_source})
|
1136
|
+
ds.sql.should == "SELECT * FROM b WHERE (a = b)"
|
1137
|
+
ds.from(:c).sql.should == "SELECT * FROM c WHERE (a = c)"
|
1138
|
+
end
|
1139
|
+
|
1128
1140
|
specify "should raise if called without a block" do
|
1129
1141
|
proc{Sequel.delay}.should raise_error(Sequel::Error)
|
1130
1142
|
end
|
@@ -171,6 +171,11 @@ describe Sequel::Dataset, "graphing" do
|
|
171
171
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points INNER JOIN lines ON (lines.x = points.id)'
|
172
172
|
end
|
173
173
|
|
174
|
+
it "should accept a :join_only option" do
|
175
|
+
ds = @ds1.graph(:lines, {:x=>:id}, :join_only=>true)
|
176
|
+
ds.sql.should == 'SELECT * FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
177
|
+
end
|
178
|
+
|
174
179
|
it "should not select any columns from the graphed table if :select option is false" do
|
175
180
|
ds = @ds1.graph(:lines, {:x=>:id}, :select=>false).graph(:graphs, :id=>:graph_id)
|
176
181
|
ds.sql.should == 'SELECT points.id, points.x, points.y, graphs.id AS graphs_id, graphs.name, graphs.x AS graphs_x, graphs.y AS graphs_y, graphs.lines_x FROM points LEFT OUTER JOIN lines ON (lines.x = points.id) LEFT OUTER JOIN graphs ON (graphs.id = lines.graph_id)'
|
@@ -41,6 +41,14 @@ describe "Dataset::PlaceholderLiteralizer" do
|
|
41
41
|
@db.sqls.should == ["SELECT s FROM items WHERE ((a = 1) AND (b = (c + 1)) AND (id = 1)) HAVING h", "SELECT s2 FROM items WHERE ((a = 2) AND (b = (d + 1)) AND (id = 2)) HAVING h2"]
|
42
42
|
end
|
43
43
|
|
44
|
+
specify "should handle calls with placeholders and delayed arguments that take dataset argument" do
|
45
|
+
d = @ds.select(Sequel.delay{|ds| ds.first_source})
|
46
|
+
loader = @c.loader(d){|pl, ds| ds.where(:a=>pl.arg).where(:b=>Sequel.+(pl.arg, 1)).where(pl.arg)}
|
47
|
+
loader.first(1, :c, :id=>1).should == @h
|
48
|
+
loader.first(2, :d, :id=>2).should == @h
|
49
|
+
@db.sqls.should == ["SELECT items FROM items WHERE ((a = 1) AND (b = (c + 1)) AND (id = 1))", "SELECT items FROM items WHERE ((a = 2) AND (b = (d + 1)) AND (id = 2))"]
|
50
|
+
end
|
51
|
+
|
44
52
|
specify "should handle calls with a placeholders used as filter arguments" do
|
45
53
|
loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
|
46
54
|
loader.first(:id=>1).should == @h
|
@@ -249,4 +249,22 @@ describe Sequel::Model, "caching" do
|
|
249
249
|
@c.cache_delete_pk(1).should == nil
|
250
250
|
@c.cache_get_pk(1).should == nil
|
251
251
|
end
|
252
|
+
|
253
|
+
it "should support overriding the cache key prefix" do
|
254
|
+
c2 = Class.new(@c)
|
255
|
+
def c2.cache_key_prefix; "ceetwo" end
|
256
|
+
c3 = Class.new(c2)
|
257
|
+
@c.cache_key(:id).should_not == c2.cache_key(:id)
|
258
|
+
c2.cache_key(:id).should == c3.cache_key(:id)
|
259
|
+
|
260
|
+
@c[1]
|
261
|
+
c2.cache_get_pk(1).should == nil
|
262
|
+
m = c2[1]
|
263
|
+
c2.cache_get_pk(1).values.should == @c[1].values
|
264
|
+
c3.cache_get_pk(1).values.should == m.values
|
265
|
+
|
266
|
+
m.name << m.name
|
267
|
+
m.save
|
268
|
+
c2[1].values.should == c3[1].values
|
269
|
+
end
|
252
270
|
end
|
@@ -78,6 +78,17 @@ describe "class_table_inheritance plugin" do
|
|
78
78
|
Manager.all.collect{|x| x.class}.should == [Manager, Executive]
|
79
79
|
end
|
80
80
|
|
81
|
+
it "should have refresh return all columns in subclass after loading from superclass" do
|
82
|
+
Employee.dataset._fetch = [{:id=>1, :name=>'A', :kind=>'Executive'}]
|
83
|
+
Executive.instance_dataset._fetch = [{:id=>1, :name=>'A', :kind=>'Executive', :num_staff=>3, :num_managers=>2}]
|
84
|
+
a = Employee.first
|
85
|
+
a.class.should == Executive
|
86
|
+
a.values.should == {:id=>1, :name=>'A', :kind=>'Executive'}
|
87
|
+
a.refresh.values.should == {:id=>1, :name=>'A', :kind=>'Executive', :num_staff=>3, :num_managers=>2}
|
88
|
+
@db.sqls.should == ["SELECT employees.id, employees.name, employees.kind FROM employees LIMIT 1",
|
89
|
+
"SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (executives.id = 1) LIMIT 1"]
|
90
|
+
end
|
91
|
+
|
81
92
|
it "should return rows with the current class if cti_key is nil" do
|
82
93
|
Employee.plugin(:class_table_inheritance)
|
83
94
|
Employee.dataset._fetch = [{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Staff'}]
|
@@ -171,6 +182,29 @@ describe "class_table_inheritance plugin" do
|
|
171
182
|
m.values.should == {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2, :num_managers=>3}
|
172
183
|
end
|
173
184
|
|
185
|
+
it "should lazily load columns in middle classes correctly when loaded from parent class" do
|
186
|
+
Employee.dataset._fetch = {:id=>1, :kind=>'Executive'}
|
187
|
+
Manager.dataset._fetch = {:num_staff=>2}
|
188
|
+
e = Employee[1]
|
189
|
+
e.should be_a_kind_of(Executive)
|
190
|
+
@db.sqls.should == ["SELECT employees.id, employees.name, employees.kind FROM employees WHERE (id = 1) LIMIT 1"]
|
191
|
+
e.num_staff.should == 2
|
192
|
+
@db.sqls.should == ["SELECT managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1"]
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should eagerly load lazily columns in subclasses when loaded from parent class" do
|
196
|
+
Employee.dataset._fetch = {:id=>1, :kind=>'Executive'}
|
197
|
+
Manager.dataset._fetch = {:id=>1, :num_staff=>2}
|
198
|
+
Executive.dataset._fetch = {:id=>1, :num_managers=>3}
|
199
|
+
e = Employee.all.first
|
200
|
+
e.should be_a_kind_of(Executive)
|
201
|
+
@db.sqls.should == ["SELECT employees.id, employees.name, employees.kind FROM employees"]
|
202
|
+
e.num_staff#.should == 2
|
203
|
+
@db.sqls.should == ["SELECT managers.id, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id IN (1))"]
|
204
|
+
e.num_managers#.should == 3
|
205
|
+
@db.sqls.should == ['SELECT executives.id, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (executives.id IN (1))']
|
206
|
+
end
|
207
|
+
|
174
208
|
it "should include schema for columns for tables for ancestor classes" do
|
175
209
|
Employee.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}}
|
176
210
|
Manager.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}}
|
@@ -872,6 +872,10 @@ describe "many_through_many eager loading methods" do
|
|
872
872
|
@c1.association_join(:tags).sql.should == "SELECT * FROM artists INNER JOIN albums_artists ON (albums_artists.artist_id = artists.id) INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id)"
|
873
873
|
end
|
874
874
|
|
875
|
+
it "should support custom selects when using association_join" do
|
876
|
+
@c1.select{a(b)}.association_join(:tags).sql.should == "SELECT a(b) FROM artists INNER JOIN albums_artists ON (albums_artists.artist_id = artists.id) INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id)"
|
877
|
+
end
|
878
|
+
|
875
879
|
it "should eagerly graph a single many_through_many association" do
|
876
880
|
a = @c1.eager_graph(:tags).all
|
877
881
|
a.should == [@c1.load(:id=>1)]
|
@@ -36,12 +36,16 @@ describe "NestedAttributes plugin" do
|
|
36
36
|
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
37
37
|
@Artist.one_to_many :concerts, :class=>@Concert, :key=>:artist_id
|
38
38
|
@Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id
|
39
|
+
@Artist.one_to_one :first_concert, :class=>@Concert, :key=>:artist_id
|
40
|
+
@Concert.one_to_many :albums, :class=>@Album, :key=>:artist_id, :primary_key=>:artist_id
|
39
41
|
@Album.many_to_one :artist, :class=>@Artist, :reciprocal=>:albums
|
40
42
|
@Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
|
41
43
|
@Tag.many_to_many :albums, :class=>@Album, :left_key=>:tag_id, :right_key=>:album_id, :join_table=>:at
|
42
44
|
@Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
|
43
45
|
@Artist.nested_attributes :concerts, :destroy=>true, :remove=>true
|
44
46
|
@Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
|
47
|
+
@Artist.nested_attributes :first_concert
|
48
|
+
@Concert.nested_attributes :albums
|
45
49
|
@db.sqls
|
46
50
|
end
|
47
51
|
|
@@ -76,7 +80,7 @@ describe "NestedAttributes plugin" do
|
|
76
80
|
@Album.class_eval do
|
77
81
|
plugin :validation_helpers
|
78
82
|
def validate
|
79
|
-
|
83
|
+
validates_integer :artist_id
|
80
84
|
super
|
81
85
|
end
|
82
86
|
end
|
@@ -96,6 +100,55 @@ describe "NestedAttributes plugin" do
|
|
96
100
|
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
97
101
|
end
|
98
102
|
|
103
|
+
it "should support creating new one_to_many and one_to_one objects with composite keys with presence validations on the foreign key" do
|
104
|
+
insert = nil
|
105
|
+
@Album.class_eval do
|
106
|
+
plugin :validation_helpers
|
107
|
+
def validate
|
108
|
+
validates_integer :artist_id
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
@Concert.class_eval do
|
113
|
+
define_method :_insert do
|
114
|
+
insert = values.dup
|
115
|
+
end
|
116
|
+
def before_create # Have to define the CPK somehow.
|
117
|
+
self.tour = 'To'
|
118
|
+
self.date = '2004-04-05'
|
119
|
+
super
|
120
|
+
end
|
121
|
+
def after_create
|
122
|
+
super
|
123
|
+
self.artist_id = 3
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
c = @Concert.new(:playlist=>'Pl')
|
128
|
+
@db.sqls.should == []
|
129
|
+
c.albums_attributes = [{:name=>'Al'}]
|
130
|
+
c.save
|
131
|
+
insert.should == {:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl'}
|
132
|
+
check_sql_array(["INSERT INTO albums (name, artist_id) VALUES ('Al', 3)", "INSERT INTO albums (artist_id, name) VALUES (3, 'Al')"])
|
133
|
+
|
134
|
+
@Concert.class_eval do
|
135
|
+
plugin :validation_helpers
|
136
|
+
def validate
|
137
|
+
validates_integer :artist_id
|
138
|
+
super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
a = @Artist.new(:name=>'Ar')
|
143
|
+
a.id = 1
|
144
|
+
a.first_concert_attributes = {:playlist=>'Pl'}
|
145
|
+
@db.sqls.should == []
|
146
|
+
a.save
|
147
|
+
check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
|
148
|
+
"UPDATE concerts SET artist_id = NULL WHERE (artist_id = 1)")
|
149
|
+
insert.should == {:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl'}
|
150
|
+
end
|
151
|
+
|
99
152
|
it "should should not remove existing values from object when validating" do
|
100
153
|
@Artist.one_to_one :first_album, :class=>@Album, :key=>:id
|
101
154
|
@Artist.nested_attributes :first_album
|
@@ -130,7 +183,7 @@ describe "NestedAttributes plugin" do
|
|
130
183
|
insert = nil
|
131
184
|
@Concert.class_eval do
|
132
185
|
define_method :_insert do
|
133
|
-
insert = values
|
186
|
+
insert = values.dup
|
134
187
|
end
|
135
188
|
def before_create # Have to define the CPK somehow.
|
136
189
|
self.tour = 'To'
|
@@ -151,7 +204,7 @@ describe "NestedAttributes plugin" do
|
|
151
204
|
@Album.class_eval do
|
152
205
|
unrestrict_primary_key
|
153
206
|
define_method :_insert do
|
154
|
-
insert = values
|
207
|
+
insert = values.dup
|
155
208
|
end
|
156
209
|
end
|
157
210
|
a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:id=>7, :name=>'Al'}]})
|
@@ -166,7 +219,7 @@ describe "NestedAttributes plugin" do
|
|
166
219
|
@Artist.nested_attributes :concerts, :unmatched_pk=>:create
|
167
220
|
@Concert.class_eval do
|
168
221
|
define_method :_insert do
|
169
|
-
insert = values
|
222
|
+
insert = values.dup
|
170
223
|
end
|
171
224
|
end
|
172
225
|
a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl'}]})
|
@@ -271,6 +324,7 @@ describe "NestedAttributes plugin" do
|
|
271
324
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
272
325
|
ar.associations[:albums] = [al]
|
273
326
|
ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
|
327
|
+
ar.associations[:albums].should == []
|
274
328
|
@db.sqls.should == []
|
275
329
|
@Album.dataset._fetch = {:id=>1}
|
276
330
|
ar.save
|
@@ -284,6 +338,7 @@ describe "NestedAttributes plugin" do
|
|
284
338
|
t = @Tag.load(:id=>20, :name=>'T')
|
285
339
|
a.associations[:tags] = [t]
|
286
340
|
a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
|
341
|
+
a.associations[:tags].should == []
|
287
342
|
@db.sqls.should == []
|
288
343
|
a.save
|
289
344
|
@db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)"]
|