sequel 3.11.0 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/CHANGELOG +70 -0
  2. data/Rakefile +1 -1
  3. data/doc/active_record.rdoc +896 -0
  4. data/doc/advanced_associations.rdoc +46 -31
  5. data/doc/association_basics.rdoc +14 -9
  6. data/doc/dataset_basics.rdoc +3 -3
  7. data/doc/migration.rdoc +1011 -0
  8. data/doc/model_hooks.rdoc +198 -0
  9. data/doc/querying.rdoc +811 -86
  10. data/doc/release_notes/3.12.0.txt +304 -0
  11. data/doc/sharding.rdoc +17 -0
  12. data/doc/sql.rdoc +537 -0
  13. data/doc/validations.rdoc +501 -0
  14. data/lib/sequel/adapters/jdbc.rb +19 -27
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
  16. data/lib/sequel/adapters/mysql.rb +5 -4
  17. data/lib/sequel/adapters/odbc.rb +3 -2
  18. data/lib/sequel/adapters/shared/mssql.rb +7 -6
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -8
  21. data/lib/sequel/adapters/shared/sqlite.rb +2 -5
  22. data/lib/sequel/adapters/sqlite.rb +4 -4
  23. data/lib/sequel/core.rb +0 -1
  24. data/lib/sequel/database.rb +2 -1060
  25. data/lib/sequel/database/connecting.rb +227 -0
  26. data/lib/sequel/database/dataset.rb +58 -0
  27. data/lib/sequel/database/dataset_defaults.rb +127 -0
  28. data/lib/sequel/database/logging.rb +62 -0
  29. data/lib/sequel/database/misc.rb +246 -0
  30. data/lib/sequel/database/query.rb +390 -0
  31. data/lib/sequel/database/schema_generator.rb +7 -3
  32. data/lib/sequel/database/schema_methods.rb +351 -7
  33. data/lib/sequel/dataset/actions.rb +9 -2
  34. data/lib/sequel/dataset/misc.rb +6 -2
  35. data/lib/sequel/dataset/mutation.rb +3 -11
  36. data/lib/sequel/dataset/query.rb +49 -6
  37. data/lib/sequel/exceptions.rb +3 -0
  38. data/lib/sequel/extensions/migration.rb +395 -113
  39. data/lib/sequel/extensions/schema_dumper.rb +21 -13
  40. data/lib/sequel/model.rb +27 -25
  41. data/lib/sequel/model/associations.rb +72 -34
  42. data/lib/sequel/model/base.rb +74 -18
  43. data/lib/sequel/model/errors.rb +8 -1
  44. data/lib/sequel/plugins/active_model.rb +8 -0
  45. data/lib/sequel/plugins/association_pks.rb +87 -0
  46. data/lib/sequel/plugins/association_proxies.rb +8 -0
  47. data/lib/sequel/plugins/boolean_readers.rb +12 -6
  48. data/lib/sequel/plugins/caching.rb +14 -7
  49. data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
  50. data/lib/sequel/plugins/composition.rb +2 -1
  51. data/lib/sequel/plugins/force_encoding.rb +10 -7
  52. data/lib/sequel/plugins/hook_class_methods.rb +12 -11
  53. data/lib/sequel/plugins/identity_map.rb +9 -0
  54. data/lib/sequel/plugins/instance_hooks.rb +23 -13
  55. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  56. data/lib/sequel/plugins/many_through_many.rb +18 -4
  57. data/lib/sequel/plugins/nested_attributes.rb +1 -0
  58. data/lib/sequel/plugins/optimistic_locking.rb +1 -1
  59. data/lib/sequel/plugins/rcte_tree.rb +9 -8
  60. data/lib/sequel/plugins/schema.rb +8 -0
  61. data/lib/sequel/plugins/serialization.rb +1 -3
  62. data/lib/sequel/plugins/sharding.rb +135 -0
  63. data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
  64. data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
  65. data/lib/sequel/plugins/string_stripper.rb +26 -0
  66. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
  67. data/lib/sequel/plugins/timestamps.rb +15 -2
  68. data/lib/sequel/plugins/touch.rb +13 -0
  69. data/lib/sequel/plugins/update_primary_key.rb +48 -0
  70. data/lib/sequel/plugins/validation_class_methods.rb +8 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  72. data/lib/sequel/sql.rb +17 -20
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/postgres_spec.rb +5 -5
  75. data/spec/core/core_sql_spec.rb +17 -1
  76. data/spec/core/database_spec.rb +17 -5
  77. data/spec/core/dataset_spec.rb +31 -8
  78. data/spec/core/schema_generator_spec.rb +8 -1
  79. data/spec/core/schema_spec.rb +13 -0
  80. data/spec/extensions/association_pks_spec.rb +85 -0
  81. data/spec/extensions/hook_class_methods_spec.rb +9 -9
  82. data/spec/extensions/migration_spec.rb +339 -219
  83. data/spec/extensions/schema_dumper_spec.rb +28 -17
  84. data/spec/extensions/sharding_spec.rb +272 -0
  85. data/spec/extensions/single_table_inheritance_spec.rb +92 -4
  86. data/spec/extensions/skip_create_refresh_spec.rb +17 -0
  87. data/spec/extensions/string_stripper_spec.rb +23 -0
  88. data/spec/extensions/update_primary_key_spec.rb +65 -0
  89. data/spec/extensions/validation_class_methods_spec.rb +5 -5
  90. data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
  91. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
  92. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  93. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  94. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
  95. data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
  96. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
  97. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
  98. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
  99. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
  100. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
  101. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
  102. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
  103. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
  104. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  105. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
  106. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
  107. data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
  108. data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
  109. data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
  110. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  111. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
  112. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  113. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
  114. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  115. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
  116. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
  117. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  118. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  119. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
  120. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
  121. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
  122. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
  123. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
  124. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
  125. data/spec/integration/eager_loader_test.rb +20 -20
  126. data/spec/integration/migrator_test.rb +187 -0
  127. data/spec/integration/plugin_test.rb +150 -0
  128. data/spec/integration/schema_test.rb +13 -2
  129. data/spec/model/associations_spec.rb +41 -14
  130. data/spec/model/base_spec.rb +69 -0
  131. data/spec/model/eager_loading_spec.rb +7 -3
  132. data/spec/model/record_spec.rb +79 -4
  133. data/spec/model/validations_spec.rb +21 -9
  134. metadata +66 -5
  135. data/doc/schema.rdoc +0 -36
  136. data/lib/sequel/database/schema_sql.rb +0 -320
data/CHANGELOG CHANGED
@@ -1,3 +1,73 @@
1
+ === 3.12.0 (2010-06-01)
2
+
3
+ * Add a :deferrable option to foreign_key for creating deferrable foreign keys (hydrow)
4
+
5
+ * Add a :join_table_block many_to_many association option used by the add/remove/remove_all methods (jeremyevans)
6
+
7
+ * Add an AssociationPks plugin that adds association_pks and association_pks= methods for *_to_many associations (jeremyevans)
8
+
9
+ * Add an UpdatePrimaryKey plugin that allows you to update the primary key of a model object (jeremyevans)
10
+
11
+ * Add a SkipCreateRefresh plugin that skips the refresh when saving new model objects (jeremyevans)
12
+
13
+ * Add a StringStripper plugin that strips strings before assigning them to model attributes (jeremyevans)
14
+
15
+ * Allow the :eager_loader association proc to accept a single hash instead of 3 arguments (jeremyevans)
16
+
17
+ * Add a Dataset#order_append alias for order_more, for consistency with order_prepend (jeremyevans)
18
+
19
+ * Add a Dataset#order_prepend method that adds to the end of an existing order (jeremyevans)
20
+
21
+ * Add a Sequel::NotImplemented exception class, use instead of NotImplementedError (jeremyevans)
22
+
23
+ * Correctly handle more than 2 hierarchy levels in the single table inheritance plugin (jeremyevans)
24
+
25
+ * Allow using a custom column value<->class mapping to the single_table_inheritance plugin (jeremyevans, tmm1)
26
+
27
+ * Handle SQL::Identifiers in the schema_dumper extension (jeremyevans) (#304)
28
+
29
+ * Make sure certain alter table operations clear the schema correctly on MySQL (jeremyevans) (#301)
30
+
31
+ * Fix leak of JDBC Statement objects when using transactions on JDBC on databases that support savepoints (jeremyevans)
32
+
33
+ * Add DatabaseDisconnectError support to the ODBC adapter (Joshua Hansen)
34
+
35
+ * Make :encoding option work on MySQL in some cases where it was ignored (jeremyevans) (#300)
36
+
37
+ * Make Model::Errors#on always return nil if there are no errors on that attribute (jeremyevans)
38
+
39
+ * When using multiple plugins that add before hooks, the order that the hooks are called may have changed (jeremyevans)
40
+
41
+ * The hook_class_methods plugin no longer skips later after hooks if earlier after hooks return false (jeremyevans)
42
+
43
+ * Add Model#set_fields and update_fields, similar to set_only and update_only but ignoring other keys in the hash (jeremyevans)
44
+
45
+ * Add Model.qualified_primary_key_hash, similar to primary_key_hash but with qualified columns (jeremyevans)
46
+
47
+ * Make Model::Errors#empty? handle attributes with empty error arrays (jeremyevans)
48
+
49
+ * No longer apply association options to join table dataset when removing all many_to_many associated objects (jeremyevans)
50
+
51
+ * Log the execution times of migrations to the database's loggers (jeremyevans)
52
+
53
+ * Add a TimestampMigrator that can work with migrations where versions are timestamps, and handle migrations applied out of order (jeremyevans)
54
+
55
+ * Completely refactor Sequel::Migrator, now a class instead of a module (jeremyevans)
56
+
57
+ * Save migration version after each migration, instead of after all migrations (jeremyevans)
58
+
59
+ * Raise an error if missing a migration version (jeremyevans)
60
+
61
+ * Raise an error if using a duplicate migration version (jeremyevans)
62
+
63
+ * Add a Sequel.migration DSL for defining migrations (jeremyevans)
64
+
65
+ * Add a sharding plugin giving Sequel::Model objects support for dealing with sharding (jeremyevans)
66
+
67
+ * Handle timestamp(N) with time zone data types (hone)
68
+
69
+ * Fix MSSQL temporary table creation, but watch out as it changes the table name (gpd, jeremyevans) (#299)
70
+
1
71
  === 3.11.0 (2010-05-03)
2
72
 
3
73
  * Allow shared postgresql adapter to work with ruby 1.9 with the -Ku switch (golubev.pavel) (#298)
data/Rakefile CHANGED
@@ -59,7 +59,7 @@ task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_p
59
59
  Rake::RDocTask.new(:website_rdoc_main) do |rdoc|
60
60
  rdoc.rdoc_dir = "www/public/rdoc"
61
61
  rdoc.options += RDOC_OPTS
62
- rdoc.rdoc_files.add %w"README.rdoc CHANGELOG COPYING lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt"
62
+ rdoc.rdoc_files.add %w"README.rdoc CHANGELOG COPYING lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt lib/sequel/extensions/migration.rb"
63
63
  end
64
64
 
65
65
  Rake::RDocTask.new(:website_rdoc_adapters) do |rdoc|
@@ -0,0 +1,896 @@
1
+ = Sequel for ActiveRecord Users
2
+
3
+ This guide is aimed at helping ActiveRecord users transition to Sequel. It assumes the user is familiar with ActiveRecord 2, but if you are familiar with ActiveRecord 3, the transition should be even easier.
4
+
5
+
6
+ == Introduction
7
+
8
+ Both Sequel and ActiveRecord use the active record pattern of database access, where model instances are objects that wrap a row in a database table or view, encapsulating the database access, and adding domain logic on that data. Just like ActiveRecord, Sequel supports both associations and inheritance, though Sequel does so in a more flexible manner than ActiveRecord.
9
+
10
+ Let's quickly run through the ActiveRecord README and show how it applies to Sequel.
11
+
12
+ == Automatic Mapping
13
+
14
+ Just like ActiveRecord, Sequel maps classes to tables and automatically creates accessor methods for the columns in the table, so if you have an albums table with a primary key named "id" and a string/varchar column named "name", the minimal model class is:
15
+
16
+ class Album < Sequel::Model
17
+ end
18
+
19
+ Sequel will autogenerate the column accessors, so you can do:
20
+
21
+ album = Album.new
22
+ album.name = 'RF'
23
+
24
+ If the table name for the class doesn't match the default one Sequel to use, you can specify it manually:
25
+
26
+ class Album < Sequel::Model(:records)
27
+ end
28
+
29
+ == Associations
30
+
31
+ Sequel supports most of the same association types as ActiveRecord, but it uses names that reflect the database relationships instead of ones that imply ownership:
32
+
33
+ class Album < Sequel::Model
34
+ many_to_one :artist
35
+ one_to_many :tracks
36
+ many_to_many :tags
37
+ end
38
+
39
+ == Compositions
40
+
41
+ Sequel's +composition+ plugin allows you to easily create accessor methods that are composed of one or more of the database's columns, similar to ActiveRecord's +composed_of+:
42
+
43
+ class Artist < Sequel::Model
44
+ plugin :composition
45
+ composition :address, :mapping=>[:street, :city, :state, :zip]
46
+ end
47
+
48
+ == Validations
49
+
50
+ Sequel's +validation_class_methods+ plugin is modeled directly on ActiveRecord's validations, but the recommended approach is to use the +validation_helpers+ plugin inside a +validate+ instance method:
51
+
52
+ class Album < Sequel::Model
53
+ plugin :validation_helpers
54
+
55
+ def validate
56
+ super
57
+ validates_presence [:name, :copies_sold]
58
+ validates_unique [:name, :artist_id]
59
+ end
60
+ end
61
+
62
+ == Hooks/Callbacks
63
+
64
+ Sequel's +hook_class_methods+ plugin is modeled directly on ActiveRecord's callbacks, but the recommended approach is to define you hooks/callbacks as instance methods:
65
+
66
+ class Album < Sequel::Model
67
+ def before_create
68
+ self.copies_sold ||= 0
69
+ super
70
+ end
71
+
72
+ def after_update
73
+ super
74
+ AuditLog.create(:log=>"Updated Album #{id}")
75
+ end
76
+ end
77
+
78
+ Observers can be implemented completely by hooks, so Sequel doesn't allow you to define them separately.
79
+
80
+ == Inheritance
81
+
82
+ Sequel supports both single table inheritance and class table inheritance using plugins:
83
+
84
+ class Employee < Sequel::Model
85
+ plugin :single_table_inheritance, :class_name_column
86
+ # or
87
+ plugin :class_table_inheritance
88
+ end
89
+
90
+ class Staff < Employee
91
+ end
92
+
93
+ class Manager < Employee
94
+ end
95
+
96
+ class Executive < Manager
97
+ end
98
+
99
+ == Transactions
100
+
101
+ Sequel supports transactions via the Database object (which we recommend using the DB constant for single database applications):
102
+
103
+ DB.transaction do
104
+ album.artist.num_albums -= 1
105
+ album.artist.save
106
+ album.delete
107
+ end
108
+
109
+ For model classes, you can always access the database via +db+:
110
+
111
+ Album.db.transaction do
112
+ album.artist.num_albums -= 1
113
+ album.artist.save
114
+ album.delete
115
+ end
116
+
117
+ == Reflection
118
+
119
+ Just like ActiveRecord, Sequel has full reflection support for columns, associations, and many other things:
120
+
121
+ Album.columns # => [:id, :name, :artist_id, :copies_sold]
122
+ reflection = Album.association_reflection(:artist)
123
+ reflection[:type] == :many_to_one
124
+
125
+ == Direct Manipulation
126
+
127
+ Just like ActiveRecord, Sequel doesn't use sessions, it lets you modify objects and have them be saved inside the call to +save+:
128
+
129
+ album = Album[1234]
130
+ # modify album
131
+ album.save
132
+
133
+ == Database Abstraction
134
+
135
+ Sequel supports far more database abstraction than ActiveRecord, and setting up the database connection is easy:
136
+
137
+ DB = Sequel.sqlite # memory database
138
+ DB = Sequel.connect('postgres://user:pass@host/database') # connection string
139
+ DB = Sequel.connect(:adapter=>'postgres', :user=>'?', :password=>'?',
140
+ :host=>'?', :database=>'?') # option hash
141
+
142
+ == Logging
143
+
144
+ Sequel supports logging of all database queries by allowing multiple loggers for each database:
145
+
146
+ DB.loggers << Logger.new($stdout)
147
+
148
+ == Migrations
149
+
150
+ Sequel supports migrations and has a migrator similar to ActiveRecord:
151
+
152
+ Sequel.migration do
153
+ up do
154
+ create_table(:albums) do
155
+ primary_key :id
156
+ String :name
157
+ end
158
+ end
159
+
160
+ down do
161
+ drop_table(:albums)
162
+ end
163
+ end
164
+
165
+ == Differences
166
+
167
+ By now, you should have the idea that Sequel supports most things that ActiveRecord supports. The rest of this guide is going to go over how Sequel differs from ActiveRecord in terms of operation.
168
+
169
+ === Method Chaining
170
+
171
+ Unlike ActiveRecord 2, Sequel uses method chains on datasets for retrieving objects, so instead of:
172
+
173
+ Album.find(:all, :conditions=>['name > ? AND artist_id = ?', 'RF', 1],
174
+ :order=>'copies_sold', :select=>'id, name')
175
+
176
+ Sequel uses:
177
+
178
+ Album.filter{name > 'RF'}.filter(:artist_id=>1).order(:copies_sold).
179
+ select(:id, :name).all
180
+
181
+ Note that the records aren't retrieved until +all+ is called.
182
+
183
+ ActiveRecord 3 adopts this method chaining approach, so if you are familiar with it, it should be even easier to transition to Sequel.
184
+
185
+ === No Need for SQL String Fragments
186
+
187
+ As the example above shows, most ActiveRecord code uses SQL string fragments. With Sequel, you rarely need to. Sequel's DSL allows you to create complex queries without ever specifying SQL string fragments (called literal strings in Sequel).
188
+
189
+ If you want to use SQL string fragments, Sequel makes it easy by using the <tt>String#lit</tt> method:
190
+
191
+ Album.select('id, name'.lit)
192
+
193
+ This usage is not encouraged, though. The recommended way is to use symbols to represent the columns:
194
+
195
+ Album.select(:id, :name)
196
+
197
+ Sequel keeps datasets in an abstract format, allowing for powerful capabilities. For example, let's say you wanted to join to the artists table. Sequel can automatically qualify all references in the current dataset, so that it can be safely joined:
198
+
199
+ Album.select(:id, :name).qualify.join(:artists, :id=>:artist_id)
200
+
201
+ This isn't possible when you use an SQL string fragment. Another case where using an SQL string fragment hurts you is when the SQL syntax cannot handle all cases:
202
+
203
+ Album.filter('id NOT IN ?', ids_array)
204
+
205
+ That will work fine if +ids_array+ is not empty, but will not work correctly if it is empty. With Sequel, you do:
206
+
207
+ Album.exclude(:id=>ids_array)
208
+
209
+ That will handle cases where +ids_array+ is empty correctly.
210
+
211
+ A third reason to not use SQL string fragments is database independence. For example, if you want a case insensitive search that works on both PostgreSQL and MySQL, the following won't work:
212
+
213
+ Album.filter('name LIKE ?', 'A%')
214
+
215
+ This is because LIKE is case sensitive on PostgreSQL, but case insensitive on MySQL. With Sequel, you would do:
216
+
217
+ Album.filter(:name.ilike('A%'))
218
+
219
+ This will do a case insensitive search on both databases. If you want a case sensitive search on both, you can use +like+ instead of +ilike+.
220
+
221
+ String concatention is a similar area, where MySQL and PostgreSQL handle things differently. With Sequel, the same code can work on both databases:
222
+
223
+ Album.select(:name.sql_string + ' - Name')
224
+
225
+ == Flexible Overriding
226
+
227
+ Unlike ActiveRecord, which forces you to alias methods if you want to override them, with Sequel you just override the methods and call super:
228
+
229
+ class Sequel::Model
230
+ def after_update
231
+ super
232
+ AuditLog.create(:log=>"#{model.name} with primary key #{pk} updated")
233
+ end
234
+ end
235
+
236
+ With that code, you have enabled auditing for all model object updates.
237
+
238
+ Let's say you want to override accessor methods. In Sequel, instead of using +read_attribute+ and +write_attribute+, you can just call super:
239
+
240
+ class Track < Sequel::Model
241
+ # database holds length in integer seconds,
242
+ # but you want it in minutes as a float
243
+
244
+ def length=(minutes)
245
+ super((minutes*60).to_i)
246
+ end
247
+
248
+ def length
249
+ super/60.0
250
+ end
251
+ end
252
+
253
+ You can override almost all model class or instance methods this way, just remember to call +super+.
254
+
255
+ == +method_missing+ Missing
256
+
257
+ Sequel does not use +method_missing+ unless the object truely needs it. Neither <tt>Sequel::Model</tt> nor <tt>Sequel::Dataset</tt> nor <tt>Sequel::Database</tt> implement +method_missing+ at either a class or model level. So if you call +methods+, you can see which methods are available, and if they aren't listed, then the object won't respond to them. Among other things, this means Sequel does not support dynamic finders. So instead of:
258
+
259
+ Album.find_or_create_by_name("RF")
260
+
261
+ You just use:
262
+
263
+ Album.find_or_create(:name=>"RF")
264
+
265
+ At the instance level, this means that if you select columns that aren't in the models table, you need to use <tt>Model#[]</tt> to access them:
266
+
267
+ album = Album.join(:artist, :id=>:artist_id).
268
+ select(:albums__id, :albums__name, :artists__name___artist).first
269
+ # SELECT albums.id, albums.name, artists.name AS artist
270
+
271
+ album.artist # Error!
272
+ album[:artist] # Works
273
+
274
+ == Associations
275
+
276
+ Sequel associations are similar to ActiveRecord associations in some ways, and much different in others. Sequel provides four association creation methods that map to ActiveRecord's associations:
277
+
278
+ ActiveRecord :: Sequel
279
+ +belongs_to+ :: +many_to_one+
280
+ +has_one+ :: +one_to_one+
281
+ +has_many+ :: +one_to_many+
282
+ +has_and_belongs_to_many+ :: +many_to_many+
283
+
284
+ Like ActiveRecord, when you create an association in Sequel, it creates an instance method with the same name that returns either the matching object or nil for the <tt>*_to_one</tt> associations, or an array of matching objects for the <tt>*_to_many</tt> associations.
285
+
286
+ Updating <tt>*_to_many</tt> associations is very different, however. ActiveRecord makes the association method returns an association proxy that looks like an array, but has a bunch of added methods to manipulate the associated records. Sequel uses instance methods on the object instead of a proxy to modify the association. Here's a basic example:
287
+
288
+ Artist.one_to_many :albums
289
+ Album.many_to_one :artist
290
+
291
+ artist = Artist[1]
292
+ album = Album[1]
293
+
294
+ artist.albums # array of albums
295
+ album.artist # Artist instance or nil
296
+
297
+ artist.add_album(album) # associate album to artist
298
+ artist.remove_album(album) # disassociate album from artist
299
+ artist.remove_all_albums # disassociate all albums from artist
300
+
301
+ Sequel doesn't have a <tt>has_many :through</tt> association, instead you can use a +many_to_many+ association in most cases. Sequel ships with a +many_through_many+ plugin that allows you to set up a many to many relationship through an arbitrary number of join tables.
302
+
303
+ Sequel doesn't come with support for polymorphic associations. Using polymorphic associations is generally bad from a database design perspective, as it violates referential integrity. If you have an old database and must have polymorphic associations, there is an external +sequel_polymorphic+ plugin that can handle them, just by using the standard association options provided by Sequel.
304
+
305
+ Sequel doesn't directly support creating a bunch of associated objects and delaying saving them to the database when the main object with save, like you can with the <tt>association.build</tt> methods in ActiveRecord. You can use +before_save or +after_save+ hooks, or the +nested_attributes+ or +instance_hooks+ plugins to get similar support.
306
+
307
+ Sequel supports the same basic association hooks/callbacks as ActiveRecord. It also supports <tt>:after_load</tt>, which is run after the associated objects are loaded. For <tt>*_to_one</tt> associations, it supports +before_set+ and +after_set+ hooks, since a setter method is used instead of an add/remove method pair.
308
+
309
+ If you pass a block to an association method, it's used to return a modified dataset used for the association, instead of to create an association extension:
310
+
311
+ Artist.one_to_many :gold_albums, :class=>:Album do |ds|
312
+ ds.filter{copies_sold > 500000}
313
+ end
314
+
315
+ If you want to create an association extension, you can use the <tt>:extend</tt> association option with a module, which ActiveRecord also supports. In Sequel, the extensions are applied to the association dataset, not to the array of associated objects. You can access the association dataset using the +association_dataset+ method:
316
+
317
+ artist.albums_dataset
318
+ album.artist_dataset
319
+
320
+ Association datasets are just like any other Sequel dataset, in that you can filter them and manipulate them further:
321
+
322
+ gold_albums = artist.albums_dataset.filter{copies_sold > 500000}.order(:name).all
323
+
324
+ Sequel caches associated objects similarly to ActiveRecord, and you can skip the cache by passing +true+ to the association method, just like ActiveRecord.
325
+
326
+ === Eager Loading
327
+
328
+ ActiveRecord tries to guess whether to use preloading or JOINs for eager loading by scanning the SQL string fragments you provide for table names. This is error prone and Sequel avoids it by giving you separate methods. In Sequel, +eager+ is used for preloading and +eager_graph+ is used for JOINs. Both have the same API:
329
+
330
+ Artist.eager(:albums=>[:tags, :tracks])
331
+ Album.eager_graph(:artist, :tracks)
332
+
333
+ With either way of eager loading, you must call +all+ to retrieve all records at once. You cannot use +each+, +map+, or one of the other Enumerable methods. Just like +each+, +all+ takes a block that iterates over the records:
334
+
335
+ Artist.eager(:albums=>[:tags, :tracks]).each{|a| ...} # No cookie
336
+ Artist.eager(:albums=>[:tags, :tracks]).all{|a| ...} # Cookie
337
+
338
+ Like ActiveRecord, Sequel supports cascading of eager loading for both methods of eager loading.
339
+
340
+ Unlike ActiveRecord, Sequel allows you to eager load custom associations using the <tt>:eager_loader</tt> and <tt>:eager_grapher</tt> association options. See the {Advanced Associations guide}[link:files/doc/advanced_associations_rdoc.html] for more details.
341
+
342
+ Table aliasing when eager loading via +eager_graph+ is different in Sequel than ActiveRecord. Sequel will always attempt to use the association name, not the table name, for any associations. If the association name has already been used, Sequel will append _N to it, where N starts at 0 and increases by 1. For example, for a self referential association:
343
+
344
+ class Node < Sequel::Model
345
+ many_to_one :parent, :class=>self
346
+ one_to_many :children, :class=>self, :key=>:parent_id
347
+ end
348
+
349
+ Node.eager_graph(:parent=>:parent, :children=>{:children=>:children}).all
350
+
351
+ # SELECT nodes.id, nodes.parent_id, -- main table
352
+ # parent.id AS parent_id_0, parent.parent_id AS parent_parent_id, -- parent
353
+ # parent_0.id AS parent_0_id, parent_0.parent_id AS parent_0_parent_id, -- grandparent
354
+ # children.id AS children_id, children.parent_id AS children_parent_id, -- children
355
+ # children_0.id AS children_0_id, children_0.parent_id AS children_0_parent_id, -- grandchildren
356
+ # children_1.id AS children_1_id, children_1.parent_id AS children_1_parent_id -- great grandchidren
357
+ # FROM nodes -- main table
358
+ # LEFT OUTER JOIN nodes AS parent ON (parent.id = nodes.parent_id) -- parent
359
+ # LEFT OUTER JOIN nodes AS parent_0 ON (parent_0.id = parent.parent_id) -- grandparent
360
+ # LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) -- children
361
+ # LEFT OUTER JOIN nodes AS children_0 ON (children_0.parent_id = children.id) -- grandchildren
362
+ # LEFT OUTER JOIN nodes AS children_1 ON (children_1.parent_id = children_0.id) -- great grandchildren
363
+
364
+ === Options
365
+
366
+ Sequel supports many more association options than ActiveRecord, but here's a mapping of ActiveRecord association options to Sequel association options. Note that when you specify columns in Sequel, you use symbols, not strings. Where ActiveRecord would use an SQL string fragment with embedded commas for multiple columns, Sequel would use an array of column symbols.
367
+
368
+ === Shared options
369
+
370
+ These options are shared by more than one ActiveRecord association.
371
+
372
+ ActiveRecord option :: Sequel option
373
+ <tt>:class_name</tt> :: <tt>:class</tt>
374
+ <tt>:conditions</tt> :: <tt>:conditions</tt>
375
+ <tt>:select</tt> :: <tt>:select</tt>
376
+ <tt>:order</tt> :: <tt>:order</tt>
377
+ <tt>:extend</tt> :: <tt>:extend</tt>
378
+ <tt>:limit</tt> :: <tt>:limit</tt>
379
+ <tt>:offset</tt> :: <tt>:limit</tt> with an array with the second element being the offset
380
+ <tt>:uniq</tt> :: <tt>:uniq</tt>
381
+ <tt>:validate</tt> :: <tt>:validate</tt>
382
+ <tt>:dependent</tt> :: The +associations_dependencies+ plugin
383
+ <tt>:polymorphic</tt>, <tt>:as</tt>, <tt>:source_type</tt> :: The +sequel_polymorphic+ external plugin
384
+ <tt>:include</tt> :: <tt>:eager</tt>, <tt>:eager_graph</tt>
385
+ <tt>:readonly</tt> :: No equivalent, the Sequel <tt>:read_only</tt> option just means the modification methods are not created (it makes the association read only, not records retrieved through the association)
386
+ <tt>:through</tt>, <tt>:source</tt> :: Use a +many_to_many+ association
387
+ <tt>:touch</tt> :: The +touch+ plugin
388
+ <tt>:autosave</tt> :: A +before_save+ or +after_save+ hook
389
+ <tt>:finder_sql</tt> :: <tt>:dataset</tt> to set a custom dataset
390
+ <tt>:counter_sql</tt> :: No direct equivalent, but a count on the dataset will use the custom dataset specified by <tt>:dataset</tt>
391
+ <tt>:group</tt> :: Use the association block to add the group to the dataset
392
+ <tt>:having</tt> :: Use the association block to add the having to the dataset
393
+
394
+ === +belongs_to+
395
+
396
+ +belongs_to+ option :: +many_to_one+ option
397
+ <tt>:foreign_key</tt> :: <tt>:key</tt>
398
+ <tt>:primary_key</tt> :: <tt>:primary_key</tt>
399
+ <tt>:counter_cache</tt> :: No equivalent
400
+
401
+ === +has_one+, +has_many+
402
+
403
+ +has_one+, +has_many+ option :: +one_to_one+, +one_to_many+ option
404
+ <tt>:foreign_key</tt> :: <tt>:key</tt>
405
+
406
+ === +has_and_belongs_to_many+
407
+
408
+ +has_and_belongs_to_many+ option :: +many_to_many+ option
409
+ <tt>:foreign_key</tt> :: <tt>:left_key</tt>
410
+ <tt>:association_foreign_key</tt> :: <tt>:right_key</tt>
411
+ <tt>:join_table</tt> :: <tt>:join_table</tt>
412
+ <tt>:delete_sql</tt> :: Override the _remove_association method for the association
413
+ <tt>:insert_sql</tt> :: Override the _add_association method for the association
414
+
415
+ == Validation Errors
416
+
417
+ If there are errors when validating an object in Sequel, they are stored in a <tt>Sequel::Model::Errors</tt> instance. It's mostly similar to <tt>ActiveRecord::Errors</tt>, so this section will just go over the differences.
418
+
419
+ <tt>Sequel::Model::Errors</tt> is a hash subclass where keys are usually column symbols (not required), and values are arrays of error messages. So if you have two error messages on the same column, +each+ will only yield once, not twice.
420
+
421
+ The +add_on_blank+, +add_on_empty+, +add_to_base+, +each_full+, +generate_message+, <tt>invalid?</tt>, +on_base+, and +to_xml+ methods don't exist. <tt>[]</tt> should not be used directly, instead you should use +on+.
422
+
423
+ You can think of <tt>Sequel::Model::Errors</tt> as a subset of <tt>ActiveRecord::Errors</tt> if you stick to +on+, +add+, and +full_messages+.
424
+
425
+ == Sequel Configuration Flags
426
+
427
+ Unlike ActiveRecord, Sequel's behavior depends on how you configure it. In Sequel, you can set flags at the global, class, and instance level that change the behavior of Sequel. Here's a brief description of the flags:
428
+
429
+ +raise_on_save_failure+ :: Whether to raise an error instead of returning nil on a failure to save/create/save_changes/etc due to a validation failure or a before_* hook returning false. By default, an error is raised, when this is set to false, nil is returned instead.
430
+ +raise_on_typecast_failure+ :: Whether to raise an error when unable to typecast data for a column (default: true). This should be set to false if you want to use validations to display nice error messages to the user (e.g. most web applications). You can use the +validates_not_string+ validation in connection with this option to check for typecast failures for columns that aren�t blobs or strings.
431
+ +require_modification+ :: Whether to raise an error if an UPDATE or DELETE query related to a model instance does not modify exactly 1 row. If set to false, Sequel will not check the number of rows modified (default: true if the database supports it).
432
+ +strict_param_setting+ :: Whether new/set/update and their variants should raise an error if an invalid key is used. A key is invalid if no setter method exists for that key or the access to the setter method is restricted (e.g. due to it being a primary key field). If set to false, silently skip any key where the setter method doesn�t exist or access to it is restricted.
433
+ +typecast_empty_string_to_nil+ :: Whether to typecast the empty string ('') to nil for columns that are not string or blob. In most cases the empty string would be the way to specify a NULL SQL value in string form (nil.to_s == ��), and an empty string would not usually be typecast correctly for other types, so the default is true.
434
+ +typecast_on_assignment+ :: Whether to typecast attribute values on assignment (default: true). If set to false, no typecasting is done, so it will be left up to the database to typecast the value correctly.
435
+ +use_transactions+ :: Whether to use a transaction by default when saving/deleting records (default: true). If you are sending database queries in before or after hooks, you shouldn�t change the default setting without a good reason.
436
+
437
+ == ActiveRecord Method to Sequel Method Mapping
438
+
439
+ This part of the guide will list Sequel equivalents for ActiveRecord methods, hopefully allowing you to convert your existing ActiveRecord code to Sequel code more easily.
440
+
441
+ === Class Methods with Significantly Different Behavior
442
+
443
+ ==== +abstract_class+, <tt>abstract_class=</tt>, <tt>abstract_class?</tt>
444
+
445
+ With Sequel, these methods don't exist because it is it doesn't default to using single table inheritance in subclasses. ActiveRecord assumes that subclasses of Model classes use single table inheritance, and you have to set <tt>abstract_class = true</tt> to use an abstract class. In Sequel, you must use the +single_table_inheritance+ or +class_tabble_inheritance+ plugin to configure inheritance in the database.
446
+
447
+ ==== +all+
448
+
449
+ In both Sequel and ActiveRecord, calling +all+ will give you an array of all records. However, while in ActiveRecord you pass options to +all+ to filter or order the results, in Sequel you call dataset methods to filter or order the results, and then end the method chain with a call to +all+ to return the records.
450
+
451
+ ==== +column_names+
452
+
453
+ Sequel uses symbols for columns, so the +columns+ method returns an array of symbols. If you want an array of strings:
454
+
455
+ Album.columns.map{|x| x.to_s}
456
+
457
+ ==== +columns+
458
+
459
+ <tt>Sequel::Model.columns</tt> returns an array of column name symbols. The closest similar thing would be to get the database schema hash for each column:
460
+
461
+ Album.columns.map{|x| Album.db_schema[x]}
462
+
463
+ ==== +composed_of+
464
+
465
+ As mentioned earlier, Sequel uses the +composition+ plugin for this:
466
+
467
+ class Artist < Sequel::Model
468
+ plugin :composition
469
+ composition :address, :mapping=>[:street, :city, :state, :zip]
470
+ end
471
+
472
+ ==== <tt>connected?</tt>
473
+
474
+ <tt>Sequel::Model</tt> raises an exception if you haven't instantiated a <tt>Sequel::Database</tt> object before loading the model class. However, if you want to test the connection to the database:
475
+
476
+ Sequel::Model.db.test_connection
477
+
478
+ Note that +test_connection+ will return true if a connection can be made, but will probably raise an exception if it cannot be made.
479
+
480
+ ==== +connection+
481
+
482
+ Sequel only uses connections for the minimum about of time necessary, checking them out to do a query, and returning them as soon as the query finishes. If you do want direct access to the connection object:
483
+
484
+ Sequel::Model.db.synchronize do |connection|
485
+ ...
486
+ end
487
+
488
+ Note that the connection is yielded to the block, and the block ensures it is returned to the pool. Sequel doesn't have a method that returns a connection, since that would check it out with no ability to ensure it is returned to the pool
489
+
490
+ ==== +count+
491
+
492
+ Without arguments, +count+ works the same in Sequel as in ActiveRecord. For a single column:
493
+
494
+ Album.get{count(column)}
495
+
496
+ For options, you should use method chaining to build your query and execute it.
497
+
498
+ ==== +count_by_sql+
499
+
500
+ You can call +with_sql+ to set the SQL to use, and the +single_value+ to retrieve the result.
501
+
502
+ Album.with_sql("SELECT COUNT(*) ...").single_value
503
+
504
+ ==== +delete+, +delete_all+
505
+
506
+ Calling +delete+ directly on the class will probably delete all rows in the table. You want to filter first, then call +delete+:
507
+
508
+ Album.filter(:id=>id).delete
509
+ Album.filter("artist_id = ?", 5).delete
510
+
511
+ ==== +destroy+, +destroy_all+
512
+
513
+ Similar to +delete+, you filter first, then +destroy+:
514
+
515
+ Album.filter(:id=>id).destroy
516
+ Album.filter("artist_id = ?", 5).destroy
517
+
518
+ ==== +establish_connection+
519
+
520
+ If you want to use a specific <tt>Sequel::Database</tt> object, you can use <tt>db=</tt>:
521
+
522
+ BACKUP_DB = Sequel.connect(...)
523
+ Album.db = BACKUP_DB
524
+
525
+ If you want a specific dataset in that database, you can use +set_dataset+ or <tt>dataset=</tt>:
526
+
527
+ Album.set_dataset BACKUP_DB[:albums]
528
+ Album.dataset = BACKUP_DB[:albums]
529
+
530
+ ==== <tt>exists?</tt>
531
+
532
+ You need to filter the dataset first, then call <tt>empty?</tt> and invert the result:
533
+
534
+ !Album.filter(:id=>1).empty?
535
+
536
+ ==== +find+
537
+
538
+ ActiveRecord's +find+ can be used for a lot of different things. If you are trying to find a single object given a primary key:
539
+
540
+ Album[1]
541
+
542
+ Note that Sequel returns nil if no record is found, it doesn't raise an exception.
543
+
544
+ If you want to find multiple objects using an array of primary keys:
545
+
546
+ Album.filter(:id=>[1, 2, 3]).all
547
+
548
+ If you are using <tt>find(:first, ...)</tt>, you use a method chain instead of passing the options, and end it with +first+:
549
+
550
+ Album.filter(:artist_id=>1).order(:name).first
551
+
552
+ If you are using <tt>find(:last, ...)</tt>, you need to specify an order in Sequel, but the same method chain approach is used, which you end with +last+:
553
+
554
+ Album.filter(:artist_id=>1).order(:name).last
555
+ # You could also do:
556
+ Album.filter(:artist_id=>1).order(:name.desc).first
557
+
558
+ If you are using <tt>find(:all, ...)</tt>, you use a method chain instead of passing the options, and end it with +all+:
559
+
560
+ Album.filter(:artist_id=>1).order(:name).all
561
+
562
+ Here's a mapping of ActiveRecord +find+ options to <tt>Sequel::Dataset</tt> methods:
563
+
564
+ :conditions :: filter, where
565
+ :order :: order
566
+ :group :: group
567
+ :limit :: limit # first argument
568
+ :offset :: limit # second argument
569
+ :joins :: join, left_join, etc. # many other join methods
570
+ :include :: eager, eager_graph # eager does preloading, eager_graph does JOINs
571
+ :select :: select
572
+ :from :: from
573
+ :read_only :: # No Sequel equivalent
574
+ :lock :: for_update, lock_style
575
+
576
+ ==== +find_by_sql+
577
+
578
+ Similar to +count_by_sql+, you use +with_sql+, followed by +all+:
579
+
580
+ Album.with_sql("SELECT * FROM albums WHERE ...").all
581
+
582
+ ==== +first+
583
+
584
+ Just like with <tt>find(:first, ...)</tt>, you use a method chain instead of passing the options, and end it with +first+:
585
+
586
+ Album.filter(:artist_id=>1).order(:name).first
587
+
588
+ ==== +last+
589
+
590
+ Just like with <tt>find(:last, ...)</tt>, you use a method chain instead of passing the options, make sure it includes an order, and end it with +last+:
591
+
592
+ Album.filter(:artist_id=>1).order(:name).last
593
+
594
+ ==== +named_scope+
595
+
596
+ For a pure filter, you can use +subset+:
597
+
598
+ Album.subset(:debut, :position => 1)
599
+ Album.subset(:gold){copies_sold > 500000}
600
+
601
+ For anything more complex, you can use +def_dataset_method+:
602
+
603
+ Album.def_dataset_method(:by_artist) do |artist_id|
604
+ filter(:artist_id=>artist_id)
605
+ end
606
+ Album.def_dataset_method(:by_release_date){order(:release_date)}
607
+
608
+ ==== +reset_column_information+
609
+
610
+ If you want to completely reload the schema for the table:
611
+
612
+ Album.send(:get_db_schema, true)
613
+
614
+ ==== +serialize+, +seralized_attributes+
615
+
616
+ Sequel ships with a +serialization+ plugin that you can use. It's more flexible than ActiveRecord's, since you can serialize to marshal or json in addition to yaml:
617
+
618
+ class Album < Sequel::Model
619
+ plugin :serialization, :json, :permissions
620
+ end
621
+
622
+ For +serialized_attributes+, you can use +serialization_map+, which is also a hash, but keys are column symbols and values are either <tt>:marshal</tt>, <tt>:yaml</tt>, or <tt>:json</tt>, specifying the serialization format.
623
+
624
+ ==== +set_inheritance_column+
625
+
626
+ This is something that must be specified when you are loading the +single_table_inheritance+ plugin:
627
+
628
+ class Album < Sequel::Model
629
+ plugin :single_table_inheritance, :column
630
+ end
631
+
632
+ ==== +set_sequence_name+
633
+
634
+ Sequel will usually auto discover the sequence to use. However, on Oracle this should be specified by making sure the model's dataset includes a sequence:
635
+
636
+ class Album < Sequel::Model(ORACLE_DB[:albums].sequence('albums_seq'))
637
+ end
638
+
639
+ ==== <tt>table_exists?</tt>
640
+
641
+ This is a <tt>Sequel::Database</tt> method:
642
+
643
+ Album.db.table_exists?(Album.table_name)
644
+
645
+ With the +schema+ plugin, you can use it directly:
646
+
647
+ Album.plugin :schema
648
+ Album.table_exists?
649
+
650
+ ==== +transaction+
651
+
652
+ As mentioned earlier, +transaction+ is a database method in Sequel, which you can access via the +db+ method:
653
+
654
+ Album.db.transaction{}
655
+
656
+ ==== +update+, +update_all+
657
+
658
+ Just like +delete+ and +destroy+, you filter first, then +update+:
659
+
660
+ Album.filter(:id=>id).update(:name=>'RF')
661
+ Album.filter("artist_id = ?", 5).update(:copies_sold=>0)
662
+
663
+ Note that +update+ in that case will operate on a dataset, so it won't run model validations or hooks. If you want those run:
664
+
665
+ Album[id].update(:name=>'RF')
666
+ Album.filter("artist_id = ?", 5).all{|a| a.update(:copies_sold=>0)}
667
+
668
+ ==== +with_scope+
669
+
670
+ Sequel works a little differently than with_scope. Instead of using nested blocks, you just use a cleaner method chain. +with_scope+ is often used an an around_filter or similar construct, where in Sequel, you would just need to assign do a dataset in a before filter, and use that dataset in the action.
671
+
672
+ === Class Methods with Roughly the Same Behavior
673
+
674
+ Note that Sequel uses symbols almost everywhere to represent columns, while ActiveRecord often returns columns as strings.
675
+
676
+ ActiveRecord Method :: Sequel Method
677
+ +attr_accessible+ :: +set_allowed_columns+
678
+ +attr_protected+ :: +set_restricted_columns+
679
+ +average+ :: +avg+
680
+ +belongs_to+ :: +many_to_one+
681
+ +columns_hash+ :: +db_schema+
682
+ +changed+ :: +changed_columns+
683
+ +create+ :: +create+
684
+ +has_and_belongs_to_many+ :: +many_to_many+
685
+ +has_one+ :: +one_to_one+
686
+ +has_many+ :: +one_to_many+
687
+ +inheritance_column+ :: +sti_key+
688
+ +inspect+ :: +inspect+
689
+ +maximum+ :: +max+
690
+ +minimum+ :: +min+
691
+ +new+ :: +new+
692
+ +primary_key+ :: +primary_key+
693
+ <tt>respond_to?</tt> :: <tt>respond_to?</tt>
694
+ +set_primary_key+ :: +set_primary_key+
695
+ +sum+ :: +sum+
696
+ +table_name+ :: +table_name+
697
+
698
+ === Class Methods without an Equivalent
699
+
700
+ ActiveRecord Method :: Notes, Workarounds
701
+ +accepts_nested_attributes_for+ :: Use the +nested_attributes+ plugin
702
+ +attr_readonly+ :: Don't update the columns (duh!), or add a before_update hook that deletes them from the values hash
703
+ +attribute_method_suffix+ :: No equivalent
704
+ +alias_attribute_with_dirty+ :: No equivalent
705
+ +base_class+ :: Not needed internally, you can probably use <tt>sti_dataset.model</tt> if you are using single table inheritance
706
+ +benchmark+ :: Just use the +benchmark+ library from ruby's stdlib
707
+ +calculate+ :: No direct equivalent, just build the query manually and execute it
708
+ +cache+ :: No equivalent
709
+ <tt>cache_attribute?</tt> :: No equivalent
710
+ +cache_attributes+ :: No equivalent
711
+ +cached_attributes+ :: No equivalent
712
+ <tt>changed?</tt> :: <tt>changed_columns.include?(column)</tt>
713
+ +changes+ :: No equivalent
714
+ <tt>clear_active_connections!</tt> :: Sequel doesn't leak connections like ActiveRecord, so you don't need to worry about this
715
+ <tt>clear_reloadable_connections!</tt> :: Sequel doesn't leak connections like ActiveRecord, so you don't need to worry about this
716
+ +content_columns+ :: Not needed internally, you can probably do <tt>Album.columns.map{|x| x.to_s}.delete_if{|x| x == Album.primary_key || x =~ /_(id|count)\z/}</tt>
717
+ +decrement_counter+ :: <tt>Album.filter(:id=>:id).update(:counter_name=>:counter_name - 1)</tt>
718
+ +define_attribute_methods+, +define_read_methods+ :: <tt>def_column_accessor(*columns)</tt>, a private method
719
+ <tt>descends_from_active_record?</tt> :: Not needed internally, if using single table inheritance, <tt>Album.sti_dataset.model == Album</tt>
720
+ +find_each+, +find_in_batches+ :: Use the +pagination+ extension
721
+ <tt>generated_methods?</tt> :: No equivalent
722
+ +increment_counter+ :: <tt>Album.filter(:id=>:id).update(:counter_name=>:counter_name + 1)</tt>
723
+ <tt>instance_method_already_implemented?</tt> :: No equivalent, Sequel does not create column accessors that override other methods, it just skips them.
724
+ <tt>match_attribute_method?</tt> :: No equivalent
725
+ +readonly_attributes+ :: No equivalent
726
+ +remove_connection+ :: Not necessary in Sequel. If you want to disconnect an existing connection: <tt>Album.db.disconnect</tt>
727
+ +require_mysql+ :: A public method, really?
728
+ +silence+ :: No equivalent. Because the logger is handled at the <tt>Sequel::Database</tt> level, there is no thread-safe way to turn it off for specific blocks.
729
+ +scopes+ :: No equivalent
730
+ +sti_name+ :: No equivalent.
731
+ +update_counters+ :: <tt>Album.filter(:id=>:id).update(:counter_name=>:counter_name + 1, :other_counter=>:other_counter - 1)</tt>
732
+ +uncached+ :: No equivalent
733
+
734
+ === Instance Methods with Significantly Different Behavior
735
+
736
+ ==== +attribute_names+
737
+
738
+ +keys+ returns the columns as unsorted symbols, so:
739
+
740
+ album.keys.map{|x| x.to_s}.sort
741
+
742
+ ==== +becomes+
743
+
744
+ Assuming the record already exists in the database:
745
+
746
+ gold_album = GoldAlbum[1]
747
+ album = Album.load(gold_album.values)
748
+
749
+ If it is a new record:
750
+
751
+ gold_album = GoldAlbum.new(:name=>'a')
752
+ album = Album.new
753
+ album.send(:set_values, gold_album.values)
754
+
755
+ ==== +clone+
756
+
757
+ You probably have to use +set_values+, a private method:
758
+
759
+ album = Album[1]
760
+ cloned_album = Album.new
761
+ cloned_album.send(:set_values, album.values.dup)
762
+
763
+ ==== +column_for_attribute+
764
+
765
+ You can access this through the +db_schema+ hash:
766
+
767
+ album.db_schema[:column]
768
+
769
+ ==== +connection+
770
+
771
+ Just like in the class method, you have to access it through the database:
772
+
773
+ album.db.synchronize do |connection|
774
+ end
775
+
776
+ ==== +decrement+, +increment+
777
+
778
+ You can just modify the values hash directly:
779
+
780
+ album.values[:column] ||= 0
781
+ album.values[:column] -= 1 # or += 1 for increment
782
+
783
+ ==== <tt>decrement!</tt>, <tt>increment!</tt>
784
+
785
+ Assuming you want the full behavior of saving just one column without validating:
786
+
787
+ album.values[:column] ||= 0
788
+ album.values[:column] -= 1 # or += 1 for increment!
789
+ album.save(:column, :validate=>false)
790
+
791
+ ==== +freeze+, <tt>frozen?</tt>
792
+
793
+ Sequel doesn't support freezing objects directly, but you can do it yourself:
794
+
795
+ album.values.freeze
796
+ album.values.frozen?
797
+
798
+ ==== <tt>has_attribute?</tt>
799
+
800
+ You have to check the values hash:
801
+
802
+ album.values.has_key?(:column)
803
+
804
+ ==== <tt>invalid?</tt>
805
+
806
+ You can use <tt>unless valid?</tt> or <tt>!valid?</tt>.
807
+
808
+ ==== +save+, <tt>save!</tt>, +save_with_validation+, <tt>save_with_validation!</tt>
809
+
810
+ Sequel defaults to raising exceptions when +save+ fails, but this is configurable behavior by setting the +raise_on_save_failure+ flag on the class or instance:
811
+
812
+ album.raise_on_save_failure = true
813
+ album.save # raise exception if failure
814
+ album.raise_on_save_failure = false
815
+ album.save # return nil if failure
816
+
817
+ You can pass the <tt>:validate=>false</tt> option to not validate the object when saving.
818
+
819
+ ==== +toggle+, <tt>toggle</tt>
820
+
821
+ No equivalent, but very easy to add:
822
+
823
+ album.column = !album.column
824
+
825
+ If you want to save just that column:
826
+
827
+ album.save(:column, :validate=>false)
828
+
829
+ ==== +transaction+
830
+
831
+ Just like in the class, you can access the transaction method through the +db+:
832
+
833
+ album.db.transaction{}
834
+
835
+ ==== +update_attribute+
836
+
837
+ To only set and save a specific column:
838
+
839
+ album.set(:column => value)
840
+ album.save(:column, :validate=>false)
841
+
842
+ ==== +update_attributes+, <tt>update_attributes!</tt>
843
+
844
+ These would both use +update+, but see the notes on the +raise_on_save_failure+ flag:
845
+
846
+ album.update(:column1=>value1, :column2=>value2)
847
+
848
+ === Instance Methods with Roughly the Same Behavior
849
+
850
+ Note that Sequel uses symbols almost everywhere to represent columns, while ActiveRecord often returns columns as strings.
851
+
852
+ ActiveRecord Method :: Sequel Method
853
+ <tt>==</tt> :: <tt>===</tt>, <tt>==</tt> compares by all values, not just id
854
+ <tt>[]</tt> :: <tt>[]</tt>
855
+ <tt>[]=</tt> :: <tt>[]=</tt>
856
+ +after_create+ :: +after_create+
857
+ +after_destroy+ :: +after_destroy+
858
+ +after_save+ :: +after_save+
859
+ +after_update+ :: +after_update+
860
+ +after_validation+ :: +after_validation+
861
+ +attributes+ :: +values+
862
+ <tt>attributes=</tt> :: +set+
863
+ +before_create+ :: +before_create+
864
+ +before_destroy+ :: +before_destroy+
865
+ +before_save+ :: +before_save+
866
+ +before_update+ :: +before_update+
867
+ +before_validation+ :: +before_validation+
868
+ +cache_key+ :: +cache_key+, if using the +caching+ plugin
869
+ +destroy+ :: +destroy+
870
+ <tt>eql?</tt> :: <tt>===</tt>
871
+ +errors+ :: +errors+
872
+ +hash+ :: +hash+
873
+ +id+ :: +pk+
874
+ +inspect+ :: +inspect+
875
+ <tt>lock!</tt> :: <tt>lock!</tt>
876
+ <tt>new_record?</tt> :: <tt>new?</tt>
877
+ +reload_with_autosave_associations+ :: +reload+
878
+ +to_param+ :: +to_param+, if using the +active_model+ plugin
879
+ +touch+ :: +touch+, if using the +touch+ plugin
880
+ <tt>valid?</tt> :: <tt>valid?</tt>
881
+
882
+ === Instance Methods without an Equivalent
883
+
884
+ ActiveRecord Method :: Notes, Workarounds
885
+ +after_validation_on_create+, +after_validation_on_update+ :: Use +after_validation+ and <tt>if new?</tt> or <tt>unless new?</tt>
886
+ +as_json+, +from_json+, +to_json+, +from_xml+, +to_xml+ :: No equivalent
887
+ +attribute_for_inspect+ :: <tt>album[:column].inspect</tt>
888
+ <tt>attribute_present?</tt> :: <tt>!album[:column].blank?</tt> if using the +blank+ extension
889
+ +attributes_before_type_cast+ :: Sequel typecasts at a low level, so model objects never see values before they are type cast
890
+ +before_validation_on_create+, +before_validation_on_update+ :: Use +before_validation+ and <tt>if new?</tt> or <tt>unless new?</tt>
891
+ <tt>id=</tt> :: Sequel doesn't have a special primary key setter method, but you can use: <tt>album.send("#{primary_key}=", value)</tt>
892
+ +mark_for_destruction+, <tt>marked_for_destruction?</tt> :: Use a +before_save+ or +after_save+ hook or the +instance_hooks+ plugin
893
+ <tt>readonly!</tt> :: No equivalent
894
+ <tt>readonly?</tt> :: No equivalent
895
+ <tt>rollback_active_record_state!</tt> :: No equivalent
896
+ +with_transaction_returning_status+ :: No equivalent