sequel 3.12.1 → 3.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/README.rdoc +137 -118
- data/Rakefile +21 -66
- data/doc/active_record.rdoc +9 -9
- data/doc/advanced_associations.rdoc +59 -188
- data/doc/association_basics.rdoc +15 -2
- data/doc/cheat_sheet.rdoc +38 -33
- data/doc/dataset_filtering.rdoc +16 -7
- data/doc/prepared_statements.rdoc +7 -7
- data/doc/querying.rdoc +5 -4
- data/doc/release_notes/3.13.0.txt +210 -0
- data/doc/sharding.rdoc +1 -1
- data/doc/sql.rdoc +5 -5
- data/doc/validations.rdoc +11 -11
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/do.rb +3 -3
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +39 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -4
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +10 -1
- data/lib/sequel/adapters/shared/mysql.rb +63 -0
- data/lib/sequel/adapters/shared/postgres.rb +61 -3
- data/lib/sequel/adapters/sqlite.rb +105 -18
- data/lib/sequel/connection_pool.rb +31 -30
- data/lib/sequel/core.rb +58 -58
- data/lib/sequel/core_sql.rb +52 -43
- data/lib/sequel/database/misc.rb +11 -0
- data/lib/sequel/database/query.rb +55 -17
- data/lib/sequel/dataset/actions.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -3
- data/lib/sequel/dataset/sql.rb +24 -11
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/metaprogramming.rb +4 -0
- data/lib/sequel/model.rb +37 -19
- data/lib/sequel/model/associations.rb +33 -25
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/model/plugins.rb +7 -2
- data/lib/sequel/plugins/active_model.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +2 -2
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
- data/lib/sequel/plugins/identity_map.rb +3 -3
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +212 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +6 -7
- data/lib/sequel/plugins/tree.rb +118 -0
- data/lib/sequel/plugins/xml_serializer.rb +321 -0
- data/lib/sequel/sql.rb +315 -206
- data/lib/sequel/timezones.rb +40 -17
- data/lib/sequel/version.rb +8 -2
- data/spec/adapters/firebird_spec.rb +2 -2
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/mysql_spec.rb +2 -2
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +36 -6
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +3 -3
- data/spec/core/core_sql_spec.rb +31 -13
- data/spec/core/database_spec.rb +39 -2
- data/spec/core/dataset_spec.rb +24 -12
- data/spec/core/expression_filters_spec.rb +5 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core/version_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +82 -67
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +3 -2
- data/spec/extensions/composition_spec.rb +2 -5
- data/spec/extensions/force_encoding_spec.rb +3 -1
- data/spec/extensions/hook_class_methods_spec.rb +1 -1
- data/spec/extensions/identity_map_spec.rb +1 -1
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +154 -0
- data/spec/extensions/lazy_attributes_spec.rb +1 -2
- data/spec/extensions/list_spec.rb +251 -0
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -3
- data/spec/extensions/migration_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +5 -6
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +6 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +119 -0
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/extensions/xml_serializer_spec.rb +142 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +29 -14
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +1 -1
- data/spec/integration/plugin_test.rb +316 -1
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +8 -8
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +35 -20
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +49 -34
- data/spec/model/base_spec.rb +1 -1
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +1 -1
- data/spec/model/hooks_spec.rb +1 -1
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +7 -1
- data/spec/model/plugins_spec.rb +1 -1
- data/spec/model/record_spec.rb +1 -3
- data/spec/model/spec_helper.rb +2 -2
- data/spec/model/validations_spec.rb +1 -1
- metadata +29 -5
data/spec/core/dataset_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
2
|
|
3
3
|
context "Dataset" do
|
4
4
|
before do
|
@@ -465,6 +465,7 @@ context "Dataset#where" do
|
|
465
465
|
@dataset.filter([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
|
466
466
|
@dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
467
467
|
@dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
|
468
|
+
@dataset.filter([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
|
468
469
|
|
469
470
|
@dataset.exclude(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id NOT IN (SELECT id FROM test WHERE (region = 'Asia')))"
|
470
471
|
@dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
@@ -472,6 +473,7 @@ context "Dataset#where" do
|
|
472
473
|
@dataset.exclude([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
|
473
474
|
@dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
474
475
|
@dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
|
476
|
+
@dataset.exclude([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
|
475
477
|
end
|
476
478
|
|
477
479
|
specify "should handle IN/NOT IN queries with multiple columns and an array where the database doesn't support it" do
|
@@ -1204,6 +1206,11 @@ context "Dataset#order" do
|
|
1204
1206
|
'SELECT * FROM test ORDER BY name, price DESC'
|
1205
1207
|
end
|
1206
1208
|
|
1209
|
+
specify "should accept :nulls options for asc and desc" do
|
1210
|
+
@dataset.order(:name.asc(:nulls=>:last), :price.desc(:nulls=>:first)).sql.should ==
|
1211
|
+
'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
|
1212
|
+
end
|
1213
|
+
|
1207
1214
|
specify "should overrun a previous ordering" do
|
1208
1215
|
@dataset.order(:name).order(:stamp).sql.should ==
|
1209
1216
|
'SELECT * FROM test ORDER BY stamp'
|
@@ -1385,6 +1392,11 @@ context "Dataset#reverse_order" do
|
|
1385
1392
|
'SELECT * FROM test ORDER BY name DESC, price ASC'
|
1386
1393
|
end
|
1387
1394
|
|
1395
|
+
specify "should handles NULLS ordering correctly when reversing" do
|
1396
|
+
@dataset.reverse_order(:name.asc(:nulls=>:first), :price.desc(:nulls=>:last)).sql.should ==
|
1397
|
+
'SELECT * FROM test ORDER BY name DESC NULLS LAST, price ASC NULLS FIRST'
|
1398
|
+
end
|
1399
|
+
|
1388
1400
|
specify "should reverse a previous ordering if no arguments are given" do
|
1389
1401
|
@dataset.order(:name).reverse_order.sql.should ==
|
1390
1402
|
'SELECT * FROM test ORDER BY name DESC'
|
@@ -2185,12 +2197,12 @@ end
|
|
2185
2197
|
context "Dataset#range" do
|
2186
2198
|
before do
|
2187
2199
|
c = Class.new(Sequel::Dataset) do
|
2188
|
-
|
2200
|
+
class_variable_set(:@@sql, nil)
|
2189
2201
|
|
2190
|
-
def last_sql;
|
2202
|
+
def last_sql; self.class.send(:class_variable_get, :@@sql); end
|
2191
2203
|
|
2192
2204
|
def fetch_rows(sql)
|
2193
|
-
|
2205
|
+
self.class.send(:class_variable_set, :@@sql, sql)
|
2194
2206
|
yield(:v1 => 1, :v2 => 10)
|
2195
2207
|
end
|
2196
2208
|
end
|
@@ -2218,12 +2230,12 @@ end
|
|
2218
2230
|
context "Dataset#interval" do
|
2219
2231
|
before do
|
2220
2232
|
c = Class.new(Sequel::Dataset) do
|
2221
|
-
|
2233
|
+
class_variable_set(:@@sql, nil)
|
2222
2234
|
|
2223
|
-
def last_sql;
|
2235
|
+
def last_sql; self.class.send(:class_variable_get, :@@sql); end
|
2224
2236
|
|
2225
2237
|
def fetch_rows(sql)
|
2226
|
-
|
2238
|
+
self.class.send(:class_variable_set, :@@sql, sql)
|
2227
2239
|
yield(:v => 1234)
|
2228
2240
|
end
|
2229
2241
|
end
|
@@ -2428,14 +2440,14 @@ end
|
|
2428
2440
|
context "Dataset#[]" do
|
2429
2441
|
before do
|
2430
2442
|
@c = Class.new(Sequel::Dataset) do
|
2431
|
-
|
2443
|
+
class_variable_set(:@@last_dataset, nil)
|
2432
2444
|
|
2433
2445
|
def self.last_dataset
|
2434
|
-
|
2446
|
+
class_variable_get(:@@last_dataset)
|
2435
2447
|
end
|
2436
2448
|
|
2437
2449
|
def single_record
|
2438
|
-
|
2450
|
+
self.class.send(:class_variable_set, :@@last_dataset, opts ? clone(opts) : self)
|
2439
2451
|
{1 => 2, 3 => 4}
|
2440
2452
|
end
|
2441
2453
|
end
|
@@ -3443,8 +3455,8 @@ context "Sequel::Dataset#qualify_to_first_source" do
|
|
3443
3455
|
@ds.filter{(a+b)<(c-3)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE ((t.a + t.b) < (t.c - 3))'
|
3444
3456
|
end
|
3445
3457
|
|
3446
|
-
specify "should handle SQL::
|
3447
|
-
@ds.filter(:a=>[:b, :c].
|
3458
|
+
specify "should handle SQL::ValueLists" do
|
3459
|
+
@ds.filter(:a=>[:b, :c].sql_value_list).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.c))'
|
3448
3460
|
end
|
3449
3461
|
|
3450
3462
|
specify "should handle SQL::Subscripts" do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'spec_helper')
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
2
2
|
|
3
3
|
Regexp.send(:include, Sequel::SQL::StringMethods)
|
4
4
|
String.send(:include, Sequel::SQL::StringMethods)
|
@@ -556,6 +556,10 @@ context Sequel::SQL::VirtualRow do
|
|
556
556
|
@d.l{rank(:over, :frame=>:rows){}}.should == 'rank() OVER (ROWS UNBOUNDED PRECEDING)'
|
557
557
|
end
|
558
558
|
|
559
|
+
it "should support :frame=>'some string' option for window function calls" do
|
560
|
+
@d.l{rank(:over, :frame=>'RANGE BETWEEN 3 PRECEDING AND CURRENT ROW'){}}.should == 'rank() OVER (RANGE BETWEEN 3 PRECEDING AND CURRENT ROW)'
|
561
|
+
end
|
562
|
+
|
559
563
|
it "should raise an error if an invalid :frame option is used" do
|
560
564
|
proc{@d.l{rank(:over, :frame=>:blah){}}}.should raise_error(Sequel::Error)
|
561
565
|
end
|
data/spec/core/schema_spec.rb
CHANGED
data/spec/core/spec_helper.rb
CHANGED
data/spec/core/version_spec.rb
CHANGED
@@ -1,76 +1,91 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
-
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'active_model'
|
5
|
+
require 'test/unit'
|
6
|
+
if Test::Unit.respond_to?(:run=)
|
7
|
+
Test::Unit.run = false
|
8
|
+
require 'test/unit/testresult'
|
9
|
+
elsif defined?(MiniTest::Unit)
|
10
|
+
class << MiniTest::Unit
|
11
|
+
def autorun; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
rescue LoadError => e
|
15
|
+
skip_warn "active_model plugin: can't load active_model (#{e.class}: #{e})"
|
16
|
+
else
|
3
17
|
describe "ActiveModel plugin" do
|
4
18
|
specify "should be compliant to the ActiveModel spec" do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
@c.plugin :active_model
|
26
|
-
@m = @model = @c.new
|
27
|
-
@o = @c.load({})
|
28
|
-
end
|
29
|
-
include ActiveModel::Lint::Tests
|
19
|
+
tc = Class.new(Test::Unit::TestCase)
|
20
|
+
tc.class_eval do
|
21
|
+
define_method(:setup) do
|
22
|
+
class ::AMLintTest < Sequel::Model
|
23
|
+
set_primary_key :id
|
24
|
+
columns :id, :id2
|
25
|
+
def delete; end
|
26
|
+
end
|
27
|
+
@c = AMLintTest
|
28
|
+
@c.plugin :active_model
|
29
|
+
@m = @model = @c.new
|
30
|
+
@o = @c.load({})
|
31
|
+
super()
|
32
|
+
end
|
33
|
+
def teardown
|
34
|
+
super
|
35
|
+
Object.send(:remove_const, :AMLintTest)
|
36
|
+
end
|
37
|
+
include ActiveModel::Lint::Tests
|
30
38
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
39
|
+
# Should return self, not a proxy object
|
40
|
+
def test__to_model
|
41
|
+
assert_equal @m.to_model.object_id.should, @m.object_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def test__to_key
|
45
|
+
assert_equal nil, @m.to_key
|
46
|
+
@o.id = 1
|
47
|
+
assert_equal [1], @o.to_key
|
48
|
+
@c.set_primary_key [:id2, :id]
|
49
|
+
@o.id2 = 2
|
50
|
+
assert_equal [2, 1], @o.to_key
|
51
|
+
@o.destroy
|
52
|
+
assert_equal nil, @o.to_key
|
53
|
+
end
|
54
|
+
|
55
|
+
def test__to_param
|
56
|
+
assert_equal nil, @m.to_param
|
57
|
+
@o.id = 1
|
58
|
+
assert_equal '1', @o.to_param
|
59
|
+
@c.set_primary_key [:id2, :id]
|
60
|
+
@o.id2 = 2
|
61
|
+
assert_equal '2-1', @o.to_param
|
62
|
+
@o.meta_def(:to_param_joiner){'|'}
|
63
|
+
assert_equal '2|1', @o.to_param
|
64
|
+
@o.destroy
|
65
|
+
assert_equal nil, @o.to_param
|
66
|
+
end
|
59
67
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
def test__persisted?
|
69
|
+
assert_equal false, @m.persisted?
|
70
|
+
assert_equal true, @o.persisted?
|
71
|
+
@m.destroy
|
72
|
+
@o.destroy
|
73
|
+
assert_equal false, @m.persisted?
|
74
|
+
assert_equal false, @o.persisted?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
if defined?(MiniTest::Unit)
|
78
|
+
tc.instance_methods.map{|x| x.to_s}.reject{|n| n !~ /\Atest_/}.each do |m|
|
79
|
+
i = tc.new(m)
|
80
|
+
i.setup
|
81
|
+
i.send(m)
|
82
|
+
i.teardown
|
71
83
|
end
|
84
|
+
else
|
85
|
+
res = ::Test::Unit::TestResult.new
|
86
|
+
tc.suite.run(res){}
|
87
|
+
res.failure_count.should == 0
|
72
88
|
end
|
73
|
-
s.should =~ /0 failures, 0 errors/
|
74
89
|
end
|
75
90
|
end
|
76
91
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
2
|
|
3
3
|
describe "class_table_inheritance plugin" do
|
4
4
|
before do
|
@@ -88,7 +88,8 @@ describe "class_table_inheritance plugin" do
|
|
88
88
|
|
89
89
|
it "should return rows with the current class if cti_key is nil" do
|
90
90
|
Employee.plugin(:class_table_inheritance)
|
91
|
-
|
91
|
+
ds = Employee.dataset
|
92
|
+
def ds.fetch_rows(sql)
|
92
93
|
yield({:kind=>'Employee'})
|
93
94
|
yield({:kind=>'Manager'})
|
94
95
|
yield({:kind=>'Executive'})
|
@@ -1,9 +1,6 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
2
|
|
3
|
-
|
4
|
-
require 'json'
|
5
|
-
|
6
|
-
describe "Serialization plugin" do
|
3
|
+
describe "Composition plugin" do
|
7
4
|
before do
|
8
5
|
@c = Class.new(Sequel::Model(:items))
|
9
6
|
@c.plugin :composition
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
2
|
if RUBY_VERSION >= '1.9.0'
|
3
3
|
describe "force_encoding plugin" do
|
4
4
|
before do
|
@@ -114,4 +114,6 @@ describe "force_encoding plugin" do
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
+
else
|
118
|
+
skip_warn "force_encoding plugin: only works on ruby 1.9"
|
117
119
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'json'
|
5
|
+
rescue LoadError => e
|
6
|
+
skip_warn "json_serializer plugin: can't load json (#{e.class}: #{e})"
|
7
|
+
else
|
8
|
+
describe "Sequel::Plugins::JsonSerializer" do
|
9
|
+
before do
|
10
|
+
class ::Artist < Sequel::Model
|
11
|
+
plugin :json_serializer
|
12
|
+
columns :id, :name
|
13
|
+
one_to_many :albums
|
14
|
+
end
|
15
|
+
class ::Album < Sequel::Model
|
16
|
+
attr_accessor :blah
|
17
|
+
plugin :json_serializer
|
18
|
+
columns :id, :name, :artist_id
|
19
|
+
many_to_one :artist
|
20
|
+
end
|
21
|
+
@artist = Artist.load(:id=>2, :name=>'YJM')
|
22
|
+
@artist.associations[:albums] = []
|
23
|
+
@album = Album.load(:id=>1, :name=>'RF')
|
24
|
+
@album.artist = @artist
|
25
|
+
@album.blah = 'Blah'
|
26
|
+
end
|
27
|
+
after do
|
28
|
+
Object.send(:remove_const, :Artist)
|
29
|
+
Object.send(:remove_const, :Album)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should round trip successfully" do
|
33
|
+
JSON.parse(@artist.to_json).should == @artist
|
34
|
+
JSON.parse(@album.to_json).should == @album
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should handle the :only option" do
|
38
|
+
JSON.parse(@artist.to_json(:only=>:name)).should == Artist.load(:name=>@artist.name)
|
39
|
+
JSON.parse(@album.to_json(:only=>[:id, :name])).should == Album.load(:id=>@album.id, :name=>@album.name)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should handle the :except option" do
|
43
|
+
JSON.parse(@artist.to_json(:except=>:id)).should == Artist.load(:name=>@artist.name)
|
44
|
+
JSON.parse(@album.to_json(:except=>[:id, :artist_id])).should == Album.load(:name=>@album.name)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should handle the :include option for associations" do
|
48
|
+
JSON.parse(@artist.to_json(:include=>:albums)).albums.should == [@album]
|
49
|
+
JSON.parse(@album.to_json(:include=>:artist)).artist.should == @artist
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should handle the :include option for arbitrary attributes" do
|
53
|
+
JSON.parse(@album.to_json(:include=>:blah)).blah.should == @album.blah
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should handle multiple inclusions using an array for the :include option" do
|
57
|
+
a = JSON.parse(@album.to_json(:include=>[:blah, :artist]))
|
58
|
+
a.blah.should == @album.blah
|
59
|
+
a.artist.should == @artist
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should handle cascading using a hash for the :include option" do
|
63
|
+
JSON.parse(@artist.to_json(:include=>{:albums=>{:include=>:artist}})).albums.map{|a| a.artist}.should == [@artist]
|
64
|
+
JSON.parse(@album.to_json(:include=>{:artist=>{:include=>:albums}})).artist.albums.should == [@album]
|
65
|
+
|
66
|
+
JSON.parse(@artist.to_json(:include=>{:albums=>{:only=>:name}})).albums.should == [Album.load(:name=>@album.name)]
|
67
|
+
JSON.parse(@album.to_json(:include=>{:artist=>{:except=>:name}})).artist.should == Artist.load(:id=>@artist.id)
|
68
|
+
|
69
|
+
JSON.parse(@artist.to_json(:include=>{:albums=>{:include=>{:artist=>{:include=>:albums}}}})).albums.map{|a| a.artist.albums}.should == [[@album]]
|
70
|
+
JSON.parse(@album.to_json(:include=>{:artist=>{:include=>{:albums=>{:only=>:name}}}})).artist.albums.should == [Album.load(:name=>@album.name)]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should handle the :include option cascading with an empty hash" do
|
74
|
+
JSON.parse(@album.to_json(:include=>{:artist=>{}})).artist.should == @artist
|
75
|
+
JSON.parse(@album.to_json(:include=>{:blah=>{}})).blah.should == @album.blah
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should accept a :naked option to not include the JSON.create_id, so parsing yields a plain hash" do
|
79
|
+
JSON.parse(@album.to_json(:naked=>true)).should == @album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should support #from_json to set column values" do
|
83
|
+
@artist.from_json('{"name": "AS"}')
|
84
|
+
@artist.name.should == 'AS'
|
85
|
+
@artist.id.should == 2
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should raise an exception for json keys that aren't associations, columns, or setter methods" do
|
89
|
+
Album.send(:undef_method, :blah=)
|
90
|
+
proc{JSON.parse(@album.to_json(:include=>:blah))}.should raise_error(Sequel::Error)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should support a to_json class and dataset method" do
|
94
|
+
album = @album
|
95
|
+
Album.dataset.meta_def(:all){[album]}
|
96
|
+
JSON.parse(Album.to_json).should == [@album]
|
97
|
+
JSON.parse(Album.to_json(:include=>:artist)).map{|x| x.artist}.should == [@artist]
|
98
|
+
JSON.parse(Album.dataset.to_json(:only=>:name)).should == [Album.load(:name=>@album.name)]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should have dataset to_json method work with naked datasets" do
|
102
|
+
album = @album
|
103
|
+
ds = Album.dataset.naked
|
104
|
+
ds.meta_def(:all){[album.values]}
|
105
|
+
JSON.parse(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should propagate class default options to instance to_json output" do
|
109
|
+
class ::Album2 < Sequel::Model
|
110
|
+
attr_accessor :blah
|
111
|
+
plugin :json_serializer, :naked => true, :except => :id
|
112
|
+
columns :id, :name, :artist_id
|
113
|
+
many_to_one :artist
|
114
|
+
end
|
115
|
+
@album2 = Album2.load(:id=>2, :name=>'JK')
|
116
|
+
@album2.artist = @artist
|
117
|
+
@album2.blah = 'Gak'
|
118
|
+
JSON.parse(@album2.to_json).should == @album2.values.reject{|k,v| k.to_s == 'id'}.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
119
|
+
JSON.parse(@album2.to_json(:only => :name)).should == @album2.values.reject{|k,v| k.to_s != 'name'}.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
120
|
+
JSON.parse(@album2.to_json(:except => :artist_id)).should == @album2.values.reject{|k,v| k.to_s == 'artist_id'}.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should handle the :root option to qualify single records" do
|
124
|
+
@album.to_json(:root=>true, :except => [:name, :artist_id]).to_s.should == '{"album":{"id":1}}'
|
125
|
+
@album.to_json(:root=>true, :only => :name).to_s.should == '{"album":{"name":"RF"}}'
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should handle the :root option to qualify a dataset of records" do
|
129
|
+
album = @album
|
130
|
+
Album.dataset.meta_def(:all){[album, album]}
|
131
|
+
Album.dataset.to_json(:root=>true, :only => :id).to_s.should == '{"albums":[{"album":{"id":1}},{"album":{"id":1}}]}'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should store the default options in json_serializer_opts" do
|
135
|
+
Album.json_serializer_opts.should == {}
|
136
|
+
c = Class.new(Album)
|
137
|
+
c.plugin :json_serializer, :naked=>true
|
138
|
+
c.json_serializer_opts.should == {:naked=>true}
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should work correctly when subclassing" do
|
142
|
+
class ::Artist2 < Artist
|
143
|
+
plugin :json_serializer, :only=>:name
|
144
|
+
end
|
145
|
+
JSON.parse(Artist2.load(:id=>2, :name=>'YYY').to_json).should == Artist2.load(:name=>'YYY')
|
146
|
+
class ::Artist3 < Artist2
|
147
|
+
plugin :json_serializer, :naked=>:true
|
148
|
+
end
|
149
|
+
JSON.parse(Artist3.load(:id=>2, :name=>'YYY').to_json).should == {"name"=>'YYY'}
|
150
|
+
Object.send(:remove_const, :Artist2)
|
151
|
+
Object.send(:remove_const, :Artist3)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|