3 category: Ruby libraries
10 Sequel is a database toolkit for Ruby.
12 - <https://github.com/jeremyevans/sequel>
19 DB = Sequel.sqlite('my_blog.db')
20 DB = Sequel.connect('postgres://user:password@localhost/my_db')
21 DB = Sequel.postgres('my_db', :user => 'user', :password => 'password', :host => 'localhost')
22 DB = Sequel.ado('mydb')
24 ### Open an SQLite memory database
26 Without a filename argument, the sqlite adapter will setup a new sqlite database in memory.
30 ### Logging SQL statements
33 DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)]
35 DB.loggers << Logger.new(...)
39 DB.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
40 dataset = DB["SELECT age FROM users WHERE name = ?", name]
42 DB.fetch("SELECT name FROM users") do |row|
49 dataset = DB.from(:items)
51 ### Most dataset methods are chainable
53 dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
57 dataset.insert(:name => 'Sharon', :grade => 50)
62 dataset.all # => [{...}, {...}, ...]
63 dataset.first # => {...}
65 ### Update/Delete rows
67 dataset.filter(~:active).delete
68 dataset.filter('price < ?', 100).update(:active => true)
70 ### Datasets are Enumerable
72 dataset.map{|r| r[:name]}
73 dataset.map(:name) # same as above
75 dataset.inject(0){|sum, r| sum + r[:value]}
76 dataset.sum(:value) # same as above
78 ### Filtering (see also doc/dataset_filtering.rdoc)
82 dataset.filter(:name => 'abc')
83 dataset.filter('name = ?', 'abc')
87 dataset.filter{value > 100}
88 dataset.exclude{value <= 100}
92 dataset.filter(:value => 50..100)
93 dataset.where{(value >= 50) & (value <= 100)}
95 dataset.where('value IN ?', [50,75,100])
96 dataset.where(:value=>[50,75,100])
98 dataset.where(:id=>other_dataset.select(:other_id))
100 #### Subselects as scalar values
102 dataset.where('price > (SELECT avg(price) + 100 FROM table)')
103 dataset.filter{price > dataset.select(avg(price) + 100)}
107 DB[:items].filter(:name.like('AL%'))
108 DB[:items].filter(:name => /^AL/)
112 DB[:items].filter{(x > 5) & (y > 10)}.sql
113 # SELECT * FROM items WHERE ((x > 5) AND (y > 10))
115 DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
116 # SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))
118 #### Mathematical operators
120 DB[:items].filter((:x + :y) > :z).sql
121 # SELECT * FROM items WHERE ((x + y) > z)
123 DB[:items].filter{price - 100 < avg(price)}.sql
124 # SELECT * FROM items WHERE ((price - 100) < avg(price))
129 dataset.reverse_order(:kind)
130 dataset.order(:kind.desc, :name)
134 dataset.limit(30) # LIMIT 30
135 dataset.limit(30, 10) # LIMIT 30 OFFSET 10
139 DB[:items].left_outer_join(:categories, :id => :category_id).sql
140 # SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id
142 DB[:items].join(:categories, :id => :category_id).join(:groups, :id => :items__group_id)
143 # SELECT * FROM items INNER JOIN categories ON categories.id = items.category_id INNER JOIN groups ON groups.id = items.group_id
145 ### Aggregate functions methods
147 dataset.count #=> record count
153 dataset.group_and_count(:category)
154 dataset.group(:category).select(:category, :AVG.sql_function(:price))
156 ### SQL Functions / Literals
158 dataset.update(:updated_at => :NOW.sql_function)
159 dataset.update(:updated_at => 'NOW()'.lit)
161 dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
162 dataset.update(:updated_at => :DateValue.sql_function('1/1/2001'))
164 ### Schema Manipulation
166 DB.create_table :items do
168 String :name, :unique => true, :null => false
169 TrueClass :active, :default => true
170 foreign_key :category_id, :categories
178 DB.create_table :test do
180 enum :system, :elements => ['mac', 'linux', 'windows']
185 DB[:items].select(:name.as(:item_name))
186 DB[:items].select(:name___item_name)
187 DB[:items___items_table].select(:items_table__name___item_name)
188 # SELECT items_table.name AS item_name FROM items AS items_table
193 dataset.insert(:first_name => 'Inigo', :last_name => 'Montoya')
194 dataset.insert(:first_name => 'Farm', :last_name => 'Boy')
195 end # Either both are inserted or neither are inserted
197 Database#transaction is re-entrant:
199 DB.transaction do # BEGIN issued only here
201 dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
203 end # COMMIT issued only here
205 Transactions are aborted if an error is raised:
208 raise "some error occurred"
209 end # ROLLBACK issued and the error is re-raised
211 Transactions can also be aborted by raising Sequel::Rollback:
214 raise(Sequel::Rollback) if something_bad_happened
215 end # ROLLBACK issued and no error raised
217 Savepoints can be used if the database supports it:
220 dataset << {:first_name => 'Farm', :last_name => 'Boy'} # Inserted
221 DB.transaction(:savepoint=>true) # This savepoint is rolled back
222 dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} # Not inserted
223 raise(Sequel::Rollback) if something_bad_happened
225 dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted
230 dataset.sql # "SELECT * FROM items"
231 dataset.delete_sql # "DELETE FROM items"
232 dataset.where(:name => 'sequel').exists # "EXISTS ( SELECT * FROM items WHERE name = 'sequel' )"
233 dataset.columns #=> array of columns in the result set, does a SELECT
234 DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
236 ----------------------------------------------------------------------------------------------------------------------------------------------------------------
240 http://sequel.rubyforge.org/rdoc/files/doc/association_basics_rdoc.html
241 http://sequel.rubyforge.org/rdoc/classes/Sequel/Schema/Generator.html
242 http://sequel.rubyforge.org/rdoc/files/doc/validations_rdoc.html
243 http://sequel.rubyforge.org/rdoc/classes/Sequel/Model.html
247 database.alter_table :deals do
248 add_column :name, String
249 drop_column :column_name
250 rename_column :from, :to
252 add_constraint :valid_name, :name.like('A%')
253 drop_constraint :constraint
255 add_full_text_index :body
256 add_spacial_index [columns]
261 add_foreign_key :artist_id, :table
263 add_unique_constraint [columns]
264 set_column_allow_null :foo, false
265 set_column_default :title, ''
267 set_column_type :price, 'char(10)'
270 ### Model associations
272 class Deal < Sequel::Model
274 # Us (left) <=> Them (right)
275 many_to_many :images,
278 join_table: :image_links
284 many_to_one :parent, class: self
285 one_to_many :children, key: :parent_id, class: self
287 one_to_many :gold_albums, class: :Album do |ds|
288 ds.filter { copies_sold > 50000 }
291 Provided by many_to_many
296 Deal[1].remove_all_images
302 errors.add(:name, 'cannot be empty') if !name || name.empty?
304 validates_presence [:title, :site]
305 validates_unique :name
306 validates_format /\Ahttps?:\/\//, :website, :message=>'is not a valid URL'
307 validates_includes %w(a b c), :type
308 validates_integer :rating
309 validates_numeric :number
310 validates_type String, [:title, :description]
312 validates_integer :rating if new?
314 # options: :message =>, :allow_nil =>, :allow_blank =>,
317 validates_exact_length 17, :isbn
318 validates_min_length 3, :name
319 validates_max_length 100, :name
320 validates_length_range 3..100, :name
324 @values[:filename] = name
335 deal.destroy # Calls hooks
336 deal.delete # No hooks
339 deal.hash # Only uniques
340 deal.keys #=> [:id, :name]
365 class Deal < Sequel::Model
368 primary_key [:id, :title]
369 String :name, primary_key: true
376 check(:price) { num > 0 }
378 foreign_key :artist_id
379 String :artist_name, key: :id
382 index [:artist_id, :name]
383 full_text_index :title
385 # String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
386 # Date, DateTime, Time, File, TrueClass, FalseClass
390 ### Unrestrict primary key
392 Category.create id: 'travel' # error
393 Category.unrestrict_primary_key
394 Category.create id: 'travel' # ok