{"id":31718222,"url":"https://github.com/24c02/slocks","last_synced_at":"2025-11-11T11:20:27.865Z","repository":{"id":318589014,"uuid":"1071927315","full_name":"24c02/slocks","owner":"24c02","description":"block kit on rails!","archived":false,"fork":false,"pushed_at":"2025-10-08T03:45:02.000Z","size":17,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-08T04:21:52.644Z","etag":null,"topics":["block-kit","rails","slack","slack-bot","slackbot"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/24c02.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-08T02:41:05.000Z","updated_at":"2025-10-08T03:45:06.000Z","dependencies_parsed_at":"2025-10-08T04:21:54.924Z","dependency_job_id":"9a00c6c0-69c8-4c5d-953c-a48daf7b3999","html_url":"https://github.com/24c02/slocks","commit_stats":null,"previous_names":["24c02/slocks"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/24c02/slocks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/24c02%2Fslocks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/24c02%2Fslocks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/24c02%2Fslocks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/24c02%2Fslocks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/24c02","download_url":"https://codeload.github.com/24c02/slocks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/24c02%2Fslocks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279016595,"owners_count":26085852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["block-kit","rails","slack","slack-bot","slackbot"],"created_at":"2025-10-09T02:33:21.897Z","updated_at":"2025-10-14T07:02:10.682Z","avatar_url":"https://github.com/24c02.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Slocks\n\n**Slocks** is a DSL \u0026 a Rails template handler for generating Slack [Block Kit](https://docs.slack.dev/block-kit/) surfaces.\n\nNo implied warranty. Solid chance it won't work.\n\n## Why Slocks?\nI've lost too many hours of my life to \n\\*write\\*→\\*⌘C\\*→\\*⌘-Tab to Block Kit Builder\\*→\\*⌘V\\*→\"dammit\"→\\*⌘-Tab\\*→\\*rewrite\\*→\\*repeat\\*.\n\nNo more.\n## Installation\n\nAdd to your Gemfile:\n\n```ruby\ngem 'slocks'\n```\n\nThen run:\n\n```bash\nbundle install\n```\n\n## Quick Start\n\nCreate a template with the **.slack_blocks** extension.\n\n```ruby\n# app/views/notifications/order_shipped.slack_blocks\n\nheader \"📦 Your order has shipped!\"\n\nsection \"*Order ##{@order.id}* is on its way\", markdown: true\n\ndivider\n\nsection \"Tracking:\",\n        fields: [\n          mrkdwn_text(\"*Carrier:* #{@order.carrier}\"),\n          mrkdwn_text(\"*Tracking:* `#{@order.tracking_number}`\")\n        ]\n\nactions [ button(\"Track Package\", \"track_#{@order.id}\", style: \"primary\", url: @order.tracking_url) ]\n```\n\nRender it in your controller or service:\n\n```ruby\nclass OrdersController \u003c ApplicationController\n  def mark\n    @user = current_user\n    \n    payload = render_to_string(\n      template: 'notifications/welcome',\n      formats: [:slack_message]\n    )\n    \n    slack_client.chat_postMessage(\n      channel: @user.slack_id,\n      **JSON.parse(payload, symbolize_names: true)\n    )\n  end\nend\n```\n\n## Blocks Reference\n\n### Layout Blocks\n\n#### Header\n```ruby\nheader \"🎉 Great news, Baltimore!\"\n```\n\n#### Section\n```ruby\n# simple text\nsection \"hello world\"\n\n# mrkdwn text\nsection \"to *bold*ly _italic_ize...\", markdown: true\n\n# fields\nsection \"details:\",\n  fields: [\n    mrkdwn_text(\"*computer:* over\"),\n    mrkdwn_text(\"*virus:* VERY yes.\")\n  ]\n\n# accessorizing a little (images, buttons, etc)\nsection \"click to continue\",\n  accessory: button(\"click me, i dare you\", \"explode_action\")\n```\n\n#### Divider\n```ruby\ndivider\n```\n\n#### Context\n```ruby\ncontext [\n  mrkdwn_text(\"Last updated: #{time_ago_in_words(@updated_at)} ago\"),\n  image_element(\"https://crouton.net/crouton.png\", \"icon\")\n]\n```\n\n#### Actions\n(some these won't do anything unless you use them somewhere expecting accessories! it'd do you a lot of good to read Slack's Block Kit docs before you try anything...)\n\n```ruby\nactions [\n  button(\"Approve\", \"approve\", style: \"primary\"),\n  button(\"Reject\", \"reject\", style: \"danger\"),\n  button(\"More Info\", \"info\")\n]\n```\n\n#### Image\n```ruby\nimage \"https://example.com/photo.jpg\", \"Photo description\",\n  title: \"Amazing Photo\"\n```\n\n#### Input\n```ruby\ninput \"Your name\",\n  plain_text_input(\"name_input\", placeholder: \"Enter your name\")\n\ninput \"Choose an option\",\n  select_menu(\"option_select\",\n    placeholder: \"Select one\",\n    options: [\n      option(\"Option 1\", \"opt1\"),\n      option(\"Option 2\", \"opt2\")\n    ]\n  )\n```\n\n#### File\n```ruby\nfile \"F123ABC456\", source: \"remote\"\n```\n\n#### Video\n```ruby\nvideo \"How to use Slocks\",\n  \"https://example.com/video\",\n  \"https://example.com/thumbnail.jpg\",\n  \"https://example.com/video.mp4\",\n  alt_text: \"Tutorial video\"\n```\n\n### Block Elements\n\n#### Buttons\n```ruby\nbutton(\"Click me\", \"action_id\")\nbutton(\"Primary\", \"action\", style: \"primary\")\nbutton(\"Danger\", \"delete\", style: \"danger\")\nbutton(\"Link\", \"link\", url: \"https://example.com\")\n```\n\n#### Select Menus\n```ruby\n# Static select\nselect_menu(\"action_id\",\n  placeholder: \"Choose one\",\n  options: [\n    option(\"Label 1\", \"value1\"),\n    option(\"Label 2\", \"value2\")\n  ]\n)\n\n# Multi-select\nmulti_select_menu(\"action_id\",\n  placeholder: \"Choose multiple\",\n  options: [...]\n)\n\n# User select\nusers_select(\"user_action\", placeholder: \"Select a user\")\n\n# Channel select\nchannels_select(\"channel_action\", placeholder: \"Select a channel\")\n\n# Conversation select\nconversations_select(\"convo_action\", placeholder: \"Select a conversation\")\n```\n\n#### Inputs\n```ruby\n# Text input\nplain_text_input(\"action_id\",\n  placeholder: \"Type here...\",\n  multiline: true\n)\n\n# Email input\nemail_input(\"email_action\", placeholder: \"your@email.com\")\n\n# URL input\nurl_input(\"url_action\", placeholder: \"https://...\")\n\n# Number input\nnumber_input(\"number_action\",\n  is_decimal_allowed: true,\n  min_value: 0,\n  max_value: 100\n)\n```\n\n#### Date \u0026 Time Pickers\n```ruby\ndate_picker(\"date_action\", placeholder: \"Select a date\")\ntime_picker(\"time_action\", placeholder: \"Select a time\")\ndatetime_picker(\"datetime_action\")\n```\n\n#### Other Elements\n```ruby\n# Checkboxes\ncheckboxes(\"check_action\", [\n  option(\"Option 1\", \"val1\"),\n  option(\"Option 2\", \"val2\")\n])\n\n# Radio buttons\nradio_buttons(\"radio_action\", [\n  option(\"Choice 1\", \"c1\"),\n  option(\"Choice 2\", \"c2\")\n])\n\n# Overflow menu\noverflow(\"overflow_action\", [\n  option(\"Edit\", \"edit\"),\n  option(\"Delete\", \"delete\")\n])\n\n# File input\nfile_input(\"file_action\", filetypes: [\"pdf\", \"doc\"], max_files: 5)\n```\n\n### Composition Objects\n\n#### Text Objects\n```ruby\nmrkdwn_text(\"*Bold* and _italic_\")\nplain_text(\"Plain text\", emoji: true)\n```\n\n#### Option \u0026 Option Groups\n```ruby\noption(\"Display text\", \"value\")\noption(\"With description\", \"val\", description: \"More details\")\n\noption_group(\"Group label\", [\n  option(\"Opt 1\", \"v1\"),\n  option(\"Opt 2\", \"v2\")\n])\n```\n\n#### Confirm Dialog\n```ruby\nconfirm_dialog(\n  title: \"Are you sure?\",\n  text: \"This action cannot be undone\",\n  confirm: \"Yes, delete it\",\n  deny: \"Cancel\",\n  style: \"danger\"\n)\n```\n\n## Modals\n\nCreate a modal template at `app/views/modals/feedback.slack_modal`:\n\n```ruby\ntitle \"Send Feedback\"\nsubmit \"Submit\"\nclose \"Cancel\"\ncallback \"feedback_modal\"\n\nsection \"How was your experience?\", markdown: true\n\ninput \"Rating\",\n  select_menu(\"rating\",\n    placeholder: \"Choose rating\",\n    options: [\n      option(\"⭐⭐⭐⭐⭐ Excellent\", \"5\"),\n      option(\"⭐⭐⭐⭐ Good\", \"4\"),\n      option(\"⭐⭐⭐ Okay\", \"3\"),\n      option(\"⭐⭐ Poor\", \"2\"),\n      option(\"⭐ Bad\", \"1\")\n    ]\n  )\n\ninput \"Comments\",\n  plain_text_input(\"comments\",\n    placeholder: \"Tell us more...\",\n    multiline: true\n  ),\n  optional: true\n```\n\nRender it:\n\n```ruby\nmodal_json = render_to_string(\n  template: 'modals/feedback',\n  formats: [:slack_modal]\n)\n\nslack_client.views_open(\n  trigger_id: params[:trigger_id],\n  view: JSON.parse(modal_json, symbolize_names: true)\n)\n```\n\n## Rails Helpers\n\nAll Rails helpers work in templates:\n\n```ruby\nsection \"Order summary:\",\n  fields: [\n    mrkdwn_text(\"*Items:* #{pluralize(@order.items.count, 'item')}\"),\n    mrkdwn_text(\"*Total:* #{number_to_currency(@order.total)}\"),\n    mrkdwn_text(\"*Ordered:* #{time_ago_in_words(@order.created_at)} ago\")\n  ]\n```\n\n## Partials\n\nRender partials just like ERB:\n\n```ruby\n# app/views/orders/_order.slack_blocks\nsection \"*Order ##{@order.id}*\",\n  fields: [\n    mrkdwn_text(\"*Status:* #{@order.status}\"),\n    mrkdwn_text(\"*Total:* #{number_to_currency(@order.total)}\")\n  ]\n\n# app/views/orders/summary.slack_blocks\nheader \"Your Orders\"\n\nrender @orders  # Renders _order.slack_blocks for each order\n```\n\n## Rich Text (Advanced)\n\nFor rich text blocks with complex formatting:\n\n```ruby\nrich_text do\n  section do\n    text \"Hello \", bold: true\n    text \"world!\", italic: true\n    emoji \"wave\"\n    link \"https://example.com\", text: \"Click here\"\n  end\n  \n  list(style: \"bullet\") do\n    text \"First item\"\n    text \"Second item\"\n  end\nend\n```\n\n## Contributing\n\nBug reports are welcome, but pull requests are much _much_ welcomer.\n\nThis repo lives on GitHub at https://github.com/24c02/slocks. Happy hacking!\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Credits\n\nbuilt with \u003c3️ by nora and various LLM-slop providers.\n\nheavily inspired by Akira Matsuda's fantastic [JB](https://github.com/amatsuda/jb/) gem.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F24c02%2Fslocks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F24c02%2Fslocks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F24c02%2Fslocks/lists"}