sharoa
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 【第28週】パRails輪読会🚂 \(2024\-02\-26\~ 2024\-03\-01\) ###### tags: `パRails🚂` - [開催概要](https://hackmd.io/4A_8ahJtQNi5hz713N5Y7w?view) - [パRails輪読会 ノートまとめ](https://hackmd.io/jsgA8Pf_RVioOgSL_VOR4g) - サンプルコード・正誤表:[サポートページ:パーフェクトRuby on Rails【増補改訂版】:|技術評論社](https://gihyo.jp/book/2020/978-4-297-11462-6/support) - **サンプルコードをダウンロードしただけでは、すぐに`rails s`できない可能性が高い**です。 以下のページに環境構築の手順をまとめたので、ドライバーをやってくださるという方は、こちらに沿って環境構築をお願いします🙏 ⏩ [環境構築の手順 2022年版](https://hackmd.io/y7qb2BRMT2Wd4tAtKYObcQ) ⏩ [環境構築の手順 2023年版](https://hackmd.io/3_lnn8_QRD6wEjwFbWcBzQ) 💁‍♀️ [更新の手順 2章編](https://hackmd.io/pZAmF2EjSmyQ38lUtJ_CqA) ## 目次 [TOC] ------ ## 2024\-02\-26(月) ### ファシリ @sadanora ### ドライバー @sharoa ### 読んだところ 10-4 「環境によって可変する設定値や秘匿情報の管理」から 10-4-3 「クラウドサービスの活用」まで。 PR: ### 次回 10-5 「ログ出力」から。🚂 ### 学んだこと・感想 - @sharoa - Dockerって本当に難しい。けど、将来的に必ず出会う子ではあるので、ざっくりでも覚えておけたらな、と思う。 - 単純な環境変数化には限界がある。ので、クラウドサービスを活用する。 - このあたりのお話も難しいな〜という感じで、うまく表現できないのですが、とにかく Dockerに触れる機会ができたらパRailsを思い出す!! - @ayu-0505 - 環境変数(起動時に可変してしまう設定値を注入する)を利用して環境ごとの差異をまとめ上げる - そのほかの秘匿情報はcredentials.yml.encを利用する。こちらは暗号化されているのでマスターキーを環境変数として実行時に渡せば復号化してくれる。 - デプロイ先のクラウドサービス(AWS,GCPなど)で権限管理システムが提供されていることが多いので、そちらを利用する。 - @sadanora - Dockerむずかしい(N回目) - 環境変数での管理にも限界があって、いい感じに管理するためにはクラウドサービスの仕組みを利用したり工夫が必要 - 秘匿情報の管理とかは、GitHub ActionsでCI設定するときにやったことと同じような話かもしれないと思いました。 - @motohiro-mm - 環境変数をDockerのコンテナイメージに含めることで、秘匿情報を個々で管理する必要性はなくなる - が、その環境変数に指定する値を誰がどこで管理するのかという問題は生じる - そのため(権限管理のため)のクラウドサービスがある、そうすると管理しやすくなる - KeyManagementService(KMS) - @sugiwe - VSCode上で`⌘ j`は、ターミナルを開くショートカット - `IAM`は`アイアム`と読む - 権限管理はAWSなどのプラットフォーム側に任せられるサービスがある - 今日もフムフム過ぎた(=よくわからなかった…) - というか書籍のこれ以降はほぼフムフムな内容なのでは…頭の中にインデックスを作る意識を持って食らいつきたいと思います。 - @moegi29 - コンテナは完結したファイルシステムの状態でパッケージングされその状態のまま起動することがメリット。環境変数をつかってコンテナ起動時に可変する設定値を注入することができる - docker-compose.ymlを記述することで簡単に読みこむことが出来る - dockerを使う場合、-eオプションで環境変数を引き渡す - IAM:ユーザー権限管理するサービスのこと。 - S3:ファイルを保存しておくサービス。 - KMS:アプリケーションと AWS のサービス全体で暗号キーを作成するサービス ------ ## 2024\-02\-27(火) ### ファシリ @shodan ### ドライバー @ayu-0505 ### 読んだところ 10-6 HTTPサーバとの通信 から 10-6-2 コンテナ内部のネットワーク設定 まで PR: ### 次回 10-6 457p コラム「真の本番環境へ」から。🚂 ### 学んだこと・感想 - @sharoa - コンテナ上のファイルシステムに新しくデータを保存しないことは、コンテナを利用してWebアプリケーションを運用する際に重要なこと。 - コンテナ運用の鉄則らしいです。 - Dockerには、`logging driver`というログをどこに出力するかをコントロールできる機能が組み込まれている - エラーが発生した時に情報を手早く収集するためには、外部の専門サービスを利用するのが良いとのこと。(セルフホストで運用するにはかなりの手間がかかるらしい。) - レイテンシ(latency)⇨データ転送における指標のひとつで、転送要求を出してから実際にデータが送られてくるまでに生じる通信の遅延時間のこと - `docker-compose`を利用すれば、簡単に複数のコンテナ間での相互通信環境を構築することができる。 - 10章もあとコラムのみ!! - @sugiwe - 色々な読み方を知った - fluentd(フルエントディー) - syslog(シスログ) - occurence(オカレンス) - ログ出力はDockerの中に溜まっていくと見に行くのが大変なので、RollbarやSentryといったエラートラッキングのサービスを活用すると良い(Slackに通知とかもしてくれる。想像すると、確かにターミナルに出すとかログファイルを見に行く等よりも圧倒的に便利そう) - PumaとUnicornはどちらもアプリケーションサーバ(動物縛りなのだろうか) - Railsは直接HTTPを受け取って処理できるのだけど、unicornの特性によりI/O待ちによる無駄な時間が発生してしまうので、それを防ぐために、内部ネットワークで高速通信できて大量のコネクションをバッファして置ける場所としてHTTPサーバが必要になる。 - @shodan - Dockerのコンテナイメージはdisposableであることが大事。 - ログファイルはデフォルトではDocker内のアプリのディレクトリ内に溜まっていくので、外部に出力させることが大事。 - Railsのログ出力先は`config/environments`以下の各環境の設定ファイルから変更できる。 - エラー内容を検知して保持・通知してくれるWebサービスもある。 - Railsアプリケーションを本番運用する際は、ほとんどのケースでnginxなどのHTTPサーバを前段に置く。 - pumaやunicornなどのアプリケーションサーバでも外部のクライアントからのHTTP通信を処理できるけれど、それだとレイテンシが起きたりするから。 - @moegi29 - Dockerのコンテナ上のファイルシステムには新しくデータを保存しないことが重要でコンテナ運用していくうえでの鉄則。 - Dockerにはログをどこに出力するかコントロールできるlogging driverという機能がある。 - コンテナでRailsを運用したい場合はログを標準出力に出すよう設定変更しておく - 多くのエラートラッキングサービスはGitHubやSlack等との連携機能があり自動的にIssueを立てたりすぐ通知してくれるのは便利、だけど現場のひとは大変そう。。 - @sadanora - コンテナ上のファイルシステムには新しくデータを保存しないことが鉄則らしい - エラーも標準出力に出力するようにしたりする - railsのviewファイルとかは普通に追加したり編集したりすると思うけど、そういったものとは別の話?dockerわからないのでいまひとつピンときてない。 - @ayu-0505 - コンテナはイメージをもとに起動したら常に同様な環境で起動できることが利点。(使い捨てできること) - そのため、新しいデータの保存はコンテナ外であることが望ましい(ActiveStrageの画像等は外部ストレージを使う) - Dockerにはログ出力先をコントロールする機能がある。 - Railsのログ出力のデフォルトはプロジェクトルートのlogディレクトリなので、標準出力に変更しておく - エラー検知システムをセルフホストで運用するのは大変なので、外部サービスを利用するとよい。(例:Rollbar) - アプリケーションサーバの負担を減らすために、HTTPサーバを利用するが、Dockerではコンテナ間の相互通信環境を構築することができる。 ------ ## 2024\-02\-28(水) ### ファシリ @moegi29 ### ドライバー @ayu-0505 ### 読んだところ 10-6 457p コラム「真の本番環境へ」から 11-2-1 「エンティティと値オブジェクト」まで。 PR: ### 次回 11-2-2 「いつ導入すべきか」から。🚂 ### 学んだこと・感想 - @sharoa - プロフェッショナルな現場でのコンテナ活用は当たり前ですけど、とっても難しそう。コラムのことは覚えておこうという感じです。 - 「`Skinny Controller, Fat Model`(コントローラは薄く、モデルを厚く)」というRailsアプリケーションを開発するときに基本となる考え方がある。 - アプリケーションの核となるロジックはモデルに実装すべきという設計原則のこと。(コントローラやビューではない。) - ドメインモデルとアクティブレコードの項というか11−1の節は全てにおいて大事そうな内容で、そして難しいな、と思いました。 - エンティティや値オブジェクトは今後の実装例などを読み進めていって理解ができたらいいな、と思っています。 - @sugiwe - Railsの基本の考え方の1つ「Skinny Controller, Fat Model」は重要。 - アプリが対象とする問題領域のことをドメインと呼ぶ。「エンジニアもドメイン知識を学ぶべきである」みたいな言説を見かけるけど、このドメインの意味であろうと思った。 - ドメインを分析して構成概念を抽出することをモデリングと呼び、その結果得られた概念のことをドメインモデルと呼ぶ。 - ドメインモデルに関連する属性と振る舞いを持ったオブジェクトとして定義される。その振る舞いのことをドメインロジック(ビジネスロジック)と呼ぶ。 - Railsのモデルは、ドメインモデルとドメインロジックを実装するレイヤーである。 - ドメインモデル単体ではアプリケーションにならない。別でデータベースがあり、データベースとデータのやり取りをすることで初めてアプリケーションになる。その、ドメインモデルからデータベースを操作するための仕組みがActiveRecord、ということになる、か?Railsアプリの全体感がふわ〜っと掴めたような掴めていないような…という気持ちになりました。 - RailsのActiveRecordとは別の広い概念としてアクティブレコードがある。 - 一意に識別されるオブジェクトのことをエンティティと呼ぶ。日本人を例にすると、マイナンバーという識別子を持つ日本人というエンティティ、ってこと? - @moegi29 - コラムでプロの現場でコンテナ活用するにはどんなものが活用されているのか紹介されていてKubertnetesとかcapistrano等知らないツールが知れた - 今日でてきたアクティブレコードはRailsのActiveRecordとは違う話だった。11章に入りDockerとは別の方向に難しい。まだ消化しきれてないので後程読み直しておきます - @motohiro-mm - 明日は最初から参加できそうなので、ついていけるように今日のところまで読んでおきます…! - @sadanora - RailsのActiveRecordのアーキテクチャパターンはアクティブレコードパターン - テーブルの1行と1つのインスタンスが対応し、ドメインロジックを記述するクラスを作成するパターン - アーキテクチャパターンという言葉を初めて聞いた。デザインパターンと何が違うのか気になる。 - エンティティ - 識別子をもっている。一意に識別できる。 - 値オブジェクト - 所持するインスタンスが異なっても同じ値として扱ってもいいもの? - 生年月日とか。同じ生年月日のユーザーが二人いたとしても、日付としての値は同じもの。 - ayu-0505 - コンテナオーケストレーションツール =コンテナ管理ツール - ドメインモデル(アプリケーションが対象とする問題領域をモデル化したもの) - ドメインロジックはようするにモデルにおけるメソッド(モデルのふるまいかな?) - アクティブレコードはアーキテクチャパターンの一種(なのでRailsもモデルとクラスを関連づけるものをActiveRecordと呼ぶ) - 単一テーブル継承、要復習。 - 識別子によって一意に判断できるオブジェクトをエンティティ、内容が同じであれば同一とみなすオブジェクトを値オブジェクトと呼ぶ ----- ## 2024\-02\-29(木) ### ファシリ @motohiro ### ドライバー ### 読んだところ 11-2-2 「いつ導入すべきか」から 11-2-3 「実装例」まで。 PR: ### 次回 11-3 「サービスオブジェクト」から。🚂 ### 学んだこと・感想 - @sharoa - Railsには複数のモデルから扱われる特定の意味を持った値を実装するレイヤーが用意されていない。本の中の例でいうと、解決方法としては値オブジェクトを導入すると良いとのこと。 - 値オブジェクトには実装上の利点やモデリングの上での利点がある。 - 質の高いアプリケーションを開発するには、ドメインモデルを正確に抽出することはもちろん、抽出したドメインモデルを実装に反映させることも同じくらい重要とのこと。 - 値オブジェクトを導入することで、ドメインモデルをより正確に実装に反映できる。 - composed_ofは、モデルの属性を値オブジェクトとして表現するための仕組みのこと。 - `classify`⇨与えられたテーブル名に対応するクラス名を返す - 今日も難しい。 - @moegi29 - 値オブジェクトの実装例で出てきた`Delegate`はあるオブジェクト(一般的にはモデルオブジェクト)から別のオブジェクトへのメソッド呼び出しを委譲するために使用されるもの。 - テーブル名をクラス名にするclassifyするという - DBに紐づくモデルと紐づかないモデルがある。app/models/以下に何も継承しないクラスを作って良い - 今日も断片的にわかったようなわかってないような理解レベルなので読み直しておきます - @sadanora - 昨日参加したイベントで技術的負債の返済の中でドメインモデルを見直したという話がありました - 途中からでしたが、オブジェクト指向っぽい話で面白かったです - オブジェクト指向よくわからないので勉強したいと思いました - 継承、委譲、合成とか理解したい - 眼科でものもらい(霰粒腫)をとってきてすっきりしました。 - @shodan - DBと紐づかないクラスはbootcampにもあるはありますね。 - https://github.com/fjordllc/bootcamp/blob/main/app/models/redirection.rb - リダイレクト先を決めたりするメソッドを持っているクラス。 - `compose_of`という仕組みは、アクティブレコードに基づいたモデルのカラムと、アクティブレコードに基づかない値オブジェクトを紐づけることができる。 - Fatなコントローラ・モデルをなんとかしろ!と言われた時に、思い出せるようにしたい。 - ayu-0505 - 値オブジェクト利用は、複数のモデルで再利用されやすいロジックをまとめるための一手法 - 正確なドメインモデルを正確に実装に反映させる - アクティブレコードによるRDBへの紐付きがないクラス(テーブルを持たないクラス)による利点がある - composed_ofを使用すると、より少ない記述で値オブジェクトを活用できる。 - `self[:column]`で値を取得できる(NULL時に例外エラーを起こさずにNULLを取得できる) - @motohiro-mm - 複数のモデルで共通のロジック(メソッドとか)をもたせたいときに、値オブジェクトを使う - クラスとして作成して`delegate`をつかう(詳細は分かってない…) - 持たせたいクラスには`composed_of`で値オブジェクトのアクセサメソッドが簡単に定義できる - 値オブジェクト以外の方法は、前回でてきた単一テーブル継承 - @sugiwe - `.phone_number`と`[:phone_number]`の違い(該当するものがないとき、前者はno methodエラーになり、後者はnilが返る?) - `composed_of`、何が何やらさっぱりだったけどayuさんがおっしゃっていた`attr_reader`との比較でちょっとだけイメージが湧いたような気がします - 例えばUserモデル内にphone_numberがあり、Companyモデル内に同じくphone_numberがあって、それぞれに同じロジック(`mobile?`など)を作るのはDRYじゃないので、それをうまく整理するための仕組みの紹介、というのが今日学んだこと…? - 詳細はさっぱりわからなかったのですが概念的にはなるほど〜と思いました。 ----- ## 2024\-03\-01(金) ### ファシリ @ayu-0505 ### ドライバー ### 読んだところ 11-3 「サービスオブジェクト」から 11-3-4 「導入時のポイント」まで PR: ### 次回 ### 学んだこと・感想 - @sharoa - サービスオブジェクトとは、モデルに実装すると不自然なドメインロジックが出てくることがあり、こういったロジックを独立したオブジェクトとして定義したもののことを言う。 - サービスオブジェクトはモデルとは異なるので、自身の動作を変更するような状態を持たない。(注意ポイントらしい) - サービスオブジェクトを導入することに当たって特に注意すべきポイントとして、  - モデルに実装すべきロジックまで実装しない  - イベントの見落としがないかを確認する - がある。 - 472pの最後の文章の塊がすごく大事な気がするので覚えておきたい。 - @sadanora - 複数のモデルのオブジェクトを組み合わせて表現するようなロジックなどはモデルに実装すると不自然 - そのようなロジックを独立したオブジェクトとして定義したものをサービスオブジェクトという - ロジックではなくイベントとして扱うことが自然なら、サービスオブジェクトではなくイベントを表現するモデルとした方がいいケースもあり、一長一短がある - @shodan - Railsアプリが複雑になってくると、複数のモデル・オブジェクトが絡むドメインロジックをどのモデルのインスタンスメソッドに書くか、という問題が出てくるらしい。 - それに対するアプローチの一つが、「サービスオブジェクト」の導入。 - その複雑なロジックそのものを独立したクラス(とそのオブジェクト)として定義すること。 - 本の例で言えば「複数のBankAccountオブジェクトが絡んだ、銀行口座の送金サービス」。複雑。 - そのロジックが何かを「記録」するものであれば、DBと紐づくモデルのロジックとして定義した方がいいかも。 - その方がRailsのレールに乗れるから。 - 難しいですね……。 - @motohiro-mm - サービスオブジェクト:ドメインロジックを定義する、Databaseに保存する必要のないロジックをいれる - モデルとは異なり、自身の動作を変更するような状態をもたない、引数が同じなら同じ結果を得られる - モデルに実装すべきロジック、と、サービスオブジェクトに定義してよいロジック、の違いがよく分かっていない… - Databaseに残した方が良いイベントがあるときはモデルとして実装する - そのほうがコントローラもscaffoldで作ったものをそのまま使える - ただしテストがしづらい - モデルに実装すると不自然なドメインロジック、というのがたぶんピンときていない…(たぶん上記と同じことが分かっていない) - @sakanobu - 実装レベルでも難しいのにサービスオブジェクトなど概念レベルでも難しいので理解度がかなりフワフワしている - 11-1-2であった「データベースのテーブルをクラス、レコードをクラスのインスタンス、カラムをインスタンスの属性に対応させる」というのを忘れず、複数のオブジェクトを扱うロジックなのにモデルのインスタンスメソッドとして実装しようとしたら「これはマズい!」と気付けるようにしたい! - どうすれば実現できるかで手一杯なのに、どこにロジックを実装すべきかも考えるのは本当に難しいなと感じています - 「データベースのテーブルをクラス」として表すよくあるケースは `User.all` のような SQL で検索を書けるようなクラスメソッドのことを指すのかなと思いました - 都道府県ごとの手数料の違いというケースだと BankAccount モデル、Money モデル、BankAccountMoneyTransfer モデル、更には別のクラスを新設すべきか…などどこに実装すべきか、メッチャ悩みどころでスゴい良い疑問だと思いました! - 北海道、本州、沖縄などで大まかに分けるとしても更に別の複数のクラスが必要なので複雑な設計になりそう… - 人や物をテーブルとして表すのは直感的なので気付きそうですが、送金という実態のないものをテーブルとして表すことを見落とさないかは少し不安になりました - 「送金する」のような動詞を「送金」という動名詞として捉えるみたいな発想もテーブルやクラスの発見に必要なのかなと思いました - @ayu-0505 - サービスオブジェクトとは、モデルに実装すると不自然なドメインロジックを抽出してオブジェクト化したもの(複数のオブジェクト操作を必要とするなど) - モデルに実装すべきロジックまでサービスにしてしまわないように注意! - イベント(DBなどに記録が必要なものなど)の見落としがないか注意! - イベントをDBに紐づくクラスとして作る方法もある(イベントクラス)。それならばRailsの規約に沿うことが可能。 - イベントクラスにするか、サービスクラスにするかは一長一短あるので、適宜使い分ける。 -----

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully