├── .env.development ├── .env.test ├── .erb_lint.yml ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── lint-and-test.yml ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc ├── .rspec ├── .standard.yml ├── .standard_rubocop_extensions.yml ├── CLAUDE.md ├── Dockerfile.dev ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Procfile ├── Procfile.dev ├── README.md ├── Rakefile ├── app.json ├── app ├── assets │ ├── builds │ │ └── .keep │ ├── images │ │ ├── avatar.png │ │ └── icons │ │ │ ├── add_fill.svg │ │ │ ├── alert_fill.svg │ │ │ ├── arrow_left_fill.svg │ │ │ ├── caret-left-regular.svg │ │ │ ├── caret-right-regular.svg │ │ │ ├── check_circle_fill.svg │ │ │ ├── check_fill.svg │ │ │ ├── close_fill.svg │ │ │ ├── edit_4_fill.svg │ │ │ ├── edit_4_line.svg │ │ │ ├── entrance_line.svg │ │ │ ├── external_link_line.svg │ │ │ ├── forbid_circle_line.svg │ │ │ ├── home_4_fill.svg │ │ │ ├── home_4_line.svg │ │ │ ├── logo.svg │ │ │ ├── more_1_fill.svg │ │ │ ├── notification_fill.svg │ │ │ ├── notification_line.svg │ │ │ ├── right_fill.svg │ │ │ ├── rocket_line.svg │ │ │ ├── search_fill.svg │ │ │ ├── search_line.svg │ │ │ ├── settings_5_line.svg │ │ │ ├── user_4_fill.svg │ │ │ └── user_4_line.svg │ └── stylesheets │ │ └── application.css ├── components │ ├── application_component.rb │ ├── blank_slate_component.html.erb │ ├── blank_slate_component.rb │ ├── buttons │ │ ├── check_suggested_profile_button_component.html.erb │ │ ├── check_suggested_profile_button_component.rb │ │ ├── follow_button_component.html.erb │ │ ├── follow_button_component.rb │ │ ├── follow_button_with_avatar_component.html.erb │ │ ├── follow_button_with_avatar_component.rb │ │ ├── stamp_button_component.html.erb │ │ └── stamp_button_component.rb │ ├── cards │ │ ├── detailed_post_card_component.html.erb │ │ ├── detailed_post_card_component.rb │ │ ├── form_errors_card_component.html.erb │ │ ├── form_errors_card_component.rb │ │ ├── link_card_component.html.erb │ │ ├── link_card_component.rb │ │ ├── notification_card │ │ │ ├── stamp_notification_component.html.erb │ │ │ └── stamp_notification_component.rb │ │ ├── notification_card_component.html.erb │ │ ├── notification_card_component.rb │ │ ├── post_card │ │ │ ├── body_component.html.erb │ │ │ ├── body_component.rb │ │ │ ├── footer_component.html.erb │ │ │ └── footer_component.rb │ │ ├── post_card_component.html.erb │ │ ├── post_card_component.rb │ │ ├── post_nav_link_card_component.html.erb │ │ ├── post_nav_link_card_component.rb │ │ ├── profile_card_component.html.erb │ │ └── profile_card_component.rb │ ├── containers │ │ ├── basic_container_component.html.erb │ │ └── basic_container_component.rb │ ├── dropdowns │ │ ├── post_options_dropdown_component.html.erb │ │ ├── post_options_dropdown_component.rb │ │ ├── profile_options_dropdown_component.html.erb │ │ └── profile_options_dropdown_component.rb │ ├── entry_headers │ │ ├── basic_entry_header_component.html.erb │ │ └── basic_entry_header_component.rb │ ├── footers │ │ ├── basic_footer_component.html.erb │ │ └── basic_footer_component.rb │ ├── forms │ │ ├── keyword_search_form_component.html.erb │ │ ├── keyword_search_form_component.rb │ │ ├── post_form_component.html.erb │ │ └── post_form_component.rb │ ├── icons │ │ ├── logo_icon_component.html.erb │ │ ├── logo_icon_component.rb │ │ ├── navbar_menu_icon_component.html.erb │ │ ├── navbar_menu_icon_component.rb │ │ ├── stamped_icon_component.rb │ │ └── unstamped_icon_component.rb │ ├── images │ │ ├── avatar_image_component.html.erb │ │ ├── avatar_image_component.rb │ │ ├── profile_avatar_image_component.html.erb │ │ └── profile_avatar_image_component.rb │ ├── layouts │ │ ├── basic_layout_component.html.erb │ │ ├── basic_layout_component.rb │ │ ├── simple_layout_component.html.erb │ │ └── simple_layout_component.rb │ ├── links │ │ ├── post_nav_link_component.html.erb │ │ └── post_nav_link_component.rb │ ├── lists │ │ ├── post_card_list_component.html.erb │ │ └── post_card_list_component.rb │ ├── modals │ │ ├── base │ │ │ ├── modal_box_component.html.erb │ │ │ ├── modal_box_component.rb │ │ │ ├── modal_component.html.erb │ │ │ └── modal_component.rb │ │ ├── post_form_modal_component.html.erb │ │ └── post_form_modal_component.rb │ ├── navbar_menu_component.html.erb │ ├── navbar_menu_component.rb │ ├── navbars │ │ ├── bottom_navbar_component.html.erb │ │ ├── bottom_navbar_component.rb │ │ ├── top_navbar_component.html.erb │ │ └── top_navbar_component.rb │ ├── paginations │ │ ├── pagination_component.erb │ │ └── pagination_component.rb │ ├── tabs │ │ ├── timeline_tabs_component.html.erb │ │ └── timeline_tabs_component.rb │ └── toasts │ │ ├── flash_toast_component.html.erb │ │ └── flash_toast_component.rb ├── controllers │ ├── accounts │ │ ├── create_controller.rb │ │ └── new_controller.rb │ ├── api │ │ └── v1 │ │ │ ├── README.md │ │ │ ├── application_controller.rb │ │ │ ├── follows │ │ │ ├── create_controller.rb │ │ │ └── destroy_controller.rb │ │ │ ├── internal │ │ │ ├── accounts │ │ │ │ └── create_controller.rb │ │ │ ├── application_controller.rb │ │ │ ├── email_confirmations │ │ │ │ ├── challenges │ │ │ │ │ └── create_controller.rb │ │ │ │ ├── create_controller.rb │ │ │ │ └── show_controller.rb │ │ │ ├── passwords │ │ │ │ └── update_controller.rb │ │ │ ├── posts │ │ │ │ └── show_controller.rb │ │ │ ├── profiles │ │ │ │ └── posts │ │ │ │ │ └── index_controller.rb │ │ │ └── sessions │ │ │ │ └── create_controller.rb │ │ │ ├── notifications │ │ │ └── index_controller.rb │ │ │ ├── posts │ │ │ ├── create_controller.rb │ │ │ ├── destroy_controller.rb │ │ │ └── show_controller.rb │ │ │ ├── profiles │ │ │ ├── me │ │ │ │ ├── show_controller.rb │ │ │ │ └── update_controller.rb │ │ │ └── posts │ │ │ │ └── index_controller.rb │ │ │ ├── stamps │ │ │ ├── create_controller.rb │ │ │ └── destroy_controller.rb │ │ │ ├── suggested_profiles │ │ │ ├── checks │ │ │ │ └── create_controller.rb │ │ │ └── index_controller.rb │ │ │ ├── timeline │ │ │ └── show_controller.rb │ │ │ └── users │ │ │ └── me │ │ │ ├── show_controller.rb │ │ │ └── update_controller.rb │ ├── application_controller.rb │ ├── checks │ │ └── create_controller.rb │ ├── communities │ │ └── show_controller.rb │ ├── controller_concerns │ │ ├── api │ │ │ ├── authenticatable.rb │ │ │ └── v1 │ │ │ │ └── form_errorable.rb │ │ ├── authenticatable.rb │ │ ├── authorizable.rb │ │ ├── email_confirmation_findable.rb │ │ └── localizable.rb │ ├── email_confirmations │ │ ├── create_controller.rb │ │ └── new_controller.rb │ ├── followees │ │ └── index_controller.rb │ ├── follows │ │ ├── create_controller.rb │ │ └── destroy_controller.rb │ ├── home │ │ └── show_controller.rb │ ├── links │ │ ├── create_controller.rb │ │ └── new_controller.rb │ ├── manifests │ │ └── show_controller.rb │ ├── notifications │ │ └── index_controller.rb │ ├── password_resets │ │ ├── create_controller.rb │ │ └── new_controller.rb │ ├── passwords │ │ ├── edit_controller.rb │ │ └── update_controller.rb │ ├── posts │ │ ├── create_controller.rb │ │ ├── destroy_controller.rb │ │ ├── new_controller.rb │ │ └── show_controller.rb │ ├── privacies │ │ └── show_controller.rb │ ├── profiles │ │ ├── atom │ │ │ └── show_controller.rb │ │ └── show_controller.rb │ ├── public │ │ └── show_controller.rb │ ├── search │ │ ├── profiles │ │ │ └── index_controller.rb │ │ └── show_controller.rb │ ├── sessions │ │ ├── create_controller.rb │ │ ├── destroy_controller.rb │ │ └── new_controller.rb │ ├── settings │ │ ├── emails │ │ │ ├── show_controller.rb │ │ │ └── update_controller.rb │ │ ├── index_controller.rb │ │ ├── profiles │ │ │ ├── show_controller.rb │ │ │ └── update_controller.rb │ │ └── users │ │ │ ├── show_controller.rb │ │ │ └── update_controller.rb │ ├── sign_up │ │ ├── create_controller.rb │ │ └── new_controller.rb │ ├── stamps │ │ ├── create_controller.rb │ │ └── destroy_controller.rb │ ├── terms │ │ └── show_controller.rb │ └── welcome │ │ └── show_controller.rb ├── forms │ ├── account_form.rb │ ├── application_form.rb │ ├── delete_post_form.rb │ ├── discard_post_form.rb │ ├── email_confirmation_challenge_form.rb │ ├── email_confirmation_form.rb │ ├── email_update_form.rb │ ├── follow_form.rb │ ├── form_concerns │ │ └── password_validatable.rb │ ├── keyword_search_form.rb │ ├── link_data_fetcher_form.rb │ ├── link_form.rb │ ├── password_reset_form.rb │ ├── post_form.rb │ ├── profile_form.rb │ ├── session_form.rb │ ├── stamp_form.rb │ ├── suggested_follow_form.rb │ └── user_form.rb ├── helpers │ ├── application_helper.rb │ ├── flash_toast_helper.rb │ ├── language_helper.rb │ ├── profiles_helper.rb │ ├── text_helper.rb │ └── time_helper.rb ├── javascript │ ├── application.ts │ ├── controllers │ │ ├── autosize-controller.ts │ │ ├── character-counter-controller.ts │ │ ├── clipboard-controller.ts │ │ ├── dropdown-controller.ts │ │ ├── flash-toast-controller.ts │ │ ├── flash-toast-dispatch-controller.ts │ │ ├── link-card-form-controller.ts │ │ └── modal-controller.ts │ └── utils │ │ ├── event-dispatcher.ts │ │ └── fetcher.ts ├── jobs │ ├── add_followed_profile_posts_to_timeline_job.rb │ ├── add_post_to_timeline_job.rb │ ├── application_job.rb │ ├── create_suggested_follows_job.rb │ ├── delete_post_job.rb │ ├── fanout_post_job.rb │ └── remove_followed_profile_posts_from_timeline_job.rb ├── lib │ └── mewst │ │ └── test │ │ └── resource_helpers.rb ├── mailers │ ├── application_mailer.rb │ └── email_confirmation_mailer.rb ├── models │ ├── actor.rb │ ├── application_record.rb │ ├── email_confirmation.rb │ ├── email_confirmation_event.rb │ ├── follow.rb │ ├── follow_checker.rb │ ├── gravatar.rb │ ├── home_timeline_post.rb │ ├── link.rb │ ├── link_data_fetcher.rb │ ├── locale.rb │ ├── model_concerns │ │ └── notifiable.rb │ ├── notifiable_type.rb │ ├── notification.rb │ ├── oauth_access_token.rb │ ├── oauth_application.rb │ ├── page_info.rb │ ├── post.rb │ ├── post_link.rb │ ├── profile.rb │ ├── profile │ │ ├── home_timeline.rb │ │ └── postability.rb │ ├── profile_avatar_kind.rb │ ├── profile_owner_type.rb │ ├── session.rb │ ├── sign_up_stopper.rb │ ├── stamp.rb │ ├── stamp_checker.rb │ ├── suggested_follow.rb │ ├── url.rb │ ├── user.rb │ ├── user_profile.rb │ └── v1 │ │ └── response_error_code.rb ├── policies │ ├── application_policy.rb │ ├── post_policy.rb │ └── profile_policy.rb ├── resources │ ├── concerns │ │ └── resource_concerns │ │ │ └── error_responsable.rb │ └── v1 │ │ ├── account_resource.rb │ │ ├── application_resource.rb │ │ ├── email_confirmation_resource.rb │ │ ├── follow_form_error_resource.rb │ │ ├── form_error_resource.rb │ │ ├── notification_resource.rb │ │ ├── post_form_error_resource.rb │ │ ├── post_resource.rb │ │ ├── profile_resource.rb │ │ ├── response_error_resource.rb │ │ ├── session_form_error_resource.rb │ │ ├── stamp_form_error_resource.rb │ │ ├── stamp_notification_item_resource.rb │ │ ├── suggested_follow_form_error_resource.rb │ │ ├── unfollow_form_error_resource.rb │ │ ├── user_resource.rb │ │ └── via_resource.rb ├── serializers │ └── v1 │ │ ├── account_serializer.rb │ │ ├── application_serializer.rb │ │ ├── email_confirmation_serializer.rb │ │ ├── form_error_serializer.rb │ │ ├── notification_serializer.rb │ │ ├── oauth_access_token_serializer.rb │ │ ├── page_info_serializer.rb │ │ ├── post_serializer.rb │ │ ├── profile_serializer.rb │ │ ├── response_error_serializer.rb │ │ ├── stamp_notification_item_serializer.rb │ │ ├── user_serializer.rb │ │ └── via_serializer.rb ├── use_cases │ ├── add_followed_profile_posts_to_timeline_use_case.rb │ ├── application_use_case.rb │ ├── check_suggested_follow_use_case.rb │ ├── confirm_email_use_case.rb │ ├── create_account_use_case.rb │ ├── create_email_confirmation_use_case.rb │ ├── create_link_use_case.rb │ ├── create_post_use_case.rb │ ├── create_session_use_case.rb │ ├── create_stamp_use_case.rb │ ├── create_suggested_follows_use_case.rb │ ├── delete_post_use_case.rb │ ├── delete_stamp_use_case.rb │ ├── discard_post_use_case.rb │ ├── fanout_post_use_case.rb │ ├── follow_profile_use_case.rb │ ├── remove_followed_profile_posts_from_timeline_use_case.rb │ ├── unfollow_profile_use_case.rb │ ├── update_password_use_case.rb │ ├── update_profile_use_case.rb │ └── update_user_use_case.rb ├── validators │ ├── unreserved_atname_validator.rb │ └── url_validator.rb └── views │ ├── accounts │ └── new │ │ └── call.html.erb │ ├── checks │ └── create │ │ └── call.html.erb │ ├── email_confirmation_mailer │ ├── email_confirmation.en.html.erb │ └── email_confirmation.ja.html.erb │ ├── email_confirmations │ └── new │ │ └── call.html.erb │ ├── followees │ └── index │ │ └── call.html.erb │ ├── follows │ └── create │ │ └── call.html.erb │ ├── home │ └── show │ │ └── call.html.erb │ ├── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── links │ ├── create │ │ └── call.html.erb │ └── new │ │ └── call.html.erb │ ├── manifests │ └── show │ │ └── call.json.erb │ ├── notifications │ └── index │ │ └── call.html.erb │ ├── password_resets │ └── new │ │ └── call.html.erb │ ├── passwords │ └── edit │ │ └── call.html.erb │ ├── posts │ ├── create │ │ └── call.html.erb │ ├── new │ │ └── call.html.erb │ └── show │ │ └── call.html.erb │ ├── profiles │ ├── atom │ │ └── show │ │ │ └── call.atom.builder │ └── show │ │ └── call.html.erb │ ├── public │ └── show │ │ └── call.html.erb │ ├── search │ ├── profiles │ │ └── index │ │ │ └── call.html.erb │ └── show │ │ └── call.html.erb │ ├── sessions │ └── new │ │ └── call.html.erb │ ├── settings │ ├── emails │ │ └── show │ │ │ └── call.html.erb │ ├── index │ │ └── call.html.erb │ ├── profiles │ │ └── show │ │ │ └── call.html.erb │ └── users │ │ └── show │ │ └── call.html.erb │ ├── sign_up │ └── new │ │ └── call.html.erb │ ├── stamps │ └── create │ │ └── call.html.erb │ └── welcome │ └── show │ └── call.html.erb ├── bin ├── check ├── dev ├── erb_lint ├── erblint ├── good_job ├── puma ├── pumactl ├── rails ├── rake ├── rspec ├── setup ├── srb ├── srb-rbi ├── standardrb └── tapioca ├── config.ru ├── config ├── application.rb ├── boot.rb ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── alba.rb │ ├── assets.rb │ ├── content_security_policy.rb │ ├── doorkeeper.rb │ ├── enable_yjit.rb │ ├── filter_parameter_logging.rb │ ├── good_job.rb │ ├── inflections.rb │ ├── inline_svg.rb │ ├── not_nil.rb │ ├── permissions_policy.rb │ ├── rails_live_reload.rb │ ├── resend.rb │ ├── sentry.rb │ ├── session_store.rb │ ├── sorbet.rb │ ├── strong_migrations.rb │ ├── time_formats.rb │ └── types.rb ├── locales │ ├── actionmailer.en.yml │ ├── actionmailer.ja.yml │ ├── activerecord.en.yml │ ├── activerecord.ja.yml │ ├── doorkeeper.en.yml │ ├── errors.en.yml │ ├── errors.ja.yml │ ├── forms.en.yml │ ├── forms.ja.yml │ ├── messages.en.yml │ ├── messages.ja.yml │ ├── meta.en.yml │ ├── meta.ja.yml │ ├── nouns.en.yml │ ├── nouns.ja.yml │ ├── verbs.en.yml │ └── verbs.ja.yml ├── mewst.yml ├── puma.rb └── routes.rb ├── db ├── migrate │ ├── 20220000000001_initial_setup.rb │ ├── 20221119004702_create_initial_tables.rb │ ├── 20230830155958_create_good_jobs.rb │ ├── 20231015050126_add_time_zone_to_users.rb │ ├── 20231025165318_add_discarded_at_to_posts_and_profiles.rb │ ├── 20231028081207_add_notification_tables.rb │ ├── 20231107174301_rename_comment_to_content_on_posts.rb │ ├── 20231112022226_add_last_post_at_to_profiles.rb │ ├── 20231112024413_create_suggested_follows.rb │ ├── 20231113161740_add_via_id_to_posts.rb │ ├── 20231113162310_add_validation_via_id_to_posts.rb │ ├── 20231115145714_add_null_false_to_oauth_application_id_on_posts.rb │ ├── 20231115172238_change_email_columns_to_citext.rb │ ├── 20231216042900_drop_follow_notifications.rb │ ├── 20240115144958_create_good_job_settings.rb │ ├── 20240115144959_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb │ ├── 20240115144960_create_good_job_batches.rb │ ├── 20240115144961_create_good_job_executions.rb │ ├── 20240115144962_create_good_jobs_error_event.rb │ ├── 20240115144963_recreate_good_job_cron_indexes_with_conditional.rb │ ├── 20240115144964_create_good_job_labels.rb │ ├── 20240115144965_create_good_job_labels_index.rb │ ├── 20240115144966_remove_good_job_active_id_index.rb │ ├── 20240304164746_update_schema_202403.rb │ ├── 20240324094358_create_home_timeline_posts.rb │ ├── 20240403174827_create_sessions.rb │ ├── 20240404162020_remove_sign_in_columns_on_users.rb │ ├── 20240414152059_remove_stamps_count_on_posts.rb │ ├── 20240414155158_create_links.rb │ ├── 20240414155325_create_post_links.rb │ ├── 20240422190022_fix_notifications_for_delegated_type.rb │ └── 20240427202315_add_gravatar_url_to_profiles.rb ├── seeds.rb └── structure.sql ├── docker-compose.yml ├── fixtures └── vcr_cassettes │ └── link_data_fetcher │ ├── redirect_other_domain.yml │ ├── redirect_www_to_no_www.yml │ ├── threads_url.yml │ ├── valid.yml │ ├── without_canonical.yml │ ├── without_og_image.yml │ ├── without_title.yml │ └── youtube_short_url.yml ├── lib ├── mewst │ └── ui │ │ ├── base.rb │ │ ├── button.html.erb │ │ ├── button.rb │ │ ├── card.html.erb │ │ ├── card.rb │ │ ├── card │ │ ├── body.html.erb │ │ └── body.rb │ │ ├── dropdown.html.erb │ │ ├── dropdown.rb │ │ ├── dropdown │ │ ├── button.html.erb │ │ ├── button.rb │ │ ├── item.html.erb │ │ ├── item.rb │ │ ├── list.html.erb │ │ └── list.rb │ │ ├── icon.html.erb │ │ ├── icon.rb │ │ ├── tabs.html.erb │ │ ├── tabs.rb │ │ └── tabs │ │ ├── item.html.erb │ │ └── item.rb └── tasks │ ├── sorbet.rake │ └── suggested_follow.rake ├── openapi └── v1.yaml ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon.png ├── favicon.ico ├── icon-192.png ├── icon-512.png ├── icon.svg ├── maintenance.html ├── og-image.png └── robots.txt ├── sorbet ├── config ├── rbi │ ├── annotations │ │ ├── .gitattributes │ │ ├── actionmailer.rbi │ │ ├── actionpack.rbi │ │ ├── actionview.rbi │ │ ├── activejob.rbi │ │ ├── activemodel.rbi │ │ ├── activerecord.rbi │ │ ├── activesupport.rbi │ │ ├── faraday.rbi │ │ ├── globalid.rbi │ │ ├── minitest.rbi │ │ ├── pundit.rbi │ │ ├── railties.rbi │ │ └── rainbow.rbi │ ├── dsl │ │ ├── .gitattributes │ │ ├── abstract_controller │ │ │ ├── caching.rbi │ │ │ ├── caching │ │ │ │ └── fragments.rbi │ │ │ ├── callbacks.rbi │ │ │ ├── helpers.rbi │ │ │ ├── rendering.rbi │ │ │ └── url_for.rbi │ │ ├── account_form.rbi │ │ ├── accounts │ │ │ ├── create_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── action_controller │ │ │ ├── api_rendering.rbi │ │ │ ├── caching.rbi │ │ │ ├── conditional_get.rbi │ │ │ ├── content_security_policy.rbi │ │ │ ├── data_streaming.rbi │ │ │ ├── etag_with_flash.rbi │ │ │ ├── etag_with_template_digest.rbi │ │ │ ├── flash.rbi │ │ │ ├── form_builder.rbi │ │ │ ├── helpers.rbi │ │ │ ├── params_wrapper.rbi │ │ │ ├── redirecting.rbi │ │ │ ├── renderers.rbi │ │ │ ├── renderers │ │ │ │ └── all.rbi │ │ │ ├── request_forgery_protection.rbi │ │ │ ├── rescue.rbi │ │ │ ├── test_case │ │ │ │ └── behavior.rbi │ │ │ └── url_for.rbi │ │ ├── action_dispatch │ │ │ ├── assertions.rbi │ │ │ ├── integration_test.rbi │ │ │ └── routing │ │ │ │ ├── route_set │ │ │ │ └── mounted_helpers.rbi │ │ │ │ └── url_for.rbi │ │ ├── action_mailer │ │ │ ├── callbacks.rbi │ │ │ ├── delivery_methods.rbi │ │ │ ├── form_builder.rbi │ │ │ ├── mail_delivery_job.rbi │ │ │ ├── queued_delivery.rbi │ │ │ ├── rescuable.rbi │ │ │ └── test_case │ │ │ │ └── behavior.rbi │ │ ├── action_view │ │ │ ├── helpers.rbi │ │ │ ├── helpers │ │ │ │ ├── form_helper.rbi │ │ │ │ ├── form_tag_helper.rbi │ │ │ │ └── text_helper.rbi │ │ │ ├── layouts.rbi │ │ │ └── rendering.rbi │ │ ├── active_job │ │ │ ├── callbacks.rbi │ │ │ ├── exceptions.rbi │ │ │ ├── execution.rbi │ │ │ ├── logging.rbi │ │ │ ├── queue_adapter.rbi │ │ │ ├── queue_name.rbi │ │ │ ├── queue_priority.rbi │ │ │ └── test_helper │ │ │ │ └── test_queue_adapter.rbi │ │ ├── active_model │ │ │ ├── api.rbi │ │ │ ├── attribute_methods.rbi │ │ │ ├── attributes.rbi │ │ │ ├── conversion.rbi │ │ │ ├── dirty.rbi │ │ │ ├── model.rbi │ │ │ ├── serializers │ │ │ │ └── json.rbi │ │ │ ├── validations.rbi │ │ │ └── validations │ │ │ │ └── callbacks.rbi │ │ ├── active_record │ │ │ ├── attribute_methods.rbi │ │ │ ├── attribute_methods │ │ │ │ ├── dirty.rbi │ │ │ │ ├── serialization.rbi │ │ │ │ └── time_zone_conversion.rbi │ │ │ ├── attributes.rbi │ │ │ ├── callbacks.rbi │ │ │ ├── core.rbi │ │ │ ├── counter_cache.rbi │ │ │ ├── encryption │ │ │ │ └── encryptable_record.rbi │ │ │ ├── inheritance.rbi │ │ │ ├── integration.rbi │ │ │ ├── locking │ │ │ │ └── optimistic.rbi │ │ │ ├── model_schema.rbi │ │ │ ├── nested_attributes.rbi │ │ │ ├── normalization.rbi │ │ │ ├── readonly_attributes.rbi │ │ │ ├── reflection.rbi │ │ │ ├── scoping.rbi │ │ │ ├── scoping │ │ │ │ └── default.rbi │ │ │ ├── secure_password.rbi │ │ │ ├── serialization.rbi │ │ │ ├── signed_id.rbi │ │ │ ├── timestamp.rbi │ │ │ ├── token_for.rbi │ │ │ └── validations.rbi │ │ ├── active_support │ │ │ ├── actionable_error.rbi │ │ │ ├── callbacks.rbi │ │ │ ├── rescuable.rbi │ │ │ └── testing │ │ │ │ └── file_fixtures.rbi │ │ ├── actor.rbi │ │ ├── add_followed_profile_posts_to_timeline_job.rbi │ │ ├── add_post_to_timeline_job.rbi │ │ ├── api │ │ │ └── v1 │ │ │ │ ├── application_controller.rbi │ │ │ │ └── internal │ │ │ │ └── application_controller.rbi │ │ ├── application_component.rbi │ │ ├── application_controller.rbi │ │ ├── application_mailer.rbi │ │ ├── checks │ │ │ └── create_controller.rbi │ │ ├── create_suggested_follows_job.rbi │ │ ├── delete_post_form.rbi │ │ ├── delete_post_job.rbi │ │ ├── discard │ │ │ └── model.rbi │ │ ├── discard_post_form.rbi │ │ ├── doorkeeper │ │ │ ├── access_grant.rbi │ │ │ ├── access_grant_mixin.rbi │ │ │ ├── access_token.rbi │ │ │ ├── access_token_mixin.rbi │ │ │ ├── application.rbi │ │ │ ├── application_controller.rbi │ │ │ ├── application_metal_controller.rbi │ │ │ └── application_mixin.rbi │ │ ├── email_confirmation.rbi │ │ ├── email_confirmation_challenge_form.rbi │ │ ├── email_confirmation_form.rbi │ │ ├── email_confirmation_mailer.rbi │ │ ├── email_confirmations │ │ │ ├── create_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── email_update_form.rbi │ │ ├── fanout_post_job.rbi │ │ ├── follow.rbi │ │ ├── follow_form.rbi │ │ ├── followees │ │ │ └── index_controller.rbi │ │ ├── follows │ │ │ ├── create_controller.rbi │ │ │ └── destroy_controller.rbi │ │ ├── generated_path_helpers_module.rbi │ │ ├── generated_url_helpers_module.rbi │ │ ├── good_job │ │ │ ├── active_job_extensions │ │ │ │ ├── concurrency.rbi │ │ │ │ ├── labels.rbi │ │ │ │ └── notify_options.rbi │ │ │ ├── advisory_lockable.rbi │ │ │ ├── application_controller.rbi │ │ │ ├── base_execution.rbi │ │ │ ├── batch_record.rbi │ │ │ ├── discrete_execution.rbi │ │ │ ├── execution.rbi │ │ │ ├── frontends_controller.rbi │ │ │ ├── job.rbi │ │ │ ├── process.rbi │ │ │ └── setting.rbi │ │ ├── home │ │ │ └── show_controller.rbi │ │ ├── home_timeline_post.rbi │ │ ├── keyword_search_form.rbi │ │ ├── link.rbi │ │ ├── link_data_fetcher_form.rbi │ │ ├── link_form.rbi │ │ ├── links │ │ │ ├── create_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── mewst │ │ │ └── ui │ │ │ │ └── base.rbi │ │ ├── notification.rbi │ │ ├── notifications │ │ │ └── index_controller.rbi │ │ ├── oauth_access_token.rbi │ │ ├── oauth_application.rbi │ │ ├── password_reset_form.rbi │ │ ├── password_resets │ │ │ ├── create_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── passwords │ │ │ ├── edit_controller.rbi │ │ │ └── update_controller.rbi │ │ ├── post.rbi │ │ ├── post_form.rbi │ │ ├── post_link.rbi │ │ ├── posts │ │ │ ├── create_controller.rbi │ │ │ ├── destroy_controller.rbi │ │ │ ├── new_controller.rbi │ │ │ └── show_controller.rbi │ │ ├── profile.rbi │ │ ├── profile_form.rbi │ │ ├── profiles │ │ │ ├── atom │ │ │ │ └── show_controller.rbi │ │ │ └── show_controller.rbi │ │ ├── public │ │ │ └── show_controller.rbi │ │ ├── rails │ │ │ └── application_controller.rbi │ │ ├── remove_followed_profile_posts_from_timeline_job.rbi │ │ ├── search │ │ │ ├── profiles │ │ │ │ └── index_controller.rbi │ │ │ └── show_controller.rbi │ │ ├── sentry │ │ │ └── send_event_job.rbi │ │ ├── session.rbi │ │ ├── session_form.rbi │ │ ├── sessions │ │ │ ├── create_controller.rbi │ │ │ ├── destroy_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── settings │ │ │ ├── emails │ │ │ │ ├── show_controller.rbi │ │ │ │ └── update_controller.rbi │ │ │ ├── index_controller.rbi │ │ │ ├── profiles │ │ │ │ ├── show_controller.rbi │ │ │ │ └── update_controller.rbi │ │ │ └── users │ │ │ │ ├── show_controller.rbi │ │ │ │ └── update_controller.rbi │ │ ├── sign_up │ │ │ ├── create_controller.rbi │ │ │ └── new_controller.rbi │ │ ├── stamp.rbi │ │ ├── stamp_form.rbi │ │ ├── stamps │ │ │ ├── create_controller.rbi │ │ │ └── destroy_controller.rbi │ │ ├── suggested_follow.rbi │ │ ├── suggested_follow_form.rbi │ │ ├── time.rbi │ │ ├── user.rbi │ │ ├── user_form.rbi │ │ ├── user_profile.rbi │ │ ├── view_component │ │ │ ├── preview.rbi │ │ │ ├── slotable.rbi │ │ │ └── translatable.rbi │ │ ├── view_components_system_test_controller.rbi │ │ └── welcome │ │ │ └── show_controller.rbi │ ├── gems │ │ ├── .gitattributes │ │ ├── actioncable@7.1.5.1.rbi │ │ ├── actionmailbox@7.1.5.1.rbi │ │ ├── actionmailer@7.1.5.1.rbi │ │ ├── actionpack@7.1.5.1.rbi │ │ ├── actiontext@7.1.5.1.rbi │ │ ├── actionview@7.1.5.1.rbi │ │ ├── activejob@7.1.5.1.rbi │ │ ├── activemodel@7.1.5.1.rbi │ │ ├── activerecord@7.1.5.1.rbi │ │ ├── activerecord_cursor_paginate@0.2.0.rbi │ │ ├── activestorage@7.1.5.1.rbi │ │ ├── activesupport@7.1.5.1.rbi │ │ ├── addressable@2.8.7.rbi │ │ ├── alba@3.2.0.rbi │ │ ├── ast@2.4.2.rbi │ │ ├── base64@0.2.0.rbi │ │ ├── bcrypt@3.1.20.rbi │ │ ├── benchmark@0.4.0.rbi │ │ ├── better_html@2.1.1.rbi │ │ ├── bigdecimal@3.1.8.rbi │ │ ├── bindex@0.8.1.rbi │ │ ├── bootsnap@1.18.3.rbi │ │ ├── builder@3.3.0.rbi │ │ ├── bullet@7.1.6.rbi │ │ ├── capybara@3.40.0.rbi │ │ ├── childprocess@5.0.0.rbi │ │ ├── committee-rails@0.8.0.rbi │ │ ├── committee@5.4.0.rbi │ │ ├── concurrent-ruby@1.3.4.rbi │ │ ├── connection_pool@2.4.1.rbi │ │ ├── crass@1.0.6.rbi │ │ ├── cssbundling-rails@1.4.0.rbi │ │ ├── csv@3.3.0.rbi │ │ ├── cuprite@0.15.1.rbi │ │ ├── date@3.4.1.rbi │ │ ├── diff-lcs@1.5.1.rbi │ │ ├── discard@1.4.0.rbi │ │ ├── doorkeeper@5.8.1.rbi │ │ ├── dotenv-rails@3.1.2.rbi │ │ ├── dotenv@3.1.2.rbi │ │ ├── drb@2.2.1.rbi │ │ ├── email_validator@2.2.4.rbi │ │ ├── enumerize@2.8.1.rbi │ │ ├── erb_lint@0.9.0.rbi │ │ ├── erubi@1.13.0.rbi │ │ ├── et-orbi@1.2.11.rbi │ │ ├── factory_bot@6.4.5.rbi │ │ ├── factory_bot_rails@6.4.3.rbi │ │ ├── faraday-net_http@3.4.0.rbi │ │ ├── faraday@2.12.1.rbi │ │ ├── ferrum@0.15.rbi │ │ ├── ffi@1.16.3.rbi │ │ ├── fugit@1.11.1.rbi │ │ ├── globalid@1.2.1.rbi │ │ ├── good_job@3.29.3.rbi │ │ ├── http_accept_language@2.1.1.rbi │ │ ├── httparty@0.22.0.rbi │ │ ├── i18n@1.14.6.rbi │ │ ├── inline_svg@1.9.0.rbi │ │ ├── io-console@0.8.0.rbi │ │ ├── jb@0.8.2.rbi │ │ ├── jsbundling-rails@1.3.0.rbi │ │ ├── json@2.9.0.rbi │ │ ├── json_schema@0.21.0.rbi │ │ ├── language_server-protocol@3.17.0.3.rbi │ │ ├── launchy@3.0.1.rbi │ │ ├── letter_opener@1.10.0.rbi │ │ ├── letter_opener_web@3.0.0.rbi │ │ ├── lint_roller@1.1.0.rbi │ │ ├── listen@3.9.0.rbi │ │ ├── logger@1.6.3.rbi │ │ ├── lograge@0.14.0.rbi │ │ ├── loofah@2.23.1.rbi │ │ ├── mail@2.8.1.rbi │ │ ├── marcel@1.0.4.rbi │ │ ├── matrix@0.4.2.rbi │ │ ├── meta-tags@2.22.1.rbi │ │ ├── method_source@1.1.0.rbi │ │ ├── mini_mime@1.1.5.rbi │ │ ├── minitest@5.25.4.rbi │ │ ├── msgpack@1.7.2.rbi │ │ ├── multi_xml@0.7.1.rbi │ │ ├── mutex_m@0.3.0.rbi │ │ ├── net-http@0.6.0.rbi │ │ ├── net-imap@0.5.1.rbi │ │ ├── net-pop@0.1.2.rbi │ │ ├── net-protocol@0.2.2.rbi │ │ ├── net-smtp@0.5.0.rbi │ │ ├── netrc@0.11.0.rbi │ │ ├── nio4r@2.7.4.rbi │ │ ├── nokogiri@1.17.1.rbi │ │ ├── oj@3.16.4.rbi │ │ ├── openapi_parser@2.1.0.rbi │ │ ├── parallel@1.26.3.rbi │ │ ├── parser@3.3.6.0.rbi │ │ ├── pg@1.5.6.rbi │ │ ├── prism@1.2.0.rbi │ │ ├── propshaft@0.9.0.rbi │ │ ├── psych@5.2.2.rbi │ │ ├── public_suffix@6.0.0.rbi │ │ ├── puma@6.5.0.rbi │ │ ├── pundit@2.4.0.rbi │ │ ├── raabro@1.4.0.rbi │ │ ├── racc@1.8.1.rbi │ │ ├── rack-rewrite@1.5.1.rbi │ │ ├── rack-session@2.0.0.rbi │ │ ├── rack-test@2.1.0.rbi │ │ ├── rack@3.0.11.rbi │ │ ├── rackup@2.2.1.rbi │ │ ├── rails-dom-testing@2.2.0.rbi │ │ ├── rails-html-sanitizer@1.6.2.rbi │ │ ├── rails-i18n@7.0.9.rbi │ │ ├── rails@7.1.5.1.rbi │ │ ├── rails_autolink@1.1.8.rbi │ │ ├── rails_live_reload@0.3.6.rbi │ │ ├── railties@7.1.5.1.rbi │ │ ├── rainbow@3.1.1.rbi │ │ ├── rake@13.2.1.rbi │ │ ├── rb-fsevent@0.11.2.rbi │ │ ├── rb-inotify@0.10.1.rbi │ │ ├── rbi@0.2.1.rbi │ │ ├── rdoc@6.10.0.rbi │ │ ├── regexp_parser@2.9.3.rbi │ │ ├── reline@0.6.0.rbi │ │ ├── request_store@1.5.1.rbi │ │ ├── resend@0.14.0.rbi │ │ ├── rexml@3.3.9.rbi │ │ ├── rspec-core@3.13.2.rbi │ │ ├── rspec-expectations@3.13.3.rbi │ │ ├── rspec-mocks@3.13.2.rbi │ │ ├── rspec-rails@7.1.0.rbi │ │ ├── rspec-support@3.13.2.rbi │ │ ├── rubocop-ast@1.36.2.rbi │ │ ├── rubocop-capybara@2.20.0.rbi │ │ ├── rubocop-factory_bot@2.26.1.rbi │ │ ├── rubocop-performance@1.22.1.rbi │ │ ├── rubocop-rails@2.25.0.rbi │ │ ├── rubocop-rspec@2.31.0.rbi │ │ ├── rubocop-rspec_rails@2.28.3.rbi │ │ ├── rubocop-sorbet@0.7.0.rbi │ │ ├── rubocop@1.68.0.rbi │ │ ├── ruby-progressbar@1.13.0.rbi │ │ ├── securerandom@0.4.1.rbi │ │ ├── sentry-rails@5.22.0.rbi │ │ ├── sentry-ruby@5.22.0.rbi │ │ ├── smart_properties@1.17.0.rbi │ │ ├── spoom@1.5.0.rbi │ │ ├── stackprof@0.2.27.rbi │ │ ├── standard-custom@1.0.2.rbi │ │ ├── standard-performance@1.5.0.rbi │ │ ├── standard-rails@1.1.0.rbi │ │ ├── standard-sorbet@0.0.2.rbi │ │ ├── standard@1.42.1.rbi │ │ ├── stringio@3.1.2.rbi │ │ ├── strong_migrations@1.8.0.rbi │ │ ├── tapioca@0.16.5.rbi │ │ ├── thor@1.3.2.rbi │ │ ├── timeout@0.4.2.rbi │ │ ├── tzinfo@2.0.6.rbi │ │ ├── unicode-display_width@2.6.0.rbi │ │ ├── uniform_notifier@1.16.0.rbi │ │ ├── uri@1.0.2.rbi │ │ ├── vcr@6.2.0.rbi │ │ ├── view_component@3.20.0.rbi │ │ ├── web-console@4.2.1.rbi │ │ ├── webrick@1.9.0.rbi │ │ ├── websocket-driver@0.7.6.rbi │ │ ├── websocket-extensions@0.1.5.rbi │ │ ├── xpath@3.2.0.rbi │ │ ├── yard-sorbet@0.9.0.rbi │ │ ├── yard@0.9.37.rbi │ │ └── zeitwerk@2.7.1.rbi │ ├── manual │ │ ├── account.rbi │ │ ├── active_record │ │ │ └── relation.rbi │ │ ├── addressable.rbi │ │ ├── api │ │ │ └── v1 │ │ │ │ ├── application_controller.rbi │ │ │ │ ├── form_errorable.rbi │ │ │ │ └── timeline │ │ │ │ └── show_controller.rbi │ │ ├── application_helper.rbi │ │ ├── commented_repost.rbi │ │ ├── component_data_fetcher_helper.rbi │ │ ├── controller_concerns │ │ │ ├── api │ │ │ │ └── authenticatable.rbi │ │ │ ├── authenticatable.rbi │ │ │ ├── authorizable.rbi │ │ │ ├── email_confirmation_findable.rbi │ │ │ └── localizable.rbi │ │ ├── email_confirmation.rbi │ │ ├── email_confirmation_mailer.rbi │ │ ├── faraday.rbi │ │ ├── flash_toast_helper.rbi │ │ ├── form │ │ │ └── password_validatable.rbi │ │ ├── google │ │ │ └── cloud │ │ │ │ ├── pubsub.rbi │ │ │ │ ├── pubsub │ │ │ │ ├── credentials.rbi │ │ │ │ ├── project.rbi │ │ │ │ └── topic.rbi │ │ │ │ └── tasks │ │ │ │ └── v2 │ │ │ │ └── cloud_tasks │ │ │ │ ├── client.rbi │ │ │ │ └── credentials.rbi │ │ ├── icons │ │ │ ├── logo_icon_component.rbi │ │ │ ├── stamped_icon_component.rbi │ │ │ └── unstamped_icon_component.rbi │ │ ├── internal_authenticatable.rbi │ │ ├── kernel.rbi │ │ ├── language_helper.rbi │ │ ├── localizable.rbi │ │ ├── mewst │ │ │ └── application.rbi │ │ ├── model_concerns │ │ │ └── notifiable.rbi │ │ ├── nil_class.rbi │ │ ├── notification.rbi │ │ ├── post.rbi │ │ ├── post │ │ │ ├── private_association_relation.rbi │ │ │ └── private_relation.rbi │ │ ├── profile.rbi │ │ ├── profile │ │ │ └── private_relation.rbi │ │ ├── pubsub │ │ │ └── subscribable.rbi │ │ ├── repost.rbi │ │ ├── session │ │ │ └── private_collection_proxy.rbi │ │ ├── text_helper.rbi │ │ ├── time_helper.rbi │ │ └── user.rbi │ ├── shims │ │ ├── loofah.rbi │ │ └── minitest.rbi │ └── todo.rbi └── tapioca │ ├── config.yml │ └── require.rb ├── spec ├── factories │ ├── actor.rb │ ├── email_confirmation.rb │ ├── link.rb │ ├── oauth_application.rb │ ├── oauth_oauth_access_token.rb │ ├── post.rb │ ├── profile.rb │ └── user.rb ├── forms │ └── post_form_spec.rb ├── models │ ├── link_data_fetcher_spec.rb │ └── url_spec.rb ├── rails_helper.rb ├── requests │ ├── profiles │ │ └── show_spec.rb │ ├── sign_in │ │ └── new_spec.rb │ ├── sign_up │ │ └── new_spec.rb │ ├── v1 │ │ ├── follows │ │ │ ├── create_spec.rb │ │ │ └── destroy_spec.rb │ │ ├── internal │ │ │ ├── accounts │ │ │ │ └── create_spec.rb │ │ │ ├── email_confirmations │ │ │ │ ├── challenges │ │ │ │ │ └── create_spec.rb │ │ │ │ ├── create_spec.rb │ │ │ │ └── show_spec.rb │ │ │ ├── posts │ │ │ │ └── show_spec.rb │ │ │ ├── profiles │ │ │ │ └── posts │ │ │ │ │ └── index_spec.rb │ │ │ └── sessions │ │ │ │ └── create_spec.rb │ │ ├── notifications │ │ │ └── index_spec.rb │ │ ├── posts │ │ │ ├── create_spec.rb │ │ │ ├── destroy_spec.rb │ │ │ └── show_spec.rb │ │ ├── profiles │ │ │ ├── me │ │ │ │ └── update_spec.rb │ │ │ └── posts │ │ │ │ └── index_spec.rb │ │ ├── stamps │ │ │ ├── create_spec.rb │ │ │ └── destroy_spec.rb │ │ ├── suggested_profiles │ │ │ ├── checks │ │ │ │ └── create_spec.rb │ │ │ └── index_spec.rb │ │ ├── timeline │ │ │ └── show_spec.rb │ │ └── users │ │ │ └── me │ │ │ ├── show_spec.rb │ │ │ └── update_spec.rb │ └── welcome │ │ └── show_spec.rb ├── spec_helper.rb ├── support │ ├── capybara.rb │ ├── committee.rb │ ├── factory_bot.rb │ ├── request_helpers.rb │ └── vcr.rb ├── system │ └── welcome │ │ └── show_spec.rb └── use_cases │ ├── create_post_use_case_spec.rb │ ├── create_suggested_follows_use_case_spec.rb │ └── delete_post_use_case_spec.rb ├── tailwind.config.js └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgresql://postgres@postgresql:5432/mewst_development 2 | 3 | MEWST_COMMUNITY_URL=https://wikino.app/s/mewst/pages/54 4 | MEWST_EMAIL_DOMAIN=mewst.com 5 | MEWST_HOST=localhost 6 | MEWST_ORIGIN_PORT=3000 7 | MEWST_PORT=3300 8 | MEWST_PRIVACY_URL=https://wikino.app/s/mewst/pages/70 9 | MEWST_SIGN_UP_STOPPER_ENABLED=false 10 | MEWST_TERMS_URL=https://wikino.app/s/mewst/pages/71 11 | MEWST_URL=http://localhost:3300 12 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgresql://postgres@postgresql:5432/mewst_test 2 | 3 | MEWST_CHROME_URL: http://chrome:3333 4 | MEWST_HOST: localhost 5 | -------------------------------------------------------------------------------- /.erb_lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | EnableDefaultLinters: true 3 | linters: 4 | SpaceInHtmlTag: 5 | # 閉じタグを次の行で書きたいので無効にする 6 | enabled: false 7 | glob: "**/*.erb" 8 | exclude: 9 | - "**/vendor/**/*" 10 | - "**/node_modules/**/*" 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | /.bundle 4 | /.claude/settings.local.json 5 | /.devcontainer 6 | /.env.*.local 7 | /.env.local 8 | /.idea 9 | /.vscode 10 | /app/assets/builds/* 11 | /bin/sync 12 | /config/master.key 13 | /docker-compose.override.yml 14 | /lib/tasks/dev.rake 15 | /log/* 16 | /node_modules 17 | /private 18 | /public/uploads/* 19 | /tmp/* 20 | /vendor/bundle 21 | /yarn-error.log 22 | !/app/assets/builds/.keep 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/claude/base"] 2 | path = docs/claude/base 3 | url = https://github.com/shimpleco/claude-docs.git 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /docs 2 | /fixtures 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": true, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "all", 7 | "plugins": ["prettier-plugin-tailwindcss", "@ttskch/prettier-plugin-tailwindcss-anywhere"], 8 | "overrides": [ 9 | { 10 | "files": "*.html.erb", 11 | "options": { 12 | "parser": "anywhere" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | --require rails_helper 4 | -------------------------------------------------------------------------------- /.standard.yml: -------------------------------------------------------------------------------- 1 | extend_config: 2 | - .standard_rubocop_extensions.yml 3 | 4 | plugins: 5 | - standard-rails 6 | - standard-sorbet 7 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # Mewst開発ガイド 2 | 3 | Mewstはマイクロブログサービスです。 4 | ユーザーは短文のポストを作成することができ、ユーザーをフォローするとタイムラインにポストが時系列で表示されます。 5 | 6 | ## 基本情報 7 | 8 | - @docs/claude/base/coding-conventions/css.md 9 | - @docs/claude/base/coding-conventions/general.md 10 | - @docs/claude/base/coding-conventions/rails.md 11 | - @docs/claude/base/coding-conventions/rspec.md 12 | - @docs/claude/base/coding-conventions/ruby.md 13 | - @docs/claude/base/command-reference.md 14 | - @docs/claude/base/repository-structure.md 15 | - @docs/claude/base/tech-stack.md 16 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bin/puma -C config/puma.rb 2 | worker: bin/good_job start 3 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | css: yarn build:css --watch 2 | js: yarn build --watch 3 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "cron": [ 3 | { 4 | "command": "bin/rails suggested_follow:create", 5 | "schedule": "0 * * * *" 6 | } 7 | ], 8 | "scripts": { 9 | "dokku": { 10 | "postdeploy": "bin/rails db:migrate" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/assets/builds/.keep: -------------------------------------------------------------------------------- 1 | NOTE: 2 | `app/assets/builds/` ディレクトリがリポジトリ上に存在しないと 3 | `assets:precompile` したとき `public/assets/` にファイルが出力されないことがある 4 | https://github.com/rails/jsbundling-rails/issues/87#issuecomment-1046365785 5 | -------------------------------------------------------------------------------- /app/assets/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/app/assets/images/avatar.png -------------------------------------------------------------------------------- /app/assets/images/icons/caret-left-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/assets/images/icons/caret-right-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/assets/images/icons/search_fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | search_fill 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* https://fontawesome.com/docs/web/add-icons/svg-bare */ 6 | .mst-fa-icon { 7 | height: 1em; 8 | vertical-align: -0.125em; 9 | width: 1em; 10 | } 11 | 12 | .mst-svg-icon { 13 | fill: currentcolor; 14 | } 15 | -------------------------------------------------------------------------------- /app/components/application_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationComponent < ViewComponent::Base 5 | extend T::Sig 6 | 7 | delegate :mst_absolute_time, :mst_image_url, :mst_time_ago_in_words, :name_with_atname, :signed_in?, :viewer!, to: :helpers 8 | end 9 | -------------------------------------------------------------------------------- /app/components/blank_slate_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 🐈‍⬛ 5 |
6 | 7 |
8 |
9 | <%= message %> 10 |
11 | 12 | <%= content %> 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /app/components/blank_slate_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class BlankSlateComponent < ApplicationComponent 5 | sig { params(message: String, class_name: String).void } 6 | def initialize(message:, class_name: "") 7 | @message = message 8 | @class_name = class_name 9 | end 10 | 11 | sig { returns(String) } 12 | attr_reader :message 13 | private :message 14 | 15 | sig { returns(String) } 16 | attr_reader :class_name 17 | private :class_name 18 | end 19 | -------------------------------------------------------------------------------- /app/components/buttons/check_suggested_profile_button_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= button_to profile_check_path(target_profile.atname), { 2 | class: "btn btn-ghost no-animation rounded-full w-full", 3 | } do %> 4 | <%= t("verbs.hide") %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/components/buttons/check_suggested_profile_button_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Buttons::CheckSuggestedProfileButtonComponent < ApplicationComponent 5 | sig { params(target_profile: Profile).void } 6 | def initialize(target_profile:) 7 | @target_profile = target_profile 8 | end 9 | 10 | sig { returns(Profile) } 11 | attr_reader :target_profile 12 | private :target_profile 13 | end 14 | -------------------------------------------------------------------------------- /app/components/buttons/stamp_button_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Buttons::StampButtonComponent < ApplicationComponent 5 | sig { params(post: Post, stamp_checker: StampChecker).void } 6 | def initialize(post:, stamp_checker:) 7 | @post = post 8 | @stamp_checker = stamp_checker 9 | end 10 | 11 | sig { returns(Post) } 12 | attr_reader :post 13 | private :post 14 | 15 | sig { returns(StampChecker) } 16 | attr_reader :stamp_checker 17 | private :stamp_checker 18 | end 19 | -------------------------------------------------------------------------------- /app/components/cards/detailed_post_card_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::DetailedPostCardComponent < ApplicationComponent 5 | sig { params(post: Post, stamp_checker: StampChecker).void } 6 | def initialize(post:, stamp_checker:) 7 | @post = post 8 | @stamp_checker = stamp_checker 9 | end 10 | 11 | sig { returns(Post) } 12 | attr_reader :post 13 | private :post 14 | 15 | sig { returns(StampChecker) } 16 | attr_reader :stamp_checker 17 | private :stamp_checker 18 | 19 | sig { returns(Profile) } 20 | private def profile 21 | post.profile.not_nil! 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/components/cards/form_errors_card_component.html.erb: -------------------------------------------------------------------------------- 1 |
" role="alert"> 2 |
3 |
4 | <%= t "nouns.error" %> 5 |
6 | 7 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /app/components/cards/form_errors_card_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::FormErrorsCardComponent < ApplicationComponent 5 | sig { params(errors: ActiveModel::Errors, class_name: String).void } 6 | def initialize(errors:, class_name: "") 7 | @errors = errors 8 | @class_name = class_name 9 | end 10 | 11 | sig { returns(String) } 12 | attr_reader :class_name 13 | private :class_name 14 | 15 | sig { returns(T::Boolean) } 16 | private def render? 17 | !@errors.empty? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/components/cards/link_card_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% if link.image_url.present? %> 4 | <%= image_tag(link.image_url, { 5 | class: "aspect-video max-h-[300px] object-contain w-full", 6 | loading: "lazy", 7 | size: "16x9", 8 | }) %> 9 | <% end %> 10 | 11 |
12 |
13 | <%= link.domain %> 14 |
15 | 16 |
17 | <%= link.title %> 18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /app/components/cards/link_card_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::LinkCardComponent < ApplicationComponent 5 | sig { params(link: Link).void } 6 | def initialize(link:) 7 | @link = link 8 | end 9 | 10 | sig { returns(Link) } 11 | attr_reader :link 12 | private :link 13 | end 14 | -------------------------------------------------------------------------------- /app/components/cards/notification_card/stamp_notification_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= t("messages.components.notification_card.stamped") %> 4 |
5 | 6 |
7 | <%= link_to profile_post_path(target_post.profile.atname, target_post.id) do %> 8 |
9 | <%= target_post.content %> 10 |
11 | <% end %> 12 |
13 |
14 | -------------------------------------------------------------------------------- /app/components/cards/notification_card/stamp_notification_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::NotificationCard::StampNotificationComponent < ApplicationComponent 5 | sig { params(notification: Notification).void } 6 | def initialize(notification:) 7 | @notification = notification 8 | end 9 | 10 | sig { returns(Notification) } 11 | attr_reader :notification 12 | private :notification 13 | 14 | sig { returns(Post) } 15 | def target_post 16 | notification.notifiable.post.not_nil! 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/components/cards/notification_card_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Mewst::UI::Card.new do %> 2 | <%= render Mewst::UI::Card::Body.new do %> 3 | <%= render EntryHeaders::BasicEntryHeaderComponent.new(profile: source_profile, time: notification.notified_at, follow_checker:) %> 4 | 5 | <% case notification.deserialized_notifiable_type %> 6 | <% when NotifiableType::Stamp %> 7 | <%= render Cards::NotificationCard::StampNotificationComponent.new(notification:) %> 8 | <% end %> 9 | <% end %> 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/components/cards/post_card/body_component.html.erb: -------------------------------------------------------------------------------- 1 |
"> 2 | <%= helpers.render_content(post.content) %> 3 |
4 | -------------------------------------------------------------------------------- /app/components/cards/post_card/body_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::PostCard::BodyComponent < ApplicationComponent 5 | sig { params(post: Post, class_name: String).void } 6 | def initialize(post:, class_name: "") 7 | @post = post 8 | @class_name = class_name 9 | end 10 | 11 | sig { returns(Post) } 12 | attr_reader :post 13 | private :post 14 | 15 | sig { returns(String) } 16 | attr_reader :class_name 17 | private :class_name 18 | 19 | sig { returns(Profile) } 20 | private def profile 21 | post.profile.not_nil! 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/components/cards/post_card/footer_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render Buttons::StampButtonComponent.new(post:, stamp_checker:) %> 3 | 4 | <%= render Dropdowns::PostOptionsDropdownComponent.new(post:) %> 5 |
6 | -------------------------------------------------------------------------------- /app/components/cards/post_card/footer_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::PostCard::FooterComponent < ApplicationComponent 5 | sig { params(post: Post, stamp_checker: StampChecker).void } 6 | def initialize(post:, stamp_checker:) 7 | @post = post 8 | @stamp_checker = stamp_checker 9 | end 10 | 11 | sig { returns(Post) } 12 | attr_reader :post 13 | private :post 14 | 15 | sig { returns(StampChecker) } 16 | attr_reader :stamp_checker 17 | private :stamp_checker 18 | 19 | sig { returns(Profile) } 20 | private def profile 21 | post.profile.not_nil! 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/components/cards/post_nav_link_card_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Mewst::UI::Card.new do %> 2 | <%= render Mewst::UI::Card::Body.new do %> 3 | <%= post.content.truncate(10) %> 4 | <% end %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/components/cards/post_nav_link_card_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module Cards 5 | class PostNavLinkCardComponent < ApplicationComponent 6 | sig { params(post: Post).void } 7 | def initialize(post:) 8 | @post = post 9 | end 10 | 11 | sig { returns(Post) } 12 | attr_reader :post 13 | private :post 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/components/cards/profile_card_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Cards::ProfileCardComponent < ApplicationComponent 5 | renders_one :actions_menu 6 | 7 | sig { params(profile: Profile).void } 8 | def initialize(profile:) 9 | @profile = profile 10 | end 11 | 12 | sig { returns(Profile) } 13 | attr_reader :profile 14 | private :profile 15 | end 16 | -------------------------------------------------------------------------------- /app/components/containers/basic_container_component.html.erb: -------------------------------------------------------------------------------- 1 |
"> 2 | <%= content %> 3 |
4 | -------------------------------------------------------------------------------- /app/components/containers/basic_container_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Containers::BasicContainerComponent < ApplicationComponent 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /app/components/dropdowns/post_options_dropdown_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Dropdowns::PostOptionsDropdownComponent < ApplicationComponent 5 | sig { params(post: Post).void } 6 | def initialize(post:) 7 | @post = post 8 | end 9 | 10 | sig { returns(Post) } 11 | attr_reader :post 12 | private :post 13 | end 14 | -------------------------------------------------------------------------------- /app/components/dropdowns/profile_options_dropdown_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Dropdowns::ProfileOptionsDropdownComponent < ApplicationComponent 5 | sig { params(profile: Profile).void } 6 | def initialize(profile:) 7 | @profile = profile 8 | end 9 | 10 | sig { returns(Profile) } 11 | attr_reader :profile 12 | private :profile 13 | end 14 | -------------------------------------------------------------------------------- /app/components/footers/basic_footer_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Footers::BasicFooterComponent < ApplicationComponent 5 | end 6 | -------------------------------------------------------------------------------- /app/components/forms/keyword_search_form_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with method: :get, model: form, url: form_url do |f| %> 2 | 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/components/forms/keyword_search_form_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Forms::KeywordSearchFormComponent < ApplicationComponent 5 | sig { params(form: KeywordSearchForm, form_url: String).void } 6 | def initialize(form:, form_url:) 7 | @form = form 8 | @form_url = form_url 9 | end 10 | 11 | sig { returns(KeywordSearchForm) } 12 | attr_reader :form 13 | private :form 14 | 15 | sig { returns(String) } 16 | attr_reader :form_url 17 | private :form_url 18 | end 19 | -------------------------------------------------------------------------------- /app/components/forms/post_form_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Forms::PostFormComponent < ApplicationComponent 5 | sig { params(form: PostForm, from_modal: T::Boolean).void } 6 | def initialize(form:, from_modal: false) 7 | @form = form 8 | @from_modal = from_modal 9 | end 10 | 11 | sig { returns(PostForm) } 12 | attr_reader :form 13 | private :form 14 | 15 | sig { returns(T::Boolean) } 16 | attr_reader :from_modal 17 | private :from_modal 18 | end 19 | -------------------------------------------------------------------------------- /app/components/icons/logo_icon_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Mewst::UI::Icon.new(name: "logo", color: "base-content", size:, class_name:) %> 2 | -------------------------------------------------------------------------------- /app/components/icons/logo_icon_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Icons::LogoIconComponent < ApplicationComponent 5 | sig { params(size: String, class_name: String).void } 6 | def initialize(size:, class_name: "") 7 | @size = size 8 | @class_name = class_name 9 | end 10 | 11 | sig { returns(String) } 12 | attr_reader :size 13 | private :size 14 | 15 | sig { returns(String) } 16 | attr_reader :class_name 17 | private :class_name 18 | end 19 | -------------------------------------------------------------------------------- /app/components/icons/navbar_menu_icon_component.html.erb: -------------------------------------------------------------------------------- 1 | <% if active %> 2 | <%= render Mewst::UI::Icon.new(name: active_name, color: "base-content", size:) %> 3 | <% else %> 4 | <%= render Mewst::UI::Icon.new(name:, color: "gray-400", size:) %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/components/images/avatar_image_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.div(class: "avatar") do %> 2 | <%= tag.div(class: class_names("rounded-full", class_name)) do %> 3 | <%= image_tag(image_url, alt:, loading: "lazy", size:) %> 4 | <% end %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/components/images/profile_avatar_image_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Images::AvatarImageComponent.new(image_url: profile.avatar_url, width:, alt:, class_name:) %> 2 | -------------------------------------------------------------------------------- /app/components/layouts/basic_layout_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render Navbars::TopNavbarComponent.new %> 3 | 4 |
"> 5 | <%= content %> 6 |
7 | 8 | <% if with_footer %> 9 |
10 | <%= render Footers::BasicFooterComponent.new %> 11 |
12 | <% end %> 13 | 14 |
15 | <%= render Toasts::FlashToastComponent.new(flash:) %> 16 | <%= render Navbars::BottomNavbarComponent.new %> 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/components/layouts/basic_layout_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Layouts::BasicLayoutComponent < ApplicationComponent 5 | sig { params(with_footer: T::Boolean, main_class_name: String).void } 6 | def initialize(with_footer: true, main_class_name: "") 7 | @with_footer = with_footer 8 | @main_class_name = main_class_name 9 | end 10 | 11 | sig { returns(T::Boolean) } 12 | attr_reader :with_footer 13 | private :with_footer 14 | 15 | sig { returns(String) } 16 | attr_reader :main_class_name 17 | private :main_class_name 18 | end 19 | -------------------------------------------------------------------------------- /app/components/layouts/simple_layout_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= content %> 4 |
5 | 6 |
7 | <%= render Toasts::FlashToastComponent.new(flash:) %> 8 |
9 |
10 | -------------------------------------------------------------------------------- /app/components/layouts/simple_layout_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Layouts::SimpleLayoutComponent < ApplicationComponent 5 | end 6 | -------------------------------------------------------------------------------- /app/components/lists/post_card_list_component.html.erb: -------------------------------------------------------------------------------- 1 |
" id="post-card-list"> 2 | <% posts.each do |post| %> 3 | <%= render Cards::PostCardComponent.new(post:, stamp_checker:, follow_checker:) %> 4 | <% end %> 5 |
6 | -------------------------------------------------------------------------------- /app/components/modals/base/modal_box_component.html.erb: -------------------------------------------------------------------------------- 1 |
"> 2 |
3 | 6 |
7 | 8 | <%= content %> 9 |
10 | -------------------------------------------------------------------------------- /app/components/modals/base/modal_box_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Modals::Base::ModalBoxComponent < ApplicationComponent 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /app/components/modals/base/modal_component.html.erb: -------------------------------------------------------------------------------- 1 | " 3 | data-controller="modal" 4 | > 5 | <%= content %> 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/components/modals/base/modal_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Modals::Base::ModalComponent < ApplicationComponent 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /app/components/modals/post_form_modal_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Modals::Base::ModalComponent.new do %> 2 | <%= render Modals::Base::ModalBoxComponent.new(class_name: "bg-base-300") do %> 3 | 4 | <%= render Forms::PostFormComponent.new(form:, from_modal: true) %> 5 | 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/components/modals/post_form_modal_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Modals::PostFormModalComponent < ApplicationComponent 5 | sig { params(form: PostForm).void } 6 | def initialize(form:) 7 | @form = form 8 | end 9 | 10 | sig { returns(PostForm) } 11 | attr_reader :form 12 | private :form 13 | end 14 | -------------------------------------------------------------------------------- /app/components/navbars/bottom_navbar_component.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render NavbarMenuComponent.new(class_name: "border border-slate-400 me-auto ms-auto") %> 3 |
4 | -------------------------------------------------------------------------------- /app/components/navbars/bottom_navbar_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Navbars::BottomNavbarComponent < ApplicationComponent 5 | end 6 | -------------------------------------------------------------------------------- /app/components/navbars/top_navbar_component.html.erb: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /app/components/navbars/top_navbar_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Navbars::TopNavbarComponent < ApplicationComponent 5 | end 6 | -------------------------------------------------------------------------------- /app/components/tabs/timeline_tabs_component.html.erb: -------------------------------------------------------------------------------- 1 | <%= render Mewst::UI::Tabs.new(class_name:) do |component| %> 2 | <% component.with_item(href: home_path, active: current_page?("/home")) do %> 3 | <%= t("nouns.home") %> 4 | <% end %> 5 | 6 | <% component.with_item(href: public_path, active: current_page?("/public")) do %> 7 | <%= t("nouns.public") %> 8 | <% end %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/components/tabs/timeline_tabs_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Tabs::TimelineTabsComponent < ApplicationComponent 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /app/components/toasts/flash_toast_component.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Toasts::FlashToastComponent < ApplicationComponent 5 | sig { params(flash: ActionDispatch::Flash::FlashHash).void } 6 | def initialize(flash:) 7 | @flash = flash 8 | end 9 | 10 | sig { returns(T.nilable(Symbol)) } 11 | private def flash_toast_type 12 | @flash.keys.first&.to_sym 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/accounts/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Accounts::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | include ControllerConcerns::EmailConfirmationFindable 8 | 9 | around_action :set_locale 10 | before_action :require_no_authentication 11 | before_action :require_succeeded_email_confirmation 12 | 13 | sig { returns(T.untyped) } 14 | def call 15 | @form = AccountForm.new(email: @email_confirmation.not_nil!.email.not_nil!) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/api/v1/README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > `api/v1` は現在工事中です。Web APIを公開するときに修正します 3 | -------------------------------------------------------------------------------- /app/controllers/api/v1/application_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Api::V1::ApplicationController < ActionController::API 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/api/v1/internal/application_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Api::V1::Internal::ApplicationController < ActionController::API 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/api/v1/profiles/me/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Api::V1::Profiles::Me::ShowController < Api::V1::ApplicationController 5 | # include ControllerConcerns::PublicAuthenticatable 6 | # 7 | # def call 8 | # profile_resource = V1::ProfileResource.new(viewer: current_viewer!, profile: current_viewer!.profile.not_nil!) 9 | # 10 | # render( 11 | # json: { 12 | # profile: V1::ProfileSerializer.new(profile_resource).to_h 13 | # } 14 | # ) 15 | # end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/api/v1/users/me/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Api::V1::Users::Me::ShowController < Api::V1::ApplicationController 5 | # include ControllerConcerns::PublicAuthenticatable 6 | # 7 | # def call 8 | # user_resource = V1::UserResource.new(user: current_viewer!.user.not_nil!) 9 | # 10 | # render( 11 | # json: { 12 | # user: V1::UserSerializer.new(user_resource).to_h 13 | # } 14 | # ) 15 | # end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationController < ActionController::Base 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/communities/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Communities::ShowController < ApplicationController 5 | sig { returns(T.untyped) } 6 | def call 7 | redirect_to(Rails.configuration.mewst["community_url"], allow_other_host: true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/controller_concerns/api/authenticatable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Api::Authenticatable 5 | # extend T::Sig 6 | # extend ActiveSupport::Concern 7 | # 8 | # included do 9 | # before_action :doorkeeper_authorize! 10 | # end 11 | # 12 | # sig { returns(T.nilable(Actor)) } 13 | # def current_viewer 14 | # @current_viewer ||= T.let(doorkeeper_token&.resource_owner, T.nilable(Actor)) 15 | # end 16 | # 17 | # sig { returns(Actor) } 18 | # def current_viewer! 19 | # current_viewer.not_nil! 20 | # end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/controller_concerns/api/v1/form_errorable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Api::V1::FormErrorable 5 | # extend T::Sig 6 | # extend ActiveSupport::Concern 7 | # 8 | # sig { params(resource_class: T.class_of(V1::FormErrorResource), errors: ActiveModel::Errors).void } 9 | # def response_form_errors(resource_class:, errors:) 10 | # resource_errors = resource_class.from_errors(errors: errors) 11 | # 12 | # render( 13 | # json: V1::FormErrorSerializer.new(resource_errors), 14 | # status: :unprocessable_entity 15 | # ) 16 | # end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/controller_concerns/authorizable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Authorizable 5 | extend T::Sig 6 | extend ActiveSupport::Concern 7 | 8 | include Pundit::Authorization 9 | 10 | sig(:final) { returns(Actor) } 11 | def pundit_user 12 | current_viewer! 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/email_confirmations/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmations::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | include ControllerConcerns::EmailConfirmationFindable 8 | 9 | around_action :set_locale 10 | before_action :require_email_confirmation_id 11 | 12 | sig { returns(T.untyped) } 13 | def call 14 | @form = EmailConfirmationChallengeForm.new 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/links/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Links::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = LinkDataFetcherForm.new(target_url: params[:url]) 14 | @host_and_path = Url.new(params[:url]).shorten_host_and_path 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/manifests/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Manifests::ShowController < ApplicationController 5 | sig { returns(T.untyped) } 6 | def call 7 | respond_to do |format| 8 | format.json { render(layout: false) } 9 | format.any { render(plain: "Not Found", status: :not_found) } 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/password_resets/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class PasswordResets::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_no_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = EmailConfirmationForm.new 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/passwords/edit_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Passwords::EditController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | include ControllerConcerns::EmailConfirmationFindable 8 | 9 | around_action :set_locale 10 | before_action :require_no_authentication 11 | before_action :require_succeeded_email_confirmation 12 | 13 | sig { returns(T.untyped) } 14 | def call 15 | @form = PasswordResetForm.new 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/posts/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Posts::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = PostForm.new(content: params[:content], with_frame: params[:with_frame]) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/posts/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Posts::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | 10 | sig { returns(T.untyped) } 11 | def call 12 | @profile = Profile.kept.find_by!(atname: params[:atname]) 13 | @post = @profile.posts.kept.find(params[:post_id]) 14 | @stamp_checker = StampChecker.new(profile: viewer&.profile, posts: [@post]) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/privacies/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Privacies::ShowController < ApplicationController 5 | sig { returns(T.untyped) } 6 | def call 7 | redirect_to(Rails.configuration.mewst["privacy_url"], allow_other_host: true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/profiles/atom/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Profiles::Atom::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | 10 | sig { returns(T.untyped) } 11 | def call 12 | @profile = Profile.kept.find_by!(atname: params[:atname]) 13 | @posts = @profile.posts.kept.order(published_at: :desc).limit(15) 14 | 15 | render(formats: :atom) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/search/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Search::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @profiles = viewer!.checkable_suggested_followees.order(created_at: :desc).limit(30) 14 | @follow_checker = FollowChecker.new(profile: viewer!.profile.not_nil!, target_profiles: @profiles) 15 | @form = KeywordSearchForm.new 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/sessions/destroy_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Sessions::DestroyController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | sign_out 14 | 15 | flash[:notice] = t("messages.authentication.signed_out_successfully") 16 | redirect_to root_path 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/sessions/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Sessions::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_no_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = SessionForm.new 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/settings/emails/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Settings::Emails::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = EmailUpdateForm.new 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/settings/index_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Settings::IndexController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/settings/users/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Settings::Users::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = UserForm.new( 14 | locale: viewer!.locale.serialize, 15 | time_zone: viewer!.time_zone 16 | ) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/sign_up/new_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class SignUp::NewController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | before_action :require_no_authentication 10 | 11 | sig { returns(T.untyped) } 12 | def call 13 | @form = EmailConfirmationForm.new 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/terms/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Terms::ShowController < ApplicationController 5 | sig { returns(T.untyped) } 6 | def call 7 | redirect_to(Rails.configuration.mewst["terms_url"], allow_other_host: true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/welcome/show_controller.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class Welcome::ShowController < ApplicationController 5 | include ControllerConcerns::Authenticatable 6 | include ControllerConcerns::Localizable 7 | 8 | around_action :set_locale 9 | 10 | sig { returns(T.untyped) } 11 | def call 12 | redirect_to(home_path) if signed_in? 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/forms/application_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationForm 5 | extend T::Sig 6 | 7 | include ActiveModel::Model 8 | include ActiveModel::Attributes 9 | 10 | # @overload 11 | sig { returns(Symbol) } 12 | def self.i18n_scope 13 | :forms 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/forms/delete_post_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DeletePostForm < ApplicationForm 5 | attribute :target_post_id, :string 6 | 7 | validates :target_post, presence: true 8 | 9 | sig { returns(T.nilable(Post)) } 10 | def target_post 11 | Post.discarded.find_by(id: target_post_id) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/forms/discard_post_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DiscardPostForm < ApplicationForm 5 | sig { returns(T.nilable(Profile)) } 6 | attr_accessor :profile 7 | 8 | attribute :target_post_id, :string 9 | 10 | validates :profile, presence: true 11 | validates :target_post, presence: true 12 | 13 | sig { returns(T.nilable(Post)) } 14 | def target_post 15 | profile&.posts&.kept&.find_by(id: target_post_id) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/forms/email_confirmation_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmationForm < ApplicationForm 5 | attribute :email, :string 6 | 7 | validates :email, email: true, presence: true 8 | end 9 | -------------------------------------------------------------------------------- /app/forms/email_update_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class EmailUpdateForm < ApplicationForm 5 | attribute :new_email, :string 6 | 7 | validates :new_email, email: true, presence: true 8 | validate :email_uniqueness 9 | 10 | sig { void } 11 | private def email_uniqueness 12 | if User.find_by(email: new_email) 13 | errors.add(:new_email, :uniqueness) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/forms/form_concerns/password_validatable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module FormConcerns::PasswordValidatable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | validates :password, length: {minimum: User::PASSWORD_MIN_LENGTH}, presence: true 9 | validate do |record| 10 | password_value = record.password 11 | 12 | if password_value.present? && password_value.bytesize > ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED 13 | record.errors.add(:password, :password_too_long) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/forms/keyword_search_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class KeywordSearchForm < ApplicationForm 5 | attribute :q, :string 6 | end 7 | -------------------------------------------------------------------------------- /app/forms/link_data_fetcher_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class LinkDataFetcherForm < ApplicationForm 5 | attribute :target_url, :string 6 | 7 | validates :target_url, presence: true, url: true 8 | 9 | sig { void } 10 | def add_fetch_error! 11 | errors.add(:target_url, :fetch_error) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/forms/link_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class LinkForm < ApplicationForm 5 | attribute :canonical_url, :string 6 | attribute :domain, :string 7 | attribute :title, :string 8 | attribute :image_url, :string 9 | 10 | validates :canonical_url, presence: true, url: true 11 | validates :domain, presence: true 12 | validates :title, presence: true 13 | validates :image_url, url: {allow_blank: true} 14 | end 15 | -------------------------------------------------------------------------------- /app/forms/password_reset_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class PasswordResetForm < ApplicationForm 5 | include FormConcerns::PasswordValidatable 6 | 7 | attribute :password, :string 8 | end 9 | -------------------------------------------------------------------------------- /app/forms/post_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class PostForm < ApplicationForm 5 | attribute :content, :string 6 | attribute :canonical_url, :string, default: "" 7 | attribute :with_frame, :boolean, default: false 8 | 9 | validates :content, length: {maximum: Post::MAXIMUM_CONTENT_LENGTH}, presence: true 10 | validates :canonical_url, url: {allow_blank: true} 11 | end 12 | -------------------------------------------------------------------------------- /app/forms/session_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class SessionForm < ApplicationForm 5 | attribute :email, :string 6 | attribute :password, :string 7 | 8 | validates :email, email: true, presence: true 9 | validates :password, presence: true 10 | validate :authentication 11 | 12 | sig { returns(T.nilable(User)) } 13 | def user 14 | @user ||= T.let(User.find_by(email:), T.nilable(User)) 15 | end 16 | 17 | sig { void } 18 | private def authentication 19 | unless user&.authenticate(password) 20 | errors.add(:base, :unauthenticated) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/forms/stamp_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class StampForm < ApplicationForm 5 | attribute :target_post_id, :string 6 | 7 | validates :target_post, presence: true 8 | 9 | sig { returns(T.nilable(Post)) } 10 | def target_post 11 | Post.find_by(id: target_post_id) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/forms/suggested_follow_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class SuggestedFollowForm < ApplicationForm 5 | sig { returns(T.nilable(Profile)) } 6 | attr_accessor :source_profile 7 | 8 | attribute :target_atname, :string 9 | 10 | validates :source_profile, presence: true 11 | validates :target_profile, presence: true 12 | 13 | sig { returns(T.nilable(Profile)) } 14 | def target_profile 15 | Profile.kept.find_by(atname: target_atname) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/forms/user_form.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class UserForm < ApplicationForm 5 | attribute :locale, :string 6 | attribute :time_zone, :string 7 | 8 | validates :locale, inclusion: {in: I18n.available_locales.map(&:to_s)}, presence: true 9 | validates :time_zone, inclusion: {in: ActiveSupport::TimeZone.all.map { |tz| tz.tzinfo.name }}, presence: true 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/flash_toast_helper.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module FlashToastHelper 5 | extend T::Sig 6 | 7 | sig { params(type: String, message: String).returns(String) } 8 | def dispatch_to_flash_toast(type:, message:) 9 | tag.div( 10 | data: { 11 | controller: "flash-toast-dispatch", 12 | flash_toast_dispatch_type_value: type, 13 | flash_toast_dispatch_message_html_value: message 14 | } 15 | ) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/helpers/language_helper.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module LanguageHelper 5 | extend T::Sig 6 | 7 | sig { params(lang: Symbol).returns(String) } 8 | def lang_option_text(lang:) 9 | text = t("nouns.#{lang}") 10 | text_native = t("nouns.#{lang}_native") 11 | 12 | if text == text_native 13 | text 14 | else 15 | "#{text} - #{text_native}" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/profiles_helper.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ProfilesHelper 5 | extend T::Sig 6 | 7 | sig { params(profile: Profile).returns(String) } 8 | def name_with_atname(profile:) 9 | profile.name.present? ? "#{profile.name} (@#{profile.atname})" : "@#{profile.atname}" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/helpers/text_helper.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module TextHelper 5 | extend T::Sig 6 | 7 | sig { params(content: String).returns(String) } 8 | def render_content(content) 9 | # 2回以上の改行は1つの空行にする 10 | replaced_content = content.gsub(/\n{2,}/, "
\n") 11 | auto_link(simple_format(replaced_content), html: {class: "link link-info", target: "_blank"}, link: :urls) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/javascript/controllers/autosize-controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | import autosize from "autosize"; 3 | 4 | export default class extends Controller { 5 | initialize() { 6 | autosize(this.element); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/javascript/controllers/flash-toast-dispatch-controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | import { EventDispatcher } from "../utils/event-dispatcher"; 4 | 5 | export default class extends Controller { 6 | static values = { 7 | messageHtml: String, 8 | type: String, 9 | }; 10 | 11 | declare readonly messageHtmlValue: string; 12 | declare readonly typeValue: string; 13 | 14 | connect() { 15 | new EventDispatcher("flash-toast:show", { 16 | type: this.typeValue, 17 | messageHtml: this.messageHtmlValue, 18 | }).dispatch(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/javascript/controllers/modal-controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class extends Controller { 4 | connect() { 5 | this.element.showModal(); 6 | } 7 | 8 | close(event: CustomEvent) { 9 | // モーダルの中でリンクカードを追加したときモーダルを閉じないようにする 10 | if (event.target.dataset.action !== "turbo:submit-end->modal#close") { 11 | return; 12 | } 13 | 14 | if (event.detail.success) { 15 | this.element.close(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/javascript/utils/event-dispatcher.ts: -------------------------------------------------------------------------------- 1 | export class EventDispatcher { 2 | event!: CustomEvent; 3 | 4 | constructor(eventName: string, detail: any = {}) { 5 | this.event = new CustomEvent(eventName, { 6 | detail, 7 | }); 8 | } 9 | 10 | dispatch() { 11 | document.dispatchEvent(this.event); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/jobs/add_followed_profile_posts_to_timeline_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class AddFollowedProfilePostsToTimelineJob < ApplicationJob 5 | sig { params(source_profile_id: T::Mewst::DatabaseId, target_profile_id: T::Mewst::DatabaseId).void } 6 | def perform(source_profile_id:, target_profile_id:) 7 | source_profile = Profile.kept.find(source_profile_id) 8 | target_profile = Profile.kept.find(target_profile_id) 9 | 10 | AddFollowedProfilePostsToTimelineUseCase.new.call(source_profile:, target_profile:) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/jobs/add_post_to_timeline_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class AddPostToTimelineJob < ApplicationJob 5 | queue_with_priority PRIORITY.fetch(:high) 6 | 7 | sig { params(profile_id: T::Mewst::DatabaseId, post_id: T::Mewst::DatabaseId).void } 8 | def perform(profile_id:, post_id:) 9 | profile = Profile.kept.find(profile_id) 10 | post = Post.find(post_id) 11 | 12 | profile.home_timeline.add_post!(post:) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationJob < ActiveJob::Base 5 | extend T::Sig 6 | 7 | PRIORITY = T.let({ 8 | high: 0, 9 | medium: 1, 10 | low: 2 11 | }.freeze, T::Hash[Symbol, Integer]) 12 | 13 | queue_as :default 14 | 15 | queue_with_priority PRIORITY.fetch(:medium) 16 | 17 | retry_on StandardError, wait: :exponentially_longer, attempts: 5 18 | end 19 | -------------------------------------------------------------------------------- /app/jobs/create_suggested_follows_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class CreateSuggestedFollowsJob < ApplicationJob 5 | sig { params(source_profile_id: T::Mewst::DatabaseId).void } 6 | def perform(source_profile_id:) 7 | source_profile = Profile.kept.find(source_profile_id) 8 | 9 | CreateSuggestedFollowsUseCase.new.call(source_profile:) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/jobs/delete_post_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DeletePostJob < ApplicationJob 5 | queue_with_priority PRIORITY.fetch(:low) 6 | 7 | sig { params(target_post_id: T::Mewst::DatabaseId).void } 8 | def perform(target_post_id:) 9 | form = DeletePostForm.new(target_post_id: target_post_id) 10 | 11 | if form.valid? 12 | DeletePostUseCase.new.call(target_post: form.target_post.not_nil!) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/jobs/fanout_post_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class FanoutPostJob < ApplicationJob 5 | queue_with_priority PRIORITY.fetch(:high) 6 | 7 | sig { params(post_id: T::Mewst::DatabaseId).void } 8 | def perform(post_id:) 9 | post = Post.find(post_id) 10 | 11 | FanoutPostUseCase.new.call(post:) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/jobs/remove_followed_profile_posts_from_timeline_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class RemoveFollowedProfilePostsFromTimelineJob < ApplicationJob 5 | sig { params(source_profile_id: T::Mewst::DatabaseId, target_profile_id: T::Mewst::DatabaseId).void } 6 | def perform(source_profile_id:, target_profile_id:) 7 | source_profile = Profile.kept.find(source_profile_id) 8 | target_profile = Profile.kept.find(target_profile_id) 9 | 10 | RemoveFollowedProfilePostsFromTimelineUseCase.new.call(source_profile:, target_profile:) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationMailer < ActionMailer::Base 5 | extend T::Sig 6 | 7 | default from: "Mewst " 8 | end 9 | -------------------------------------------------------------------------------- /app/mailers/email_confirmation_mailer.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmationMailer < ApplicationMailer 5 | sig { params(email_confirmation_id: T::Mewst::DatabaseId, locale: String).void } 6 | def email_confirmation(email_confirmation_id:, locale:) 7 | email_confirmation = EmailConfirmation.active.find(email_confirmation_id) 8 | 9 | @email = email_confirmation.email 10 | @code = email_confirmation.code 11 | 12 | I18n.with_locale(locale) do 13 | subject = default_i18n_subject 14 | mail(to: @email, subject:) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationRecord < ActiveRecord::Base 5 | extend T::Sig 6 | 7 | primary_abstract_class 8 | end 9 | -------------------------------------------------------------------------------- /app/models/email_confirmation_event.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmationEvent < T::Enum 5 | enums do 6 | EmailUpdate = new("email_update") 7 | PasswordReset = new("password_reset") 8 | SignUp = new("sign_up") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/follow.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Follow < ApplicationRecord 5 | belongs_to :source_profile, class_name: "Profile" 6 | belongs_to :target_profile, class_name: "Profile" 7 | 8 | sig { void } 9 | def check_suggested! 10 | SuggestedFollow.find_by(source_profile:, target_profile:)&.touch(:checked_at) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/gravatar.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Gravatar 5 | extend T::Sig 6 | 7 | sig { params(email: String).void } 8 | def initialize(email:) 9 | @email = email 10 | end 11 | 12 | sig { params(size: Integer).returns(String) } 13 | def url(size:) 14 | return "" if email.blank? 15 | 16 | params = URI.encode_www_form("s" => size) 17 | "https://www.gravatar.com/avatar/#{hash}?#{params}" 18 | end 19 | 20 | sig { returns(String) } 21 | private def hash 22 | Digest::SHA256.hexdigest(email) 23 | end 24 | 25 | sig { returns(String) } 26 | attr_reader :email 27 | private :email 28 | end 29 | -------------------------------------------------------------------------------- /app/models/home_timeline_post.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class HomeTimelinePost < ApplicationRecord 5 | belongs_to :post 6 | belongs_to :profile 7 | 8 | scope :visible, -> { where(published_at: 1.month.ago..) } 9 | scope :invisible, -> { where(published_at: ...1.month.ago) } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/link.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Link < ApplicationRecord 5 | end 6 | -------------------------------------------------------------------------------- /app/models/locale.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Locale < T::Enum 5 | enums do 6 | En = new 7 | Ja = new 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/models/model_concerns/notifiable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ModelConcerns::Notifiable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | has_one :notification, as: :notifiable, dependent: :restrict_with_exception 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/notifiable_type.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class NotifiableType < T::Enum 5 | enums do 6 | Stamp = new("Stamp") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/models/notification.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Notification < ApplicationRecord 5 | extend Enumerize 6 | 7 | delegated_type :notifiable, types: ( 8 | NotifiableType.values.map(&:serialize) + 9 | # HACK: 2つ以上のモデル名を指定しないと型エラーになるため、notifiable では必要ないがNilClassを追加している 10 | # https://wikino.app/s/shimbaco/pages/345 11 | ["NilClass"] 12 | ) 13 | 14 | belongs_to :source_profile, class_name: "Profile" 15 | belongs_to :target_profile, class_name: "Profile" 16 | 17 | sig { returns(NotifiableType) } 18 | def deserialized_notifiable_type 19 | NotifiableType.deserialize(notifiable_type) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/oauth_access_token.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class OauthAccessToken < Doorkeeper::AccessToken 5 | extend T::Sig 6 | 7 | belongs_to :resource_owner, class_name: "Actor" 8 | end 9 | -------------------------------------------------------------------------------- /app/models/oauth_application.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class OauthApplication < Doorkeeper::Application 5 | extend T::Sig 6 | 7 | MEWST_WEB_UID = "mewst-web" 8 | 9 | sig { returns(OauthApplication) } 10 | def self.mewst_web 11 | find_by!(uid: MEWST_WEB_UID) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/post_link.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class PostLink < ApplicationRecord 5 | belongs_to :post 6 | belongs_to :link 7 | end 8 | -------------------------------------------------------------------------------- /app/models/profile/postability.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Profile::Postability 5 | extend T::Sig 6 | 7 | sig { params(profile: Profile).void } 8 | def initialize(profile:) 9 | @profile = profile 10 | end 11 | 12 | sig { params(post: Post).void } 13 | def delete_post(post:) 14 | ActiveRecord::Base.transaction do 15 | post.destroy! 16 | end 17 | end 18 | 19 | sig { returns(Profile) } 20 | attr_reader :profile 21 | private :profile 22 | end 23 | -------------------------------------------------------------------------------- /app/models/profile_avatar_kind.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ProfileAvatarKind < T::Enum 5 | enums do 6 | Default = new("default") 7 | External = new("external") 8 | Gravatar = new("gravatar") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/profile_owner_type.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ProfileOwnerType < T::Enum 5 | enums do 6 | User = new("User") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/models/session.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Session < ApplicationRecord 5 | COOKIE_KEY = :mewst_session_token 6 | 7 | has_secure_token 8 | 9 | belongs_to :actor 10 | 11 | sig { params(ip_address: T.nilable(String), user_agent: T.nilable(String), signed_in_at: ActiveSupport::TimeWithZone).returns(Session) } 12 | def self.start!(ip_address:, user_agent:, signed_in_at: Time.zone.now) 13 | create!( 14 | ip_address: ip_address || "", 15 | user_agent: user_agent || "", 16 | signed_in_at: 17 | ) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/sign_up_stopper.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class SignUpStopper 5 | extend T::Sig 6 | 7 | sig { returns(T::Boolean) } 8 | def self.enabled? 9 | Rails.configuration.mewst["sign_up_stopper_enabled"] == true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/stamp.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Stamp < ApplicationRecord 5 | include ModelConcerns::Notifiable 6 | 7 | belongs_to :post 8 | belongs_to :profile 9 | 10 | sig { void } 11 | def notify! 12 | Notification.where( 13 | source_profile: profile, 14 | target_profile: post.not_nil!.profile, 15 | notifiable: self 16 | ).first_or_create!(notified_at: Time.current) 17 | 18 | nil 19 | end 20 | 21 | sig { void } 22 | def unnotify! 23 | notification&.destroy! 24 | 25 | nil 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/models/suggested_follow.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class SuggestedFollow < ApplicationRecord 5 | belongs_to :source_profile, class_name: "Profile" 6 | belongs_to :target_profile, class_name: "Profile" 7 | 8 | scope :not_checked, -> { where(checked_at: nil) } 9 | end 10 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class User < ApplicationRecord 5 | extend Enumerize 6 | 7 | PASSWORD_MIN_LENGTH = 8 8 | 9 | enumerize :locale, in: I18n.available_locales 10 | 11 | has_many :actors, dependent: :restrict_with_exception 12 | has_one :user_profile, dependent: :restrict_with_exception 13 | has_one :profile, through: :user_profile 14 | 15 | has_secure_password 16 | 17 | validates :email, presence: true, uniqueness: true 18 | 19 | sig { returns(Actor) } 20 | def first_actor 21 | actors.find_by!(profile:) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/user_profile.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class UserProfile < ApplicationRecord 5 | belongs_to :user 6 | belongs_to :profile 7 | end 8 | -------------------------------------------------------------------------------- /app/models/v1/response_error_code.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::ResponseErrorCode < T::Enum 5 | enums do 6 | InvalidInputData = new("invalid_input_data") 7 | NotFound = new("not_found") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/policies/post_policy.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class PostPolicy < ApplicationPolicy 5 | sig { returns(T::Boolean) } 6 | def destroy? 7 | profile == T.cast(record, Post).profile 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/policies/profile_policy.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ProfilePolicy < ApplicationPolicy 5 | sig { returns(T::Boolean) } 6 | def show? 7 | profile == T.cast(record, Post).profile 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/resources/concerns/resource_concerns/error_responsable.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ResourceConcerns::ErrorResponsable 5 | extend T::Sig 6 | extend T::Helpers 7 | 8 | interface! 9 | 10 | sig { abstract.returns(V1::ResponseErrorCode) } 11 | def code 12 | end 13 | 14 | sig { abstract.returns(String) } 15 | def message 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/resources/v1/application_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::ApplicationResource 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /app/resources/v1/email_confirmation_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::EmailConfirmationResource < V1::ApplicationResource 5 | delegate :id, :email, :event, to: :email_confirmation 6 | 7 | sig { params(email_confirmation: EmailConfirmation).void } 8 | def initialize(email_confirmation:) 9 | @email_confirmation = email_confirmation 10 | end 11 | 12 | sig { returns(T.nilable(String)) } 13 | def succeeded_at 14 | email_confirmation.succeeded_at&.iso8601 15 | end 16 | 17 | sig { returns(EmailConfirmation) } 18 | attr_reader :email_confirmation 19 | private :email_confirmation 20 | end 21 | -------------------------------------------------------------------------------- /app/resources/v1/follow_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::FollowFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :target_atname 9 | "atname" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/post_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::PostFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :target_post 9 | "post_id" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/response_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::ResponseErrorResource < V1::ApplicationResource 5 | include ResourceConcerns::ErrorResponsable 6 | 7 | sig { override.returns(String) } 8 | attr_reader :message 9 | 10 | sig { override.returns(V1::ResponseErrorCode) } 11 | attr_reader :code 12 | 13 | sig { params(code: V1::ResponseErrorCode, message: String).void } 14 | def initialize(code:, message:) 15 | @code = code 16 | @message = message 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/resources/v1/session_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::SessionFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :base 9 | "email_or_password" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/stamp_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::StampFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :target_post 9 | "post_id" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/suggested_follow_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::SuggestedFollowFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :target_profile 9 | "atname" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/unfollow_form_error_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::UnfollowFormErrorResource < V1::FormErrorResource 5 | sig { override.returns(T.nilable(String)) } 6 | def field 7 | case error.attribute 8 | when :target_profile 9 | "atname" 10 | else 11 | error.attribute.to_s 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/resources/v1/user_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::UserResource < V1::ApplicationResource 5 | delegate :id, :locale, :time_zone, to: :user 6 | 7 | sig { params(user: User).void } 8 | def initialize(user:) 9 | @user = user 10 | end 11 | 12 | sig { returns(User) } 13 | attr_reader :user 14 | private :user 15 | end 16 | -------------------------------------------------------------------------------- /app/resources/v1/via_resource.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::ViaResource < V1::ApplicationResource 5 | delegate :name, to: :oauth_application 6 | 7 | sig { params(oauth_application: OauthApplication).void } 8 | def initialize(oauth_application:) 9 | @oauth_application = oauth_application 10 | end 11 | 12 | sig { returns(OauthApplication) } 13 | attr_reader :oauth_application 14 | private :oauth_application 15 | end 16 | -------------------------------------------------------------------------------- /app/serializers/v1/account_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::AccountSerializer < V1::ApplicationSerializer 5 | root_key :account 6 | 7 | one :oauth_access_token, resource: V1::OauthAccessTokenSerializer 8 | one :profile, resource: V1::ProfileSerializer, &:profile_resource 9 | one :user, resource: V1::UserSerializer 10 | end 11 | -------------------------------------------------------------------------------- /app/serializers/v1/application_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::ApplicationSerializer 5 | extend T::Sig 6 | 7 | include Alba::Resource 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/email_confirmation_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::EmailConfirmationSerializer < V1::ApplicationSerializer 5 | root_key :email_confirmation, :email_confirmations 6 | 7 | attributes :id, :email, :event, :succeeded_at 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/form_error_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::FormErrorSerializer < V1::ApplicationSerializer 5 | root_key :error, :errors 6 | 7 | attributes :code, :field, :message 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/notification_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::NotificationSerializer < V1::ApplicationSerializer 5 | root_key :notification, :notifications 6 | 7 | attributes :id, :kind, :notified_at 8 | 9 | one :source_profile, resource: V1::ProfileSerializer 10 | 11 | one :item, resource: ->(resource) { 12 | case resource 13 | when V1::StampNotificationItemResource 14 | V1::StampNotificationItemSerializer 15 | else 16 | raise "Unknown resource class: #{resource.class.inspect}" 17 | end 18 | } 19 | end 20 | -------------------------------------------------------------------------------- /app/serializers/v1/oauth_access_token_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::OauthAccessTokenSerializer < V1::ApplicationSerializer 5 | root_key :oauth_access_token, :oauth_access_tokens 6 | 7 | attributes :token 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/page_info_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::PageInfoSerializer < V1::ApplicationSerializer 5 | root_key :page_info 6 | 7 | attributes :end_cursor 8 | attributes :has_next_page 9 | attributes :has_previous_page 10 | attributes :start_cursor 11 | end 12 | -------------------------------------------------------------------------------- /app/serializers/v1/post_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::PostSerializer < V1::ApplicationSerializer 5 | root_key :post, :posts 6 | 7 | attributes :id, :content, :published_at, :viewer_has_stamped 8 | 9 | one :profile, resource: V1::ProfileSerializer 10 | one :via, resource: V1::ViaSerializer 11 | end 12 | -------------------------------------------------------------------------------- /app/serializers/v1/profile_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::ProfileSerializer < V1::ApplicationSerializer 5 | root_key :profile, :profiles 6 | 7 | attributes :id, :atname, :name, :description, :avatar_url, :viewer_has_followed 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/response_error_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::ResponseErrorSerializer < V1::ApplicationSerializer 5 | root_key :error, :errors 6 | 7 | attributes :code, :message 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/stamp_notification_item_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::StampNotificationItemSerializer < V1::ApplicationSerializer 5 | one :source_profile, resource: V1::ProfileSerializer 6 | one :target_post, resource: V1::PostSerializer 7 | end 8 | -------------------------------------------------------------------------------- /app/serializers/v1/user_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class V1::UserSerializer < V1::ApplicationSerializer 5 | root_key :user, :users 6 | 7 | attributes :id, :locale, :time_zone 8 | end 9 | -------------------------------------------------------------------------------- /app/serializers/v1/via_serializer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | class V1::ViaSerializer < V1::ApplicationSerializer 5 | attributes :name 6 | end 7 | -------------------------------------------------------------------------------- /app/use_cases/add_followed_profile_posts_to_timeline_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class AddFollowedProfilePostsToTimelineUseCase < ApplicationUseCase 5 | sig { params(source_profile: Profile, target_profile: Profile).void } 6 | def call(source_profile:, target_profile:) 7 | target_posts = target_profile.posts.kept.order(published_at: :desc).limit(30) 8 | 9 | ActiveRecord::Base.transaction do 10 | target_posts.each do |post| 11 | source_profile.home_timeline.add_post!(post:) 12 | end 13 | end 14 | 15 | nil 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/use_cases/application_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ApplicationUseCase 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /app/use_cases/check_suggested_follow_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class CheckSuggestedFollowUseCase < ApplicationUseCase 5 | sig { params(source_profile: Profile, target_profile: Profile).void } 6 | def call(source_profile:, target_profile:) 7 | suggested_follow = source_profile.suggested_follows.find_by(target_profile:) 8 | return unless suggested_follow 9 | 10 | suggested_follow.touch(:checked_at) 11 | 12 | nil 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/use_cases/confirm_email_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class ConfirmEmailUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :email_confirmation, EmailConfirmation 7 | end 8 | 9 | sig { params(viewer: T.nilable(Actor), email_confirmation: EmailConfirmation).returns(Result) } 10 | def call(viewer:, email_confirmation:) 11 | ActiveRecord::Base.transaction do 12 | email_confirmation.success! 13 | email_confirmation.process_after_success!(viewer:) 14 | end 15 | 16 | Result.new(email_confirmation:) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/use_cases/create_link_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class CreateLinkUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :link, Link 7 | end 8 | 9 | sig { params(canonical_url: String, domain: String, title: String, image_url: String).returns(Result) } 10 | def call(canonical_url:, domain:, title:, image_url:) 11 | link = Link.new(canonical_url:, domain:, title:, image_url:) 12 | 13 | link.save! 14 | 15 | Result.new(link:) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/use_cases/create_session_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class CreateSessionUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :session, Session 7 | end 8 | 9 | sig { params(actor: Actor, ip_address: T.nilable(String), user_agent: T.nilable(String)).returns(Result) } 10 | def call(actor:, ip_address:, user_agent:) 11 | session = ActiveRecord::Base.transaction do 12 | actor.sessions.start!(ip_address:, user_agent:) 13 | end 14 | 15 | Result.new(session:) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/use_cases/create_suggested_follows_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class CreateSuggestedFollowsUseCase < ApplicationUseCase 5 | sig { params(source_profile: Profile).void } 6 | def call(source_profile:) 7 | source_profile.create_suggested_follows! 8 | 9 | nil 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/use_cases/delete_post_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DeletePostUseCase < ApplicationUseCase 5 | sig { params(target_post: Post).void } 6 | def call(target_post:) 7 | ActiveRecord::Base.transaction do 8 | target_post.stamps.find_each do |stamp| 9 | stamp.notification&.destroy! 10 | stamp.reload.destroy! 11 | end 12 | 13 | target_post.home_timeline_posts.find_each(&:destroy!) 14 | target_post.post_link&.destroy! 15 | target_post.reload.destroy! 16 | end 17 | 18 | nil 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/use_cases/delete_stamp_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DeleteStampUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :post, Post 7 | end 8 | 9 | sig { params(viewer: Actor, target_post: Post).returns(Result) } 10 | def call(viewer:, target_post:) 11 | stamp = viewer.stamps.find_by(post: target_post) 12 | 13 | ApplicationRecord.transaction do 14 | stamp&.unnotify! 15 | stamp&.reload&.destroy! 16 | end 17 | 18 | Result.new(post: target_post.reload) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/use_cases/discard_post_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class DiscardPostUseCase < ApplicationUseCase 5 | sig { params(target_post: Post).void } 6 | def call(target_post:) 7 | ApplicationRecord.transaction do 8 | target_post.discard! 9 | DeletePostJob.perform_later(target_post_id: target_post.id.not_nil!) 10 | end 11 | 12 | nil 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/use_cases/fanout_post_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class FanoutPostUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :post, Post 7 | end 8 | 9 | sig { params(post: Post).returns(Result) } 10 | def call(post:) 11 | followers = post.profile.not_nil!.followers 12 | 13 | batch = GoodJob::Batch.new 14 | batch.add do 15 | followers.find_each do |follower| 16 | AddPostToTimelineJob.perform_later(profile_id: follower.id.not_nil!, post_id: post.id.not_nil!) 17 | end 18 | end 19 | batch.enqueue 20 | 21 | Result.new(post:) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/use_cases/remove_followed_profile_posts_from_timeline_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class RemoveFollowedProfilePostsFromTimelineUseCase < ApplicationUseCase 5 | sig { params(source_profile: Profile, target_profile: Profile).void } 6 | def call(source_profile:, target_profile:) 7 | source_profile.home_timeline_posts.joins(:post).merge(target_profile.posts).find_each do |home_timeline_post| 8 | home_timeline_post.destroy! 9 | end 10 | 11 | nil 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/use_cases/update_password_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class UpdatePasswordUseCase < ApplicationUseCase 5 | sig { params(email: String, password: String).void } 6 | def call(email:, password:) 7 | User.find_by!(email:).update!(password:) 8 | 9 | nil 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/use_cases/update_user_use_case.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class UpdateUserUseCase < ApplicationUseCase 5 | class Result < T::Struct 6 | const :user, User 7 | end 8 | 9 | sig { params(viewer: Actor, locale: String, time_zone: String).returns(Result) } 10 | def call(viewer:, locale:, time_zone:) 11 | viewer.user.not_nil!.update!(locale:, time_zone:) 12 | 13 | Result.new(user: viewer.user.not_nil!) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/validators/url_validator.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class UrlValidator < ActiveModel::EachValidator 5 | extend T::Sig 6 | 7 | sig { params(record: ApplicationForm, attribute: Symbol, value: String).void } 8 | def validate_each(record, attribute, value) 9 | return if value.blank? 10 | 11 | unless Url.new(value).valid? 12 | record.errors.add(attribute, :url) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/checks/create/call.html.erb: -------------------------------------------------------------------------------- 1 | <% if @form.errors.any? %> 2 | 3 | 6 | 7 | <% else %> 8 | 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/email_confirmation_mailer/email_confirmation.en.html.erb: -------------------------------------------------------------------------------- 1 | Hello, <%= @email %>.
2 |
3 | The confirmation code is as follows. 4 |
5 | <%= @code %>
6 |
7 | The confirmation code is valid for 15 minutes.
8 |
9 | If this email does not concern you, please ignore it.
10 |
11 | Mewst
12 | <%= link_to ENV.fetch("MEWST_URL"), ENV.fetch("MEWST_URL") %> 13 | -------------------------------------------------------------------------------- /app/views/email_confirmation_mailer/email_confirmation.ja.html.erb: -------------------------------------------------------------------------------- 1 | <%= @email %> さん、こんにちは。
2 |
3 | 確認用コードは下記になります。
4 |
5 | <%= @code %>
6 |
7 | 確認用コードの有効期間は15分です。
8 |
9 | もしこのメールに心当たりが無い場合は無視してください。
10 |
11 | Mewst
12 | <%= link_to ENV.fetch("MEWST_URL"), ENV.fetch("MEWST_URL") %> 13 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/links/create/call.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | <%= render Cards::LinkCardComponent.new(link: @link) %> 5 | 6 | 12 |
13 | 14 | <%= hidden_field_tag "post_form[canonical_url]", @link.canonical_url %> 15 |
16 |
17 | -------------------------------------------------------------------------------- /app/views/links/new/call.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= form_with model: @form, url: link_list_path do |f| %> 3 | <%= render Cards::FormErrorsCardComponent.new(errors: f.object.errors) %> 4 | 5 | <%= f.hidden_field :target_url %> 6 | 7 | <%= f.button( 8 | class: "btn btn-ghost btn-xs no-animation rounded-full textarea-bordered", 9 | type: :submit 10 | ) do %> 11 | <%= t("verbs.add_link_card", host_and_path: @host_and_path) %> 12 | <% end %> 13 | <% end %> 14 | 15 | -------------------------------------------------------------------------------- /app/views/posts/create/call.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/posts/new/call.html.erb: -------------------------------------------------------------------------------- 1 | <% title = t("meta.title.posts.new") %> 2 | <% set_meta_tags(title:) %> 3 | 4 | <%= render ::Layouts::BasicLayoutComponent.new(with_footer: false, main_class_name: "grid place-items-center") do %> 5 | <%= render Containers::BasicContainerComponent.new do %> 6 | <% if @form.with_frame %> 7 | 8 | <%= render Modals::PostFormModalComponent.new(form: @form) %> 9 | 10 | <% else %> 11 | <%= render Forms::PostFormComponent.new(form: @form) %> 12 | <% end %> 13 | <% end %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/stamps/create/call.html.erb: -------------------------------------------------------------------------------- 1 | <% if @form.errors.any? %> 2 | 3 | 6 | 7 | <% else %> 8 | 9 | 12 | 13 | <% end %> 14 | -------------------------------------------------------------------------------- /bin/check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def system!(*args) 4 | system(*args, exception: true) 5 | end 6 | 7 | [ 8 | "bin/rails zeitwerk:check", 9 | "bin/rails sorbet:update", 10 | "yarn prettier . --write", 11 | "bin/erb_lint --lint-all", 12 | "bin/standardrb", 13 | "bin/srb tc", 14 | "bin/rspec" 15 | ].each do |command| 16 | puts "=== `#{command}` を実行中..." 17 | system!(command) 18 | end 19 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if ! foreman version &> /dev/null 4 | then 5 | echo "Installing foreman..." 6 | gem install foreman 7 | fi 8 | 9 | foreman start -f Procfile.dev "$@" 10 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # typed: false 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require_relative "config/environment" 6 | 7 | run Rails.application 8 | Rails.application.load_server 9 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 5 | 6 | require "bundler/setup" # Set up gems listed in the Gemfile. 7 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 8 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | Et2zgyIQbrHxgJdzZwfsXvyob+t+Nc7iJ41haR2UcAV0oZvGR0QyXLry8w6wM08f/JA7u0QnUsIO2zW6+VpScvLC/PQJet3fdRX0LdqodDOVXriFc+23W5kmvibl4j0u6YTlQG5pbe7/R27KmY9Q8X1nTtrS8QGLgC8G/aDWLyitDad14/p0elVZycbqfAXuIGyXIWiYbjjrZv3nPwTiu4YcGAzrh+q1FTSdvF9q0vbNVem/9dWhot8tzt8WWcVgJMEjtQcL2RJ8/53eg1brWJuZSbtID27HG4T/JpPkhfT4CsvUHzK2/9mTjTU48ClkJzveHX/pKWrxa8aI7e15Vf1gN4muwxn0Lb7gfXRQNj0u5ZKMsfm+BAhVJgPA8yEGk82vXHE+OSFyPW2MvWDvJt+ryTqSx0HfmzXo--zVVC6t6rowoy67J8--NsgEHE4XFy73eRCDUDWWjQ== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | # For details on connection pooling, see rails configuration guide 5 | # http://guides.rubyonrails.org/configuring.html#database-pooling 6 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 7 | url: <%= ENV.fetch("DATABASE_URL") %> 8 | 9 | development: 10 | <<: *default 11 | 12 | test: 13 | <<: *default 14 | 15 | production: 16 | <<: *default 17 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | # Load the Rails application. 5 | require_relative "application" 6 | 7 | # Initialize the Rails application. 8 | Rails.application.initialize! 9 | -------------------------------------------------------------------------------- /config/initializers/alba.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | Alba.backend = :oj_rails 5 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Version of your assets, change this if you want to expire all your assets. 6 | Rails.application.config.assets.version = "1.0" 7 | 8 | # Add additional assets to the asset load path. 9 | # Rails.application.config.assets.paths << Emoji.images_path 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/enable_yjit.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | # Automatically enable YJIT as of Ruby 3.3, as it brings very 5 | # sizeable performance improvements. 6 | 7 | # If you are deploying to a memory constrained environment 8 | # you may want to delete this file, but otherwise it's free 9 | # performance. 10 | if defined? RubyVM::YJIT.enable 11 | Rails.application.config.after_initialize do 12 | RubyVM::YJIT.enable 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | # Be sure to restart your server when you modify this file. 5 | 6 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 7 | # Use this to limit dissemination of sensitive information. 8 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 9 | Rails.application.config.filter_parameters += [ 10 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 11 | ] 12 | -------------------------------------------------------------------------------- /config/initializers/good_job.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | Rails.application.configure do 5 | config.good_job.max_threads = 3 6 | config.good_job.queues = "*" 7 | # https://github.com/bensheldon/good_job/tree/00f80cb127df622234c2a899e963367cc1b387df#job-priority 8 | config.good_job.smaller_number_is_higher_priority = true 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/inline_svg.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | InlineSvg.configure do |config| 5 | config.asset_finder = InlineSvg::PropshaftAssetFinder 6 | end 7 | -------------------------------------------------------------------------------- /config/initializers/not_nil.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module Kernel 5 | def not_nil! 6 | self 7 | end 8 | end 9 | 10 | class NilClass 11 | def not_nil! 12 | raise TypeError.new("Expected not to be nil") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | # Be sure to restart your server when you modify this file. 5 | 6 | # Define an application-wide HTTP permissions policy. For further 7 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 8 | 9 | # Rails.application.config.permissions_policy do |policy| 10 | # policy.camera :none 11 | # policy.gyroscope :none 12 | # policy.microphone :none 13 | # policy.usb :none 14 | # policy.fullscreen :self 15 | # policy.payment :self, "https://secure.example.com" 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/resend.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | if Rails.env.production? 5 | Resend.api_key = Rails.configuration.mewst["resend_api_key"] 6 | end 7 | -------------------------------------------------------------------------------- /config/initializers/sentry.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | Sentry.init do |config| 5 | config.dsn = Rails.configuration.mewst["sentry_dsn"] 6 | config.breadcrumbs_logger = %i[active_support_logger http_logger] 7 | 8 | # Set traces_sample_rate to 1.0 to capture 100% 9 | # of transactions for performance monitoring. 10 | # We recommend adjusting this value in production. 11 | config.traces_sample_rate = 0.5 12 | 13 | # Set profiles_sample_rate to profile 100% 14 | # of sampled transactions. 15 | # We recommend adjusting this value in production. 16 | config.profiles_sample_rate = 0.5 17 | end 18 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | Rails.application.config.session_store :cookie_store, 5 | key: "_mewst_session", 6 | expire_after: 10.years 7 | -------------------------------------------------------------------------------- /config/initializers/sorbet.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | T::Configuration.enable_final_checks_on_hooks 5 | -------------------------------------------------------------------------------- /config/initializers/time_formats.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | Date::DATE_FORMATS[:ym] = "%Y-%m" 5 | Time::DATE_FORMATS[:ymd] = "%Y-%m-%d" 6 | Time::DATE_FORMATS[:ymdhm] = "%Y-%m-%d %H:%M" 7 | -------------------------------------------------------------------------------- /config/initializers/types.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module T::Mewst 5 | extend T::Sig 6 | 7 | DatabaseId = T.type_alias { String } 8 | end 9 | -------------------------------------------------------------------------------- /config/locales/actionmailer.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | email_confirmation_mailer: 3 | email_confirmation: 4 | subject: "[Mewst] Confirmation code" 5 | -------------------------------------------------------------------------------- /config/locales/actionmailer.ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | email_confirmation_mailer: 3 | email_confirmation: 4 | subject: "[Mewst] 確認用コード" 5 | -------------------------------------------------------------------------------- /config/locales/activerecord.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | activerecord: 3 | attributes: 4 | profile: 5 | atname: At name 6 | avatar_kinds: 7 | default: Default 8 | gravatar: Gravatar 9 | external: External site image 10 | image_url: Image URL 11 | gravatar_email: Gravatar email 12 | description: Description 13 | name: Name 14 | -------------------------------------------------------------------------------- /config/locales/activerecord.ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | activerecord: 3 | attributes: 4 | profile: 5 | atname: アット名 6 | avatar_kinds: 7 | default: デフォルト 8 | gravatar: Gravatar 9 | external: 外部サイトの画像 10 | image_url: 画像URL 11 | gravatar_email: Gravatarのメールアドレス 12 | description: 自己紹介 13 | name: 名前 14 | -------------------------------------------------------------------------------- /config/locales/errors.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | errors: 3 | messages: 4 | url: is not a URL 5 | -------------------------------------------------------------------------------- /config/locales/errors.ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | errors: 3 | messages: 4 | url: はURLではありません 5 | -------------------------------------------------------------------------------- /config/locales/verbs.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | verbs: 3 | add: Add 4 | add_link_card: "Add link card: %{host_and_path}" 5 | close: Close 6 | connect: Connect 7 | copy_link_to_post: Copy link to post 8 | copy_link_to_profile: Copy link to profile 9 | delete: Delete 10 | disconnect: Disconnect 11 | follow: Follow 12 | hide: Hide 13 | post: Post 14 | search: Search 15 | sign_in: Sign in 16 | sign_up: Create account 17 | submit: Submit 18 | update: Update 19 | -------------------------------------------------------------------------------- /config/locales/verbs.ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | verbs: 3 | add: 追加する 4 | add_link_card: "リンクカードを追加する: %{host_and_path}" 5 | close: 閉じる 6 | connect: 連携する 7 | copy_link_to_post: ポストへのリンクをコピー 8 | copy_link_to_profile: プロフィールへのリンクをコピー 9 | delete: 削除する 10 | disconnect: 連携解除する 11 | follow: フォローする 12 | hide: 非表示にする 13 | post: 投稿する 14 | search: 検索する 15 | sign_in: ログインする 16 | sign_up: アカウントを作成する 17 | submit: 送信する 18 | update: 更新する 19 | -------------------------------------------------------------------------------- /config/mewst.yml: -------------------------------------------------------------------------------- 1 | shared: 2 | community_url: <%= ENV["MEWST_COMMUNITY_URL"] %> 3 | email_domain: <%= ENV["MEWST_EMAIL_DOMAIN"] %> 4 | privacy_url: <%= ENV["MEWST_PRIVACY_URL"] %> 5 | resend_api_key: <%= ENV["MEWST_RESEND_API_KEY"] %> 6 | reserved_atnames: 7 | - admin 8 | - administrator 9 | - administrators 10 | - admins 11 | - atname 12 | - example 13 | - me 14 | - official 15 | - self 16 | sentry_dsn: <%= ENV["MEWST_SENTRY_DSN"] %> 17 | sign_up_stopper_enabled: <%= ENV["MEWST_SIGN_UP_STOPPER_ENABLED"] %> 18 | terms_url: <%= ENV["MEWST_TERMS_URL"] %> 19 | url: <%= ENV["MEWST_URL"] %> 20 | -------------------------------------------------------------------------------- /db/migrate/20231015050126_add_time_zone_to_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddTimeZoneToUsers < ActiveRecord::Migration[7.0] 4 | def change 5 | add_column :users, :time_zone, :string, null: false, default: "Etc/UTC" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20231025165318_add_discarded_at_to_posts_and_profiles.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddDiscardedAtToPostsAndProfiles < ActiveRecord::Migration[7.0] 4 | disable_ddl_transaction! 5 | 6 | def change 7 | %i[posts profiles].each do |table_name| 8 | add_column table_name, :discarded_at, :datetime 9 | add_index table_name, :discarded_at, algorithm: :concurrently 10 | end 11 | 12 | safety_assured do 13 | remove_column :profiles, :deleted_at # standard:disable Rails/ReversibleMigration 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20231107174301_rename_comment_to_content_on_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RenameCommentToContentOnPosts < ActiveRecord::Migration[7.0] 4 | def change 5 | StrongMigrations.disable_check(:rename_column) 6 | rename_column :posts, :comment, :content 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20231112022226_add_last_post_at_to_profiles.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddLastPostAtToProfiles < ActiveRecord::Migration[7.0] 4 | disable_ddl_transaction! 5 | 6 | def change 7 | add_column :profiles, :last_post_at, :datetime 8 | add_index :profiles, :last_post_at, algorithm: :concurrently 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20231113161740_add_via_id_to_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddViaIdToPosts < ActiveRecord::Migration[7.0] 4 | disable_ddl_transaction! 5 | 6 | def change 7 | add_column :posts, :oauth_application_id, :uuid 8 | add_index :posts, :oauth_application_id, algorithm: :concurrently 9 | add_foreign_key :posts, :oauth_applications, column: :oauth_application_id, validate: false 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20231113162310_add_validation_via_id_to_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddValidationViaIdToPosts < ActiveRecord::Migration[7.0] 4 | def change 5 | validate_foreign_key :posts, :oauth_applications 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20231115145714_add_null_false_to_oauth_application_id_on_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddNullFalseToOauthApplicationIdOnPosts < ActiveRecord::Migration[7.0] 4 | def change 5 | StrongMigrations.disable_check(:change_column_null_postgresql) 6 | change_column_null :posts, :oauth_application_id, false 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20231115172238_change_email_columns_to_citext.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ChangeEmailColumnsToCitext < ActiveRecord::Migration[7.0] 4 | def change 5 | StrongMigrations.disable_check(:change_column) 6 | # standard:disable Rails/ReversibleMigration 7 | change_column :email_confirmations, :email, :citext 8 | change_column :users, :email, :citext 9 | # standard:enable Rails/ReversibleMigration 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20231216042900_drop_follow_notifications.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DropFollowNotifications < ActiveRecord::Migration[7.1] 4 | def change 5 | drop_table :follow_notifications # standard:disable Rails/ReversibleMigration 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240115144958_create_good_job_settings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGoodJobSettings < ActiveRecord::Migration[7.1] 4 | def change 5 | reversible do |dir| 6 | dir.up do 7 | # Ensure this incremental update migration is idempotent 8 | # with monolithic install migration. 9 | return if connection.table_exists?(:good_job_settings) 10 | end 11 | end 12 | 13 | create_table :good_job_settings, id: :uuid do |t| 14 | t.timestamps 15 | t.text :key 16 | t.jsonb :value 17 | t.index :key, unique: true 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20240115144962_create_good_jobs_error_event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGoodJobsErrorEvent < ActiveRecord::Migration[7.1] 4 | def change 5 | reversible do |dir| 6 | dir.up do 7 | # Ensure this incremental update migration is idempotent 8 | # with monolithic install migration. 9 | return if connection.column_exists?(:good_jobs, :error_event) 10 | end 11 | end 12 | 13 | add_column :good_jobs, :error_event, :integer, limit: 2 14 | add_column :good_job_executions, :error_event, :integer, limit: 2 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20240115144964_create_good_job_labels.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGoodJobLabels < ActiveRecord::Migration[7.1] 4 | def change 5 | reversible do |dir| 6 | dir.up do 7 | # Ensure this incremental update migration is idempotent 8 | # with monolithic install migration. 9 | return if connection.column_exists?(:good_jobs, :labels) 10 | end 11 | end 12 | 13 | add_column :good_jobs, :labels, :text, array: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240324094358_create_home_timeline_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateHomeTimelinePosts < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :home_timeline_posts, id: false do |t| 6 | t.uuid :id, default: "generate_ulid()", null: false, primary_key: true 7 | t.references :profile, foreign_key: true, null: false, type: :uuid 8 | t.references :post, foreign_key: true, null: false, type: :uuid 9 | t.datetime :published_at, null: false 10 | 11 | t.index %i[profile_id post_id], unique: true 12 | t.index :published_at 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240403174827_create_sessions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateSessions < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :sessions, id: false do |t| 6 | t.uuid :id, default: "generate_ulid()", null: false, primary_key: true 7 | t.references :actor, foreign_key: true, null: false, type: :uuid 8 | t.string :token, index: {unique: true}, null: false 9 | t.string :ip_address, default: "", null: false 10 | t.string :user_agent, default: "", null: false 11 | t.datetime :signed_in_at, null: false 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240404162020_remove_sign_in_columns_on_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveSignInColumnsOnUsers < ActiveRecord::Migration[7.1] 4 | def change 5 | safety_assured do 6 | remove_column :users, :sign_in_count, :integer 7 | remove_column :users, :current_signed_in_at, :datetime 8 | remove_column :users, :last_signed_in_at, :datetime 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20240414152059_remove_stamps_count_on_posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveStampsCountOnPosts < ActiveRecord::Migration[7.1] 4 | def change 5 | safety_assured do 6 | remove_column :posts, :stamps_count, :integer 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20240414155158_create_links.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateLinks < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :links, id: false do |t| 6 | t.uuid :id, default: "generate_ulid()", null: false, primary_key: true 7 | t.string :canonical_url, index: {unique: true}, null: false 8 | t.string :domain, null: false 9 | t.string :title, null: false 10 | t.string :image_url, default: "", null: false 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20240414155325_create_post_links.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreatePostLinks < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :post_links, id: false do |t| 6 | t.uuid :id, default: "generate_ulid()", null: false, primary_key: true 7 | t.references :post, foreign_key: true, null: false, type: :uuid 8 | t.references :link, foreign_key: true, null: false, type: :uuid 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20240422190022_fix_notifications_for_delegated_type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class FixNotificationsForDelegatedType < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :notifications, :notifiable_id, :uuid, null: false 6 | drop_table :stamp_notifications # rubocop:disable Rails/ReversibleMigration 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20240427202315_add_gravatar_url_to_profiles.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddGravatarUrlToProfiles < ActiveRecord::Migration[7.1] 4 | def change 5 | StrongMigrations.disable_check(:rename_column) 6 | rename_column :profiles, :avatar_url, :image_url 7 | 8 | safety_assured do 9 | change_table :profiles, bulk: true do |t| 10 | t.string :gravatar_email, default: "", null: false 11 | t.string :gravatar_url, default: "", null: false 12 | t.string :avatar_kind, default: "default", null: false 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | OauthApplication.where(uid: OauthApplication::MEWST_WEB_UID).first_or_create!( 4 | name: "Mewst for Web", 5 | redirect_uri: "https://example.com/callback", 6 | scopes: "" 7 | ) 8 | -------------------------------------------------------------------------------- /lib/mewst/ui/base.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Base < ViewComponent::Base 5 | extend T::Sig 6 | end 7 | -------------------------------------------------------------------------------- /lib/mewst/ui/button.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_tag(as, class: class_names(class_name).presence, **options) do %> 2 | <%= content %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /lib/mewst/ui/button.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Button < Mewst::UI::Base 5 | sig { params(as: Symbol, class_name: String, options: T::Hash[Symbol, T.anything]).void } 6 | def initialize(as: :button, class_name: "", **options) 7 | @as = as 8 | @class_name = class_name 9 | @options = options 10 | end 11 | 12 | sig { returns(Symbol) } 13 | attr_reader :as 14 | private :as 15 | 16 | sig { returns(String) } 17 | attr_reader :class_name 18 | private :class_name 19 | 20 | sig { returns(T::Hash[Symbol, T.anything]) } 21 | attr_reader :options 22 | private :options 23 | end 24 | -------------------------------------------------------------------------------- /lib/mewst/ui/card.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.div(class: class_names("bg-base-300 card", class_name)) do %> 2 | <%= content %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /lib/mewst/ui/card.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Card < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/card/body.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.div(class: class_names("card-body p-4", class_name)) do %> 2 | <%= content %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /lib/mewst/ui/card/body.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Card::Body < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown.html.erb: -------------------------------------------------------------------------------- 1 |
" 3 | data-action="keydown.esc->dropdown#close" 4 | data-controller="dropdown" 5 | > 6 | <%= content %> 7 |
8 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Dropdown < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/button.html.erb: -------------------------------------------------------------------------------- 1 | 5 | <%= content %> 6 | 7 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/button.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Dropdown::Button < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/item.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.li( 2 | class: class_names(class_name).presence 3 | ) do %> 4 | <%= content %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/item.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Dropdown::Item < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/list.html.erb: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /lib/mewst/ui/dropdown/list.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Dropdown::List < Mewst::UI::Base 5 | sig { params(class_name: String).void } 6 | def initialize(class_name: "") 7 | @class_name = class_name 8 | end 9 | 10 | sig { returns(String) } 11 | attr_reader :class_name 12 | private :class_name 13 | end 14 | -------------------------------------------------------------------------------- /lib/mewst/ui/icon.html.erb: -------------------------------------------------------------------------------- 1 | <%= inline_svg_tag("icons/#{name}.svg", { 2 | class: icon_class_name, 3 | size:, 4 | }) %> 5 | -------------------------------------------------------------------------------- /lib/mewst/ui/tabs.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.div( 2 | class: class_names("tabs tabs-bordered", class_name), 3 | role: "tablist" 4 | ) do %> 5 | <% items.each do |item| %> 6 | <%= item %> 7 | <% end %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /lib/mewst/ui/tabs.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Tabs < Mewst::UI::Base 5 | renders_many :items, Mewst::UI::Tabs::Item 6 | 7 | sig { params(class_name: String).void } 8 | def initialize(class_name: "") 9 | @class_name = class_name 10 | end 11 | 12 | sig { returns(String) } 13 | attr_reader :class_name 14 | private :class_name 15 | end 16 | -------------------------------------------------------------------------------- /lib/mewst/ui/tabs/item.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.a( 2 | class: class_names("font-semibold h-12 tab", class_name, "tab-active": active, "text-gray-500": !active), 3 | href:, 4 | role: "tab" 5 | ) do %> 6 | <%= content %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /lib/mewst/ui/tabs/item.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | class Mewst::UI::Tabs::Item < Mewst::UI::Base 5 | sig { params(href: String, active: T::Boolean, class_name: String).void } 6 | def initialize(href:, active: false, class_name: "") 7 | @href = href 8 | @active = active 9 | @class_name = class_name 10 | end 11 | 12 | sig { returns(String) } 13 | attr_reader :href 14 | private :href 15 | 16 | sig { returns(T::Boolean) } 17 | attr_reader :active 18 | private :active 19 | 20 | sig { returns(String) } 21 | attr_reader :class_name 22 | private :class_name 23 | end 24 | -------------------------------------------------------------------------------- /lib/tasks/sorbet.rake: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | namespace :sorbet do 5 | task update: :environment do 6 | system "bin/tapioca gem" 7 | system "bin/tapioca dsl" 8 | system "bin/tapioca todo" 9 | system "bin/tapioca annotations" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/tasks/suggested_follow.rake: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | namespace :suggested_follow do 5 | desc "おすすめプロフィールを作成する" 6 | task create: :environment do 7 | # Note: limitに指定している数値に深い意味はない 8 | Profile.kept.sort_by_latest_post.limit(1_000).each do |source_profile| 9 | CreateSuggestedFollowsUseCase.new.call(source_profile:) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/public/favicon.ico -------------------------------------------------------------------------------- /public/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/public/icon-192.png -------------------------------------------------------------------------------- /public/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/public/icon-512.png -------------------------------------------------------------------------------- /public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mewstcom/mewst/3ac44bfbed0978ba6e61d5ca44658ea91323379c/public/og-image.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /sorbet/config: -------------------------------------------------------------------------------- 1 | --dir 2 | . 3 | --ignore=vendor/ 4 | -------------------------------------------------------------------------------- /sorbet/rbi/annotations/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-vendored=true 2 | -------------------------------------------------------------------------------- /sorbet/rbi/annotations/actionmailer.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This file was pulled from a central RBI files repository. 5 | # Please run `bin/tapioca annotations` to update it. 6 | 7 | class ActionMailer::Base 8 | sig { params(headers: T.untyped, block: T.nilable(T.proc.params(arg0: ActionMailer::Collector).void)).returns(Mail::Message) } 9 | def mail(headers = nil, &block); end 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/annotations/faraday.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This file was pulled from a central RBI files repository. 5 | # Please run `bin/tapioca annotations` to update it. 6 | 7 | module Faraday 8 | class << self 9 | sig { params(url: T.untyped, options: T::Hash[Symbol, T.untyped], block: T.nilable(T.proc.params(connection: Faraday::Connection).void)).returns(Faraday::Connection) } 10 | def new(url = nil, options = {}, &block); end 11 | end 12 | end 13 | 14 | class Faraday::Response 15 | sig { returns(T::Boolean) } 16 | def success?; end 17 | end 18 | -------------------------------------------------------------------------------- /sorbet/rbi/annotations/pundit.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This file was pulled from a central RBI files repository. 5 | # Please run `bin/tapioca annotations` to update it. 6 | 7 | module Pundit::Authorization 8 | sig { void } 9 | def skip_authorization; end 10 | 11 | sig { void } 12 | def skip_policy_scope; end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-generated=true 2 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/abstract_controller/rendering.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `AbstractController::Rendering`. 5 | # Please instead update this file by running `bin/tapioca dsl AbstractController::Rendering`. 6 | 7 | 8 | module AbstractController::Rendering 9 | mixes_in_class_methods ::ActionView::ViewPaths::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_controller/api_rendering.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionController::ApiRendering`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionController::ApiRendering`. 6 | 7 | 8 | module ActionController::ApiRendering 9 | mixes_in_class_methods ::ActionView::ViewPaths::ClassMethods 10 | mixes_in_class_methods ::ActionView::Rendering::ClassMethods 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_controller/data_streaming.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionController::DataStreaming`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionController::DataStreaming`. 6 | 7 | 8 | module ActionController::DataStreaming 9 | mixes_in_class_methods ::ActionController::Rendering::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_controller/flash.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionController::Flash`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionController::Flash`. 6 | 7 | 8 | module ActionController::Flash 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _flash_types; end 15 | def _flash_types=(value); end 16 | def _flash_types?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_controller/form_builder.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionController::FormBuilder`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionController::FormBuilder`. 6 | 7 | 8 | module ActionController::FormBuilder 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _default_form_builder; end 15 | def _default_form_builder=(value); end 16 | def _default_form_builder?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_dispatch/assertions.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionDispatch::Assertions`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionDispatch::Assertions`. 6 | 7 | 8 | module ActionDispatch::Assertions 9 | mixes_in_class_methods ::ActionDispatch::Assertions::RoutingAssertions::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_dispatch/integration_test.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionDispatch::IntegrationTest`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionDispatch::IntegrationTest`. 6 | 7 | 8 | class ActionDispatch::IntegrationTest 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_mailer/callbacks.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionMailer::Callbacks`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionMailer::Callbacks`. 6 | 7 | 8 | module ActionMailer::Callbacks 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def __callbacks; end 15 | def __callbacks=(value); end 16 | def __callbacks?; end 17 | end 18 | 19 | module GeneratedInstanceMethods 20 | def __callbacks; end 21 | def __callbacks?; end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_mailer/form_builder.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionMailer::FormBuilder`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionMailer::FormBuilder`. 6 | 7 | 8 | module ActionMailer::FormBuilder 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _default_form_builder; end 15 | def _default_form_builder=(value); end 16 | def _default_form_builder?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_view/helpers.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionView::Helpers`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionView::Helpers`. 6 | 7 | 8 | module ActionView::Helpers 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | 12 | mixes_in_class_methods ::ActionView::Helpers::UrlHelper::ClassMethods 13 | mixes_in_class_methods ::ActionView::Helpers::SanitizeHelper::ClassMethods 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_view/helpers/form_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionView::Helpers::FormHelper`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionView::Helpers::FormHelper`. 6 | 7 | 8 | module ActionView::Helpers::FormHelper 9 | mixes_in_class_methods ::ActionView::Helpers::UrlHelper::ClassMethods 10 | mixes_in_class_methods ::ActionView::Helpers::SanitizeHelper::ClassMethods 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_view/helpers/form_tag_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionView::Helpers::FormTagHelper`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionView::Helpers::FormTagHelper`. 6 | 7 | 8 | module ActionView::Helpers::FormTagHelper 9 | mixes_in_class_methods ::ActionView::Helpers::UrlHelper::ClassMethods 10 | mixes_in_class_methods ::ActionView::Helpers::SanitizeHelper::ClassMethods 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_view/helpers/text_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionView::Helpers::TextHelper`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionView::Helpers::TextHelper`. 6 | 7 | 8 | module ActionView::Helpers::TextHelper 9 | mixes_in_class_methods ::ActionView::Helpers::SanitizeHelper::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/action_view/rendering.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActionView::Rendering`. 5 | # Please instead update this file by running `bin/tapioca dsl ActionView::Rendering`. 6 | 7 | 8 | module ActionView::Rendering 9 | mixes_in_class_methods ::ActionView::ViewPaths::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_job/logging.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveJob::Logging`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveJob::Logging`. 6 | 7 | 8 | module ActiveJob::Logging 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def log_arguments; end 15 | def log_arguments=(value); end 16 | def log_arguments?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_job/queue_adapter.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveJob::QueueAdapter`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveJob::QueueAdapter`. 6 | 7 | 8 | module ActiveJob::QueueAdapter 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _queue_adapter; end 15 | def _queue_adapter=(value); end 16 | def _queue_adapter_name; end 17 | def _queue_adapter_name=(value); end 18 | end 19 | 20 | module GeneratedInstanceMethods; end 21 | end 22 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_job/queue_priority.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveJob::QueuePriority`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveJob::QueuePriority`. 6 | 7 | 8 | module ActiveJob::QueuePriority 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def priority; end 15 | def priority=(value); end 16 | def priority?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_job/test_helper/test_queue_adapter.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveJob::TestHelper::TestQueueAdapter`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveJob::TestHelper::TestQueueAdapter`. 6 | 7 | 8 | module ActiveJob::TestHelper::TestQueueAdapter 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _test_adapter; end 15 | def _test_adapter=(value); end 16 | end 17 | 18 | module GeneratedInstanceMethods; end 19 | end 20 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_model/conversion.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveModel::Conversion`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveModel::Conversion`. 6 | 7 | 8 | module ActiveModel::Conversion 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def param_delimiter; end 15 | def param_delimiter=(value); end 16 | def param_delimiter?; end 17 | end 18 | 19 | module GeneratedInstanceMethods 20 | def param_delimiter=(value); end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_record/counter_cache.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveRecord::CounterCache`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveRecord::CounterCache`. 6 | 7 | 8 | module ActiveRecord::CounterCache 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _counter_cache_columns; end 15 | def _counter_cache_columns=(value); end 16 | def _counter_cache_columns?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_record/readonly_attributes.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveRecord::ReadonlyAttributes`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveRecord::ReadonlyAttributes`. 6 | 7 | 8 | module ActiveRecord::ReadonlyAttributes 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def _attr_readonly; end 15 | def _attr_readonly=(value); end 16 | def _attr_readonly?; end 17 | end 18 | 19 | module GeneratedInstanceMethods; end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_record/secure_password.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveRecord::SecurePassword`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveRecord::SecurePassword`. 6 | 7 | 8 | module ActiveRecord::SecurePassword 9 | mixes_in_class_methods ::ActiveModel::SecurePassword::ClassMethods 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/api/v1/application_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Api::V1::ApplicationController`. 5 | # Please instead update this file by running `bin/tapioca dsl Api::V1::ApplicationController`. 6 | 7 | 8 | class Api::V1::ApplicationController 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/api/v1/internal/application_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Api::V1::Internal::ApplicationController`. 5 | # Please instead update this file by running `bin/tapioca dsl Api::V1::Internal::ApplicationController`. 6 | 7 | 8 | class Api::V1::Internal::ApplicationController 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/application_component.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ApplicationComponent`. 5 | # Please instead update this file by running `bin/tapioca dsl ApplicationComponent`. 6 | 7 | 8 | class ApplicationComponent 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/application_mailer.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ApplicationMailer`. 5 | # Please instead update this file by running `bin/tapioca dsl ApplicationMailer`. 6 | 7 | 8 | class ApplicationMailer 9 | include GeneratedUrlHelpersModule 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/delete_post_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `DeletePostForm`. 5 | # Please instead update this file by running `bin/tapioca dsl DeletePostForm`. 6 | 7 | 8 | class DeletePostForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_post_id; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_post_id=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/discard_post_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `DiscardPostForm`. 5 | # Please instead update this file by running `bin/tapioca dsl DiscardPostForm`. 6 | 7 | 8 | class DiscardPostForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_post_id; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_post_id=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/doorkeeper/access_grant_mixin.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Doorkeeper::AccessGrantMixin`. 5 | # Please instead update this file by running `bin/tapioca dsl Doorkeeper::AccessGrantMixin`. 6 | 7 | 8 | module Doorkeeper::AccessGrantMixin 9 | mixes_in_class_methods ::Doorkeeper::Models::Orderable::ClassMethods 10 | mixes_in_class_methods ::Doorkeeper::Models::SecretStorable::ClassMethods 11 | mixes_in_class_methods ::Doorkeeper::Models::ResourceOwnerable::ClassMethods 12 | end 13 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/doorkeeper/access_token_mixin.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Doorkeeper::AccessTokenMixin`. 5 | # Please instead update this file by running `bin/tapioca dsl Doorkeeper::AccessTokenMixin`. 6 | 7 | 8 | module Doorkeeper::AccessTokenMixin 9 | mixes_in_class_methods ::Doorkeeper::Models::Orderable::ClassMethods 10 | mixes_in_class_methods ::Doorkeeper::Models::SecretStorable::ClassMethods 11 | mixes_in_class_methods ::Doorkeeper::Models::ResourceOwnerable::ClassMethods 12 | mixes_in_class_methods ::Doorkeeper::Models::ExpirationTimeSqlMath::ClassMethods 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/doorkeeper/application_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Doorkeeper::ApplicationController`. 5 | # Please instead update this file by running `bin/tapioca dsl Doorkeeper::ApplicationController`. 6 | 7 | 8 | class Doorkeeper::ApplicationController 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/doorkeeper/application_metal_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Doorkeeper::ApplicationMetalController`. 5 | # Please instead update this file by running `bin/tapioca dsl Doorkeeper::ApplicationMetalController`. 6 | 7 | 8 | class Doorkeeper::ApplicationMetalController 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/doorkeeper/application_mixin.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Doorkeeper::ApplicationMixin`. 5 | # Please instead update this file by running `bin/tapioca dsl Doorkeeper::ApplicationMixin`. 6 | 7 | 8 | module Doorkeeper::ApplicationMixin 9 | mixes_in_class_methods ::Doorkeeper::Models::Orderable::ClassMethods 10 | mixes_in_class_methods ::Doorkeeper::Models::SecretStorable::ClassMethods 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/email_confirmation_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `EmailConfirmationForm`. 5 | # Please instead update this file by running `bin/tapioca dsl EmailConfirmationForm`. 6 | 7 | 8 | class EmailConfirmationForm 9 | sig { returns(T.nilable(::String)) } 10 | def email; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def email=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/email_confirmation_mailer.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `EmailConfirmationMailer`. 5 | # Please instead update this file by running `bin/tapioca dsl EmailConfirmationMailer`. 6 | 7 | 8 | class EmailConfirmationMailer 9 | class << self 10 | sig { params(email_confirmation_id: ::String, locale: ::String).returns(::ActionMailer::MessageDelivery) } 11 | def email_confirmation(email_confirmation_id:, locale:); end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/email_update_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `EmailUpdateForm`. 5 | # Please instead update this file by running `bin/tapioca dsl EmailUpdateForm`. 6 | 7 | 8 | class EmailUpdateForm 9 | sig { returns(T.nilable(::String)) } 10 | def new_email; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def new_email=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/fanout_post_job.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `FanoutPostJob`. 5 | # Please instead update this file by running `bin/tapioca dsl FanoutPostJob`. 6 | 7 | 8 | class FanoutPostJob 9 | class << self 10 | sig do 11 | params( 12 | post_id: ::String, 13 | block: T.nilable(T.proc.params(job: FanoutPostJob).void) 14 | ).returns(T.any(FanoutPostJob, FalseClass)) 15 | end 16 | def perform_later(post_id:, &block); end 17 | 18 | sig { params(post_id: ::String).void } 19 | def perform_now(post_id:); end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/follow_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `FollowForm`. 5 | # Please instead update this file by running `bin/tapioca dsl FollowForm`. 6 | 7 | 8 | class FollowForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_atname; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_atname=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/good_job/active_job_extensions/labels.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `GoodJob::ActiveJobExtensions::Labels`. 5 | # Please instead update this file by running `bin/tapioca dsl GoodJob::ActiveJobExtensions::Labels`. 6 | 7 | 8 | module GoodJob::ActiveJobExtensions::Labels 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def good_job_labels; end 15 | def good_job_labels=(value); end 16 | end 17 | 18 | module GeneratedInstanceMethods; end 19 | end 20 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/good_job/active_job_extensions/notify_options.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `GoodJob::ActiveJobExtensions::NotifyOptions`. 5 | # Please instead update this file by running `bin/tapioca dsl GoodJob::ActiveJobExtensions::NotifyOptions`. 6 | 7 | 8 | module GoodJob::ActiveJobExtensions::NotifyOptions 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def good_job_notify; end 15 | def good_job_notify=(value); end 16 | end 17 | 18 | module GeneratedInstanceMethods; end 19 | end 20 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/good_job/frontends_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `GoodJob::FrontendsController`. 5 | # Please instead update this file by running `bin/tapioca dsl GoodJob::FrontendsController`. 6 | 7 | 8 | class GoodJob::FrontendsController 9 | sig { returns(HelperProxy) } 10 | def helpers; end 11 | 12 | module HelperMethods 13 | include ::ActionController::Base::HelperMethods 14 | include ::GoodJob::IconsHelper 15 | include ::GoodJob::ApplicationHelper 16 | end 17 | 18 | class HelperProxy < ::ActionView::Base 19 | include HelperMethods 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/keyword_search_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `KeywordSearchForm`. 5 | # Please instead update this file by running `bin/tapioca dsl KeywordSearchForm`. 6 | 7 | 8 | class KeywordSearchForm 9 | sig { returns(T.nilable(::String)) } 10 | def q; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def q=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/link_data_fetcher_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `LinkDataFetcherForm`. 5 | # Please instead update this file by running `bin/tapioca dsl LinkDataFetcherForm`. 6 | 7 | 8 | class LinkDataFetcherForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_url; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_url=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/mewst/ui/base.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Mewst::UI::Base`. 5 | # Please instead update this file by running `bin/tapioca dsl Mewst::UI::Base`. 6 | 7 | 8 | class Mewst::UI::Base 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/password_reset_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `PasswordResetForm`. 5 | # Please instead update this file by running `bin/tapioca dsl PasswordResetForm`. 6 | 7 | 8 | class PasswordResetForm 9 | sig { returns(T.nilable(::String)) } 10 | def password; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def password=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/session_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `SessionForm`. 5 | # Please instead update this file by running `bin/tapioca dsl SessionForm`. 6 | 7 | 8 | class SessionForm 9 | sig { returns(T.nilable(::String)) } 10 | def email; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def email=(value); end 14 | 15 | sig { returns(T.nilable(::String)) } 16 | def password; end 17 | 18 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 19 | def password=(value); end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/stamp_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `StampForm`. 5 | # Please instead update this file by running `bin/tapioca dsl StampForm`. 6 | 7 | 8 | class StampForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_post_id; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_post_id=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/suggested_follow_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `SuggestedFollowForm`. 5 | # Please instead update this file by running `bin/tapioca dsl SuggestedFollowForm`. 6 | 7 | 8 | class SuggestedFollowForm 9 | sig { returns(T.nilable(::String)) } 10 | def target_atname; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def target_atname=(value); end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/time.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `Time`. 5 | # Please instead update this file by running `bin/tapioca dsl Time`. 6 | 7 | 8 | class Time 9 | class << self 10 | sig { returns(::ActiveSupport::TimeWithZone) } 11 | def current; end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/user_form.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `UserForm`. 5 | # Please instead update this file by running `bin/tapioca dsl UserForm`. 6 | 7 | 8 | class UserForm 9 | sig { returns(T.nilable(::String)) } 10 | def locale; end 11 | 12 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 13 | def locale=(value); end 14 | 15 | sig { returns(T.nilable(::String)) } 16 | def time_zone; end 17 | 18 | sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } 19 | def time_zone=(value); end 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/view_component/preview.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ViewComponent::Preview`. 5 | # Please instead update this file by running `bin/tapioca dsl ViewComponent::Preview`. 6 | 7 | 8 | class ViewComponent::Preview 9 | include GeneratedUrlHelpersModule 10 | include GeneratedPathHelpersModule 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/view_component/translatable.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ViewComponent::Translatable`. 5 | # Please instead update this file by running `bin/tapioca dsl ViewComponent::Translatable`. 6 | 7 | 8 | module ViewComponent::Translatable 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def i18n_backend; end 15 | def i18n_backend=(value); end 16 | end 17 | 18 | module GeneratedInstanceMethods 19 | def i18n_backend; end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-generated=true 2 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actioncable@7.1.5.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actioncable` gem. 5 | # Please instead update this file by running `bin/tapioca gem actioncable`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actionmailbox@7.1.5.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actionmailbox` gem. 5 | # Please instead update this file by running `bin/tapioca gem actionmailbox`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actiontext@7.1.5.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actiontext` gem. 5 | # Please instead update this file by running `bin/tapioca gem actiontext`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/activestorage@7.1.5.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `activestorage` gem. 5 | # Please instead update this file by running `bin/tapioca gem activestorage`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/better_html@2.1.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `better_html` gem. 5 | # Please instead update this file by running `bin/tapioca gem better_html`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/bindex@0.8.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `bindex` gem. 5 | # Please instead update this file by running `bin/tapioca gem bindex`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/builder@3.3.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `builder` gem. 5 | # Please instead update this file by running `bin/tapioca gem builder`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/crass@1.0.6.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `crass` gem. 5 | # Please instead update this file by running `bin/tapioca gem crass`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/csv@3.3.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `csv` gem. 5 | # Please instead update this file by running `bin/tapioca gem csv`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/cuprite@0.15.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `cuprite` gem. 5 | # Please instead update this file by running `bin/tapioca gem cuprite`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/dotenv-rails@3.1.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `dotenv-rails` gem. 5 | # Please instead update this file by running `bin/tapioca gem dotenv-rails`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/erb_lint@0.9.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `erb_lint` gem. 5 | # Please instead update this file by running `bin/tapioca gem erb_lint`. 6 | 7 | 8 | # source://erb_lint//lib/erb_lint/version.rb#3 9 | module ERBLint; end 10 | 11 | # source://erb_lint//lib/erb_lint/version.rb#4 12 | ERBLint::VERSION = T.let(T.unsafe(nil), String) 13 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/ferrum@0.15.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `ferrum` gem. 5 | # Please instead update this file by running `bin/tapioca gem ferrum`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/ffi@1.16.3.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `ffi` gem. 5 | # Please instead update this file by running `bin/tapioca gem ffi`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/io-console@0.8.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `io-console` gem. 5 | # Please instead update this file by running `bin/tapioca gem io-console`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/marcel@1.0.4.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `marcel` gem. 5 | # Please instead update this file by running `bin/tapioca gem marcel`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/method_source@1.1.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `method_source` gem. 5 | # Please instead update this file by running `bin/tapioca gem method_source`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/multi_xml@0.7.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `multi_xml` gem. 5 | # Please instead update this file by running `bin/tapioca gem multi_xml`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/net-pop@0.1.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `net-pop` gem. 5 | # Please instead update this file by running `bin/tapioca gem net-pop`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rails@7.1.5.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rails` gem. 5 | # Please instead update this file by running `bin/tapioca gem rails`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rails_autolink@1.1.8.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rails_autolink` gem. 5 | # Please instead update this file by running `bin/tapioca gem rails_autolink`. 6 | 7 | # source://rails_autolink//lib/rails_autolink.rb#1 8 | module RailsAutolink; end 9 | 10 | # source://rails_autolink//lib/rails_autolink.rb#2 11 | class RailsAutolink::Railtie < ::Rails::Railtie; end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rb-fsevent@0.11.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rb-fsevent` gem. 5 | # Please instead update this file by running `bin/tapioca gem rb-fsevent`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rb-inotify@0.10.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rb-inotify` gem. 5 | # Please instead update this file by running `bin/tapioca gem rb-inotify`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/reline@0.6.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `reline` gem. 5 | # Please instead update this file by running `bin/tapioca gem reline`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rspec-core@3.13.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rspec-core` gem. 5 | # Please instead update this file by running `bin/tapioca gem rspec-core`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rspec-mocks@3.13.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rspec-mocks` gem. 5 | # Please instead update this file by running `bin/tapioca gem rspec-mocks`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rubocop-performance@1.22.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rubocop-performance` gem. 5 | # Please instead update this file by running `bin/tapioca gem rubocop-performance`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rubocop-rails@2.25.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rubocop-rails` gem. 5 | # Please instead update this file by running `bin/tapioca gem rubocop-rails`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/smart_properties@1.17.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `smart_properties` gem. 5 | # Please instead update this file by running `bin/tapioca gem smart_properties`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/standard-custom@1.0.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `standard-custom` gem. 5 | # Please instead update this file by running `bin/tapioca gem standard-custom`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/standard-performance@1.5.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `standard-performance` gem. 5 | # Please instead update this file by running `bin/tapioca gem standard-performance`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/stringio@3.1.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `stringio` gem. 5 | # Please instead update this file by running `bin/tapioca gem stringio`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/websocket-driver@0.7.6.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `websocket-driver` gem. 5 | # Please instead update this file by running `bin/tapioca gem websocket-driver`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/websocket-extensions@0.1.5.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `websocket-extensions` gem. 5 | # Please instead update this file by running `bin/tapioca gem websocket-extensions`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 9 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/account.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Account 5 | def self.enumerize(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/active_record/relation.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class ActiveRecord::Relation 5 | def arel_table 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/addressable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Addressable::URI; end 5 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/api/v1/application_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class V1::ApplicationController 5 | end 6 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/api/v1/form_errorable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::V1::FormErrorable 5 | def render(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/api/v1/timeline/show_controller.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class V1::Timeline::ShowController 5 | end 6 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/application_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ApplicationHelper 5 | def display_meta_tags(*args) 6 | end 7 | 8 | def meta_tags 9 | end 10 | 11 | def request 12 | end 13 | 14 | def t(*args) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/commented_repost.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class CommentedRepost 5 | def self.counter_culture(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/component_data_fetcher_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ComponentDataFetcherHelper 5 | def tag; end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/controller_concerns/api/authenticatable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Api::Authenticatable 5 | def self.before_action(*args) 6 | end 7 | 8 | def doorkeeper_token 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/controller_concerns/authenticatable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Authenticatable 5 | def self.helper_method(*args) 6 | end 7 | 8 | def cookies 9 | end 10 | 11 | def flash 12 | end 13 | 14 | def home_path 15 | end 16 | 17 | def redirect_to(*args) 18 | end 19 | 20 | def request 21 | end 22 | 23 | def reset_session 24 | end 25 | 26 | def root_path 27 | end 28 | 29 | def session 30 | end 31 | 32 | def t(*args) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/controller_concerns/authorizable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Authorizable 5 | def current_viewer! 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/controller_concerns/email_confirmation_findable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::EmailConfirmationFindable 5 | def redirect_to(*args) 6 | end 7 | 8 | def root_path 9 | end 10 | 11 | def session 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/controller_concerns/localizable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Localizable 5 | def viewer 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/email_confirmation.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmation 5 | def self.enumerize(*args) 6 | end 7 | 8 | def self.event 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/email_confirmation_mailer.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class EmailConfirmationMailer 5 | def local_url(*args); end 6 | def settings_email_callback_url(*args); end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/faraday.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module Faraday 5 | def self.get(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/flash_toast_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module FlashToastHelper 5 | def tag(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/form/password_validatable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module FormConcerns::PasswordValidatable 5 | def self.validate(*args) 6 | end 7 | 8 | def self.validates(*args) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/pubsub.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module Google::Cloud::PubSub 5 | def self.new(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/pubsub/credentials.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Google::Cloud::PubSub::Credentials 5 | def self.new(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/pubsub/project.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Google::Cloud::PubSub::Project 5 | def topic(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/pubsub/topic.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Google::Cloud::PubSub::Topic 5 | def publish_async(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/tasks/v2/cloud_tasks/client.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Google::Cloud::Tasks::V2::CloudTasks::Client 5 | def create_task(*args); end 6 | def queue_path(*args); end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/google/cloud/tasks/v2/cloud_tasks/credentials.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Google::Cloud::Tasks::V2::CloudTasks::Credentials 5 | def self.new(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/icons/logo_icon_component.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Icons::LogoIconComponent 5 | def self.erb_template(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/icons/stamped_icon_component.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Icons::StampedIconComponent 5 | def self.erb_template(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/icons/unstamped_icon_component.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Icons::UnstampedIconComponent 5 | def self.erb_template(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/internal_authenticatable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::InternalAuthenticatable 5 | def self.before_action(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/kernel.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module Kernel 5 | sig { returns(T.self_type) } 6 | def not_nil!; end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/language_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module LanguageHelper 5 | def t(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/localizable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ControllerConcerns::Localizable 5 | def current_viewer; end 6 | def http_accept_language; end 7 | def params; end 8 | def session; end 9 | end 10 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/mewst/application.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Mewst::Application 5 | def self.config_for(*args) 6 | end 7 | 8 | def self.r301(*args) 9 | end 10 | 11 | def self.send_file(*args) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/model_concerns/notifiable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module ModelConcerns::Notifiable 5 | def self.has_one(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/nil_class.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class NilClass 5 | sig { returns(T.noreturn) } 6 | def not_nil!; end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/notification.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Notification 5 | def self.enumerize(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/post.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Post 5 | def self.enumerize(*args); end 6 | def kind_comment_post?; end 7 | def kind_repost?; end 8 | end 9 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/post/private_association_relation.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Post::PrivateAssociationRelation 5 | include ActiveRecordCursorPaginate::Extension 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/post/private_relation.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Post::PrivateRelation 5 | include ActiveRecordCursorPaginate::Extension 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/profile.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Profile 5 | def self.enumerize(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/profile/private_relation.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Profile::PrivateRelation 5 | include ActiveRecordCursorPaginate::Extension 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/pubsub/subscribable.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module Pubsub::Subscribable 5 | def authenticate_or_request_with_http_token; end 6 | def params; end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/repost.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Repost 5 | def self.counter_culture(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/session/private_collection_proxy.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class Session::PrivateCollectionProxy 5 | def start!(*args) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/text_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module TextHelper 5 | def auto_link(*args); end 6 | def simple_format(*args); end 7 | end 8 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/time_helper.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | module TimeHelper 5 | def format(*args) 6 | end 7 | 8 | def time_ago_in_words(*args) 9 | end 10 | 11 | def viewer 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/manual/user.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | # frozen_string_literal: true 3 | 4 | class User 5 | def self.enumerize(*args); end 6 | end 7 | -------------------------------------------------------------------------------- /sorbet/rbi/shims/loofah.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | class Loofah::Scrubber; end 4 | -------------------------------------------------------------------------------- /sorbet/rbi/shims/minitest.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | class Minitest::Test; end 4 | -------------------------------------------------------------------------------- /sorbet/rbi/todo.rbi: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT MANUALLY 2 | # This is an autogenerated file for unresolved constants. 3 | # Please instead update this file by running `bin/tapioca todo`. 4 | 5 | # typed: false 6 | 7 | module ::RedisClient::PIDCache::CoreExt; end 8 | module Capybara::Cuprite::Driver; end 9 | module V1::FollowForm; end 10 | module V1::PostForm; end 11 | module V1::StampForm; end 12 | -------------------------------------------------------------------------------- /sorbet/tapioca/config.yml: -------------------------------------------------------------------------------- 1 | gem: 2 | # Add your `gem` command parameters here: 3 | # 4 | # exclude: 5 | # - gem_name 6 | # doc: true 7 | # workers: 5 8 | dsl: 9 | # Add your `dsl` command parameters here: 10 | # 11 | # exclude: 12 | # - SomeGeneratorName 13 | # workers: 5 14 | -------------------------------------------------------------------------------- /sorbet/tapioca/require.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | # Add your extra requires here (`bin/tapioca require` can be used to boostrap this list) 5 | -------------------------------------------------------------------------------- /spec/factories/actor.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :actor do 6 | user { association :user } 7 | profile { user.profile } 8 | 9 | trait :with_access_token_for_web do 10 | after(:create) do |actor| 11 | application = create(:oauth_application, :mewst_web) 12 | create(:oauth_access_token, application:, resource_owner: actor) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/factories/email_confirmation.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :email_confirmation do 6 | sequence(:email) { |n| "test-#{n}@example.com" } 7 | event { :sign_up } 8 | sequence(:code) { |n| "00000#{n}" } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/link.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :link do 6 | sequence(:canonical_url) { |n| "https://example.com/#{n}" } 7 | domain { "example.com" } 8 | sequence(:title) { |n| "Page Title ##{n}" } 9 | image_url { "https://example.com/image.jpg" } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/factories/oauth_application.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :oauth_application do 6 | sequence(:name) { |n| "OAuth App #{n}" } 7 | sequence(:uid) { |n| "oauth-app-#{n}" } 8 | secret { "secret" } 9 | redirect_uri { "https://example.com/callback" } 10 | scopes { "read" } 11 | 12 | trait :mewst_web do 13 | name { "Mewst for Web" } 14 | uid { OauthApplication::MEWST_WEB_UID } 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/factories/oauth_oauth_access_token.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :oauth_access_token do 6 | resource_owner(factory: :actor) 7 | application(factory: :oauth_application) 8 | sequence(:token) { |n| "token_#{n}" } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/post.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :post do 6 | profile 7 | sequence(:content) { |n| "test_#{n}" } 8 | published_at { Time.current } 9 | oauth_application 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/factories/profile.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :profile do 6 | sequence(:atname) { |n| "test_#{n}" } 7 | owner_type { ProfileOwnerType::User.serialize } 8 | joined_at { Time.current } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/user.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | FactoryBot.define do 5 | factory :user do 6 | profile { association :profile, owner_type: ProfileOwnerType::User.serialize } 7 | sequence(:email) { |n| "test_#{n}@example.com" } 8 | password { "passw0rd" } 9 | locale { :ja } 10 | time_zone { "Asia/Tokyo" } 11 | signed_up_at { Time.current } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/sign_in/new_spec.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | RSpec.describe "GET /sign_in", type: :request do 5 | context "ログインしているとき" do 6 | let!(:actor) { FactoryBot.create(:actor) } 7 | 8 | before do 9 | sign_in actor 10 | end 11 | 12 | it "ホーム画面にリダイレクトすること" do 13 | get "/sign_in" 14 | 15 | expect(response).to redirect_to(home_path) 16 | end 17 | end 18 | 19 | context "ログインしていないとき" do 20 | it "ページが表示されること" do 21 | get "/sign_in" 22 | 23 | expect(response.body).to include("ログイン") 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/requests/sign_up/new_spec.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | RSpec.describe "GET /sign_up", type: :request do 5 | context "ログインしているとき" do 6 | let!(:actor) { FactoryBot.create(:actor) } 7 | 8 | before do 9 | sign_in actor 10 | end 11 | 12 | it "ホーム画面にリダイレクトすること" do 13 | get "/sign_up" 14 | 15 | expect(response).to redirect_to(home_path) 16 | end 17 | end 18 | 19 | context "ログインしていないとき" do 20 | it "ページが表示されること" do 21 | get "/sign_up" 22 | 23 | expect(response.body).to include("アカウント作成") 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/requests/welcome/show_spec.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | RSpec.describe "GET /", type: :request do 5 | context "ログインしているとき" do 6 | let!(:actor) { FactoryBot.create(:actor) } 7 | 8 | before do 9 | sign_in actor 10 | end 11 | 12 | it "ホーム画面にリダイレクトすること" do 13 | get "/" 14 | 15 | expect(response).to redirect_to(home_path) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/support/committee.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | RSpec.configure do |config| 5 | config.include Committee::Rails::Test::Methods 6 | config.add_setting :committee_options 7 | config.committee_options = { 8 | query_hash_key: "rack.request.query_hash", 9 | parse_response_by_content_type: false, 10 | strict_reference_validation: true 11 | } 12 | 13 | config.before :all, api_version: :v1 do 14 | RSpec.configure do |config| 15 | config.committee_options[:schema_path] = Rails.root.join("openapi/v1.yaml").to_s 16 | config.committee_options[:prefix] = "/v1" 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/support/factory_bot.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | RSpec.configure do |config| 5 | config.include FactoryBot::Syntax::Methods 6 | end 7 | -------------------------------------------------------------------------------- /spec/support/request_helpers.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | module RequestHelpers 5 | def sign_in(actor, password: "passw0rd") 6 | post(sign_in_path, params: {session_form: {email: actor.email, password:}}) 7 | expect(cookies[Session::COOKIE_KEY]).not_to be_nil 8 | end 9 | end 10 | 11 | RSpec.configure do |config| 12 | config.include RequestHelpers, type: :request 13 | end 14 | -------------------------------------------------------------------------------- /spec/support/vcr.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | VCR.configure do |config| 5 | config.cassette_library_dir = "fixtures/vcr_cassettes" 6 | config.hook_into :faraday 7 | end 8 | -------------------------------------------------------------------------------- /spec/system/welcome/show_spec.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | # GitHub Actionsでのテスト実行時にエラーになるため、一旦スキップする 5 | RSpec.xdescribe "トップページ", type: :system do 6 | it do 7 | visit "/" 8 | 9 | expect(page).to have_content "現在ベータ版です" 10 | end 11 | end 12 | --------------------------------------------------------------------------------