| Revision | abd1baacad8c9f850dd33151d523f63919cca149 (tree) |
|---|---|
| Time | 2023-12-14 23:55:05 |
| Author | |
| Commiter | Erik |
Regular updates
| @@ -12,8 +12,8 @@ sheet: | ||
| 12 | 12 | suffix: cheatsheet |
| 13 | 13 | |
| 14 | 14 | social_list: |
| 15 | - default_description: "Ridiculous collection of web development cheatsheets" | |
| 16 | - description: "The ultimate cheatsheet for TITLE." | |
| 15 | + default_description: 'Ridiculous collection of web development cheatsheets' | |
| 16 | + description: 'The ultimate cheatsheet for TITLE.' | |
| 17 | 17 | facebook_share: Share on Facebook |
| 18 | 18 | twitter_share: Share on Twitter |
| 19 | 19 |
| @@ -33,7 +33,7 @@ search_form: | ||
| 33 | 33 | |
| 34 | 34 | comments_area: |
| 35 | 35 | suffix: for this cheatsheet. |
| 36 | - link: "Write yours!" | |
| 36 | + link: 'Write yours!' | |
| 37 | 37 | |
| 38 | 38 | not_found: |
| 39 | 39 | title: Not found |
| @@ -41,10 +41,11 @@ not_found: | ||
| 41 | 41 | home: Back to home |
| 42 | 42 | |
| 43 | 43 | announcement: |
| 44 | - id: 2017-10-26-twitter | |
| 44 | + # id: 2017-10-26-twitter | |
| 45 | + id: 2023-12-14 | |
| 45 | 46 | title: | |
| 46 | - New: We're on Twitter ♥️ | |
| 47 | + We're on Twitter ♥️ | |
| 47 | 48 | body: | |
| 48 | - Follow [@devhints](https://twitter.com/devhints) on Twitter for daily "today I learned" snippets! | |
| 49 | + Follow [@devhints](https://twitter.com/devhints) on X/Twitter for daily "today I learned" snippets. | |
| 49 | 50 | |
| 50 | - [](https://twitter.com/devhints) | |
| 51 | + Also: I've started a new blog with some insights on web devlopment. Have a look! [**ricostacruz.com/posts**](https://ricostacruz.com/posts?utm_source=devhints) |
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | Suggestions and corrections? <a href='https://github.com/rstacruz/cheatsheets/issues/907'>Send them in</a>. |
| 6 | 6 | <i class='fleuron'></i> |
| 7 | 7 | I'm <a href="http://ricostacruz.com">Rico Sta. Cruz</a>. |
| 8 | - Check out my <a href="http://ricostacruz.com/til">Today I learned blog</a> for more. | |
| 8 | + Check out my <a href="http://ricostacruz.com/posts">Today I learned blog</a> for more. | |
| 9 | 9 | </p> |
| 10 | 10 | |
| 11 | 11 | {% if page.url != '/index.html' %} |
| @@ -14,11 +14,12 @@ keywords: | ||
| 14 | 14 | - Command substitution |
| 15 | 15 | --- |
| 16 | 16 | |
| 17 | -Getting started | |
| 18 | ---------------- | |
| 17 | +## Getting started | |
| 18 | + | |
| 19 | 19 | {: .-three-column} |
| 20 | 20 | |
| 21 | 21 | ### Introduction |
| 22 | + | |
| 22 | 23 | {: .-intro} |
| 23 | 24 | |
| 24 | 25 | This is a quick reference to getting started with Bash scripting. |
| @@ -44,6 +45,7 @@ echo $name # see below | ||
| 44 | 45 | echo "$name" |
| 45 | 46 | echo "${name}!" |
| 46 | 47 | ``` |
| 48 | + | |
| 47 | 49 | Generally quote your variables unless they contain wildcards to expand or command fragments. |
| 48 | 50 | |
| 49 | 51 | ```bash |
| @@ -78,6 +80,7 @@ git commit || echo "Commit failed" | ||
| 78 | 80 | ``` |
| 79 | 81 | |
| 80 | 82 | ### Functions |
| 83 | + | |
| 81 | 84 | {: id='functions-example'} |
| 82 | 85 | |
| 83 | 86 | ```bash |
| @@ -91,6 +94,7 @@ echo "You are $(get_name)" | ||
| 91 | 94 | See: [Functions](#functions) |
| 92 | 95 | |
| 93 | 96 | ### Conditionals |
| 97 | + | |
| 94 | 98 | {: id='conditionals-example'} |
| 95 | 99 | |
| 96 | 100 | ```bash |
| @@ -118,17 +122,17 @@ See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash | ||
| 118 | 122 | echo {A,B}.js |
| 119 | 123 | ``` |
| 120 | 124 | |
| 121 | -| Expression | Description | | |
| 122 | -| ---------- | ------------------- | | |
| 123 | -| `{A,B}` | Same as `A B` | | |
| 124 | -| `{A,B}.js` | Same as `A.js B.js` | | |
| 125 | -| `{1..5}` | Same as `1 2 3 4 5` | | |
| 125 | +| Expression | Description | | |
| 126 | +| ---------------------- | --------------------- | | |
| 127 | +| `{A,B}` | Same as `A B` | | |
| 128 | +| `{A,B}.js` | Same as `A.js B.js` | | |
| 129 | +| `{1..5}` | Same as `1 2 3 4 5` | | |
| 130 | +| <code>{{1..3},{7..9}}</code> | Same as `1 2 3 7 8 9` | | |
| 126 | 131 | |
| 127 | 132 | See: [Brace expansion](https://web.archive.org/web/20230207192110/https://wiki.bash-hackers.org/syntax/expansion/brace) |
| 128 | 133 | |
| 134 | +## Parameter expansions | |
| 129 | 135 | |
| 130 | -Parameter expansions | |
| 131 | --------------------- | |
| 132 | 136 | {: .-three-column} |
| 133 | 137 | |
| 134 | 138 | ### Basics |
| @@ -247,8 +251,8 @@ echo "${str^^}" #=> "HELLO WORLD!" (all uppercase) | ||
| 247 | 251 | |
| 248 | 252 | Omitting the `:` removes the (non)nullity checks, e.g. `${foo-val}` expands to `val` if unset otherwise `$foo`. |
| 249 | 253 | |
| 250 | -Loops | |
| 251 | ------ | |
| 254 | +## Loops | |
| 255 | + | |
| 252 | 256 | {: .-three-column} |
| 253 | 257 | |
| 254 | 258 | ### Basic for loop |
| @@ -299,8 +303,8 @@ while true; do | ||
| 299 | 303 | done |
| 300 | 304 | ``` |
| 301 | 305 | |
| 302 | -Functions | |
| 303 | ---------- | |
| 306 | +## Functions | |
| 307 | + | |
| 304 | 308 | {: .-three-column} |
| 305 | 309 | |
| 306 | 310 | ### Defining functions |
| @@ -353,21 +357,21 @@ fi | ||
| 353 | 357 | |
| 354 | 358 | ### Arguments |
| 355 | 359 | |
| 356 | -| Expression | Description | | |
| 357 | -| --- | --- | | |
| 358 | -| `$#` | Number of arguments | | |
| 359 | -| `$*` | All positional arguments (as a single word) | | |
| 360 | -| `$@` | All positional arguments (as separate strings) | | |
| 361 | -| `$1` | First argument | | |
| 362 | -| `$_` | Last argument of the previous command | | |
| 360 | +| Expression | Description | | |
| 361 | +| ---------- | ---------------------------------------------- | | |
| 362 | +| `$#` | Number of arguments | | |
| 363 | +| `$*` | All positional arguments (as a single word) | | |
| 364 | +| `$@` | All positional arguments (as separate strings) | | |
| 365 | +| `$1` | First argument | | |
| 366 | +| `$_` | Last argument of the previous command | | |
| 363 | 367 | |
| 364 | 368 | **Note**: `$@` and `$*` must be quoted in order to perform as described. |
| 365 | 369 | Otherwise, they do exactly the same thing (arguments as separate strings). |
| 366 | 370 | |
| 367 | 371 | See [Special parameters](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). |
| 368 | 372 | |
| 369 | -Conditionals | |
| 370 | ------------- | |
| 373 | +## Conditionals | |
| 374 | + | |
| 371 | 375 | {: .-three-column} |
| 372 | 376 | |
| 373 | 377 | ### Conditions |
| @@ -375,7 +379,7 @@ Conditionals | ||
| 375 | 379 | 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. |
| 376 | 380 | |
| 377 | 381 | | Condition | Description | |
| 378 | -| --- | --- | | |
| 382 | +| ------------------------ | --------------------- | | |
| 379 | 383 | | `[[ -z STRING ]]` | Empty string | |
| 380 | 384 | | `[[ -n STRING ]]` | Not empty string | |
| 381 | 385 | | `[[ STRING == STRING ]]` | Equal | |
| @@ -405,7 +409,7 @@ Note that `[[` is actually a command/program that returns either `0` (true) or ` | ||
| 405 | 409 | ### File conditions |
| 406 | 410 | |
| 407 | 411 | | Condition | Description | |
| 408 | -| --- | --- | | |
| 412 | +| ----------------------- | ----------------------- | | |
| 409 | 413 | | `[[ -e FILE ]]` | Exists | |
| 410 | 414 | | `[[ -r FILE ]]` | Readable | |
| 411 | 415 | | `[[ -h FILE ]]` | Symlink | |
| @@ -461,8 +465,7 @@ if [[ -e "file.txt" ]]; then | ||
| 461 | 465 | fi |
| 462 | 466 | ``` |
| 463 | 467 | |
| 464 | -Arrays | |
| 465 | ------- | |
| 468 | +## Arrays | |
| 466 | 469 | |
| 467 | 470 | ### Defining arrays |
| 468 | 471 |
| @@ -509,8 +512,8 @@ for i in "${arrayName[@]}"; do | ||
| 509 | 512 | done |
| 510 | 513 | ``` |
| 511 | 514 | |
| 512 | -Dictionaries | |
| 513 | ------------- | |
| 515 | +## Dictionaries | |
| 516 | + | |
| 514 | 517 | {: .-three-column} |
| 515 | 518 | |
| 516 | 519 | ### Defining |
| @@ -556,8 +559,7 @@ for key in "${!sounds[@]}"; do | ||
| 556 | 559 | done |
| 557 | 560 | ``` |
| 558 | 561 | |
| 559 | -Options | |
| 560 | -------- | |
| 562 | +## Options | |
| 561 | 563 | |
| 562 | 564 | ### Options |
| 563 | 565 |
| @@ -581,8 +583,7 @@ shopt -s globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b | ||
| 581 | 583 | Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob |
| 582 | 584 | matches. |
| 583 | 585 | |
| 584 | -History | |
| 585 | -------- | |
| 586 | +## History | |
| 586 | 587 | |
| 587 | 588 | ### Commands |
| 588 | 589 |
| @@ -625,9 +626,7 @@ History | ||
| 625 | 626 | |
| 626 | 627 | `!!` can be replaced with any valid expansion i.e. `!cat`, `!-2`, `!42`, etc. |
| 627 | 628 | |
| 628 | - | |
| 629 | -Miscellaneous | |
| 630 | -------------- | |
| 629 | +## Miscellaneous | |
| 631 | 630 | |
| 632 | 631 | ### Numeric calculations |
| 633 | 632 |
| @@ -640,7 +639,7 @@ $(($RANDOM%200)) # Random number 0..199 | ||
| 640 | 639 | ``` |
| 641 | 640 | |
| 642 | 641 | ```bash |
| 643 | -declare -i count # Declare as type integer | |
| 642 | +declare -i count # Declare as type integer | |
| 644 | 643 | count+=1 # Increment |
| 645 | 644 | ``` |
| 646 | 645 |
| @@ -732,18 +731,18 @@ printf '%i+%i=%i\n' 1 2 3 4 5 9 | ||
| 732 | 731 | |
| 733 | 732 | ### Transform strings |
| 734 | 733 | |
| 735 | -| Command option | Description | | |
| 736 | -| ------------------ | --------------------------------------------------- | | |
| 737 | -| `-c` | Operations apply to characters not in the given set | | |
| 738 | -| `-d` | Delete characters | | |
| 739 | -| `-s` | Replaces repeated characters with single occurrence | | |
| 740 | -| `-t` | Truncates | | |
| 741 | -| `[:upper:]` | All upper case letters | | |
| 742 | -| `[:lower:]` | All lower case letters | | |
| 743 | -| `[:digit:]` | All digits | | |
| 744 | -| `[:space:]` | All whitespace | | |
| 745 | -| `[:alpha:]` | All letters | | |
| 746 | -| `[:alnum:]` | All letters and digits | | |
| 734 | +| Command option | Description | | |
| 735 | +| -------------- | --------------------------------------------------- | | |
| 736 | +| `-c` | Operations apply to characters not in the given set | | |
| 737 | +| `-d` | Delete characters | | |
| 738 | +| `-s` | Replaces repeated characters with single occurrence | | |
| 739 | +| `-t` | Truncates | | |
| 740 | +| `[:upper:]` | All upper case letters | | |
| 741 | +| `[:lower:]` | All lower case letters | | |
| 742 | +| `[:digit:]` | All digits | | |
| 743 | +| `[:space:]` | All whitespace | | |
| 744 | +| `[:alpha:]` | All letters | | |
| 745 | +| `[:alnum:]` | All letters and digits | | |
| 747 | 746 | |
| 748 | 747 | #### Example |
| 749 | 748 |
| @@ -838,10 +837,11 @@ fi | ||
| 838 | 837 | ``` |
| 839 | 838 | |
| 840 | 839 | ## Also see |
| 840 | + | |
| 841 | 841 | {: .-one-column} |
| 842 | 842 | |
| 843 | -* [Bash-hackers wiki](https://web.archive.org/web/20230406205817/https://wiki.bash-hackers.org/) _(bash-hackers.org)_ | |
| 844 | -* [Shell vars](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars) _(bash-hackers.org)_ | |
| 845 | -* [Learn bash in y minutes](https://learnxinyminutes.com/docs/bash/) _(learnxinyminutes.com)_ | |
| 846 | -* [Bash Guide](http://mywiki.wooledge.org/BashGuide) _(mywiki.wooledge.org)_ | |
| 847 | -* [ShellCheck](https://www.shellcheck.net/) _(shellcheck.net)_ | |
| 843 | +- [Bash-hackers wiki](https://web.archive.org/web/20230406205817/https://wiki.bash-hackers.org/) _(bash-hackers.org)_ | |
| 844 | +- [Shell vars](https://web.archive.org/web/20230318164746/https://wiki.bash-hackers.org/syntax/shellvars) _(bash-hackers.org)_ | |
| 845 | +- [Learn bash in y minutes](https://learnxinyminutes.com/docs/bash/) _(learnxinyminutes.com)_ | |
| 846 | +- [Bash Guide](http://mywiki.wooledge.org/BashGuide) _(mywiki.wooledge.org)_ | |
| 847 | +- [ShellCheck](https://www.shellcheck.net/) _(shellcheck.net)_ |
| @@ -95,7 +95,7 @@ Also consider [pry-rails](https://rubygems.org/gems/pry-rails). | ||
| 95 | 95 | |
| 96 | 96 | > ls -i # Instance vars |
| 97 | 97 | |
| 98 | - > ls -G xx # Grey by regex | |
| 98 | + > ls -G xx # Grep by regex | |
| 99 | 99 | |
| 100 | 100 | ## Shell integration |
| 101 | 101 |
| @@ -5,6 +5,7 @@ layout: 2017/sheet | ||
| 5 | 5 | --- |
| 6 | 6 | |
| 7 | 7 | ### About |
| 8 | + | |
| 8 | 9 | {: .-intro} |
| 9 | 10 | |
| 10 | 11 | Sequel is a database toolkit for Ruby. |
| @@ -13,382 +14,460 @@ Sequel is a database toolkit for Ruby. | ||
| 13 | 14 | |
| 14 | 15 | ### Open a database |
| 15 | 16 | |
| 16 | - require 'rubygems' | |
| 17 | - require 'sequel' | |
| 17 | +``` | |
| 18 | +require 'rubygems' | |
| 19 | +require 'sequel' | |
| 18 | 20 | |
| 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') | |
| 21 | +DB = Sequel.sqlite('my_blog.db') | |
| 22 | +DB = Sequel.connect('postgres://user:password@localhost/my_db') | |
| 23 | +DB = Sequel.postgres('my_db', :user => 'user', :password => 'password', :host => 'localhost') | |
| 24 | +DB = Sequel.ado('mydb') | |
| 25 | +``` | |
| 23 | 26 | |
| 24 | 27 | ### Open an SQLite memory database |
| 25 | 28 | |
| 26 | 29 | Without a filename argument, the sqlite adapter will setup a new sqlite database in memory. |
| 27 | 30 | |
| 28 | - DB = Sequel.sqlite | |
| 31 | +``` | |
| 32 | +DB = Sequel.sqlite | |
| 33 | +``` | |
| 29 | 34 | |
| 30 | 35 | ### Logging SQL statements |
| 31 | 36 | |
| 32 | - require 'logger' | |
| 33 | - DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)] | |
| 34 | - # or | |
| 35 | - DB.loggers << Logger.new(...) | |
| 37 | +``` | |
| 38 | +require 'logger' | |
| 39 | +DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)] | |
| 40 | +# or | |
| 41 | +DB.loggers << Logger.new(...) | |
| 42 | +``` | |
| 36 | 43 | |
| 37 | 44 | ### Using raw SQL |
| 38 | 45 | |
| 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] | |
| 41 | - dataset.map(:age) | |
| 42 | - DB.fetch("SELECT name FROM users") do |row| | |
| 43 | - p row[:name] | |
| 44 | - end | |
| 46 | +``` | |
| 47 | +DB.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)" | |
| 48 | +dataset = DB["SELECT age FROM users WHERE name = ?", name] | |
| 49 | +dataset.map(:age) | |
| 50 | +DB.fetch("SELECT name FROM users") do |row| | |
| 51 | + p row[:name] | |
| 52 | +end | |
| 53 | +``` | |
| 45 | 54 | |
| 46 | 55 | ### Create a dataset |
| 47 | 56 | |
| 48 | - dataset = DB[:items] | |
| 49 | - dataset = DB.from(:items) | |
| 57 | +``` | |
| 58 | +dataset = DB[:items] | |
| 59 | +dataset = DB.from(:items) | |
| 60 | +``` | |
| 50 | 61 | |
| 51 | 62 | ### Most dataset methods are chainable |
| 52 | 63 | |
| 53 | - dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department) | |
| 64 | +``` | |
| 65 | +dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department) | |
| 66 | +``` | |
| 54 | 67 | |
| 55 | 68 | ### Insert rows |
| 56 | 69 | |
| 57 | - dataset.insert(:name => 'Sharon', :grade => 50) | |
| 70 | +``` | |
| 71 | +dataset.insert(:name => 'Sharon', :grade => 50) | |
| 72 | +``` | |
| 58 | 73 | |
| 59 | 74 | ### Retrieve rows |
| 60 | 75 | |
| 61 | - dataset.each{|r| p r} | |
| 62 | - dataset.all # => [{...}, {...}, ...] | |
| 63 | - dataset.first # => {...} | |
| 76 | +``` | |
| 77 | +dataset.each{|r| p r} | |
| 78 | +dataset.all # => [{...}, {...}, ...] | |
| 79 | +dataset.first # => {...} | |
| 80 | +``` | |
| 64 | 81 | |
| 65 | 82 | ### Update/Delete rows |
| 66 | 83 | |
| 67 | - dataset.filter(~:active).delete | |
| 68 | - dataset.filter('price < ?', 100).update(:active => true) | |
| 84 | +``` | |
| 85 | +dataset.filter(~:active).delete | |
| 86 | +dataset.filter('price < ?', 100).update(:active => true) | |
| 87 | +``` | |
| 69 | 88 | |
| 70 | 89 | ### Datasets are Enumerable |
| 71 | 90 | |
| 72 | - dataset.map{|r| r[:name]} | |
| 73 | - dataset.map(:name) # same as above | |
| 91 | +``` | |
| 92 | +dataset.map{|r| r[:name]} | |
| 93 | +dataset.map(:name) # same as above | |
| 74 | 94 | |
| 75 | - dataset.inject(0){|sum, r| sum + r[:value]} | |
| 76 | - dataset.sum(:value) # same as above | |
| 95 | +dataset.inject(0){|sum, r| sum + r[:value]} | |
| 96 | +dataset.sum(:value) # same as above | |
| 97 | +``` | |
| 77 | 98 | |
| 78 | 99 | ### Filtering (see also doc/dataset_filtering.rdoc) |
| 79 | 100 | |
| 80 | 101 | #### Equality |
| 81 | 102 | |
| 82 | - dataset.filter(:name => 'abc') | |
| 83 | - dataset.filter('name = ?', 'abc') | |
| 103 | +``` | |
| 104 | +dataset.filter(:name => 'abc') | |
| 105 | +dataset.filter('name = ?', 'abc') | |
| 106 | +``` | |
| 84 | 107 | |
| 85 | 108 | #### Inequality |
| 86 | 109 | |
| 87 | - dataset.filter{value > 100} | |
| 88 | - dataset.exclude{value <= 100} | |
| 110 | +``` | |
| 111 | +dataset.filter{value > 100} | |
| 112 | +dataset.exclude{value <= 100} | |
| 113 | +``` | |
| 89 | 114 | |
| 90 | 115 | #### Inclusion |
| 91 | 116 | |
| 92 | - dataset.filter(:value => 50..100) | |
| 93 | - dataset.where{(value >= 50) & (value <= 100)} | |
| 117 | +``` | |
| 118 | +dataset.filter(:value => 50..100) | |
| 119 | +dataset.where{(value >= 50) & (value <= 100)} | |
| 94 | 120 | |
| 95 | - dataset.where('value IN ?', [50,75,100]) | |
| 96 | - dataset.where(:value=>[50,75,100]) | |
| 121 | +dataset.where('value IN ?', [50,75,100]) | |
| 122 | +dataset.where(:value=>[50,75,100]) | |
| 97 | 123 | |
| 98 | - dataset.where(:id=>other_dataset.select(:other_id)) | |
| 124 | +dataset.where(:id=>other_dataset.select(:other_id)) | |
| 125 | +``` | |
| 99 | 126 | |
| 100 | 127 | #### Subselects as scalar values |
| 101 | 128 | |
| 102 | - dataset.where('price > (SELECT avg(price) + 100 FROM table)') | |
| 103 | - dataset.filter{price > dataset.select(avg(price) + 100)} | |
| 129 | +``` | |
| 130 | +dataset.where('price > (SELECT avg(price) + 100 FROM table)') | |
| 131 | +dataset.filter{price > dataset.select(avg(price) + 100)} | |
| 132 | +``` | |
| 104 | 133 | |
| 105 | 134 | #### LIKE/Regexp |
| 106 | 135 | |
| 107 | - DB[:items].filter(:name.like('AL%')) | |
| 108 | - DB[:items].filter(:name => /^AL/) | |
| 136 | +``` | |
| 137 | +DB[:items].filter(:name.like('AL%')) | |
| 138 | +DB[:items].filter(:name => /^AL/) | |
| 139 | +``` | |
| 109 | 140 | |
| 110 | 141 | #### AND/OR/NOT |
| 111 | 142 | |
| 112 | - DB[:items].filter{(x > 5) & (y > 10)}.sql | |
| 113 | - # SELECT * FROM items WHERE ((x > 5) AND (y > 10)) | |
| 143 | +``` | |
| 144 | +DB[:items].filter{(x > 5) & (y > 10)}.sql | |
| 145 | +# SELECT * FROM items WHERE ((x > 5) AND (y > 10)) | |
| 114 | 146 | |
| 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)) | |
| 147 | +DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql | |
| 148 | +# SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3)) | |
| 149 | +``` | |
| 117 | 150 | |
| 118 | 151 | #### Mathematical operators |
| 119 | 152 | |
| 120 | - DB[:items].filter((:x + :y) > :z).sql | |
| 121 | - # SELECT * FROM items WHERE ((x + y) > z) | |
| 153 | +``` | |
| 154 | +DB[:items].filter((:x + :y) > :z).sql | |
| 155 | +# SELECT * FROM items WHERE ((x + y) > z) | |
| 122 | 156 | |
| 123 | - DB[:items].filter{price - 100 < avg(price)}.sql | |
| 124 | - # SELECT * FROM items WHERE ((price - 100) < avg(price)) | |
| 157 | +DB[:items].filter{price - 100 < avg(price)}.sql | |
| 158 | +# SELECT * FROM items WHERE ((price - 100) < avg(price)) | |
| 159 | +``` | |
| 125 | 160 | |
| 126 | 161 | ### Ordering |
| 127 | 162 | |
| 128 | - dataset.order(:kind) | |
| 129 | - dataset.reverse_order(:kind) | |
| 130 | - dataset.order(:kind.desc, :name) | |
| 163 | +``` | |
| 164 | +dataset.order(:kind) | |
| 165 | +dataset.reverse_order(:kind) | |
| 166 | +dataset.order(:kind.desc, :name) | |
| 167 | +``` | |
| 131 | 168 | |
| 132 | 169 | ### Limit/Offset |
| 133 | 170 | |
| 134 | - dataset.limit(30) # LIMIT 30 | |
| 135 | - dataset.limit(30, 10) # LIMIT 30 OFFSET 10 | |
| 171 | +``` | |
| 172 | +dataset.limit(30) # LIMIT 30 | |
| 173 | +dataset.limit(30, 10) # LIMIT 30 OFFSET 10 | |
| 174 | +``` | |
| 136 | 175 | |
| 137 | 176 | ### Joins |
| 138 | 177 | |
| 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 | |
| 178 | +``` | |
| 179 | +DB[:items].left_outer_join(:categories, :id => :category_id).sql | |
| 180 | +# SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id | |
| 181 | + | |
| 182 | +DB[:items].join(:categories, :id => :category_id).join(:groups, :id => :items__group_id) | |
| 183 | +# SELECT * FROM items INNER JOIN categories ON categories.id = items.category_id INNER JOIN groups ON groups.id = items.group_id | |
| 184 | +``` | |
| 141 | 185 | |
| 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 | |
| 144 | - | |
| 145 | 186 | ### Aggregate functions methods |
| 146 | 187 | |
| 147 | - dataset.count #=> record count | |
| 148 | - dataset.max(:price) | |
| 149 | - dataset.min(:price) | |
| 150 | - dataset.avg(:price) | |
| 151 | - dataset.sum(:stock) | |
| 188 | +``` | |
| 189 | +dataset.count #=> record count | |
| 190 | +dataset.max(:price) | |
| 191 | +dataset.min(:price) | |
| 192 | +dataset.avg(:price) | |
| 193 | +dataset.sum(:stock) | |
| 152 | 194 | |
| 153 | - dataset.group_and_count(:category) | |
| 154 | - dataset.group(:category).select(:category, :AVG.sql_function(:price)) | |
| 195 | +dataset.group_and_count(:category) | |
| 196 | +dataset.group(:category).select(:category, :AVG.sql_function(:price)) | |
| 197 | +``` | |
| 155 | 198 | |
| 156 | 199 | ### SQL Functions / Literals |
| 157 | 200 | |
| 158 | - dataset.update(:updated_at => :NOW.sql_function) | |
| 159 | - dataset.update(:updated_at => 'NOW()'.lit) | |
| 201 | +``` | |
| 202 | +dataset.update(:updated_at => :NOW.sql_function) | |
| 203 | +dataset.update(:updated_at => 'NOW()'.lit) | |
| 160 | 204 | |
| 161 | - dataset.update(:updated_at => "DateValue('1/1/2001')".lit) | |
| 162 | - dataset.update(:updated_at => :DateValue.sql_function('1/1/2001')) | |
| 205 | +dataset.update(:updated_at => "DateValue('1/1/2001')".lit) | |
| 206 | +dataset.update(:updated_at => :DateValue.sql_function('1/1/2001')) | |
| 207 | +``` | |
| 163 | 208 | |
| 164 | 209 | ### Schema Manipulation |
| 165 | 210 | |
| 166 | - DB.create_table :items do | |
| 167 | - primary_key :id | |
| 168 | - String :name, :unique => true, :null => false | |
| 169 | - TrueClass :active, :default => true | |
| 170 | - foreign_key :category_id, :categories | |
| 171 | - DateTime :created_at | |
| 172 | - | |
| 173 | - index :created_at | |
| 174 | - end | |
| 211 | +``` | |
| 212 | +DB.create_table :items do | |
| 213 | + primary_key :id | |
| 214 | + String :name, :unique => true, :null => false | |
| 215 | + TrueClass :active, :default => true | |
| 216 | + foreign_key :category_id, :categories | |
| 217 | + DateTime :created_at | |
| 175 | 218 | |
| 176 | - DB.drop_table :items | |
| 219 | + index :created_at | |
| 220 | +end | |
| 177 | 221 | |
| 178 | - DB.create_table :test do | |
| 179 | - String :zipcode | |
| 180 | - enum :system, :elements => ['mac', 'linux', 'windows'] | |
| 181 | - end | |
| 222 | +DB.drop_table :items | |
| 223 | + | |
| 224 | +DB.create_table :test do | |
| 225 | + String :zipcode | |
| 226 | + enum :system, :elements => ['mac', 'linux', 'windows'] | |
| 227 | +end | |
| 228 | +``` | |
| 182 | 229 | |
| 183 | 230 | ### Aliasing |
| 184 | 231 | |
| 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 | |
| 232 | +``` | |
| 233 | +DB[:items].select(:name.as(:item_name)) | |
| 234 | +DB[:items].select(:name___item_name) | |
| 235 | +DB[:items___items_table].select(:items_table__name___item_name) | |
| 236 | +# SELECT items_table.name AS item_name FROM items AS items_table | |
| 237 | +``` | |
| 189 | 238 | |
| 190 | 239 | ### Transactions |
| 191 | 240 | |
| 192 | - DB.transaction do | |
| 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 | |
| 241 | +``` | |
| 242 | +DB.transaction do | |
| 243 | + dataset.insert(:first_name => 'Inigo', :last_name => 'Montoya') | |
| 244 | + dataset.insert(:first_name => 'Farm', :last_name => 'Boy') | |
| 245 | +end # Either both are inserted or neither are inserted | |
| 246 | +``` | |
| 196 | 247 | |
| 197 | 248 | Database#transaction is re-entrant: |
| 198 | 249 | |
| 199 | - DB.transaction do # BEGIN issued only here | |
| 200 | - DB.transaction | |
| 201 | - dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} | |
| 202 | - end | |
| 203 | - end # COMMIT issued only here | |
| 250 | +``` | |
| 251 | +DB.transaction do # BEGIN issued only here | |
| 252 | + DB.transaction | |
| 253 | + dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} | |
| 254 | + end | |
| 255 | +end # COMMIT issued only here | |
| 256 | +``` | |
| 204 | 257 | |
| 205 | 258 | Transactions are aborted if an error is raised: |
| 206 | 259 | |
| 207 | - DB.transaction do | |
| 208 | - raise "some error occurred" | |
| 209 | - end # ROLLBACK issued and the error is re-raised | |
| 260 | +``` | |
| 261 | +DB.transaction do | |
| 262 | + raise "some error occurred" | |
| 263 | +end # ROLLBACK issued and the error is re-raised | |
| 264 | +``` | |
| 210 | 265 | |
| 211 | 266 | Transactions can also be aborted by raising Sequel::Rollback: |
| 212 | 267 | |
| 213 | - DB.transaction do | |
| 214 | - raise(Sequel::Rollback) if something_bad_happened | |
| 215 | - end # ROLLBACK issued and no error raised | |
| 268 | +``` | |
| 269 | +DB.transaction do | |
| 270 | + raise(Sequel::Rollback) if something_bad_happened | |
| 271 | +end # ROLLBACK issued and no error raised | |
| 272 | +``` | |
| 216 | 273 | |
| 217 | 274 | Savepoints can be used if the database supports it: |
| 218 | 275 | |
| 219 | - DB.transaction do | |
| 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 | |
| 224 | - end | |
| 225 | - dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted | |
| 226 | - end | |
| 276 | +``` | |
| 277 | +DB.transaction do | |
| 278 | + dataset << {:first_name => 'Farm', :last_name => 'Boy'} # Inserted | |
| 279 | + DB.transaction(:savepoint=>true) # This savepoint is rolled back | |
| 280 | + dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} # Not inserted | |
| 281 | + raise(Sequel::Rollback) if something_bad_happened | |
| 282 | + end | |
| 283 | + dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted | |
| 284 | +end | |
| 285 | +``` | |
| 227 | 286 | |
| 228 | 287 | ### Miscellaneous: |
| 229 | 288 | |
| 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, ...}], ...] | |
| 289 | +``` | |
| 290 | +dataset.sql # "SELECT * FROM items" | |
| 291 | +dataset.delete_sql # "DELETE FROM items" | |
| 292 | +dataset.where(:name => 'sequel').exists # "EXISTS ( SELECT * FROM items WHERE name = 'sequel' )" | |
| 293 | +dataset.columns #=> array of columns in the result set, does a SELECT | |
| 294 | +DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...] | |
| 295 | +``` | |
| 235 | 296 | |
| 236 | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 297 | +--- | |
| 237 | 298 | |
| 238 | 299 | ### Documents |
| 239 | 300 | |
| 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 | |
| 301 | +``` | |
| 302 | +http://sequel.rubyforge.org/rdoc/files/doc/association_basics_rdoc.html | |
| 303 | +http://sequel.rubyforge.org/rdoc/classes/Sequel/Schema/Generator.html | |
| 304 | +http://sequel.rubyforge.org/rdoc/files/doc/validations_rdoc.html | |
| 305 | +http://sequel.rubyforge.org/rdoc/classes/Sequel/Model.html | |
| 306 | +``` | |
| 244 | 307 | |
| 245 | 308 | ### Alter table |
| 246 | 309 | |
| 247 | - database.alter_table :deals do | |
| 248 | - add_column :name, String | |
| 249 | - drop_column :column_name | |
| 250 | - rename_column :from, :to | |
| 310 | +``` | |
| 311 | +database.alter_table :deals do | |
| 312 | + add_column :name, String | |
| 313 | + drop_column :column_name | |
| 314 | + rename_column :from, :to | |
| 251 | 315 | |
| 252 | - add_constraint :valid_name, :name.like('A%') | |
| 253 | - drop_constraint :constraint | |
| 316 | + add_constraint :valid_name, :name.like('A%') | |
| 317 | + drop_constraint :constraint | |
| 254 | 318 | |
| 255 | - add_full_text_index :body | |
| 256 | - add_spacial_index [columns] | |
| 319 | + add_full_text_index :body | |
| 320 | + add_spacial_index [columns] | |
| 257 | 321 | |
| 258 | - add_index :price | |
| 259 | - drop_index :index | |
| 322 | + add_index :price | |
| 323 | + drop_index :index | |
| 260 | 324 | |
| 261 | - add_foreign_key :artist_id, :table | |
| 262 | - add_primary_key :id | |
| 263 | - add_unique_constraint [columns] | |
| 264 | - set_column_allow_null :foo, false | |
| 265 | - set_column_default :title, '' | |
| 325 | + add_foreign_key :artist_id, :table | |
| 326 | + add_primary_key :id | |
| 327 | + add_unique_constraint [columns] | |
| 328 | + set_column_allow_null :foo, false | |
| 329 | + set_column_default :title, '' | |
| 266 | 330 | |
| 267 | - set_column_type :price, 'char(10)' | |
| 268 | - end | |
| 331 | + set_column_type :price, 'char(10)' | |
| 332 | +end | |
| 333 | +``` | |
| 269 | 334 | |
| 270 | 335 | ### Model associations |
| 271 | 336 | |
| 272 | - class Deal < Sequel::Model | |
| 337 | +``` | |
| 338 | +class Deal < Sequel::Model | |
| 273 | 339 | |
| 274 | - # Us (left) <=> Them (right) | |
| 275 | - many_to_many :images, | |
| 276 | - left_id: :deal_id, | |
| 277 | - right_id: :image_id, | |
| 278 | - join_table: :image_links | |
| 340 | + # Us (left) <=> Them (right) | |
| 341 | + many_to_many :images, | |
| 342 | + left_key: :deal_id, | |
| 343 | + right_key: :image_id, | |
| 344 | + join_table: :image_links | |
| 279 | 345 | |
| 280 | - one_to_many :files, | |
| 281 | - key: :deal_id, | |
| 282 | - class: :DataFile, | |
| 346 | + one_to_many :files, | |
| 347 | + key: :deal_id, | |
| 348 | + class: :DataFile, | |
| 283 | 349 | |
| 284 | - many_to_one :parent, class: self | |
| 285 | - one_to_many :children, key: :parent_id, class: self | |
| 350 | + many_to_one :parent, class: self | |
| 351 | + one_to_many :children, key: :parent_id, class: self | |
| 286 | 352 | |
| 287 | - one_to_many :gold_albums, class: :Album do |ds| | |
| 288 | - ds.filter { copies_sold > 50000 } | |
| 289 | - end | |
| 353 | + one_to_many :gold_albums, class: :Album do |ds| | |
| 354 | + ds.filter { copies_sold > 50000 } | |
| 355 | + end | |
| 356 | +``` | |
| 290 | 357 | |
| 291 | 358 | Provided by many_to_many |
| 292 | 359 | |
| 293 | - Deal[1].images | |
| 294 | - Deal[1].add_image | |
| 295 | - Deal[1].remove_image | |
| 296 | - Deal[1].remove_all_images | |
| 360 | +``` | |
| 361 | +Deal[1].images | |
| 362 | +Deal[1].add_image | |
| 363 | +Deal[1].remove_image | |
| 364 | +Deal[1].remove_all_images | |
| 365 | +``` | |
| 297 | 366 | |
| 298 | 367 | ### Validations |
| 299 | 368 | |
| 300 | - def validate | |
| 301 | - super | |
| 302 | - errors.add(:name, 'cannot be empty') if !name || name.empty? | |
| 303 | - | |
| 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] | |
| 311 | - | |
| 312 | - validates_integer :rating if new? | |
| 313 | - | |
| 314 | - # options: :message =>, :allow_nil =>, :allow_blank =>, | |
| 315 | - # :allow_missing =>, | |
| 316 | - | |
| 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 | |
| 321 | - | |
| 322 | - # Setter override | |
| 323 | - def filename=(name) | |
| 324 | - @values[:filename] = name | |
| 325 | - end | |
| 326 | - end | |
| 369 | +``` | |
| 370 | +def validate | |
| 371 | + super | |
| 372 | + errors.add(:name, 'cannot be empty') if !name || name.empty? | |
| 373 | + | |
| 374 | + validates_presence [:title, :site] | |
| 375 | + validates_unique :name | |
| 376 | + validates_format /\Ahttps?:\/\//, :website, :message=>'is not a valid URL' | |
| 377 | + validates_includes %w(a b c), :type | |
| 378 | + validates_integer :rating | |
| 379 | + validates_numeric :number | |
| 380 | + validates_type String, [:title, :description] | |
| 381 | + | |
| 382 | + validates_integer :rating if new? | |
| 383 | + | |
| 384 | + # options: :message =>, :allow_nil =>, :allow_blank =>, | |
| 385 | + # :allow_missing =>, | |
| 386 | + | |
| 387 | + validates_exact_length 17, :isbn | |
| 388 | + validates_min_length 3, :name | |
| 389 | + validates_max_length 100, :name | |
| 390 | + validates_length_range 3..100, :name | |
| 391 | + | |
| 392 | + # Setter override | |
| 393 | + def filename=(name) | |
| 394 | + @values[:filename] = name | |
| 327 | 395 | end |
| 396 | + end | |
| 397 | +end | |
| 328 | 398 | |
| 329 | - deal.errors | |
| 399 | +deal.errors | |
| 400 | +``` | |
| 330 | 401 | |
| 331 | 402 | ### Model stuff |
| 332 | 403 | |
| 333 | - deal = Deal[1] | |
| 334 | - deal.changed_columns | |
| 335 | - deal.destroy # Calls hooks | |
| 336 | - deal.delete # No hooks | |
| 337 | - deal.exists? | |
| 338 | - deal.new? | |
| 339 | - deal.hash # Only uniques | |
| 340 | - deal.keys #=> [:id, :name] | |
| 341 | - deal.modified! | |
| 342 | - deal.modified? | |
| 343 | - | |
| 344 | - deal.lock! | |
| 404 | +``` | |
| 405 | +deal = Deal[1] | |
| 406 | +deal.changed_columns | |
| 407 | +deal.destroy # Calls hooks | |
| 408 | +deal.delete # No hooks | |
| 409 | +deal.exists? | |
| 410 | +deal.new? | |
| 411 | +deal.hash # Only uniques | |
| 412 | +deal.keys #=> [:id, :name] | |
| 413 | +deal.modified! | |
| 414 | +deal.modified? | |
| 415 | + | |
| 416 | +deal.lock! | |
| 417 | +``` | |
| 345 | 418 | |
| 346 | 419 | ### Callbacks |
| 347 | 420 | |
| 348 | - before_create | |
| 349 | - after_create | |
| 421 | +``` | |
| 422 | +before_create | |
| 423 | +after_create | |
| 350 | 424 | |
| 351 | - before_validation | |
| 352 | - after_validation | |
| 353 | - before_save | |
| 354 | - before_update | |
| 355 | - UPDATE QUERY | |
| 356 | - after_update | |
| 357 | - after_save | |
| 425 | +before_validation | |
| 426 | +after_validation | |
| 427 | +before_save | |
| 428 | +before_update | |
| 429 | +UPDATE QUERY | |
| 430 | +after_update | |
| 431 | +after_save | |
| 358 | 432 | |
| 359 | - before_destroy | |
| 360 | - DELETE QUERY | |
| 361 | - after_destroy | |
| 433 | +before_destroy | |
| 434 | +DELETE QUERY | |
| 435 | +after_destroy | |
| 436 | +``` | |
| 362 | 437 | |
| 363 | 438 | ### Schema |
| 364 | 439 | |
| 365 | - class Deal < Sequel::Model | |
| 366 | - set_schema do | |
| 367 | - primary_key :id | |
| 368 | - primary_key [:id, :title] | |
| 369 | - String :name, primary_key: true | |
| 370 | - | |
| 371 | - String :title | |
| 372 | - Numeric :price | |
| 373 | - DateTime :expires | |
| 374 | - | |
| 375 | - unique :whatever | |
| 376 | - check(:price) { num > 0 } | |
| 377 | - | |
| 378 | - foreign_key :artist_id | |
| 379 | - String :artist_name, key: :id | |
| 380 | - | |
| 381 | - index :title | |
| 382 | - index [:artist_id, :name] | |
| 383 | - full_text_index :title | |
| 384 | - | |
| 385 | - # String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal, | |
| 386 | - # Date, DateTime, Time, File, TrueClass, FalseClass | |
| 387 | - end | |
| 388 | - end | |
| 440 | +``` | |
| 441 | +class Deal < Sequel::Model | |
| 442 | + set_schema do | |
| 443 | + primary_key :id | |
| 444 | + primary_key [:id, :title] | |
| 445 | + String :name, primary_key: true | |
| 446 | + | |
| 447 | + String :title | |
| 448 | + Numeric :price | |
| 449 | + DateTime :expires | |
| 450 | + | |
| 451 | + unique :whatever | |
| 452 | + check(:price) { num > 0 } | |
| 453 | + | |
| 454 | + foreign_key :artist_id | |
| 455 | + String :artist_name, key: :id | |
| 456 | + | |
| 457 | + index :title | |
| 458 | + index [:artist_id, :name] | |
| 459 | + full_text_index :title | |
| 460 | + | |
| 461 | + # String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal, | |
| 462 | + # Date, DateTime, Time, File, TrueClass, FalseClass | |
| 463 | + end | |
| 464 | +end | |
| 465 | +``` | |
| 389 | 466 | |
| 390 | 467 | ### Unrestrict primary key |
| 391 | 468 | |
| 392 | - Category.create id: 'travel' # error | |
| 393 | - Category.unrestrict_primary_key | |
| 394 | - Category.create id: 'travel' # ok | |
| 469 | +``` | |
| 470 | +Category.create id: 'travel' # error | |
| 471 | +Category.unrestrict_primary_key | |
| 472 | +Category.create id: 'travel' # ok | |
| 473 | +``` |
| @@ -244,7 +244,7 @@ type Point = { x: number; y: number }; | ||
| 244 | 244 | type P = keyof Point; // x | y |
| 245 | 245 | ``` |
| 246 | 246 | |
| 247 | -## Conditinal Types | |
| 247 | +## Conditional Types | |
| 248 | 248 | |
| 249 | 249 | ```ts |
| 250 | 250 | // SomeType extends OtherType ? TrueType : FalseType; |