From abd1baacad8c9f850dd33151d523f63919cca149 Mon Sep 17 00:00:00 2001
From: Erik
{{1..3},{7..9}}
| Same as `1 2 3 7 8 9` |
See: [Brace expansion](https://web.archive.org/web/20230207192110/https://wiki.bash-hackers.org/syntax/expansion/brace)
+## Parameter expansions
-Parameter expansions
---------------------
{: .-three-column}
### Basics
@@ -247,8 +251,8 @@ echo "${str^^}" #=> "HELLO WORLD!" (all uppercase)
Omitting the `:` removes the (non)nullity checks, e.g. `${foo-val}` expands to `val` if unset otherwise `$foo`.
-Loops
------
+## Loops
+
{: .-three-column}
### Basic for loop
@@ -299,8 +303,8 @@ while true; do
done
```
-Functions
----------
+## Functions
+
{: .-three-column}
### Defining functions
@@ -353,21 +357,21 @@ fi
### Arguments
-| Expression | Description |
-| --- | --- |
-| `$#` | Number of arguments |
-| `$*` | All positional arguments (as a single word) |
-| `$@` | All positional arguments (as separate strings) |
-| `$1` | First argument |
-| `$_` | Last argument of the previous command |
+| Expression | Description |
+| ---------- | ---------------------------------------------- |
+| `$#` | Number of arguments |
+| `$*` | All positional arguments (as a single word) |
+| `$@` | All positional arguments (as separate strings) |
+| `$1` | First argument |
+| `$_` | Last argument of the previous command |
**Note**: `$@` and `$*` must be quoted in order to perform as described.
Otherwise, they do exactly the same thing (arguments as separate strings).
See [Special parameters](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables).
-Conditionals
-------------
+## Conditionals
+
{: .-three-column}
### Conditions
@@ -375,7 +379,7 @@ Conditionals
Note that `[[` is actually a command/program that returns either `0` (true) or `1` (false). Any program that obeys the same logic (like all base utils, such as `grep(1)` or `ping(1)`) can be used as condition, see examples.
| Condition | Description |
-| --- | --- |
+| ------------------------ | --------------------- |
| `[[ -z STRING ]]` | Empty string |
| `[[ -n STRING ]]` | Not empty string |
| `[[ STRING == STRING ]]` | Equal |
@@ -405,7 +409,7 @@ Note that `[[` is actually a command/program that returns either `0` (true) or `
### File conditions
| Condition | Description |
-| --- | --- |
+| ----------------------- | ----------------------- |
| `[[ -e FILE ]]` | Exists |
| `[[ -r FILE ]]` | Readable |
| `[[ -h FILE ]]` | Symlink |
@@ -461,8 +465,7 @@ if [[ -e "file.txt" ]]; then
fi
```
-Arrays
-------
+## Arrays
### Defining arrays
@@ -509,8 +512,8 @@ for i in "${arrayName[@]}"; do
done
```
-Dictionaries
-------------
+## Dictionaries
+
{: .-three-column}
### Defining
@@ -556,8 +559,7 @@ for key in "${!sounds[@]}"; do
done
```
-Options
--------
+## Options
### Options
@@ -581,8 +583,7 @@ shopt -s globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b
Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob
matches.
-History
--------
+## History
### Commands
@@ -625,9 +626,7 @@ History
`!!` can be replaced with any valid expansion i.e. `!cat`, `!-2`, `!42`, etc.
-
-Miscellaneous
--------------
+## Miscellaneous
### Numeric calculations
@@ -640,7 +639,7 @@ $(($RANDOM%200)) # Random number 0..199
```
```bash
-declare -i count # Declare as type integer
+declare -i count # Declare as type integer
count+=1 # Increment
```
@@ -732,18 +731,18 @@ printf '%i+%i=%i\n' 1 2 3 4 5 9
### Transform strings
-| Command option | Description |
-| ------------------ | --------------------------------------------------- |
-| `-c` | Operations apply to characters not in the given set |
-| `-d` | Delete characters |
-| `-s` | Replaces repeated characters with single occurrence |
-| `-t` | Truncates |
-| `[:upper:]` | All upper case letters |
-| `[:lower:]` | All lower case letters |
-| `[:digit:]` | All digits |
-| `[:space:]` | All whitespace |
-| `[:alpha:]` | All letters |
-| `[:alnum:]` | All letters and digits |
+| Command option | Description |
+| -------------- | --------------------------------------------------- |
+| `-c` | Operations apply to characters not in the given set |
+| `-d` | Delete characters |
+| `-s` | Replaces repeated characters with single occurrence |
+| `-t` | Truncates |
+| `[:upper:]` | All upper case letters |
+| `[:lower:]` | All lower case letters |
+| `[:digit:]` | All digits |
+| `[:space:]` | All whitespace |
+| `[:alpha:]` | All letters |
+| `[:alnum:]` | All letters and digits |
#### Example
@@ -838,10 +837,11 @@ fi
```
## Also see
+
{: .-one-column}
-* [Bash-hackers wiki](https://web.archive.org/web/20230406205817/https://wiki.bash-hackers.org/) _(bash-hackers.org)_
-* [Shell vars](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars) _(bash-hackers.org)_
-* [Learn bash in y minutes](https://learnxinyminutes.com/docs/bash/) _(learnxinyminutes.com)_
-* [Bash Guide](http://mywiki.wooledge.org/BashGuide) _(mywiki.wooledge.org)_
-* [ShellCheck](https://www.shellcheck.net/) _(shellcheck.net)_
+- [Bash-hackers wiki](https://web.archive.org/web/20230406205817/https://wiki.bash-hackers.org/) _(bash-hackers.org)_
+- [Shell vars](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars) _(bash-hackers.org)_
+- [Learn bash in y minutes](https://learnxinyminutes.com/docs/bash/) _(learnxinyminutes.com)_
+- [Bash Guide](http://mywiki.wooledge.org/BashGuide) _(mywiki.wooledge.org)_
+- [ShellCheck](https://www.shellcheck.net/) _(shellcheck.net)_
diff --git a/pry.md b/pry.md
index fcb0a3c..8e77b1a 100644
--- a/pry.md
+++ b/pry.md
@@ -95,7 +95,7 @@ Also consider [pry-rails](https://rubygems.org/gems/pry-rails).
> ls -i # Instance vars
- > ls -G xx # Grey by regex
+ > ls -G xx # Grep by regex
## Shell integration
diff --git a/sequel.md b/sequel.md
index c57b600..a15750d 100644
--- a/sequel.md
+++ b/sequel.md
@@ -5,6 +5,7 @@ layout: 2017/sheet
---
### About
+
{: .-intro}
Sequel is a database toolkit for Ruby.
@@ -13,382 +14,460 @@ Sequel is a database toolkit for Ruby.
### Open a database
- require 'rubygems'
- require 'sequel'
+```
+require 'rubygems'
+require 'sequel'
- DB = Sequel.sqlite('my_blog.db')
- DB = Sequel.connect('postgres://user:password@localhost/my_db')
- DB = Sequel.postgres('my_db', :user => 'user', :password => 'password', :host => 'localhost')
- DB = Sequel.ado('mydb')
+DB = Sequel.sqlite('my_blog.db')
+DB = Sequel.connect('postgres://user:password@localhost/my_db')
+DB = Sequel.postgres('my_db', :user => 'user', :password => 'password', :host => 'localhost')
+DB = Sequel.ado('mydb')
+```
### Open an SQLite memory database
Without a filename argument, the sqlite adapter will setup a new sqlite database in memory.
- DB = Sequel.sqlite
+```
+DB = Sequel.sqlite
+```
### Logging SQL statements
- require 'logger'
- DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)]
- # or
- DB.loggers << Logger.new(...)
+```
+require 'logger'
+DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)]
+# or
+DB.loggers << Logger.new(...)
+```
### Using raw SQL
- DB.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
- dataset = DB["SELECT age FROM users WHERE name = ?", name]
- dataset.map(:age)
- DB.fetch("SELECT name FROM users") do |row|
- p row[:name]
- end
+```
+DB.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
+dataset = DB["SELECT age FROM users WHERE name = ?", name]
+dataset.map(:age)
+DB.fetch("SELECT name FROM users") do |row|
+ p row[:name]
+end
+```
### Create a dataset
- dataset = DB[:items]
- dataset = DB.from(:items)
+```
+dataset = DB[:items]
+dataset = DB.from(:items)
+```
### Most dataset methods are chainable
- dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
+```
+dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
+```
### Insert rows
- dataset.insert(:name => 'Sharon', :grade => 50)
+```
+dataset.insert(:name => 'Sharon', :grade => 50)
+```
### Retrieve rows
- dataset.each{|r| p r}
- dataset.all # => [{...}, {...}, ...]
- dataset.first # => {...}
+```
+dataset.each{|r| p r}
+dataset.all # => [{...}, {...}, ...]
+dataset.first # => {...}
+```
### Update/Delete rows
- dataset.filter(~:active).delete
- dataset.filter('price < ?', 100).update(:active => true)
+```
+dataset.filter(~:active).delete
+dataset.filter('price < ?', 100).update(:active => true)
+```
### Datasets are Enumerable
- dataset.map{|r| r[:name]}
- dataset.map(:name) # same as above
+```
+dataset.map{|r| r[:name]}
+dataset.map(:name) # same as above
- dataset.inject(0){|sum, r| sum + r[:value]}
- dataset.sum(:value) # same as above
+dataset.inject(0){|sum, r| sum + r[:value]}
+dataset.sum(:value) # same as above
+```
### Filtering (see also doc/dataset_filtering.rdoc)
#### Equality
- dataset.filter(:name => 'abc')
- dataset.filter('name = ?', 'abc')
+```
+dataset.filter(:name => 'abc')
+dataset.filter('name = ?', 'abc')
+```
#### Inequality
- dataset.filter{value > 100}
- dataset.exclude{value <= 100}
+```
+dataset.filter{value > 100}
+dataset.exclude{value <= 100}
+```
#### Inclusion
- dataset.filter(:value => 50..100)
- dataset.where{(value >= 50) & (value <= 100)}
+```
+dataset.filter(:value => 50..100)
+dataset.where{(value >= 50) & (value <= 100)}
- dataset.where('value IN ?', [50,75,100])
- dataset.where(:value=>[50,75,100])
+dataset.where('value IN ?', [50,75,100])
+dataset.where(:value=>[50,75,100])
- dataset.where(:id=>other_dataset.select(:other_id))
+dataset.where(:id=>other_dataset.select(:other_id))
+```
#### Subselects as scalar values
- dataset.where('price > (SELECT avg(price) + 100 FROM table)')
- dataset.filter{price > dataset.select(avg(price) + 100)}
+```
+dataset.where('price > (SELECT avg(price) + 100 FROM table)')
+dataset.filter{price > dataset.select(avg(price) + 100)}
+```
#### LIKE/Regexp
- DB[:items].filter(:name.like('AL%'))
- DB[:items].filter(:name => /^AL/)
+```
+DB[:items].filter(:name.like('AL%'))
+DB[:items].filter(:name => /^AL/)
+```
#### AND/OR/NOT
- DB[:items].filter{(x > 5) & (y > 10)}.sql
- # SELECT * FROM items WHERE ((x > 5) AND (y > 10))
+```
+DB[:items].filter{(x > 5) & (y > 10)}.sql
+# SELECT * FROM items WHERE ((x > 5) AND (y > 10))
- DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
- # SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))
+DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
+# SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))
+```
#### Mathematical operators
- DB[:items].filter((:x + :y) > :z).sql
- # SELECT * FROM items WHERE ((x + y) > z)
+```
+DB[:items].filter((:x + :y) > :z).sql
+# SELECT * FROM items WHERE ((x + y) > z)
- DB[:items].filter{price - 100 < avg(price)}.sql
- # SELECT * FROM items WHERE ((price - 100) < avg(price))
+DB[:items].filter{price - 100 < avg(price)}.sql
+# SELECT * FROM items WHERE ((price - 100) < avg(price))
+```
### Ordering
- dataset.order(:kind)
- dataset.reverse_order(:kind)
- dataset.order(:kind.desc, :name)
+```
+dataset.order(:kind)
+dataset.reverse_order(:kind)
+dataset.order(:kind.desc, :name)
+```
### Limit/Offset
- dataset.limit(30) # LIMIT 30
- dataset.limit(30, 10) # LIMIT 30 OFFSET 10
+```
+dataset.limit(30) # LIMIT 30
+dataset.limit(30, 10) # LIMIT 30 OFFSET 10
+```
### Joins
- DB[:items].left_outer_join(:categories, :id => :category_id).sql
- # SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id
+```
+DB[:items].left_outer_join(:categories, :id => :category_id).sql
+# SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id
+
+DB[:items].join(:categories, :id => :category_id).join(:groups, :id => :items__group_id)
+# SELECT * FROM items INNER JOIN categories ON categories.id = items.category_id INNER JOIN groups ON groups.id = items.group_id
+```
- DB[:items].join(:categories, :id => :category_id).join(:groups, :id => :items__group_id)
- # SELECT * FROM items INNER JOIN categories ON categories.id = items.category_id INNER JOIN groups ON groups.id = items.group_id
-
### Aggregate functions methods
- dataset.count #=> record count
- dataset.max(:price)
- dataset.min(:price)
- dataset.avg(:price)
- dataset.sum(:stock)
+```
+dataset.count #=> record count
+dataset.max(:price)
+dataset.min(:price)
+dataset.avg(:price)
+dataset.sum(:stock)
- dataset.group_and_count(:category)
- dataset.group(:category).select(:category, :AVG.sql_function(:price))
+dataset.group_and_count(:category)
+dataset.group(:category).select(:category, :AVG.sql_function(:price))
+```
### SQL Functions / Literals
- dataset.update(:updated_at => :NOW.sql_function)
- dataset.update(:updated_at => 'NOW()'.lit)
+```
+dataset.update(:updated_at => :NOW.sql_function)
+dataset.update(:updated_at => 'NOW()'.lit)
- dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
- dataset.update(:updated_at => :DateValue.sql_function('1/1/2001'))
+dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
+dataset.update(:updated_at => :DateValue.sql_function('1/1/2001'))
+```
### Schema Manipulation
- DB.create_table :items do
- primary_key :id
- String :name, :unique => true, :null => false
- TrueClass :active, :default => true
- foreign_key :category_id, :categories
- DateTime :created_at
-
- index :created_at
- end
+```
+DB.create_table :items do
+ primary_key :id
+ String :name, :unique => true, :null => false
+ TrueClass :active, :default => true
+ foreign_key :category_id, :categories
+ DateTime :created_at
- DB.drop_table :items
+ index :created_at
+end
- DB.create_table :test do
- String :zipcode
- enum :system, :elements => ['mac', 'linux', 'windows']
- end
+DB.drop_table :items
+
+DB.create_table :test do
+ String :zipcode
+ enum :system, :elements => ['mac', 'linux', 'windows']
+end
+```
### Aliasing
- DB[:items].select(:name.as(:item_name))
- DB[:items].select(:name___item_name)
- DB[:items___items_table].select(:items_table__name___item_name)
- # SELECT items_table.name AS item_name FROM items AS items_table
+```
+DB[:items].select(:name.as(:item_name))
+DB[:items].select(:name___item_name)
+DB[:items___items_table].select(:items_table__name___item_name)
+# SELECT items_table.name AS item_name FROM items AS items_table
+```
### Transactions
- DB.transaction do
- dataset.insert(:first_name => 'Inigo', :last_name => 'Montoya')
- dataset.insert(:first_name => 'Farm', :last_name => 'Boy')
- end # Either both are inserted or neither are inserted
+```
+DB.transaction do
+ dataset.insert(:first_name => 'Inigo', :last_name => 'Montoya')
+ dataset.insert(:first_name => 'Farm', :last_name => 'Boy')
+end # Either both are inserted or neither are inserted
+```
Database#transaction is re-entrant:
- DB.transaction do # BEGIN issued only here
- DB.transaction
- dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
- end
- end # COMMIT issued only here
+```
+DB.transaction do # BEGIN issued only here
+ DB.transaction
+ dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
+ end
+end # COMMIT issued only here
+```
Transactions are aborted if an error is raised:
- DB.transaction do
- raise "some error occurred"
- end # ROLLBACK issued and the error is re-raised
+```
+DB.transaction do
+ raise "some error occurred"
+end # ROLLBACK issued and the error is re-raised
+```
Transactions can also be aborted by raising Sequel::Rollback:
- DB.transaction do
- raise(Sequel::Rollback) if something_bad_happened
- end # ROLLBACK issued and no error raised
+```
+DB.transaction do
+ raise(Sequel::Rollback) if something_bad_happened
+end # ROLLBACK issued and no error raised
+```
Savepoints can be used if the database supports it:
- DB.transaction do
- dataset << {:first_name => 'Farm', :last_name => 'Boy'} # Inserted
- DB.transaction(:savepoint=>true) # This savepoint is rolled back
- dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} # Not inserted
- raise(Sequel::Rollback) if something_bad_happened
- end
- dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted
- end
+```
+DB.transaction do
+ dataset << {:first_name => 'Farm', :last_name => 'Boy'} # Inserted
+ DB.transaction(:savepoint=>true) # This savepoint is rolled back
+ dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} # Not inserted
+ raise(Sequel::Rollback) if something_bad_happened
+ end
+ dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted
+end
+```
### Miscellaneous:
- dataset.sql # "SELECT * FROM items"
- dataset.delete_sql # "DELETE FROM items"
- dataset.where(:name => 'sequel').exists # "EXISTS ( SELECT * FROM items WHERE name = 'sequel' )"
- dataset.columns #=> array of columns in the result set, does a SELECT
- DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
+```
+dataset.sql # "SELECT * FROM items"
+dataset.delete_sql # "DELETE FROM items"
+dataset.where(:name => 'sequel').exists # "EXISTS ( SELECT * FROM items WHERE name = 'sequel' )"
+dataset.columns #=> array of columns in the result set, does a SELECT
+DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
+```
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+---
### Documents
- http://sequel.rubyforge.org/rdoc/files/doc/association_basics_rdoc.html
- http://sequel.rubyforge.org/rdoc/classes/Sequel/Schema/Generator.html
- http://sequel.rubyforge.org/rdoc/files/doc/validations_rdoc.html
- http://sequel.rubyforge.org/rdoc/classes/Sequel/Model.html
+```
+http://sequel.rubyforge.org/rdoc/files/doc/association_basics_rdoc.html
+http://sequel.rubyforge.org/rdoc/classes/Sequel/Schema/Generator.html
+http://sequel.rubyforge.org/rdoc/files/doc/validations_rdoc.html
+http://sequel.rubyforge.org/rdoc/classes/Sequel/Model.html
+```
### Alter table
- database.alter_table :deals do
- add_column :name, String
- drop_column :column_name
- rename_column :from, :to
+```
+database.alter_table :deals do
+ add_column :name, String
+ drop_column :column_name
+ rename_column :from, :to
- add_constraint :valid_name, :name.like('A%')
- drop_constraint :constraint
+ add_constraint :valid_name, :name.like('A%')
+ drop_constraint :constraint
- add_full_text_index :body
- add_spacial_index [columns]
+ add_full_text_index :body
+ add_spacial_index [columns]
- add_index :price
- drop_index :index
+ add_index :price
+ drop_index :index
- add_foreign_key :artist_id, :table
- add_primary_key :id
- add_unique_constraint [columns]
- set_column_allow_null :foo, false
- set_column_default :title, ''
+ add_foreign_key :artist_id, :table
+ add_primary_key :id
+ add_unique_constraint [columns]
+ set_column_allow_null :foo, false
+ set_column_default :title, ''
- set_column_type :price, 'char(10)'
- end
+ set_column_type :price, 'char(10)'
+end
+```
### Model associations
- class Deal < Sequel::Model
+```
+class Deal < Sequel::Model
- # Us (left) <=> Them (right)
- many_to_many :images,
- left_id: :deal_id,
- right_id: :image_id,
- join_table: :image_links
+ # Us (left) <=> Them (right)
+ many_to_many :images,
+ left_key: :deal_id,
+ right_key: :image_id,
+ join_table: :image_links
- one_to_many :files,
- key: :deal_id,
- class: :DataFile,
+ one_to_many :files,
+ key: :deal_id,
+ class: :DataFile,
- many_to_one :parent, class: self
- one_to_many :children, key: :parent_id, class: self
+ many_to_one :parent, class: self
+ one_to_many :children, key: :parent_id, class: self
- one_to_many :gold_albums, class: :Album do |ds|
- ds.filter { copies_sold > 50000 }
- end
+ one_to_many :gold_albums, class: :Album do |ds|
+ ds.filter { copies_sold > 50000 }
+ end
+```
Provided by many_to_many
- Deal[1].images
- Deal[1].add_image
- Deal[1].remove_image
- Deal[1].remove_all_images
+```
+Deal[1].images
+Deal[1].add_image
+Deal[1].remove_image
+Deal[1].remove_all_images
+```
### Validations
- def validate
- super
- errors.add(:name, 'cannot be empty') if !name || name.empty?
-
- validates_presence [:title, :site]
- validates_unique :name
- validates_format /\Ahttps?:\/\//, :website, :message=>'is not a valid URL'
- validates_includes %w(a b c), :type
- validates_integer :rating
- validates_numeric :number
- validates_type String, [:title, :description]
-
- validates_integer :rating if new?
-
- # options: :message =>, :allow_nil =>, :allow_blank =>,
- # :allow_missing =>,
-
- validates_exact_length 17, :isbn
- validates_min_length 3, :name
- validates_max_length 100, :name
- validates_length_range 3..100, :name
-
- # Setter override
- def filename=(name)
- @values[:filename] = name
- end
- end
+```
+def validate
+ super
+ errors.add(:name, 'cannot be empty') if !name || name.empty?
+
+ validates_presence [:title, :site]
+ validates_unique :name
+ validates_format /\Ahttps?:\/\//, :website, :message=>'is not a valid URL'
+ validates_includes %w(a b c), :type
+ validates_integer :rating
+ validates_numeric :number
+ validates_type String, [:title, :description]
+
+ validates_integer :rating if new?
+
+ # options: :message =>, :allow_nil =>, :allow_blank =>,
+ # :allow_missing =>,
+
+ validates_exact_length 17, :isbn
+ validates_min_length 3, :name
+ validates_max_length 100, :name
+ validates_length_range 3..100, :name
+
+ # Setter override
+ def filename=(name)
+ @values[:filename] = name
end
+ end
+end
- deal.errors
+deal.errors
+```
### Model stuff
- deal = Deal[1]
- deal.changed_columns
- deal.destroy # Calls hooks
- deal.delete # No hooks
- deal.exists?
- deal.new?
- deal.hash # Only uniques
- deal.keys #=> [:id, :name]
- deal.modified!
- deal.modified?
-
- deal.lock!
+```
+deal = Deal[1]
+deal.changed_columns
+deal.destroy # Calls hooks
+deal.delete # No hooks
+deal.exists?
+deal.new?
+deal.hash # Only uniques
+deal.keys #=> [:id, :name]
+deal.modified!
+deal.modified?
+
+deal.lock!
+```
### Callbacks
- before_create
- after_create
+```
+before_create
+after_create
- before_validation
- after_validation
- before_save
- before_update
- UPDATE QUERY
- after_update
- after_save
+before_validation
+after_validation
+before_save
+before_update
+UPDATE QUERY
+after_update
+after_save
- before_destroy
- DELETE QUERY
- after_destroy
+before_destroy
+DELETE QUERY
+after_destroy
+```
### Schema
- class Deal < Sequel::Model
- set_schema do
- primary_key :id
- primary_key [:id, :title]
- String :name, primary_key: true
-
- String :title
- Numeric :price
- DateTime :expires
-
- unique :whatever
- check(:price) { num > 0 }
-
- foreign_key :artist_id
- String :artist_name, key: :id
-
- index :title
- index [:artist_id, :name]
- full_text_index :title
-
- # String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
- # Date, DateTime, Time, File, TrueClass, FalseClass
- end
- end
+```
+class Deal < Sequel::Model
+ set_schema do
+ primary_key :id
+ primary_key [:id, :title]
+ String :name, primary_key: true
+
+ String :title
+ Numeric :price
+ DateTime :expires
+
+ unique :whatever
+ check(:price) { num > 0 }
+
+ foreign_key :artist_id
+ String :artist_name, key: :id
+
+ index :title
+ index [:artist_id, :name]
+ full_text_index :title
+
+ # String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
+ # Date, DateTime, Time, File, TrueClass, FalseClass
+ end
+end
+```
### Unrestrict primary key
- Category.create id: 'travel' # error
- Category.unrestrict_primary_key
- Category.create id: 'travel' # ok
+```
+Category.create id: 'travel' # error
+Category.unrestrict_primary_key
+Category.create id: 'travel' # ok
+```
diff --git a/typescript.md b/typescript.md
index d9cd501..7a4e6e7 100644
--- a/typescript.md
+++ b/typescript.md
@@ -244,7 +244,7 @@ type Point = { x: number; y: number };
type P = keyof Point; // x | y
```
-## Conditinal Types
+## Conditional Types
```ts
// SomeType extends OtherType ? TrueType : FalseType;
--
2.11.0