epugh-sequel 0.0.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 (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,126 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe String do
4
+ deprec_specify "#camelize and #camelcase should transform the word to CamelCase" do
5
+ "egg_and_hams".camelize.should == "EggAndHams"
6
+ "egg_and_hams".camelize(false).should == "eggAndHams"
7
+ "post".camelize.should == "Post"
8
+ "post".camelcase.should == "Post"
9
+ end
10
+
11
+ deprec_specify "#constantize should eval the string to get a constant" do
12
+ "String".constantize.should == String
13
+ "String::Inflections".constantize.should == String::Inflections
14
+ proc{"BKSDDF".constantize}.should raise_error
15
+ proc{"++A++".constantize}.should raise_error
16
+ end
17
+
18
+ deprec_specify "#dasherize should transform underscores to dashes" do
19
+ "egg_and_hams".dasherize.should == "egg-and-hams"
20
+ "post".dasherize.should == "post"
21
+ end
22
+
23
+ deprec_specify "#demodulize should remove any preceding modules" do
24
+ "String::Inflections::Blah".demodulize.should == "Blah"
25
+ "String::Inflections".demodulize.should == "Inflections"
26
+ "String".demodulize.should == "String"
27
+ end
28
+
29
+ deprec_specify "#humanize should remove _i, transform underscore to spaces, and capitalize" do
30
+ "egg_and_hams".humanize.should == "Egg and hams"
31
+ "post".humanize.should == "Post"
32
+ "post_id".humanize.should == "Post"
33
+ end
34
+
35
+ deprec_specify "#titleize and #titlecase should underscore, humanize, and capitalize all words" do
36
+ "egg-and: hams".titleize.should == "Egg And: Hams"
37
+ "post".titleize.should == "Post"
38
+ "post".titlecase.should == "Post"
39
+ end
40
+
41
+ deprec_specify "#underscore should add underscores between CamelCased words, change :: to / and - to _, and downcase" do
42
+ "EggAndHams".underscore.should == "egg_and_hams"
43
+ "EGGAndHams".underscore.should == "egg_and_hams"
44
+ "Egg::And::Hams".underscore.should == "egg/and/hams"
45
+ "post".underscore.should == "post"
46
+ "post-id".underscore.should == "post_id"
47
+ end
48
+
49
+ deprec_specify "#pluralize should transform words from singular to plural" do
50
+ "post".pluralize.should == "posts"
51
+ "octopus".pluralize.should =="octopi"
52
+ "the blue mailman".pluralize.should == "the blue mailmen"
53
+ "CamelOctopus".pluralize.should == "CamelOctopi"
54
+ end
55
+
56
+ deprec_specify "#singularize should transform words from plural to singular" do
57
+ "posts".singularize.should == "post"
58
+ "octopi".singularize.should == "octopus"
59
+ "the blue mailmen".singularize.should == "the blue mailman"
60
+ "CamelOctopi".singularize.should == "CamelOctopus"
61
+ end
62
+
63
+ deprec_specify "#tableize should transform class names to table names" do
64
+ "RawScaledScorer".tableize.should == "raw_scaled_scorers"
65
+ "egg_and_ham".tableize.should == "egg_and_hams"
66
+ "fancyCategory".tableize.should == "fancy_categories"
67
+ end
68
+
69
+ deprec_specify "#classify should tranform table names to class names" do
70
+ "egg_and_hams".classify.should == "EggAndHam"
71
+ "post".classify.should == "Post"
72
+ end
73
+
74
+ deprec_specify "#foreign_key should create a foreign key name from a class name" do
75
+ "Message".foreign_key.should == "message_id"
76
+ "Message".foreign_key(false).should == "messageid"
77
+ "Admin::Post".foreign_key.should == "post_id"
78
+ end
79
+ end
80
+
81
+ describe String::Inflections do
82
+ before do
83
+ deprec do
84
+ @plurals, @singulars, @uncountables = String.inflections.plurals.dup, String.inflections.singulars.dup, String.inflections.uncountables.dup
85
+ end
86
+ end
87
+ after do
88
+ deprec do
89
+ String.inflections.plurals.replace(@plurals)
90
+ String.inflections.singulars.replace(@singulars)
91
+ String.inflections.uncountables.replace(@uncountables)
92
+ Sequel.inflections.plurals.replace(@plurals)
93
+ Sequel.inflections.singulars.replace(@singulars)
94
+ Sequel.inflections.uncountables.replace(@uncountables)
95
+ end
96
+ end
97
+
98
+ deprec_specify "should be possible to clear the list of singulars, plurals, and uncountables" do
99
+ String.inflections.clear(:plurals)
100
+ String.inflections.plurals.should == []
101
+ String.inflections.plural('blah', 'blahs')
102
+ String.inflections.clear
103
+ String.inflections.plurals.should == []
104
+ String.inflections.singulars.should == []
105
+ String.inflections.uncountables.should == []
106
+ end
107
+
108
+ deprec_specify "should be able to specify new inflection rules" do
109
+ String.inflections do |i|
110
+ i.plural(/xx$/i, 'xxx')
111
+ i.singular(/ttt$/i, 'tt')
112
+ i.irregular('yy', 'yyy')
113
+ i.uncountable(%w'zz')
114
+ end
115
+ 'roxx'.pluralize.should == 'roxxx'
116
+ 'rottt'.singularize.should == 'rott'
117
+ 'yy'.pluralize.should == 'yyy'
118
+ 'yyy'.singularize.should == 'yy'
119
+ 'zz'.pluralize.should == 'zz'
120
+ 'zz'.singularize.should == 'zz'
121
+ end
122
+
123
+ deprec_specify "should be yielded and returned by String.inflections" do
124
+ String.inflections{|i| i.should == String::Inflections}.should == String::Inflections
125
+ end
126
+ end
@@ -0,0 +1,588 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model do
4
+ it "should have class method aliased as model" do
5
+ Sequel::Model.instance_methods.collect{|x| x.to_s}.should include("model")
6
+
7
+ model_a = Class.new(Sequel::Model(:items))
8
+ model_a.new.model.should be(model_a)
9
+ end
10
+
11
+ it "should be associated with a dataset" do
12
+ model_a = Class.new(Sequel::Model) { set_dataset MODEL_DB[:as] }
13
+
14
+ model_a.dataset.should be_a_kind_of(MockDataset)
15
+ model_a.dataset.opts[:from].should == [:as]
16
+
17
+ model_b = Class.new(Sequel::Model) { set_dataset MODEL_DB[:bs] }
18
+
19
+ model_b.dataset.should be_a_kind_of(MockDataset)
20
+ model_b.dataset.opts[:from].should == [:bs]
21
+
22
+ model_a.dataset.opts[:from].should == [:as]
23
+ end
24
+
25
+ end
26
+
27
+ describe Sequel::Model, "dataset & schema" do
28
+ before do
29
+ @model = Class.new(Sequel::Model(:items))
30
+ end
31
+
32
+ it "creates dynamic model subclass with set table name" do
33
+ @model.table_name.should == :items
34
+ end
35
+
36
+ it "defaults to primary key of id" do
37
+ @model.primary_key.should == :id
38
+ end
39
+
40
+ it "allow primary key change" do
41
+ @model.set_primary_key :ssn
42
+ @model.primary_key.should == :ssn
43
+ end
44
+
45
+ it "allows dataset change" do
46
+ @model.set_dataset(MODEL_DB[:foo])
47
+ @model.table_name.should == :foo
48
+ end
49
+
50
+ it "set_dataset should take a symbol" do
51
+ @model.db = MODEL_DB
52
+ @model.set_dataset(:foo)
53
+ @model.table_name.should == :foo
54
+ end
55
+
56
+ it "set_dataset should raise an error unless given a Symbol or Dataset" do
57
+ proc{@model.set_dataset(Object.new)}.should raise_error(Sequel::Error)
58
+ end
59
+
60
+ it "set_dataset should add the destroy method to the dataset" do
61
+ ds = MODEL_DB[:foo]
62
+ ds.should_not respond_to(:destroy)
63
+ @model.set_dataset(ds)
64
+ ds.should respond_to(:destroy)
65
+ end
66
+
67
+ deprec_specify "sets schema with implicit table name" do
68
+ @model.set_schema do
69
+ primary_key :ssn, :string
70
+ end
71
+ @model.primary_key.should == :ssn
72
+ @model.table_name.should == :items
73
+ end
74
+
75
+ deprec_specify "sets schema with explicit table name" do
76
+ @model.set_schema :foo do
77
+ primary_key :id
78
+ end
79
+ @model.primary_key.should == :id
80
+ @model.table_name.should == :foo
81
+ end
82
+
83
+ it "doesn't raise an error on set_dataset if there is an error raised getting the schema" do
84
+ @model.meta_def(:get_db_schema){raise Sequel::Error}
85
+ proc{@model.set_dataset(MODEL_DB[:foo])}.should_not raise_error
86
+ end
87
+
88
+ it "doesn't raise an error on inherited if there is an error setting the dataset" do
89
+ @model.meta_def(:set_dataset){raise Sequel::Error}
90
+ proc{Class.new(@model)}.should_not raise_error
91
+ end
92
+ end
93
+
94
+ describe Sequel::Model, "#sti_key" do
95
+ before do
96
+ deprec do
97
+ class ::StiTest < Sequel::Model
98
+ def kind=(x); self[:kind] = x; end
99
+ def refresh; end
100
+ set_sti_key :kind
101
+ end
102
+ end
103
+ class ::StiTestSub1 < StiTest
104
+ end
105
+ class ::StiTestSub2 < StiTest
106
+ end
107
+ @ds = StiTest.dataset
108
+ MODEL_DB.reset
109
+ end
110
+
111
+ deprec_specify "should return rows with the correct class based on the polymorphic_key value" do
112
+ def @ds.fetch_rows(sql)
113
+ yield({:kind=>'StiTest'})
114
+ yield({:kind=>'StiTestSub1'})
115
+ yield({:kind=>'StiTestSub2'})
116
+ end
117
+ StiTest.all.collect{|x| x.class}.should == [StiTest, StiTestSub1, StiTestSub2]
118
+ end
119
+
120
+ deprec_specify "should fallback to the main class if polymophic_key value is NULL" do
121
+ def @ds.fetch_rows(sql)
122
+ yield({:kind=>nil})
123
+ end
124
+ StiTest.all.collect{|x| x.class}.should == [StiTest]
125
+ end
126
+
127
+ deprec_specify "should fallback to the main class if the given class does not exist" do
128
+ def @ds.fetch_rows(sql)
129
+ yield({:kind=>'StiTestSub3'})
130
+ end
131
+ StiTest.all.collect{|x| x.class}.should == [StiTest]
132
+ end
133
+
134
+ deprec_specify "should add a before_create hook that sets the model class name for the key" do
135
+ StiTest.new.save
136
+ StiTestSub1.new.save
137
+ StiTestSub2.new.save
138
+ MODEL_DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTest')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub2')"]
139
+ end
140
+
141
+ deprec_specify "should add a filter to model datasets inside subclasses hook to only retreive objects with the matching key" do
142
+ StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
143
+ StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (kind = 'StiTestSub1')"
144
+ StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (kind = 'StiTestSub2')"
145
+ end
146
+ end
147
+
148
+ describe Sequel::Model, "constructor" do
149
+
150
+ before(:each) do
151
+ @m = Class.new(Sequel::Model)
152
+ @m.columns :a, :b
153
+ end
154
+
155
+ it "should accept a hash" do
156
+ m = @m.new(:a => 1, :b => 2)
157
+ m.values.should == {:a => 1, :b => 2}
158
+ m.should be_new
159
+ end
160
+
161
+ it "should accept a block and yield itself to the block" do
162
+ block_called = false
163
+ m = @m.new {|i| block_called = true; i.should be_a_kind_of(@m); i.values[:a] = 1}
164
+
165
+ block_called.should be_true
166
+ m.values[:a].should == 1
167
+ end
168
+
169
+ end
170
+
171
+ describe Sequel::Model, "new" do
172
+
173
+ before(:each) do
174
+ @m = Class.new(Sequel::Model) do
175
+ set_dataset MODEL_DB[:items]
176
+ columns :x, :id
177
+ end
178
+ end
179
+
180
+ it "should be marked as new?" do
181
+ o = @m.new
182
+ o.should be_new
183
+ end
184
+
185
+ it "should not be marked as new? once it is saved" do
186
+ o = @m.new(:x => 1)
187
+ o.should be_new
188
+ o.save
189
+ o.should_not be_new
190
+ end
191
+
192
+ it "should use the last inserted id as primary key if not in values" do
193
+ d = @m.dataset
194
+ def d.insert(*args)
195
+ super
196
+ 1234
197
+ end
198
+
199
+ def d.first
200
+ {:x => 1, :id => 1234}
201
+ end
202
+
203
+ o = @m.new(:x => 1)
204
+ o.save
205
+ o.id.should == 1234
206
+
207
+ o = @m.load(:x => 1, :id => 333)
208
+ o.save
209
+ o.id.should == 333
210
+ end
211
+
212
+ end
213
+
214
+ describe Sequel::Model, ".subset" do
215
+ before do
216
+ MODEL_DB.reset
217
+
218
+ @c = Class.new(Sequel::Model(:items))
219
+ end
220
+
221
+ specify "should create a filter on the underlying dataset" do
222
+ proc {@c.new_only}.should raise_error(NoMethodError)
223
+
224
+ @c.subset(:new_only) {:age.sql_number < 'new'}
225
+
226
+ @c.new_only.sql.should == "SELECT * FROM items WHERE (age < 'new')"
227
+ @c.dataset.new_only.sql.should == "SELECT * FROM items WHERE (age < 'new')"
228
+
229
+ @c.subset(:pricey) {:price.sql_number > 100}
230
+
231
+ @c.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
232
+ @c.dataset.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
233
+
234
+ @c.pricey.new_only.sql.should == "SELECT * FROM items WHERE ((price > 100) AND (age < 'new'))"
235
+ @c.new_only.pricey.sql.should == "SELECT * FROM items WHERE ((age < 'new') AND (price > 100))"
236
+ end
237
+
238
+ end
239
+
240
+ describe Sequel::Model, ".find" do
241
+
242
+ before(:each) do
243
+ MODEL_DB.reset
244
+
245
+ @c = Class.new(Sequel::Model(:items))
246
+
247
+ $cache_dataset_row = {:name => 'sharon', :id => 1}
248
+ @dataset = @c.dataset
249
+ $sqls = []
250
+ @dataset.extend(Module.new {
251
+ def fetch_rows(sql)
252
+ $sqls << sql
253
+ yield $cache_dataset_row
254
+ end
255
+ })
256
+ end
257
+
258
+ it "should return the first record matching the given filter" do
259
+ @c.find(:name => 'sharon').should be_a_kind_of(@c)
260
+ $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
261
+
262
+ @c.find(:name.like('abc%')).should be_a_kind_of(@c)
263
+ $sqls.last.should == "SELECT * FROM items WHERE (name LIKE 'abc%') LIMIT 1"
264
+ end
265
+
266
+ specify "should accept filter blocks" do
267
+ @c.find{:id.sql_number > 1}.should be_a_kind_of(@c)
268
+ $sqls.last.should == "SELECT * FROM items WHERE (id > 1) LIMIT 1"
269
+
270
+ @c.find {(:x.sql_number > 1) & (:y.sql_number < 2)}.should be_a_kind_of(@c)
271
+ $sqls.last.should == "SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"
272
+ end
273
+
274
+ end
275
+
276
+ describe Sequel::Model, ".fetch" do
277
+
278
+ before(:each) do
279
+ MODEL_DB.reset
280
+ @c = Class.new(Sequel::Model(:items))
281
+ end
282
+
283
+ it "should return instances of Model" do
284
+ @c.fetch("SELECT * FROM items").first.should be_a_kind_of(@c)
285
+ end
286
+
287
+ it "should return true for .empty? and not raise an error on empty selection" do
288
+ rows = @c.fetch("SELECT * FROM items WHERE FALSE")
289
+ @c.send(:define_method, :fetch_rows){|sql| yield({:count => 0})}
290
+ proc {rows.empty?}.should_not raise_error
291
+ end
292
+ end
293
+
294
+ describe Sequel::Model, ".find_or_create" do
295
+
296
+ before(:each) do
297
+ MODEL_DB.reset
298
+ @c = Class.new(Sequel::Model(:items)) do
299
+ no_primary_key
300
+ columns :x
301
+ end
302
+ end
303
+
304
+ it "should find the record" do
305
+ @c.find_or_create(:x => 1)
306
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (x = 1) LIMIT 1"]
307
+
308
+ MODEL_DB.reset
309
+ end
310
+
311
+ it "should create the record if not found" do
312
+ @c.meta_def(:find) do |*args|
313
+ dataset.filter(*args).first
314
+ nil
315
+ end
316
+
317
+ @c.find_or_create(:x => 1)
318
+ MODEL_DB.sqls.should == [
319
+ "SELECT * FROM items WHERE (x = 1) LIMIT 1",
320
+ "INSERT INTO items (x) VALUES (1)"
321
+ ]
322
+ end
323
+ end
324
+
325
+ describe Sequel::Model, ".delete_all" do
326
+
327
+ before(:each) do
328
+ MODEL_DB.reset
329
+ @c = Class.new(Sequel::Model(:items)) do
330
+ no_primary_key
331
+ end
332
+
333
+ @c.dataset.meta_def(:delete) {MODEL_DB << delete_sql}
334
+ end
335
+
336
+ deprec_specify "should delete all records in the dataset" do
337
+ @c.delete_all
338
+ MODEL_DB.sqls.should == ["DELETE FROM items"]
339
+ end
340
+
341
+ end
342
+
343
+ describe Sequel::Model, ".destroy_all" do
344
+
345
+ before(:each) do
346
+ MODEL_DB.reset
347
+ @c = Class.new(Sequel::Model(:items)) do
348
+ no_primary_key
349
+ end
350
+
351
+ @c.dataset.meta_def(:delete) {MODEL_DB << delete_sql}
352
+ end
353
+
354
+ deprec_specify "should delete all records in the dataset" do
355
+ @c.dataset.meta_def(:destroy) {MODEL_DB << "DESTROY this stuff"}
356
+ @c.destroy_all
357
+ MODEL_DB.sqls.should == ["DESTROY this stuff"]
358
+ end
359
+
360
+ deprec_specify "should call dataset.destroy" do
361
+ @c.dataset.should_receive(:destroy).and_return(true)
362
+ @c.destroy_all
363
+ end
364
+ end
365
+
366
+ describe Sequel::Model, ".all" do
367
+
368
+ before(:each) do
369
+ MODEL_DB.reset
370
+ @c = Class.new(Sequel::Model(:items)) do
371
+ no_primary_key
372
+ end
373
+
374
+ @c.dataset.meta_def(:all) {1234}
375
+ end
376
+
377
+ it "should return all records in the dataset" do
378
+ @c.all.should == 1234
379
+ end
380
+
381
+ end
382
+
383
+ class DummyModelBased < Sequel::Model(:blog)
384
+ end
385
+
386
+ describe Sequel::Model, "(:tablename)" do
387
+
388
+ it "should allow reopening of descendant classes" do
389
+ proc do
390
+ eval "class DummyModelBased < Sequel::Model(:blog); end"
391
+ end.should_not raise_error
392
+ end
393
+
394
+ end
395
+
396
+ describe Sequel::Model, "A model class without a primary key" do
397
+
398
+ before(:each) do
399
+ MODEL_DB.reset
400
+ @c = Class.new(Sequel::Model(:items)) do
401
+ columns :x
402
+ no_primary_key
403
+ end
404
+ end
405
+
406
+ it "should be able to insert records without selecting them back" do
407
+ i = nil
408
+ proc {i = @c.create(:x => 1)}.should_not raise_error
409
+ i.class.should be(@c)
410
+ i.values.to_hash.should == {:x => 1}
411
+
412
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)']
413
+ end
414
+
415
+ it "should raise when deleting" do
416
+ o = @c.new
417
+ proc {o.delete}.should raise_error
418
+ end
419
+
420
+ it "should insert a record when saving" do
421
+ o = @c.new(:x => 2)
422
+ o.should be_new
423
+ o.save
424
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (2)']
425
+ end
426
+
427
+ end
428
+
429
+ describe Sequel::Model, "attribute accessors" do
430
+ before do
431
+ MODEL_DB.reset
432
+ @dataset = Sequel::Dataset.new(MODEL_DB)
433
+ def @dataset.columns; [:x, :y]; end
434
+ @c = Class.new(Sequel::Model) do
435
+ def self.db_schema
436
+ set_columns(Array(@columns))
437
+ @db_schema = {:x=>{}, :y=>{}}
438
+ end
439
+ def self.set_dataset(ds, opts={})
440
+ @columns = ds.columns
441
+ db_schema
442
+ end
443
+ end
444
+ end
445
+
446
+ it "should be created on set_dataset" do
447
+ %w'x y x= y='.each do |x|
448
+ @c.instance_methods.collect{|y| y.to_s}.should_not include(x)
449
+ end
450
+ @c.set_dataset(@dataset)
451
+ %w'x y x= y='.each do |x|
452
+ @c.instance_methods.collect{|y| y.to_s}.should include(x)
453
+ end
454
+ o = @c.new
455
+ %w'x y x= y='.each do |x|
456
+ o.methods.collect{|y| y.to_s}.should include(x)
457
+ end
458
+
459
+ o.x.should be_nil
460
+ o.x = 34
461
+ o.x.should == 34
462
+ end
463
+
464
+ it "should be only accept one argument for the write accessor" do
465
+ @c.set_dataset(@dataset)
466
+ o = @c.new
467
+
468
+ o.x = 34
469
+ o.x.should == 34
470
+ proc{o.send(:x=)}.should raise_error
471
+ proc{o.send(:x=, 3, 4)}.should raise_error
472
+ end
473
+ end
474
+
475
+ describe Sequel::Model, ".[]" do
476
+
477
+ before(:each) do
478
+ MODEL_DB.reset
479
+
480
+ @c = Class.new(Sequel::Model(:items))
481
+
482
+ $cache_dataset_row = {:name => 'sharon', :id => 1}
483
+ @dataset = @c.dataset
484
+ $sqls = []
485
+ @dataset.extend(Module.new {
486
+ def fetch_rows(sql)
487
+ $sqls << sql
488
+ yield $cache_dataset_row
489
+ end
490
+ })
491
+ end
492
+
493
+ it "should return the first record for the given pk" do
494
+ @c[1].should be_a_kind_of(@c)
495
+ $sqls.last.should == "SELECT * FROM items WHERE (id = 1) LIMIT 1"
496
+ @c[9999].should be_a_kind_of(@c)
497
+ $sqls.last.should == "SELECT * FROM items WHERE (id = 9999) LIMIT 1"
498
+ end
499
+
500
+ it "should work correctly for custom primary key" do
501
+ @c.set_primary_key :name
502
+ @c['sharon'].should be_a_kind_of(@c)
503
+ $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
504
+ end
505
+
506
+ it "should work correctly for composite primary key" do
507
+ @c.set_primary_key [:node_id, :kind]
508
+ @c[3921, 201].should be_a_kind_of(@c)
509
+ $sqls.last.should =~ \
510
+ /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
511
+ end
512
+ end
513
+
514
+ context "Model#inspect" do
515
+ setup do
516
+ @o = Sequel::Model.load(:x => 333)
517
+ end
518
+
519
+ specify "should include the class name and the values" do
520
+ @o.inspect.should == '#<Sequel::Model @values={:x=>333}>'
521
+ end
522
+ end
523
+
524
+ context "Model.db_schema" do
525
+ setup do
526
+ @c = Class.new(Sequel::Model(:items)) do
527
+ def self.columns; orig_columns; end
528
+ end
529
+ @dataset = Sequel::Dataset.new(nil).from(:items)
530
+ @dataset.meta_def(:db){@db ||= Sequel::Database.new}
531
+ def @dataset.naked; self; end
532
+ def @dataset.columns; []; end
533
+ def @dataset.def_mutation_method(*names); end
534
+ end
535
+
536
+ specify "should use the database's schema_for_table and set the columns and dataset columns" do
537
+ d = @dataset.db
538
+ def d.schema(table, opts = {})
539
+ [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
540
+ end
541
+ @c.dataset = @dataset
542
+ @c.db_schema.should == {:x=>{:type=>:integer}, :y=>{:type=>:string}}
543
+ @c.columns.should == [:x, :y]
544
+ @c.dataset.instance_variable_get(:@columns).should == [:x, :y]
545
+ end
546
+
547
+ specify "should restrict the schema and columns for datasets with a :select option" do
548
+ ds = @dataset.select(:x, :y___z)
549
+ d = ds.db
550
+ def d.schema(table, opts = {})
551
+ [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
552
+ end
553
+ def @c.columns; [:x, :z]; end
554
+ @c.dataset = ds
555
+ @c.db_schema.should == {:x=>{:type=>:integer}, :z=>{}}
556
+ end
557
+
558
+ specify "should not use schema_for_table if the dataset uses multiple tables or custom sql" do
559
+ ds = @dataset.join(:x, :id)
560
+ d = ds.db
561
+ e = false
562
+ d.meta_def(:schema){|table| e = true}
563
+ def @c.columns; [:x]; end
564
+ @c.dataset = ds
565
+ @c.db_schema.should == {:x=>{}}
566
+ e.should == false
567
+ end
568
+
569
+ specify "should fallback to fetching records if schema_for_table raises an error" do
570
+ ds = @dataset.join(:x, :id)
571
+ d = ds.db
572
+ def d.schema(table)
573
+ raise StandardError
574
+ end
575
+ def @c.columns; [:x]; end
576
+ @c.dataset = ds
577
+ @c.db_schema.should == {:x=>{}}
578
+ end
579
+ end
580
+
581
+ context "Model.str_columns" do
582
+ deprec_specify "should return the columns as frozen strings" do
583
+ c = Class.new(Sequel::Model)
584
+ c.meta_def(:columns){[:a, :b]}
585
+ c.orig_str_columns.should == %w'a b'
586
+ proc{c.orig_str_columns.first << 'a'}.should raise_error
587
+ end
588
+ end