sequel 3.12.1 → 3.13.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.
Files changed (150) hide show
  1. data/CHANGELOG +42 -0
  2. data/README.rdoc +137 -118
  3. data/Rakefile +21 -66
  4. data/doc/active_record.rdoc +9 -9
  5. data/doc/advanced_associations.rdoc +59 -188
  6. data/doc/association_basics.rdoc +15 -2
  7. data/doc/cheat_sheet.rdoc +38 -33
  8. data/doc/dataset_filtering.rdoc +16 -7
  9. data/doc/prepared_statements.rdoc +7 -7
  10. data/doc/querying.rdoc +5 -4
  11. data/doc/release_notes/3.13.0.txt +210 -0
  12. data/doc/sharding.rdoc +1 -1
  13. data/doc/sql.rdoc +5 -5
  14. data/doc/validations.rdoc +11 -11
  15. data/lib/sequel/adapters/ado.rb +1 -1
  16. data/lib/sequel/adapters/do.rb +3 -3
  17. data/lib/sequel/adapters/firebird.rb +3 -3
  18. data/lib/sequel/adapters/jdbc/h2.rb +39 -0
  19. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  20. data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
  21. data/lib/sequel/adapters/mysql.rb +7 -4
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/shared/mssql.rb +10 -1
  24. data/lib/sequel/adapters/shared/mysql.rb +63 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +61 -3
  26. data/lib/sequel/adapters/sqlite.rb +105 -18
  27. data/lib/sequel/connection_pool.rb +31 -30
  28. data/lib/sequel/core.rb +58 -58
  29. data/lib/sequel/core_sql.rb +52 -43
  30. data/lib/sequel/database/misc.rb +11 -0
  31. data/lib/sequel/database/query.rb +55 -17
  32. data/lib/sequel/dataset/actions.rb +2 -1
  33. data/lib/sequel/dataset/query.rb +2 -3
  34. data/lib/sequel/dataset/sql.rb +24 -11
  35. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  36. data/lib/sequel/metaprogramming.rb +4 -0
  37. data/lib/sequel/model.rb +37 -19
  38. data/lib/sequel/model/associations.rb +33 -25
  39. data/lib/sequel/model/base.rb +2 -2
  40. data/lib/sequel/model/plugins.rb +7 -2
  41. data/lib/sequel/plugins/active_model.rb +1 -1
  42. data/lib/sequel/plugins/association_pks.rb +2 -2
  43. data/lib/sequel/plugins/association_proxies.rb +1 -1
  44. data/lib/sequel/plugins/boolean_readers.rb +2 -2
  45. data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
  46. data/lib/sequel/plugins/identity_map.rb +3 -3
  47. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  48. data/lib/sequel/plugins/json_serializer.rb +212 -0
  49. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  50. data/lib/sequel/plugins/list.rb +174 -0
  51. data/lib/sequel/plugins/many_through_many.rb +2 -2
  52. data/lib/sequel/plugins/rcte_tree.rb +6 -7
  53. data/lib/sequel/plugins/tree.rb +118 -0
  54. data/lib/sequel/plugins/xml_serializer.rb +321 -0
  55. data/lib/sequel/sql.rb +315 -206
  56. data/lib/sequel/timezones.rb +40 -17
  57. data/lib/sequel/version.rb +8 -2
  58. data/spec/adapters/firebird_spec.rb +2 -2
  59. data/spec/adapters/informix_spec.rb +1 -1
  60. data/spec/adapters/mssql_spec.rb +2 -2
  61. data/spec/adapters/mysql_spec.rb +2 -2
  62. data/spec/adapters/oracle_spec.rb +1 -1
  63. data/spec/adapters/postgres_spec.rb +36 -6
  64. data/spec/adapters/spec_helper.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +1 -1
  66. data/spec/core/connection_pool_spec.rb +3 -3
  67. data/spec/core/core_sql_spec.rb +31 -13
  68. data/spec/core/database_spec.rb +39 -2
  69. data/spec/core/dataset_spec.rb +24 -12
  70. data/spec/core/expression_filters_spec.rb +5 -1
  71. data/spec/core/object_graph_spec.rb +1 -1
  72. data/spec/core/schema_generator_spec.rb +1 -1
  73. data/spec/core/schema_spec.rb +1 -1
  74. data/spec/core/spec_helper.rb +1 -1
  75. data/spec/core/version_spec.rb +1 -1
  76. data/spec/extensions/active_model_spec.rb +82 -67
  77. data/spec/extensions/association_dependencies_spec.rb +1 -1
  78. data/spec/extensions/association_pks_spec.rb +1 -1
  79. data/spec/extensions/association_proxies_spec.rb +1 -1
  80. data/spec/extensions/blank_spec.rb +1 -1
  81. data/spec/extensions/boolean_readers_spec.rb +1 -1
  82. data/spec/extensions/caching_spec.rb +1 -1
  83. data/spec/extensions/class_table_inheritance_spec.rb +3 -2
  84. data/spec/extensions/composition_spec.rb +2 -5
  85. data/spec/extensions/force_encoding_spec.rb +3 -1
  86. data/spec/extensions/hook_class_methods_spec.rb +1 -1
  87. data/spec/extensions/identity_map_spec.rb +1 -1
  88. data/spec/extensions/inflector_spec.rb +1 -1
  89. data/spec/extensions/instance_filters_spec.rb +1 -1
  90. data/spec/extensions/instance_hooks_spec.rb +1 -1
  91. data/spec/extensions/json_serializer_spec.rb +154 -0
  92. data/spec/extensions/lazy_attributes_spec.rb +1 -2
  93. data/spec/extensions/list_spec.rb +251 -0
  94. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  95. data/spec/extensions/many_through_many_spec.rb +3 -3
  96. data/spec/extensions/migration_spec.rb +1 -1
  97. data/spec/extensions/named_timezones_spec.rb +5 -6
  98. data/spec/extensions/nested_attributes_spec.rb +1 -1
  99. data/spec/extensions/optimistic_locking_spec.rb +1 -1
  100. data/spec/extensions/pagination_spec.rb +1 -1
  101. data/spec/extensions/pretty_table_spec.rb +1 -1
  102. data/spec/extensions/query_spec.rb +1 -1
  103. data/spec/extensions/rcte_tree_spec.rb +1 -1
  104. data/spec/extensions/schema_dumper_spec.rb +3 -2
  105. data/spec/extensions/schema_spec.rb +1 -1
  106. data/spec/extensions/serialization_spec.rb +6 -2
  107. data/spec/extensions/sharding_spec.rb +1 -1
  108. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  109. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  110. data/spec/extensions/spec_helper.rb +7 -3
  111. data/spec/extensions/sql_expr_spec.rb +1 -1
  112. data/spec/extensions/string_date_time_spec.rb +1 -1
  113. data/spec/extensions/string_stripper_spec.rb +1 -1
  114. data/spec/extensions/subclasses_spec.rb +1 -1
  115. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  116. data/spec/extensions/thread_local_timezones_spec.rb +1 -1
  117. data/spec/extensions/timestamps_spec.rb +1 -1
  118. data/spec/extensions/touch_spec.rb +1 -1
  119. data/spec/extensions/tree_spec.rb +119 -0
  120. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  121. data/spec/extensions/update_primary_key_spec.rb +1 -1
  122. data/spec/extensions/validation_class_methods_spec.rb +1 -1
  123. data/spec/extensions/validation_helpers_spec.rb +1 -1
  124. data/spec/extensions/xml_serializer_spec.rb +142 -0
  125. data/spec/integration/associations_test.rb +1 -1
  126. data/spec/integration/database_test.rb +1 -1
  127. data/spec/integration/dataset_test.rb +29 -14
  128. data/spec/integration/eager_loader_test.rb +1 -1
  129. data/spec/integration/migrator_test.rb +1 -1
  130. data/spec/integration/model_test.rb +1 -1
  131. data/spec/integration/plugin_test.rb +316 -1
  132. data/spec/integration/prepared_statement_test.rb +1 -1
  133. data/spec/integration/schema_test.rb +8 -8
  134. data/spec/integration/spec_helper.rb +1 -1
  135. data/spec/integration/timezone_test.rb +1 -1
  136. data/spec/integration/transaction_test.rb +35 -20
  137. data/spec/integration/type_test.rb +1 -1
  138. data/spec/model/association_reflection_spec.rb +1 -1
  139. data/spec/model/associations_spec.rb +49 -34
  140. data/spec/model/base_spec.rb +1 -1
  141. data/spec/model/dataset_methods_spec.rb +4 -4
  142. data/spec/model/eager_loading_spec.rb +1 -1
  143. data/spec/model/hooks_spec.rb +1 -1
  144. data/spec/model/inflector_spec.rb +1 -1
  145. data/spec/model/model_spec.rb +7 -1
  146. data/spec/model/plugins_spec.rb +1 -1
  147. data/spec/model/record_spec.rb +1 -3
  148. data/spec/model/spec_helper.rb +2 -2
  149. data/spec/model/validations_spec.rb +1 -1
  150. metadata +29 -5
@@ -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
- @@sql = nil
2200
+ class_variable_set(:@@sql, nil)
2189
2201
 
2190
- def last_sql; @@sql; end
2202
+ def last_sql; self.class.send(:class_variable_get, :@@sql); end
2191
2203
 
2192
2204
  def fetch_rows(sql)
2193
- @@sql = sql
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
- @@sql = nil
2233
+ class_variable_set(:@@sql, nil)
2222
2234
 
2223
- def last_sql; @@sql; end
2235
+ def last_sql; self.class.send(:class_variable_get, :@@sql); end
2224
2236
 
2225
2237
  def fetch_rows(sql)
2226
- @@sql = sql
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
- @@last_dataset = nil
2443
+ class_variable_set(:@@last_dataset, nil)
2432
2444
 
2433
2445
  def self.last_dataset
2434
- @@last_dataset
2446
+ class_variable_get(:@@last_dataset)
2435
2447
  end
2436
2448
 
2437
2449
  def single_record
2438
- @@last_dataset = opts ? clone(opts) : self
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::SQLArrays" do
3447
- @ds.filter(:a=>[:b, :c].sql_array).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.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
@@ -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 Sequel::Dataset, " graphing" do
4
4
  before 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
  describe Sequel::Schema::Generator do
4
4
  before 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
  context "DB#create_table" do
4
4
  before do
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
  unless Object.const_defined?('Sequel')
3
- $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
3
+ $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
4
4
  require 'sequel/core'
5
5
  end
6
6
 
@@ -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 "Sequel.version" do
4
4
  specify "should be in the form X.Y.Z with all being numbers" do
@@ -1,76 +1,91 @@
1
- require File.join(File.dirname(__FILE__), "spec_helper")
2
- if RUBY_PLATFORM !~ /(win|w)32|java$/
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
- s = ''
6
- IO.popen('-') do |f|
7
- if f
8
- s = f.read
9
- else
10
- begin
11
- require 'rubygems'
12
- require 'active_model'
13
- rescue LoadError
14
- puts "0 failures, 0 errors, skipping tests"
15
- else
16
- require 'test/unit'
17
- require "test/unit/ui/console/testrunner"
18
- class AMLintTest < Test::Unit::TestCase
19
- def setup
20
- @c = Class.new(Sequel::Model) do
21
- set_primary_key :id
22
- columns :id, :id2
23
- def delete; end
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
- # Should return self, not a proxy object
32
- def test__to_model
33
- assert_equal @m.to_model.object_id.should, @m.object_id
34
- end
35
-
36
- def test__to_key
37
- assert_equal nil, @m.to_key
38
- @o.id = 1
39
- assert_equal [1], @o.to_key
40
- @c.set_primary_key [:id2, :id]
41
- @o.id2 = 2
42
- assert_equal [2, 1], @o.to_key
43
- @o.destroy
44
- assert_equal nil, @o.to_key
45
- end
46
-
47
- def test__to_param
48
- assert_equal nil, @m.to_param
49
- @o.id = 1
50
- assert_equal '1', @o.to_param
51
- @c.set_primary_key [:id2, :id]
52
- @o.id2 = 2
53
- assert_equal '2-1', @o.to_param
54
- @o.meta_def(:to_param_joiner){'|'}
55
- assert_equal '2|1', @o.to_param
56
- @o.destroy
57
- assert_equal nil, @o.to_param
58
- end
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
- def test__persisted?
61
- assert_equal false, @m.persisted?
62
- assert_equal true, @o.persisted?
63
- @m.destroy
64
- @o.destroy
65
- assert_equal false, @m.persisted?
66
- assert_equal false, @o.persisted?
67
- end
68
- end
69
- Test::Unit::UI::Console::TestRunner.run(AMLintTest)
70
- end
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 "AssociationDependencies plugin" do
4
4
  before 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
  describe "Sequel::Plugins::AssociationPks" do
4
4
  before 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
  describe "Sequel::Plugins::AssociationProxies" do
4
4
  before 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
  context "Object#blank?" do
4
4
  specify "it should be true if the object responds true to empty?" 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
  describe Sequel::Model, "BooleanReaders plugin" do
4
4
  before 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
  describe Sequel::Model, "caching" do
4
4
  before 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
  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
- def @ds.fetch_rows(sql)
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
- require 'yaml'
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
@@ -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 "Model hooks" do
4
4
  before 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
  describe "Sequel::Plugins::IdentityMap" do
4
4
  before 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
  describe String do
4
4
  it "#camelize and #camelcase should transform the word to CamelCase" 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
  describe "instance_filters plugin" do
4
4
  before 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
  describe "InstanceHooks plugin" do
4
4
  def r(x)
@@ -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