sequel 4.26.0 → 4.27.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.
@@ -166,6 +166,14 @@ END_MIG
166
166
  type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
167
167
  [:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
168
168
  if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
169
+ type_hash.delete(:type)
170
+ end
171
+
172
+ unless gen.columns.empty?
173
+ type_hash[:keep_order] = true
174
+ end
175
+
176
+ if type_hash.empty?
169
177
  gen.primary_key(name)
170
178
  else
171
179
  gen.primary_key(name, type_hash)
@@ -378,7 +386,7 @@ END_MIG
378
386
  x.delete(:on_delete) if x[:on_delete] == :no_action
379
387
  x.delete(:on_update) if x[:on_update] == :no_action
380
388
  end
381
- if pkn = primary_key_name
389
+ if (pkn = primary_key_name) && !@primary_key[:keep_order]
382
390
  cols.delete_if{|x| x[:name] == pkn}
383
391
  pk = @primary_key.dup
384
392
  pkname = pk.delete(:name)
@@ -391,6 +399,9 @@ END_MIG
391
399
  strings << if table = c.delete(:table)
392
400
  c.delete(:type) if c[:type] == Integer || c[:type] == 'integer'
393
401
  "foreign_key #{name.inspect}, #{table.inspect}#{opts_inspect(c)}"
402
+ elsif pkn == name
403
+ @db.serial_primary_key_options.each{|k,v| c.delete(k) if v == c[k]}
404
+ "primary_key #{name.inspect}#{opts_inspect(c)}"
394
405
  else
395
406
  type = c.delete(:type)
396
407
  opts = opts_inspect(c)
@@ -1743,6 +1743,32 @@ module Sequel
1743
1743
 
1744
1744
  private
1745
1745
 
1746
+ # Run code directly after the INSERT query, before after_create.
1747
+ # This is only a temporary API, it should not be overridden by external code.
1748
+ def _after_create(pk)
1749
+ @this = nil
1750
+ @new = false
1751
+ @was_new = true
1752
+ end
1753
+
1754
+ # Run code after around_save returns, before calling after_commit.
1755
+ # This is only a temporary API, it should not be overridden by external code.
1756
+ def _after_save(pk)
1757
+ if @was_new
1758
+ @was_new = nil
1759
+ pk ? _save_refresh : changed_columns.clear
1760
+ else
1761
+ @columns_updated = nil
1762
+ end
1763
+ @modified = false
1764
+ end
1765
+
1766
+ # Run code directly after the UPDATE query, before after_update.
1767
+ # This is only a temporary API, it should not be overridden by external code.
1768
+ def _after_update
1769
+ @this = nil
1770
+ end
1771
+
1746
1772
  # Run code before any validation is done, but also run it before saving
1747
1773
  # even if validation is skipped. This is a private hook. It exists so that
1748
1774
  # plugins can set values automatically before validation (as the values
@@ -1860,7 +1886,6 @@ module Sequel
1860
1886
  def _save(opts)
1861
1887
  sh = {:server=>this_server}
1862
1888
  db.after_rollback(sh){after_rollback} if uacr = use_after_commit_rollback
1863
- was_new = false
1864
1889
  pk = nil
1865
1890
  called_save = false
1866
1891
  called_cu = false
@@ -1868,14 +1893,11 @@ module Sequel
1868
1893
  called_save = true
1869
1894
  raise_hook_failure(:before_save) if before_save == false
1870
1895
  if new?
1871
- was_new = true
1872
1896
  around_create do
1873
1897
  called_cu = true
1874
1898
  raise_hook_failure(:before_create) if before_create == false
1875
1899
  pk = _insert
1876
- @this = nil
1877
- @new = false
1878
- @was_new = true
1900
+ _after_create(pk)
1879
1901
  after_create
1880
1902
  true
1881
1903
  end
@@ -1898,7 +1920,7 @@ module Sequel
1898
1920
  changed_columns.reject!{|c| columns.include?(c)}
1899
1921
  end
1900
1922
  _update_columns(@columns_updated)
1901
- @this = nil
1923
+ _after_update
1902
1924
  after_update
1903
1925
  true
1904
1926
  end
@@ -1908,17 +1930,11 @@ module Sequel
1908
1930
  true
1909
1931
  end
1910
1932
  raise_hook_failure(:around_save) unless called_save
1911
- if was_new
1912
- @was_new = nil
1913
- pk ? _save_refresh : changed_columns.clear
1914
- else
1915
- @columns_updated = nil
1916
- end
1917
- @modified = false
1933
+ _after_save(pk)
1918
1934
  db.after_commit(sh){after_commit} if uacr
1919
1935
  self
1920
1936
  end
1921
-
1937
+
1922
1938
  # Refresh the object after saving it, used to get
1923
1939
  # default values of all columns. Separated from _save so it
1924
1940
  # can be overridden to avoid the refresh.
@@ -44,6 +44,13 @@ module Sequel
44
44
  @destroyed = true
45
45
  end
46
46
 
47
+ # Mark current instance as destroyed if the transaction in which this
48
+ # instance is created is rolled back.
49
+ def before_create
50
+ db.after_rollback{@destroyed = true}
51
+ super
52
+ end
53
+
47
54
  # Return ::ActiveModel::Name instance for the class.
48
55
  def model_name
49
56
  model.model_name
@@ -0,0 +1,48 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The before_after_save plugin reorders some internal
4
+ # Sequel operations so they happen before after_create,
5
+ # after_update, and after_save are called, instead of
6
+ # after. These operations are:
7
+ #
8
+ # * Resetting the explicit modified flag
9
+ # * Refreshing the model or clearing changed columns after creation
10
+ #
11
+ # This behavior will become the default in Sequel 5.
12
+ #
13
+ # Usage:
14
+ #
15
+ # # Make all model subclasses perform the operations before after_save
16
+ # Sequel::Model.plugin :before_after_save
17
+ #
18
+ # # Make the Album class perform the operations before after_save
19
+ # Album.plugin :before_after_save
20
+ module BeforeAfterSave
21
+ module InstanceMethods
22
+ private
23
+
24
+ # Refresh and reset modified flag right after INSERT query.
25
+ def _after_create(pk)
26
+ super
27
+ @modified = false
28
+ pk ? _save_refresh : changed_columns.clear
29
+ end
30
+
31
+ # Don't refresh or reset modified flag, as it was already done.
32
+ def _after_save(pk)
33
+ if @was_new
34
+ @was_new = nil
35
+ else
36
+ @columns_updated = nil
37
+ end
38
+ end
39
+
40
+ # Refresh and reset modified flag right after UPDATE query.
41
+ def _after_update
42
+ super
43
+ @modified = false
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,12 +1,16 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # DefaultsSetter is a simple plugin that sets non-nil/NULL default values upon
4
- # initialize:
3
+ # The defaults_setter plugin makes the column getter methods return the default
4
+ # values for new objects, if the values have not already been set. Example:
5
5
  #
6
6
  # # column a default NULL
7
7
  # # column b default 2
8
- # album = Album.new.values # {:b => 2}
9
- # album = Album.new(:a=>1, :b=>3).values # {:a => 1, :b => 3}
8
+ # album = Album.new
9
+ # album.a # => nil
10
+ # album.b # => 2
11
+ # album = Album.new(:a=>1, :b=>3)
12
+ # album.a # => 1
13
+ # album.b # => 3
10
14
  #
11
15
  # Usage:
12
16
  #
@@ -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 = 26
6
+ MINOR = 27
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
@@ -20,6 +20,7 @@ end
20
20
  describe "PostgreSQL", '#create_table' do
21
21
  before do
22
22
  @db = DB
23
+ @db.test_connection
23
24
  DB.sqls.clear
24
25
  end
25
26
  after do
@@ -2868,6 +2869,15 @@ describe 'PostgreSQL json type' do
2868
2869
  end
2869
2870
  end
2870
2871
 
2872
+ if DB.server_version >= 90500 && json_type == :jsonb
2873
+ @db.get(pg_json.call([nil, 2]).op.strip_nulls[1]).must_equal 2
2874
+ @db.get(pg_json.call([nil, 2]).op.pretty).must_equal "[\n null,\n 2\n]"
2875
+ @db.from((jo - 'b').keys.as(:k)).select_order_map(:k).must_equal %w'a'
2876
+ @db.from(jo.delete_path(['b','c'])['b'].keys.as(:k)).select_order_map(:k).must_equal %w'd'
2877
+ @db.from(jo.concat('c'=>'d').keys.as(:k)).select_order_map(:k).must_equal %w'a b c'
2878
+ @db.get(jo.set(%w'a', 'f'=>'g')['a']['f']).must_equal 'g'
2879
+ end
2880
+
2871
2881
  @db.from(jo.keys.as(:k)).select_order_map(:k).must_equal %w'a b'
2872
2882
  @db.from(jo.each).select_order_map(:key).must_equal %w'a b'
2873
2883
  @db.from(jo.each).order(:key).select_map(:value).must_equal [1, {'c'=>2, 'd'=>{'e'=>3}}]
data/spec/bin_spec.rb CHANGED
@@ -203,6 +203,10 @@ END
203
203
  bin(:args=>'-S foo -C', :stderr=>true).must_equal "Error: Cannot specify -S and -C together\n"
204
204
  end
205
205
 
206
+ it "should warn if providing too many arguments" do
207
+ bin(:args=>'-c "" "" 1 2 3 4', :stderr=>true).must_equal "Warning: last 5 arguments ignored\n"
208
+ end
209
+
206
210
  it "should use a mock database if no database is given" do
207
211
  bin(:args=>'-c "print DB.adapter_scheme"', :no_conn=>true).must_equal "mock"
208
212
  end
@@ -2728,6 +2728,23 @@ describe "Dataset#single_record" do
2728
2728
  end
2729
2729
  end
2730
2730
 
2731
+ describe "Dataset#single_record!" do
2732
+ before do
2733
+ @db = Sequel.mock
2734
+ end
2735
+
2736
+ it "should call each and return the first record" do
2737
+ @db.fetch = [{:a=>1}, {:a=>2}]
2738
+ @db[:test].single_record!.must_equal(:a=>1)
2739
+ @db.sqls.must_equal ['SELECT * FROM test']
2740
+ end
2741
+
2742
+ it "should return nil if no record is present" do
2743
+ @db[:test].single_record!.must_equal nil
2744
+ @db.sqls.must_equal ['SELECT * FROM test']
2745
+ end
2746
+ end
2747
+
2731
2748
  describe "Dataset#single_value" do
2732
2749
  before do
2733
2750
  @db = Sequel.mock
@@ -2752,6 +2769,23 @@ describe "Dataset#single_value" do
2752
2769
  end
2753
2770
  end
2754
2771
 
2772
+ describe "Dataset#single_value!" do
2773
+ before do
2774
+ @db = Sequel.mock
2775
+ end
2776
+
2777
+ it "should call each and return the first value of the first record" do
2778
+ @db.fetch = [{:a=>1, :b=>2}, {:a=>3, :b=>4}]
2779
+ @db[:test].single_value!.to_s.must_match /\A(1|2)\z/
2780
+ @db.sqls.must_equal ['SELECT * FROM test']
2781
+ end
2782
+
2783
+ it "should return nil if no records" do
2784
+ @db[:test].single_value!.must_equal nil
2785
+ @db.sqls.must_equal ['SELECT * FROM test']
2786
+ end
2787
+ end
2788
+
2755
2789
  describe "Dataset#get" do
2756
2790
  before do
2757
2791
  @d = Sequel.mock(:fetch=>proc{|s| {:name=>s}})[:test]
@@ -30,6 +30,19 @@ describe Sequel::Schema::Generator do
30
30
  @columns[3][:primary_key].must_equal nil
31
31
  end
32
32
 
33
+ it "should respect existing column order if primary_key :keep_order is used" do
34
+ generator = Sequel::Schema::Generator.new(Sequel.mock) do
35
+ string :title
36
+ primary_key :id, :keep_order=>true
37
+ end
38
+
39
+ columns = generator.columns
40
+ columns.last[:name].must_equal :id
41
+ columns.last[:primary_key].must_equal true
42
+ columns.first[:name].must_equal :title
43
+ columns.first[:primary_key].must_equal nil
44
+ end
45
+
33
46
  it "counts definitions correctly" do
34
47
  @columns.size.must_equal 6
35
48
  @indexes.size.must_equal 2
@@ -111,6 +111,18 @@ describe "DB#create_table" do
111
111
  primary_key :id, :type => :serial, :auto_increment => false
112
112
  end
113
113
  @db.sqls.must_equal ['CREATE TABLE cats (id serial PRIMARY KEY)']
114
+
115
+ @db.create_table(:cats) do
116
+ Integer :a
117
+ primary_key :id
118
+ end
119
+ @db.sqls.must_equal ['CREATE TABLE cats (id integer PRIMARY KEY AUTOINCREMENT, a integer)']
120
+
121
+ @db.create_table(:cats) do
122
+ Integer :a
123
+ primary_key :id, :keep_order=>true
124
+ end
125
+ @db.sqls.must_equal ['CREATE TABLE cats (a integer, id integer PRIMARY KEY AUTOINCREMENT)']
114
126
  end
115
127
 
116
128
  it "should allow naming primary key constraint with :primary_key_constraint_name option" do
@@ -1571,6 +1583,11 @@ describe "Schema Parser" do
1571
1583
  @db.schema(:x).last.last[:auto_increment].must_equal false
1572
1584
  end
1573
1585
 
1586
+ it "should set :auto_increment to true by default if set and not the first column" do
1587
+ meta_def(@db, :schema_parse_table){|*| [[:b, {}], [:a, {:primary_key=>true, :db_type=>'integer'}]]}
1588
+ @db.schema(:x).last.last[:auto_increment].must_equal true
1589
+ end
1590
+
1574
1591
  it "should convert various types of table name arguments" do
1575
1592
  meta_def(@db, :schema_parse_table) do |t, opts|
1576
1593
  [[t, opts]]
@@ -2,122 +2,84 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  begin
4
4
  require 'active_model'
5
- begin
6
- require 'minitest'
7
- if defined?(MiniTest::Unit)
8
- class << MiniTest::Unit
9
- def autorun; end
10
- end
11
- end
12
- if defined?(MiniTest::Test)
13
- test_class = MiniTest::Test
14
- end
15
- rescue
16
- require 'test/unit'
17
- test_class = Test::Unit::TestCase
18
- if Test::Unit.respond_to?(:run=)
19
- Test::Unit.run = false
20
- require 'test/unit/testresult'
21
- end
22
- end
23
5
  rescue LoadError => e
24
6
  skip_warn "active_model plugin: can't load active_model (#{e.class}: #{e})"
25
7
  else
26
8
  describe "ActiveModel plugin" do
27
- it "should be compliant to the ActiveModel spec" do
28
- tc = Class.new(test_class)
29
- tc.class_eval do
30
- define_method(:setup) do
31
- class ::AMLintTest < Sequel::Model
32
- set_primary_key :id
33
- columns :id, :id2
34
- def delete; end
35
- end
36
- module ::Blog
37
- class Post < Sequel::Model
38
- plugin :active_model
39
- end
40
- end
41
- @c = AMLintTest
42
- @c.plugin :active_model
43
- @m = @model = @c.new
44
- @o = @c.load({})
45
- super()
46
- end
47
- def teardown
48
- super
49
- Object.send(:remove_const, :AMLintTest)
50
- Object.send(:remove_const, :Blog)
9
+ before do
10
+ class ::AMLintTest < Sequel::Model
11
+ set_primary_key :id
12
+ columns :id, :id2
13
+ def delete; end
14
+ end
15
+ module ::Blog
16
+ class Post < Sequel::Model
17
+ plugin :active_model
51
18
  end
52
- include ActiveModel::Lint::Tests
19
+ end
20
+ @c = AMLintTest
21
+ @c.plugin :active_model
22
+ @m = @model = @c.new
23
+ @o = @c.load({})
24
+ end
25
+ after do
26
+ Object.send(:remove_const, :AMLintTest)
27
+ Object.send(:remove_const, :Blog)
28
+ end
29
+ include ActiveModel::Lint::Tests
53
30
 
54
- # Should return self, not a proxy object
55
- def test__to_model
56
- assert_equal @m.to_model.object_id, @m.object_id
57
- end
58
-
59
- def test__to_key
60
- assert_equal nil, @m.to_key
61
- @o.id = 1
62
- assert_equal [1], @o.to_key
63
- @o.id = nil
64
- assert_equal nil, @o.to_key
31
+ it ".to_model should return self, not a proxy object" do
32
+ @m.object_id.must_equal @m.to_model.object_id
33
+ end
34
+
35
+ it "#to_key should return a key array, or nil" do
36
+ @o.to_key.must_equal nil
37
+ @o.id = 1
38
+ @o.to_key.must_equal [1]
39
+ @o.id = nil
40
+ @o.to_key.must_equal nil
65
41
 
66
- @c.set_primary_key [:id2, :id]
67
- assert_equal nil, @o.to_key
68
- @o.id = 1
69
- @o.id2 = 2
70
- assert_equal [2, 1], @o.to_key
71
- @o.destroy
72
- assert_equal [2, 1], @o.to_key
73
- @o.id = nil
74
- assert_equal nil, @o.to_key
75
- end
76
-
77
- def test__to_param
78
- assert_equal nil, @m.to_param
79
- @o.id = 1
80
- assert_equal '1', @o.to_param
81
- @c.set_primary_key [:id2, :id]
82
- @o.id2 = 2
83
- assert_equal '2-1', @o.to_param
84
- @o.meta_def(:to_param_joiner){'|'}
85
- assert_equal '2|1', @o.to_param
86
- @o.destroy
87
- assert_equal nil, @o.to_param
88
- end
42
+ @c.set_primary_key [:id2, :id]
43
+ @o.to_key.must_equal nil
44
+ @o.id = 1
45
+ @o.id2 = 2
46
+ @o.to_key.must_equal [2, 1]
47
+ @o.destroy
48
+ @o.to_key.must_equal [2, 1]
49
+ @o.id = nil
50
+ @o.to_key.must_equal nil
51
+ end
52
+
53
+ it "#to_param should return a param string or nil" do
54
+ @o.to_param.must_equal nil
55
+ @o.id = 1
56
+ @o.to_param.must_equal '1'
57
+ @c.set_primary_key [:id2, :id]
58
+ @o.id2 = 2
59
+ @o.to_param.must_equal '2-1'
60
+ @o.meta_def(:to_param_joiner){'|'}
61
+ @o.to_param.must_equal '2|1'
62
+ @o.destroy
63
+ @o.to_param.must_equal nil
64
+ end
89
65
 
90
- def test__persisted?
91
- assert_equal false, @m.persisted?
92
- assert_equal true, @o.persisted?
93
- @m.destroy
94
- @o.destroy
95
- assert_equal false, @m.persisted?
96
- assert_equal false, @o.persisted?
97
- end
66
+ it "#persisted? should return true if the object exists and has not been destroyed" do
67
+ @m.persisted?.must_equal false
68
+ @o.persisted?.must_equal true
69
+ @m.destroy
70
+ @o.destroy
71
+ @m.persisted?.must_equal false
72
+ @o.persisted?.must_equal false
73
+ end
98
74
 
99
- # Should return self, not a proxy object
100
- def test__to_partial_path
101
- assert_equal 'am_lint_tests/am_lint_test', @m.to_partial_path
102
- assert_equal 'blog/posts/post', Blog::Post.new.to_partial_path
103
- end
104
-
105
- end
106
- if defined?(MiniTest::Test) || defined?(MiniTest::Unit)
107
- tc.instance_methods.map{|x| x.to_s}.reject{|n| n !~ /\Atest_/}.each do |m|
108
- i = tc.new(m)
109
- i.setup
110
- i.send(m)
111
- i.teardown
112
- end
113
- else
114
- res = ::Test::Unit::TestResult.new
115
- tc.suite.run(res){}
116
- if res.failure_count > 0
117
- puts res.instance_variable_get(:@failures)
118
- end
119
- res.failure_count.must_equal 0
120
- end
75
+ it "#persisted? should return false if the object is created and the transaction is rolled back" do
76
+ DB.transaction(:rollback=>:always){@m.save}
77
+ @m.persisted?.must_equal false
78
+ end
79
+
80
+ it "#to_partial_path should return a path string" do
81
+ @m.to_partial_path.must_equal 'am_lint_tests/am_lint_test'
82
+ Blog::Post.new.to_partial_path.must_equal 'blog/posts/post'
121
83
  end
122
- end
123
84
  end
85
+ end