sequel 4.13.0 → 4.14.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.
- 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)"]
|