sequel 4.26.0 → 4.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +62 -0
- data/README.rdoc +4 -4
- data/bin/sequel +8 -0
- data/doc/opening_databases.rdoc +5 -7
- data/doc/postgresql.rdoc +13 -0
- data/doc/release_notes/4.27.0.txt +78 -0
- data/doc/release_notes/4.28.0.txt +57 -0
- data/doc/release_notes/4.29.0.txt +41 -0
- data/doc/thread_safety.rdoc +1 -1
- data/doc/transactions.rdoc +4 -1
- data/doc/validations.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +9 -3
- data/lib/sequel/adapters/shared/postgres.rb +2 -2
- data/lib/sequel/adapters/sqlanywhere.rb +3 -3
- data/lib/sequel/core.rb +8 -18
- data/lib/sequel/database/query.rb +7 -2
- data/lib/sequel/database/schema_generator.rb +17 -4
- data/lib/sequel/database/transactions.rb +3 -3
- data/lib/sequel/dataset/actions.rb +38 -9
- data/lib/sequel/dataset/sql.rb +8 -3
- data/lib/sequel/extensions/date_arithmetic.rb +3 -0
- data/lib/sequel/extensions/pg_json_ops.rb +58 -5
- data/lib/sequel/extensions/schema_dumper.rb +12 -1
- data/lib/sequel/model/base.rb +32 -16
- data/lib/sequel/plugins/active_model.rb +7 -0
- data/lib/sequel/plugins/before_after_save.rb +48 -0
- data/lib/sequel/plugins/boolean_subsets.rb +56 -0
- data/lib/sequel/plugins/csv_serializer.rb +1 -1
- data/lib/sequel/plugins/defaults_setter.rb +8 -4
- data/lib/sequel/plugins/json_serializer.rb +25 -4
- data/lib/sequel/plugins/list.rb +9 -9
- data/lib/sequel/plugins/subset_conditions.rb +36 -0
- data/lib/sequel/plugins/uuid.rb +72 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +11 -1
- data/spec/bin_spec.rb +4 -0
- data/spec/core/database_spec.rb +35 -0
- data/spec/core/dataset_spec.rb +34 -0
- data/spec/core/schema_generator_spec.rb +13 -0
- data/spec/core/schema_spec.rb +17 -0
- data/spec/extensions/active_model_spec.rb +70 -108
- data/spec/extensions/before_after_save_spec.rb +40 -0
- data/spec/extensions/boolean_subsets_spec.rb +47 -0
- data/spec/extensions/date_arithmetic_spec.rb +17 -0
- data/spec/extensions/json_serializer_spec.rb +7 -0
- data/spec/extensions/list_spec.rb +11 -0
- data/spec/extensions/pg_json_ops_spec.rb +46 -0
- data/spec/extensions/schema_dumper_spec.rb +18 -0
- data/spec/extensions/subset_conditions_spec.rb +38 -0
- data/spec/extensions/uuid_spec.rb +106 -0
- data/spec/integration/dataset_test.rb +14 -0
- data/spec/integration/prepared_statement_test.rb +3 -3
- data/spec/integration/schema_test.rb +7 -1
- data/spec/integration/transaction_test.rb +22 -0
- data/spec/model/class_dataset_methods_spec.rb +4 -0
- data/spec/model/model_spec.rb +1 -1
- data/spec/model/record_spec.rb +7 -1
- metadata +16 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Plugins
|
|
5
|
+
# The uuid plugin creates hooks that automatically create a uuid for every
|
|
6
|
+
# instance. Note that this uses SecureRandom.uuid to create UUIDs, and
|
|
7
|
+
# that method is not defined on ruby 1.8.7. If you would like to use this
|
|
8
|
+
# on ruby 1.8.7, you need to override the Model#create_uuid private method
|
|
9
|
+
# to return a valid uuid.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
#
|
|
13
|
+
# # Uuid all model instances using +uuid+
|
|
14
|
+
# # (called before loading subclasses)
|
|
15
|
+
# Sequel::Model.plugin :uuid
|
|
16
|
+
#
|
|
17
|
+
# # Uuid Album instances, with custom column name
|
|
18
|
+
# Album.plugin :uuid, :field=>my_uuid
|
|
19
|
+
module Uuid
|
|
20
|
+
# Configure the plugin by setting the available options. Note that
|
|
21
|
+
# if this method is run more than once, previous settings are ignored,
|
|
22
|
+
# and it will just use the settings given or the default settings. Options:
|
|
23
|
+
# :field :: The field to hold the uuid (default: :uuid)
|
|
24
|
+
# :force :: Whether to overwrite an existing uuid (default: false)
|
|
25
|
+
def self.configure(model, opts=OPTS)
|
|
26
|
+
model.instance_eval do
|
|
27
|
+
@uuid_field = opts[:field]||:uuid
|
|
28
|
+
@uuid_overwrite = opts[:force]||false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module ClassMethods
|
|
33
|
+
# The field to store the uuid
|
|
34
|
+
attr_reader :uuid_field
|
|
35
|
+
|
|
36
|
+
# Whether to overwrite the create uuid if it already exists
|
|
37
|
+
def uuid_overwrite?
|
|
38
|
+
@uuid_overwrite
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Plugins.inherited_instance_variables(self, :@uuid_field=>nil, :@uuid_overwrite=>nil)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module InstanceMethods
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Set the uuid when creating
|
|
48
|
+
def _before_validation
|
|
49
|
+
set_uuid if new?
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Create a new UUID. This method can be overridden to use a separate
|
|
54
|
+
# method for creating UUIDs. Note that this method does not work on
|
|
55
|
+
# ruby 1.8.7, you will have to override it if you are using ruby 1.8.7.
|
|
56
|
+
def create_uuid
|
|
57
|
+
SecureRandom.uuid
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# If the object has accessor methods for the uuid field, and the uuid
|
|
61
|
+
# value is nil or overwriting it is allowed, set the uuid.
|
|
62
|
+
def set_uuid(uuid=create_uuid)
|
|
63
|
+
field = model.uuid_field
|
|
64
|
+
meth = :"#{field}="
|
|
65
|
+
if respond_to?(field) && respond_to?(meth) && (model.uuid_overwrite? || get_column_value(field).nil?)
|
|
66
|
+
set_column_value(meth, uuid)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/sequel/version.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Sequel
|
|
|
3
3
|
MAJOR = 4
|
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
5
5
|
# release, generally around once a month.
|
|
6
|
-
MINOR =
|
|
6
|
+
MINOR = 29
|
|
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
|
|
@@ -986,7 +987,7 @@ describe "A PostgreSQL database" do
|
|
|
986
987
|
t2 = "ruby sequel " * 1000
|
|
987
988
|
@db[:posts].insert(:title=>t1)
|
|
988
989
|
@db[:posts].insert(:title=>t2)
|
|
989
|
-
@db[:posts].full_text_search(:title, 'ruby & sequel', :rank=>true).select_map(:title).must_equal [
|
|
990
|
+
@db[:posts].full_text_search(:title, 'ruby & sequel', :rank=>true).select_map(:title).must_equal [t2, t1]
|
|
990
991
|
end
|
|
991
992
|
|
|
992
993
|
it "should support spatial indexes" 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
|
data/spec/core/database_spec.rb
CHANGED
|
@@ -614,6 +614,17 @@ describe "Database#table_exists?" do
|
|
|
614
614
|
db.table_exists?(:b).must_equal true
|
|
615
615
|
db.table_exists?(:c).must_equal true
|
|
616
616
|
end
|
|
617
|
+
|
|
618
|
+
it "should use a savepoint if inside a transaction" do
|
|
619
|
+
db = Sequel.mock(:fetch=>[Sequel::Error, [], [{:a=>1}]])
|
|
620
|
+
def db.supports_savepoints?; true end
|
|
621
|
+
db.transaction do
|
|
622
|
+
db.table_exists?(:a).must_equal false
|
|
623
|
+
end
|
|
624
|
+
db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SELECT NULL AS nil FROM a LIMIT 1", "ROLLBACK TO SAVEPOINT autopoint_1", "COMMIT"]
|
|
625
|
+
db.table_exists?(:b).must_equal true
|
|
626
|
+
db.table_exists?(:c).must_equal true
|
|
627
|
+
end
|
|
617
628
|
end
|
|
618
629
|
|
|
619
630
|
DatabaseTransactionSpecs = shared_description do
|
|
@@ -964,6 +975,30 @@ describe "Database#transaction with savepoint support" do
|
|
|
964
975
|
|
|
965
976
|
include DatabaseTransactionSpecs
|
|
966
977
|
|
|
978
|
+
it "should support :retry_on option for automatically retrying transactions when using :savepoint option" do
|
|
979
|
+
a = []
|
|
980
|
+
@db.transaction do
|
|
981
|
+
@db.transaction(:retry_on=>Sequel::SerializationFailure, :savepoint=>true) do
|
|
982
|
+
a << 1
|
|
983
|
+
raise Sequel::SerializationFailure if a.length == 1
|
|
984
|
+
end
|
|
985
|
+
end
|
|
986
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "ROLLBACK TO SAVEPOINT autopoint_1", "SAVEPOINT autopoint_1", "RELEASE SAVEPOINT autopoint_1", "COMMIT"]
|
|
987
|
+
a.must_equal [1, 1]
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
it "should support :retry_on option for automatically retrying transactions inside an :auto_savepoint transaction" do
|
|
991
|
+
a = []
|
|
992
|
+
@db.transaction(:auto_savepoint=>true) do
|
|
993
|
+
@db.transaction(:retry_on=>Sequel::SerializationFailure) do
|
|
994
|
+
a << 1
|
|
995
|
+
raise Sequel::SerializationFailure if a.length == 1
|
|
996
|
+
end
|
|
997
|
+
end
|
|
998
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "ROLLBACK TO SAVEPOINT autopoint_1", "SAVEPOINT autopoint_1", "RELEASE SAVEPOINT autopoint_1", "COMMIT"]
|
|
999
|
+
a.must_equal [1, 1]
|
|
1000
|
+
end
|
|
1001
|
+
|
|
967
1002
|
it "should support after_commit inside savepoints" do
|
|
968
1003
|
@db.transaction do
|
|
969
1004
|
@db.after_commit{@db.execute('foo')}
|
data/spec/core/dataset_spec.rb
CHANGED
|
@@ -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
|
data/spec/core/schema_spec.rb
CHANGED
|
@@ -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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::BeforeAfterSave" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel.mock(:numrows=>1, :fetch=>{:id=>1, :name=>'b'})
|
|
6
|
+
@c = Class.new(Sequel::Model(@db[:test]))
|
|
7
|
+
@ds = @c.dataset
|
|
8
|
+
@c.columns :id, :name
|
|
9
|
+
@c.plugin :before_after_save
|
|
10
|
+
@c.plugin :instance_hooks
|
|
11
|
+
@o = @c.new
|
|
12
|
+
@db.sqls
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should reset modified flag before calling after hooks" do
|
|
16
|
+
a = []
|
|
17
|
+
@o.after_create_hook{@o.modified?.must_equal false; a << 1}
|
|
18
|
+
@o.after_save_hook{@o.modified?.must_equal false; a << 2}
|
|
19
|
+
|
|
20
|
+
@o.modified!
|
|
21
|
+
@o.save
|
|
22
|
+
a.must_equal [1, 2]
|
|
23
|
+
|
|
24
|
+
@o.after_save_hook{@o.modified?.must_equal false; a << 2}
|
|
25
|
+
@o.after_update_hook{@o.modified?.must_equal false; a << 3}
|
|
26
|
+
a = []
|
|
27
|
+
@o.modified!
|
|
28
|
+
@o.save
|
|
29
|
+
a.must_equal [3, 2]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should refresh the instance before calling after hooks" do
|
|
33
|
+
a = []
|
|
34
|
+
@o.after_create_hook{@o.values.must_equal(:id=>1, :name=>'b'); a << 1}
|
|
35
|
+
@o.after_save_hook{@o.values.must_equal(:id=>1, :name=>'b'); a << 2}
|
|
36
|
+
|
|
37
|
+
@o.save
|
|
38
|
+
a.must_equal [1, 2]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "boolean_subsets plugin" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel::Database.new
|
|
6
|
+
def @db.supports_schema_parsing?() true end
|
|
7
|
+
def @db.schema(*args)
|
|
8
|
+
[[:asdaf9898as, {}], [:active, {:type=>:boolean}]]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
@c = Class.new(Sequel::Model(@db[:items]))
|
|
12
|
+
@p = proc do
|
|
13
|
+
@columns = [:asdaf9898as, :active]
|
|
14
|
+
def columns; @columns; end
|
|
15
|
+
end
|
|
16
|
+
@c.instance_eval(&@p)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should create subsets only for boolean attributes" do
|
|
20
|
+
@c.plugin(:boolean_subsets)
|
|
21
|
+
@c.active.sql.must_equal "SELECT * FROM items WHERE (active IS TRUE)"
|
|
22
|
+
@c.respond_to?(:asdaf9898as).must_equal false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should handle a block passed to the plugin" do
|
|
26
|
+
@c.plugin(:boolean_subsets){|c| ["where_#{c}", c]}
|
|
27
|
+
@c.where_active.sql.must_equal "SELECT * FROM items WHERE active"
|
|
28
|
+
@c.respond_to?(:active).must_equal false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should create boolean subsets when set_dataset is called" do
|
|
32
|
+
c = Class.new(Sequel::Model(@db))
|
|
33
|
+
c.instance_eval(&@p)
|
|
34
|
+
c.plugin(:boolean_subsets)
|
|
35
|
+
c.respond_to?(:active).must_equal false
|
|
36
|
+
|
|
37
|
+
c.set_dataset(@db[:items])
|
|
38
|
+
c.active.sql.must_equal "SELECT * FROM items WHERE (active IS TRUE)"
|
|
39
|
+
c.respond_to?(:asdaf9898as).must_equal false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should handle cases where getting the columns raises an error" do
|
|
43
|
+
@c.meta_def(:columns){raise Sequel::Error}
|
|
44
|
+
@c.plugin(:boolean_subsets)
|
|
45
|
+
@c.respond_to?(:active).must_equal false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -65,6 +65,23 @@ describe "date_arithmetic extension" do
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
it "should use existing method" do
|
|
69
|
+
db = Sequel.mock
|
|
70
|
+
db.extend_datasets do
|
|
71
|
+
def date_add_sql_append(sql, da)
|
|
72
|
+
interval = ''
|
|
73
|
+
each_valid_interval_unit(da.interval, Sequel::SQL::DateAdd::DatasetMethods::DEF_DURATION_UNITS) do |value, sql_unit|
|
|
74
|
+
interval << "#{value} #{sql_unit} "
|
|
75
|
+
end
|
|
76
|
+
literal_append(sql, Sequel.function(:da, da.expr, interval))
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
db.extension :date_arithmetic
|
|
80
|
+
db.literal(Sequel.date_add(:a, @h0)).must_equal "da(a, '')"
|
|
81
|
+
db.literal(Sequel.date_add(:a, @h1)).must_equal "da(a, '1 days ')"
|
|
82
|
+
db.literal(Sequel.date_add(:a, @h2)).must_equal "da(a, '1 years 1 months 1 days 1 hours 1 minutes 1 seconds ')"
|
|
83
|
+
end
|
|
84
|
+
|
|
68
85
|
it "should correctly literalize on Postgres" do
|
|
69
86
|
db = dbf.call(:postgres)
|
|
70
87
|
db.literal(Sequel.date_add(:a, @h0)).must_equal "CAST(a AS timestamp)"
|
|
@@ -43,6 +43,13 @@ describe "Sequel::Plugins::JsonSerializer" do
|
|
|
43
43
|
Artist.from_json(Artist.load(:name=>Date.today).to_json).must_equal Artist.load(:name=>Date.today)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
it "should support setting json_serializer_opts on models" do
|
|
47
|
+
@artist.json_serializer_opts(:only=>:name)
|
|
48
|
+
Sequel.parse_json([@artist].to_json).must_equal [{'name'=>@artist.name}]
|
|
49
|
+
@artist.json_serializer_opts(:include=>{:albums=>{:only=>:name}})
|
|
50
|
+
Sequel.parse_json([@artist].to_json).must_equal [{'name'=>@artist.name, 'albums'=>[{'name'=>@album.name}]}]
|
|
51
|
+
end
|
|
52
|
+
|
|
46
53
|
it "should handle the :only option" do
|
|
47
54
|
Artist.from_json(@artist.to_json(:only=>:name)).must_equal Artist.load(:name=>@artist.name)
|
|
48
55
|
Album.from_json(@album.to_json(:only=>[:id, :name])).must_equal Album.load(:id=>@album.id, :name=>@album.name)
|
|
@@ -264,4 +264,15 @@ describe "List plugin" do
|
|
|
264
264
|
@o.prev(-1).must_equal @c.load(:id=>9, :position=>4)
|
|
265
265
|
@db.sqls.must_equal ["SELECT * FROM items WHERE (position = 4) ORDER BY position LIMIT 1"]
|
|
266
266
|
end
|
|
267
|
+
|
|
268
|
+
it "should work correctly with validation on position" do
|
|
269
|
+
@c.class_eval do
|
|
270
|
+
def validate
|
|
271
|
+
super
|
|
272
|
+
errors.add(:position, "not set") unless position
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
@c.create
|
|
276
|
+
@db.sqls.must_equal ["SELECT max(position) AS max FROM items LIMIT 1", "INSERT INTO items (position) VALUES (2)", "SELECT * FROM items WHERE (id = 10) ORDER BY position LIMIT 1"]
|
|
277
|
+
end
|
|
267
278
|
end
|
|
@@ -108,6 +108,11 @@ describe "Sequel::Postgres::JSONOp" do
|
|
|
108
108
|
@l[@jb.array_elements_text].must_equal "jsonb_array_elements_text(j)"
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
it "should have #strip_nulls use the json_strip_nulls function" do
|
|
112
|
+
@l[@j.strip_nulls].must_equal "json_strip_nulls(j)"
|
|
113
|
+
@l[@jb.strip_nulls].must_equal "jsonb_strip_nulls(j)"
|
|
114
|
+
end
|
|
115
|
+
|
|
111
116
|
it "should have #typeof use the json_typeof function" do
|
|
112
117
|
@l[@j.typeof].must_equal "json_typeof(j)"
|
|
113
118
|
@l[@jb.typeof].must_equal "jsonb_typeof(j)"
|
|
@@ -173,6 +178,47 @@ describe "Sequel::Postgres::JSONOp" do
|
|
|
173
178
|
@l[@jb.contained_by([1, 2])].must_equal "(j <@ '[1,2]'::jsonb)"
|
|
174
179
|
end
|
|
175
180
|
|
|
181
|
+
it "#concat should use the || operator" do
|
|
182
|
+
@l[@jb.concat(:h1)].must_equal "(j || h1)"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "#concat should handle hashes" do
|
|
186
|
+
@l[@jb.concat('a'=>'b')].must_equal "(j || '{\"a\":\"b\"}'::jsonb)"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "#concat should handle arrays" do
|
|
190
|
+
@l[@jb.concat([1, 2])].must_equal "(j || '[1,2]'::jsonb)"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "#set should use the jsonb_set function" do
|
|
194
|
+
@l[@jb.set(:a, :h)].must_equal "jsonb_set(j, a, h, true)"
|
|
195
|
+
@l[@jb.set(:a, :h, false)].must_equal "jsonb_set(j, a, h, false)"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "#set should handle hashes" do
|
|
199
|
+
@l[@jb.set(:a, 'a'=>'b')].must_equal "jsonb_set(j, a, '{\"a\":\"b\"}'::jsonb, true)"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "#set should handle arrays" do
|
|
203
|
+
@l[@jb.set(%w'a b', [1, 2])].must_equal "jsonb_set(j, ARRAY['a','b'], '[1,2]'::jsonb, true)"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it "#pretty should use the jsonb_pretty function" do
|
|
207
|
+
@l[@jb.pretty].must_equal "jsonb_pretty(j)"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it "#- should use the - operator" do
|
|
211
|
+
@l[@jb - 1].must_equal "(j - 1)"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "#delete_path should use the #- operator" do
|
|
215
|
+
@l[@jb.delete_path(:a)].must_equal "(j #- a)"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it "#delete_path should handle arrays" do
|
|
219
|
+
@l[@jb.delete_path(['a'])].must_equal "(j #- ARRAY['a'])"
|
|
220
|
+
end
|
|
221
|
+
|
|
176
222
|
it "#has_key? and aliases should use the ? operator" do
|
|
177
223
|
@l[@jb.has_key?('a')].must_equal "(j ? 'a')"
|
|
178
224
|
@l[@jb.include?('a')].must_equal "(j ? 'a')"
|