sequel 3.44.0 → 3.45.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 (97) hide show
  1. data/CHANGELOG +44 -0
  2. data/Rakefile +12 -4
  3. data/doc/reflection.rdoc +3 -3
  4. data/doc/release_notes/3.45.0.txt +179 -0
  5. data/doc/schema_modification.rdoc +1 -1
  6. data/doc/transactions.rdoc +23 -0
  7. data/lib/sequel/adapters/db2.rb +1 -0
  8. data/lib/sequel/adapters/ibmdb.rb +19 -3
  9. data/lib/sequel/adapters/jdbc.rb +15 -0
  10. data/lib/sequel/adapters/jdbc/derby.rb +1 -5
  11. data/lib/sequel/adapters/jdbc/h2.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  17. data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
  18. data/lib/sequel/adapters/mysql.rb +4 -0
  19. data/lib/sequel/adapters/mysql2.rb +5 -1
  20. data/lib/sequel/adapters/oracle.rb +18 -0
  21. data/lib/sequel/adapters/postgres.rb +11 -1
  22. data/lib/sequel/adapters/shared/access.rb +14 -2
  23. data/lib/sequel/adapters/shared/cubrid.rb +1 -11
  24. data/lib/sequel/adapters/shared/db2.rb +11 -6
  25. data/lib/sequel/adapters/shared/mssql.rb +10 -10
  26. data/lib/sequel/adapters/shared/mysql.rb +11 -1
  27. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
  28. data/lib/sequel/adapters/shared/oracle.rb +16 -15
  29. data/lib/sequel/adapters/shared/postgres.rb +91 -59
  30. data/lib/sequel/adapters/shared/sqlite.rb +1 -4
  31. data/lib/sequel/adapters/tinytds.rb +15 -0
  32. data/lib/sequel/connection_pool/threaded.rb +1 -1
  33. data/lib/sequel/core.rb +10 -0
  34. data/lib/sequel/database/connecting.rb +2 -0
  35. data/lib/sequel/database/misc.rb +46 -4
  36. data/lib/sequel/database/query.rb +33 -14
  37. data/lib/sequel/database/schema_methods.rb +0 -5
  38. data/lib/sequel/dataset/misc.rb +9 -0
  39. data/lib/sequel/dataset/mutation.rb +9 -7
  40. data/lib/sequel/dataset/sql.rb +13 -0
  41. data/lib/sequel/exceptions.rb +3 -0
  42. data/lib/sequel/extensions/connection_validator.rb +1 -1
  43. data/lib/sequel/extensions/date_arithmetic.rb +0 -8
  44. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  45. data/lib/sequel/extensions/named_timezones.rb +18 -2
  46. data/lib/sequel/extensions/pg_array.rb +5 -1
  47. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  48. data/lib/sequel/extensions/pg_hstore.rb +2 -0
  49. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  50. data/lib/sequel/extensions/pg_json.rb +3 -1
  51. data/lib/sequel/extensions/pg_range.rb +2 -0
  52. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  53. data/lib/sequel/extensions/pg_row.rb +2 -0
  54. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  55. data/lib/sequel/extensions/query.rb +18 -22
  56. data/lib/sequel/model/associations.rb +3 -4
  57. data/lib/sequel/model/base.rb +2 -0
  58. data/lib/sequel/plugins/force_encoding.rb +2 -0
  59. data/lib/sequel/plugins/json_serializer.rb +155 -39
  60. data/lib/sequel/plugins/serialization.rb +14 -2
  61. data/lib/sequel/plugins/unlimited_update.rb +31 -0
  62. data/lib/sequel/plugins/validation_class_methods.rb +6 -4
  63. data/lib/sequel/plugins/xml_serializer.rb +133 -30
  64. data/lib/sequel/sql.rb +2 -0
  65. data/lib/sequel/timezones.rb +4 -0
  66. data/lib/sequel/version.rb +1 -1
  67. data/spec/adapters/mysql_spec.rb +0 -11
  68. data/spec/adapters/postgres_spec.rb +86 -54
  69. data/spec/adapters/spec_helper.rb +6 -0
  70. data/spec/core/connection_pool_spec.rb +16 -0
  71. data/spec/core/database_spec.rb +77 -1
  72. data/spec/core/dataset_spec.rb +30 -15
  73. data/spec/core/expression_filters_spec.rb +55 -13
  74. data/spec/core/mock_adapter_spec.rb +4 -0
  75. data/spec/core/schema_spec.rb +0 -2
  76. data/spec/core/spec_helper.rb +5 -0
  77. data/spec/core_extensions_spec.rb +33 -28
  78. data/spec/extensions/constraint_validations_spec.rb +2 -2
  79. data/spec/extensions/core_refinements_spec.rb +12 -12
  80. data/spec/extensions/json_serializer_spec.rb +137 -31
  81. data/spec/extensions/named_timezones_spec.rb +10 -0
  82. data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
  83. data/spec/extensions/pg_json_spec.rb +14 -0
  84. data/spec/extensions/pg_row_spec.rb +11 -0
  85. data/spec/extensions/pretty_table_spec.rb +2 -2
  86. data/spec/extensions/query_spec.rb +11 -8
  87. data/spec/extensions/serialization_spec.rb +20 -0
  88. data/spec/extensions/spec_helper.rb +8 -2
  89. data/spec/extensions/sql_expr_spec.rb +1 -1
  90. data/spec/extensions/unlimited_update_spec.rb +20 -0
  91. data/spec/extensions/xml_serializer_spec.rb +68 -16
  92. data/spec/integration/dataset_test.rb +28 -0
  93. data/spec/integration/spec_helper.rb +6 -0
  94. data/spec/integration/transaction_test.rb +39 -0
  95. data/spec/model/model_spec.rb +1 -1
  96. data/spec/sequel_coverage.rb +15 -0
  97. metadata +8 -3
@@ -240,14 +240,14 @@ describe "constraint_validations extension" do
240
240
  @db.create_table(:foo){String :name; validate{like 'foo%', :name}}
241
241
  sqls = @db.sqls
242
242
  parse_insert(sqls.slice!(1)).should == {:validation_type=>"like", :column=>"name", :table=>"foo", :argument=>'foo%'}
243
- sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name LIKE 'foo%')))"]
243
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name LIKE 'foo%' ESCAPE '\\')))"]
244
244
  end
245
245
 
246
246
  it "should support :ilike constraint validation" do
247
247
  @db.create_table(:foo){String :name; validate{ilike 'foo%', :name}}
248
248
  sqls = @db.sqls
249
249
  parse_insert(sqls.slice!(1)).should == {:validation_type=>"ilike", :column=>"name", :table=>"foo", :argument=>'foo%'}
250
- sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name ILIKE 'foo%')))"]
250
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (UPPER(name) LIKE UPPER('foo%') ESCAPE '\\')))"]
251
251
  end
252
252
 
253
253
  it "should support :unique constraint validation" do
@@ -69,35 +69,35 @@ describe "Core refinements" do
69
69
  end
70
70
 
71
71
  it "should support LIKE via Symbol#like" do
72
- @d.l(:x.like('a')).should == '(x LIKE \'a\')'
72
+ @d.l(:x.like('a')).should == '(x LIKE \'a\' ESCAPE \'\\\')'
73
73
  @d.l(:x.like(/a/)).should == '(x ~ \'a\')'
74
- @d.l(:x.like('a', 'b')).should == '((x LIKE \'a\') OR (x LIKE \'b\'))'
74
+ @d.l(:x.like('a', 'b')).should == '((x LIKE \'a\' ESCAPE \'\\\') OR (x LIKE \'b\' ESCAPE \'\\\'))'
75
75
  @d.l(:x.like(/a/, /b/i)).should == '((x ~ \'a\') OR (x ~* \'b\'))'
76
- @d.l(:x.like('a', /b/)).should == '((x LIKE \'a\') OR (x ~ \'b\'))'
76
+ @d.l(:x.like('a', /b/)).should == '((x LIKE \'a\' ESCAPE \'\\\') OR (x ~ \'b\'))'
77
77
  end
78
78
 
79
79
  it "should support NOT LIKE via Symbol#like and Symbol#~" do
80
- @d.l(~:x.like('a')).should == '(x NOT LIKE \'a\')'
80
+ @d.l(~:x.like('a')).should == '(x NOT LIKE \'a\' ESCAPE \'\\\')'
81
81
  @d.l(~:x.like(/a/)).should == '(x !~ \'a\')'
82
- @d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\') AND (x NOT LIKE \'b\'))'
82
+ @d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\' ESCAPE \'\\\') AND (x NOT LIKE \'b\' ESCAPE \'\\\'))'
83
83
  @d.l(~:x.like(/a/, /b/i)).should == '((x !~ \'a\') AND (x !~* \'b\'))'
84
- @d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\') AND (x !~ \'b\'))'
84
+ @d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\' ESCAPE \'\\\') AND (x !~ \'b\'))'
85
85
  end
86
86
 
87
87
  it "should support ILIKE via Symbol#ilike" do
88
- @d.l(:x.ilike('a')).should == '(x ILIKE \'a\')'
88
+ @d.l(:x.ilike('a')).should == '(UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\')'
89
89
  @d.l(:x.ilike(/a/)).should == '(x ~* \'a\')'
90
- @d.l(:x.ilike('a', 'b')).should == '((x ILIKE \'a\') OR (x ILIKE \'b\'))'
90
+ @d.l(:x.ilike('a', 'b')).should == '((UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\') OR (UPPER(x) LIKE UPPER(\'b\') ESCAPE \'\\\'))'
91
91
  @d.l(:x.ilike(/a/, /b/i)).should == '((x ~* \'a\') OR (x ~* \'b\'))'
92
- @d.l(:x.ilike('a', /b/)).should == '((x ILIKE \'a\') OR (x ~* \'b\'))'
92
+ @d.l(:x.ilike('a', /b/)).should == '((UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\') OR (x ~* \'b\'))'
93
93
  end
94
94
 
95
95
  it "should support NOT ILIKE via Symbol#ilike and Symbol#~" do
96
- @d.l(~:x.ilike('a')).should == '(x NOT ILIKE \'a\')'
96
+ @d.l(~:x.ilike('a')).should == '(UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\')'
97
97
  @d.l(~:x.ilike(/a/)).should == '(x !~* \'a\')'
98
- @d.l(~:x.ilike('a', 'b')).should == '((x NOT ILIKE \'a\') AND (x NOT ILIKE \'b\'))'
98
+ @d.l(~:x.ilike('a', 'b')).should == '((UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\') AND (UPPER(x) NOT LIKE UPPER(\'b\') ESCAPE \'\\\'))'
99
99
  @d.l(~:x.ilike(/a/, /b/i)).should == '((x !~* \'a\') AND (x !~* \'b\'))'
100
- @d.l(~:x.ilike('a', /b/)).should == '((x NOT ILIKE \'a\') AND (x !~* \'b\'))'
100
+ @d.l(~:x.ilike('a', /b/)).should == '((UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\') AND (x !~* \'b\'))'
101
101
  end
102
102
 
103
103
  it "should support sql_expr on arrays with all two pairs" do
@@ -3,12 +3,15 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
3
3
  describe "Sequel::Plugins::JsonSerializer" do
4
4
  before do
5
5
  class ::Artist < Sequel::Model
6
+ unrestrict_primary_key
6
7
  plugin :json_serializer
7
8
  columns :id, :name
8
9
  def_column_accessor :id, :name
10
+ @db_schema = {:id=>{:type=>:integer}}
9
11
  one_to_many :albums
10
12
  end
11
13
  class ::Album < Sequel::Model
14
+ unrestrict_primary_key
12
15
  attr_accessor :blah
13
16
  plugin :json_serializer
14
17
  columns :id, :name, :artist_id
@@ -27,8 +30,8 @@ describe "Sequel::Plugins::JsonSerializer" do
27
30
  end
28
31
 
29
32
  it "should round trip successfully" do
30
- JSON.parse(@artist.to_json).should == @artist
31
- JSON.parse(@album.to_json).should == @album
33
+ Artist.from_json(@artist.to_json).should == @artist
34
+ Album.from_json(@album.to_json).should == @album
32
35
  end
33
36
 
34
37
  it "should handle ruby objects in values" do
@@ -37,52 +40,106 @@ describe "Sequel::Plugins::JsonSerializer" do
37
40
  super(Date.parse(v))
38
41
  end
39
42
  end
40
- JSON.parse(Artist.load(:name=>Date.today).to_json).should == Artist.load(:name=>Date.today)
43
+ Artist.from_json(Artist.load(:name=>Date.today).to_json).should == Artist.load(:name=>Date.today)
44
+ end
45
+
46
+ it "should have .json_create method for creating an instance from a hash parsed from JSON" do
47
+ JSON.parse(@artist.to_json, :create_additions=>true).should == @artist
48
+ JSON.parse(@artist.to_json(:include=>{:albums=>{:include=>{:artist=>{:include=>:albums}}}}), :create_additions=>true).albums.map{|a| a.artist.albums}.should == [[@album]]
49
+ end
50
+
51
+ it "should have .json_create method raise error if not given a hash" do
52
+ proc{Artist.json_create([])}.should raise_error(Sequel::Error)
41
53
  end
42
54
 
43
55
  it "should handle the :only option" do
44
- JSON.parse(@artist.to_json(:only=>:name)).should == Artist.load(:name=>@artist.name)
45
- JSON.parse(@album.to_json(:only=>[:id, :name])).should == Album.load(:id=>@album.id, :name=>@album.name)
56
+ Artist.from_json(@artist.to_json(:only=>:name)).should == Artist.load(:name=>@artist.name)
57
+ Album.from_json(@album.to_json(:only=>[:id, :name])).should == Album.load(:id=>@album.id, :name=>@album.name)
46
58
  end
47
59
 
48
60
  it "should handle the :except option" do
49
- JSON.parse(@artist.to_json(:except=>:id)).should == Artist.load(:name=>@artist.name)
50
- JSON.parse(@album.to_json(:except=>[:id, :artist_id])).should == Album.load(:name=>@album.name)
61
+ Artist.from_json(@artist.to_json(:except=>:id)).should == Artist.load(:name=>@artist.name)
62
+ Album.from_json(@album.to_json(:except=>[:id, :artist_id])).should == Album.load(:name=>@album.name)
51
63
  end
52
64
 
53
65
  it "should handle the :include option for associations" do
54
- JSON.parse(@artist.to_json(:include=>:albums)).albums.should == [@album]
55
- JSON.parse(@album.to_json(:include=>:artist)).artist.should == @artist
66
+ Artist.from_json(@artist.to_json(:include=>:albums), :associations=>:albums).albums.should == [@album]
67
+ Album.from_json(@album.to_json(:include=>:artist), :associations=>:artist).artist.should == @artist
68
+ end
69
+
70
+ it "should raise an error if attempting to parse json when providing array to non-array association or vice-versa" do
71
+ proc{Artist.from_json('{"albums":{"id":1,"name":"RF","artist_id":2,"json_class":"Album"},"id":2,"name":"YJM","json_class":"Artist"}', :associations=>:albums)}.should raise_error(Sequel::Error)
72
+ proc{Album.from_json('{"artist":[{"id":2,"name":"YJM","json_class":"Artist"}],"id":1,"name":"RF","json_class":"Album","artist_id":2}', :associations=>:artist)}.should raise_error(Sequel::Error)
73
+ end
74
+
75
+ it "should raise an error if attempting to parse an array containing non-hashes" do
76
+ proc{Artist.from_json('[{"id":2,"name":"YJM","json_class":"Artist"}, 2]')}.should raise_error(Sequel::Error)
77
+ end
78
+
79
+ it "should raise an error if attempting to parse invalid JSON" do
80
+ begin
81
+ Sequel.instance_eval do
82
+ alias pj parse_json
83
+ def parse_json(v)
84
+ v
85
+ end
86
+ end
87
+ proc{Album.from_json('1')}.should raise_error(Sequel::Error)
88
+ ensure
89
+ Sequel.instance_eval do
90
+ alias parse_json pj
91
+ end
92
+ end
93
+ end
94
+
95
+ it "should handle case where Sequel.parse_json already returns an instance" do
96
+ begin
97
+ Sequel.instance_eval do
98
+ alias pj parse_json
99
+ def parse_json(v)
100
+ Album.load(:id=>3)
101
+ end
102
+ end
103
+ ::Album.from_json('1').should == Album.load(:id=>3)
104
+ ensure
105
+ Sequel.instance_eval do
106
+ alias parse_json pj
107
+ end
108
+ end
56
109
  end
57
110
 
58
111
  it "should handle the :include option for arbitrary attributes" do
59
- JSON.parse(@album.to_json(:include=>:blah)).blah.should == @album.blah
112
+ Album.from_json(@album.to_json(:include=>:blah)).blah.should == @album.blah
60
113
  end
61
114
 
62
115
  it "should handle multiple inclusions using an array for the :include option" do
63
- a = JSON.parse(@album.to_json(:include=>[:blah, :artist]))
116
+ a = Album.from_json(@album.to_json(:include=>[:blah, :artist]), :associations=>:artist)
64
117
  a.blah.should == @album.blah
65
118
  a.artist.should == @artist
66
119
  end
67
120
 
68
121
  it "should handle cascading using a hash for the :include option" do
69
- JSON.parse(@artist.to_json(:include=>{:albums=>{:include=>:artist}})).albums.map{|a| a.artist}.should == [@artist]
70
- JSON.parse(@album.to_json(:include=>{:artist=>{:include=>:albums}})).artist.albums.should == [@album]
122
+ Artist.from_json(@artist.to_json(:include=>{:albums=>{:include=>:artist}}), :associations=>{:albums=>{:associations=>:artist}}).albums.map{|a| a.artist}.should == [@artist]
123
+ Album.from_json(@album.to_json(:include=>{:artist=>{:include=>:albums}}), :associations=>{:artist=>{:associations=>:albums}}).artist.albums.should == [@album]
71
124
 
72
- JSON.parse(@artist.to_json(:include=>{:albums=>{:only=>:name}})).albums.should == [Album.load(:name=>@album.name)]
73
- JSON.parse(@album.to_json(:include=>{:artist=>{:except=>:name}})).artist.should == Artist.load(:id=>@artist.id)
125
+ Artist.from_json(@artist.to_json(:include=>{:albums=>{:only=>:name}}), :associations=>[:albums]).albums.should == [Album.load(:name=>@album.name)]
126
+ Album.from_json(@album.to_json(:include=>{:artist=>{:except=>:name}}), :associations=>[:artist]).artist.should == Artist.load(:id=>@artist.id)
74
127
 
75
- JSON.parse(@artist.to_json(:include=>{:albums=>{:include=>{:artist=>{:include=>:albums}}}})).albums.map{|a| a.artist.albums}.should == [[@album]]
76
- JSON.parse(@album.to_json(:include=>{:artist=>{:include=>{:albums=>{:only=>:name}}}})).artist.albums.should == [Album.load(:name=>@album.name)]
128
+ Artist.from_json(@artist.to_json(:include=>{:albums=>{:include=>{:artist=>{:include=>:albums}}}}), :associations=>{:albums=>{:associations=>{:artist=>{:associations=>:albums}}}}).albums.map{|a| a.artist.albums}.should == [[@album]]
129
+ Album.from_json(@album.to_json(:include=>{:artist=>{:include=>{:albums=>{:only=>:name}}}}), :associations=>{:artist=>{:associations=>:albums}}).artist.albums.should == [Album.load(:name=>@album.name)]
130
+ end
131
+
132
+ it "should automatically cascade parsing for all associations if :all_associations is used" do
133
+ Artist.from_json(@artist.to_json(:include=>{:albums=>{:include=>:artist}}), :all_associations=>true).albums.map{|a| a.artist}.should == [@artist]
77
134
  end
78
135
 
79
136
  it "should handle the :include option cascading with an empty hash" do
80
- JSON.parse(@album.to_json(:include=>{:artist=>{}})).artist.should == @artist
81
- JSON.parse(@album.to_json(:include=>{:blah=>{}})).blah.should == @album.blah
137
+ Album.from_json(@album.to_json(:include=>{:artist=>{}}), :associations=>:artist).artist.should == @artist
138
+ Album.from_json(@album.to_json(:include=>{:blah=>{}})).blah.should == @album.blah
82
139
  end
83
140
 
84
141
  it "should accept a :naked option to not include the JSON.create_id, so parsing yields a plain hash" do
85
- JSON.parse(@album.to_json(:naked=>true)).should == @album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}
142
+ Sequel.parse_json(@album.to_json(:naked=>true)).should == @album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}
86
143
  end
87
144
 
88
145
  it "should support #from_json to set column values" do
@@ -107,35 +164,39 @@ describe "Sequel::Plugins::JsonSerializer" do
107
164
  proc{@album.from_json('{"artist_id": 3}', :fields=>['name'], :missing=>:raise)}.should raise_error(Sequel::Error)
108
165
  end
109
166
 
167
+ it "should have #from_json raise an error if parsed json isn't a hash" do
168
+ proc{@artist.from_json('[]')}.should raise_error(Sequel::Error)
169
+ end
170
+
110
171
  it "should raise an exception for json keys that aren't associations, columns, or setter methods" do
111
172
  Album.send(:undef_method, :blah=)
112
- proc{JSON.parse(@album.to_json(:include=>:blah))}.should raise_error(Sequel::Error)
173
+ proc{Album.from_json(@album.to_json(:include=>:blah))}.should raise_error(Sequel::Error)
113
174
  end
114
175
 
115
176
  it "should support a to_json class and dataset method" do
116
177
  Album.dataset._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
117
178
  Artist.dataset._fetch = {:id=>2, :name=>'YJM'}
118
- JSON.parse(Album.to_json).should == [@album]
119
- JSON.parse(Album.to_json(:include=>:artist)).map{|x| x.artist}.should == [@artist]
120
- JSON.parse(Album.dataset.to_json(:only=>:name)).should == [Album.load(:name=>@album.name)]
179
+ Album.array_from_json(Album.to_json).should == [@album]
180
+ Album.array_from_json(Album.to_json(:include=>:artist), :associations=>:artist).map{|x| x.artist}.should == [@artist]
181
+ Album.array_from_json(Album.dataset.to_json(:only=>:name)).should == [Album.load(:name=>@album.name)]
121
182
  end
122
183
 
123
184
  it "should have dataset to_json method work with naked datasets" do
124
185
  album = @album
125
186
  ds = Album.dataset.naked
126
187
  ds._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
127
- JSON.parse(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
188
+ Sequel.parse_json(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
128
189
  end
129
190
 
130
191
  it "should have dataset to_json method respect :array option for the array to use" do
131
- a = Album.load(:id=>1, :name=>'RF', :artist_id=>3)
132
- JSON.parse(Album.to_json(:array=>[a])).should == [a]
192
+ a = Album.new(:name=>'RF', :artist_id=>3)
193
+ Album.array_from_json(Album.to_json(:array=>[a])).should == [a]
133
194
 
134
195
  a.associations[:artist] = artist = Artist.load(:id=>3, :name=>'YJM')
135
- JSON.parse(Album.to_json(:array=>[a], :include=>:artist)).first.artist.should == artist
196
+ Album.array_from_json(Album.to_json(:array=>[a], :include=>:artist), :associations=>:artist).first.artist.should == artist
136
197
 
137
198
  artist.associations[:albums] = [a]
138
- x = JSON.parse(Artist.to_json(:array=>[artist], :include=>:albums))
199
+ x = Artist.array_from_json(Artist.to_json(:array=>[artist], :include=>:albums), :associations=>[:albums])
139
200
  x.should == [artist]
140
201
  x.first.albums.should == [a]
141
202
  end
@@ -191,12 +252,57 @@ describe "Sequel::Plugins::JsonSerializer" do
191
252
  class ::Artist2 < Artist
192
253
  plugin :json_serializer, :only=>:name
193
254
  end
194
- JSON.parse(Artist2.load(:id=>2, :name=>'YYY').to_json).should == Artist2.load(:name=>'YYY')
255
+ Artist2.from_json(Artist2.load(:id=>2, :name=>'YYY').to_json).should == Artist2.load(:name=>'YYY')
195
256
  class ::Artist3 < Artist2
196
257
  plugin :json_serializer, :naked=>:true
197
258
  end
198
- JSON.parse(Artist3.load(:id=>2, :name=>'YYY').to_json).should == {"name"=>'YYY'}
259
+ Sequel.parse_json(Artist3.load(:id=>2, :name=>'YYY').to_json).should == {"name"=>'YYY'}
199
260
  Object.send(:remove_const, :Artist2)
200
261
  Object.send(:remove_const, :Artist3)
201
262
  end
263
+
264
+ it "should have :associations option take precedence over :all_assocations" do
265
+ Artist.from_json(@artist.to_json(:include=>:albums), :associations=>[], :all_associations=>true, :fields=>[]).associations.should == {}
266
+ end
267
+
268
+ it "should allow overriding of :all_columns options in associated objects" do
269
+ Album.restrict_primary_key
270
+ Artist.from_json(@artist.to_json(:include=>:albums), :associations=>{:albums=>{:fields=>[:id, :name, :artist_id], :missing=>:raise}}, :all_columns=>true).albums.should == [@album]
271
+ end
272
+
273
+ it "should handle typecasting if setter columns exist when using :all_columns" do
274
+ Artist.restrict_primary_key
275
+ Artist.from_json('{"id":"2","name":"YJM","json_class":"Artist"}', :all_columns=>true).should == @artist
276
+ end
277
+
278
+ it "should allow setting columns that are restricted if :all_columns is used" do
279
+ Artist.restrict_primary_key
280
+ Artist.from_json(@artist.to_json, :all_columns=>true).should == @artist
281
+ end
282
+
283
+ it "should raise an error if using :all_columns and non-column is in the JSON" do
284
+ proc{Artist.from_json('{"foo":"bar"}', :all_columns=>true)}.should raise_error(Sequel::Error)
285
+ end
286
+
287
+ it "should raise an error if attempting to set a restricted column and :all_columns is not used" do
288
+ Artist.restrict_primary_key
289
+ proc{Artist.from_json(@artist.to_json)}.should raise_error(Sequel::Error)
290
+ end
291
+
292
+ it "should raise an error if an unsupported association is passed in the :associations option" do
293
+ Artist.association_reflections.delete(:albums)
294
+ proc{Artist.from_json(@artist.to_json(:include=>:albums), :associations=>:albums)}.should raise_error(Sequel::Error)
295
+ end
296
+
297
+ it "should raise an error if using from_json and JSON parsing returns an array" do
298
+ proc{Artist.from_json([@artist].to_json)}.should raise_error(Sequel::Error)
299
+ end
300
+
301
+ it "should raise an error if using array_from_json and JSON parsing does not return an array" do
302
+ proc{Artist.array_from_json(@artist.to_json)}.should raise_error(Sequel::Error)
303
+ end
304
+
305
+ it "should raise an error if using an unsupported :associations option" do
306
+ proc{Artist.from_json(@artist.to_json, :associations=>'')}.should raise_error(Sequel::Error)
307
+ end
202
308
  end
@@ -19,6 +19,7 @@ describe "Sequel named_timezones extension" do
19
19
  Sequel.datetime_class = DateTime
20
20
  end
21
21
  after do
22
+ Sequel.tzinfo_disambiguator = nil
22
23
  Sequel.default_timezone = nil
23
24
  Sequel.datetime_class = Time
24
25
  end
@@ -53,6 +54,15 @@ describe "Sequel named_timezones extension" do
53
54
  dt.offset.should == -7/24.0
54
55
  end
55
56
 
57
+ it "should raise an error for ambiguous timezones by default" do
58
+ proc{Sequel.database_to_application_timestamp('2004-10-31T01:30:00')}.should raise_error(Sequel::InvalidValue)
59
+ end
60
+
61
+ it "should support tzinfo_disambiguator= to handle ambiguous timezones automatically" do
62
+ Sequel.tzinfo_disambiguator = proc{|datetime, periods| periods.first}
63
+ Sequel.database_to_application_timestamp('2004-10-31T01:30:00').should == DateTime.parse('2004-10-30T22:30:00-07:00')
64
+ end
65
+
56
66
  it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
57
67
  dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
58
68
  dt.should == @dt
@@ -62,4 +62,9 @@ describe "pg_auto_parameterize extension" do
62
62
  it "should not auto parameterize when using cursors" do
63
63
  @db[:table].filter(:a=>1).use_cursor.opts[:no_auto_parameterize].should be_true
64
64
  end
65
+
66
+ it "should have a working create_view" do
67
+ @db.create_view :foo, @db[:table].filter(:a=>1)
68
+ @db.sqls.should == ["CREATE VIEW foo AS SELECT * FROM table WHERE (a = 1)"]
69
+ end
65
70
  end
@@ -39,6 +39,20 @@ describe "pg_json extension" do
39
39
  it "should raise an error when attempting to parse invalid json" do
40
40
  proc{@m.parse_json('')}.should raise_error(Sequel::InvalidValue)
41
41
  proc{@m.parse_json('1')}.should raise_error(Sequel::InvalidValue)
42
+
43
+ begin
44
+ Sequel.instance_eval do
45
+ alias pj parse_json
46
+ def parse_json(v)
47
+ v
48
+ end
49
+ end
50
+ proc{@m.parse_json('1')}.should raise_error(Sequel::InvalidValue)
51
+ ensure
52
+ Sequel.instance_eval do
53
+ alias parse_json pj
54
+ end
55
+ end
42
56
  end
43
57
 
44
58
  it "should literalize HStores to strings correctly" do
@@ -144,6 +144,17 @@ describe "pg_row extension" do
144
144
  db.register_row_type(:foo)
145
145
  db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
146
146
  "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
147
+
148
+ begin
149
+ pgnt = Sequel::Postgres::PG_NAMED_TYPES.dup
150
+ Sequel::Postgres::PG_NAMED_TYPES.clear
151
+ db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
152
+ db.reset_conversion_procs
153
+ db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
154
+ "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
155
+ ensure
156
+ Sequel::Postgres::PG_NAMED_TYPES.replace pgnt
157
+ end
147
158
  end
148
159
 
149
160
  it "should handle ArrayRows and HashRows in bound variables" do
@@ -44,7 +44,7 @@ describe "PrettyTable" do
44
44
  @data3 = [
45
45
  {:aaa => 1},
46
46
  {:bb => 2},
47
- {:c => 3}
47
+ {:c => 3.1}
48
48
  ]
49
49
 
50
50
  @output = StringIO.new
@@ -80,7 +80,7 @@ describe "PrettyTable" do
80
80
  Sequel::PrettyTable.print(@data3, [:aaa, :bb, :c])
81
81
  @output.rewind
82
82
  @output.read.should == \
83
- "+---+--+-+\n|aaa|bb|c|\n+---+--+-+\n| 1| | |\n| | 2| |\n| | |3|\n+---+--+-+\n"
83
+ "+---+--+---+\n|aaa|bb|c |\n+---+--+---+\n| 1| | |\n| | 2| |\n| | |3.1|\n+---+--+---+\n"
84
84
  end
85
85
 
86
86
  specify "should print only the specified columns" do
@@ -77,15 +77,18 @@ describe "Dataset#query" do
77
77
  q.class.should == @d.class
78
78
  q.sql.should == "SELECT * FROM xyz ORDER BY stamp"
79
79
  end
80
-
81
- specify "should raise on non-chainable method calls" do
82
- proc {@d.query {first_source}}.should raise_error(Sequel::Error)
80
+
81
+ specify "should support blocks that end in nil" do
82
+ condition = false
83
+ q = @d.query do
84
+ from :xyz
85
+ order_by :stamp if condition
86
+ end
87
+ q.sql.should == "SELECT * FROM xyz"
83
88
  end
84
89
 
85
- specify "should raise on each, insert, update, delete" do
86
- proc {@d.query {each}}.should raise_error(Sequel::Error)
87
- proc {@d.query {insert(:x => 1)}}.should raise_error(Sequel::Error)
88
- proc {@d.query {update(:x => 1)}}.should raise_error(Sequel::Error)
89
- proc {@d.query {delete}}.should raise_error(Sequel::Error)
90
+ specify "should raise on non-chainable method calls" do
91
+ proc {@d.query {row_proc}}.should raise_error(Sequel::Error)
92
+ proc {@d.query {all}}.should raise_error(Sequel::Error)
90
93
  end
91
94
  end
@@ -114,6 +114,26 @@ describe "Serialization plugin" do
114
114
  "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
115
115
  end
116
116
 
117
+ it "should handle old non-base-64 encoded marshal serialization format" do
118
+ @c.set_primary_key :id
119
+ @c.plugin :serialization, :marshal, :abc, :def
120
+ @c.dataset._fetch = [:id => 1, :abc =>Marshal.dump(1), :def =>Marshal.dump('hello')]
121
+
122
+ o = @c.first
123
+ o.abc.should == 1
124
+ o.def.should == "hello"
125
+ end
126
+
127
+ it "should raise exception for bad marshal data" do
128
+ @c.set_primary_key :id
129
+ @c.plugin :serialization, :marshal, :abc, :def
130
+ @c.dataset._fetch = [:id => 1, :abc =>'foo', :def =>'bar']
131
+
132
+ o = @c.first
133
+ proc{o.abc}.should raise_error
134
+ proc{o.def}.should raise_error
135
+ end
136
+
117
137
  it "should translate values to and from json serialization format using accessor methods" do
118
138
  @c.set_primary_key :id
119
139
  @c.plugin :serialization, :json, :abc, :def