Copy disabled (too large)
Download .txt
Showing preview only (16,718K chars total). Download the full file to get everything.
Repository: adrienpoly/rubyvideo
Branch: main
Commit: e6cdb725d6ad
Files: 3709
Total size: 15.1 MB
Directory structure:
gitextract_xohe3ehl/
├── .annotaterb.yml
├── .cursor/
│ ├── commands/
│ │ └── plan_commands.md
│ └── rules/
│ ├── cursorrules.mdc
│ └── viewcomponents.mdc
├── .devcontainer/
│ ├── Dockerfile
│ ├── devcontainer.json
│ └── docker-compose.yml
├── .dockerignore
├── .erb_lint.yml
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│ ├── pull_request_template.md
│ ├── skills/
│ │ └── event-data/
│ │ └── SKILL.md
│ └── workflows/
│ ├── ci.yml
│ ├── copilot-setup-steps.yml
│ ├── deploy-staging.yml
│ ├── migrate-production.yml
│ ├── migrate-staging.yml
│ ├── release-lock-production.yml
│ ├── release-lock-staging.yml
│ ├── seed-production.yml
│ └── seed-staging.yml
├── .gitignore
├── .herb.yml
├── .kamal/
│ ├── hooks/
│ │ ├── docker-setup.sample
│ │ ├── post-deploy.sample
│ │ ├── post-proxy-reboot.sample
│ │ ├── pre-build.sample
│ │ ├── pre-connect.sample
│ │ ├── pre-deploy.sample
│ │ └── pre-proxy-reboot.sample
│ ├── secrets
│ └── secrets.staging
├── .mcp.json
├── .node-version
├── .prettierignore
├── .ruby-version
├── .standard.yml
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CLAUDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── Guardfile
├── Procfile.dev
├── README.md
├── Rakefile
├── app/
│ ├── assets/
│ │ ├── builds/
│ │ │ └── .keep
│ │ └── stylesheets/
│ │ ├── application.css
│ │ ├── bridge/
│ │ │ └── components.css
│ │ └── components/
│ │ ├── button.css
│ │ ├── diff.css
│ │ ├── dropdown.css
│ │ ├── event.css
│ │ ├── form.css
│ │ ├── hotwire-combobox.css
│ │ ├── iframe.css
│ │ ├── markdown.css
│ │ ├── modal.css
│ │ ├── nav.css
│ │ ├── pagination.css
│ │ ├── skeleton.css
│ │ ├── spotlight.css
│ │ ├── tabs.css
│ │ ├── transition.css
│ │ ├── typography.css
│ │ └── video.css
│ ├── avo/
│ │ ├── actions/
│ │ │ ├── approve_topic.rb
│ │ │ ├── assign_canonical_event.rb
│ │ │ ├── assign_canonical_speaker.rb
│ │ │ ├── assign_canonical_topic.rb
│ │ │ ├── assign_canonical_user.rb
│ │ │ ├── clear_user.rb
│ │ │ ├── enhance_transcript.rb
│ │ │ ├── extract_topics.rb
│ │ │ ├── fetch_duration.rb
│ │ │ ├── find_topic_talks.rb
│ │ │ ├── geocode_record.rb
│ │ │ ├── reject_topic.rb
│ │ │ ├── summarize.rb
│ │ │ ├── talk_index.rb
│ │ │ ├── talk_ingest.rb
│ │ │ ├── transcript.rb
│ │ │ ├── update_from_yml.rb
│ │ │ └── user_fetch_github.rb
│ │ ├── cards/
│ │ │ ├── duplicates_summary.rb
│ │ │ ├── reversed_name_duplicates_list.rb
│ │ │ ├── reversed_name_duplicates_metric.rb
│ │ │ ├── same_name_duplicates_list.rb
│ │ │ ├── same_name_duplicates_metric.rb
│ │ │ ├── suspicious_signals_breakdown.rb
│ │ │ ├── suspicious_summary.rb
│ │ │ ├── suspicious_users_list.rb
│ │ │ ├── suspicious_users_metric.rb
│ │ │ ├── unavailable_videos_by_event.rb
│ │ │ ├── unavailable_videos_list.rb
│ │ │ ├── unavailable_videos_metric.rb
│ │ │ └── unavailable_videos_summary.rb
│ │ ├── dashboards/
│ │ │ ├── default.rb
│ │ │ ├── duplicates.rb
│ │ │ ├── suspicious.rb
│ │ │ └── unavailable_videos.rb
│ │ ├── filters/
│ │ │ ├── aliasable_type.rb
│ │ │ ├── attended_as.rb
│ │ │ ├── bio.rb
│ │ │ ├── bio_presence.rb
│ │ │ ├── canonical.rb
│ │ │ ├── enhanced_transcript.rb
│ │ │ ├── geocoded_presence.rb
│ │ │ ├── github.rb
│ │ │ ├── github_handle.rb
│ │ │ ├── github_handle_presence.rb
│ │ │ ├── has_duplicate.rb
│ │ │ ├── involvement_role.rb
│ │ │ ├── language.rb
│ │ │ ├── location_presence.rb
│ │ │ ├── name.rb
│ │ │ ├── provider.rb
│ │ │ ├── published.rb
│ │ │ ├── raw_transcript.rb
│ │ │ ├── slug.rb
│ │ │ ├── summary.rb
│ │ │ ├── suspicious.rb
│ │ │ ├── talk_event.rb
│ │ │ ├── title.rb
│ │ │ ├── topic_talks.rb
│ │ │ ├── topics.rb
│ │ │ ├── video_availability.rb
│ │ │ ├── video_provider.rb
│ │ │ └── without_talks.rb
│ │ └── resources/
│ │ ├── alias.rb
│ │ ├── city.rb
│ │ ├── connected_account.rb
│ │ ├── contributor.rb
│ │ ├── event.rb
│ │ ├── event_involvement.rb
│ │ ├── event_participation.rb
│ │ ├── event_series.rb
│ │ ├── favorite_user.rb
│ │ ├── geocode_result.rb
│ │ ├── organization.rb
│ │ ├── session.rb
│ │ ├── sponsor.rb
│ │ ├── suggestion.rb
│ │ ├── talk.rb
│ │ ├── talk_topic.rb
│ │ ├── talk_transcript.rb
│ │ ├── topic.rb
│ │ ├── topic_gem.rb
│ │ ├── user.rb
│ │ ├── user_talk.rb
│ │ ├── watch_list.rb
│ │ └── watch_list_talk.rb
│ ├── channels/
│ │ └── application_cable/
│ │ ├── channel.rb
│ │ └── connection.rb
│ ├── clients/
│ │ ├── application_client.rb
│ │ ├── blue_sky.rb
│ │ ├── github/
│ │ │ ├── client.rb
│ │ │ ├── contributors_client.rb
│ │ │ └── user_client.rb
│ │ ├── ruby_conferences/
│ │ │ └── client.rb
│ │ ├── speakerdeck/
│ │ │ └── client.rb
│ │ └── youtube/
│ │ ├── channels.rb
│ │ ├── client.rb
│ │ ├── playlist_items.rb
│ │ ├── playlists.rb
│ │ ├── thumbnail.rb
│ │ ├── transcript.rb
│ │ └── video.rb
│ ├── components/
│ │ ├── application_component.rb
│ │ ├── events/
│ │ │ ├── upcoming_event_banner_component.html.erb
│ │ │ └── upcoming_event_banner_component.rb
│ │ ├── hover_card/
│ │ │ ├── base_component.html.erb
│ │ │ ├── base_component.rb
│ │ │ ├── event_component.rb
│ │ │ └── user_component.rb
│ │ ├── tito/
│ │ │ ├── button_component.html.erb
│ │ │ ├── button_component.rb
│ │ │ ├── widget_component.html.erb
│ │ │ └── widget_component.rb
│ │ └── ui/
│ │ ├── avatar_component.html.erb
│ │ ├── avatar_component.rb
│ │ ├── avatar_group_component.html.erb
│ │ ├── avatar_group_component.rb
│ │ ├── badge_component.rb
│ │ ├── button_component.rb
│ │ ├── divider_component.rb
│ │ ├── dropdown_component.html.erb
│ │ ├── dropdown_component.rb
│ │ ├── modal_component.html.erb
│ │ ├── modal_component.rb
│ │ ├── stamp_component.html.erb
│ │ └── stamp_component.rb
│ ├── controllers/
│ │ ├── admin/
│ │ │ └── suggestions_controller.rb
│ │ ├── analytics/
│ │ │ └── dashboards_controller.rb
│ │ ├── announcements_controller.rb
│ │ ├── api/
│ │ │ └── v1/
│ │ │ └── embed/
│ │ │ ├── base_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── profiles_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ ├── stickers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── topics_controller.rb
│ │ ├── application_controller.rb
│ │ ├── avo/
│ │ │ ├── aliases_controller.rb
│ │ │ ├── cities_controller.rb
│ │ │ ├── connected_accounts_controller.rb
│ │ │ ├── contributors_controller.rb
│ │ │ ├── event_involvements_controller.rb
│ │ │ ├── event_participations_controller.rb
│ │ │ ├── event_series_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── favorite_users_controller.rb
│ │ │ ├── geocode_results_controller.rb
│ │ │ ├── organizations_controller.rb
│ │ │ ├── sessions_controller.rb
│ │ │ ├── speaker_talks_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── sponsors_controller.rb
│ │ │ ├── suggestions_controller.rb
│ │ │ ├── talk_topics_controller.rb
│ │ │ ├── talk_transcripts_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ ├── topic_gems_controller.rb
│ │ │ ├── topics_controller.rb
│ │ │ ├── user_talks_controller.rb
│ │ │ ├── users_controller.rb
│ │ │ ├── watch_list_talks_controller.rb
│ │ │ └── watch_lists_controller.rb
│ │ ├── browse_controller.rb
│ │ ├── cfp_controller.rb
│ │ ├── cities_controller.rb
│ │ ├── concerns/
│ │ │ ├── analytics.rb
│ │ │ ├── authenticable.rb
│ │ │ ├── event_data.rb
│ │ │ ├── event_map_markers.rb
│ │ │ ├── favorite_users.rb
│ │ │ ├── geo_map_layers.rb
│ │ │ ├── location_events.rb
│ │ │ ├── metadata.rb
│ │ │ ├── profile_data.rb
│ │ │ ├── remote_modal.rb
│ │ │ ├── spotlight_search.rb
│ │ │ ├── turbo/
│ │ │ │ └── force_response.rb
│ │ │ └── watched_talks.rb
│ │ ├── continents/
│ │ │ ├── base_controller.rb
│ │ │ └── countries_controller.rb
│ │ ├── continents_controller.rb
│ │ ├── contributions_controller.rb
│ │ ├── coordinates_controller.rb
│ │ ├── countries/
│ │ │ ├── base_controller.rb
│ │ │ └── cities_controller.rb
│ │ ├── countries_controller.rb
│ │ ├── event_participations_controller.rb
│ │ ├── events/
│ │ │ ├── archive_controller.rb
│ │ │ ├── attendances_controller.rb
│ │ │ ├── cfp_controller.rb
│ │ │ ├── collectibles_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── involvements_controller.rb
│ │ │ ├── meetups_controller.rb
│ │ │ ├── participants_controller.rb
│ │ │ ├── past_controller.rb
│ │ │ ├── related_talks_controller.rb
│ │ │ ├── schedules_controller.rb
│ │ │ ├── series_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── sponsors_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ ├── tickets_controller.rb
│ │ │ ├── todos_controller.rb
│ │ │ ├── upcoming_controller.rb
│ │ │ ├── venues_controller.rb
│ │ │ ├── videos_controller.rb
│ │ │ └── years_controller.rb
│ │ ├── events_controller.rb
│ │ ├── favorite_users_controller.rb
│ │ ├── gems_controller.rb
│ │ ├── hotwire/
│ │ │ └── native/
│ │ │ └── v1/
│ │ │ ├── android/
│ │ │ │ └── path_configurations_controller.rb
│ │ │ └── ios/
│ │ │ └── path_configurations_controller.rb
│ │ ├── hover_cards/
│ │ │ ├── events_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── leaderboard_controller.rb
│ │ ├── locations/
│ │ │ ├── base_controller.rb
│ │ │ ├── map_controller.rb
│ │ │ ├── meetups_controller.rb
│ │ │ ├── past_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── online_controller.rb
│ │ ├── organizations/
│ │ │ ├── logos_controller.rb
│ │ │ └── wrapped_controller.rb
│ │ ├── organizations_controller.rb
│ │ ├── page_controller.rb
│ │ ├── profiles/
│ │ │ ├── aliases_controller.rb
│ │ │ ├── claims_controller.rb
│ │ │ ├── connect_controller.rb
│ │ │ ├── enhance_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── involvements_controller.rb
│ │ │ ├── map_controller.rb
│ │ │ ├── mutual_events_controller.rb
│ │ │ ├── notes_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ ├── stickers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── wrapped_controller.rb
│ │ ├── profiles_controller.rb
│ │ ├── recommendations_controller.rb
│ │ ├── sessions/
│ │ │ └── omniauth_controller.rb
│ │ ├── sessions_controller.rb
│ │ ├── settings_controller.rb
│ │ ├── sitemaps_controller.rb
│ │ ├── speakers_controller.rb
│ │ ├── sponsors/
│ │ │ └── missing_controller.rb
│ │ ├── spotlight/
│ │ │ ├── events_controller.rb
│ │ │ ├── kinds_controller.rb
│ │ │ ├── languages_controller.rb
│ │ │ ├── locations_controller.rb
│ │ │ ├── organizations_controller.rb
│ │ │ ├── series_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── topics_controller.rb
│ │ ├── stamps_controller.rb
│ │ ├── states/
│ │ │ ├── base_controller.rb
│ │ │ └── cities_controller.rb
│ │ ├── states_controller.rb
│ │ ├── talks/
│ │ │ ├── recommendations_controller.rb
│ │ │ ├── slides_controller.rb
│ │ │ └── watched_talks_controller.rb
│ │ ├── talks_controller.rb
│ │ ├── templates_controller.rb
│ │ ├── todos_controller.rb
│ │ ├── topics_controller.rb
│ │ ├── watch_list_talks_controller.rb
│ │ ├── watch_lists_controller.rb
│ │ ├── watched_talks_controller.rb
│ │ └── wrapped_controller.rb
│ ├── helpers/
│ │ ├── application_helper.rb
│ │ ├── events/
│ │ │ └── talks_helper.rb
│ │ ├── events_helper.rb
│ │ ├── icon_helper.rb
│ │ ├── language_helper.rb
│ │ ├── location_helper.rb
│ │ ├── markdown_helper.rb
│ │ ├── page_helper.rb
│ │ ├── speakers/
│ │ │ └── enhance_helper.rb
│ │ ├── speakers_helper.rb
│ │ ├── spotlight_helper.rb
│ │ ├── suggestions_helper.rb
│ │ ├── talks_helper.rb
│ │ └── view_component_helper.rb
│ ├── javascript/
│ │ ├── controllers/
│ │ │ ├── application.js
│ │ │ ├── auto-click_controller.js
│ │ │ ├── auto_submit_controller.js
│ │ │ ├── bridge/
│ │ │ │ └── button_controller.js
│ │ │ ├── collapsible_controller.js
│ │ │ ├── content_row_controller.js
│ │ │ ├── copy_to_clipboard_controller.js
│ │ │ ├── dropdown_controller.js
│ │ │ ├── event_controller.js
│ │ │ ├── event_list_controller.js
│ │ │ ├── events_filter_controller.js
│ │ │ ├── events_view_switcher_controller.js
│ │ │ ├── geolocation_controller.js
│ │ │ ├── hover_card_controller.js
│ │ │ ├── index.js
│ │ │ ├── lazy_loading_controller.js
│ │ │ ├── map_controller.js
│ │ │ ├── modal_controller.js
│ │ │ ├── preserve_scroll_controller.js
│ │ │ ├── pronouns_select_controller.js
│ │ │ ├── scroll_controller.js
│ │ │ ├── scroll_into_view_controller.js
│ │ │ ├── splide_controller.js
│ │ │ ├── spotlight_search_controller.js
│ │ │ ├── tabs_controller.js
│ │ │ ├── talks_filter_controller.js
│ │ │ ├── talks_filter_pill_controller.js
│ │ │ ├── talks_navigation_controller.js
│ │ │ ├── toggable_controller.js
│ │ │ ├── tooltip_controller.js
│ │ │ ├── top_banner_controller.js
│ │ │ ├── transition_controller.js
│ │ │ ├── video_player_controller.js
│ │ │ ├── watched_talk_form_controller.js
│ │ │ └── wrapped_stories_controller.js
│ │ ├── entrypoints/
│ │ │ ├── application.css
│ │ │ ├── application.js
│ │ │ └── chartkick.js
│ │ ├── helpers/
│ │ │ └── timing_helpers.js
│ │ └── support/
│ │ └── appsignal.js
│ ├── jobs/
│ │ ├── application_job.rb
│ │ ├── generate_wrapped_screenshot_job.rb
│ │ ├── geocode_record_job.rb
│ │ ├── recurring/
│ │ │ ├── fetch_contributors_job.rb
│ │ │ ├── mark_suspicious_users_job.rb
│ │ │ ├── rollup_job.rb
│ │ │ ├── youtube_thumbnail_validation_job.rb
│ │ │ ├── youtube_video_availability_job.rb
│ │ │ ├── youtube_video_duration_job.rb
│ │ │ └── youtube_video_statistics_job.rb
│ │ └── typesense_index_job.rb
│ ├── lib/
│ │ ├── command.rb
│ │ ├── download_sponsors.rb
│ │ ├── duration.rb
│ │ └── router.rb
│ ├── mailers/
│ │ └── application_mailer.rb
│ ├── models/
│ │ ├── active_record/
│ │ │ └── sqlite/
│ │ │ └── index.rb
│ │ ├── ahoy/
│ │ │ ├── event.rb
│ │ │ └── visit.rb
│ │ ├── alias.rb
│ │ ├── announcement.rb
│ │ ├── application_record.rb
│ │ ├── cfp.rb
│ │ ├── city.rb
│ │ ├── concerns/
│ │ │ ├── appsignal/
│ │ │ │ └── admin_namespace.rb
│ │ │ ├── geocodeable.rb
│ │ │ ├── locatable.rb
│ │ │ ├── rollupable.rb
│ │ │ ├── sluggable.rb
│ │ │ ├── suggestable.rb
│ │ │ ├── todoable.rb
│ │ │ ├── url_normalizable.rb
│ │ │ ├── watchable.rb
│ │ │ └── yaml_file.rb
│ │ ├── connected_account.rb
│ │ ├── continent.rb
│ │ ├── contributor.rb
│ │ ├── coordinate_location.rb
│ │ ├── country.rb
│ │ ├── cue.rb
│ │ ├── current.rb
│ │ ├── email_verification_token.rb
│ │ ├── event/
│ │ │ ├── assets.rb
│ │ │ ├── cfp_file.rb
│ │ │ ├── involvements_file.rb
│ │ │ ├── schedule.rb
│ │ │ ├── sponsors_file.rb
│ │ │ ├── static_metadata.rb
│ │ │ ├── tickets.rb
│ │ │ ├── transcripts_file.rb
│ │ │ ├── typesense_searchable.rb
│ │ │ ├── venue.rb
│ │ │ └── videos_file.rb
│ │ ├── event.rb
│ │ ├── event_involvement.rb
│ │ ├── event_participation.rb
│ │ ├── event_series/
│ │ │ ├── static_metadata.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── event_series.rb
│ │ ├── favorite_user.rb
│ │ ├── geocode_result.rb
│ │ ├── language.rb
│ │ ├── llm/
│ │ │ ├── client.rb
│ │ │ └── request.rb
│ │ ├── location.rb
│ │ ├── online_location.rb
│ │ ├── organization/
│ │ │ ├── typesense_searchable.rb
│ │ │ └── wrapped_screenshot_generator.rb
│ │ ├── organization.rb
│ │ ├── password_reset_token.rb
│ │ ├── prompts/
│ │ │ ├── base.rb
│ │ │ ├── talk/
│ │ │ │ ├── enhance_transcript.rb
│ │ │ │ ├── summary.rb
│ │ │ │ └── topics.rb
│ │ │ └── topic/
│ │ │ ├── match_talks.rb
│ │ │ └── suggest_gems.rb
│ │ ├── rollup.rb
│ │ ├── search/
│ │ │ ├── backend/
│ │ │ │ ├── sqlite_fts/
│ │ │ │ │ └── indexer.rb
│ │ │ │ ├── sqlite_fts.rb
│ │ │ │ ├── typesense/
│ │ │ │ │ ├── circuit_breaker.rb
│ │ │ │ │ ├── indexer.rb
│ │ │ │ │ ├── kind_indexer.rb
│ │ │ │ │ ├── language_indexer.rb
│ │ │ │ │ └── location_indexer.rb
│ │ │ │ └── typesense.rb
│ │ │ └── backend.rb
│ │ ├── session.rb
│ │ ├── speaker.rb
│ │ ├── sponsor.rb
│ │ ├── stamp.rb
│ │ ├── state.rb
│ │ ├── static/
│ │ │ ├── backends/
│ │ │ │ ├── array_backend.rb
│ │ │ │ ├── file_backend.rb
│ │ │ │ └── multi_file_backend.rb
│ │ │ ├── city.rb
│ │ │ ├── event.rb
│ │ │ ├── event_series.rb
│ │ │ ├── speaker.rb
│ │ │ ├── sponsor.rb
│ │ │ ├── topic.rb
│ │ │ ├── transcript.rb
│ │ │ └── video.rb
│ │ ├── sticker.rb
│ │ ├── suggestion.rb
│ │ ├── talk/
│ │ │ ├── agents.rb
│ │ │ ├── downloader.rb
│ │ │ ├── index.rb
│ │ │ ├── similar_recommender.rb
│ │ │ ├── sqlite_fts_searchable.rb
│ │ │ ├── thumbnails.rb
│ │ │ ├── transcript.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── talk.rb
│ │ ├── talk_topic.rb
│ │ ├── template.rb
│ │ ├── todo.rb
│ │ ├── topic/
│ │ │ ├── agents.rb
│ │ │ ├── gem_info.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── topic.rb
│ │ ├── topic_gem.rb
│ │ ├── transcript.rb
│ │ ├── uk_nation.rb
│ │ ├── user/
│ │ │ ├── duplicate_detector.rb
│ │ │ ├── index.rb
│ │ │ ├── profiles.rb
│ │ │ ├── speakerdeck_feed.rb
│ │ │ ├── sqlite_fts_searchable.rb
│ │ │ ├── suspicion_detector.rb
│ │ │ ├── talk_recommender.rb
│ │ │ ├── typesense_searchable.rb
│ │ │ ├── watched_talk_seeder.rb
│ │ │ ├── wrapped_image_generator.rb
│ │ │ └── wrapped_screenshot_generator.rb
│ │ ├── user.rb
│ │ ├── user_talk.rb
│ │ ├── watch_list.rb
│ │ ├── watch_list_talk.rb
│ │ ├── watched_talk.rb
│ │ └── youtube/
│ │ ├── null_parser.rb
│ │ ├── video_metadata.rb
│ │ ├── video_metadata_baltic_ruby_2024.rb
│ │ ├── video_metadata_kaigi_on_rails.rb
│ │ └── video_metadata_rails_world.rb
│ ├── schemas/
│ │ ├── address_schema.rb
│ │ ├── cfp_schema.rb
│ │ ├── coordinates_schema.rb
│ │ ├── event_schema.rb
│ │ ├── featured_city_schema.rb
│ │ ├── hotel_schema.rb
│ │ ├── involvement_schema.rb
│ │ ├── location_schema.rb
│ │ ├── maps_schema.rb
│ │ ├── schedule_schema.rb
│ │ ├── series_schema.rb
│ │ ├── speaker_schema.rb
│ │ ├── sponsors_schema.rb
│ │ ├── transcript_schema.rb
│ │ ├── venue_schema.rb
│ │ └── video_schema.rb
│ ├── serializers/
│ │ └── transcript_serializer.rb
│ ├── tools/
│ │ ├── cfp_create_tool.rb
│ │ ├── cfp_info_tool.rb
│ │ ├── cfp_update_tool.rb
│ │ ├── event_create_tool.rb
│ │ ├── event_lookup_tool.rb
│ │ ├── event_series_create_tool.rb
│ │ ├── event_series_events_tool.rb
│ │ ├── event_series_lookup_tool.rb
│ │ ├── event_talks_tool.rb
│ │ ├── geocode_tool.rb
│ │ ├── github_profile_tool.rb
│ │ ├── speaker_lookup_tool.rb
│ │ ├── speaker_talks_tool.rb
│ │ ├── speakerdeck_deck_tool.rb
│ │ ├── speakerdeck_user_decks_tool.rb
│ │ ├── venue_create_tool.rb
│ │ ├── vimeo_video_tool.rb
│ │ ├── youtube_channel_videos_tool.rb
│ │ ├── youtube_playlist_items_tool.rb
│ │ ├── youtube_playlist_tool.rb
│ │ ├── youtube_video_tool.rb
│ │ └── youtube_videos_tool.rb
│ └── views/
│ ├── admin/
│ │ └── suggestions/
│ │ ├── _suggestion.html.erb
│ │ └── index.html.erb
│ ├── analytics/
│ │ └── dashboards/
│ │ ├── daily_page_views.html.erb
│ │ ├── daily_visits.html.erb
│ │ ├── monthly_page_views.html.erb
│ │ ├── monthly_visits.html.erb
│ │ ├── show.html.erb
│ │ ├── top_landing_pages.html.erb
│ │ ├── top_referrers.html.erb
│ │ ├── top_searches.html.erb
│ │ ├── yearly_conferences.html.erb
│ │ └── yearly_talks.html.erb
│ ├── announcements/
│ │ ├── feed.rss.builder
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ ├── avo/
│ │ └── cards/
│ │ ├── _duplicates_summary.html.erb
│ │ ├── _reversed_name_duplicates_list.html.erb
│ │ ├── _same_name_duplicates_list.html.erb
│ │ ├── _suspicious_signals_breakdown.html.erb
│ │ ├── _suspicious_summary.html.erb
│ │ ├── _suspicious_users_list.html.erb
│ │ ├── _unavailable_videos_by_event.html.erb
│ │ ├── _unavailable_videos_list.html.erb
│ │ └── _unavailable_videos_summary.html.erb
│ ├── browse/
│ │ ├── _content_row.html.erb
│ │ ├── _content_row_skeleton.html.erb
│ │ ├── _featured_events.html.erb
│ │ ├── _kind_row.html.erb
│ │ ├── _topic_row.html.erb
│ │ ├── index.html.erb
│ │ └── sections/
│ │ ├── _beginner_friendly.html.erb
│ │ ├── _continue_watching.html.erb
│ │ ├── _deep_dives.html.erb
│ │ ├── _event_rows.html.erb
│ │ ├── _events_attended.html.erb
│ │ ├── _evergreen.html.erb
│ │ ├── _favorite_rubyists.html.erb
│ │ ├── _favorite_speakers.html.erb
│ │ ├── _featured_events.html.erb
│ │ ├── _for_you.html.erb
│ │ ├── _from_bookmarks.html.erb
│ │ ├── _hidden_gems.html.erb
│ │ ├── _inspiring.html.erb
│ │ ├── _language_rows.html.erb
│ │ ├── _mind_blowing.html.erb
│ │ ├── _most_bookmarked.html.erb
│ │ ├── _most_liked.html.erb
│ │ ├── _newest_talks.html.erb
│ │ ├── _popular.html.erb
│ │ ├── _popular_topics.html.erb
│ │ ├── _popular_youtube.html.erb
│ │ ├── _quick_watches.html.erb
│ │ ├── _recently_published.html.erb
│ │ ├── _recommended_community.html.erb
│ │ ├── _talk_kinds.html.erb
│ │ ├── _topic_rows.html.erb
│ │ ├── _trending.html.erb
│ │ └── _unwatched_attended.html.erb
│ ├── cfp/
│ │ ├── _event_list.html.erb
│ │ └── index.html.erb
│ ├── cities/
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── continents/
│ │ ├── countries/
│ │ │ └── index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── contributions/
│ │ ├── _events_without_dates.html.erb
│ │ ├── _events_without_location.html.erb
│ │ ├── _events_without_videos.html.erb
│ │ ├── _introduction.html.erb
│ │ ├── _missing_videos_cue.html.erb
│ │ ├── _speakers_without_github.html.erb
│ │ ├── _talks_dates_out_of_bounds.html.erb
│ │ ├── _talks_without_slides.html.erb
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ ├── coordinates/
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── countries/
│ │ ├── cities/
│ │ │ └── index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ ├── state.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── event_participations/
│ │ ├── create.turbo_stream.erb
│ │ └── destroy.turbo_stream.erb
│ ├── events/
│ │ ├── _card.html.erb
│ │ ├── _event.html.erb
│ │ ├── _event_list.html.erb
│ │ ├── _featured.html.erb
│ │ ├── _featured_card.html.erb
│ │ ├── _featured_card_list.html.erb
│ │ ├── _featured_home.html.erb
│ │ ├── _form.html.erb
│ │ ├── _header.html.erb
│ │ ├── _my_attendance.html.erb
│ │ ├── _my_attendance_day.html.erb
│ │ ├── _my_attendance_talk.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _participation_button.html.erb
│ │ ├── _plain_list.html.erb
│ │ ├── archive/
│ │ │ └── index.html.erb
│ │ ├── attendances/
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── cfp/
│ │ │ └── index.html.erb
│ │ ├── collectibles/
│ │ │ └── index.html.erb
│ │ ├── edit.html.erb
│ │ ├── events/
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── index.html.erb
│ │ ├── involvements/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── new.html.erb
│ │ ├── participants/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── related_talks/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── schedules/
│ │ │ ├── _day.html.erb
│ │ │ ├── _schedule.html.erb
│ │ │ ├── index.html.erb
│ │ │ ├── missing_schedule.html.erb
│ │ │ └── show.html.erb
│ │ ├── series/
│ │ │ ├── _card.html.erb
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ ├── speakers/
│ │ │ └── index.html.erb
│ │ ├── sponsors/
│ │ │ └── index.html.erb
│ │ ├── talks/
│ │ │ └── index.html.erb
│ │ ├── tickets/
│ │ │ └── show.html.erb
│ │ ├── todos/
│ │ │ └── index.html.erb
│ │ ├── venues/
│ │ │ ├── missing_venue.html.erb
│ │ │ └── show.html.erb
│ │ ├── videos/
│ │ │ └── index.html.erb
│ │ └── years/
│ │ ├── _desktop_calendar.html.erb
│ │ ├── _event_hover_card.html.erb
│ │ ├── _header.html.erb
│ │ ├── _mobile_calendar.html.erb
│ │ ├── _yearly_events.html.erb
│ │ └── index.html.erb
│ ├── favorite_users/
│ │ ├── _favorite_user.html.erb
│ │ ├── _form.html.erb
│ │ ├── _list.html.erb
│ │ ├── _no_favorites.html.erb
│ │ └── index.html.erb
│ ├── gems/
│ │ ├── _gem_card.html.erb
│ │ ├── index.html.erb
│ │ ├── index.turbo_stream.erb
│ │ ├── show.html.erb
│ │ ├── talks.html.erb
│ │ └── talks.turbo_stream.erb
│ ├── home/
│ │ └── index.html.erb
│ ├── hover_cards/
│ │ ├── events/
│ │ │ ├── _content.html.erb
│ │ │ └── show.html.erb
│ │ └── users/
│ │ ├── _content.html.erb
│ │ └── show.html.erb
│ ├── identity/
│ │ ├── emails/
│ │ │ └── edit.html.erb
│ │ └── password_resets/
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── layouts/
│ │ ├── application.html.erb
│ │ ├── application.turbo_stream.erb
│ │ ├── mailer.html.erb
│ │ ├── mailer.text.erb
│ │ ├── modal.html.erb
│ │ ├── turbo_rails/
│ │ │ └── frame.turbo_stream.erb
│ │ └── wrapped.html.erb
│ ├── leaderboard/
│ │ ├── _speaker.html.erb
│ │ └── index.html.erb
│ ├── online/
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ └── show.html.erb
│ ├── organizations/
│ │ ├── _card.html.erb
│ │ ├── _compact_card.html.erb
│ │ ├── _header.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _organization.html.erb
│ │ ├── index.html.erb
│ │ ├── logos/
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ └── wrapped/
│ │ ├── index.html.erb
│ │ └── pages/
│ │ └── _summary_card_horizontal.html.erb
│ ├── page/
│ │ ├── about.md
│ │ ├── assets.html.erb
│ │ ├── components/
│ │ │ ├── _avatars.html.erb
│ │ │ ├── _buttons.html.erb
│ │ │ ├── _dropdowns.html.erb
│ │ │ ├── _links.html.erb
│ │ │ ├── _modals.html.erb
│ │ │ ├── _stamps.html.erb
│ │ │ ├── _tabs.html.erb
│ │ │ └── _tooltips.html.erb
│ │ ├── components.html.erb
│ │ ├── contributors.html.erb
│ │ ├── featured.html.erb
│ │ ├── home.html.erb
│ │ ├── privacy.md
│ │ ├── stickers.html.erb
│ │ └── uses.md
│ ├── profiles/
│ │ ├── _actions.html.erb
│ │ ├── _aliases.html.erb
│ │ ├── _events.html.erb
│ │ ├── _form.html.erb
│ │ ├── _header.html.erb
│ │ ├── _header_content.html.erb
│ │ ├── _involvements.html.erb
│ │ ├── _map.html.erb
│ │ ├── _mutual_events.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _notes.html.erb
│ │ ├── _socials.html.erb
│ │ ├── _stamps.html.erb
│ │ ├── _stickers.html.erb
│ │ ├── _tab_layout.html.erb
│ │ ├── _talks.html.erb
│ │ ├── _topics.html.erb
│ │ ├── actions/
│ │ │ ├── _admin.html.erb
│ │ │ ├── _anonymous.html.erb
│ │ │ ├── _owner.html.erb
│ │ │ └── _signed_in.html.erb
│ │ ├── aliases/
│ │ │ └── index.html.erb
│ │ ├── connect/
│ │ │ ├── _claim_profile.html.erb
│ │ │ ├── _friend_prompt.html.erb
│ │ │ ├── _no_profile_found.html.erb
│ │ │ └── show.html.erb
│ │ ├── edit.html.erb
│ │ ├── enhance/
│ │ │ └── update.turbo_stream.erb
│ │ ├── events/
│ │ │ └── index.html.erb
│ │ ├── involvements/
│ │ │ └── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── mutual_events/
│ │ │ └── index.html.erb
│ │ ├── notes/
│ │ │ ├── edit.html.erb
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ ├── stickers/
│ │ │ └── index.html.erb
│ │ ├── talks/
│ │ │ └── index.html.erb
│ │ └── wrapped/
│ │ ├── _share.html.erb
│ │ ├── card.html.erb
│ │ ├── index.html.erb
│ │ ├── pages/
│ │ │ ├── _bookends.html.erb
│ │ │ ├── _closing.html.erb
│ │ │ ├── _conference_buddies.html.erb
│ │ │ ├── _content_mix.html.erb
│ │ │ ├── _contributor.html.erb
│ │ │ ├── _cover.html.erb
│ │ │ ├── _event_map.html.erb
│ │ │ ├── _events_attended.html.erb
│ │ │ ├── _fun_facts.html.erb
│ │ │ ├── _involvements.html.erb
│ │ │ ├── _languages.html.erb
│ │ │ ├── _og_image.html.erb
│ │ │ ├── _passport_holder.html.erb
│ │ │ ├── _speaker_journey.html.erb
│ │ │ ├── _speaking_calendar.html.erb
│ │ │ ├── _stamps.html.erb
│ │ │ ├── _stickers.html.erb
│ │ │ ├── _summary_card.html.erb
│ │ │ ├── _summary_card_horizontal.html.erb
│ │ │ ├── _top_events.html.erb
│ │ │ ├── _top_speakers.html.erb
│ │ │ ├── _top_topics.html.erb
│ │ │ ├── _watch_twin.html.erb
│ │ │ ├── _watching_calendar.html.erb
│ │ │ └── _watching_journey.html.erb
│ │ └── private.html.erb
│ ├── recommendations/
│ │ └── index.html.erb
│ ├── sessions/
│ │ └── new.html.erb
│ ├── settings/
│ │ └── show.html.erb
│ ├── shared/
│ │ ├── _breakpoints.html.erb
│ │ ├── _cfp_countdown_badge.html.erb
│ │ ├── _city_sidebar.html.erb
│ │ ├── _country_card.html.erb
│ │ ├── _date_grouped_events.html.erb
│ │ ├── _event_countdown_badge.html.erb
│ │ ├── _event_row.html.erb
│ │ ├── _events_section.html.erb
│ │ ├── _filter_buttons.html.erb
│ │ ├── _flashes.html.erb
│ │ ├── _footer.html.erb
│ │ ├── _location_header.html.erb
│ │ ├── _location_map.html.erb
│ │ ├── _location_meetups.html.erb
│ │ ├── _location_navigation.html.erb
│ │ ├── _location_past.html.erb
│ │ ├── _location_show.html.erb
│ │ ├── _location_sidebar.html.erb
│ │ ├── _location_stamps.html.erb
│ │ ├── _location_users.html.erb
│ │ ├── _map_layer_controls.html.erb
│ │ ├── _map_time_filter.html.erb
│ │ ├── _meta_talk_card.html.erb
│ │ ├── _month_grouped_events.html.erb
│ │ ├── _navbar.html.erb
│ │ ├── _nearby_events_section.html.erb
│ │ ├── _rubyists_preview.html.erb
│ │ ├── _spotlight_search.html.erb
│ │ ├── _stamps_grid.html.erb
│ │ ├── _stickers_display.html.erb
│ │ ├── _toast.html.erb
│ │ ├── _top_banner.html.erb
│ │ ├── _user_dropdown.html.erb
│ │ ├── _user_mobile_dropdown.html.erb
│ │ └── navbar/
│ │ ├── _link.html.erb
│ │ └── _search_bar.html.erb
│ ├── speakers/
│ │ ├── _card.html.erb
│ │ ├── _speaker.html.erb
│ │ ├── _speaker.json.jbuilder
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── index.turbo_stream.erb
│ │ └── show.html.erb
│ ├── sponsors/
│ │ └── missing/
│ │ └── index.html.erb
│ ├── spotlight/
│ │ ├── events/
│ │ │ ├── _event.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ ├── kinds/
│ │ │ └── index.turbo_stream.erb
│ │ ├── languages/
│ │ │ └── index.turbo_stream.erb
│ │ ├── locations/
│ │ │ └── index.turbo_stream.erb
│ │ ├── organizations/
│ │ │ └── index.turbo_stream.erb
│ │ ├── series/
│ │ │ └── index.turbo_stream.erb
│ │ ├── speakers/
│ │ │ ├── _speaker.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ ├── talks/
│ │ │ ├── _talk.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ └── topics/
│ │ └── index.turbo_stream.erb
│ ├── stamps/
│ │ └── index.html.erb
│ ├── states/
│ │ ├── cities/
│ │ │ └── index.html.erb
│ │ ├── country_index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── talks/
│ │ ├── _card.html.erb
│ │ ├── _card_horizontal.html.erb
│ │ ├── _card_thumbnail.html.erb
│ │ ├── _event.html.erb
│ │ ├── _event_tab.html.erb
│ │ ├── _explore_event.html.erb
│ │ ├── _form.html.erb
│ │ ├── _schedule_tab.html.erb
│ │ ├── _sections.html.erb
│ │ ├── _similar_talks_tab.html.erb
│ │ ├── _speaker_tab.html.erb
│ │ ├── _talk.html.erb
│ │ ├── _talk_filter.html.erb
│ │ ├── _transcript.html.erb
│ │ ├── _video_player.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── new.html.erb
│ │ ├── recommendations/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── show.json.jbuilder
│ │ ├── slides/
│ │ │ └── show.html.erb
│ │ ├── video_providers/
│ │ │ ├── _children.html.erb
│ │ │ ├── _mp4.html.erb
│ │ │ ├── _not_published.html.erb
│ │ │ ├── _not_recorded.html.erb
│ │ │ ├── _scheduled.html.erb
│ │ │ ├── _video_unavailable.html.erb
│ │ │ ├── _vimeo.html.erb
│ │ │ └── _youtube.html.erb
│ │ └── watched_talks/
│ │ ├── _button.html.erb
│ │ ├── _feedback_banner.html.erb
│ │ ├── _feedback_questions.html.erb
│ │ ├── _form.html.erb
│ │ ├── _watched_overlay.html.erb
│ │ ├── _where_watched.html.erb
│ │ ├── create.turbo_stream.erb
│ │ ├── destroy.turbo_stream.erb
│ │ ├── new.html.erb
│ │ ├── toggle_attendance.turbo_stream.erb
│ │ └── update.turbo_stream.erb
│ ├── templates/
│ │ ├── _talk_fields.html.erb
│ │ ├── create.turbo_stream.erb
│ │ ├── delete_child.turbo_stream.erb
│ │ ├── new.html.erb
│ │ ├── new_child.turbo_stream.erb
│ │ └── speakers_search.turbo_stream.erb
│ ├── todos/
│ │ ├── _by_event.html.erb
│ │ ├── _by_type.html.erb
│ │ └── index.html.erb
│ ├── topics/
│ │ ├── _badge.html.erb
│ │ ├── _badge_list.html.erb
│ │ ├── _gem_info.html.erb
│ │ ├── _talks_cursor.html.erb
│ │ ├── _topic.html.erb
│ │ ├── index.html.erb
│ │ ├── index.turbo_stream.erb
│ │ ├── show.html.erb
│ │ └── show.turbo_stream.erb
│ ├── users/
│ │ └── _card.html.erb
│ ├── watch_lists/
│ │ ├── _watch_list.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── watched_talks/
│ │ └── index.html.erb
│ └── wrapped/
│ ├── _card.html.erb
│ ├── _featured.html.erb
│ └── index.html.erb
├── bin/
│ ├── brakeman
│ ├── bundle
│ ├── bundler-audit
│ ├── ci
│ ├── dev
│ ├── docker-entrypoint
│ ├── dump_prod.sh
│ ├── jobs
│ ├── lint
│ ├── mcp_server
│ ├── rails
│ ├── rake
│ ├── rubocop
│ ├── setup
│ ├── thrust
│ ├── update_lrug_meetups
│ └── vite
├── config/
│ ├── application.rb
│ ├── appsignal.yml
│ ├── boot.rb
│ ├── bundler-audit.yml
│ ├── cable.yml
│ ├── cache.yml
│ ├── ci.rb
│ ├── credentials/
│ │ ├── development.key
│ │ ├── development.yml.enc
│ │ ├── production.yml.enc
│ │ ├── staging.yml.enc
│ │ ├── test.key
│ │ └── test.yml.enc
│ ├── database.yml
│ ├── deploy.staging.yml
│ ├── deploy.yml
│ ├── environment.rb
│ ├── environments/
│ │ ├── development.rb
│ │ ├── production.rb
│ │ ├── staging.rb
│ │ └── test.rb
│ ├── initializers/
│ │ ├── active_genie.rb
│ │ ├── ahoy.rb
│ │ ├── assets.rb
│ │ ├── avo.rb
│ │ ├── content_security_policy.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── geocoder.rb
│ │ ├── groupe_date.rb
│ │ ├── inflections.rb
│ │ ├── locale.rb
│ │ ├── markdown.rb
│ │ ├── meta_tags.rb
│ │ ├── omniauth.rb
│ │ ├── openai.rb
│ │ ├── pagy.rb
│ │ ├── permissions_policy.rb
│ │ ├── reactionview.rb
│ │ ├── ruby_llm.rb
│ │ ├── typesense.rb
│ │ ├── yjit.rb
│ │ └── yt.rb
│ ├── litestream.staging.yml
│ ├── litestream.yml
│ ├── locales/
│ │ ├── en.yml
│ │ ├── models/
│ │ │ └── event_participation.en.yml
│ │ └── transliterate.en.yml
│ ├── puma.rb
│ ├── queue.yml
│ ├── recurring.yml
│ ├── routes.rb
│ ├── storage.yml
│ └── vite.json
├── config.ru
├── content/
│ └── announcements/
│ ├── 2026-01-01-happy-new-year-2026.md
│ ├── 2026-01-21-introducing-web-components.md
│ ├── 2026-01-22-announcements-and-news.md
│ ├── 2026-03-01-february-newsletter.md
│ └── 2026-04-01-march-newsletter.md
├── data/
│ ├── acts-as-conference/
│ │ ├── acts-as-conference-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── acts-as-conference-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── african-ruby-community/
│ │ ├── african-ruby-mini-conference-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── akashi-rb/
│ │ ├── akashi-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── aloha-on-rails/
│ │ ├── aloha-on-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── aloha-rubyconf/
│ │ ├── aloha-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── amsterdam-rb/
│ │ ├── amsterdam-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ancient-city-ruby/
│ │ ├── ancient-city-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── arrrrcamp/
│ │ ├── arrrrcamp-2009-may/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2009-october/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── artificialruby/
│ │ ├── artificialruby/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── austinrb/
│ │ ├── austinrb/
│ │ │ ├── cfp.yml
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── balkanruby/
│ │ ├── balkanruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── balticruby/
│ │ ├── balticruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balticruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balticruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bangkok-rb/
│ │ ├── ruby-tuesday/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── barcelona-rb/
│ │ ├── barcelona-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── baruco/
│ │ ├── barcelona-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bathruby/
│ │ ├── bathruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── bathruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── bathruby-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── belfastruby/
│ │ ├── belfast-rubyfest-2026/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── belfastruby-meetup/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── big-ruby/
│ │ ├── big-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── big-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── birmingham-on-rails/
│ │ ├── birmingham-on-rails-2020/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── blastoffrails/
│ │ ├── blastoffrails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── blue-ridge-ruby/
│ │ ├── blue-ridge-ruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── blue-ridge-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── blue-ridge-ruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bluegrass-ruby/
│ │ ├── bluegrass-ruby/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bridgetownconf/
│ │ ├── bridgetownconf-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── brightonruby/
│ │ ├── brightonruby-2014/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2017/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2018/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2020/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2022/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── burlington-ruby-conference/
│ │ ├── burlington-ruby-conference-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── cascadia-ruby/
│ │ ├── cascadia-ruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── cascadia-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── cascadia-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ceru-camp/
│ │ ├── ceru-camp-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── chicagoruby/
│ │ ├── chicagoruby/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── conferencia-rails/
│ │ ├── conferencia-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── conferencia-rails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── conferencia-rails-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── deccan-queen-on-rails/
│ │ ├── deccan-queen-on-rails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── deccanrubyconf/
│ │ ├── deccanrubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── dotrb/
│ │ ├── dotrb-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── dresden-rb/
│ │ ├── dresden-rb-meetup/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── emea-on-rails/
│ │ ├── emea-on-rails-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── erubycon/
│ │ ├── erubycon-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── eurucamp/
│ │ ├── eurucamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── eurucamp-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── euruko/
│ │ ├── euruko-2003/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2004/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2005/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── noruko-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── featured_cities.yml
│ ├── fosdem/
│ │ ├── ruby-devroom-at-fosdem-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── friendly-rb/
│ │ ├── friendly-rb-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── friendly-rb-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── friendly-rb-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── frozen-rails/
│ │ ├── frozen-rails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── fukuoka-rubyistkaigi/
│ │ ├── fukuoka-rubyistkaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubyistkaigi-04/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubyistkaigi-05/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── garden-city-ruby/
│ │ ├── garden-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── garden-city-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── geneva-rb/
│ │ ├── geneva-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── gogaruco/
│ │ ├── gogaruco-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── goruco/
│ │ ├── goruco-2007/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2010/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── grill-rb/
│ │ ├── grill-rb-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── guru-pr/
│ │ ├── series.yml
│ │ └── tech-day-by-guru-pr-2016/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── haggis-ruby/
│ │ ├── haggis-ruby-2024/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── haggis-ruby-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── helsinki-ruby/
│ │ ├── helsinki-ruby-brigade/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── helveticruby/
│ │ ├── helveticruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2026/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── hirakatarb/
│ │ ├── hirakatarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── hokuriku-rubykaigi/
│ │ ├── hokuriku-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── isle-of-ruby/
│ │ ├── isle-of-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── jrubyconf-eu/
│ │ ├── jrubyconf-eu-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kaigi-on-rails/
│ │ ├── kaigi-on-rails-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kansai-rubykaigi/
│ │ ├── kansai-rubykaigi-08/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── kansai-rubykaigi-09/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kashiwarb/
│ │ ├── kashiwarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── keeprubyweird/
│ │ ├── keep-ruby-weird-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kiwi-ruby/
│ │ ├── kiwi-ruby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kiwi-ruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── koberb/
│ │ ├── koberb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── krk-rb/
│ │ ├── krk-rb-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kyobashirb/
│ │ ├── kyobashirb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kyotorb/
│ │ ├── kyotorb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── la-conf/
│ │ └── series.yml
│ ├── la-rubyconf/
│ │ ├── la-rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── latvian-ruby-community/
│ │ ├── latviarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── london-ruby-unconference/
│ │ ├── london-ruby-unconference-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── london-ruby-unconference-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── london-ruby-unconference-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lone-star-ruby-conf/
│ │ ├── lone-star-ruby-conf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lrug/
│ │ ├── lrug-meetup/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lyon-rb/
│ │ ├── lyon-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── madison-ruby/
│ │ ├── madison-ruby-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2012/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2013/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2014/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2015/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── madrid-rb/
│ │ ├── madrid-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── magic-ruby/
│ │ ├── magic-ruby-2011/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── magic-ruby-2012/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── matsue-rubykaigi/
│ │ ├── matsue-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-05/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-06/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-07/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-08/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-09/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-10/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-11/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-12/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── merseyrails/
│ │ ├── merseyrails/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── montreal-rb/
│ │ ├── montreal-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── mountainwest-rubyconf/
│ │ ├── mountainwest-rubyconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── munich-rubyshift/
│ │ ├── munich-rubyshift/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nairuby/
│ │ ├── rubyconf-kenya-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2017/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── naniwarb/
│ │ ├── naniwarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── picorubyoverflowkaigi-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nantes-rb/
│ │ ├── nantes-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nepal-ruby/
│ │ ├── nepal-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nickel-city-ruby/
│ │ ├── nickel-city-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nickel-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nordicruby/
│ │ ├── nordicruby-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── oedo-rubykaigi/
│ │ ├── oedo-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── osaka-rubykaigi/
│ │ ├── osaka-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── oxente-rails/
│ │ ├── oxente-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── paris-rb/
│ │ ├── paris-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── paris-rb-conf/
│ │ ├── paris-rb-conf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── paris-rb-conf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── philly-rb/
│ │ ├── philly-rb/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── pivorak/
│ │ ├── pivorak-conf-1/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-2/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-3/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-4/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-5/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── polishrubyusergroup/
│ │ ├── ruby-warsaw-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── poznanrubyusergroup/
│ │ ├── poznan-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-at-scale-summit/
│ │ ├── rails-at-scale-summit-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-at-scale-summit-2026/
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── rails-camp-south/
│ │ ├── rails-camp-south-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-uk/
│ │ ├── rails-camp-uk-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-us/
│ │ ├── rails-camp-us-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-west/
│ │ ├── rails-camp-west-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2025/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rails-girls-sao-paulo/
│ │ ├── rails-girls-sao-paulo-evolution-2026/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rails-hackathon/
│ │ ├── rails-hackathon-community/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rails-hackathon-hotwire/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-israel/
│ │ ├── rails-israel-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-konferenz/
│ │ ├── rails-konferenz-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-outreach-workshop/
│ │ ├── rails-outreach-workshop-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-pacific/
│ │ ├── rails-pacific-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-pacific-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-remote-conf/
│ │ ├── rails-remote-conf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-saas-conference/
│ │ ├── rails-saas-conference-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-saas-conference-2023/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-studio/
│ │ ├── rails-studio-2006-pasadena/
│ │ │ └── event.yml
│ │ ├── rails-studio-portland/
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── rails-summit-latin-america/
│ │ ├── rails-summit-latin-america-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-to-italy/
│ │ └── series.yml
│ ├── rails-underground/
│ │ ├── rails-underground-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-world/
│ │ ├── rails-world-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsberry/
│ │ ├── railsberry-2012/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsclub/
│ │ ├── railsclub-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsconf/
│ │ ├── railsconf-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsconf-europe/
│ │ ├── railsconf-europe-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-europe-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-europe-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railswaycon/
│ │ ├── railswaycon-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rbqconf/
│ │ ├── rbqconf-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── red-dirt-rubyconf/
│ │ ├── red-dirt-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dirt-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── red-dot-ruby-conference/
│ │ ├── red-dot-ruby-conference-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── redfrogconf/
│ │ ├── redfrogconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rocky-mountain-ruby/
│ │ ├── rocky-mountain-ruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rossconf/
│ │ ├── rossconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2015-berlin/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rs-on-rails/
│ │ ├── rs-on-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-argentina/
│ │ ├── rubysur-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-australia/
│ │ ├── ruby-australia-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-banitsa/
│ │ ├── ruby-banitsa-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-banitsa-conf/
│ │ ├── ruby-banitsa-conf-2024/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-community-conference/
│ │ ├── ruby-community-conference-summer-edition-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-summer-edition-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2026/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-cwb/
│ │ ├── ruby-cwb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-dcamp/
│ │ ├── ruby-dcamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-dev-summit/
│ │ ├── ruby-dev-summit-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-df/
│ │ ├── ruby-df-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-foo/
│ │ ├── ruby-foo-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-for-good/
│ │ ├── ruby-for-good-2016/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2020/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2023/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2024/
│ │ │ ├── event.yml
│ │ │ └── venue.yml
│ │ ├── ruby-for-good-2025/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-belgium-2025/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-belgium-2026/
│ │ │ ├── event.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── ruby-frankfurt/
│ │ ├── ruby-frankfurt/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-fringe/
│ │ ├── futureruby-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfringe-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-galaxy/
│ │ ├── ruby-galaxy-v01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v03/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v05/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v06/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-hoedown/
│ │ ├── nuby-hoedown-2010/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2007/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2008/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2010/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-in-common/
│ │ ├── ruby-in-common-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-in-london/
│ │ ├── ruby-in-london-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-india/
│ │ ├── pune-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-maizuru/
│ │ ├── ruby-maizuru-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-manor/
│ │ ├── ruby-manor-1/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-2/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-3/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-4/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-midwest/
│ │ ├── ruby-midwest-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-midwest-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-montevideo/
│ │ ├── ruby-montevideo/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-ales/
│ │ ├── ruby-on-ales-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-ice/
│ │ ├── ruby-on-ice-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ice-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-rails-global-summit/
│ │ ├── ruby-on-rails-global-summit-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-rails-switzerland/
│ │ ├── railshock/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-waves/
│ │ ├── ruby-on-waves-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-phil/
│ │ ├── ruby-phil/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-retreat-au/
│ │ ├── rails-camp-au-1-2007/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-10-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-11-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-12-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-13-2013/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-14-2013/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-15-2014/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-16-2014/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-17-2015/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-18-2015/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-19-2016/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-2-2007/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-20-2016/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-21-2017/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-22-2017/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-23-2018/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-24-2018/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-25-2019/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-26-2019/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-27-2022/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-3-2008/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-4-2008/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-5-2009/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-6-2009/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-7-2010/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-8-2010/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-9-2011/
│ │ │ └── event.yml
│ │ ├── ruby-retreat-au-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-au-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-retreat-nz/
│ │ ├── ruby-retreat-nz-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-nz-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-nz-2025/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-romania/
│ │ ├── ruby-romania-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-summit-china/
│ │ ├── ruby-summit-china-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-tuesday/
│ │ ├── ruby-tuesday-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-turkiye/
│ │ ├── ruby-turkiye/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-web-conference/
│ │ ├── ruby-web-conference-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-wine/
│ │ ├── ruby-wine-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-wine-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubybelgium/
│ │ ├── rubybelgium-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyc/
│ │ ├── rubyc-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubycon/
│ │ ├── rubycon-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf/
│ │ ├── rubyconf-2001/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2002/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2003/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2004/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2005/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2022-mini/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-africa/
│ │ ├── rubyconf-africa-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-africa-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-africa-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-argentina/
│ │ ├── rubyconf-argentina-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-au/
│ │ ├── rubyconf-au-2013/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2014/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2016/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2017/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-austria/
│ │ ├── rubyconf-austria-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-brazil/
│ │ ├── rubyconf-brazil-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-by/
│ │ ├── rubyconfby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-china/
│ │ ├── rubyconf-china-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-china-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-china-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-colombia/
│ │ ├── rubyconf-colombia-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-india/
│ │ ├── rubyconf-india-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2019/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-indonesia/
│ │ ├── rubyconf-indonesia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-jakarta/
│ │ ├── rubyconf-jakarta-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-lt/
│ │ ├── rubyconf-lt-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-lt-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-lt-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-my/
│ │ ├── rubyconf-kl-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-my-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-my-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-philippines/
│ │ ├── rubyconf-philippines-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-portugal/
│ │ ├── rubyconf-portugal-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-portugal-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-portugal-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-taiwan/
│ │ ├── rubyconf-taiwan-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-uruguay/
│ │ ├── rubyconf-uruguay-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-uruguay-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconfth/
│ │ ├── rubyconf-th-2020/
│ │ │ └── event.yml
│ │ ├── rubyconfth-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2022/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2023/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyday/
│ │ ├── rubyday-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2015/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyenrails/
│ │ ├── rubyandrails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyeurope/
│ │ ├── rubyeurope-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyevents/
│ │ ├── rubyevents/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyfuza/
│ │ ├── rubyfuza-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyhack/
│ │ ├── rubyhack-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhack-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhack-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyhiroba/
│ │ ├── rubyhiroba-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhiroba-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubykaigi/
│ │ ├── rubykaigi-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubykansai/
│ │ ├── rubykansai-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubymotion-inspect/
│ │ ├── rubymotion-inspect-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubymotion-inspect-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubymx/
│ │ ├── rubymx-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubynation/
│ │ ├── rubynation-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyness/
│ │ ├── rubyness-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyrussia/
│ │ ├── rubyrussia-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyrx/
│ │ ├── rubyrx-2009-philadelphia/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrx-2009-washington-dc/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubysauna/
│ │ ├── rubysauna-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyunconf/
│ │ ├── rubyunconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyworld/
│ │ ├── rubyworld-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2024/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rulu/
│ │ ├── rulu-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rupy/
│ │ ├── rupy-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── saint-p-rubyconf/
│ │ ├── saint-p-rubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── scotlandonrails/
│ │ ├── scotlandonrails-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scotlandonrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── scottish-ruby-conf/
│ │ ├── scottish-ruby-conf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── sekigahara-rubykaigi/
│ │ ├── sekigahara-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── sf-bay-area-ruby/
│ │ ├── series.yml
│ │ └── sf-bay-area-ruby-meetup/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── sfruby/
│ │ ├── series.yml
│ │ └── sfruby-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── shinosakarb/
│ │ ├── series.yml
│ │ └── shinosakarb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── shrug/
│ │ ├── series.yml
│ │ └── shrug-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── sin-city-ruby/
│ │ ├── series.yml
│ │ ├── sin-city-ruby-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── sin-city-ruby-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── sin-city-ruby-2025/
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── solidusconf/
│ │ ├── series.yml
│ │ ├── solidusconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── solidusconf-2022/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── southeast-ruby/
│ │ ├── series.yml
│ │ ├── southeast-ruby-2017/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── southeast-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── southeast-ruby-2019/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── speakers.yml
│ ├── steelcityruby/
│ │ ├── series.yml
│ │ ├── steelcityruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── steelcityruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── steelcityruby-2014/
│ │ ├── event.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── the-rails-edge/
│ │ ├── series.yml
│ │ └── the-rails-edge-2007/
│ │ ├── event.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── thoughtbot-open-summit/
│ │ ├── series.yml
│ │ ├── thoughtbot-open-summit-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── thoughtbot-open-summit-2025/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── tiny-ruby-conf/
│ │ ├── series.yml
│ │ ├── tiny-ruby-conf-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── tiny-ruby-conf-2026/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ └── venue.yml
│ ├── tokyo-rubykaigi/
│ │ ├── series.yml
│ │ ├── tokyo-rubykaigi-10/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── tokyo-rubykaigi-11/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── tokyo-rubykaigi-12/
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── topics.yml
│ ├── toronto-ruby/
│ │ ├── series.yml
│ │ └── toronto-ruby-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── tropicalrb/
│ │ ├── abril-pro-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── abril-pro-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── abril-pro-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── series.yml
│ │ ├── tropical-on-rails-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── tropical-on-rails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── tropicalrb-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── tropicalrb-2024/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── umedarb/
│ │ ├── series.yml
│ │ └── umedarb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── valencia-rb/
│ │ ├── series.yml
│ │ └── valencia-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── vienna-rb/
│ │ ├── series.yml
│ │ └── vienna-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── wakayama-rb/
│ │ ├── series.yml
│ │ └── wakayama-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── windycityrails/
│ │ ├── series.yml
│ │ ├── windycityrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── windycityrails-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── windycityrails-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── windycityrails-2016/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── winnipeg-rb/
│ │ ├── series.yml
│ │ └── winnipeg-rb-meetup/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── wnb-rb/
│ │ ├── series.yml
│ │ └── wnb-rb-meetup/
│ │ ├── event.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── wroclove-rb/
│ │ ├── series.yml
│ │ ├── wroclove-rb-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── wroclove-rb-2026/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ └── xoruby/
│ ├── series.yml
│ ├── xoruby-atlanta-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-austin-2025/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-chicago-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-new-orleans-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-portland-2025/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ └── xoruby-san-diego-2025/
│ ├── event.yml
│ ├── involvements.yml
│ ├── schedule.yml
│ ├── sponsors.yml
│ ├── venue.yml
│ └── videos.yml
├── db/
│ ├── cache_migrate/
│ │ ├── 20240516085648_create_solid_cache_entries.solid_cache.rb
│ │ ├── 20240516085649_add_key_hash_and_byte_size_to_solid_cache_entries.solid_cache.rb
│ │ ├── 20240516085650_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.solid_cache.rb
│ │ └── 20240516085651_remove_key_index_from_solid_cache_entries.solid_cache.rb
│ ├── cache_schema.rb
│ ├── migrate/
│ │ ├── 20230512050041_create_speakers.rb
│ │ ├── 20230512051225_create_talks.rb
│ │ ├── 20230521202931_create_join_table_speakers_talks.rb
│ │ ├── 20230521211825_add_talks_count_to_speaker.rb
│ │ ├── 20230522051939_create_suggestions.rb
│ │ ├── 20230522133312_create_users.rb
│ │ ├── 20230522133313_create_sessions.rb
│ │ ├── 20230522133314_create_password_reset_tokens.rb
│ │ ├── 20230522133315_create_email_verification_tokens.rb
│ │ ├── 20230522212710_create_events.rb
│ │ ├── 20230522213244_add_event_references_to_talk.rb
│ │ ├── 20230525132507_add_index_slug_to_speaker.rb
│ │ ├── 20230604215247_create_ahoy_visits_and_events.rb
│ │ ├── 20230614230539_rename_event_table_to_organisation.rb
│ │ ├── 20230614231651_create_new_events.rb
│ │ ├── 20230614232412_add_youtube_channel_id_to_organisation.rb
│ │ ├── 20230614233712_add_slug_to_organisation.rb
│ │ ├── 20230614234246_add_name_to_event.rb
│ │ ├── 20230615053959_add_slug_to_event.rb
│ │ ├── 20230615055319_add_thumbnails_to_talk.rb
│ │ ├── 20230615055800_fix_foreign_constrain.rb
│ │ ├── 20230615105716_add_date_to_talk.rb
│ │ ├── 20230622202051_add_upvotes_and_views_to_talks.rb
│ │ ├── 20230625215935_add_twitter_to_organisation.rb
│ │ ├── 20230720104208_add_uniq_index_speaker_talk.rb
│ │ ├── 20230720151537_add_primary_key_to_speaker_talks.rb
│ │ ├── 20240611113918_add_transcript_to_talk.rb
│ │ ├── 20240614060121_add_service_name_to_active_storage_blobs.active_storage.rb
│ │ ├── 20240614060122_create_active_storage_variant_records.active_storage.rb
│ │ ├── 20240614060123_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│ │ ├── 20240709194147_add_enhanced_transcript_to_talk.rb
│ │ ├── 20240709200506_rename_transcript_in_talk.rb
│ │ ├── 20240718202658_add_summary_to_talk.rb
│ │ ├── 20240811121204_create_topics.rb
│ │ ├── 20240811122145_create_talk_topics.rb
│ │ ├── 20240815155647_add_uniq_index_on_talk_topic.rb
│ │ ├── 20240816074626_add_status_to_topic.rb
│ │ ├── 20240817083428_add_canonical_reference_to_topic.rb
│ │ ├── 20240818212042_add_language_to_talk.rb
│ │ ├── 20240819134138_drop_year_from_talk.rb
│ │ ├── 20240821191208_add_talks_count_to_event.rb
│ │ ├── 20240823221832_add_slides_url_to_talks.rb
│ │ ├── 20240901163900_add_canonical_reference_to_speaker.rb
│ │ ├── 20240902204752_add_canonical_reference_to_event.rb
│ │ ├── 20240908072757_create_watch_list_talks.rb
│ │ ├── 20240908072819_create_watch_lists.rb
│ │ ├── 20240909194059_create_connected_accounts.rb
│ │ ├── 20240912163015_add_name_to_user.rb
│ │ ├── 20240912163159_encrypt_user_fields.rb
│ │ ├── 20240912164120_encrypt_connected_account_field.rb
│ │ ├── 20240914162252_add_github_handle_to_user.rb
│ │ ├── 20240918050951_add_approved_by_to_suggestion.rb
│ │ ├── 20240918215740_add_suggestor_to_suggestions.rb
│ │ ├── 20240919193323_add_index_on_updated_at_for_talks.rb
│ │ ├── 20240920054416_add_talks_count_to_topic.rb
│ │ ├── 20240920154237_add_talks_count_to_watch_list.rb
│ │ ├── 20241019135118_create_talk_fts.rb
│ │ ├── 20241020174649_add_speakerdeck_to_speaker.rb
│ │ ├── 20241022012631_add_summarized_using_ai_to_talk.rb
│ │ ├── 20241023093844_add_pronouns_to_speaker.rb
│ │ ├── 20241023150341_remove_index_talk_topics_on_talk_id.rb
│ │ ├── 20241023154126_recreate_talk_topic_indexes.rb
│ │ ├── 20241029112719_add_mastodown_bluesky_linkedin_to_speaker.rb
│ │ ├── 20241031112333_add_kind_to_talk.rb
│ │ ├── 20241101185604_add_external_player_to_talk.rb
│ │ ├── 20241105143943_add_website_to_events.rb
│ │ ├── 20241105151601_create_watched_talks.rb
│ │ ├── 20241108215612_add_indexes_speaker_talks.rb
│ │ ├── 20241122155013_add_index_on_slug_for_topics.rb
│ │ ├── 20241122163052_add_index_on_kind_for_talks.rb
│ │ ├── 20241128073415_add_bsky_metadata_to_speaker.rb
│ │ ├── 20241130095835_add_parent_talk_to_talk.rb
│ │ ├── 20241203001515_add_github_metadata_to_speaker.rb
│ │ ├── 20241227192232_add_duration_to_talk.rb
│ │ ├── 20250102175230_create_speaker_full_text_search.rb
│ │ ├── 20250104095544_create_talk_transcript.rb
│ │ ├── 20250104230927_create_rollups.rb
│ │ ├── 20250108221813_set_view_count_default_on_talk.rb
│ │ ├── 20250115215944_add_index_video_provider_to_talk.rb
│ │ ├── 20250124210823_add_discarded_at_speaker_talk.rb
│ │ ├── 20250127012711_add_published_at_and_announced_at_to_talk.rb
│ │ ├── 20250128070756_add_unique_index_on_github_in_speaker.rb
│ │ ├── 20250128085252_remove_github_of_speaker_with_canonical.rb
│ │ ├── 20250218073648_normalize_github_handle.rb
│ │ ├── 20250426063312_add_llm_requests.rb
│ │ ├── 20250427062021_add_success_to_llm_requests.rb
│ │ ├── 20250521011731_add_original_title_to_talks.rb
│ │ ├── 20250524174213_normalize_speaker_website.rb
│ │ ├── 20250605203429_add_start_and_end_date_to_event.rb
│ │ ├── 20250609072113_remove_talk_parent_id_self.rb
│ │ ├── 20250614131810_add_kind_to_event.rb
│ │ ├── 20250617115359_add_date_precision_to_events.rb
│ │ ├── 20250618000304_add_call_for_papers_to_events.rb
│ │ ├── 20250719180249_create_sponsors.rb
│ │ ├── 20250719190411_add_slug_to_sponsors.rb
│ │ ├── 20250719200918_create_event_sponsors.rb
│ │ ├── 20250719204509_add_banner_url_to_sponsors.rb
│ │ ├── 20250719205313_add_main_location_to_sponsors.rb
│ │ ├── 20250719205756_remove_url_fields_from_sponsors.rb
│ │ ├── 20250719225634_add_tier_to_event_sponsors.rb
│ │ ├── 20250719231642_add_logo_url_to_sponsors.rb
│ │ ├── 20250808095915_add_logo_urls_to_sponsors.rb
│ │ ├── 20250808192916_clean_sponsor_website_urls.rb
│ │ ├── 20250809194302_add_badge_to_event_sponsors.rb
│ │ ├── 20250813211012_decrypt_user_name.rb
│ │ ├── 20250814214127_add_speaker_attributes_to_users.rb
│ │ ├── 20250814214146_create_user_talks.rb
│ │ ├── 20250814214412_create_users_search_index.rb
│ │ ├── 20250817060410_set_users_email_null_true.rb
│ │ ├── 20250817223427_update_user_indexes.rb
│ │ ├── 20250819022348_create_cfps.rb
│ │ ├── 20250820183955_add_progress_seconds_to_watched_talks.rb
│ │ ├── 20250820225005_add_watched_talks_count_to_users.rb
│ │ ├── 20250822175423_add_unique_index_to_event_sponsors.rb
│ │ ├── 20250823020826_remove_cfp_fields_from_events.rb
│ │ ├── 20250901185702_update_suggestions_suggestable.rb
│ │ ├── 20250903125458_create_event_participations.rb
│ │ ├── 20250920194438_add_country_code_to_event.rb
│ │ ├── 20250930165602_add_location_to_user.rb
│ │ ├── 20251010092526_create_event_involvements.rb
│ │ ├── 20251012114916_create_contributors.rb
│ │ ├── 20251019071129_add_case_insensitive_github_handle_index_to_users.rb
│ │ ├── 20251019212653_remove_badge_awards_table.rb
│ │ ├── 20251028143012_create_aliases.rb
│ │ ├── 20251028143916_add_marked_for_deletion_to_users.rb
│ │ ├── 20251028143917_migrate_canonical_users_to_aliases.rb
│ │ ├── 20251029032709_rename_organisation_to_event_series.rb
│ │ ├── 20251126171800_rename_sponsor_to_organization.rb
│ │ ├── 20251126171948_rename_event_sponsor_to_sponsor.rb
│ │ ├── 20251126203734_add_coordinates_to_events.rb
│ │ ├── 20251201143109_add_event_names_to_talks_search_index.rb
│ │ ├── 20251201144052_change_slug_to_optional_in_aliases.rb
│ │ ├── 20251203110611_change_youtube_channel_id_nullable_on_event_series.rb
│ │ ├── 20251203110659_change_youtube_channel_name_nullable_on_event_series.rb
│ │ ├── 20251204145850_add_static_id_to_talks.rb
│ │ ├── 20251204195048_change_static_id_null_constraint_on_talks.rb
│ │ ├── 20251206194409_add_additional_resources_to_talks.rb
│ │ ├── 20260101090225_add_wrapped_public_to_users.rb
│ │ ├── 20260101125732_create_active_storage_tables.active_storage.rb
│ │ ├── 20260103114901_add_feedback_to_watched_talks.rb
│ │ ├── 20260103122047_add_feedback_shared_at_to_watched_talks.rb
│ │ ├── 20260103125129_backfill_watched_at_on_watched_talks.rb
│ │ ├── 20260103130348_add_watched_to_watched_talks.rb
│ │ ├── 20260103134407_add_feedback_enabled_to_users.rb
│ │ ├── 20260103210603_add_settings_to_users.rb
│ │ ├── 20260103232220_create_topic_gems.rb
│ │ ├── 20260104181518_create_favorite_users.rb
│ │ ├── 20260105100000_add_indexes_to_ahoy_visits_for_cleanup.rb
│ │ ├── 20260105120000_add_video_provider_to_talks_search_index.rb
│ │ ├── 20260106092124_add_geocoding_to_users.rb
│ │ ├── 20260106121436_add_state_to_events.rb
│ │ ├── 20260106121459_create_featured_cities.rb
│ │ ├── 20260106135053_create_geocode_results.rb
│ │ ├── 20260106141308_add_location_to_events.rb
│ │ ├── 20260106232656_add_marked_suspicious_at_to_users.rb
│ │ ├── 20260109095858_add_geocode_metadata_to_events.rb
│ │ ├── 20260109152525_change_aliases_slug_index.rb
│ │ ├── 20260110193214_rename_featured_cities_to_cities.rb
│ │ ├── 20260110194925_rename_state_to_state_code_on_events_and_users.rb
│ │ ├── 20260110201634_remove_city_column_from_cities.rb
│ │ ├── 20260112065507_add_youtube_thumbnail_checked_at_to_talks.rb
│ │ ├── 20260112071922_add_video_availability_fields_to_talks.rb
│ │ ├── 20260301182003_add_notes_to_favorite_user.rb
│ │ └── 20260306110802_add_level_to_sponsors.rb
│ ├── queue_migrate/
│ │ ├── 20240516090838_create_solid_queue_tables.solid_queue.rb
│ │ ├── 20240516090839_add_missing_index_to_blocked_executions.solid_queue.rb
│ │ ├── 20240516090840_create_recurring_executions.solid_queue.rb
│ │ ├── 20240814230055_create_recurring_tasks.solid_queue.rb
│ │ ├── 20240908182634_add_name_to_processes.solid_queue.rb
│ │ ├── 20240908191029_make_name_not_null.solid_queue.rb
│ │ └── 20240908191030_change_solid_queue_recurring_tasks_static_to_not_null.solid_queue.rb
│ ├── queue_schema.rb
│ ├── schema.rb
│ └── seeds.rb
├── docker-compose.typesense.yml
├── docs/
│ ├── ADDING_CFPS.md
│ ├── ADDING_EVENTS.md
│ ├── ADDING_INVOLVEMENTS.md
│ ├── ADDING_MEETUPS.md
│ ├── ADDING_SCHEDULES.md
│ ├── ADDING_SPONSORS.md
│ ├── ADDING_UNPUBLISHED_TALKS.md
│ ├── ADDING_VENUES.md
│ ├── ADDING_VIDEOS.md
│ ├── ADDING_VISUAL_ASSETS.md
│ └── FIXING_PROFILE_NAMES.md
├── lib/
│ ├── assets/
│ │ └── .keep
│ ├── authenticator.rb
│ ├── generators/
│ │ ├── cfp/
│ │ │ ├── USAGE
│ │ │ ├── cfp_generator.rb
│ │ │ └── templates/
│ │ │ ├── cfp.yml.tt
│ │ │ └── header.yml.tt
│ │ ├── event/
│ │ │ ├── USAGE
│ │ │ ├── event_generator.rb
│ │ │ └── templates/
│ │ │ └── event.yml.tt
│ │ ├── event_base.rb
│ │ ├── schedule/
│ │ │ ├── USAGE
│ │ │ ├── schedule_generator.rb
│ │ │ └── templates/
│ │ │ └── schedule.yml.tt
│ │ ├── sponsors/
│ │ │ ├── USAGE
│ │ │ ├── sponsors_generator.rb
│ │ │ └── templates/
│ │ │ └── sponsors.yml.tt
│ │ ├── talk/
│ │ │ ├── USAGE
│ │ │ ├── talk_generator.rb
│ │ │ └── templates/
│ │ │ ├── lightning_talks.yml.tt
│ │ │ ├── talk.yml.tt
│ │ │ └── videos.yml.tt
│ │ └── venue/
│ │ ├── USAGE
│ │ ├── templates/
│ │ │ ├── location.yml.tt
│ │ │ └── venue.yml.tt
│ │ └── venue_generator.rb
│ ├── guard/
│ │ └── data_import.rb
│ ├── protobuf/
│ │ └── message_type.rb
│ ├── schemas/
│ │ ├── address_schema.json
│ │ ├── cfp_schema.json
│ │ ├── cfps_schema.json
│ │ ├── coordinates_schema.json
│ │ ├── event_schema.json
│ │ ├── featured_city_schema.json
│ │ ├── hotel_schema.json
│ │ ├── involvement_schema.json
│ │ ├── location_schema.json
│ │ ├── maps_schema.json
│ │ ├── schedule_schema.json
│ │ ├── series_schema.json
│ │ ├── speaker_schema.json
│ │ ├── sponsors_schema.json
│ │ ├── tiers_sponsors_schema.json
│ │ ├── transcript_schema.json
│ │ ├── venue_schema.json
│ │ ├── video_schema.json
│ │ └── videos_schema.json
│ ├── tasks/
│ │ ├── annotate_rb.rake
│ │ ├── assets.rake
│ │ ├── backfill_event_involvements.rake
│ │ ├── backfill_speaker_participation.rake
│ │ ├── cities.rake
│ │ ├── cleanup_ahoy_visits.rake
│ │ ├── contributors.rake
│ │ ├── db.rake
│ │ ├── download.rake
│ │ ├── dump.rake
│ │ ├── event_assets.rake
│ │ ├── geolocate.thor
│ │ ├── schema.rake
│ │ ├── search.rake
│ │ ├── seed.rake
│ │ ├── speakerdeck.rake
│ │ ├── speakers.rake
│ │ ├── thumbnails.rake
│ │ ├── topics.rake
│ │ ├── transcripts.rake
│ │ ├── update_video_statistics.rake
│ │ ├── users.rake
│ │ └── validate.rake
│ └── templates/
│ └── erb/
│ └── scaffold/
│ ├── _form.html.erb.tt
│ ├── edit.html.erb.tt
│ ├── index.html.erb.tt
│ ├── new.html.erb.tt
│ ├── partial.html.erb.tt
│ └── show.html.erb.tt
├── log/
│ └── .keep
├── package.json
├── postcss.config.js
├── public/
│ ├── 400.html
│ ├── 404.html
│ ├── 406-unsupported-browser.html
│ ├── 422.html
│ ├── 500.html
│ └── robots.txt
├── scripts/
│ ├── create_events.rb
│ ├── create_videos_yml.rb
│ ├── dump_speakers.rb
│ ├── dump_talks.rb
│ ├── extract_videos.rb
│ ├── import_event.rb
│ ├── prepare_series.rb
│ └── sort_speakers.rb
├── storage/
│ └── .keep
├── tailwind.config.js
├── test/
│ ├── application_system_test_case.rb
│ ├── channels/
│ │ └── application_cable/
│ │ └── connection_test.rb
│ ├── clients/
│ │ └── youtube/
│ │ ├── channels_test.rb
│ │ ├── playlist_items_test.rb
│ │ ├── playlists_test.rb
│ │ ├── transcript_test.rb
│ │ └── video_test.rb
│ ├── components/
│ │ ├── application_component_test.rb
│ │ ├── events/
│ │ │ ├── upcoming_event_banner_component/
│ │ │ │ └── helper_methods_test.rb
│ │ │ └── upcoming_event_banner_component_test.rb
│ │ ├── tito/
│ │ │ ├── button_component_test.rb
│ │ │ └── widget_component_test.rb
│ │ └── ui/
│ │ ├── avatar_component_test.rb
│ │ ├── badge_component_test.rb
│ │ ├── button_component_test.rb
│ │ └── stamp_component_test.rb
│ ├── controllers/
│ │ ├── analytics/
│ │ │ └── dashboards_controller_test.rb
│ │ ├── announcements_controller_test.rb
│ │ ├── cfp_controller_test.rb
│ │ ├── countries_controller_test.rb
│ │ ├── events/
│ │ │ ├── archive_controller_test.rb
│ │ │ ├── meetups_controller_test.rb
│ │ │ ├── participations_controller_test.rb
│ │ │ ├── related_talks_controller_test.rb
│ │ │ ├── schedule_controller_test.rb
│ │ │ ├── series_controller_test.rb
│ │ │ └── sponsors_controller_test.rb
│ │ ├── events_controller_test.rb
│ │ ├── favorite_users_controller_test.rb
│ │ ├── locations/
│ │ │ └── users_controller_test.rb
│ │ ├── organizations/
│ │ │ ├── logos_controller_test.rb
│ │ │ └── wrapped_controller_test.rb
│ │ ├── organizations_controller_test.rb
│ │ ├── page_controller_test.rb
│ │ ├── profiles/
│ │ │ ├── connect_controller_test.rb
│ │ │ ├── enhance_controller_test.rb
│ │ │ ├── involvements_controller_test.rb
│ │ │ ├── map_controller_test.rb
│ │ │ ├── notes_controller_test.rb
│ │ │ └── wrapped_controller_test.rb
│ │ ├── profiles_controller_test.rb
│ │ ├── recommendations_controller_test.rb
│ │ ├── sessions/
│ │ │ ├── omniauth_controller_test copy.rb
│ │ │ └── omniauth_controller_test.rb
│ │ ├── sessions_controller_test.rb
│ │ ├── speakers_controller_test.rb
│ │ ├── sponsors/
│ │ │ └── missing_controller_test.rb
│ │ ├── spotlight/
│ │ │ ├── events_controller_test.rb
│ │ │ ├── languages_controller_test.rb
│ │ │ ├── locations_controller_test.rb
│ │ │ ├── organizations_controller_test.rb
│ │ │ ├── series_controller_test.rb
│ │ │ ├── speakers_controller_test.rb
│ │ │ ├── talks_controller_test.rb
│ │ │ └── topics_controller_test.rb
│ │ ├── suggestions_controller_test.rb
│ │ ├── talks/
│ │ │ ├── recommendations_controller_test.rb
│ │ │ ├── slides_controller_test.rb
│ │ │ └── watched_talks_controller_test.rb
│ │ ├── talks_controller_test.rb
│ │ ├── topics_controller_test.rb
│ │ ├── watch_list_talks_controller_test.rb
│ │ ├── watch_lists_controller_test.rb
│ │ ├── watched_talks_controller_test.rb
│ │ └── wrapped_controller_test.rb
│ ├── fixtures/
│ │ ├── cfps.yml
│ │ ├── cities.yml
│ │ ├── connected_accounts.yml
│ │ ├── contributors.yml
│ │ ├── event_series.yml
│ │ ├── events.yml
│ │ ├── favorite_users.yml
│ │ ├── files/
│ │ │ └── .keep
│ │ ├── organizations.yml
│ │ ├── sponsors.yml
│ │ ├── suggestions.yml
│ │ ├── talk/
│ │ │ └── transcripts.yml
│ │ ├── talk_topics.yml
│ │ ├── talks.yml
│ │ ├── topics.yml
│ │ ├── user_talks.yml
│ │ ├── users.yml
│ │ ├── watch_lists.yml
│ │ └── watched_talks.yml
│ ├── helpers/
│ │ ├── event_tracking_helper.rb
│ │ └── icon_helper_test.rb
│ ├── integration/
│ │ └── .keep
│ ├── jobs/
│ │ ├── geocode_record_job_test.rb
│ │ └── recurring/
│ │ ├── rollup_job_test.rb
│ │ ├── youtube_thumbnail_validation_job_test.rb
│ │ ├── youtube_video_availability_job_test.rb
│ │ └── youtube_video_statistics_job_test.rb
│ ├── lib/
│ │ ├── download_sponsors_test.rb
│ │ ├── generators/
│ │ │ ├── cfp_generator_test.rb
│ │ │ ├── event_generator_test.rb
│ │ │ ├── schedule_generator_test.rb
│ │ │ ├── sponsors_generator_test.rb
│ │ │ ├── talk_generator_test.rb
│ │ │ └── venue_generator_test.rb
│ │ └── schema_export_test.rb
│ ├── models/
│ │ ├── ahoy/
│ │ │ └── visit_test.rb
│ │ ├── alias_test.rb
│ │ ├── announcement_test.rb
│ │ ├── cfp_test.rb
│ │ ├── city_test.rb
│ │ ├── concerns/
│ │ │ └── geocodeable_test.rb
│ │ ├── connected_account_test.rb
│ │ ├── continent_test.rb
│ │ ├── contributor_test.rb
│ │ ├── country_test.rb
│ │ ├── event/
│ │ │ ├── assets_test.rb
│ │ │ ├── static_metadata_test.rb
│ │ │ ├── tickets/
│ │ │ │ └── provider_detection_test.rb
│ │ │ ├── tickets_test.rb
│ │ │ └── videos_file_test.rb
│ │ ├── event_geocoding_test.rb
│ │ ├── event_participation_test.rb
│ │ ├── event_series_test.rb
│ │ ├── event_test.rb
│ │ ├── favorite_user_test.rb
│ │ ├── language_test.rb
│ │ ├── location_test.rb
│ │ ├── online_location_test.rb
│ │ ├── organization_test.rb
│ │ ├── rollup_test.rb
│ │ ├── search/
│ │ │ ├── backend/
│ │ │ │ ├── sqlite_fts/
│ │ │ │ │ └── indexer_test.rb
│ │ │ │ ├── sqlite_fts_test.rb
│ │ │ │ ├── typesense/
│ │ │ │ │ ├── indexer_test.rb
│ │ │ │ │ ├── language_indexer_test.rb
│ │ │ │ │ └── location_indexer_test.rb
│ │ │ │ └── typesense_test.rb
│ │ │ └── backend_test.rb
│ │ ├── sponsor_test.rb
│ │ ├── state_test.rb
│ │ ├── static/
│ │ │ ├── city_test.rb
│ │ │ └── event_test.rb
│ │ ├── suggestion_test.rb
│ │ ├── talk_test.rb
│ │ ├── talk_topic_test.rb
│ │ ├── template_test.rb
│ │ ├── topic_test.rb
│ │ ├── uk_nation_test.rb
│ │ ├── user/
│ │ │ ├── duplicate_detector_test.rb
│ │ │ ├── profiles_test.rb
│ │ │ ├── suspicion_detector_test.rb
│ │ │ └── talk_recommendations_test.rb
│ │ ├── user_geocoding_test.rb
│ │ ├── user_talk_test.rb
│ │ ├── user_test.rb
│ │ └── youtube/
│ │ ├── video_metadata_rails_worldtest.rb
│ │ └── video_metadata_test.rb
│ ├── system/
│ │ ├── call_for_papers_test.rb
│ │ ├── events_test.rb
│ │ ├── sessions_test.rb
│ │ ├── speakers_test.rb
│ │ ├── sponsors_test.rb
│ │ ├── spotlight_search_test.rb
│ │ └── talks_test.rb
│ ├── tasks/
│ │ ├── db_seed_test.rb
│ │ └── seed_test.rb
│ ├── test_helper.rb
│ ├── tools/
│ │ ├── cfp_create_tool_test.rb
│ │ ├── cfp_info_tool_test.rb
│ │ └── cfp_update_tool_test.rb
│ └── vcr_cassettes/
│ ├── profiles/
│ │ └── enhance_controller_test/
│ │ └── patch.yml
│ ├── recurring_youtube_statistics_job.yml
│ ├── sessions/
│ │ └── omniauth_controller_test/
│ │ └── creates_a_new_user_if_not_exists_github.yml
│ ├── talks/
│ │ ├── extract_topics.yml
│ │ ├── summarize.yml
│ │ └── transcript-enhancement.yml
│ ├── user/
│ │ └── enhance_profile_job_test.yml
│ ├── youtube/
│ │ ├── channels-scrapping.yml
│ │ ├── channels.yml
│ │ ├── playlist_items/
│ │ │ └── all.yml
│ │ ├── playlists/
│ │ │ └── all.yml
│ │ ├── transcript.yml
│ │ └── transcript_not_available.yml
│ ├── youtube_duration.yml
│ ├── youtube_statistics.yml
│ ├── youtube_statistics_invalid.yml
│ └── youtube_video_transcript.yml
├── tmp/
│ └── .keep
├── vendor/
│ └── .keep
├── vite.config.mjs
└── yaml/
├── enforce_strings.mjs
└── formatter.mjs
================================================
FILE CONTENTS
================================================
================================================
FILE: .annotaterb.yml
================================================
---
:position: before
:position_in_additional_file_patterns: before
:position_in_class: before
:position_in_factory: before
:position_in_fixture: before
:position_in_routes: before
:position_in_serializer: before
:position_in_test: before
:classified_sort: true
:exclude_controllers: true
:exclude_factories: false
:exclude_fixtures: false
:exclude_helpers: true
:exclude_scaffolds: true
:exclude_serializers: false
:exclude_sti_subclasses: false
:exclude_tests: true
:force: false
:format_markdown: false
:format_rdoc: false
:format_yard: false
:frozen: false
:ignore_model_sub_dir: false
:ignore_unknown_models: false
:include_version: false
:show_check_constraints: false
:show_complete_foreign_keys: false
:show_foreign_keys: true
:show_indexes: true
:simple_indexes: true
:sort: true
:timestamp: false
:trace: false
:with_comment: true
:with_column_comments: true
:with_table_comments: true
:active_admin: false
:command:
:debug: false
:hide_default_column_types: ""
:hide_limit_column_types: ""
:ignore_columns:
:ignore_routes:
:models: true
:routes: false
:skip_on_db_migrate: false
:target_action: :do_annotations
:wrapper:
:wrapper_close:
:wrapper_open:
:classes_default_to_s: []
:additional_file_patterns: []
:model_dir:
- app/models
:require: []
:root_dir:
- ""
================================================
FILE: .cursor/commands/plan_commands.md
================================================
The user will provide a feature description. Your job is to:
1. Create a technical plan that concisely describes the feature the user wants to build.
2. Research the files and functions that need to be changed to implement the feature
3. Avoid any product manager style sections (no success criteria, timeline, migration, etc)
4. Avoid writing any actual code in the plan.
5. Include specific and verbatim details from the user's prompt to ensure the plan is accurate.
This is strictly a technical requirements document that should:
1. Include a brief description to set context at the top
2. Point to all the relevant files and functions that need to be changed or created
3. Explain any algorithms that are used step-by-step
4. If necessary, breaks up the work into logical phases. Ideally this should be done in a way that has an initial "data layer" phase that defines the types and db changes that need to run, followed by N phases that can be done in parallel (eg. Phase 2A - routes, Phase 2B - UI, etc). Only include phases if it's a REALLY big feature.
If the user's requirements are unclear, especially after researching the relevant files, you may ask up to 5 clarifying questions before writing the plan. If you do so, incorporate the user's answers into the plan.
Prioritize being concise and precise. Make the plan as tight as possible without losing any of the critical details from the user's requirements.
Write the plan into an <N>\_PLAN.md file with the next available feature number
================================================
FILE: .cursor/rules/cursorrules.mdc
================================================
---
alwaysApply: true
---
# RubyVideo Rails 8 Project - Cursor Rules
You are working on RubyVideo, a modern Ruby on Rails 8 application that aggregates and curates Ruby conference videos. This is a sophisticated video platform with AI-powered features, search, analytics, and social features.
## Project Overview
**Tech Stack:**
- Rails 8.0 with SQLite database
- Frontend: Vite + Tailwind CSS + daisyUI + Stimulus controllers
- Background Jobs: Solid Queue
- Caching: Solid Cache
- Authentication: Custom session-based auth with GitHub OAuth
- Admin: Avo admin panel
- Deployment: Kamal on Hetzner VPS
- Analytics: Ahoy for page tracking
- AI Features: OpenAI integration for summarization and topic extraction
**Architecture:**
- ViewComponent pattern for reusable UI components
- Concerns for shared model behavior
- Associated Objects pattern using `active_record-associated_object`
- Slug-based routing for SEO
- Background jobs for data processing
- YAML-based conference data structure
## Development Standards
### Ruby/Rails Code
**General Rails Conventions:**
- Follow Rails 8 conventions and modern Rails patterns
- Use `standardrb` for Ruby formatting (never `rubocop`)
- Models include concerns: `Suggestable`, `Sluggable`, `Rollupable`, `Searchable`, `Watchable`
- Prefer `belongs_to` with `optional: true` when appropriate
- Use `has_many` with `dependent: :destroy` for cascade deletes
**Model Patterns:**
```ruby
class Talk < ApplicationRecord
include Rollupable
include Sluggable
include Suggestable
include Searchable
include Watchable
configure_slug(attribute: :title, auto_suffix_on_collision: true)
belongs_to :event, optional: true, counter_cache: :talks_count, touch: true
has_many :speaker_talks, dependent: :destroy
has_many :speakers, through: :speaker_talks
validates :title, presence: true
enum :kind, ["talk", "keynote", "panel"].index_by(&:itself), default: "talk"
scope :watchable, -> { where(external_player: false) }
scope :canonical, -> { where(canonical_id: nil) }
end
```
**Controller Patterns:**
- Inherit from `ApplicationController` which includes:
- `Authenticable` - session-based authentication
- `Metadata` - SEO meta tags
- `Analytics` - page view tracking
- Use `skip_before_action :authenticate_user!` for public pages
- Follow RESTful routing patterns
- Use `Current.user` for current user access
- Implement pagination with `pagy` gem
```ruby
class TalksController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show]
def show
@talk = Talk.includes(:speakers, :topics).find_by(slug: params[:slug])
return redirect_to(root_path, status: :moved_permanently) unless @talk
@pagy, @related_talks = pagy(@talk.related_talks, limit: 6)
end
end
```
**Authentication Patterns:**
- Session-based authentication, not JWT
- Use `Current.user` to access current user
- GitHub OAuth integration available
- Admin users have `admin: true` boolean
- Route constraints use `Authenticator` module
### ViewComponent Architecture
> **Detailed UI Component Rules**: See [viewcomponents.mdc](./viewcomponents.mdc) for comprehensive UI component guidelines.
**Component Structure:**
- All components inherit from `ApplicationComponent`
- Components located in `app/components/`
- UI components in `app/components/ui/` namespace
```ruby
class Ui::ButtonComponent < ApplicationComponent
option :kind, type: Dry::Types["coercible.symbol"].enum(:primary, :secondary), default: proc { :primary }
option :size, type: Dry::Types["coercible.symbol"].enum(:sm, :md, :lg), default: proc { :md }
private
def component_classes
class_names(
"btn",
KIND_MAPPING[kind],
SIZE_MAPPING[size]
)
end
end
```
**Component Conventions:**
- Use mapping constants for CSS class variants
- Implement `component_classes` method for dynamic styling
- Use `class_names` helper for conditional classes
- Support both `param` and `option` for parameters
- Include `display` option for conditional rendering
### Frontend Development
**Stimulus Controllers:**
- Located in `app/javascript/controllers/`
- Follow Stimulus conventions with kebab-case names
- Use Stimulus actions for interactive components
**CSS/Styling:**
- Tailwind CSS with daisyUI components
- Follow utility-first approach
- Use CSS variables for theming
- Responsive design with mobile-first approach
**Assets:**
- Vite for asset bundling
- Images in `app/assets/images/`
- Use WebP format for optimized images
### Database & Models
**Schema Patterns:**
- Use `annotaterb` gem for model annotations
- SQLite database with proper indexing
- Use foreign key constraints
- Counter cache patterns: `counter_cache: :talks_count`
**Data Validation:**
- Validate presence of required fields
- Use Rails enum for status fields
- Normalize data with `normalizes` callback
- Encrypt sensitive data: `encrypts :email, deterministic: true`
**Associations:**
- Use `inverse_of` for bidirectional associations
- `strict_loading: false` where needed for performance
- Scoped associations: `has_many :approved_topics, -> { approved }`
### Background Jobs & Processing
**Job Patterns:**
- Use Solid Queue for background processing
- Jobs in `app/jobs/` directory
- Process video metadata, transcripts, AI summaries
- Handle external API calls asynchronously
**Queue Configuration:**
```ruby
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = {database: {writing: :queue}}
```
### Search & Data
**Data Structure:**
- Conference data stored in YAML files under `data/`
- Organizations, speakers, events, and videos defined in YAML
- Rake tasks process and import data
### Testing Standards
**Test Structure:**
- Use Minitest (not RSpec)
- Parallel test execution enabled
- VCR for API mocking
- Fixtures for test data
- System tests with Capybara + Selenium
**Test Patterns:**
```ruby
class TalkTest < ActiveSupport::TestCase
test "creates slug from title" do
talk = talks(:ruby_conf_talk)
assert_equal "awesome-ruby-talk", talk.slug
end
test "belongs to event" do
talk = talks(:ruby_conf_talk)
assert_equal events(:ruby_conf_2023), talk.event
end
end
```
**Test Helper Setup:**
- Search reindexing in setup
- Sign in helper: `sign_in_as(user)`
- VCR cassettes for external API calls
### API & Integrations
**External APIs:**
- YouTube API for video metadata
- GitHub API for user authentication
- OpenAI API for AI features
- All API clients in `app/clients/` directory
**AI Features:**
- Transcript enhancement
- Topic extraction
- Talk summarization
- All AI prompts stored in `app/models/prompts/`
### Development Commands
**Common Commands:**
- `bin/setup` - Full setup including Docker and database seeding
- `bin/dev` - Start Rails server, jobs, and Vite
- `bin/lint` - Run all linters and formatters
- `bin/rails test` - Run full test suite
- `bundle exec standardrb --fix` - Fix Ruby formatting
- `yarn format` - Fix JavaScript formatting
**Data Management:**
- `bin/rails db:seed:all` - Seed database from YAML files
- YAML files define conference structure and video metadata
### Deployment & Production
**Kamal Deployment:**
- Use Kamal for containerized deployment
- Dockerfile included for production builds
- Environment-specific configurations
**Monitoring:**
- AppSignal for error tracking and performance monitoring
- Ahoy for user analytics
- Mission Control for job monitoring
## Code Quality Standards
1. **Always run `standardrb --fix` for Ruby formatting**
2. **Use `yarn format` for JavaScript formatting**
3. **Include tests for new functionality**
4. **Follow Rails naming conventions**
5. **Use ViewComponents for reusable UI elements**
6. **Implement proper error handling**
7. **Add appropriate database indexes**
8. **Use background jobs for long-running tasks**
9. **Follow security best practices**
10. **Write clear, descriptive commit messages**
## Common Patterns to Follow
- Models use concerns for shared behavior
- Controllers are slim with business logic in models/services
- ViewComponents for reusable UI with design system consistency
- Background jobs for external API calls and heavy processing
- Proper caching strategies with Solid Cache
- SEO-friendly URLs with slugs
- Responsive design with Tailwind CSS
- Session-based authentication
- Comprehensive test coverage
When implementing features, always consider performance, security, accessibility, and maintainability. Follow Rails conventions and this project's established patterns.
================================================
FILE: .cursor/rules/viewcomponents.mdc
================================================
---
alwaysApply: false
---
# RubyVideo ViewComponents - UI Component Rules
> **Main Project Rules**: See [cursorrules.mdc](./cursorrules.mdc) for complete RubyVideo project guidelines.
## UI Component Architecture
This document defines specific rules for creating and maintaining UI components in the `Ui::` namespace within RubyVideo's ViewComponent architecture.
## Component Structure & Inheritance
### Base Component Pattern
All UI components MUST inherit from `ApplicationComponent`:
```ruby
class Ui::ComponentNameComponent < ApplicationComponent
# Component implementation
end
```
### ApplicationComponent Features
The base `ApplicationComponent` provides:
- `Dry::Initializer` integration for type-safe parameters
- Automatic `attributes` handling for HTML attributes
- `display` option for conditional rendering
- `render?` method that respects the `display` option
## UI Component Conventions
### 1. Component Naming
- **File naming**: `snake_case_component.rb` (e.g., `button_component.rb`)
- **Class naming**: `Ui::PascalCaseComponent` (e.g., `Ui::ButtonComponent`)
- **Template naming**: `snake_case_component.html.erb` (e.g., `button_component.html.erb`)
### 2. Parameter Definition
Use `Dry::Types` for type-safe parameters with proper defaults:
```ruby
class Ui::ButtonComponent < ApplicationComponent
# Required parameter
param :text, default: proc {}
# Optional parameter with type constraint
option :url, Dry::Types["coercible.string"], optional: true
# Enum parameter with default
option :kind, type: Dry::Types["coercible.symbol"].enum(:primary, :secondary), default: proc { :primary }
# Boolean parameter
option :disabled, type: Dry::Types["strict.bool"], default: proc { false }
end
```
### 3. CSS Class Management
#### Mapping Constants Pattern
Define mapping constants for CSS class variants:
```ruby
class Ui::ButtonComponent < ApplicationComponent
KIND_MAPPING = {
primary: "btn-primary",
secondary: "btn-secondary",
neutral: "btn-neutral btn-outline",
ghost: "btn-ghost"
}.freeze
SIZE_MAPPING = {
sm: "btn-sm",
md: "",
lg: "btn-lg"
}.freeze
end
```
#### Component Classes Method
Implement `component_classes` method for dynamic styling:
```ruby
private
def component_classes
class_names(
"btn", # Base class
KIND_MAPPING[kind], # Variant class
SIZE_MAPPING[size], # Size class
"btn-outline": outline, # Conditional class
"btn-disabled": disabled # State class
)
end
```
#### Final Classes Method
Combine component classes with user-provided classes:
```ruby
private
def classes
[component_classes, attributes[:class]].join(" ")
end
```
### 4. Content Handling
#### Content Method Pattern
Handle both parameter content and block content:
```ruby
private
def content
text.presence || super # Use param if provided, otherwise use block content
end
```
#### Call Method for Complex Rendering
Use `call` method for complex rendering logic:
```ruby
def call
case button_kind
when :link
link_to(url, class: classes, **attributes.except(:class)) { content }
when :button
tag.button(type: type, class: classes, **attributes.except(:class)) { content }
end
end
```
### 5. Stimulus Integration
#### Before Render Hook
Use `before_render` for Stimulus controller setup:
```ruby
def before_render
attributes[:data] = {
controller: "modal",
modal_open_value: open,
action: "keydown.esc->modal#close"
}.merge(attributes[:data] || {})
end
```
#### Data Attributes
Pass Stimulus values through data attributes:
```ruby
attributes[:data] = {
controller: "dropdown",
dropdown_open_value: open,
action: "click->dropdown#toggle"
}
```
## Common UI Component Patterns
### Button Component
```ruby
class Ui::ButtonComponent < ApplicationComponent
KIND_MAPPING = {
primary: "btn-primary",
secondary: "btn-secondary",
ghost: "btn-ghost"
}.freeze
SIZE_MAPPING = {
sm: "btn-sm",
md: "",
lg: "btn-lg"
}.freeze
param :text, default: proc {}
option :url, Dry::Types["coercible.string"], optional: true
option :kind, type: Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), default: proc { :primary }
option :size, type: Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
option :disabled, type: Dry::Types["strict.bool"], default: proc { false }
def call
if url.present?
link_to(url, class: classes, **attributes.except(:class)) { content }
else
tag.button(type: :button, class: classes, **attributes.except(:class)) { content }
end
end
private
def classes
[component_classes, attributes[:class]].join(" ")
end
def component_classes
class_names(
"btn",
KIND_MAPPING[kind],
SIZE_MAPPING[size],
"btn-disabled": disabled
)
end
def content
text.presence || super
end
end
```
### Badge Component
```ruby
class Ui::BadgeComponent < ApplicationComponent
KIND_MAPPING = {
primary: "badge-primary",
secondary: "badge-secondary",
neutral: "badge-neutral"
}.freeze
SIZE_MAPPING = {
xs: "badge-xs",
sm: "badge-sm",
md: "badge-md",
lg: "badge-lg"
}.freeze
param :text, optional: true
option :kind, type: Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), default: proc { :primary }
option :size, type: Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
def call
content_tag(:span, class: classes, **attributes.except(:class)) do
content
end
end
private
def classes
[component_classes, attributes[:class]].join(" ")
end
def component_classes
class_names(
"badge",
KIND_MAPPING[kind],
SIZE_MAPPING[size]
)
end
def content
text.presence || super
end
end
```
### Modal Component
```ruby
class Ui::ModalComponent < ApplicationComponent
POSITION_MAPPING = {
top: "modal-top",
middle: "modal-middle",
bottom: "modal-bottom",
responsive: "modal-bottom sm:modal-middle"
}.freeze
SIZE_MAPPING = {
md: "",
lg: "!max-w-[800px]"
}.freeze
option :open, type: Dry::Types["strict.bool"], default: proc { false }
option :position, type: Dry::Types["coercible.symbol"].enum(*POSITION_MAPPING.keys), default: proc { :responsive }
option :size, type: Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
def before_render
attributes[:data] = {
controller: "modal",
modal_open_value: open,
action: "keydown.esc->modal#close"
}.merge(attributes[:data] || {})
end
private
def classes
[component_classes, attributes.delete(:class)].compact_blank.join(" ")
end
def component_classes
class_names(
"modal",
POSITION_MAPPING[position]
)
end
def size_class
SIZE_MAPPING[size]
end
end
```
## Template Patterns
### Basic Template Structure
```erb
<!-- button_component.html.erb -->
<%= call %>
```
### Complex Template with Slots
```erb
<!-- modal_component.html.erb -->
<div class="<%= classes %>" <%= tag.attributes(attributes) %>>
<div class="modal-box <%= size_class %>">
<% if close_button %>
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<% end %>
<%= content %>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</div>
```
## Testing UI Components
### Component Test Structure
```ruby
class Ui::ButtonComponentTest < ViewComponent::TestCase
test "renders primary button" do
render_inline(Ui::ButtonComponent.new("Click me", kind: :primary))
assert_selector("button.btn.btn-primary", text: "Click me")
end
test "renders as link when url provided" do
render_inline(Ui::ButtonComponent.new("Click me", url: "/test"))
assert_selector("a.btn", text: "Click me", href: "/test")
end
test "applies custom classes" do
render_inline(Ui::ButtonComponent.new("Click me", class: "custom-class"))
assert_selector("button.custom-class")
end
end
```
## Best Practices
### 1. Type Safety
- Always use `Dry::Types` for parameter validation
- Define enum constraints for variant options
- Use `optional: true` for non-required parameters
### 2. CSS Architecture
- Use mapping constants for variant classes
- Implement `component_classes` method for dynamic styling
- Support custom classes through `attributes[:class]`
### 3. Content Flexibility
- Support both parameter content and block content
- Use `content` method to handle both cases
- Implement `call` method for complex rendering logic
### 4. Stimulus Integration
- Use `before_render` for controller setup
- Pass values through data attributes
- Support custom actions and controllers
### 5. Accessibility
- Include proper ARIA attributes
- Support keyboard navigation
- Ensure proper semantic HTML
### 6. Performance
- Use `freeze` on mapping constants
- Minimize method calls in templates
- Cache computed values when appropriate
## Common Mistakes to Avoid
1. **Don't** hardcode CSS classes in templates
2. **Don't** forget to handle both parameter and block content
3. **Don't** skip type validation for parameters
4. **Don't** forget to merge custom attributes
5. **Don't** ignore accessibility requirements
6. **Don't** create components without proper mapping constants
## Component Checklist
When creating a new UI component, ensure:
- [ ] Inherits from `ApplicationComponent`
- [ ] Uses proper naming convention (`Ui::NameComponent`)
- [ ] Defines mapping constants for variants
- [ ] Implements `component_classes` method
- [ ] Handles both parameter and block content
- [ ] Supports custom CSS classes
- [ ] Includes proper type validation
- [ ] Has corresponding template file
- [ ] Includes comprehensive tests
- [ ] Follows accessibility guidelines
- [ ] Documents all options and parameters
This ensures consistency across all UI components and maintains the design system's integrity.
================================================
FILE: .devcontainer/Dockerfile
================================================
ARG RUBY_VERSION=4.0.1
FROM docker.io/library/ruby:$RUBY_VERSION
# Rails app lives here
WORKDIR /rails
# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y \
build-essential \
# curl \
# git \
# imagemagick \ # Generate Assets
libjemalloc2 \
libvips \
# libyaml-dev \
node-gyp \
pkg-config \
python-is-python3 \
# sqlite3 \
chromium chromium-driver \
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy lockfiles and setup script
# COPY Gemfile Gemfile.lock package.json .env.sample .ruby-version ./
# COPY bin ./bin
# Copy the whole application so we don't stumble on setting up the database
COPY . ./
# Install JavaScript dependencies
ARG NODE_VERSION=22.15.1
ARG YARN_VERSION=1.22.22
ENV PATH=/usr/local/node/bin:$PATH
RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
/tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
npm install -g yarn@$YARN_VERSION && \
rm -rf /tmp/node-build-master
# Setup dependencies
RUN bin/setup --skip-server
# Default command
CMD ["sleep", "infinity"]
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "RubyEvents Dev",
"dockerComposeFile": "./docker-compose.yml",
"service": "rails-app",
"workspaceFolder": "/rails",
"overrideCommand": true,
"customizations": {
"vscode": {
"settings": {
"git.terminalGitEditor": true
},
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"testdouble.vscode-standard-ruby",
"marcoroth.herb-lsp",
"redhat.vscode-yaml"
]
}
},
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/sshd:1": {}
},
"portsAttributes": {
"3000": {
"label": "Ruby Events Rails Server",
"onAutoForward": "notify"
},
"3036": {
"label": "Vite Server",
"onAutoForward": "silent"
}
},
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached"
]
}
================================================
FILE: .devcontainer/docker-compose.yml
================================================
name: "RubyEvents"
services:
rails-app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
command: sleep infinity
depends_on:
- typesense
environment:
- RAILS_ENV=development
- NODE_ENV=development
- TYPESENSE_HOST=typesense
- TYPESENSE_PORT=8108
ports:
- "3000:3000"
volumes:
- ..:/rails:cached # Mounts the local project directory
- node_modules:/rails/node_modules # Prevents overwriting node_modules so yarn install works correctly
stdin_open: true
tty: true
typesense:
image: typesense/typesense:29.0
container_name: rubyevents-typesense
restart: unless-stopped
ports:
- "8108:8108"
volumes:
- typesense-data:/data
environment:
- TYPESENSE_DATA_DIR=/data
- TYPESENSE_ENABLE_CORS=true
command: ["--data-dir", "/data", "--api-key", "xyz", "--enable-cors", "true"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8108/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
volumes:
node_modules:
typesense-data:
driver: local
================================================
FILE: .dockerignore
================================================
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
# Ignore git directory.
/.git/
# Ignore bundler config.
/.bundle
# Ignore all default key files.
/config/master.key
/config/credentials/*.key
# Ignore all environment files.
/.env*
!/.env.sample
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep
# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/
!/tmp/storage/.keep
# Ignore assets.
/node_modules/
/app/assets/builds/*
!/app/assets/builds/.keep
/public/assets
/scripts/*
/docs/*
/load_testing/*
/test/*
================================================
FILE: .erb_lint.yml
================================================
---
EnableDefaultLinters: true
glob: "**/*.{html}{+*,}.erb"
exclude:
- vendor/bundle/**/*
- node_modules/**/*
- tmp/**/*
- log/**/*
- app/views/profiles/wrapped/**/*
linters:
ErbSafety:
enabled: true
PartialInstanceVariable:
enabled: true
exclude:
- app/views/contributions/_events_without_dates.html.erb
- app/views/contributions/_events_without_location.html.erb
- app/views/contributions/_events_without_videos.html.erb
- app/views/contributions/_missing_videos_cue.html.erb
- app/views/contributions/_speakers_without_github.html.erb
- app/views/contributions/_talks_dates_out_of_bounds.html.erb
- app/views/contributions/_talks_without_slides.html.erb
- app/views/events/_my_attendance_day.html.erb
- app/views/profiles/wrapped/_share.html.erb
Rubocop:
enabled: true
rubocop_config:
require: standard
inherit_gem:
standard: config/base.yml
Layout/InitialIndentation:
Enabled: false
Layout/TrailingEmptyLines:
Enabled: false
Lint/UselessAssignment:
Enabled: false
Lint/ItWithoutArgumentsInBlock:
Enabled: false
================================================
FILE: .eslintignore
================================================
app/javascript/controllers/index.js
================================================
FILE: .eslintrc
================================================
{ "extends": "standard" }
================================================
FILE: .gitattributes
================================================
# See https://git-scm.com/docs/gitattributes for more about git attribute files.
# Mark the database schema as having been generated.
db/schema.rb linguist-generated
# Mark any vendored files as having been vendored.
vendor/* linguist-vendored
config/credentials/*.yml.enc diff=rails_credentials
config/credentials.yml.enc diff=rails_credentials
================================================
FILE: .github/pull_request_template.md
================================================
## Description
<!-- Please describe your changes. -->
## Screenshots
<!-- Add screenshots or GIFs if applicable. -->
## Testing Steps
<!-- List steps to test your changes. -->
<!-- If this is a content PR, link to the affected pages -->
## References
<!-- Link to related issues, discussions, other PRs, or references you used to write the PR. -->
- closes #<!-- issue number -->
================================================
FILE: .github/skills/event-data/SKILL.md
================================================
---
name: event-data
description: Guide for handling event data. Use when asked to update event data such as CFPs, Talks, Schedule, Sponsors, Involvements, or Videos. Use when updating any file in the /data/ directory.
---
# Important Notes
If something is unclear, use the AskUserTool to ask for clarification.
**Always use the generators if possible.**
The generators will create a file with the correct structure, and will help you avoid formatting errors.
If you do not have a paramter for the generator, do not pass it as a parameter.
The generaator will create a reasonable fault or TODO for someone else to fill out later.
`bin/lint` must be called before commiting any changes to confirm the structure of the file is correct.
# Adding a talk
Review documentation in docs/ADDING_UNPUBLISHED_TALKS.md.
Review the available parameters for the TalkGenerator.
```bash
bin/rails g talk --help
```
Create a command to reproduce the talk.
For example, if the user says "Create a lightning talk from Chris Hasiński, the title is Doom, and it's for the Ruby Community Conference".
```bash
bin/rails g talk --event ruby-community-conference-winter-edition-2026 --speaker "Chris Hasiński" --title "Doom" --kind lightning_talk
```
Exclude any missing parameters, and let the generator create TODOs for someone else to fill out later.
If the rubyevents MCP is available, and the user did not provide an event series slug and event, use the EventLookupTool to find the correct event.
Call the generator once per talk, and do not attempt to create multiple talks in one command.
Run `bin/lint` once all talks are added to confirm the structure.
# Generating a Schedule
Load Documentation from docs/ADDING_SCHEDULES.md into context.
Call the help command and review the available parameters for the ScheduleGenerator.
```bash
bin/rails g schedule --help
```
Create a command to approximate the schedule provided by the user.
Modify the yaml file to match the schedule exactly.
Run `bin/lint` to confirm the schedule structure.
# Generating a Sponsors file
Load Documentation from docs/ADDING_SPONSORS.md into context.
Review the available parameters for the SponsorGenerator.
```bash
bin/rails g sponsor --help
```
Generate the sponsor file with appropriate tiers and sponsor names.
# Speakers
When updating speakers.yml, the structure is:
```yaml
- name: "Speaker Name"
github: "github_handle"
slug: "speaker-slug"
```
Other fields are permitted, but these are the fields I want you to focus on.
The GitHub handle is how we deduplicate speakers, and populate their profile, so the key should always be present.
These speakers are used for talks and involvements, so if a speaker is missing, you need to create a new record for them here.
If the GitHub is unknown:
```yaml
- name: "Speaker Name"
github: ""
slug: "speaker-slug"
```
If the speaker has multiple aliases, they'll be included as aliases.
```yaml
- name: "Speaker Name"
github: "github_handle"
slug: "speaker-slug"
aliases:
- name: "Other Name"
slug: "other-slug"
```
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
branches: ["*"]
push:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
RAILS_ENV: test
RUBOCOP_CACHE_ROOT: tmp/rubocop
steps:
- uses: actions/checkout@v5
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Prepare RuboCop cache
uses: actions/cache@v4
env:
DEPENDENCIES_HASH: ${{ hashFiles('**/.rubocop.yml', 'Gemfile.lock') }}
with:
path: ${{ env.RUBOCOP_CACHE_ROOT }}
key: rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
restore-keys: |
rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-
- name: StandardRB Check
run: bundle exec standardrb --format github --format "Standard::Formatter"
- name: StandardJS Check
run: yarn lint
- name: Lint YAML data files
run: yarn lint:yml
- name: Validate YAML data schemas
run: bin/rails validate:all
- name: erb-lint Check
run: bundle exec erb_lint --lint-all
- name: Herb Linter Check
run: npx @herb-tools/linter
- name: Herb Analyze (parse/compile)
run: bundle exec herb analyze app/
test:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
RAILS_ENV: test
steps:
- uses: actions/checkout@v5
- name: Install ImageMagick
run: sudo apt-get update && sudo apt-get install -y imagemagick
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build assets
run: bin/vite build --clear --mode=test
- name: Prepare database
run: |
bin/rails db:create
bin/rails db:schema:load
- name: Run tests
run: |
bin/rails test
bin/rails test:system
seed_smoke_test:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
RAILS_ENV: test
SEED_SMOKE_TEST: true
steps:
- uses: actions/checkout@v5
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build assets
run: bin/vite build --clear --mode=test
- name: Prepare database
run: |
bin/rails db:create
bin/rails db:schema:load
- name: Run Seed Smoke Test
run: bin/rails test test/tasks/seed_test.rb
- name: Verify all thumbnails for child talks are present
run: bin/rails verify_thumbnails
- name: Validate no duplicated speakers in database
run: bin/rails validate:speaker_duplicates
build:
name: Build Docker Image
runs-on: ubuntu-24.04-arm
permissions:
contents: read
packages: write
attestations: write
id-token: write
timeout-minutes: 20
env:
DOCKER_BUILDKIT: 1
RAILS_ENV: production
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx for cache
uses: docker/setup-buildx-action@v3
- name: Expose GitHub Runtime for cache
uses: crazy-max/ghaction-github-runtime@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image (PR)
if: ${{ github.event_name == 'pull_request' }}
run: docker build .
env:
DOCKER_BUILDKIT: 1
- name: Build and push Docker image
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: bundle exec kamal build push
env:
KAMAL_RAILS_MASTER_KEY: ${{ secrets.KAMAL_RAILS_MASTER_KEY }}
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
deploy:
needs: [lint, test, seed_smoke_test, build]
name: Deploy
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Deploy with Kamal
run: bundle exec kamal deploy --skip-push
env:
KAMAL_RAILS_MASTER_KEY: ${{ secrets.KAMAL_RAILS_MASTER_KEY }}
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
GEOLOCATE_API_KEY: ${{ secrets.GEOLOCATE_API_KEY }}
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
TYPESENSE_API_KEY: ${{ secrets.TYPESENSE_API_KEY }}
TYPESENSE_NODES: ${{ secrets.TYPESENSE_NODES }}
TYPESENSE_NEAREST_NODE: ${{ secrets.TYPESENSE_NEAREST_NODE }}
- name: Cleanup on cancellation
if: always() && steps.deploy.conclusion == 'cancelled'
run: bundle exec kamal lock release
================================================
FILE: .github/workflows/copilot-setup-steps.yml
================================================
name: Setup Development Environment for GitHub Copilot
# Automatically run the setup steps when they are changed to allow for easy validation, and
# allow manual testing through the repository's "Actions" tab
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
copilot-setup-steps:
runs-on: ubuntu-latest
# Only let github read the repository contents
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 30 # Fetch past 30 commits, so reasonable amount of commit context is included
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run bin/setup
run: bin/setup --skip-server
- name: Validate setup
run: |
echo "Testing database connectivity..."
bundle exec rails runner \
"puts 'Database connection: OK'; \
puts 'User count: ' + User.count.to_s"
echo "Running code style validation..."
bundle exec standardrb
================================================
FILE: .github/workflows/deploy-staging.yml
================================================
name: Deploy to Staging
on: workflow_dispatch
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
timeout-minutes: 20
env:
DOCKER_BUILDKIT: 1
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy to Staging
env:
VERSION: ${{ github.sha }}
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
KAMAL_RAILS_MASTER_KEY: ${{ secrets.KAMAL_RAILS_MASTER_KEY }}
GEOLOCATE_API_KEY: ${{ secrets.GEOLOCATE_API_KEY }}
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
run: bundle exec kamal deploy -d staging
- name: Cleanup on cancellation
if: always() && steps.deploy.conclusion == 'cancelled'
run: bundle exec kamal lock release
================================================
FILE: .github/workflows/migrate-production.yml
================================================
name: Migrate Production
on: workflow_dispatch
jobs:
migrate-production:
name: Migrate Production
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run db:migrate on Production
run: bundle exec kamal app exec --reuse "bin/rails db:migrate"
================================================
FILE: .github/workflows/migrate-staging.yml
================================================
name: Migrate Staging
on: workflow_dispatch
jobs:
migrate-staging:
name: Migrate Staging
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run db:migrate on Staging
run: bundle exec kamal app exec --reuse "bin/rails db:migrate" -d staging
================================================
FILE: .github/workflows/release-lock-production.yml
================================================
name: Release Lock Production
on: workflow_dispatch
jobs:
release-lock-production:
name: Release Lock Production
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Deploy to Production
run: bundle exec kamal lock release
================================================
FILE: .github/workflows/release-lock-staging.yml
================================================
name: Release Lock Staging
on: workflow_dispatch
jobs:
release-lock-staging:
name: Release Lock Staging
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Release lock in staging
run: bundle exec kamal lock release -d staging
================================================
FILE: .github/workflows/seed-production.yml
================================================
name: Seed Production
on: workflow_dispatch
jobs:
seed-production:
name: Seed Production
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run db:seed:all on Production
run: bundle exec kamal app exec --reuse "bin/rails db:seed:all"
================================================
FILE: .github/workflows/seed-staging.yml
================================================
name: Seed Staging
on: workflow_dispatch
jobs:
seed-staging:
name: Seed Staging
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run db:seed:all on Staging
run: bundle exec kamal app exec --reuse "bin/rails db:seed:all" -d staging
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config and cache
/.bundle
vendor/bundle/
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep
# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/
!/tmp/storage/.keep
/public/assets
# Ignore master key for decrypting credentials and more.
/config/master.key
/config/credentials/production.key
/config/credentials/staging.key
/app/assets/builds/*
!/app/assets/builds/.keep
/node_modules
/script_tmp
.env
.byebug_history
# Vite Ruby
/public/vite*
# Vite uses dotenv and suggests to ignore local-only env files. See
# https://vitejs.dev/guide/env-and-mode.html#env-files
*.local
/data.ms*
/data_tmp
data/talks_slugs.yml
/config/credentials/production.key
/config/credentials/staging.key
test-run-report*
# Ignore SQLite databases create by Litestack for action cable to be removed once we are able to move them to storage
db/*.db
db/*.db-*
# Vite Ruby
/public/vite*
node_modules
# Vite uses dotenv and suggests to ignore local-only env files. See
# https://vitejs.dev/guide/env-and-mode.html#env-files
*.local
# ignore doc folder
doc/*
service_account.json
/.cache
# asdf
.tool-versions
.DS_Store
.ssh-keys/
# IDE files
/.idea
================================================
FILE: .herb.yml
================================================
# This file configures Herb for your project and team.
# Settings here take precedence over individual editor preferences.
#
# Herb is a suite of tools for HTML+ERB templates including:
# - Linter: Validates templates and enforces best practices
# - Formatter: Auto-formats templates with intelligent indentation
# - Language Server: Provides IDE support (VS Code, Zed, Neovim, etc.)
#
# Website: https://herb-tools.dev
# Configuration: https://herb-tools.dev/configuration
# GitHub Repo: https://github.com/marcoroth/herb
#
version: 0.9.2
linter:
enabled: true
rules:
erb-no-interpolated-class-names:
enabled: false
formatter:
enabled: false
================================================
FILE: .kamal/hooks/docker-setup.sample
================================================
#!/bin/sh
echo "Docker set up on $KAMAL_HOSTS..."
================================================
FILE: .kamal/hooks/post-deploy.sample
================================================
#!/bin/sh
# A sample post-deploy hook
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
================================================
FILE: .kamal/hooks/post-proxy-reboot.sample
================================================
#!/bin/sh
echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
================================================
FILE: .kamal/hooks/pre-build.sample
================================================
#!/bin/sh
# A sample pre-build hook
#
# Checks:
# 1. We have a clean checkout
# 2. A remote is configured
# 3. The branch has been pushed to the remote
# 4. The version we are deploying matches the remote
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..." >&2
git status --porcelain >&2
exit 1
fi
first_remote=$(git remote)
if [ -z "$first_remote" ]; then
echo "No git remote set, aborting..." >&2
exit 1
fi
current_branch=$(git branch --show-current)
if [ -z "$current_branch" ]; then
echo "Not on a git branch, aborting..." >&2
exit 1
fi
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
if [ -z "$remote_head" ]; then
echo "Branch not pushed to remote, aborting..." >&2
exit 1
fi
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
exit 1
fi
exit 0
================================================
FILE: .kamal/hooks/pre-connect.sample
================================================
#!/usr/bin/env ruby
# A sample pre-connect check
#
# Warms DNS before connecting to hosts in parallel
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME
hosts = ENV["KAMAL_HOSTS"].split(",")
results = nil
max = 3
elapsed = Benchmark.realtime do
results = hosts.map do |host|
Thread.new do
tries = 1
begin
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
rescue SocketError
if tries < max
puts "Retrying DNS warmup: #{host}"
tries += 1
sleep rand
retry
else
puts "DNS warmup failed: #{host}"
host
end
end
tries
end
end.map(&:value)
end
retries = results.sum - hosts.size
nopes = results.count { |r| r == max }
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
================================================
FILE: .kamal/hooks/pre-deploy.sample
================================================
#!/usr/bin/env ruby
# A sample pre-deploy hook
#
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
#
# Fails unless the combined status is "success"
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_COMMAND
# KAMAL_SUBCOMMAND
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# Only check the build status for production deployments
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
exit 0
end
require "bundler/inline"
# true = install gems so this is fast on repeat invocations
gemfile(true, quiet: true) do
source "https://rubygems.org"
gem "octokit"
gem "faraday-retry"
end
MAX_ATTEMPTS = 72
ATTEMPTS_GAP = 10
def exit_with_error(message)
$stderr.puts message
exit 1
end
class GithubStatusChecks
attr_reader :remote_url, :git_sha, :github_client, :combined_status
def initialize
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
@git_sha = `git rev-parse HEAD`.strip
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
refresh!
end
def refresh!
@combined_status = github_client.combined_status(remote_url, git_sha)
end
def state
combined_status[:state]
end
def first_status_url
first_status = combined_status[:statuses].find { |status| status[:state] == state }
first_status && first_status[:target_url]
end
def complete_count
combined_status[:statuses].count { |status| status[:state] != "pending"}
end
def total_count
combined_status[:statuses].count
end
def current_status
if total_count > 0
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
else
"Build not started..."
end
end
end
$stdout.sync = true
puts "Checking build status..."
attempts = 0
checks = GithubStatusChecks.new
begin
loop do
case checks.state
when "success"
puts "Checks passed, see #{checks.first_status_url}"
exit 0
when "failure"
exit_with_error "Checks failed, see #{checks.first_status_url}"
when "pending"
attempts += 1
end
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
puts checks.current_status
sleep(ATTEMPTS_GAP)
checks.refresh!
end
rescue Octokit::NotFound
exit_with_error "Build status could not be found"
end
================================================
FILE: .kamal/hooks/pre-proxy-reboot.sample
================================================
#!/bin/sh
echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
================================================
FILE: .kamal/secrets
================================================
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
# Option 1: Read secrets from the environment
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$KAMAL_RAILS_MASTER_KEY
LITESTREAM_ACCESS_KEY_ID=$LITESTREAM_ACCESS_KEY_ID
LITESTREAM_SECRET_ACCESS_KEY=$LITESTREAM_SECRET_ACCESS_KEY
LITESTREAM_ENDPOINT=$LITESTREAM_ENDPOINT
GEOLOCATE_API_KEY=$GEOLOCATE_API_KEY
MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN
TYPESENSE_HOST=$TYPESENSE_HOST
TYPESENSE_PORT=$TYPESENSE_PORT
TYPESENSE_PROTOCOL=$TYPESENSE_PROTOCOL
TYPESENSE_API_KEY=$TYPESENSE_API_KEY
TYPESENSE_NODES=$TYPESENSE_NODES
TYPESENSE_NEAREST_NODE=$TYPESENSE_NEAREST_NODE
# Option 2: Read secrets via a command
# RAILS_MASTER_KEY=$(cat config/master.key)
# Option 3: Read secrets via kamal secrets helpers
# These will handle logging in and fetching the secrets in as few calls as possible
# There are adapters for 1Password, LastPass + Bitwarden
#
================================================
FILE: .kamal/secrets.staging
================================================
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
# Option 1: Read secrets from the environment
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$KAMAL_RAILS_MASTER_KEY
LITESTREAM_ACCESS_KEY_ID=$LITESTREAM_ACCESS_KEY_ID
LITESTREAM_SECRET_ACCESS_KEY=$LITESTREAM_SECRET_ACCESS_KEY
LITESTREAM_ENDPOINT=$LITESTREAM_ENDPOINT
GEOLOCATE_API_KEY=$GEOLOCATE_API_KEY
MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN
TYPESENSE_HOST=$TYPESENSE_HOST
TYPESENSE_PORT=$TYPESENSE_PORT
TYPESENSE_PROTOCOL=$TYPESENSE_PROTOCOL
TYPESENSE_API_KEY=$TYPESENSE_API_KEY
# Option 2: Read secrets via a command
# RAILS_MASTER_KEY=$(cat config/master.key)
# Option 3: Read secrets via kamal secrets helpers
# These will handle logging in and fetching the secrets in as few calls as possible
# There are adapters for 1Password, LastPass + Bitwarden
#
================================================
FILE: .mcp.json
================================================
{
"mcpServers": {
"rubyevents": {
"command": "bin/mcp_server",
"args": [],
"tools": ["*"]
}
}
}
================================================
FILE: .node-version
================================================
22.15.1
================================================
FILE: .prettierignore
================================================
transcripts.yml
================================================
FILE: .ruby-version
================================================
4.0.1
================================================
FILE: .standard.yml
================================================
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"testdouble.vscode-standard-ruby",
"marcoroth.herb-lsp",
"redhat.vscode-yaml"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.detectIndentation": false,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.minimap.enabled": false,
"editor.multiCursorModifier": "ctrlCmd",
"editor.insertSpaces": true,
"editor.tabSize": 2,
"editor.rulers": [
80,
120
],
"editor.renderControlCharacters": true,
"editor.snippetSuggestions": "top",
"editor.trimAutoWhitespace": true,
"editor.useTabStops": true,
"editor.scrollBeyondLastLine": true,
"editor.showFoldingControls": "always",
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"html.erb": "html",
"erb": "html"
},
"files.associations": {
"*.html.erb": "erb"
},
"[ruby]": {
"editor.defaultFormatter": "Shopify.ruby-lsp"
},
"rubyLsp.enabledFeatures": {
"diagnostics": false
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"tailwindCSS.includeLanguages": {
"erb": "html"
},
"tailwindCSS.emmetCompletions": true,
"tailwindCSS.validate": true,
"yaml.schemas": {
"lib/schemas/cfps_schema.json": "data/**/cfp.yml",
"lib/schemas/event_schema.json": "data/**/event.yml",
"lib/schemas/schedule_schema.json": "data/**/schedule.yml",
"lib/schemas/tiers_sponsors_schema.json": "data/**/sponsors.yml",
"lib/schemas/venue_schema.json": "data/**/venue.yml",
"lib/schemas/videos_schema.json": "**/videos.yml",
}
}
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Development Commands
### Setup & Development
- `bin/setup` - Full setup including database seeding via docker-compose
- `bin/dev` - Start Rails server, SolidQueue jobs, and Vite (for CSS/JS)
- `bin/lint` - Run all formatters and linters (StandardRB, JS Standard, ERB lint, YAML prettier)
- `bin/rails db:seed` - Seed database with conference data manually
- `bin/rails db:seed:all` - Seed database with all conference data manually
### Testing
- `bin/rails test` - Run the full test suite (uses Minitest)
- `bin/rails test test/system/` - Run system tests
- `bin/rails test test/models/speaker_test.rb` - Run specific test file
### Linting & Formatting
- `bundle exec standardrb --fix` - Fix Ruby formatting issues
- `yarn format` - Fix JavaScript formatting
- `bundle exec erb_lint --lint-all --autocorrect` - Fix ERB templates
- `yarn format:yml` - Format YAML files in data/ directory
### Jobs & Search
- `bin/jobs` - Start SolidQueue job worker
- Search reindexing happens automatically in test setup
## Architecture Overview
### Core Models & Relationships
- **Event**: Ruby conferences/meetups (belongs_to EventSeries)
- **Talk**: Conference presentations (belongs_to Event, has_many SpeakerTalks)
- **Speaker**: Presenters (has_many SpeakerTalks, has social media fields)
- **EventSeries**: Conference series/organizers (has_many Events)
- **Topic**: AI-extracted talk topics (has_many TalkTopics)
- **WatchList**: User-curated lists (belongs_to User, has_many WatchListTalks)
### Data Structure
Conference data is stored in YAML files under `/data/`:
- `data/speakers.yml` - Global speaker database
- `data/{series-slug}/series.yml` - Event series metadata (conference organizers/series)
- `data/{series-slug}/{event-name}/event.yml` - Event metadata (dates, location, colors, etc.)
- `data/{series-slug}/{event-name}/videos.yml` - Individual talk data
- `data/{series-slug}/{event-name}/schedule.yml` - Event schedules
### Technology Stack
- **Backend**: Rails 8.0, SQLite, Solid Queue, Solid Cache
- **Frontend**: Vite, Tailwind CSS, daisyUI, Stimulus
- **Admin**: Avo admin panel at `/admin`
- **Authentication**: Custom session-based auth with GitHub OAuth
- **Deployment**: Kamal on Hetzner VPS
### Key Components
- **View Components**: Located in `app/components/`, follows ViewComponent pattern
- **Clients**: API clients for YouTube, GitHub, BlueSky in `app/clients/`
- **Search**: Full-text search for Talks and Speakers using Sqlite virtual tables
- **Jobs**: Background processing for video statistics, transcripts, AI summarization
- **Analytics**: Page view tracking with Ahoy
### Authentication & Authorization
- Custom `Authenticator` module provides role-based route constraints
- Admin access required for Avo admin panel and Mission Control Jobs
- GitHub OAuth integration for user registration
- Session-based authentication (not JWT)
### Notable Conventions
- Uses slug-based routing for SEO-friendly URLs
- Talks support multiple video providers (YouTube, Vimeo, etc.)
- AI-powered features: transcript enhancement, topic extraction, summarization
- Responsive design with mobile-first approach
- Canonical references to deduplicate speakers/events/topics
### Testing Setup
- Uses Minitest (not RSpec)
- VCR for API mocking
- Parallel test execution
- Search indexes are reset in test setup
- System tests use Capybara with Selenium
### Data Import Flow
1. YAML files define conference structure
2. Rake tasks process video metadata
3. Background jobs fetch additional data (transcripts, statistics)
4. AI services enhance content (summaries, topics)
================================================
FILE: CONTRIBUTING.md
================================================
# Welcome to RubyEvents.org!
Welcome to RubyEvents.org, and thank you for contributing your time and energy to improving the platform.
We're on a mission to index all ruby events and video talks, and we need your help to do it!
A great way to get started is adding new events and content.
We have a page on the deployed site that has up-to-date information with the remaining known TODOs.
Check out the ["Getting Started: Ways to Contribute" page on RubyEvents.org](https://www.rubyevents.org/contributions) and feel free to start working on any of the remaining TODOs.
Any help is greatly appreciated.
All contributors are expected to abide by the [Code of Conduct](/CODE_OF_CONDUCT.md).
## Getting Started
We have tried to make the setup process as simple as possible so that in a few commands you can have the project with real data running locally.
### Devcontainers
In addition to the local development flow described below, we support [devcontainers](https://containers.dev) and [codespaces](https://github.com/features/codespaces).
If you open this project in VS Code and you have the [dev containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed, it will prompt you and ask if you want to reopen in a dev container.
This will set up the dev environment for you in docker, and reopen your editor from within the context of the rails container, so you can run commands and work with the project as if it was local.
All file changes will be present locally when you close the container.
- Clone RubyEvents with https, it tends to behave better, and new `gh auth login` commands won't generate new ssh keys.
- If you cannot fetch or push, use `gh auth login` to auth with GitHub.
- After the container is set up, run `bin/dev` in the terminal to start the development server. The application will be forwarded to [localhost:3000](localhost:3000).
- To run system tests, use `HEADLESS=true bin/rails test`. The HEADLESS=true environment variable ensures Chrome runs in headless mode, which is required in the container environment.
If the ruby version is updated, or you start running into issues, feel free to toss and rebuild the container.
### Local Dev Setup
#### Requirements
- Ruby 4.0.1
- Node.js 22.15.1
#### Setup
To install dependencies and prepare the database run:
```
bin/setup
```
This will seed the database with all speakers, meetups, the last 6 months of events, and all future events.
To load all historical data, run:
```
bin/rails db:seed:all
```
### Environment Variables
You can use the `.env.sample` file as a guide for the environment variables required for the project.
However, there are currently no environment variables necessary for simple app exploration.
### Starting the Application
The following command will start Rails, SolidQueue and Vite (for CSS and JS).
```
bin/dev
```
## Linter
The CI performs these checks:
- erblint
- standardrb
- standard (js)
- prettier (yaml)
Before committing your code you can run `bin/lint` to detect and potentially autocorrect lint errors and validate schemas.
To follow Tailwind CSS's recommended order of classes, you can use [Prettier](https://prettier.io/) along with the [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss), both of which are included as devDependencies. This formatting is not yet enforced by the CI.
### Typesense (Optional)
The application uses [Typesense](https://typesense.org/) for enhanced search functionality (spotlight search). Typesense is **optional** for local development. The app works without it, falling back to SQLite FTS5 for search.
**Devcontainers / Docker Compose:** Typesense is included and starts automatically.
**Local development:** Run Typesense with Docker:
```bash
docker compose -f docker-compose.typesense.yml up -d
```
Check the status of the search backends and if you can connect.
```bash
bundle exec rake search:status
```
Once running, you can reindex the data:
```bash
bin/rails search:reindex
```
Useful search commands:
```bash
bin/rails search:status # Show status of all search backends
bin/rails typesense:health # Check if Typesense is running
bin/rails typesense:stats # Show Typesense index statistics
bin/rails typesense:reindex # Full reindex of Typesense collections
bin/rails sqlite_fts:reindex # Rebuild SQLite FTS indexes
```
#### Environment Variables
Configure Typesense via environment variables in your `.env` file:
**Local development (single node):**
| Variable | Default | Description |
|----------|---------|-------------|
| `TYPESENSE_HOST` | `localhost` | Typesense server host |
| `TYPESENSE_PORT` | `8108` | Typesense server port |
| `TYPESENSE_PROTOCOL` | `http` | Protocol to use |
| `TYPESENSE_API_KEY` | `xyz` | Your Typesense API key |
**Typesense Cloud with Search Delivery Network (SDN):**
| Variable | Default | Description |
|----------|---------|-------------|
| `TYPESENSE_NODES` | - | Comma-separated list of node hosts (e.g., `xxx-1.a1.typesense.net,xxx-2.a1.typesense.net,xxx-3.a1.typesense.net`) |
| `TYPESENSE_NEAREST_NODE` | - | SDN nearest node hostname (e.g., `xxx.a1.typesense.net`) |
| `TYPESENSE_PORT` | `443` | Typesense server port |
| `TYPESENSE_PROTOCOL` | `https` | Protocol to use |
| `TYPESENSE_API_KEY` | - | Your Typesense Admin API key |
**Other options:**
| Variable | Default | Description |
|----------|---------|-------------|
| `SEARCH_INDEX_ON_IMPORT` | `true` | Whether to update search indexes when importing data from YAML files. Set to `false` to skip indexing during imports (useful for bulk imports followed by a full reindex) |
For local development with Docker, the defaults work out of the box. For production with Typesense Cloud, set `TYPESENSE_NODES` and `TYPESENSE_NEAREST_NODE` to enable the SDN configuration.
## Running the Database Seeds
After adding or modifying data, seed the database to see your changes.
If you are running the dev server, Guard will attempt to import for you on modification.
But if you are not running the dev server, or run into issues - use the seeds.
This will seed the last 6 months of conferences, and all future events and meetups.
```bash
bin/rails db:seed
```
This will seed all data and is what we use in production.
```bash
bin/rails db:seed:all
```
Import one event series and all included events.
```bash
bin/rails db:seed:event_series[blue-ridge-ruby]
```
You can also seed one event series with a script.
This will let you search and select the series.
```bash
rails runner scripts/import_event.rb blue-ridge-ruby
# Search for a series
rails runner scripts/import_event.rb
```
Import all events and event data (but not the series or anything else).
This one is good if you're updating a lot of events at once and backfilling data.
For example, adding coordinates, venues, involvements, sponsors, etc.
It will error if there's a new series (or old one because you haven't run `db:seed:all` yet).
```bash
bin/rails db:seed:events
```
Import all speakers. Great for testing profile changes.
```bash
bin/rails db:seed:speakers
```
### Troubleshooting
If you encounter a ** Process memory map: ** error, close the dev server, run seeds, and restart.
## Running Tests
We use minitest for all our testing.
Run the full test suite with:
```bash
rails test
```
Run just one test using:
```bash
rails test test/models/talk_test.rb
```
Run just one example using:
```bash
rails test test/models/talk_test.rb:6
```
## UI
For the front-end, we use [Vite](https://vite.dev/), [Tailwind CSS](https://tailwindcss.com/) with [Daisyui](https://daisyui.com/) components, [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/).
You can find existing RubyEvents components in our [component library](https://www.rubyevents.org/components).
## Queue
We use SolidQueue!
Open the rails console and run the following to clear the queue.
```
SolidQueue::Queue.new("default").clear
```
Get a count of enqueued jobs.
```
SolidQueue::Queue.new("default").size
```
## Contributing new events
Discovering and documenting new Ruby events is an ongoing effort, and a fantastic way to get familiar with the codebase!
All conference data is stored in the `/data` folder with the following structure:
```
data/
├── speakers.yml # Global speaker database
├── railsconf/ # Series folder
│ ├── series.yml # Series metadata
│ ├── railsconf-2023/ # Event folder
│ │ ├── event.yml # Event metadata
│ │ ├── videos.yml # Talk data
│ │ ├── schedule.yml # Optional schedule
│ │ ├── sponsors.yml # Optional sponsors data
│ │ └── venue.yml # Optional venue
│ └── railsconf-2024/
│ ├── event.yml
│ └── videos.yml
└── rubyconf/
├── series.yml
└── ...
```
A conference series (`series.yml`) describes a series of events.
Each folder represents a different instance of that event, and must contain an `event.yml`.
The schema for each file is located in `/app/schemas`.
If the YouTube videos for an event are available, you can [create events with a script](docs/ADDING_VIDEOS.md).
Otherwise, you can [create the event or event series manually](docs/ADDING_EVENTS.md).
There are additional guides for adding optional information:
- [visual assets](/docs/ADDING_VISUAL_ASSETS.md)
- [videos](/docs/ADDING_VIDEOS.md)
- [schedules](/docs/ADDING_SCHEDULES.md)
- [sponsors](/docs/ADDING_SPONSORS.md)
- [venues](/docs/ADDING_VENUES.md)
If you have questions about contributing events:
- Open an issue on GitHub
- Check existing event files for examples
- Reference this documentation
Your contributions help make RubyEvents a comprehensive resource for the Ruby community!
================================================
FILE: Dockerfile
================================================
# syntax=docker/dockerfile:1
# check=error=true
# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t rubyevents .
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name rubyevents rubyevents
# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=4.0.1
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
# Rails app lives here
WORKDIR /rails
# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl imagemagick libjemalloc2 libvips sqlite3 && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base AS build
# Install packages needed to build gems and node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git node-gyp pkg-config libyaml-dev python-is-python3 && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install JavaScript dependencies
ARG NODE_VERSION=22.15.1
ARG YARN_VERSION=1.22.22
ENV PATH=/usr/local/node/bin:$PATH
RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
/tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
npm install -g yarn@$YARN_VERSION && \
rm -rf /tmp/node-build-master
# Install application gems
COPY Gemfile Gemfile.lock .ruby-version ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Install node modules
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
RUN rm -rf node_modules
# Final stage for app image
FROM base
# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER 1000:1000
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start server via Thruster by default, this can be overwritten at runtime
EXPOSE 80
CMD ["./bin/thrust", "./bin/rails", "server"]
================================================
FILE: Gemfile
================================================
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby file: ".ruby-version"
# Use Rails edge
gem "rails", github: "rails/rails"
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft"
# Use sqlite3 as the database for Active Record
gem "sqlite3", ">= 2.1.0"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma"
# use jbuilder for the api
gem "jbuilder"
# Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]
# gem "jsbundling-rails"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
# gem "stimulus-rails"
# Bundle and process CSS [https://github.com/rails/cssbundling-rails]
# gem "cssbundling-rails"
# Use Redis adapter to run Action Cable in production
# gem "redis", ">= 4.0.1"
# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
# gem "kredis"
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
gem "bcrypt", "~> 3.1.7"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[windows jruby]
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
gem "kamal", "2.7.0", require: false
# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
gem "thruster", require: false
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
gem "image_processing", "~> 1.2"
# Image processing for event asset generation
gem "mini_magick"
# All sorts of useful information about every country packaged as convenient little country objects
gem "countries"
# ISO 639-1 and ISO 639-2 language code entries and convenience methods
gem "iso-639"
# A minimal client of Bluesky/ATProto API
gem "minisky", "~> 0.4.0"
# Extract Collaborator Objects from your Active Records, a new concept called Associated Objects
gem "active_record-associated_object"
# Headless Chrome driver for Capybara
gem "cuprite"
# Reusable modules for tasks like data extraction, scoring, and ranking
gem "active_genie"
# A single delightful Ruby way to work with AI.
gem "ruby_llm", "~> 1.9.1"
# A simple and clean Ruby DSL for creating JSON schemas.
gem "ruby_llm-schema"
# JSON Schema validator
gem "json_schemer"
# YouTube V3 API client.
gem "yt"
# Family of libraries that support various formats of XML "feeds".
gem "rss", "~> 0.3.1"
# Powerful and seamless HTML-aware ERB parsing and tooling.
gem "herb", "~> 0.9"
# An ActionView-compatible ERB engine with modern DX - re-imagined with Herb.
gem "reactionview", "~> 0.3"
# Agnostic pagination in plain ruby.
gem "pagy"
# gem "activerecord-enhancedsqlite3-adapter"
gem "solid_cache"
# Database-backed Active Job backend.
gem "solid_queue", github: "joshleblanc/solid_queue", branch: "async-mode"
# Operational controls for Active Job
gem "mission_control-jobs"
# Simple, powerful, first-party analytics for Rails
gem "ahoy_matey"
# The simplest way to group temporal data
gem "groupdate"
# Create beautiful JavaScript charts with one line of Ruby
gem "chartkick", "~> 5.0"
# Use Vite in Rails and bring joy to your JavaScript experience
gem "vite_rails"
# Collection of SEO helpers for Ruby on Rails.
gem "meta-tags"
# Logs performance and exception data from your app to appsignal.com
gem "appsignal"
# Autoload dotenv in Rails.
gem "dotenv-rails"
# Automatic generation of html links in texts
gem "rails_autolink", "~> 1.1"
# Easily generate XML Sitemaps
gem "sitemap_generator", "~> 6.3"
# A framework for building reusable, testable & encapsulated view components
gem "view_component"
# Adds ActiveRecord-specific methods to Dry::Initializer
gem "dry-initializer-rails"
# Type system for Ruby supporting coercions, constraints and complex types
gem "dry-types", "~> 1.7"
# Protocol Buffers are Google's data interchange format.
gem "google-protobuf", require: false
# ActiveJob::Performs adds the `performs` macro to set up jobs by convention.
gem "active_job-performs"
# Use the OpenAI API with Ruby!
gem "ruby-openai"
# Repairs broken JSON strings.
gem "json-repair", "~> 0.2.0"
# Markdown that smells nice
gem "redcarpet", "~> 3.6"
# Syntax highlighting
gem "rouge", "~> 4.4"
# Country Select Plugin
gem "country_select"
# Admin panel framework and Content Management System for Ruby on Rails.
gem "avo"
group :avo, optional: true do
gem "avo-pro", source: "https://packager.dev/avo-hq/"
end
# Marksmith is a GitHub-style markdown editor for Ruby on Rails applications.
gem "marksmith"
# A fast, safe, extensible parser for CommonMark. This wraps the comrak Rust crate.
gem "commonmarker", ">= 2.6.1"
# ActiveRecord like interface to read only access and query static YAML files
gem "frozen_record", "~> 0.27.2"
# A convenient way to diff string in ruby
gem "diffy"
# ActiveRecord soft-deletes done right
gem "discard"
# Makes consuming restful web services dead easy.
gem "httparty"
# Use OmniAuth to support multi-provider authentication [https://github.com/omniauth/omniauth]
gem "omniauth"
# Official OmniAuth strategy for GitHub.
gem "omniauth-github"
# Provides a mitigation against CVE-2015-9284 [https://github.com/cookpad/omniauth-rails_csrf_protection]
gem "omniauth-rails_csrf_protection"
# An accessible autocomplete for Ruby on Rails apps using Hotwire.
gem "hotwire_combobox", "~> 0.4.0"
# Common locale data and translations for Rails i18n.
gem "rails-i18n", "~> 8.0"
# Ruby standards gems
gem "openssl" # https://github.com/ruby/openssl/issues/949
# Class to build custom data structures, similar to a Hash.
gem "ostruct"
# Complete geocoding solution for Ruby.
gem "geocoder", github: "alexreisner/geocoder" # Use latest geocoder with Nominatim improvements until next release
# RubyGems.org API wrapper for gem information
gem "gems"
# A glamorous CLI toolkit for Ruby
gem "gum", "~> 0.3.1"
# Regex pattern searching in files
gem "grepfruit"
# Create beautiful JavaScript maps with one line of Ruby
gem "mapkick-rb", "~> 0.2.0"
# Typesense Ruby client
gem "typesense", "~> 4.1"
# Typesense integration to your favorite ORM
gem "typesense-rails", "~> 1.0.0.rc4"
group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "bundler-audit", require: false
gem "debug", platforms: %i[mri windows]
gem "byebug"
gem "minitest-difftastic", "~> 0.2"
end
group :development do
# A gem for generating annotations for Rails projects.
gem "annotaterb"
# Use console on exceptions pages [https://github.com/rails/web-console]
gem "web-console"
# Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
gem "rack-mini-profiler"
# For call-stack profiling flamegraphs
gem "stackprof"
# Speed up commands on slow machines / big apps [https://github.com/rails/spring]
# gem "spring"
# Use listen to watch files for changes [https://github.com/guard/listen]
gem "listen", "~> 3.5"
# Guard for watching file changes and auto-importing [https://github.com/guard/guard]
gem "guard"
gem "ruby-lsp-rails", require: false
gem "standardrb", "~> 1.0", require: false
gem "erb_lint", require: false
gem "authentication-zero", require: false
end
group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
gem "rails-controller-testing"
gem "selenium-webdriver"
gem "vcr", "~> 6.1"
gem "webmock"
end
================================================
FILE: Guardfile
================================================
# Guardfile for watching YAML files in data/ and auto-importing them
require_relative "lib/guard/data_import"
guard :data_import, wait_for_delay: 2 do
watch(%r{^data/speakers\.yml$})
watch(%r{^data/topics\.yml$})
watch(%r{^data/featured_cities\.yml$})
watch(%r{^data/[^/]+/series\.yml$})
watch(%r{^data/[^/]+/[^/]+/event\.yml$})
watch(%r{^data/[^/]+/[^/]+/videos\.yml$})
watch(%r{^data/[^/]+/[^/]+/cfp\.yml$})
watch(%r{^data/[^/]+/[^/]+/sponsors\.yml$})
watch(%r{^data/[^/]+/[^/]+/schedule\.yml$})
end
================================================
FILE: Procfile.dev
================================================
web: bin/rails server -b 0.0.0.0
vite: bin/vite dev
jobs: bin/jobs
# guard: bundle exec guard --no-interactions
================================================
FILE: README.md
================================================
# RubyEvents.org (formerly RubyVideo.dev)
[RubyEvents.org](https://www.rubyevents.org) (formerly RubyVideo.dev), inspired by [pyvideo.org](https://pyvideo.org/), is designed to index all Ruby-related events and videos from conferences and meetups around the world. At the time of writing, the project has 6000+ videos indexed from 200+ events and 3000+ speakers.
Technically the project is built using the latest [Ruby](https://www.ruby-lang.org/) and [Rails](https://rubyonrails.org/) goodies such as [Hotwire](https://hotwired.dev/), [Solid Queue](https://github.com/rails/solid_queue), [Solid Cache](https://github.com/rails/solid_cache).
For the front end part we use [Vite](https://vite.dev/), [Tailwind](https://tailwindcss.com/) with [daisyUI](https://daisyUI.com/) components and [Stimulus](https://stimulus.hotwire.dev/).
It is deployed on an [Hetzner](https://hetzner.cloud/?ref=gyPLk7XJthjg) VPS with [Kamal](https://kamal-deploy.org/) using [SQLite](https://www.sqlite.org/) as the main database.
For compatible browsers it tries to demonstrate some of the possibilities of [Page View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API).
## Contributing
This project is open source, and contributions are greatly appreciated. One of the most direct ways to contribute at this time is by adding more content.
We also have a page on the deployed site that has up-to-date information with the remaining known TODOs. Check out the ["Getting Started: Ways to Contribute" page on RubyEvents.org](https://www.rubyevents.org/contributions) and feel free to start working on any of the remaining TODOs. Any help is greatly appreciated.
For more information on contributing, see the [Contributing Guide](/CONTRIBUTING.md).
## Code of Conduct
Please note that this project is released with a Contributor Code of Conduct. By participating in this project, you agree to abide by its terms. More details can be found in the [Code of Conduct](/CODE_OF_CONDUCT.md) document.
## Credits
Thank you [AppSignal](https://appsignal.com/r/eeab047472) for providing the APM tool that helps us monitor the application.
Thank you [Typesense](https://typesense.org/) for sponsoring our [Typesense Cloud](https://cloud.typesense.org/) instance that powers the search functionality.
## License
RubyEvents.org is open source and available under the MIT License. For more information, please see the [License](/LICENSE.md) file.
================================================
FILE: Rakefile
================================================
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative "config/application"
Rails.application.load_tasks
================================================
FILE: app/assets/builds/.keep
================================================
================================================
FILE: app/assets/stylesheets/application.css
================================================
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@import "components/button.css";
@import "components/diff.css";
@import "components/dropdown.css";
@import "components/event.css";
@import "components/form.css";
@import "components/hotwire-combobox.css";
@import "components/markdown.css";
@import "components/modal.css";
@import "components/nav.css";
@import "components/pagination.css";
@import "components/tabs.css";
@import "components/skeleton.css";
@import "components/spotlight.css";
@import "components/transition.css";
@import "components/typography.css";
@import "components/video.css";
@import "components/iframe.css";
@import "vlitejs/vlite.css";
@import "bridge/components";
@import "flag-icons/css/flag-icons.min.css";
turbo-frame[id^="browse-"][complete] {
display: contents;
}
================================================
FILE: app/assets/stylesheets/bridge/components.css
================================================
html[data-bridge-platform] {
user-select: none;
}
[data-bridge-components~="button"] [data-controller~="bridge--button"] {
display: none;
}
================================================
FILE: app/assets/stylesheets/components/button.css
================================================
@layer components {
.btn {
@apply fill-current;
}
.btn:not(.btn-sm) {
height: 2.5rem;
min-height: 2.5rem;
}
.btn-rounded {
@apply btn-outline rounded-full border border-gray-400 bg-white hover:border-gray-500 hover:bg-gray-100 hover:text-base-content/80;
}
.btn-circle {
@apply btn-rounded;
@apply h-10 w-10;
}
/* for the toolbar buttons */
.btn.btn-pill {
@apply btn-rounded btn-outline;
@apply flex-nowrap whitespace-nowrap;
/* for the watched button */
&.active {
@apply border-primary bg-primary text-primary-content;
}
&.btn-sm {
@apply text-sm font-normal;
}
}
.btn.btn-secondary {
@apply whitespace-nowrap border-primary bg-white fill-primary text-primary hover:bg-gray-100 hover:text-primary/90;
}
.btn.btn-neutral {
@apply bg-white hover:fill-neutral/80 hover:text-neutral/80;
}
}
================================================
FILE: app/assets/stylesheets/components/diff.css
================================================
@layer components {
.diff {
del {
@apply text-error;
}
ins {
@apply text-success;
}
}
}
================================================
FILE: app/assets/stylesheets/components/dropdown.css
================================================
@layer components {
details > summary {
list-style: none;
}
/* fix the dropdown button to links, by default daisyui adds padding to the form element */
/* therefore the inner button does not fill the dropdown item */
/* and only the text of the button is clickable */
.menu li form.button_to {
padding: 0;
display: block;
}
.menu li form.button_to button {
display: block;
width: 100%;
text-align: start;
padding: 0.5rem 1rem;
}
}
================================================
FILE: app/assets/stylesheets/components/event.css
================================================
@layer components {
.event-card.cancelled,
.card.cancelled {
opacity: 0.5;
}
}
================================================
FILE: app/assets/stylesheets/components/form.css
================================================
@layer components {
label {
@apply font-semibold;
}
/* because setting --rounded-btn to 1.9rem makes also all input field fully rounded */
/* we override this behaviour */
.input,
.textarea {
@apply rounded-lg border border-gray-200 bg-white;
}
}
================================================
FILE: app/assets/stylesheets/components/hotwire-combobox.css
================================================
@layer components {
.hw-combobox__main__wrapper {
@apply min-h-12;
}
.hw-combobox__main__wrapper:has(input:focus) {
box-shadow: none;
border-color: var(--fallback-bc, oklch(var(--bc)/0.2));
outline-style: solid;
outline-width: 2px;
outline-offset: 2px;
outline-color: var(--fallback-bc, oklch(var(--bc)/0.2));
}
.hw-combobox__main__wrapper input:focus {
box-shadow: none;
border-color: none;
outline-style: none;
outline-color: none;
}
:root {
--hw-focus-color: rgb(var(--fallback-bc));
--hw-combobox-width: 100%;
--hw-combobox-width--multiple: 100%;
}
}
================================================
FILE: app/assets/stylesheets/components/iframe.css
================================================
@layer components {
.responsive-iframe-container iframe {
width: 100%;
}
}
================================================
FILE: app/assets/stylesheets/components/markdown.css
================================================
@layer components {
.markdown {
p {
@apply mb-2;
}
h1,
h2,
h3,
h4,
h5,
h6 {
@apply mb-4 mt-8;
}
h1,
h2,
h3 {
@apply text-primary;
}
ul {
@apply mb-4;
li {
@apply ml-6 list-disc;
}
}
ol {
@apply mb-4;
li {
@apply ml-6 list-decimal;
}
}
a {
@apply text-primary;
}
/* Tables */
table {
@apply w-full border-collapse mb-4;
}
th,
td {
@apply border border-base-300 px-4 py-2 text-left;
}
th {
@apply bg-base-200 font-semibold;
}
tr:nth-child(even) {
@apply bg-base-200/50;
}
/* Code blocks - override prose styles */
pre {
@apply rounded-sm p-4 mb-4 overflow-x-auto text-base bg-base-200;
color: #24292e !important;
}
pre.highlight {
@apply bg-base-200;
}
pre code {
@apply text-sm;
color: inherit !important;
background: transparent !important;
}
/* Inline code */
:not(pre) > code {
@apply bg-base-200 px-1.5 py-0.5 rounded text-sm;
color: #24292e !important;
}
/* Rouge syntax highlighting - GitHub-like theme */
.highlight {
.c,
.c1,
.cm {
color: #6a737d;
font-style: italic;
} /* Comments */
.k,
.kd,
.kn,
.kp,
.kr,
.kt {
color: #d73a49;
} /* Keywords */
.s,
.s1,
.s2,
.sb,
.sc,
.sd,
.se,
.sh,
.si,
.sx {
color: #032f62;
} /* Strings */
.n,
.na,
.nb,
.nc,
.nd,
.ne,
.nf,
.ni,
.nl,
.nn,
.no,
.nt,
.nv {
color: #6f42c1;
} /* Names */
.mi,
.mf,
.mh,
.mo,
.il {
color: #005cc5;
} /* Numbers */
.o,
.ow {
color: #d73a49;
} /* Operators */
.p {
color: #24292e;
} /* Punctuation */
.cp {
color: #d73a49;
} /* Preprocessor */
.err {
color: #b31d28;
background-color: #ffeef0;
} /* Errors */
}
}
}
================================================
FILE: app/assets/stylesheets/components/modal.css
================================================
@layer components {
.modal-box {
@apply bg-white;
}
.modal-top.spotlight {
.modal-box {
@apply absolute left-0 right-0 top-4 mx-4 w-auto rounded-lg lg:top-8 lg:mx-8;
}
}
}
================================================
FILE: app/assets/stylesheets/components/nav.css
================================================
@layer components {
.desktop-menu {
@apply hidden gap-6 md:flex;
li,
title {
@apply m-0 inline-block;
div {
@apply invisible h-0.5 bg-primary;
}
&.active {
@apply relative;
& > div {
view-transition-name: navbar-active;
@apply visible absolute bottom-0 w-full font-bold;
}
}
}
}
.mobile-menu {
@apply flex md:hidden;
}
}
================================================
FILE: app/assets/stylesheets/components/pagination.css
================================================
@layer components {
nav.pagy.nav {
@apply mx-auto flex justify-normal gap-4 bg-transparent text-base-content sm:gap-8;
.current {
@apply font-semibold text-primary;
}
.page.disabled {
@apply text-base-300;
}
}
}
================================================
FILE: app/assets/stylesheets/components/skeleton.css
================================================
@layer components {
.skeleton {
border-radius: inherit;
}
}
================================================
FILE: app/assets/stylesheets/components/spotlight.css
================================================
@layer components {
.modal-box.spotlight {
@apply max-w-full;
}
[role="option"][aria-selected="true"] {
@apply font-bold;
}
}
================================================
FILE: app/assets/stylesheets/components/tabs.css
================================================
@layer components {
/* https://github.com/saadeghi/daisyui/issues/2988 */
.tabs {
display: flex;
flex-wrap: wrap;
}
.tab {
order: 0;
}
.tab-content {
order: 1;
width: 100%;
}
}
================================================
FILE: app/assets/stylesheets/components/transition.css
================================================
@layer components {
::view-transition-group(root) {
animation: none;
mix-blend-mode: normal;
}
.navbar {
view-transition-name: navbar;
contain: layout;
}
.banner-img .v-vlite {
contain: layout;
}
.card-horizontal-img img {
contain: layout;
}
.gallery img {
contain: layout;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
::view-transition-old(card-horizontal-img),
::view-transition-new(card-horizontal-img) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-image-pair(card-horizontal-img) {
isolation: none;
}
::view-transition-old(banner-img),
::view-transition-new(banner-img) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-image-pair(banner-img) {
isolation: none;
}
}
================================================
FILE: app/assets/stylesheets/components/typography.css
================================================
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@200..1000&display=swap");
:root {
font-family: Inter, sans-serif;
font-feature-settings: "liga" 1, "calt" 1; /* fix for Chrome */
}
@supports (font-variation-settings: normal) {
:root {
font-family: InterVariable, sans-serif;
}
}
@layer components {
body {
@apply bg-base-100 leading-relaxed text-base-content;
}
h1,
.h1 {
@apply font-serif text-3xl font-bold text-primary;
}
h2,
.h2 {
@apply font-serif text-xl font-bold;
}
p.secondary {
@apply text-neutral-content;
}
a.link-ghost {
@apply hover:underline;
text-decoration: none;
}
mark {
/* search results highlight */
background-color: rgb(242, 255, 140);
}
}
================================================
FILE: app/assets/stylesheets/components/video.css
================================================
@layer components {
.banner-img .v-vlite {
--vlite-controlsColor: #ffffff;
--vlite-controlsOpacity: 1;
--vlite-controlBarBackground: linear-gradient(0deg, #000 -50%, #fff);
}
.v-playbackRateSelect.v-controlButton {
color: var(--vlite-controlsColor);
text-align: center;
}
.v-loader {
display: none !important;
}
.v-bigPlay,
.v-overlay,
.v-poster {
display: none !important;
}
.v-openInYouTube.v-controlButton svg {
width: 65%;
fill: var(--vlite-controlsColor);
}
/* Picture in picture styling */
.picture-in-picture .picture-in-picture-container {
@apply sm:fixed sm:bottom-4 sm:right-4 sm:z-10 sm:h-auto sm:w-full sm:max-w-sm sm:rounded-xl xl:max-w-lg;
.v-video {
@apply rounded-xl;
}
}
}
================================================
FILE: app/avo/actions/approve_topic.rb
================================================
class Avo::Actions::ApproveTopic < Avo::BaseAction
self.name = "Publish Topic"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.approved!
end
end
end
================================================
FILE: app/avo/actions/assign_canonical_event.rb
================================================
class Avo::Actions::AssignCanonicalEvent < Avo::BaseAction
self.name = "Assign Canonical Event"
# self.visible = -> do
# true
# end
def fields
field :event_id, as: :select, name: "Canonical event",
help: "The name of the event to be set as canonical",
options: -> { Event.order(:name).pluck(:name, :id) }
end
def handle(query:, fields:, current_user:, resource:, **args)
canonical_event = Event.find(fields[:event_id])
query.each do |record|
record.assign_canonical_event!(canonical_event: canonical_event)
end
succeed "Assigning canonical event #{canonical_event.name} to #{query.count} events"
redirect_to avo.resources_event_path(canonical_event)
end
end
================================================
FILE: app/avo/actions/assign_canonical_speaker.rb
================================================
class Avo::Actions::AssignCanonicalSpeaker < Avo::BaseAction
self.name = "Assign Canonical Speaker"
# self.visible = -> do
# true
# end
def fields
field :speaker_id, as: :select, name: "Canonical speaker",
help: "The name of the speaker to be set as canonical",
options: -> { User.speakers.order(:name).pluck(:name, :id) }
end
def handle(query:, fields:, current_user:, resource:, **args)
canonical_speaker = User.find(fields[:speaker_id])
query.each do |record|
record.assign_canonical_speaker!(canonical_speaker: canonical_speaker)
end
succeed "Assigning canonical speaker #{canonical_speaker.name} to #{query.count} speakers"
redirect_to avo.resources_user_path(canonical_speaker), status: :permanent_redirect
end
end
================================================
FILE: app/avo/actions/assign_canonical_topic.rb
================================================
class Avo::Actions::AssignCanonicalTopic < Avo::BaseAction
self.name = "Assign Canonical Topic"
# self.visible = -> do
# true
# end
def fields
field :topic_id, as: :select, name: "Canonical topic",
help: "The name of the topic to be set as canonical",
options: -> { Topic.approved.order(:name).pluck(:name, :id) }
end
def handle(query:, fields:, current_user:, resource:, **args)
canonical_topic = Topic.find(fields[:topic_id])
query.each do |record|
record.assign_canonical_topic!(canonical_topic: canonical_topic)
end
succeed "Assigning canonical topic #{canonical_topic.name} to #{query.count} topics"
redirect_to avo.resources_topic_path(canonical_topic)
end
end
================================================
FILE: app/avo/actions/assign_canonical_user.rb
================================================
class Avo::Actions::AssignCanonicalUser < Avo::BaseAction
self.name = "Assign Canonical User"
def fields
field :user_id, as: :select, name: "Canonical user",
help: "The name of the speaker to be set as canonical",
options: -> { User.order(:name).pluck(:name, :id) }
end
def handle(query:, fields:, current_user:, resource:, **args)
canonical_user = User.find(fields[:user_id])
query.each do |record|
record.assign_canonical_user!(canonical_user: canonical_user)
end
succeed "Assigning canonical user #{canonical_user.name} to #{query.count} users"
redirect_to avo.resources_user_path(canonical_user), status: :permanent_redirect
end
end
================================================
FILE: app/avo/actions/clear_user.rb
================================================
class Avo::Actions::ClearUser < Avo::BaseAction
self.name = "Clear Suspicion"
self.message = "Mark this user as manually reviewed and not suspicious."
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |user|
user.clear_suspicion!
end
succeed "Cleared suspicion for #{query.count} user(s)."
end
end
================================================
FILE: app/avo/actions/enhance_transcript.rb
================================================
class Avo::Actions::EnhanceTranscript < Avo::BaseAction
self.name = "Enhance Transcript"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |item|
talk = item.is_a?(Talk::Transcript) ? item.talk : item
talk.agents.improve_transcript_later
end
end
end
================================================
FILE: app/avo/actions/extract_topics.rb
================================================
class Avo::Actions::ExtractTopics < Avo::BaseAction
self.name = "Extract Topics"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |talk|
talk.agents.analyze_topics_later
end
end
end
================================================
FILE: app/avo/actions/fetch_duration.rb
================================================
class Avo::Actions::FetchDuration < Avo::BaseAction
self.name = "Fetch duration"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.fetch_duration_from_youtube_later!
end
succeed "Fetching the duration in the background"
end
end
================================================
FILE: app/avo/actions/find_topic_talks.rb
================================================
class Avo::Actions::FindTopicTalks < Avo::BaseAction
self.name = "Find Talks"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |topic|
topic.agents.find_talks_later
end
succeed "Enqueued job to find talks for #{query.count} topic(s)"
end
end
================================================
FILE: app/avo/actions/geocode_record.rb
================================================
class Avo::Actions::GeocodeRecord < Avo::BaseAction
self.name = "Geocode location"
def handle(query:, fields:, current_user:, resource:, records:, **args)
items = records.presence || query.to_a
perform_in_background = items.size >= 10
processed = 0
skipped = 0
model_name = items.first&.model_name&.human&.downcase || "record"
items.each do |record|
unless record.geocodeable?
skipped += 1
next
end
if perform_in_background
GeocodeRecordJob.perform_later(record)
else
GeocodeRecordJob.perform_now(record)
end
processed += 1
end
message = "Geocoding #{perform_in_background ? "enqueued" : "completed"} for #{processed} #{model_name.pluralize(processed)}"
message += " (#{skipped} skipped without location)" if skipped > 0
succeed message
end
end
================================================
FILE: app/avo/actions/reject_topic.rb
================================================
class Avo::Actions::RejectTopic < Avo::BaseAction
self.name = "Reject Topic"
# self.visible = -> do
# true
# end
# def fields
# # Add Action fields here
# end
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.rejected!
end
end
end
================================================
FILE: app/avo/actions/summarize.rb
================================================
class Avo::Actions::Summarize < Avo::BaseAction
self.name = "Summarize"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |talk|
talk.agents.summarize_later
end
end
end
================================================
FILE: app/avo/actions/talk_index.rb
================================================
class Avo::Actions::TalkIndex < Avo::BaseAction
self.name = "Talk Reindex"
self.standalone = true
def handle(query:, fields:, current_user:, resource:, **args)
if query.empty?
Talk.all.in_batches.each(&:update_fts_record_later_bulk)
succeed "All Talks reindexed"
else
query.in_batches.each(&:update_fts_record_later_bulk)
succeed "Selected talks are added to the index"
end
end
end
================================================
FILE: app/avo/actions/talk_ingest.rb
================================================
class Avo::Actions::TalkIngest < Avo::BaseAction
self.name = "Ingest talk (fetch transcript, enhance transcript, summarize, extract topics)"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.agents.ingest_later
end
succeed "Ingesting the talks in the background"
end
end
================================================
FILE: app/avo/actions/transcript.rb
================================================
class Avo::Actions::Transcript < Avo::BaseAction
self.name = "Fetch raw transcript"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.fetch_and_update_raw_transcript_later!
end
succeed "Fetching the transcript in the background"
end
end
================================================
FILE: app/avo/actions/update_from_yml.rb
================================================
class Avo::Actions::UpdateFromYml < Avo::BaseAction
self.name = "Update talk from yml metadata"
def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
record.update_from_yml_metadata_later!
end
succeed "Updating talk from yml metadata"
end
end
================================================
FILE: app/avo/actions/user_fetch_github.rb
================================================
class Avo::Actions::UserFetchGitHub < Avo::BaseAction
self.name = "Fetch GitHub profile"
def handle(query:, fields:, current_user:, resource:, **args)
perform_in_background = !(query.count < 10)
query.each do |user|
perform_in_background ? user.profiles.enhance_with_github_later : user.profiles.enhance_with_github
end
end
end
================================================
FILE: app/avo/cards/duplicates_summary.rb
================================================
class Avo::Cards::DuplicatesSummary < Avo::Cards::PartialCard
self.id = "duplicates_summary"
self.label = "Duplicate Users"
self.description = "Users with potential reversed name duplicates"
self.cols = 1
self.rows = 1
self.partial = "avo/cards/duplicates_summary"
end
================================================
FILE: app/avo/cards/reversed_name_duplicates_list.rb
================================================
class Avo::Cards::ReversedNameDuplicatesList < Avo::Cards::PartialCard
self.id = "reversed_name_duplicates_list"
self.label = "Duplicate Pairs"
self.description = "Users with reversed name matches"
self.cols = 3
self.rows = 4
self.partial = "avo/cards/reversed_name_duplicates_list"
end
================================================
FILE: app/avo/cards/reversed_name_duplicates_metric.rb
================================================
class Avo::Cards::ReversedNameDuplicatesMetric < Avo::Cards::MetricCard
self.id = "reversed_name_duplicates_metric"
self.label = "Reversed Name Duplicates"
self.description = "Number of users with potential duplicate profiles (name parts reversed)"
self.cols = 1
def query
result User.with_reversed_name_duplicate.count
end
end
================================================
FILE: app/avo/cards/same_name_duplicates_list.rb
================================================
class Avo::Cards::SameNameDuplicatesList < Avo::Cards::PartialCard
self.id = "same_name_duplicates_list"
self.label = "Same Name Duplicates"
self.description = "Users with identical names (case-insensitive)"
self.cols = 3
self.rows = 2
self.partial = "avo/cards/same_name_duplicates_list"
end
================================================
FILE: app/avo/cards/same_name_duplicates_metric.rb
================================================
class Avo::Cards::SameNameDuplicatesMetric < Avo::Cards::MetricCard
self.id = "same_name_duplicates_metric"
self.label = "Same Name Duplicates"
self.description = "Users with identical names"
self.cols = 1
def query
result User::DuplicateDetector.same_name_duplicate_ids.count
end
end
================================================
FILE: app/avo/cards/suspicious_signals_breakdown.rb
================================================
class Avo::Cards::SuspiciousSignalsBreakdown < Avo::Cards::PartialCard
self.id = "suspicious_signals_breakdown"
self.label = "Signal Breakdown"
self.description = "Breakdown of suspicious signals across flagged users"
self.cols = 2
self.rows = 2
self.partial = "avo/cards/suspicious_signals_breakdown"
end
================================================
FILE: app/avo/cards/suspicious_summary.rb
================================================
class Avo::Cards::SuspiciousSummary < Avo::Cards::PartialCard
self.id = "suspicious_summary"
self.label = "Suspicious Users"
self.description = "Users flagged with suspicious signals"
self.cols = 1
self.rows = 1
self.partial = "avo/cards/suspicious_summary"
end
================================================
FILE: app/avo/cards/suspicious_users_list.rb
================================================
class Avo::Cards::SuspiciousUsersList < Avo::Cards::PartialCard
self.id = "suspicious_users_list"
self.label = "Suspicious Users"
self.description = "Users flagged as suspicious based on signal detection"
self.cols = 3
self.rows = 4
self.partial = "avo/cards/suspicious_users_list"
end
================================================
FILE: app/avo/cards/suspicious_users_metric.rb
================================================
class Avo::Cards::SuspiciousUsersMetric < Avo::Cards::MetricCard
self.id = "suspicious_users_metric"
self.label = "Suspicious Users"
self.description = "Number of users flagged as suspicious"
self.cols = 1
def query
result User.suspicious.count
end
end
================================================
FILE: app/avo/cards/unavailable_videos_by_event.rb
================================================
class Avo::Cards::UnavailableVideosByEvent < Avo::Cards::PartialCard
self.id = "unavailable_videos_by_event"
self.label = "By Event"
self.description = "Unavailable videos grouped by event"
self.cols = 2
self.rows = 2
self.partial = "avo/cards/unavailable_videos_by_event"
end
================================================
FILE: app/avo/cards/unavailable_videos_list.rb
================================================
class Avo::Cards::UnavailableVideosList < Avo::Cards::PartialCard
self.id = "unavailable_videos_list"
self.label = "Unavailable Videos"
self.description = "Talks with videos that are no longer available"
self.cols = 3
self.rows = 4
self.partial = "avo/cards/unavailable_videos_list"
end
================================================
FILE: app/avo/cards/unavailable_videos_metric.rb
================================================
class Avo::Cards::UnavailableVideosMetric < Avo::Cards::MetricCard
self.id = "unavailable_videos_metric"
self.label = "Unavailable Videos"
self.description = "Number of talks with unavailable videos"
self.cols = 1
def query
result Talk.video_unavailable.count
end
end
================================================
FILE: app/avo/cards/unavailable_videos_summary.rb
================================================
class Avo::Cards::UnavailableVideosSummary < Avo::Cards::PartialCard
self.id = "unavailable_videos_summary"
self.label = "Unavailable Videos"
self.description = "Talks with videos no longer available"
self.cols = 1
self.rows = 1
self.partial = "avo/cards/unavailable_videos_summary"
end
================================================
FILE: app/avo/dashboards/default.rb
================================================
class Avo::Dashboards::Default < Avo::Dashboards::BaseDashboard
self.id = "default"
self.name = "Dashboard"
self.description = "Overview of key metrics and alerts"
self.grid_cols = 3
def cards
card Avo::Cards::DuplicatesSummary
card Avo::Cards::SuspiciousSummary
card Avo::Cards::UnavailableVideosSummary
end
end
================================================
FILE: app/avo/dashboards/duplicates.rb
================================================
class Avo::Dashboards::Duplicates < Avo::Dashboards::BaseDashboard
self.id = "duplicates"
self.name = "Duplicate Detection"
self.description = "Find potential duplicate user profiles"
self.grid_cols = 3
def cards
card Avo::Cards::SameNameDuplicatesMetric
card Avo::Cards::ReversedNameDuplicatesMetric
card Avo::Cards::SameNameDuplicatesList
card Avo::Cards::ReversedNameDuplicatesList
end
end
================================================
FILE: app/avo/dashboards/suspicious.rb
================================================
class Avo::Dashboards::Suspicious < Avo::Dashboards::BaseDashboard
self.id = "suspicious"
self.name = "Suspicious Users"
self.description = "Monitor and manage suspicious user accounts"
self.grid_cols = 3
def cards
card Avo::Cards::SuspiciousUsersMetric
card Avo::Cards::SuspiciousSignalsBreakdown
card Avo::Cards::SuspiciousUsersList
end
end
================================================
FILE: app/avo/dashboards/unavailable_videos.rb
================================================
class Avo::Dashboards::UnavailableVideos < Avo::Dashboards::BaseDashboard
self.id = "unavailable_videos"
self.name = "Unavailable Videos"
self.description = "Monitor talks with videos that are no longer available"
self.grid_cols = 3
def cards
card Avo::Cards::UnavailableVideosMetric
card Avo::Cards::UnavailableVideosByEvent
card Avo::Cards::UnavailableVideosList
end
end
================================================
FILE: app/avo/filters/aliasable_type.rb
================================================
class Avo::Filters::AliasableType < Avo::Filters::SelectFilter
self.name = "Aliasable Type"
def apply(request, query, type)
if type
query.where(aliasable_type: type)
else
query
end
end
def options
{
"User" => "User",
"EventSeries" => "EventSeries",
"Organization" => "Organization",
"Talk" => "Talk"
}
end
end
================================================
FILE: app/avo/filters/attended_as.rb
================================================
class Avo::Filters::AttendedAs < Avo::Filters::SelectFilter
self.name = "Attended as"
def apply(request, query, attended_as)
if attended_as
query.where("attended_as is ?", attended_as)
else
query
end
end
def options
EventParticipation.attended_as
end
end
================================================
FILE: app/avo/filters/bio.rb
================================================
class Avo::Filters::Bio < Avo::Filters::TextFilter
self.name = "Bio"
self.button_label = "Filter by bio (contains)"
def apply(request, query, value)
query.where("bio LIKE ?", "%#{value}%")
end
end
================================================
FILE: app/avo/filters/bio_presence.rb
================================================
class Avo::Filters::BioPresence < Avo::Filters::BooleanFilter
self.name = "Bio presence"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query unless values["no_bio"]
query.where(bio: [nil, ""])
end
def options
{
no_bio: "Without bio"
}
end
end
================================================
FILE: app/avo/filters/canonical.rb
================================================
class Avo::Filters::Canonical < Avo::Filters::BooleanFilter
self.name = "Canonical"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query if values["canonical"] && values["not_canonical"]
if values["canonical"]
query = query.canonical
elsif values["not_canonical"]
query = query.not_canonical
end
query
end
def options
{
canonical: "Has canonical",
not_canonical: "Without canonical"
}
end
end
================================================
FILE: app/avo/filters/enhanced_transcript.rb
================================================
class Avo::Filters::EnhancedTranscript < Avo::Filters::BooleanFilter
self.name = "Enhanced transcript"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query if values["has_enhanced_transcript"] && values["no_enhanced_transcript"]
if values["has_enhanced_transcript"]
query = query.with_enhanced_transcript
elsif values["no_enhanced_transcript"]
query = query.without_enhanced_transcript
end
query
end
def options
{
has_enhanced_transcript: "With enhanced transcript",
no_enhanced_transcript: "Without enhanced transcript"
}
end
end
================================================
FILE: app/avo/filters/geocoded_presence.rb
================================================
class Avo::Filters::GeocodedPresence < Avo::Filters::BooleanFilter
self.name = "Geocoded"
def apply(request, query, values)
return query if values["geocoded"] && values["not_geocoded"]
if values["geocoded"]
query.geocoded
elsif values["not_geocoded"]
query.not_geocoded
else
query
end
end
def options
{
geocoded: "Geocoded",
not_geocoded: "Not geocoded"
}
end
end
================================================
FILE: app/avo/filters/github.rb
================================================
class Avo::Filters::GitHub < Avo::Filters::BooleanFilter
self.name = "GitHub handle"
def apply(request, query, values)
return query if values["has_github"] && values["no_github"]
if values["has_github"]
query = query.where.not(github: ["", nil])
elsif values["no_github"]
query = query.where(github: ["", nil])
end
query
end
def options
{
has_github: "With GitHub handle",
no_github: "Without GitHub handle"
}
end
end
================================================
FILE: app/avo/filters/github_handle.rb
================================================
class Avo::Filters::GitHubHandle < Avo::Filters::TextFilter
self.name = "GitHub handle (contains)"
def apply(request, query, value)
query.where("lower(github_handle) LIKE ?", "%#{value.downcase}%")
end
end
================================================
FILE: app/avo/filters/github_handle_presence.rb
================================================
class Avo::Filters::GitHubHandlePresence < Avo::Filters::BooleanFilter
self.name = "GitHub handle presence"
def apply(request, query, values)
return query if values["has_github"] && values["no_github"]
if values["has_github"]
query = query.where.not(github_handle: ["", nil])
elsif values["no_github"]
query = query.where(github_handle: ["", nil])
end
query
end
def options
{
has_github: "With GitHub handle",
no_github: "Without GitHub handle"
}
end
end
================================================
FILE: app/avo/filters/has_duplicate.rb
================================================
class Avo::Filters::HasDuplicate < Avo::Filters::BooleanFilter
self.name = "Has duplicate"
def apply(request, query, values)
if values["has_any_duplicate"]
query.with_any_duplicate
elsif values["has_reversed_name_duplicate"]
query.with_reversed_name_duplicate
elsif values["has_same_name_duplicate"]
query.with_same_name_duplicate
else
query
end
end
def options
{
has_any_duplicate: "Has any duplicate",
has_reversed_name_duplicate: "Has reversed name duplicate",
has_same_name_duplicate: "Has same name duplicate"
}
end
end
================================================
FILE: app/avo/filters/involvement_role.rb
================================================
class Avo::Filters::InvolvementRole < Avo::Filters::SelectFilter
self.name = "Role"
def apply(request, query, role)
if role
query.where(role: role)
else
query
end
end
def options
EventInvolvement.distinct.pluck(:role).compact.sort.index_by(&:itself)
end
end
================================================
FILE: app/avo/filters/language.rb
================================================
class Avo::Filters::Language < Avo::Filters::SelectFilter
self.name = "Language"
# self.visible = -> do
# true
# end
def apply(request, query, language)
if language
query.where("language is ?", language)
else
query
end
end
def options
Language.used
end
end
================================================
FILE: app/avo/filters/location_presence.rb
================================================
class Avo::Filters::LocationPresence < Avo::Filters::BooleanFilter
self.name = "Location presence"
def apply(request, query, values)
return query if values["has_location"] && values["no_location"]
if values["has_location"]
query = query.where.not(location: ["", nil])
elsif values["no_location"]
query = query.where(location: ["", nil])
end
query
end
def options
{
has_location: "With location",
no_location: "Without location"
}
end
end
================================================
FILE: app/avo/filters/name.rb
================================================
class Avo::Filters::Name < Avo::Filters::TextFilter
self.name = "Name"
self.button_label = "Filter by name (contains)"
def apply(request, query, value)
query.where("name LIKE ?", "%#{value}%")
end
end
================================================
FILE: app/avo/filters/provider.rb
================================================
class Avo::Filters::Provider < Avo::Filters::SelectFilter
self.name = "Provider"
def apply(request, query, provider)
if provider
query.where("provider is ?", provider)
else
query
end
end
def options
ConnectedAccount.providers
end
end
================================================
FILE: app/avo/filters/published.rb
================================================
class Avo::Filters::Published < Avo::Filters::BooleanFilter
self.name = "Approved Status"
def apply(request, query, values)
selected_statuses = values.select { |k, v| v }.keys
if selected_statuses.any?
query = query.where(status: selected_statuses)
end
query
end
def options
{
pending: "Pending",
approved: "Approved",
rejected: "Rejected",
duplicate: "Duplicate"
}
end
end
================================================
FILE: app/avo/filters/raw_transcript.rb
================================================
class Avo::Filters::RawTranscript < Avo::Filters::BooleanFilter
self.name = "Raw transcript"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query if values["has_transcript"] && values["no_transcript"]
if values["has_transcript"]
query = query.with_raw_transcript
elsif values["no_transcript"]
query = query.without_raw_transcript
end
query
end
def options
{
has_transcript: "With raw transcript",
no_transcript: "Without raw transcript"
}
end
end
================================================
FILE: app/avo/filters/slug.rb
================================================
class Avo::Filters::Slug < Avo::Filters::TextFilter
self.name = "Slug"
self.button_label = "Filter by slug (contains)"
def apply(request, query, value)
query.where("slug LIKE ?", "%#{value}%")
end
end
================================================
FILE: app/avo/filters/summary.rb
================================================
class Avo::Filters::Summary < Avo::Filters::BooleanFilter
self.name = "Summary"
def apply(request, query, values)
return query if values["has_summary"] && values["no_summary"]
if values["has_summary"]
query = query.with_summary
elsif values["no_summary"]
query = query.without_summary
end
query
end
def options
{
has_summary: "With summary",
no_summary: "Without summary"
}
end
end
================================================
FILE: app/avo/filters/suspicious.rb
================================================
class Avo::Filters::Suspicious < Avo::Filters::BooleanFilter
self.name = "Suspicious"
def apply(request, query, values)
return query unless values["suspicious"]
query.suspicious
end
def options
{
suspicious: "Suspicious users"
}
end
end
================================================
FILE: app/avo/filters/talk_event.rb
================================================
class Avo::Filters::TalkEvent < Avo::Filters::SelectFilter
self.name = "Talk event"
def apply(request, query, values)
query.where(event_id: values) if values
end
def options
Event.all.map { |event| [event.id, event.name] }.sort_by { |_, name| name }.to_h
end
end
================================================
FILE: app/avo/filters/title.rb
================================================
class Avo::Filters::Title < Avo::Filters::TextFilter
self.name = "Title"
self.button_label = "Filter by title (contains)"
def apply(request, query, value)
query.where("title LIKE ?", "%#{value}%")
end
end
================================================
FILE: app/avo/filters/topic_talks.rb
================================================
class Avo::Filters::TopicTalks < Avo::Filters::BooleanFilter
self.name = "Topic talks"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query if values["has_talks"] && values["no_talks"] && values["one_talk"] && values["two_talks"]
if values["has_talks"]
query = query.with_talks
elsif values["no_talks"]
query = query.without_talks
elsif values["one_talk"]
query = query.with_n_talk(1)
elsif values["two_talks"]
query = query.with_n_talk(2)
end
query
end
def options
{
has_talks: "With talk topics",
no_talks: "Without talk topics",
one_talk: "One talk",
two_talks: "Two talks"
}
end
end
================================================
FILE: app/avo/filters/topics.rb
================================================
class Avo::Filters::Topics < Avo::Filters::BooleanFilter
self.name = "Topics"
# self.visible = -> do
# true
# end
def apply(request, query, values)
return query if values["has_topics"] && values["no_topics"]
if values["has_topics"]
query = query.with_topics
elsif values["no_topics"]
query = query.without_topics
end
query
end
def options
{
has_topics: "With topics",
no_topics: "Without topics"
}
end
end
================================================
FILE: app/avo/filters/video_availability.rb
================================================
class Avo::Filters::VideoAvailability < Avo::Filters::BooleanFilter
self.name = "Video Availability"
def apply(request, query, values)
if values["available"]
query = query.video_available
end
if values["unavailable"]
query = query.video_unavailable
end
if values["watchable"]
query = query.watchable
end
if values["marked_unavailable"]
query = query.where.not(video_unavailable_at: nil)
end
query
end
def options
{
available: "Watchable & Available",
unavailable: "Watchable & Unavailable",
watchable: "Watchable",
marked_unavailable: "Marked Unavailable"
}
end
end
================================================
FILE: app/avo/filters/video_provider.rb
================================================
class Avo::Filters::VideoProvider < Avo::Filters::SelectFilter
self.name = "Video Provider"
# self.visible = -> do
# true
# end
def apply(request, query, provider)
if provider
query.where(video_provider: provider)
else
query
end
end
def options
Talk.video_providers
end
end
================================================
FILE: app/avo/filters/without_talks.rb
================================================
class Avo::Filters::WithoutTalks < Avo::Filters::BooleanFilter
self.name = "Without talks"
# self.visible = -> do
# true
# end
def apply(request, query, values)
if values["no_talks"]
query = query.without_talks
end
query
end
def options
{
no_talks: "Without talks"
}
end
end
================================================
FILE: app/avo/resources/alias.rb
================================================
class Avo::Resources::Alias < Avo::BaseResource
self.includes = [:aliasable]
self.search = {
query: -> { query.where("name LIKE ? OR slug LIKE ?", "%#{params[:q]}%", "%#{params[:q]}%") }
}
self.external_link = -> {
Avo::Resources::Alias.aliasable_link(main_app, record)
}
def filters
filter Avo::Filters::AliasableType
end
def fields
field :id, as: :id
field :aliasable, as: :belongs_to, polymorphic_as: :aliasable, types: [::User, ::Event, ::EventSeries, ::Organization, ::City]
field :aliasable_type, as: :text, readonly: true, only_on: :show
field :aliasable_id, as: :id, readonly: true, only_on: :show
field :name, as: :text, required: true
field :slug, as: :text, required: true
field :external_url,
as: :text,
hide_on: [:edit, :new],
format_using: -> { view_context.link_to(value, value, target: "_blank") if value.present? } do
Avo::Resources::Alias.aliasable_link(main_app, record)
end
field :created_at, as: :date_time, readonly: true
field :updated_at, as: :date_time, readonly: true
end
def self.aliasable_link(app, record)
case record.aliasable_type
when "User"
app.profile_path(record.aliasable.slug)
when "Event"
app.event_path(record.aliasable.slug)
when "EventSeries"
app.series_path(record.aliasable.slug)
when "Talk"
app.talk_path(record.aliasable.slug)
when "Organization"
app.organization_path(record.aliasable.slug)
when "City"
app.city_path(record.aliasable.slug)
else
raise "Unknown aliasable type: #{record.aliasable_type}"
end
end
end
================================================
FILE: app/avo/resources/city.rb
================================================
class Avo::Resources::City < Avo::BaseResource
self.model_class = ::City
self.title = :name
self.includes = []
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.search = {
query: -> { query.where("name LIKE ?", "%#{params[:q]}%") }
}
self.external_link = -> {
main_app.city_path(record)
}
self.map_view = {
mapkick_options: {
controls: true
},
record_marker: -> {
{
latitude: record.latitude,
longitude: record.longitude,
tooltip: record.name
}
},
table: {
visible: true
}
}
def fields
field :id, as: :id
field :name, as: :text, link_to_record: true, sortable: true
field :slug, as: :text, sortable: true
field :state_code, as: :text
field :country_code, as: :select, options: country_options, include_blank: true
field :featured, as: :boolean, sortable: true
field :latitude, as: :number, hide_on: :index
field :longitude, as: :number, hide_on: :index
field :geocode_metadata, as: :code, language: :json, hide_on: :index
field :created_at, as: :date_time, hide_on: :forms, sortable: true
field :updated_at, as: :date_time, hide_on: :forms, sortable: true
end
def filters
filter Avo::Filters::Name
end
def country_options
Country.select_options
end
end
================================================
FILE: app/avo/resources/connected_account.rb
================================================
# id :integer
# uid :string
# provider :string
# username :string
# user_id :integer
# access_token :string
# expires_at :datetime
# created_at :datetime
# updated_at :datetime
class Avo::Resources::ConnectedAccount < Avo::BaseResource
# self.includes = []
# self.attachments = []
self.search = {
query: -> { query.ransack(id_eq: params[:q], uid_cont: params[:q], m: "or").result(distinct: false) }
}
def fields
field :id, as: :id
field :uid, as: :text
field :provider, as: :text, sortable: true
field :username, as: :text, sortable: true
field :user, as: :belongs_to, sortable: true
field :user_id, as: :text, sortable: true, hide_on: [:index]
field :access_token, as: :text, hide_on: [:index]
field :expires_at, as: :date_time, hide_on: [:index]
field :created_at, as: :date_time, hide_on: [:index]
field :updated_at, as: :date_time, hide_on: [:index]
end
def filters
filter Avo::Filters::Provider
end
end
================================================
FILE: app/avo/resources/contributor.rb
================================================
class Avo::Resources::Contributor < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: q, m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :login, as: :text
field :name, as: :text
field :avatar_url, as: :text
field :html_url, as: :text
end
end
================================================
FILE: app/avo/resources/event.rb
================================================
class Avo::Resources::Event < Avo::BaseResource
self.includes = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.external_link = -> {
main_app.event_path(record)
}
def fields
field :id, as: :id
field :name, as: :text, link_to_record: true, sortable: true
field :date, as: :date, hide_on: :index
field :date_precision, as: :select, options: ::Event.date_precisions, hide_on: :index
field :start_date, as: :date, hide_on: :index
field :end_date, as: :date, hide_on: :index
field :location, as: :text, hide_on: :index
field :city, as: :text, hide_on: :index
field :state_code, as: :text, hide_on: :index
field :country_code, as: :select, options: country_options, include_blank: true
field :latitude, as: :number, hide_on: :index
field :longitude, as: :number, hide_on: :index
field :geocode_metadata, as: :code, hide_on: :index
field :kind, hide_on: :index
field :slug, as: :text
field :updated_at, as: :date, sortable: true
# field :suggestions, as: :has_many
field :series, as: :belongs_to
field :talks, as: :has_many
field :speakers, as: :has_many, through: :talks, class_name: "User"
field :participants, as: :has_many, through: :event_participations, class_name: "User"
field :event_involvements, as: :has_many
field :topics, as: :has_many
field :sponsors, as: :has_many
end
def actions
action Avo::Actions::AssignCanonicalEvent
action Avo::Actions::GeocodeRecord
end
def filters
filter Avo::Filters::Name
filter Avo::Filters::WithoutTalks
filter Avo::Filters::Canonical
filter Avo::Filters::LocationPresence
filter Avo::Filters::GeocodedPresence
end
def country_options
Country.select_options
end
end
================================================
FILE: app/avo/resources/event_involvement.rb
================================================
class Avo::Resources::EventInvolvement < Avo::BaseResource
self.includes = [:involvementable, :event]
self.search = {
query: -> {
query
.left_joins("LEFT JOIN users ON users.id = event_involvements.involvementable_id AND event_involvements.involvementable_type = 'User'")
.left_joins("LEFT JOIN event_series ON event_series.id = event_involvements.involvementable_id AND event_involvements.involvementable_type = 'EventSeries'")
.where("users.name LIKE ? OR event_series.name LIKE ? OR event_involvements.role LIKE ?", "%#{params[:q]}%", "%#{params[:q]}%", "%#{params[:q]}%")
}
}
def fields
field :id, as: :id
field :involvementable, as: :belongs_to, polymorphic_as: :involvementable, types: [::User, ::EventSeries], searchable: true
field :event, as: :belongs_to, searchable: true, attach_scope: -> { query.order(name: :asc) }
field :role, as: :text
field :created_at, as: :date_time
field :updated_at, as: :date_time
end
def filters
filter Avo::Filters::InvolvementRole
end
end
================================================
FILE: app/avo/resources/event_participation.rb
================================================
class Avo::Resources::EventParticipation < Avo::BaseResource
self.includes = [:user, :event]
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: q, m: "or").result(distinct: false) }
# }
self.search = {
query: -> { query.joins(:user).where("users.name LIKE ?", "%#{params[:q]}%") }
}
def fields
field :id, as: :id
field :attended_as, as: :select, enum: ::EventParticipation.attended_as
field :user, as: :belongs_to, searchable: true
field :event, as: :belongs_to, searchable: true, attach_scope: -> { query.order(name: :asc) }
end
def filters
filter Avo::Filters::AttendedAs
end
end
================================================
FILE: app/avo/resources/event_series.rb
================================================
class Avo::Resources::EventSeries < Avo::BaseResource
self.single_includes = [:events]
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.external_link = -> {
main_app.series_path(record)
}
def fields
field :id, as: :id
field :name, as: :text, link_to_record: true
field :description, as: :text, hide_on: [:index, :forms]
field :website, as: :text
field :language, as: :text
field :kind, as: :select, enum: ::EventSeries.kinds
field :frequency, as: :select, enum: ::EventSeries.frequencies, hide_on: :index
field :youtube_channel_id, as: :text, hide_on: :index
field :youtube_channel_name, as: :text, hide_on: :index
field :slug, as: :text, hide_on: :index
field :twitter, as: :text, hide_on: :index
# field :suggestions, as: :has_many
field :events, as: :has_many
field :talks, as: :has_many, through: :events
end
def filters
filter Avo::Filters::Name
filter Avo::Filters::Slug
end
end
================================================
FILE: app/avo/resources/favorite_user.rb
================================================
class Avo::Resources::FavoriteUser < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: q, m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :user, as: :belongs_to
field :favorite_user, as: :belongs_to
end
end
================================================
FILE: app/avo/resources/geocode_result.rb
================================================
class Avo::Resources::GeocodeResult < Avo::BaseResource
self.model_class = ::GeocodeResult
self.title = :query
self.includes = []
self.search = {
query: -> { query.where("query LIKE ?", "%#{params[:q]}%") }
}
def fields
field :id, as: :id
field :query, as: :text, link_to_record: true, sortable: true
field :response_body, as: :code, language: "json", hide_on: :index
field :created_at, as: :date_time, hide_on: :forms, sortable: true
field :updated_at, as: :date_time, hide_on: :forms, sortable: true
end
end
================================================
FILE: app/avo/resources/organization.rb
================================================
class Avo::Resources::Organization < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.search = {
query: -> { query.where("lower(name) LIKE ?", "%#{params[:q]&.downcase}%") }
}
def fields
field :id, as: :id
field :name, as: :text
field :kind, as: :select, enum: ::Organization.kinds
field :website, as: :text
field :slug, as: :text
field :description, as: :textarea
field :main_location, as: :text
field :sponsors, as: :has_many
field :event_involvements, as: :has_many
end
end
================================================
FILE: app/avo/resources/session.rb
================================================
class Avo::Resources::Session < Avo::BaseResource
self.includes = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id, link_to_record: true
field :user_agent, as: :text, format_using: -> { value.truncate(50) }, only_on: :index
field :user_agent, as: :text, hide_on: :index
field :ip_address, as: :text
field :user, as: :belongs_to, use_resource: Avo::Resources::User, link_to_record: true, hide_on: :index
end
end
================================================
FILE: app/avo/resources/sponsor.rb
================================================
class Avo::Resources::Sponsor < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :event, as: :belongs_to
field :organization, as: :belongs_to
field :tier, as: :text
field :badge, as: :text
end
end
================================================
FILE: app/avo/resources/suggestion.rb
================================================
class Avo::Resources::Suggestion < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :idlo
field :content, as: :text, only_on: :index do
record.content.map { |key, value| "#{key}: #{value}" }.to_sentence.truncate(50)
end
field :content, as: :key_value, hide_on: :index
field :status, as: :status, loading_when: [:pending], success_when: [:approved], link_to_record: true, hide_on: [:forms]
field :suggestable, as: :belongs_to, polymorphic_as: :suggestable
field :approved_by, as: :belongs_to
field :suggested_by, as: :belongs_to
end
end
================================================
FILE: app/avo/resources/talk.rb
================================================
class Avo::Resources::Talk < Avo::BaseResource
self.includes = [:event, :speakers, :topics]
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.keep_filters_panel_open = true
self.search = {
query: -> { query.where(id: Talk.search(params[:q]).map(&:id)) }
}
self.external_link = -> {
main_app.talk_path(record)
}
def fields
field :id, as: :id
field :title, as: :text, link_to_record: true, sortable: true
field :event, as: :belongs_to
field :speaker_tags, for_attribute: :speakers, name: "Speakers", through: :speaker_talks, as: :tags, hide_on: [:show, :forms] do
record.speakers.map(&:name)
end
field :topics, as: :tags, hide_on: [:index, :forms] do
record.topics.map(&:name)
end
field :updated_at, as: :date, sortable: true
field :slides_url, as: :text, hide_on: :index
field :summary, as: :easy_mde, hide_on: :index
field :has_raw_transcript, name: "Raw Transcript", as: :boolean do
record.raw_transcript.present?
end
field :has_enhanced_transcript, hide_on: :index, name: "Enhanced Transcript", as: :boolean do
record.enhanced_transcript.present?
end
field :raw_transcript_length, name: "Raw Transcript length", as: :number do
record.raw_transcript&.to_text&.length
end
field :enhanced_transcript_length, name: "Enhanced Transcript length", as: :number do
record.enhanced_transcript&.to_text&.length
end
field :has_duration, name: "Duration", as: :boolean do
record.duration_in_seconds.present?
end
field :has_summary, name: "Summary", as: :boolean do
record.summary.present?
end
field :has_topics, name: "Topics", as: :boolean do
record.topics.any?
end
field :language, hide_on: :index
field :slug, as: :text, hide_on: :index
field :year, as: :number, hide_on: :index
field :video_id, as: :text, hide_on: :index
field :video_provider, as: :text, hide_on: :index
field :video_available, name: "Video Available", as: :boolean do
record.video_available?
end
field :video_unavailable_at, as: :date_time, hide_on: :index
field :external_player, as: :boolean, hide_on: :index
field :date, as: :date, hide_on: :index
field :like_count, as: :number, hide_on: :index
field :view_count, as: :number, hide_on: :index
field :duration_in_seconds, as: :number, hide_on: [:index, :edit], format_using: -> { Duration.seconds_to_formatted_duration(value, raise: false) }, name: "Duration", readonly: true
field :duration_in_seconds, as: :number, hide_on: :index, show_on: [:edit], name: "Duration in seconds"
field :created_at, as: :date, hide_on: :index
field :updated_at, as: :date, sortable: true, filterable: true
field :description, as: :textarea, hide_on: :index
field :thumbnail_xs, as: :external_image, hide_on: :index
field :thumbnail_sm, as: :external_image, hide_on: :index
field :thumbnail_md, as: :external_image, hide_on: :index
field :thumbnail_lg, as: :external_image, hide_on: :index
field :thumbnail_xl, as: :external_image, hide_on: :index
# field :speaker_talks, as: :has_many, attach_scope: -> { query.order(name: :asc) }
field :speakers, as: :has_many
field :raw_transcript, as: :textarea, hide_on: :index, format_using: -> { value&.to_text }, readonly: true
field :enhanced_transcript, as: :textarea, hide_on: :index, format_using: -> { value&.to_text }, readonly: true
# field :suggestions, as: :has_many
end
def actions
action Avo::Actions::TalkIngest
action Avo::Actions::UpdateFromYml
action Avo::Actions::TalkIndex
action Avo::Actions::FetchDuration
end
def filters
filter Avo::Filters::TalkEvent
filter Avo::Filters::RawTranscript
filter Avo::Filters::EnhancedTranscript
filter Avo::Filters::Summary
filter Avo::Filters::Topics
filter Avo::Filters::Title
filter Avo::Filters::Slug
filter Avo::Filters::Language
filter Avo::Filters::VideoProvider
filter Avo::Filters::VideoAvailability
end
end
================================================
FILE: app/avo/resources/talk_topic.rb
================================================
class Avo::Resources::TalkTopic < Avo::BaseResource
self.includes = [:talk, :topic]
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :talk_title, as: :text do
record.talk.title
end
field :topic_name, as: :text do
record.topic.name
end
end
end
================================================
FILE: app/avo/resources/talk_transcript.rb
================================================
class Avo::Resources::TalkTranscript < Avo::BaseResource
# self.includes = []
# self.attachments = []
self.model_class = ::Talk::Transcript
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :talk, as: :belongs_to
field :raw_transcript, as: :textarea, hide_on: [:index], format_using: -> { value&.to_text }
field :enhanced_transcript, as: :textarea, hide_on: [:index], format_using: -> { value&.to_text }
field :created_at, as: :date_time, sortable: true, hide_on: [:index]
field :updated_at, as: :date_time, sortable: true
end
def actions
action Avo::Actions::EnhanceTranscript
end
end
================================================
FILE: app/avo/resources/topic.rb
================================================
class Avo::Resources::Topic < Avo::BaseResource
# self.includes = []
# self.attachments = []
self.search = {
query: -> { query.where("name LIKE ?", "%#{params[:q]}%") }
}
self.find_record_method = -> {
if id.is_a?(Array)
query.where(slug: id)
else
query.find_by(slug: id)
end
}
self.keep_filters_panel_open = true
self.external_link = -> {
main_app.topic_path(record)
}
def fields
field :id, as: :id
field :name, as: :text, link_to_record: true
field :talks_count, as: :number, hide_on: :forms, sortable: true
field :canonical, as: :belongs_to, use_resource: "Topic", hide_on: :index
field :description, as: :markdown, hide_on: :index
field :status, as: :status, loading_when: "pending", success_when: "approved", failed_when: "rejected", hide_on: :forms
field :status, as: :select, enum: ::Topic.statuses, only_on: :forms
field :gem_names, as: :text, hide_on: :forms do
record.topic_gems.pluck(:gem_name).join(", ")
end
field :talks, as: :has_many
field :topic_gems, as: :has_many
end
def actions
action Avo::Actions::ApproveTopic
action Avo::Actions::RejectTopic
action Avo::Actions::AssignCanonicalTopic
action Avo::Actions::FindTopicTalks
end
def filters
filter Avo::Filters::Name
filter Avo::Filters::Published
filter Avo::Filters::TopicTalks
end
end
================================================
FILE: app/avo/resources/topic_gem.rb
================================================
class Avo::Resources::TopicGem < Avo::BaseResource
self.title = :gem_name
self.search = {
query: -> { query.where("gem_name LIKE ?", "%#{params[:q]}%") }
}
self.external_link = -> {
record.rubygems_url
}
def self.name
"Gem"
end
def fields
field :id, as: :id
field :gem_name, as: :text, link_to_record: true, help: "Exact gem name from RubyGems.org (e.g., 'sidekiq', 'activerecord')"
field :topic, as: :belongs_to, link_to_record: true
end
end
================================================
FILE: app/avo/resources/user.rb
================================================
class Avo::Resources::User < Avo::BaseResource
self.title = :name
self.includes = []
self.find_record_method = -> {
if id.is_a?(Array)
if id.first.to_i == 0
query.where(slug: id).or(query.where(github_handle: id))
else
query.where(id: id)
end
else
(id.to_i == 0) ? (query.find_by_slug_or_alias(id) || query.find_by_github_handle(id)) : query.find(id)
end
}
self.search = {
query: -> { query.where("lower(name) LIKE ? OR email LIKE ?", "%#{params[:q]&.downcase}%", "%#{params[:q]}%") }
}
self.external_link = -> {
main_app.profile_path(record)
}
def fields
field :id, as: :id, link_to_record: true
field :name, as: :text, link_to_record: true
field :email, as: :text, link_to_record: true, format_using: -> { value&.truncate(30) }, only_on: :index
field :email, as: :text, link_to_record: true, hide_on: :index
field :github_handle, as: :text, link_to_record: true
field :admin, as: :boolean
field :marked_for_deletion, as: :boolean, hide_on: :index
field :suspicion_marked_at, as: :date_time, hide_on: :index
field :suspicion_cleared_at, as: :date_time, hide_on: :index
field :slug, as: :text, hide_on: :index
field :bio, as: :textarea, hide_on: :index
field :website, as: :text, hide_on: :index
field :twitter, as: :text, hide_on: :index
field :bsky, as: :text, hide_on: :index
field :linkedin, as: :text, hide_on: :index
field :mastodon, as: :text, hide_on: :index
field :speakerdeck, as: :text, hide_on: :index
field :pronouns, as: :text, hide_on: :index
field :pronouns_type, as: :text, hide_on: :index
field :location, as: :text, hide_on: :index
field :city, as: :text, hide_on: :index, readonly: true
field :state_code, as: :text, hide_on: :index, readonly: true
field :country_code, as: :text, hide_on: :index, readonly: true
field :latitude, as: :number, hide_on: :index, readonly: true
field :longitude, as: :number, hide_on: :index, readonly: true
field :geocode_metadata, as: :code, hide_on: :index, readonly: true
field :talks_count, as: :number, sortable: true
field :aliases, as: :has_many, hide_on: :index
field :talks, as: :has_many, hide_on: :index
field :user_talks, as: :has_many, hide_on: :index
field :connected_accounts, as: :has_many
field :sessions, as: :has_many
field :event_participations, as: :has_many, hide_on: :index, use_resource: Avo::Resources::EventParticipation
field :participated_events, as: :has_many, hide_on: :index, use_resource: Avo::Resources::Event
field :event_involvements, as: :has_many, hide_on: :index
end
def filters
filter Avo::Filters::Name
filter Avo::Filters::Slug
filter Avo::Filters::GitHubHandle
filter Avo::Filters::GitHubHandlePresence
filter Avo::Filters::BioPresence
filter Avo::Filters::LocationPresence
filter Avo::Filters::GeocodedPresence
filter Avo::Filters::Suspicious
filter Avo::Filters::HasDuplicate
end
def actions
action Avo::Actions::UserFetchGitHub
action Avo::Actions::GeocodeRecord
action Avo::Actions::ClearUser
end
end
================================================
FILE: app/avo/resources/user_talk.rb
================================================
class Avo::Resources::UserTalk < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: q, m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
field :user, as: :belongs_to
field :talk, as: :belongs_to
end
end
================================================
FILE: app/avo/resources/watch_list.rb
================================================
class Avo::Resources::WatchList < Avo::BaseResource
self.title = :name
self.includes = []
def fields
field :id, as: :id
field :name, as: :text
field :description, as: :textarea
field :user, as: :belongs_to
field :talks, as: :has_many, through: :watch_list_talks
end
end
================================================
FILE: app/avo/resources/watch_list_talk.rb
================================================
class Avo::Resources::WatchListTalk < Avo::BaseResource
self.includes = [:talk, :watch_list]
def fields
field :id, as: :id
field :talk, as: :belongs_to
field :watch_list, as: :belongs_to
field :created_at, as: :date_time
end
end
================================================
FILE: app/channels/application_cable/channel.rb
================================================
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
================================================
FILE: app/channels/application_cable/connection.rb
================================================
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
end
================================================
FILE: app/clients/application_client.rb
================================================
class ApplicationClient
class Error < StandardError; end
class Forbidden < Error; end
class Unauthorized < Error; end
class RateLimit < Error; end
class NotFound < Error; end
class InternalError < Error; end
BASE_URI = "https://example.org"
NET_HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError]
def initialize(token: nil)
@token = token
end
def default_headers
{
"Accept" => content_type,
"Content-Type" => content_type
}.merge(authorization_header)
end
def content_type
"application/json"
end
def authorization_header
token ? {"Authorization" => "Bearer #{token}"} : {}
end
def default_query_params
{}
end
def get(path, headers: {}, query: nil)
make_request(klass: Net::HTTP::Get, path: path, headers: headers, query: query)
end
def post(path, headers: {}, query: nil, body: nil, form_data: nil)
make_request(
klass: Net::HTTP::Post,
path: path,
headers: headers,
query: query,
body: body,
form_data: form_data
)
end
def patch(path, headers: {}, query: nil, body: nil, form_data: nil)
make_request(
klass: Net::HTTP::Patch,
path: path,
headers: headers,
query: query,
body: body,
form_data: form_data
)
end
def put(path, headers: {}, query: nil, body: nil, form_data: nil)
make_request(
klass: Net::HTTP::Put,
path: path,
headers: headers,
query: query,
body: body,
form_data: form_data
)
end
def delete(path, headers: {}, query: nil, body: nil)
make_request(klass: Net::HTTP::Delete, path: path, headers: headers, query: query, body: body)
end
def base_uri
self.class::BASE_URI
end
attr_reader :token
def make_request(klass:, path:, headers: {}, body: nil, query: nil, form_data: nil)
raise ArgumentError, "Cannot pass both body and form_data" if body.present? && form_data.present?
uri = URI("#{base_uri}#{path}")
existing_params = Rack::Utils.parse_query(uri.query).with_defaults(default_query_params)
query_params = existing_params.merge(query || {})
uri.query = Rack::Utils.build_query(query_params) if query_params.present?
Rails.logger.debug("#{klass.name.split("::").last.upcase}: #{uri}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.instance_of? URI::HTTPS
all_headers = default_headers.merge(headers)
all_headers.delete("Content-Type") if klass == Net::HTTP::Get
request = klass.new(uri.request_uri, all_headers)
if body.present?
request.body = build_body(body)
elsif form_data.present?
request.set_form(form_data, "multipart/form-data")
end
handle_response Response.new(http.request(request))
end
def handle_response(response)
case response.code
when "200", "201", "202", "203", "204"
response
when "401"
raise Unauthorized, response.body
when "403"
raise Forbidden, response.body
when "404"
raise NotFound, response.body
when "429"
raise RateLimit, response.body
when "500"
raise InternalError, response.body
else
raise Error, "#{response.code} - #{response.body}"
end
end
def build_body(body)
case body
when String
body
else
body.to_json
end
end
class Response
JSON_OBJECT_CLASS = OpenStruct
PARSER = {
"application/json" => ->(response) { JSON.parse(response.body, object_class: JSON_OBJECT_CLASS) },
"application/xml" => ->(response) { Nokogiri::XML(response.body) }
}
FALLBACK_PARSER = ->(response) { response.body }
attr_reader :original_response
delegate :code, :body, to: :original_response
delegate_missing_to :parsed_body
def initialize(original_response)
@original_response = original_response
end
def headers
@headers ||= original_response.each_header.to_h.transform_keys { |k| k.underscore.to_sym }
end
def content_type
headers[:content_type].split(";").first
end
def parsed_body
@parsed_body ||= PARSER.fetch(content_type, FALLBACK_PARSER).call(self)
end
end
end
================================================
FILE: app/clients/blue_sky.rb
================================================
# frozen_string_literal: true
BlueSky = DelegateClass(Minisky) do
alias_method :client, :__getobj__
singleton_class.attr_reader :host
@host = "api.bsky.app"
def self.build(host = self.host, **options)
new Minisky.new(host, nil, options)
end
# client = BlueSky.build("other.pds.host", id:, pass:) # Allow creating ad-hoc authenticated clients.
# client = BlueSky.new FakeMinisky.new("example.com") # Allow injecting a stubbed Minisky for testing.
delegate :new, :build, to: :class
def profile_metadata(handle)
get_request("app.bsky.actor.getProfile", {actor: handle})
end
end.build
================================================
FILE: app/clients/github/client.rb
================================================
module GitHub
class Client < ApplicationClient
BASE_URI = "https://api.github.com"
def initialize
super
end
private
def authorization_header
token ? {"Authorization" => "Bearer #{token}"} : {}
end
def content_type
"application/vnd.github+json"
end
def token
Rails.application.credentials.github&.dig(:token) || ENV["RUBYVIDEO_GITHUB_TOKEN"]
end
end
end
================================================
FILE: app/clients/github/contributors_client.rb
================================================
module GitHub
class ContributorsClient < Client
OWNER = "rubyevents"
REPO = "rubyevents"
def fetch_all
contributors = fetch_contributors
enrich_with_user_data(contributors)
end
private
def fetch_contributors
contributors = []
page = 1
per_page = 100
loop do
response = get("/repos/#{OWNER}/#{REPO}/contributors", query: {page: page, per_page: per_page})
batch = response.parsed_body
break if batch.empty?
contributors.concat(batch.reject { |c| c.login.include?("[bot]") })
page += 1
end
contributors
end
def enrich_with_user_data(contributors)
query_fields = contributors.map.with_index do |contributor, index|
%{user#{index}: user(login: "#{contributor.login}") { login name avatarUrl url }}
end.join("\n")
graphql_query = "{ #{query_fields} }"
response = post("/graphql", body: {query: graphql_query})
users = response.parsed_body.data.to_h.values.compact
users_by_login = users.index_by { |u| u.login }
contributors.map do |contributor|
user = users_by_login[contributor.login]
{
login: contributor.login,
name: user&.name,
avatar_url: user&.avatarUrl || contributor.avatar_url,
html_url: user&.url || contributor.html_url
}
end
end
end
end
================================================
FILE: app/clients/github/user_client.rb
================================================
module GitHub
class UserClient < GitHub::Client
def profile(username)
get("/users/#{username}")
end
def social_accounts(username)
get("/users/#{username}/social_accounts")
end
def search(q, per_page: 10, page: 1)
get("/search/users", query: {q: q, per_page: per_page, page: page})
end
def emails
get("/user/emails")
end
end
end
================================================
FILE: app/clients/ruby_conferences/client.rb
================================================
module RubyConferences
class Client < ApplicationClient
BASE_URI = "https://raw.githubusercontent.com/ruby-conferences/ruby-conferences.github.io/refs/heads/main"
def conferences
YAML.load(get("/_data/conferences.yml").body, permitted_classes: [Date])
end
def conferences_cached
Rails.cache.fetch("ruby-conferences/_data/conferences.yml", expires_in: 1.day) do
conferences
end
end
private
def content_type
"text/yaml"
end
end
end
================================================
FILE: app/clients/speakerdeck/client.rb
================================================
# frozen_string_literal: true
module Speakerdeck
class Client < ApplicationClient
BASE_URI = "https://speakerdeck.com"
def oembed(url)
deck_url = normalize_url(url)
get("/oembed.json", query: {url: deck_url})
end
private
def normalize_url(url)
if url.start_with?("http://", "https://")
url
else
"#{BASE_URI}/#{url}"
end
end
def authorization_header
{}
end
end
end
================================================
FILE: app/clients/youtube/channels.rb
================================================
require "open-uri"
module YouTube
class Channels < YouTube::Client
def id_by_name(channel_name:)
response = get("/channels", query: {forUsername: "\"#{channel_name}\"", key: token, part: "snippet,contentDetails,statistics"})
response.try(:items)&.first&.id || fallback_using_scrapping(channel_name: channel_name)
end
private
def default_headers
{
"Content-Type" => "application/json"
}
end
def fallback_using_scrapping(channel_name:)
# for some reason I was unable to get the channel id for paris-rb
# this is a fallback solution using a scrapping approach
html = URI.open("https://www.youtube.com/@#{channel_name}")
doc = Nokogiri::HTML(html)
meta_tag = doc.at_css('meta[itemprop="identifier"]')
meta_tag["content"]
end
end
end
================================================
FILE: app/clients/youtube/client.rb
================================================
module YouTube
class Client < ApplicationClient
BASE_URI = "https://youtube.googleapis.com/youtube/v3"
private
def all_items(path, query: {})
response = get(path, query: query.merge({key: token, maxResults: 50}))
all_items = response.items
loop do
next_page_token = response.try(:nextPageToken)
break if next_page_token.nil?
response = get(path, query: query.merge({key: token, maxResults: 50, pageToken: next_page_token}))
all_items += response.items
end
all_items
end
def default_headers
{
"Content-Type" => "application/json"
}
end
def token
Rails.application.credentials.youtube&.dig(:api_key) || ENV["YOUTUBE_API_KEY"]
end
end
end
================================================
FILE: app/clients/youtube/playlist_items.rb
================================================
module YouTube
class PlaylistItems < YouTube::Client
def all(playlist_id:)
all_items("/playlistItems", query: {playlistId: playlist_id, part: "snippet,contentDetails"}).map do |metadata|
OpenStruct.new({
id: metadata.id,
title: metadata.snippet.title,
description: metadata.snippet.description,
published_at: DateTime.parse(metadata.snippet.publishedAt).to_date.to_s,
channel_id: metadata.snippet.channelId,
year: metadata.snippet.title.match(/\d{4}/).to_s.presence || DateTime.parse(metadata.snippet.publishedAt).year,
slug: metadata.snippet.title.parameterize,
thumbnail_xs: metadata.snippet.thumbnails.default&.url,
thumbnail_sm: metadata.snippet.thumbnails.medium&.url,
thumbnail_md: metadata.snippet.thumbnails.high&.url,
thumbnail_lg: metadata.snippet.thumbnails.standard&.url,
thumbnail_xl: metadata.snippet.thumbnails.maxres&.url,
video_provider: "youtube",
video_id: metadata.contentDetails.videoId
})
end
end
end
end
================================================
FILE: app/clients/youtube/playlists.rb
================================================
module YouTube
class Playlists < YouTube::Client
DEFAULT_METADATA_PARSER = "YouTube::VideoMetadata"
def all(channel_id:, title_matcher: nil)
items = all_items("/playlists", query: {channelId: channel_id, part: "snippet,contentDetails"}).map do |metadata|
year = metadata.snippet.title.match(/\d{4}/).to_s.presence || DateTime.parse(metadata.snippet.publishedAt).year
OpenStruct.new({
id: metadata.id,
title: metadata.snippet.title,
kind: "conference",
location: "Earth",
description: metadata.snippet.description,
published_at: DateTime.parse(metadata.snippet.publishedAt).to_date.to_s,
start_date: "#{year}-xx-xx",
end_date: "#{year}-xx-xx",
channel_id: metadata.snippet.channelId,
year: year.to_i,
videos_count: metadata.contentDetails.itemCount,
metadata_parser: DEFAULT_METADATA_PARSER,
slug: metadata.snippet.title.parameterize,
banner_background: "#081625",
featured_background: "#000000",
featured_color: "#FFFFFF"
})
end
items = items.select { |item| item.title.match?(string_to_regex(title_matcher)) } if title_matcher
items
end
private
def string_to_regex(str)
Regexp.new(str, "i")
end
end
end
================================================
FILE: app/clients/youtube/thumbnail.rb
================================================
# frozen_string_literal: true
module YouTube
class Thumbnail
SIZES = %w[maxresdefault sddefault hqdefault mqdefault default].freeze
DEFAULT_MAX_SIZE = 5000
EXPECTED_ASPECT_RATIO = 16.0 / 9.0
ASPECT_RATIO_TOLERANCE = 0.1
SIZE_MAPPING = {
thumbnail_xl: "maxresdefault",
thumbnail_lg: "sddefault",
thumbnail_md: "hqdefault",
thumbnail_sm: "mqdefault",
thumbnail_xs: "default"
}.freeze
def initialize(video_id)
@video_id = video_id
end
def best_url
best_url_from("maxresdefault")
end
def best_url_for(talk_size)
starting_size = SIZE_MAPPING[talk_size.to_sym]
return nil unless starting_size
best_url_from(starting_size)
end
def best_url_from(starting_size)
start_index = SIZES.index(starting_size) || 0
candidate_sizes = SIZES[start_index..]
candidate_sizes.each do |size|
url = url_for(size)
next if default?(url)
next unless valid_aspect_ratio?(url)
return url
end
candidate_sizes.each do |size|
url = url_for(size)
return url unless default?(url)
end
nil
end
def url_for(size)
"https://i.ytimg.com/vi/#{@video_id}/#{size}.jpg"
end
def default?(url)
content_length(url) < DEFAULT_MAX_SIZE
rescue => e
Rails.logger.error("Error checking YouTube thumbnail #{url}: #{e.message}")
false
end
def valid_aspect_ratio?(url)
uri = URI.parse(url)
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.open_timeout = 5
http.read_timeout = 5
http.request_get(uri.path)
end
image = MiniMagick::Image.read(response.body)
aspect_ratio = image.width.to_f / image.height
(aspect_ratio - EXPECTED_ASPECT_RATIO).abs < ASPECT_RATIO_TOLERANCE
rescue => e
Rails.logger.error("Error checking aspect ratio for #{url}: #{e.message}")
false
end
private
def content_length(url)
uri = URI.parse(url)
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.open_timeout = 5
http.read_timeout = 5
http.request_head(uri.path)
end
response["Content-Length"].to_i
end
end
end
================================================
FILE: app/clients/youtube/transcript.rb
================================================
require "protobuf/message_type"
module YouTube
class Transcript
attr_reader :response
def get(video_id)
message = {one: "asr", two: "en"}
typedef = MessageType
two = get_base64_protobuf(message, typedef)
message = {one: video_id, two: two}
params = get_base64_protobuf(message, typedef)
url = "https://www.youtube.com/youtubei/v1/get_transcript"
headers = {"Content-Type" => "application/json"}
body = {
context: {
client: {
clientName: "WEB",
clientVersion: "2.20240313"
}
},
params: params
}
@response = HTTParty.post(url, headers: headers, body: body.to_json)
JSON.parse(@response.body)
end
def self.get(video_id)
new.get(video_id)
end
private
def encode_message(message, typedef)
encoded_message = typedef.new(message)
encoded_message.to_proto
end
def get_base64_protobuf(message, typedef)
encoded_data = encode_message(message, typedef)
Base64.encode64(encoded_data).delete("\n")
end
end
end
================================================
FILE: app/clients/youtube/video.rb
================================================
module YouTube
class Video < Client
def available?(video_id)
path = "/videos"
query = {
part: "status",
id: video_id
}
response = all_items(path, query: query)
response.present?
end
def get_statistics(video_id)
path = "/videos"
query = {
part: "statistics",
id: video_id
}
response = all_items(path, query: query)
return unless response.present?
response.each_with_object({}) do |item, hash|
hash[item["id"]] = {
view_count: item["statistics"]["viewCount"],
like_count: item["statistics"]["likeCount"]
}
end
end
def duration(video_id)
path = "/videos"
query = {
part: "contentDetails",
id: video_id
}
response = all_items(path, query: query)
duration_str = response&.first&.dig("contentDetails", "duration")
return nil unless duration_str
# Convert ISO 8601 duration (PT1H1M17S) to seconds
ActiveSupport::Duration.parse(duration_str).to_i
end
end
end
================================================
FILE: app/components/application_component.rb
================================================
# frozen_string_literal: true
class ApplicationComponent < ViewComponent::Base
extend Dry::Initializer
attr_accessor :attributes
option :display, default: proc { true }
def initialize(*, **options)
super
defined_option_keys = self.class.dry_initializer.options.map(&:source)
self.attributes = options.except(*defined_option_keys)
end
def render?
display
end
end
================================================
FILE: app/components/events/upcoming_event_banner_component.html.erb
================================================
<a href="<%= helpers.event_tickets_path(upcoming_event) %>" data-turbo-frame="_top" class="rounded-xl group border hover:opacity-95 transition-opacity duration-300 ease-in-out w-full flex flex-row px-6 py-6 gap-8 mt-4 overflow-hidden" style="background: <%= background_style %>; color: <%= text_color %>;">
<div class="aspect-video hidden md:block">
<%= image_tag helpers.image_path(upcoming_event.featured_image_path), alt: upcoming_event.name, class: "h-24 aspect-video rounded-xl group-hover:scale-105 transition ease-in-out duration-300" %>
</div>
<div class="flex flex-col w-full flex-1 self-center">
<span class="text-lg font-semibold sm:text-md">
<%= upcoming_event.name %> is coming up!
</span>
<span class="opacity-80 text-sm mt-1">
<%= upcoming_event.location %> • <%= upcoming_event.formatted_dates %>
</span>
<div class="mt-3">
<span class="btn btn-sm border-0" style="background: <%= text_color %>; color: <%= background_color %>;">
Get Tickets
</span>
</div>
</div>
</a>
================================================
FILE: app/components/events/upcoming_event_banner_component.rb
================================================
# frozen_string_literal: true
class Events::UpcomingEventBannerComponent < ApplicationComponent
option :event, optional: true
option :event_series, optional: true
def render?
upcoming_event.present? && should_show_banner?
end
def upcoming_event
@upcoming_event ||= if event.present?
event.next_upcoming_event_with_tickets
elsif event_series.present?
event_series.next_upcoming_event_with_tickets
end
end
def background_style
bg = upcoming_event.static_metadata.featured_background
return bg unless bg.start_with?("data:")
"url('#{bg}'); background-repeat: no-repeat; background-size: cover"
end
def background_color
bg = upcoming_event.static_metadata.featured_background
bg.start_with?("data:") ? "#000000" : bg
end
def text_color
upcoming_event.static_metadata.featured_color
end
private
def should_show_banner?
if event.present?
event.past?
else
true
end
end
end
================================================
FILE: app/components/hover_card/base_component.html.erb
================================================
<div class="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 w-80 opacity-0 invisible group-hover/card:opacity-100 group-hover/card:visible transition-opacity duration-150 z-50" data-hover-card-target="card" hidden>
<%= turbo_frame_tag frame_id, src: hover_card_url, loading: "lazy", target: "_top" %>
</div>
================================================
FILE: app/components/hover_card/base_component.rb
================================================
# frozen_string_literal: true
class HoverCard::BaseComponent < ViewComponent::Base
include Turbo::FramesHelper
attr_reader :record, :avatar_size
def initialize(record:, avatar_size: :sm)
@record = record
@avatar_size = avatar_size
end
def frame_id
dom_id(record, :hover_card)
end
def hover_card_url
raise NotImplementedError, "Subclasses must implement #hover_card_url"
end
def render?
record.present?
end
end
================================================
FILE: app/components/hover_card/event_component.rb
================================================
# frozen_string_literal: true
class HoverCard::EventComponent < HoverCard::BaseComponent
def hover_card_url
Router.hover_cards_event_path(slug: record.slug)
end
end
================================================
FILE: app/components/hover_card/user_component.rb
================================================
# frozen_string_literal: true
class HoverCard::UserComponent < HoverCard::BaseComponent
def hover_card_url
Router.hover_cards_user_path(slug: record.slug, avatar_size: avatar_size)
end
def render?
super && !record.suspicious?
end
end
================================================
FILE: app/components/tito/button_component.html.erb
================================================
<%= link_to label, helpers.event_tickets_path(event), class: classes %>
================================================
FILE: app/components/tito/button_component.rb
================================================
# frozen_string_literal: true
class Tito::ButtonComponent < ApplicationComponent
option :event
option :label, default: -> { "Tickets" }
def render?
display && event.tickets.available?
end
def classes
"btn btn-primary btn-sm w-full no-animation"
end
end
================================================
FILE: app/components/tito/widget_component.html.erb
================================================
<% content_for :head do %>
<script src="https://js.tito.io/v2" async></script>
<% end %>
<% if wrapper %>
<div class="mt-6 p-4 bg-gray-100 rounded-lg max-w-[700px]">
<h3 class="font-bold text-lg mb-2">Register</h3>
<tito-widget event="<%= event_slug %>"></tito-widget>
</div>
<% else %>
<tito-widget event="<%= event_slug %>"></tito-widget>
<% end %>
================================================
FILE: app/components/tito/widget_component.rb
================================================
# frozen_string_literal: true
class Tito::WidgetComponent < ApplicationComponent
option :event
option :wrapper, default: -> { true }
def render?
display && event.tickets.tito? && event.upcoming?
end
def event_slug
event.tickets.tito_event_slug
end
end
================================================
FILE: app/components/ui/avatar_component.html.erb
================================================
<% avatar_markup = capture do %>
<div class="avatar placeholder">
<div class="<%= avatar_classes %>">
<% if content %>
<%= content %>
<% else %>
<span class="<%= text_size %> <%= "hidden" if show_custom_avatar? %>"><%= initials %></span>
<% if show_custom_avatar? %>
<%= image_tag avatar_url_for_size, loading: "lazy", onerror: "this.previousElementSibling.classList.remove('hidden'); this.remove()" %>
<% end %>
<% end %>
</div>
</div>
<% end %>
<% if show_hover_card? %>
<div class="relative group/card" data-controller="hover-card" data-action="mouseenter->hover-card#reveal">
<% if show_link? %>
<%= link_to link_path do %><%= avatar_markup %><% end %>
<% else %>
<%= avatar_markup %>
<% end %>
<%= render HoverCard::UserComponent.new(record: avatarable, avatar_size: size) %>
</div>
<% elsif show_link? %>
<%= link_to link_path do %><%= avatar_markup %><% end %>
<% else %>
<%= avatar_markup %>
<% end %>
================================================
FILE: app/components/ui/avatar_component.rb
================================================
# frozen_string_literal: true
class Ui::AvatarComponent < ApplicationComponent
SIZE_MAPPING = {
xs: {
size_class: "w-6",
image_size: 32,
text_size: "text-xs"
},
sm: {
size_class: "w-8",
image_size: 48,
text_size: "text-xs"
},
md: {
size_class: "w-12",
image_size: 96,
text_size: "text-lg"
},
lg: {
size_class: "w-40",
image_size: 200,
text_size: "text-6xl"
}
}.freeze
KIND_MAPPING = {
primary: "bg-primary",
neutral: "bg-neutral"
}.freeze
param :avatarable
option :size, Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
option :size_class, Dry::Types["coercible.string"], default: proc { SIZE_MAPPING[size][:size_class] }
option :outline, type: Dry::Types["strict.bool"], default: proc { false }
option :kind, Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), default: proc { :primary }
option :hover_card, type: Dry::Types["strict.bool"], default: proc { false }
option :linked, type: Dry::Types["strict.bool"], default: proc { false }
def show_hover_card?
hover_card && avatarable.is_a?(User)
end
def show_link?
linked && avatarable.present?
end
def link_path
return nil unless avatarable.present?
if avatarable.is_a?(User)
helpers.profile_path(avatarable)
elsif avatarable.respond_to?(:slug)
helpers.speaker_path(avatarable)
end
end
def avatar_classes
[
size_class,
"rounded-full",
kind_class,
"text-neutral-content",
(outline ? "outline outline-2" : nil)
].compact.join(" ")
end
def initials
return "" unless avatarable&.name.present?
avatarable.name.split(" ").map(&:first).join
end
def has_custom_avatar?
avatarable&.respond_to?(:custom_avatar?) && avatarable.custom_avatar?
end
def avatar_url_for_size
avatarable.avatar_url(size: image_size)
end
private
def image_size
SIZE_MAPPING[size][:image_size]
end
def text_size
SIZE_MAPPING[size][:text_size]
end
def kind_class
KIND_MAPPING[kind]
end
def suspicious?
avatarable&.respond_to?(:suspicious?) && avatarable.suspicious?
end
def show_custom_avatar?
avatarable&.custom_avatar? && !suspicious?
end
end
================================================
FILE: app/components/ui/avatar_group_component.html.erb
================================================
<div class="avatar-group <%= overlap %> rtl:space-x-reverse">
<% visible_avatarables.each do |avatarable| %>
<%= render Ui::AvatarComponent.new(avatarable, size: size, hover_card: hover_card, linked: linked) %>
<% end %>
<% if remaining_count > 0 %>
<%= render Ui::AvatarComponent.new(nil, size: size) do %>
<span>+<%= remaining_count %></span>
<% end %>
<% end %>
</div>
================================================
FILE: app/components/ui/avatar_group_component.rb
================================================
# frozen_string_literal: true
class Ui::AvatarGroupComponent < ApplicationComponent
SIZE_MAPPING = {
sm: "w-8",
md: "w-12",
lg: "w-16"
}.freeze
param :avatarables, Dry::Types["strict.array"]
option :size, Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
option :max, Dry::Types["coercible.integer"], default: proc { 8 }
option :overlap, Dry::Types["coercible.string"], default: proc { "-space-x-3" }
option :hover_card, type: Dry::Types["strict.bool"], default: proc { false }
option :linked, type: Dry::Types["strict.bool"], default: proc { false }
private
def visible_avatarables
avatarables.first(max)
end
def remaining_count
[avatarables.size - max, 0].max
end
def size_class
SIZE_MAPPING[size]
end
end
================================================
FILE: app/components/ui/badge_component.rb
================================================
# frozen_string_literal: true
class Ui::BadgeComponent < ApplicationComponent
KIND_MAPPING = {
neutral: "badge-neutral",
primary: "badge-primary",
secondary: "badge-secondary",
accent: "badge-accent",
info: "badge-info",
success: "badge-success",
warning: "badge-warning",
error: "badge-error",
ghost: "badge-ghost"
}
SIZE_MAPPING = {
xs: "badge-xs",
sm: "badge-sm",
md: "badge-md",
lg: "badge-lg"
}
param :text, optional: true
option :kind, type: Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), default: proc { :primary }
option :size, type: Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
option :outline, type: Dry::Types["strict.bool"], default: proc { false }
def call
content_tag(:span, class: classes, **attributes.except(:class)) do
concat content
end
end
private
def classes
[component_classes, attributes[:class]].join(" ")
end
def component_classes
class_names(
"badge",
KIND_MAPPING[kind],
SIZE_MAPPING[size],
"badge-outline": outline
)
end
def content
text.presence || super
end
end
================================================
FILE: app/components/ui/button_component.rb
================================================
# frozen_string_literal: true
class Ui::ButtonComponent < ApplicationComponent
KIND_MAPPING = {
primary: "btn-primary",
secondary: "btn-secondary",
neutral: "btn-neutral btn-outline",
rounded: "btn btn-rounded",
pill: "btn btn-pill btn-sm",
circle: "btn btn-circle",
ghost: "btn-ghost",
link: "btn-link",
none: ""
}
SIZE_MAPPING = {
sm: "btn-sm",
md: "",
lg: "btn-lg"
}
param :text, default: proc {}
option :url, Dry::Types["coercible.string"], optional: true
option :method, Dry::Types["coercible.symbol"].enum(:get, :post, :patch, :put, :delete), optional: true
option :kind, type: Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), default: proc { :primary }
option :size, type: Dry::Types["coercible.symbol"].enum(*SIZE_MAPPING.keys), default: proc { :md }
option :type, type: Dry::Types["coercible.symbol"].enum(:button, :submit, :input), default: proc { :button }
option :disabled, type: Dry::Types["strict.bool"], default: proc { false }
option :outline, type: Dry::Types["strict.bool"], default: proc { false }
option :animation, type: Dry::Types["strict.bool"], default: proc { false }
def call
case button_kind
when :link
link_to(url, disabled: disabled, class: classes, **attributes.except(:class, :type)) { content }
when :button_to
button_to(url, disabled: disabled, method: method, class: classes, **attributes.except(:class, :type)) { content }
when :button
tag.button(type: type, disabled: disabled, class: classes, **attributes.except(:class, :type)) { content }
end
end
private
def classes
[component_classes, attributes[:class]].join(" ")
end
def component_classes
class_names(
"btn",
KIND_MAPPING[kind],
SIZE_MAPPING[size],
"btn-outline": outline,
"btn-disabled": disabled,
"no-animation": !animation # animation is disabled by default, I don't really like the effect when you enter the page
)
end
def content
text.presence || super
end
def button_kind
return :link if url.present? && default_method?
return :button_to if url.present? && !default_method?
:button
end
def default_method?
method.blank? || method == :get
end
end
================================================
FILE: app/components/ui/divider_component.rb
================================================
# frozen_string_literal: true
class Ui::DividerComponent < ApplicationComponent
KIND_MAPPING = {
horizontal: "divider-horizontal",
vertical: "divider-vertical"
}
param :text, optional: true
option :kind, type: Dry::Types["coercible.symbol"].enum(*KIND_MAPPING.keys), optional: true
def call
content_tag(:div, class: classes, **attributes.except(:class)) do
concat content
end
end
private
def classes
[component_classes, attributes[:class]].join(" ")
end
def component_classes
class_names(
"divider",
KIND_MAPPING[kind]
)
end
def content
text.presence || super
end
end
================================================
FILE: app/components/ui/dropdown_component.html.erb
================================================
<%= content_tag :details, class: classes, **attributes do %>
<summary class="flex after:hidden">
<% if toggle_open? && toggle_close? %>
<div class="swap" data-dropdown-target="swap">
<div class="swap-on"><%= toggle_open %></div>
<div class="swap-off"><%= toggle_close %></div>
</div>
<% else %>
<%= content %>
<% end %>
</summary>
<%= content_tag :ul, class: content_classes do %>
<% menu_items.each do |menu_item| %>
<%= menu_item %>
<% end %>
<% end %>
<% end %>
================================================
FILE: app/components/ui/dropdown_component.rb
================================================
# frozen_string_literal: true
class Ui::DropdownComponent < ApplicationComponent
renders_many :menu_items, types: {
divider: lambda { render Ui::DividerComponent.new(class: "my-2") },
link_to: lambda { |*args, **attributes, &block|
content_tag :li do
attributes[:class] = class_names("!whitespace-nowrap", attributes[:class])
concat link_to(*args, **attributes, &block)
end
},
button_to: lambda { |*args, **attributes|
content_tag :li do
attributes[:class] = class_names("!whitespace-nowrap", attributes[:class])
concat button_to(*args, **attributes)
end
}
}
renders_one :toggle_open
renders_one :toggle_close
OPEN_FROM_MAPPING = {
left: "dropdown-left",
right: "dropdown-right",
top: "dropdown-top",
bottom: "dropdown-bottom"
}
ALIGN_MAPPING = {
end: "dropdown-end"
}
option :open, type: Dry::Types["strict.bool"], default: proc { false }
option :hover, type: Dry::Types["strict.bool"], default: proc { false }
option :align, type: Dry::Types["coercible.symbol"].enum(*ALIGN_MAPPING.keys), optional: true
option :open_from, type: Dry::Types["coercible.symbol"].enum(*OPEN_FROM_MAPPING.keys), optional: true
private
def before_render
attributes[:data] = {controller: "dropdown"}
end
def classes
[component_classes, attributes.delete(:class)].compact_blank.join(" ")
end
def component_classes
class_names(
"dropdown",
OPEN_FROM_MAPPING[open_from],
ALIGN_MAPPING[align],
"dropdown-hover": hover,
"dropdown-open": open
)
end
def content_classes
class_names("dropdown-content menu menu-smp-2 mt-4 w-max z-30 rounded-lg shadow-2xl bg-white text-neutral", attributes.delete(:content_classes))
end
end
================================================
FILE: app/components/ui/modal_component.html.erb
================================================
<%= content_tag :dialog, role: :dialog, aria: {modal: true}, class: classes, **attributes do %>
<div class="modal-box <%= size_class %>" data-modal-target="modalBox">
<% if close_button %>
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
data-action="click->modal#close">
<%= helpers.fa :xmark %>
</button>
<% end %>
<%= content %>
</div>
<% end %>
================================================
FILE: app/components/ui/modal_component.rb
================================================
# frozen_string_literal: true
class Ui::ModalComponent < ApplicationComponent
POSITION_MAPPING = {
top: "modal-top",
bottom: "modal-bottom",
middle: "modal-middle",
responsive: "modal-bottom sm:modal-middle"
}
SIZE_MAPPING = {
md: "",
lg: "!max-w-[800px]",
xl: "!max-w-[1200px]",
full: "!max-w-[95vw] !w-[95vw] !max-h-[90vh]"
}
option :open, type: Dry::Types["strict.bool"], default: proc { false }
option :
Showing preview only (205K chars total). Download the full file or copy to clipboard to get everything.
gitextract_xohe3ehl/
├── .annotaterb.yml
├── .cursor/
│ ├── commands/
│ │ └── plan_commands.md
│ └── rules/
│ ├── cursorrules.mdc
│ └── viewcomponents.mdc
├── .devcontainer/
│ ├── Dockerfile
│ ├── devcontainer.json
│ └── docker-compose.yml
├── .dockerignore
├── .erb_lint.yml
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│ ├── pull_request_template.md
│ ├── skills/
│ │ └── event-data/
│ │ └── SKILL.md
│ └── workflows/
│ ├── ci.yml
│ ├── copilot-setup-steps.yml
│ ├── deploy-staging.yml
│ ├── migrate-production.yml
│ ├── migrate-staging.yml
│ ├── release-lock-production.yml
│ ├── release-lock-staging.yml
│ ├── seed-production.yml
│ └── seed-staging.yml
├── .gitignore
├── .herb.yml
├── .kamal/
│ ├── hooks/
│ │ ├── docker-setup.sample
│ │ ├── post-deploy.sample
│ │ ├── post-proxy-reboot.sample
│ │ ├── pre-build.sample
│ │ ├── pre-connect.sample
│ │ ├── pre-deploy.sample
│ │ └── pre-proxy-reboot.sample
│ ├── secrets
│ └── secrets.staging
├── .mcp.json
├── .node-version
├── .prettierignore
├── .ruby-version
├── .standard.yml
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CLAUDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── Guardfile
├── Procfile.dev
├── README.md
├── Rakefile
├── app/
│ ├── assets/
│ │ ├── builds/
│ │ │ └── .keep
│ │ └── stylesheets/
│ │ ├── application.css
│ │ ├── bridge/
│ │ │ └── components.css
│ │ └── components/
│ │ ├── button.css
│ │ ├── diff.css
│ │ ├── dropdown.css
│ │ ├── event.css
│ │ ├── form.css
│ │ ├── hotwire-combobox.css
│ │ ├── iframe.css
│ │ ├── markdown.css
│ │ ├── modal.css
│ │ ├── nav.css
│ │ ├── pagination.css
│ │ ├── skeleton.css
│ │ ├── spotlight.css
│ │ ├── tabs.css
│ │ ├── transition.css
│ │ ├── typography.css
│ │ └── video.css
│ ├── avo/
│ │ ├── actions/
│ │ │ ├── approve_topic.rb
│ │ │ ├── assign_canonical_event.rb
│ │ │ ├── assign_canonical_speaker.rb
│ │ │ ├── assign_canonical_topic.rb
│ │ │ ├── assign_canonical_user.rb
│ │ │ ├── clear_user.rb
│ │ │ ├── enhance_transcript.rb
│ │ │ ├── extract_topics.rb
│ │ │ ├── fetch_duration.rb
│ │ │ ├── find_topic_talks.rb
│ │ │ ├── geocode_record.rb
│ │ │ ├── reject_topic.rb
│ │ │ ├── summarize.rb
│ │ │ ├── talk_index.rb
│ │ │ ├── talk_ingest.rb
│ │ │ ├── transcript.rb
│ │ │ ├── update_from_yml.rb
│ │ │ └── user_fetch_github.rb
│ │ ├── cards/
│ │ │ ├── duplicates_summary.rb
│ │ │ ├── reversed_name_duplicates_list.rb
│ │ │ ├── reversed_name_duplicates_metric.rb
│ │ │ ├── same_name_duplicates_list.rb
│ │ │ ├── same_name_duplicates_metric.rb
│ │ │ ├── suspicious_signals_breakdown.rb
│ │ │ ├── suspicious_summary.rb
│ │ │ ├── suspicious_users_list.rb
│ │ │ ├── suspicious_users_metric.rb
│ │ │ ├── unavailable_videos_by_event.rb
│ │ │ ├── unavailable_videos_list.rb
│ │ │ ├── unavailable_videos_metric.rb
│ │ │ └── unavailable_videos_summary.rb
│ │ ├── dashboards/
│ │ │ ├── default.rb
│ │ │ ├── duplicates.rb
│ │ │ ├── suspicious.rb
│ │ │ └── unavailable_videos.rb
│ │ ├── filters/
│ │ │ ├── aliasable_type.rb
│ │ │ ├── attended_as.rb
│ │ │ ├── bio.rb
│ │ │ ├── bio_presence.rb
│ │ │ ├── canonical.rb
│ │ │ ├── enhanced_transcript.rb
│ │ │ ├── geocoded_presence.rb
│ │ │ ├── github.rb
│ │ │ ├── github_handle.rb
│ │ │ ├── github_handle_presence.rb
│ │ │ ├── has_duplicate.rb
│ │ │ ├── involvement_role.rb
│ │ │ ├── language.rb
│ │ │ ├── location_presence.rb
│ │ │ ├── name.rb
│ │ │ ├── provider.rb
│ │ │ ├── published.rb
│ │ │ ├── raw_transcript.rb
│ │ │ ├── slug.rb
│ │ │ ├── summary.rb
│ │ │ ├── suspicious.rb
│ │ │ ├── talk_event.rb
│ │ │ ├── title.rb
│ │ │ ├── topic_talks.rb
│ │ │ ├── topics.rb
│ │ │ ├── video_availability.rb
│ │ │ ├── video_provider.rb
│ │ │ └── without_talks.rb
│ │ └── resources/
│ │ ├── alias.rb
│ │ ├── city.rb
│ │ ├── connected_account.rb
│ │ ├── contributor.rb
│ │ ├── event.rb
│ │ ├── event_involvement.rb
│ │ ├── event_participation.rb
│ │ ├── event_series.rb
│ │ ├── favorite_user.rb
│ │ ├── geocode_result.rb
│ │ ├── organization.rb
│ │ ├── session.rb
│ │ ├── sponsor.rb
│ │ ├── suggestion.rb
│ │ ├── talk.rb
│ │ ├── talk_topic.rb
│ │ ├── talk_transcript.rb
│ │ ├── topic.rb
│ │ ├── topic_gem.rb
│ │ ├── user.rb
│ │ ├── user_talk.rb
│ │ ├── watch_list.rb
│ │ └── watch_list_talk.rb
│ ├── channels/
│ │ └── application_cable/
│ │ ├── channel.rb
│ │ └── connection.rb
│ ├── clients/
│ │ ├── application_client.rb
│ │ ├── blue_sky.rb
│ │ ├── github/
│ │ │ ├── client.rb
│ │ │ ├── contributors_client.rb
│ │ │ └── user_client.rb
│ │ ├── ruby_conferences/
│ │ │ └── client.rb
│ │ ├── speakerdeck/
│ │ │ └── client.rb
│ │ └── youtube/
│ │ ├── channels.rb
│ │ ├── client.rb
│ │ ├── playlist_items.rb
│ │ ├── playlists.rb
│ │ ├── thumbnail.rb
│ │ ├── transcript.rb
│ │ └── video.rb
│ ├── components/
│ │ ├── application_component.rb
│ │ ├── events/
│ │ │ ├── upcoming_event_banner_component.html.erb
│ │ │ └── upcoming_event_banner_component.rb
│ │ ├── hover_card/
│ │ │ ├── base_component.html.erb
│ │ │ ├── base_component.rb
│ │ │ ├── event_component.rb
│ │ │ └── user_component.rb
│ │ ├── tito/
│ │ │ ├── button_component.html.erb
│ │ │ ├── button_component.rb
│ │ │ ├── widget_component.html.erb
│ │ │ └── widget_component.rb
│ │ └── ui/
│ │ ├── avatar_component.html.erb
│ │ ├── avatar_component.rb
│ │ ├── avatar_group_component.html.erb
│ │ ├── avatar_group_component.rb
│ │ ├── badge_component.rb
│ │ ├── button_component.rb
│ │ ├── divider_component.rb
│ │ ├── dropdown_component.html.erb
│ │ ├── dropdown_component.rb
│ │ ├── modal_component.html.erb
│ │ ├── modal_component.rb
│ │ ├── stamp_component.html.erb
│ │ └── stamp_component.rb
│ ├── controllers/
│ │ ├── admin/
│ │ │ └── suggestions_controller.rb
│ │ ├── analytics/
│ │ │ └── dashboards_controller.rb
│ │ ├── announcements_controller.rb
│ │ ├── api/
│ │ │ └── v1/
│ │ │ └── embed/
│ │ │ ├── base_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── profiles_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ ├── stickers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── topics_controller.rb
│ │ ├── application_controller.rb
│ │ ├── avo/
│ │ │ ├── aliases_controller.rb
│ │ │ ├── cities_controller.rb
│ │ │ ├── connected_accounts_controller.rb
│ │ │ ├── contributors_controller.rb
│ │ │ ├── event_involvements_controller.rb
│ │ │ ├── event_participations_controller.rb
│ │ │ ├── event_series_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── favorite_users_controller.rb
│ │ │ ├── geocode_results_controller.rb
│ │ │ ├── organizations_controller.rb
│ │ │ ├── sessions_controller.rb
│ │ │ ├── speaker_talks_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── sponsors_controller.rb
│ │ │ ├── suggestions_controller.rb
│ │ │ ├── talk_topics_controller.rb
│ │ │ ├── talk_transcripts_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ ├── topic_gems_controller.rb
│ │ │ ├── topics_controller.rb
│ │ │ ├── user_talks_controller.rb
│ │ │ ├── users_controller.rb
│ │ │ ├── watch_list_talks_controller.rb
│ │ │ └── watch_lists_controller.rb
│ │ ├── browse_controller.rb
│ │ ├── cfp_controller.rb
│ │ ├── cities_controller.rb
│ │ ├── concerns/
│ │ │ ├── analytics.rb
│ │ │ ├── authenticable.rb
│ │ │ ├── event_data.rb
│ │ │ ├── event_map_markers.rb
│ │ │ ├── favorite_users.rb
│ │ │ ├── geo_map_layers.rb
│ │ │ ├── location_events.rb
│ │ │ ├── metadata.rb
│ │ │ ├── profile_data.rb
│ │ │ ├── remote_modal.rb
│ │ │ ├── spotlight_search.rb
│ │ │ ├── turbo/
│ │ │ │ └── force_response.rb
│ │ │ └── watched_talks.rb
│ │ ├── continents/
│ │ │ ├── base_controller.rb
│ │ │ └── countries_controller.rb
│ │ ├── continents_controller.rb
│ │ ├── contributions_controller.rb
│ │ ├── coordinates_controller.rb
│ │ ├── countries/
│ │ │ ├── base_controller.rb
│ │ │ └── cities_controller.rb
│ │ ├── countries_controller.rb
│ │ ├── event_participations_controller.rb
│ │ ├── events/
│ │ │ ├── archive_controller.rb
│ │ │ ├── attendances_controller.rb
│ │ │ ├── cfp_controller.rb
│ │ │ ├── collectibles_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── involvements_controller.rb
│ │ │ ├── meetups_controller.rb
│ │ │ ├── participants_controller.rb
│ │ │ ├── past_controller.rb
│ │ │ ├── related_talks_controller.rb
│ │ │ ├── schedules_controller.rb
│ │ │ ├── series_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── sponsors_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ ├── tickets_controller.rb
│ │ │ ├── todos_controller.rb
│ │ │ ├── upcoming_controller.rb
│ │ │ ├── venues_controller.rb
│ │ │ ├── videos_controller.rb
│ │ │ └── years_controller.rb
│ │ ├── events_controller.rb
│ │ ├── favorite_users_controller.rb
│ │ ├── gems_controller.rb
│ │ ├── hotwire/
│ │ │ └── native/
│ │ │ └── v1/
│ │ │ ├── android/
│ │ │ │ └── path_configurations_controller.rb
│ │ │ └── ios/
│ │ │ └── path_configurations_controller.rb
│ │ ├── hover_cards/
│ │ │ ├── events_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── leaderboard_controller.rb
│ │ ├── locations/
│ │ │ ├── base_controller.rb
│ │ │ ├── map_controller.rb
│ │ │ ├── meetups_controller.rb
│ │ │ ├── past_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── online_controller.rb
│ │ ├── organizations/
│ │ │ ├── logos_controller.rb
│ │ │ └── wrapped_controller.rb
│ │ ├── organizations_controller.rb
│ │ ├── page_controller.rb
│ │ ├── profiles/
│ │ │ ├── aliases_controller.rb
│ │ │ ├── claims_controller.rb
│ │ │ ├── connect_controller.rb
│ │ │ ├── enhance_controller.rb
│ │ │ ├── events_controller.rb
│ │ │ ├── involvements_controller.rb
│ │ │ ├── map_controller.rb
│ │ │ ├── mutual_events_controller.rb
│ │ │ ├── notes_controller.rb
│ │ │ ├── stamps_controller.rb
│ │ │ ├── stickers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── wrapped_controller.rb
│ │ ├── profiles_controller.rb
│ │ ├── recommendations_controller.rb
│ │ ├── sessions/
│ │ │ └── omniauth_controller.rb
│ │ ├── sessions_controller.rb
│ │ ├── settings_controller.rb
│ │ ├── sitemaps_controller.rb
│ │ ├── speakers_controller.rb
│ │ ├── sponsors/
│ │ │ └── missing_controller.rb
│ │ ├── spotlight/
│ │ │ ├── events_controller.rb
│ │ │ ├── kinds_controller.rb
│ │ │ ├── languages_controller.rb
│ │ │ ├── locations_controller.rb
│ │ │ ├── organizations_controller.rb
│ │ │ ├── series_controller.rb
│ │ │ ├── speakers_controller.rb
│ │ │ ├── talks_controller.rb
│ │ │ └── topics_controller.rb
│ │ ├── stamps_controller.rb
│ │ ├── states/
│ │ │ ├── base_controller.rb
│ │ │ └── cities_controller.rb
│ │ ├── states_controller.rb
│ │ ├── talks/
│ │ │ ├── recommendations_controller.rb
│ │ │ ├── slides_controller.rb
│ │ │ └── watched_talks_controller.rb
│ │ ├── talks_controller.rb
│ │ ├── templates_controller.rb
│ │ ├── todos_controller.rb
│ │ ├── topics_controller.rb
│ │ ├── watch_list_talks_controller.rb
│ │ ├── watch_lists_controller.rb
│ │ ├── watched_talks_controller.rb
│ │ └── wrapped_controller.rb
│ ├── helpers/
│ │ ├── application_helper.rb
│ │ ├── events/
│ │ │ └── talks_helper.rb
│ │ ├── events_helper.rb
│ │ ├── icon_helper.rb
│ │ ├── language_helper.rb
│ │ ├── location_helper.rb
│ │ ├── markdown_helper.rb
│ │ ├── page_helper.rb
│ │ ├── speakers/
│ │ │ └── enhance_helper.rb
│ │ ├── speakers_helper.rb
│ │ ├── spotlight_helper.rb
│ │ ├── suggestions_helper.rb
│ │ ├── talks_helper.rb
│ │ └── view_component_helper.rb
│ ├── javascript/
│ │ ├── controllers/
│ │ │ ├── application.js
│ │ │ ├── auto-click_controller.js
│ │ │ ├── auto_submit_controller.js
│ │ │ ├── bridge/
│ │ │ │ └── button_controller.js
│ │ │ ├── collapsible_controller.js
│ │ │ ├── content_row_controller.js
│ │ │ ├── copy_to_clipboard_controller.js
│ │ │ ├── dropdown_controller.js
│ │ │ ├── event_controller.js
│ │ │ ├── event_list_controller.js
│ │ │ ├── events_filter_controller.js
│ │ │ ├── events_view_switcher_controller.js
│ │ │ ├── geolocation_controller.js
│ │ │ ├── hover_card_controller.js
│ │ │ ├── index.js
│ │ │ ├── lazy_loading_controller.js
│ │ │ ├── map_controller.js
│ │ │ ├── modal_controller.js
│ │ │ ├── preserve_scroll_controller.js
│ │ │ ├── pronouns_select_controller.js
│ │ │ ├── scroll_controller.js
│ │ │ ├── scroll_into_view_controller.js
│ │ │ ├── splide_controller.js
│ │ │ ├── spotlight_search_controller.js
│ │ │ ├── tabs_controller.js
│ │ │ ├── talks_filter_controller.js
│ │ │ ├── talks_filter_pill_controller.js
│ │ │ ├── talks_navigation_controller.js
│ │ │ ├── toggable_controller.js
│ │ │ ├── tooltip_controller.js
│ │ │ ├── top_banner_controller.js
│ │ │ ├── transition_controller.js
│ │ │ ├── video_player_controller.js
│ │ │ ├── watched_talk_form_controller.js
│ │ │ └── wrapped_stories_controller.js
│ │ ├── entrypoints/
│ │ │ ├── application.css
│ │ │ ├── application.js
│ │ │ └── chartkick.js
│ │ ├── helpers/
│ │ │ └── timing_helpers.js
│ │ └── support/
│ │ └── appsignal.js
│ ├── jobs/
│ │ ├── application_job.rb
│ │ ├── generate_wrapped_screenshot_job.rb
│ │ ├── geocode_record_job.rb
│ │ ├── recurring/
│ │ │ ├── fetch_contributors_job.rb
│ │ │ ├── mark_suspicious_users_job.rb
│ │ │ ├── rollup_job.rb
│ │ │ ├── youtube_thumbnail_validation_job.rb
│ │ │ ├── youtube_video_availability_job.rb
│ │ │ ├── youtube_video_duration_job.rb
│ │ │ └── youtube_video_statistics_job.rb
│ │ └── typesense_index_job.rb
│ ├── lib/
│ │ ├── command.rb
│ │ ├── download_sponsors.rb
│ │ ├── duration.rb
│ │ └── router.rb
│ ├── mailers/
│ │ └── application_mailer.rb
│ ├── models/
│ │ ├── active_record/
│ │ │ └── sqlite/
│ │ │ └── index.rb
│ │ ├── ahoy/
│ │ │ ├── event.rb
│ │ │ └── visit.rb
│ │ ├── alias.rb
│ │ ├── announcement.rb
│ │ ├── application_record.rb
│ │ ├── cfp.rb
│ │ ├── city.rb
│ │ ├── concerns/
│ │ │ ├── appsignal/
│ │ │ │ └── admin_namespace.rb
│ │ │ ├── geocodeable.rb
│ │ │ ├── locatable.rb
│ │ │ ├── rollupable.rb
│ │ │ ├── sluggable.rb
│ │ │ ├── suggestable.rb
│ │ │ ├── todoable.rb
│ │ │ ├── url_normalizable.rb
│ │ │ ├── watchable.rb
│ │ │ └── yaml_file.rb
│ │ ├── connected_account.rb
│ │ ├── continent.rb
│ │ ├── contributor.rb
│ │ ├── coordinate_location.rb
│ │ ├── country.rb
│ │ ├── cue.rb
│ │ ├── current.rb
│ │ ├── email_verification_token.rb
│ │ ├── event/
│ │ │ ├── assets.rb
│ │ │ ├── cfp_file.rb
│ │ │ ├── involvements_file.rb
│ │ │ ├── schedule.rb
│ │ │ ├── sponsors_file.rb
│ │ │ ├── static_metadata.rb
│ │ │ ├── tickets.rb
│ │ │ ├── transcripts_file.rb
│ │ │ ├── typesense_searchable.rb
│ │ │ ├── venue.rb
│ │ │ └── videos_file.rb
│ │ ├── event.rb
│ │ ├── event_involvement.rb
│ │ ├── event_participation.rb
│ │ ├── event_series/
│ │ │ ├── static_metadata.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── event_series.rb
│ │ ├── favorite_user.rb
│ │ ├── geocode_result.rb
│ │ ├── language.rb
│ │ ├── llm/
│ │ │ ├── client.rb
│ │ │ └── request.rb
│ │ ├── location.rb
│ │ ├── online_location.rb
│ │ ├── organization/
│ │ │ ├── typesense_searchable.rb
│ │ │ └── wrapped_screenshot_generator.rb
│ │ ├── organization.rb
│ │ ├── password_reset_token.rb
│ │ ├── prompts/
│ │ │ ├── base.rb
│ │ │ ├── talk/
│ │ │ │ ├── enhance_transcript.rb
│ │ │ │ ├── summary.rb
│ │ │ │ └── topics.rb
│ │ │ └── topic/
│ │ │ ├── match_talks.rb
│ │ │ └── suggest_gems.rb
│ │ ├── rollup.rb
│ │ ├── search/
│ │ │ ├── backend/
│ │ │ │ ├── sqlite_fts/
│ │ │ │ │ └── indexer.rb
│ │ │ │ ├── sqlite_fts.rb
│ │ │ │ ├── typesense/
│ │ │ │ │ ├── circuit_breaker.rb
│ │ │ │ │ ├── indexer.rb
│ │ │ │ │ ├── kind_indexer.rb
│ │ │ │ │ ├── language_indexer.rb
│ │ │ │ │ └── location_indexer.rb
│ │ │ │ └── typesense.rb
│ │ │ └── backend.rb
│ │ ├── session.rb
│ │ ├── speaker.rb
│ │ ├── sponsor.rb
│ │ ├── stamp.rb
│ │ ├── state.rb
│ │ ├── static/
│ │ │ ├── backends/
│ │ │ │ ├── array_backend.rb
│ │ │ │ ├── file_backend.rb
│ │ │ │ └── multi_file_backend.rb
│ │ │ ├── city.rb
│ │ │ ├── event.rb
│ │ │ ├── event_series.rb
│ │ │ ├── speaker.rb
│ │ │ ├── sponsor.rb
│ │ │ ├── topic.rb
│ │ │ ├── transcript.rb
│ │ │ └── video.rb
│ │ ├── sticker.rb
│ │ ├── suggestion.rb
│ │ ├── talk/
│ │ │ ├── agents.rb
│ │ │ ├── downloader.rb
│ │ │ ├── index.rb
│ │ │ ├── similar_recommender.rb
│ │ │ ├── sqlite_fts_searchable.rb
│ │ │ ├── thumbnails.rb
│ │ │ ├── transcript.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── talk.rb
│ │ ├── talk_topic.rb
│ │ ├── template.rb
│ │ ├── todo.rb
│ │ ├── topic/
│ │ │ ├── agents.rb
│ │ │ ├── gem_info.rb
│ │ │ └── typesense_searchable.rb
│ │ ├── topic.rb
│ │ ├── topic_gem.rb
│ │ ├── transcript.rb
│ │ ├── uk_nation.rb
│ │ ├── user/
│ │ │ ├── duplicate_detector.rb
│ │ │ ├── index.rb
│ │ │ ├── profiles.rb
│ │ │ ├── speakerdeck_feed.rb
│ │ │ ├── sqlite_fts_searchable.rb
│ │ │ ├── suspicion_detector.rb
│ │ │ ├── talk_recommender.rb
│ │ │ ├── typesense_searchable.rb
│ │ │ ├── watched_talk_seeder.rb
│ │ │ ├── wrapped_image_generator.rb
│ │ │ └── wrapped_screenshot_generator.rb
│ │ ├── user.rb
│ │ ├── user_talk.rb
│ │ ├── watch_list.rb
│ │ ├── watch_list_talk.rb
│ │ ├── watched_talk.rb
│ │ └── youtube/
│ │ ├── null_parser.rb
│ │ ├── video_metadata.rb
│ │ ├── video_metadata_baltic_ruby_2024.rb
│ │ ├── video_metadata_kaigi_on_rails.rb
│ │ └── video_metadata_rails_world.rb
│ ├── schemas/
│ │ ├── address_schema.rb
│ │ ├── cfp_schema.rb
│ │ ├── coordinates_schema.rb
│ │ ├── event_schema.rb
│ │ ├── featured_city_schema.rb
│ │ ├── hotel_schema.rb
│ │ ├── involvement_schema.rb
│ │ ├── location_schema.rb
│ │ ├── maps_schema.rb
│ │ ├── schedule_schema.rb
│ │ ├── series_schema.rb
│ │ ├── speaker_schema.rb
│ │ ├── sponsors_schema.rb
│ │ ├── transcript_schema.rb
│ │ ├── venue_schema.rb
│ │ └── video_schema.rb
│ ├── serializers/
│ │ └── transcript_serializer.rb
│ ├── tools/
│ │ ├── cfp_create_tool.rb
│ │ ├── cfp_info_tool.rb
│ │ ├── cfp_update_tool.rb
│ │ ├── event_create_tool.rb
│ │ ├── event_lookup_tool.rb
│ │ ├── event_series_create_tool.rb
│ │ ├── event_series_events_tool.rb
│ │ ├── event_series_lookup_tool.rb
│ │ ├── event_talks_tool.rb
│ │ ├── geocode_tool.rb
│ │ ├── github_profile_tool.rb
│ │ ├── speaker_lookup_tool.rb
│ │ ├── speaker_talks_tool.rb
│ │ ├── speakerdeck_deck_tool.rb
│ │ ├── speakerdeck_user_decks_tool.rb
│ │ ├── venue_create_tool.rb
│ │ ├── vimeo_video_tool.rb
│ │ ├── youtube_channel_videos_tool.rb
│ │ ├── youtube_playlist_items_tool.rb
│ │ ├── youtube_playlist_tool.rb
│ │ ├── youtube_video_tool.rb
│ │ └── youtube_videos_tool.rb
│ └── views/
│ ├── admin/
│ │ └── suggestions/
│ │ ├── _suggestion.html.erb
│ │ └── index.html.erb
│ ├── analytics/
│ │ └── dashboards/
│ │ ├── daily_page_views.html.erb
│ │ ├── daily_visits.html.erb
│ │ ├── monthly_page_views.html.erb
│ │ ├── monthly_visits.html.erb
│ │ ├── show.html.erb
│ │ ├── top_landing_pages.html.erb
│ │ ├── top_referrers.html.erb
│ │ ├── top_searches.html.erb
│ │ ├── yearly_conferences.html.erb
│ │ └── yearly_talks.html.erb
│ ├── announcements/
│ │ ├── feed.rss.builder
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ ├── avo/
│ │ └── cards/
│ │ ├── _duplicates_summary.html.erb
│ │ ├── _reversed_name_duplicates_list.html.erb
│ │ ├── _same_name_duplicates_list.html.erb
│ │ ├── _suspicious_signals_breakdown.html.erb
│ │ ├── _suspicious_summary.html.erb
│ │ ├── _suspicious_users_list.html.erb
│ │ ├── _unavailable_videos_by_event.html.erb
│ │ ├── _unavailable_videos_list.html.erb
│ │ └── _unavailable_videos_summary.html.erb
│ ├── browse/
│ │ ├── _content_row.html.erb
│ │ ├── _content_row_skeleton.html.erb
│ │ ├── _featured_events.html.erb
│ │ ├── _kind_row.html.erb
│ │ ├── _topic_row.html.erb
│ │ ├── index.html.erb
│ │ └── sections/
│ │ ├── _beginner_friendly.html.erb
│ │ ├── _continue_watching.html.erb
│ │ ├── _deep_dives.html.erb
│ │ ├── _event_rows.html.erb
│ │ ├── _events_attended.html.erb
│ │ ├── _evergreen.html.erb
│ │ ├── _favorite_rubyists.html.erb
│ │ ├── _favorite_speakers.html.erb
│ │ ├── _featured_events.html.erb
│ │ ├── _for_you.html.erb
│ │ ├── _from_bookmarks.html.erb
│ │ ├── _hidden_gems.html.erb
│ │ ├── _inspiring.html.erb
│ │ ├── _language_rows.html.erb
│ │ ├── _mind_blowing.html.erb
│ │ ├── _most_bookmarked.html.erb
│ │ ├── _most_liked.html.erb
│ │ ├── _newest_talks.html.erb
│ │ ├── _popular.html.erb
│ │ ├── _popular_topics.html.erb
│ │ ├── _popular_youtube.html.erb
│ │ ├── _quick_watches.html.erb
│ │ ├── _recently_published.html.erb
│ │ ├── _recommended_community.html.erb
│ │ ├── _talk_kinds.html.erb
│ │ ├── _topic_rows.html.erb
│ │ ├── _trending.html.erb
│ │ └── _unwatched_attended.html.erb
│ ├── cfp/
│ │ ├── _event_list.html.erb
│ │ └── index.html.erb
│ ├── cities/
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── continents/
│ │ ├── countries/
│ │ │ └── index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── contributions/
│ │ ├── _events_without_dates.html.erb
│ │ ├── _events_without_location.html.erb
│ │ ├── _events_without_videos.html.erb
│ │ ├── _introduction.html.erb
│ │ ├── _missing_videos_cue.html.erb
│ │ ├── _speakers_without_github.html.erb
│ │ ├── _talks_dates_out_of_bounds.html.erb
│ │ ├── _talks_without_slides.html.erb
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ ├── coordinates/
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── countries/
│ │ ├── cities/
│ │ │ └── index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ ├── state.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── event_participations/
│ │ ├── create.turbo_stream.erb
│ │ └── destroy.turbo_stream.erb
│ ├── events/
│ │ ├── _card.html.erb
│ │ ├── _event.html.erb
│ │ ├── _event_list.html.erb
│ │ ├── _featured.html.erb
│ │ ├── _featured_card.html.erb
│ │ ├── _featured_card_list.html.erb
│ │ ├── _featured_home.html.erb
│ │ ├── _form.html.erb
│ │ ├── _header.html.erb
│ │ ├── _my_attendance.html.erb
│ │ ├── _my_attendance_day.html.erb
│ │ ├── _my_attendance_talk.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _participation_button.html.erb
│ │ ├── _plain_list.html.erb
│ │ ├── archive/
│ │ │ └── index.html.erb
│ │ ├── attendances/
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── cfp/
│ │ │ └── index.html.erb
│ │ ├── collectibles/
│ │ │ └── index.html.erb
│ │ ├── edit.html.erb
│ │ ├── events/
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── index.html.erb
│ │ ├── involvements/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── new.html.erb
│ │ ├── participants/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── related_talks/
│ │ │ ├── _list.html.erb
│ │ │ └── index.html.erb
│ │ ├── schedules/
│ │ │ ├── _day.html.erb
│ │ │ ├── _schedule.html.erb
│ │ │ ├── index.html.erb
│ │ │ ├── missing_schedule.html.erb
│ │ │ └── show.html.erb
│ │ ├── series/
│ │ │ ├── _card.html.erb
│ │ │ ├── index.html.erb
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ ├── speakers/
│ │ │ └── index.html.erb
│ │ ├── sponsors/
│ │ │ └── index.html.erb
│ │ ├── talks/
│ │ │ └── index.html.erb
│ │ ├── tickets/
│ │ │ └── show.html.erb
│ │ ├── todos/
│ │ │ └── index.html.erb
│ │ ├── venues/
│ │ │ ├── missing_venue.html.erb
│ │ │ └── show.html.erb
│ │ ├── videos/
│ │ │ └── index.html.erb
│ │ └── years/
│ │ ├── _desktop_calendar.html.erb
│ │ ├── _event_hover_card.html.erb
│ │ ├── _header.html.erb
│ │ ├── _mobile_calendar.html.erb
│ │ ├── _yearly_events.html.erb
│ │ └── index.html.erb
│ ├── favorite_users/
│ │ ├── _favorite_user.html.erb
│ │ ├── _form.html.erb
│ │ ├── _list.html.erb
│ │ ├── _no_favorites.html.erb
│ │ └── index.html.erb
│ ├── gems/
│ │ ├── _gem_card.html.erb
│ │ ├── index.html.erb
│ │ ├── index.turbo_stream.erb
│ │ ├── show.html.erb
│ │ ├── talks.html.erb
│ │ └── talks.turbo_stream.erb
│ ├── home/
│ │ └── index.html.erb
│ ├── hover_cards/
│ │ ├── events/
│ │ │ ├── _content.html.erb
│ │ │ └── show.html.erb
│ │ └── users/
│ │ ├── _content.html.erb
│ │ └── show.html.erb
│ ├── identity/
│ │ ├── emails/
│ │ │ └── edit.html.erb
│ │ └── password_resets/
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── layouts/
│ │ ├── application.html.erb
│ │ ├── application.turbo_stream.erb
│ │ ├── mailer.html.erb
│ │ ├── mailer.text.erb
│ │ ├── modal.html.erb
│ │ ├── turbo_rails/
│ │ │ └── frame.turbo_stream.erb
│ │ └── wrapped.html.erb
│ ├── leaderboard/
│ │ ├── _speaker.html.erb
│ │ └── index.html.erb
│ ├── online/
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ └── show.html.erb
│ ├── organizations/
│ │ ├── _card.html.erb
│ │ ├── _compact_card.html.erb
│ │ ├── _header.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _organization.html.erb
│ │ ├── index.html.erb
│ │ ├── logos/
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ └── wrapped/
│ │ ├── index.html.erb
│ │ └── pages/
│ │ └── _summary_card_horizontal.html.erb
│ ├── page/
│ │ ├── about.md
│ │ ├── assets.html.erb
│ │ ├── components/
│ │ │ ├── _avatars.html.erb
│ │ │ ├── _buttons.html.erb
│ │ │ ├── _dropdowns.html.erb
│ │ │ ├── _links.html.erb
│ │ │ ├── _modals.html.erb
│ │ │ ├── _stamps.html.erb
│ │ │ ├── _tabs.html.erb
│ │ │ └── _tooltips.html.erb
│ │ ├── components.html.erb
│ │ ├── contributors.html.erb
│ │ ├── featured.html.erb
│ │ ├── home.html.erb
│ │ ├── privacy.md
│ │ ├── stickers.html.erb
│ │ └── uses.md
│ ├── profiles/
│ │ ├── _actions.html.erb
│ │ ├── _aliases.html.erb
│ │ ├── _events.html.erb
│ │ ├── _form.html.erb
│ │ ├── _header.html.erb
│ │ ├── _header_content.html.erb
│ │ ├── _involvements.html.erb
│ │ ├── _map.html.erb
│ │ ├── _mutual_events.html.erb
│ │ ├── _navigation.html.erb
│ │ ├── _notes.html.erb
│ │ ├── _socials.html.erb
│ │ ├── _stamps.html.erb
│ │ ├── _stickers.html.erb
│ │ ├── _tab_layout.html.erb
│ │ ├── _talks.html.erb
│ │ ├── _topics.html.erb
│ │ ├── actions/
│ │ │ ├── _admin.html.erb
│ │ │ ├── _anonymous.html.erb
│ │ │ ├── _owner.html.erb
│ │ │ └── _signed_in.html.erb
│ │ ├── aliases/
│ │ │ └── index.html.erb
│ │ ├── connect/
│ │ │ ├── _claim_profile.html.erb
│ │ │ ├── _friend_prompt.html.erb
│ │ │ ├── _no_profile_found.html.erb
│ │ │ └── show.html.erb
│ │ ├── edit.html.erb
│ │ ├── enhance/
│ │ │ └── update.turbo_stream.erb
│ │ ├── events/
│ │ │ └── index.html.erb
│ │ ├── involvements/
│ │ │ └── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── mutual_events/
│ │ │ └── index.html.erb
│ │ ├── notes/
│ │ │ ├── edit.html.erb
│ │ │ └── show.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ ├── stickers/
│ │ │ └── index.html.erb
│ │ ├── talks/
│ │ │ └── index.html.erb
│ │ └── wrapped/
│ │ ├── _share.html.erb
│ │ ├── card.html.erb
│ │ ├── index.html.erb
│ │ ├── pages/
│ │ │ ├── _bookends.html.erb
│ │ │ ├── _closing.html.erb
│ │ │ ├── _conference_buddies.html.erb
│ │ │ ├── _content_mix.html.erb
│ │ │ ├── _contributor.html.erb
│ │ │ ├── _cover.html.erb
│ │ │ ├── _event_map.html.erb
│ │ │ ├── _events_attended.html.erb
│ │ │ ├── _fun_facts.html.erb
│ │ │ ├── _involvements.html.erb
│ │ │ ├── _languages.html.erb
│ │ │ ├── _og_image.html.erb
│ │ │ ├── _passport_holder.html.erb
│ │ │ ├── _speaker_journey.html.erb
│ │ │ ├── _speaking_calendar.html.erb
│ │ │ ├── _stamps.html.erb
│ │ │ ├── _stickers.html.erb
│ │ │ ├── _summary_card.html.erb
│ │ │ ├── _summary_card_horizontal.html.erb
│ │ │ ├── _top_events.html.erb
│ │ │ ├── _top_speakers.html.erb
│ │ │ ├── _top_topics.html.erb
│ │ │ ├── _watch_twin.html.erb
│ │ │ ├── _watching_calendar.html.erb
│ │ │ └── _watching_journey.html.erb
│ │ └── private.html.erb
│ ├── recommendations/
│ │ └── index.html.erb
│ ├── sessions/
│ │ └── new.html.erb
│ ├── settings/
│ │ └── show.html.erb
│ ├── shared/
│ │ ├── _breakpoints.html.erb
│ │ ├── _cfp_countdown_badge.html.erb
│ │ ├── _city_sidebar.html.erb
│ │ ├── _country_card.html.erb
│ │ ├── _date_grouped_events.html.erb
│ │ ├── _event_countdown_badge.html.erb
│ │ ├── _event_row.html.erb
│ │ ├── _events_section.html.erb
│ │ ├── _filter_buttons.html.erb
│ │ ├── _flashes.html.erb
│ │ ├── _footer.html.erb
│ │ ├── _location_header.html.erb
│ │ ├── _location_map.html.erb
│ │ ├── _location_meetups.html.erb
│ │ ├── _location_navigation.html.erb
│ │ ├── _location_past.html.erb
│ │ ├── _location_show.html.erb
│ │ ├── _location_sidebar.html.erb
│ │ ├── _location_stamps.html.erb
│ │ ├── _location_users.html.erb
│ │ ├── _map_layer_controls.html.erb
│ │ ├── _map_time_filter.html.erb
│ │ ├── _meta_talk_card.html.erb
│ │ ├── _month_grouped_events.html.erb
│ │ ├── _navbar.html.erb
│ │ ├── _nearby_events_section.html.erb
│ │ ├── _rubyists_preview.html.erb
│ │ ├── _spotlight_search.html.erb
│ │ ├── _stamps_grid.html.erb
│ │ ├── _stickers_display.html.erb
│ │ ├── _toast.html.erb
│ │ ├── _top_banner.html.erb
│ │ ├── _user_dropdown.html.erb
│ │ ├── _user_mobile_dropdown.html.erb
│ │ └── navbar/
│ │ ├── _link.html.erb
│ │ └── _search_bar.html.erb
│ ├── speakers/
│ │ ├── _card.html.erb
│ │ ├── _speaker.html.erb
│ │ ├── _speaker.json.jbuilder
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── index.turbo_stream.erb
│ │ └── show.html.erb
│ ├── sponsors/
│ │ └── missing/
│ │ └── index.html.erb
│ ├── spotlight/
│ │ ├── events/
│ │ │ ├── _event.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ ├── kinds/
│ │ │ └── index.turbo_stream.erb
│ │ ├── languages/
│ │ │ └── index.turbo_stream.erb
│ │ ├── locations/
│ │ │ └── index.turbo_stream.erb
│ │ ├── organizations/
│ │ │ └── index.turbo_stream.erb
│ │ ├── series/
│ │ │ └── index.turbo_stream.erb
│ │ ├── speakers/
│ │ │ ├── _speaker.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ ├── talks/
│ │ │ ├── _talk.html.erb
│ │ │ └── index.turbo_stream.erb
│ │ └── topics/
│ │ └── index.turbo_stream.erb
│ ├── stamps/
│ │ └── index.html.erb
│ ├── states/
│ │ ├── cities/
│ │ │ └── index.html.erb
│ │ ├── country_index.html.erb
│ │ ├── index.html.erb
│ │ ├── map/
│ │ │ └── index.html.erb
│ │ ├── meetups/
│ │ │ └── index.html.erb
│ │ ├── past/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── stamps/
│ │ │ └── index.html.erb
│ │ └── users/
│ │ └── index.html.erb
│ ├── talks/
│ │ ├── _card.html.erb
│ │ ├── _card_horizontal.html.erb
│ │ ├── _card_thumbnail.html.erb
│ │ ├── _event.html.erb
│ │ ├── _event_tab.html.erb
│ │ ├── _explore_event.html.erb
│ │ ├── _form.html.erb
│ │ ├── _schedule_tab.html.erb
│ │ ├── _sections.html.erb
│ │ ├── _similar_talks_tab.html.erb
│ │ ├── _speaker_tab.html.erb
│ │ ├── _talk.html.erb
│ │ ├── _talk_filter.html.erb
│ │ ├── _transcript.html.erb
│ │ ├── _video_player.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── new.html.erb
│ │ ├── recommendations/
│ │ │ └── index.html.erb
│ │ ├── show.html.erb
│ │ ├── show.json.jbuilder
│ │ ├── slides/
│ │ │ └── show.html.erb
│ │ ├── video_providers/
│ │ │ ├── _children.html.erb
│ │ │ ├── _mp4.html.erb
│ │ │ ├── _not_published.html.erb
│ │ │ ├── _not_recorded.html.erb
│ │ │ ├── _scheduled.html.erb
│ │ │ ├── _video_unavailable.html.erb
│ │ │ ├── _vimeo.html.erb
│ │ │ └── _youtube.html.erb
│ │ └── watched_talks/
│ │ ├── _button.html.erb
│ │ ├── _feedback_banner.html.erb
│ │ ├── _feedback_questions.html.erb
│ │ ├── _form.html.erb
│ │ ├── _watched_overlay.html.erb
│ │ ├── _where_watched.html.erb
│ │ ├── create.turbo_stream.erb
│ │ ├── destroy.turbo_stream.erb
│ │ ├── new.html.erb
│ │ ├── toggle_attendance.turbo_stream.erb
│ │ └── update.turbo_stream.erb
│ ├── templates/
│ │ ├── _talk_fields.html.erb
│ │ ├── create.turbo_stream.erb
│ │ ├── delete_child.turbo_stream.erb
│ │ ├── new.html.erb
│ │ ├── new_child.turbo_stream.erb
│ │ └── speakers_search.turbo_stream.erb
│ ├── todos/
│ │ ├── _by_event.html.erb
│ │ ├── _by_type.html.erb
│ │ └── index.html.erb
│ ├── topics/
│ │ ├── _badge.html.erb
│ │ ├── _badge_list.html.erb
│ │ ├── _gem_info.html.erb
│ │ ├── _talks_cursor.html.erb
│ │ ├── _topic.html.erb
│ │ ├── index.html.erb
│ │ ├── index.turbo_stream.erb
│ │ ├── show.html.erb
│ │ └── show.turbo_stream.erb
│ ├── users/
│ │ └── _card.html.erb
│ ├── watch_lists/
│ │ ├── _watch_list.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── watched_talks/
│ │ └── index.html.erb
│ └── wrapped/
│ ├── _card.html.erb
│ ├── _featured.html.erb
│ └── index.html.erb
├── bin/
│ ├── brakeman
│ ├── bundle
│ ├── bundler-audit
│ ├── ci
│ ├── dev
│ ├── docker-entrypoint
│ ├── dump_prod.sh
│ ├── jobs
│ ├── lint
│ ├── mcp_server
│ ├── rails
│ ├── rake
│ ├── rubocop
│ ├── setup
│ ├── thrust
│ ├── update_lrug_meetups
│ └── vite
├── config/
│ ├── application.rb
│ ├── appsignal.yml
│ ├── boot.rb
│ ├── bundler-audit.yml
│ ├── cable.yml
│ ├── cache.yml
│ ├── ci.rb
│ ├── credentials/
│ │ ├── development.key
│ │ ├── development.yml.enc
│ │ ├── production.yml.enc
│ │ ├── staging.yml.enc
│ │ ├── test.key
│ │ └── test.yml.enc
│ ├── database.yml
│ ├── deploy.staging.yml
│ ├── deploy.yml
│ ├── environment.rb
│ ├── environments/
│ │ ├── development.rb
│ │ ├── production.rb
│ │ ├── staging.rb
│ │ └── test.rb
│ ├── initializers/
│ │ ├── active_genie.rb
│ │ ├── ahoy.rb
│ │ ├── assets.rb
│ │ ├── avo.rb
│ │ ├── content_security_policy.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── geocoder.rb
│ │ ├── groupe_date.rb
│ │ ├── inflections.rb
│ │ ├── locale.rb
│ │ ├── markdown.rb
│ │ ├── meta_tags.rb
│ │ ├── omniauth.rb
│ │ ├── openai.rb
│ │ ├── pagy.rb
│ │ ├── permissions_policy.rb
│ │ ├── reactionview.rb
│ │ ├── ruby_llm.rb
│ │ ├── typesense.rb
│ │ ├── yjit.rb
│ │ └── yt.rb
│ ├── litestream.staging.yml
│ ├── litestream.yml
│ ├── locales/
│ │ ├── en.yml
│ │ ├── models/
│ │ │ └── event_participation.en.yml
│ │ └── transliterate.en.yml
│ ├── puma.rb
│ ├── queue.yml
│ ├── recurring.yml
│ ├── routes.rb
│ ├── storage.yml
│ └── vite.json
├── config.ru
├── content/
│ └── announcements/
│ ├── 2026-01-01-happy-new-year-2026.md
│ ├── 2026-01-21-introducing-web-components.md
│ ├── 2026-01-22-announcements-and-news.md
│ ├── 2026-03-01-february-newsletter.md
│ └── 2026-04-01-march-newsletter.md
├── data/
│ ├── acts-as-conference/
│ │ ├── acts-as-conference-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── acts-as-conference-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── african-ruby-community/
│ │ ├── african-ruby-mini-conference-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── akashi-rb/
│ │ ├── akashi-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── aloha-on-rails/
│ │ ├── aloha-on-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── aloha-rubyconf/
│ │ ├── aloha-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── amsterdam-rb/
│ │ ├── amsterdam-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ancient-city-ruby/
│ │ ├── ancient-city-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ancient-city-ruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── arrrrcamp/
│ │ ├── arrrrcamp-2009-may/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2009-october/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── arrrrcamp-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── artificialruby/
│ │ ├── artificialruby/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── austinrb/
│ │ ├── austinrb/
│ │ │ ├── cfp.yml
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── balkanruby/
│ │ ├── balkanruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balkanruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── balticruby/
│ │ ├── balticruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balticruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── balticruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bangkok-rb/
│ │ ├── ruby-tuesday/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── barcelona-rb/
│ │ ├── barcelona-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── baruco/
│ │ ├── barcelona-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── barcelona-ruby-conf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bathruby/
│ │ ├── bathruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── bathruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── bathruby-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── belfastruby/
│ │ ├── belfast-rubyfest-2026/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── belfastruby-meetup/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── big-ruby/
│ │ ├── big-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── big-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── birmingham-on-rails/
│ │ ├── birmingham-on-rails-2020/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── blastoffrails/
│ │ ├── blastoffrails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── blue-ridge-ruby/
│ │ ├── blue-ridge-ruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── blue-ridge-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── blue-ridge-ruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bluegrass-ruby/
│ │ ├── bluegrass-ruby/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── bridgetownconf/
│ │ ├── bridgetownconf-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── brightonruby/
│ │ ├── brightonruby-2014/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2017/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2018/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2020/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2022/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── brightonruby-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── burlington-ruby-conference/
│ │ ├── burlington-ruby-conference-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── burlington-ruby-conference-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── cascadia-ruby/
│ │ ├── cascadia-ruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── cascadia-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── cascadia-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ceru-camp/
│ │ ├── ceru-camp-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── chicagoruby/
│ │ ├── chicagoruby/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── conferencia-rails/
│ │ ├── conferencia-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── conferencia-rails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── conferencia-rails-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── deccan-queen-on-rails/
│ │ ├── deccan-queen-on-rails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── deccanrubyconf/
│ │ ├── deccanrubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── deccanrubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── dotrb/
│ │ ├── dotrb-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── dresden-rb/
│ │ ├── dresden-rb-meetup/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── emea-on-rails/
│ │ ├── emea-on-rails-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── erubycon/
│ │ ├── erubycon-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── eurucamp/
│ │ ├── eurucamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── eurucamp-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── euruko/
│ │ ├── euruko-2003/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2004/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2005/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── euruko-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── noruko-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── featured_cities.yml
│ ├── fosdem/
│ │ ├── ruby-devroom-at-fosdem-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-devroom-at-fosdem-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── friendly-rb/
│ │ ├── friendly-rb-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── friendly-rb-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── friendly-rb-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── frozen-rails/
│ │ ├── frozen-rails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── frozen-rails-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── fukuoka-rubyistkaigi/
│ │ ├── fukuoka-rubyistkaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubyistkaigi-04/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubyistkaigi-05/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── fukuoka-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── garden-city-ruby/
│ │ ├── garden-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── garden-city-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── geneva-rb/
│ │ ├── geneva-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── gogaruco/
│ │ ├── gogaruco-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── gogaruco-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── goruco/
│ │ ├── goruco-2007/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2010/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── goruco-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── grill-rb/
│ │ ├── grill-rb-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── grill-rb-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── guru-pr/
│ │ ├── series.yml
│ │ └── tech-day-by-guru-pr-2016/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── haggis-ruby/
│ │ ├── haggis-ruby-2024/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── haggis-ruby-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── helsinki-ruby/
│ │ ├── helsinki-ruby-brigade/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── helveticruby/
│ │ ├── helveticruby-2023/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── helveticruby-2026/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── hirakatarb/
│ │ ├── hirakatarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── hokuriku-rubykaigi/
│ │ ├── hokuriku-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── isle-of-ruby/
│ │ ├── isle-of-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── jrubyconf-eu/
│ │ ├── jrubyconf-eu-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── jrubyconf-eu-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kaigi-on-rails/
│ │ ├── kaigi-on-rails-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── kaigi-on-rails-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kansai-rubykaigi/
│ │ ├── kansai-rubykaigi-08/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── kansai-rubykaigi-09/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kashiwarb/
│ │ ├── kashiwarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── keeprubyweird/
│ │ ├── keep-ruby-weird-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── keep-ruby-weird-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kiwi-ruby/
│ │ ├── kiwi-ruby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── kiwi-ruby-2019/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── koberb/
│ │ ├── koberb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── krk-rb/
│ │ ├── krk-rb-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kyobashirb/
│ │ ├── kyobashirb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── kyotorb/
│ │ ├── kyotorb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── la-conf/
│ │ └── series.yml
│ ├── la-rubyconf/
│ │ ├── la-rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── la-rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── latvian-ruby-community/
│ │ ├── latviarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── london-ruby-unconference/
│ │ ├── london-ruby-unconference-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── london-ruby-unconference-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── london-ruby-unconference-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lone-star-ruby-conf/
│ │ ├── lone-star-ruby-conf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── lone-star-ruby-conf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lrug/
│ │ ├── lrug-meetup/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── lyon-rb/
│ │ ├── lyon-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── madison-ruby/
│ │ ├── madison-ruby-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2012/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2013/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2014/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2015/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── madison-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── madrid-rb/
│ │ ├── madrid-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── magic-ruby/
│ │ ├── magic-ruby-2011/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── magic-ruby-2012/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── matsue-rubykaigi/
│ │ ├── matsue-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-05/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-06/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-07/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-08/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-09/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-10/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-11/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── matsue-rubykaigi-12/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── merseyrails/
│ │ ├── merseyrails/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── montreal-rb/
│ │ ├── montreal-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── mountainwest-rubyconf/
│ │ ├── mountainwest-rubyconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── mountainwest-rubyconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── munich-rubyshift/
│ │ ├── munich-rubyshift/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nairuby/
│ │ ├── rubyconf-kenya-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2017/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-kenya-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── naniwarb/
│ │ ├── naniwarb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── picorubyoverflowkaigi-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nantes-rb/
│ │ ├── nantes-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nepal-ruby/
│ │ ├── nepal-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nickel-city-ruby/
│ │ ├── nickel-city-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nickel-city-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── nordicruby/
│ │ ├── nordicruby-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── nordicruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── oedo-rubykaigi/
│ │ ├── oedo-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── oedo-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── osaka-rubykaigi/
│ │ ├── osaka-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-03/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── osaka-rubykaigi-04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── oxente-rails/
│ │ ├── oxente-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── paris-rb/
│ │ ├── paris-rb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── paris-rb-conf/
│ │ ├── paris-rb-conf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── paris-rb-conf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── philly-rb/
│ │ ├── philly-rb/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── pivorak/
│ │ ├── pivorak-conf-1/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-2/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-3/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-4/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── pivorak-conf-5/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── polishrubyusergroup/
│ │ ├── ruby-warsaw-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── poznanrubyusergroup/
│ │ ├── poznan-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-at-scale-summit/
│ │ ├── rails-at-scale-summit-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-at-scale-summit-2026/
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── rails-camp-south/
│ │ ├── rails-camp-south-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-uk/
│ │ ├── rails-camp-uk-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-us/
│ │ ├── rails-camp-us-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-camp-west/
│ │ ├── rails-camp-west-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2025/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-camp-west-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rails-girls-sao-paulo/
│ │ ├── rails-girls-sao-paulo-evolution-2026/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rails-hackathon/
│ │ ├── rails-hackathon-community/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rails-hackathon-hotwire/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-israel/
│ │ ├── rails-israel-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-israel-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-konferenz/
│ │ ├── rails-konferenz-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-outreach-workshop/
│ │ ├── rails-outreach-workshop-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-pacific/
│ │ ├── rails-pacific-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rails-pacific-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-remote-conf/
│ │ ├── rails-remote-conf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-saas-conference/
│ │ ├── rails-saas-conference-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-saas-conference-2023/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-studio/
│ │ ├── rails-studio-2006-pasadena/
│ │ │ └── event.yml
│ │ ├── rails-studio-portland/
│ │ │ └── event.yml
│ │ └── series.yml
│ ├── rails-summit-latin-america/
│ │ ├── rails-summit-latin-america-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-to-italy/
│ │ └── series.yml
│ ├── rails-underground/
│ │ ├── rails-underground-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rails-world/
│ │ ├── rails-world-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rails-world-2026/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsberry/
│ │ ├── railsberry-2012/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsclub/
│ │ ├── railsclub-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsclub-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsconf/
│ │ ├── railsconf-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railsconf-europe/
│ │ ├── railsconf-europe-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-europe-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railsconf-europe-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── railswaycon/
│ │ ├── railswaycon-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── railswaycon-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rbqconf/
│ │ ├── rbqconf-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── red-dirt-rubyconf/
│ │ ├── red-dirt-rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dirt-rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── red-dot-ruby-conference/
│ │ ├── red-dot-ruby-conference-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── red-dot-ruby-conference-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── redfrogconf/
│ │ ├── redfrogconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rocky-mountain-ruby/
│ │ ├── rocky-mountain-ruby-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rocky-mountain-ruby-2026/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── rossconf/
│ │ ├── rossconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2015-berlin/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rossconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rs-on-rails/
│ │ ├── rs-on-rails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-argentina/
│ │ ├── rubysur-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-australia/
│ │ ├── ruby-australia-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-banitsa/
│ │ ├── ruby-banitsa-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-banitsa-conf/
│ │ ├── ruby-banitsa-conf-2024/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-community-conference/
│ │ ├── ruby-community-conference-summer-edition-2023/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-summer-edition-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2024/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-community-conference-winter-edition-2026/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-cwb/
│ │ ├── ruby-cwb-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-dcamp/
│ │ ├── ruby-dcamp-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-dev-summit/
│ │ ├── ruby-dev-summit-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-df/
│ │ ├── ruby-df-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-foo/
│ │ ├── ruby-foo-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-for-good/
│ │ ├── ruby-for-good-2016/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2020/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2023/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-2024/
│ │ │ ├── event.yml
│ │ │ └── venue.yml
│ │ ├── ruby-for-good-2025/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-belgium-2025/
│ │ │ └── event.yml
│ │ ├── ruby-for-good-belgium-2026/
│ │ │ ├── event.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── ruby-frankfurt/
│ │ ├── ruby-frankfurt/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-fringe/
│ │ ├── futureruby-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfringe-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-galaxy/
│ │ ├── ruby-galaxy-v01/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v02/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v03/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v04/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v05/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-galaxy-v06/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-hoedown/
│ │ ├── nuby-hoedown-2010/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2007/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2008/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-hoedown-2010/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-in-common/
│ │ ├── ruby-in-common-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-in-london/
│ │ ├── ruby-in-london-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-india/
│ │ ├── pune-ruby-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-maizuru/
│ │ ├── ruby-maizuru-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-manor/
│ │ ├── ruby-manor-1/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-2/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-3/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ ├── ruby-manor-4/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-midwest/
│ │ ├── ruby-midwest-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-midwest-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-montevideo/
│ │ ├── ruby-montevideo/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-ales/
│ │ ├── ruby-on-ales-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ales-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-ice/
│ │ ├── ruby-on-ice-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── ruby-on-ice-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-rails-global-summit/
│ │ ├── ruby-on-rails-global-summit-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-rails-switzerland/
│ │ ├── railshock/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-on-waves/
│ │ ├── ruby-on-waves-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-phil/
│ │ ├── ruby-phil/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-retreat-au/
│ │ ├── rails-camp-au-1-2007/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-10-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-11-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-12-2012/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-13-2013/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-14-2013/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-15-2014/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-16-2014/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-17-2015/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-18-2015/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-19-2016/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-2-2007/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-20-2016/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-21-2017/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-22-2017/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-23-2018/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-24-2018/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-25-2019/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-26-2019/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-27-2022/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-3-2008/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-4-2008/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-5-2009/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-6-2009/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-7-2010/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-8-2010/
│ │ │ └── event.yml
│ │ ├── rails-camp-au-9-2011/
│ │ │ └── event.yml
│ │ ├── ruby-retreat-au-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-au-2025/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-retreat-nz/
│ │ ├── ruby-retreat-nz-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-nz-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-retreat-nz-2025/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-romania/
│ │ ├── ruby-romania-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-summit-china/
│ │ ├── ruby-summit-china-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-tuesday/
│ │ ├── ruby-tuesday-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-turkiye/
│ │ ├── ruby-turkiye/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-web-conference/
│ │ ├── ruby-web-conference-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── ruby-wine/
│ │ ├── ruby-wine-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── ruby-wine-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubybelgium/
│ │ ├── rubybelgium-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyc/
│ │ ├── rubyc-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyc-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubycon/
│ │ ├── rubycon-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf/
│ │ ├── rubyconf-2001/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2002/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2003/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2004/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2005/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2022-mini/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-africa/
│ │ ├── rubyconf-africa-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-africa-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-africa-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-argentina/
│ │ ├── rubyconf-argentina-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-argentina-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-au/
│ │ ├── rubyconf-au-2013/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2014/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2016/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2017/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2018/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-au-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-austria/
│ │ ├── rubyconf-austria-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-brazil/
│ │ ├── rubyconf-brazil-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-brazil-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-by/
│ │ ├── rubyconfby-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfby-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-china/
│ │ ├── rubyconf-china-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-china-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-china-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-colombia/
│ │ ├── rubyconf-colombia-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-colombia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-india/
│ │ ├── rubyconf-india-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2019/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2022/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-india-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-indonesia/
│ │ ├── rubyconf-indonesia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-jakarta/
│ │ ├── rubyconf-jakarta-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-lt/
│ │ ├── rubyconf-lt-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-lt-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-lt-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-my/
│ │ ├── rubyconf-kl-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-my-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-my-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-philippines/
│ │ ├── rubyconf-philippines-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-philippines-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-portugal/
│ │ ├── rubyconf-portugal-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-portugal-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-portugal-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-taiwan/
│ │ ├── rubyconf-taiwan-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-taiwan-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconf-uruguay/
│ │ ├── rubyconf-uruguay-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyconf-uruguay-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyconfth/
│ │ ├── rubyconf-th-2020/
│ │ │ └── event.yml
│ │ ├── rubyconfth-2019/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2022/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2023/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyconfth-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyday/
│ │ ├── rubyday-2011/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2015/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2016/
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyday-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyenrails/
│ │ ├── rubyandrails-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyenrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyeurope/
│ │ ├── rubyeurope-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyevents/
│ │ ├── rubyevents/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyfuza/
│ │ ├── rubyfuza-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyfuza-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyhack/
│ │ ├── rubyhack-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhack-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhack-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyhiroba/
│ │ ├── rubyhiroba-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyhiroba-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubykaigi/
│ │ ├── rubykaigi-2006/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2007/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubykaigi-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubykansai/
│ │ ├── rubykansai-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubymotion-inspect/
│ │ ├── rubymotion-inspect-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubymotion-inspect-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubymx/
│ │ ├── rubymx-meetup/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubynation/
│ │ ├── rubynation-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubynation-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyness/
│ │ ├── rubyness-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyrussia/
│ │ ├── rubyrussia-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2023/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrussia-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyrx/
│ │ ├── rubyrx-2009-philadelphia/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyrx-2009-washington-dc/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubysauna/
│ │ ├── rubysauna-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyunconf/
│ │ ├── rubyunconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyunconf-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rubyworld/
│ │ ├── rubyworld-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2024/
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── rubyworld-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rulu/
│ │ ├── rulu-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── rulu-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── rupy/
│ │ ├── rupy-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── saint-p-rubyconf/
│ │ ├── saint-p-rubyconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2019/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── saint-p-rubyconf-2021/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── scotlandonrails/
│ │ ├── scotlandonrails-2008/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scotlandonrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── scottish-ruby-conf/
│ │ ├── scottish-ruby-conf-2010/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2011/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── scottish-ruby-conf-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── series.yml
│ ├── sekigahara-rubykaigi/
│ │ ├── sekigahara-rubykaigi-01/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ └── venue.yml
│ │ └── series.yml
│ ├── sf-bay-area-ruby/
│ │ ├── series.yml
│ │ └── sf-bay-area-ruby-meetup/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── sfruby/
│ │ ├── series.yml
│ │ └── sfruby-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── shinosakarb/
│ │ ├── series.yml
│ │ └── shinosakarb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── shrug/
│ │ ├── series.yml
│ │ └── shrug-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── sin-city-ruby/
│ │ ├── series.yml
│ │ ├── sin-city-ruby-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── sin-city-ruby-2024/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── sin-city-ruby-2025/
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── solidusconf/
│ │ ├── series.yml
│ │ ├── solidusconf-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── solidusconf-2020/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── solidusconf-2022/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── southeast-ruby/
│ │ ├── series.yml
│ │ ├── southeast-ruby-2017/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── southeast-ruby-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── southeast-ruby-2019/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── speakers.yml
│ ├── steelcityruby/
│ │ ├── series.yml
│ │ ├── steelcityruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── steelcityruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── steelcityruby-2014/
│ │ ├── event.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── the-rails-edge/
│ │ ├── series.yml
│ │ └── the-rails-edge-2007/
│ │ ├── event.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── thoughtbot-open-summit/
│ │ ├── series.yml
│ │ ├── thoughtbot-open-summit-2024/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── thoughtbot-open-summit-2025/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── tiny-ruby-conf/
│ │ ├── series.yml
│ │ ├── tiny-ruby-conf-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── tiny-ruby-conf-2026/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ └── venue.yml
│ ├── tokyo-rubykaigi/
│ │ ├── series.yml
│ │ ├── tokyo-rubykaigi-10/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── tokyo-rubykaigi-11/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── tokyo-rubykaigi-12/
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── topics.yml
│ ├── toronto-ruby/
│ │ ├── series.yml
│ │ └── toronto-ruby-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── tropicalrb/
│ │ ├── abril-pro-ruby-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── abril-pro-ruby-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── abril-pro-ruby-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── series.yml
│ │ ├── tropical-on-rails-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── tropical-on-rails-2026/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── tropicalrb-2015/
│ │ │ ├── event.yml
│ │ │ ├── involvements.yml
│ │ │ ├── schedule.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── tropicalrb-2024/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── umedarb/
│ │ ├── series.yml
│ │ └── umedarb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── valencia-rb/
│ │ ├── series.yml
│ │ └── valencia-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── vienna-rb/
│ │ ├── series.yml
│ │ └── vienna-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── wakayama-rb/
│ │ ├── series.yml
│ │ └── wakayama-rb-meetup/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── windycityrails/
│ │ ├── series.yml
│ │ ├── windycityrails-2009/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── windycityrails-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── windycityrails-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ └── windycityrails-2016/
│ │ ├── event.yml
│ │ └── videos.yml
│ ├── winnipeg-rb/
│ │ ├── series.yml
│ │ └── winnipeg-rb-meetup/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── wnb-rb/
│ │ ├── series.yml
│ │ └── wnb-rb-meetup/
│ │ ├── event.yml
│ │ ├── sponsors.yml
│ │ └── videos.yml
│ ├── wroclove-rb/
│ │ ├── series.yml
│ │ ├── wroclove-rb-2012/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2013/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2014/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2015/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2016/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2017/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2018/
│ │ │ ├── event.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2019/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2022/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2023/
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2024/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ ├── wroclove-rb-2025/
│ │ │ ├── cfp.yml
│ │ │ ├── event.yml
│ │ │ ├── sponsors.yml
│ │ │ ├── venue.yml
│ │ │ └── videos.yml
│ │ └── wroclove-rb-2026/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ └── xoruby/
│ ├── series.yml
│ ├── xoruby-atlanta-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-austin-2025/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-chicago-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-new-orleans-2025/
│ │ ├── cfp.yml
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ ├── xoruby-portland-2025/
│ │ ├── event.yml
│ │ ├── involvements.yml
│ │ ├── schedule.yml
│ │ ├── sponsors.yml
│ │ ├── venue.yml
│ │ └── videos.yml
│ └── xoruby-san-diego-2025/
│ ├── event.yml
│ ├── involvements.yml
│ ├── schedule.yml
│ ├── sponsors.yml
│ ├── venue.yml
│ └── videos.yml
├── db/
│ ├── cache_migrate/
│ │ ├── 20240516085648_create_solid_cache_entries.solid_cache.rb
│ │ ├── 20240516085649_add_key_hash_and_byte_size_to_solid_cache_entries.solid_cache.rb
│ │ ├── 20240516085650_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.solid_cache.rb
│ │ └── 20240516085651_remove_key_index_from_solid_cache_entries.solid_cache.rb
│ ├── cache_schema.rb
│ ├── migrate/
│ │ ├── 20230512050041_create_speakers.rb
│ │ ├── 20230512051225_create_talks.rb
│ │ ├── 20230521202931_create_join_table_speakers_talks.rb
│ │ ├── 20230521211825_add_talks_count_to_speaker.rb
│ │ ├── 20230522051939_create_suggestions.rb
│ │ ├── 20230522133312_create_users.rb
│ │ ├── 20230522133313_create_sessions.rb
│ │ ├── 20230522133314_create_password_reset_tokens.rb
│ │ ├── 20230522133315_create_email_verification_tokens.rb
│ │ ├── 20230522212710_create_events.rb
│ │ ├── 20230522213244_add_event_references_to_talk.rb
│ │ ├── 20230525132507_add_index_slug_to_speaker.rb
│ │ ├── 20230604215247_create_ahoy_visits_and_events.rb
│ │ ├── 20230614230539_rename_event_table_to_organisation.rb
│ │ ├── 20230614231651_create_new_events.rb
│ │ ├── 20230614232412_add_youtube_channel_id_to_organisation.rb
│ │ ├── 20230614233712_add_slug_to_organisation.rb
│ │ ├── 20230614234246_add_name_to_event.rb
│ │ ├── 20230615053959_add_slug_to_event.rb
│ │ ├── 20230615055319_add_thumbnails_to_talk.rb
│ │ ├── 20230615055800_fix_foreign_constrain.rb
│ │ ├── 20230615105716_add_date_to_talk.rb
│ │ ├── 20230622202051_add_upvotes_and_views_to_talks.rb
│ │ ├── 20230625215935_add_twitter_to_organisation.rb
│ │ ├── 20230720104208_add_uniq_index_speaker_talk.rb
│ │ ├── 20230720151537_add_primary_key_to_speaker_talks.rb
│ │ ├── 20240611113918_add_transcript_to_talk.rb
│ │ ├── 20240614060121_add_service_name_to_active_storage_blobs.active_storage.rb
│ │ ├── 20240614060122_create_active_storage_variant_records.active_storage.rb
│ │ ├── 20240614060123_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│ │ ├── 20240709194147_add_enhanced_transcript_to_talk.rb
│ │ ├── 20240709200506_rename_transcript_in_talk.rb
│ │ ├── 20240718202658_add_summary_to_talk.rb
│ │ ├── 20240811121204_create_topics.rb
│ │ ├── 20240811122145_create_talk_topics.rb
│ │ ├── 20240815155647_add_uniq_index_on_talk_topic.rb
│ │ ├── 20240816074626_add_status_to_topic.rb
│ │ ├── 20240817083428_add_canonical_reference_to_topic.rb
│ │ ├── 20240818212042_add_language_to_talk.rb
│ │ ├── 20240819134138_drop_year_from_talk.rb
│ │ ├── 20240821191208_add_talks_count_to_event.rb
│ │ ├── 20240823221832_add_slides_url_to_talks.rb
│ │ ├── 20240901163900_add_canonical_reference_to_speaker.rb
│ │ ├── 20240902204752_add_canonical_reference_to_event.rb
│ │ ├── 20240908072757_create_watch_list_talks.rb
│ │ ├── 20240908072819_create_watch_lists.rb
│ │ ├── 20240909194059_create_connected_accounts.rb
│ │ ├── 20240912163015_add_name_to_user.rb
│ │ ├── 20240912163159_encrypt_user_fields.rb
│ │ ├── 20240912164120_encrypt_connected_account_field.rb
│ │ ├── 20240914162252_add_github_handle_to_user.rb
│ │ ├── 20240918050951_add_approved_by_to_suggestion.rb
│ │ ├── 20240918215740_add_suggestor_to_suggestions.rb
│ │ ├── 20240919193323_add_index_on_updated_at_for_talks.rb
│ │ ├── 20240920054416_add_talks_count_to_topic.rb
│ │ ├── 20240920154237_add_talks_count_to_watch_list.rb
│ │ ├── 20241019135118_create_talk_fts.rb
│ │ ├── 20241020174649_add_speakerdeck_to_speaker.rb
│ │ ├── 20241022012631_add_summarized_using_ai_to_talk.rb
│ │ ├── 20241023093844_add_pronouns_to_speaker.rb
│ │ ├── 20241023150341_remove_index_talk_topics_on_talk_id.rb
│ │ ├── 20241023154126_recreate_talk_topic_indexes.rb
│ │ ├── 20241029112719_add_mastodown_bluesky_linkedin_to_speaker.rb
│ │ ├── 20241031112333_add_kind_to_talk.rb
│ │ ├── 20241101185604_add_external_player_to_talk.rb
│ │ ├── 20241105143943_add_website_to_events.rb
│ │ ├── 20241105151601_create_watched_talks.rb
│ │ ├── 20241108215612_add_indexes_speaker_talks.rb
│ │ ├── 20241122155013_add_index_on_slug_for_topics.rb
│ │ ├── 20241122163052_add_index_on_kind_for_talks.rb
│ │ ├── 20241128073415_add_bsky_metadata_to_speaker.rb
│ │ ├── 20241130095835_add_parent_talk_to_talk.rb
│ │ ├── 20241203001515_add_github_metadata_to_speaker.rb
│ │ ├── 20241227192232_add_duration_to_talk.rb
│ │ ├── 20250102175230_create_speaker_full_text_search.rb
│ │ ├── 20250104095544_create_talk_transcript.rb
│ │ ├── 20250104230927_create_rollups.rb
│ │ ├── 20250108221813_set_view_count_default_on_talk.rb
│ │ ├── 20250115215944_add_index_video_provider_to_talk.rb
│ │ ├── 20250124210823_add_discarded_at_speaker_talk.rb
│ │ ├── 20250127012711_add_published_at_and_announced_at_to_talk.rb
│ │ ├── 20250128070756_add_unique_index_on_github_in_speaker.rb
│ │ ├── 20250128085252_remove_github_of_speaker_with_canonical.rb
│ │ ├── 20250218073648_normalize_github_handle.rb
│ │ ├── 20250426063312_add_llm_requests.rb
│ │ ├── 20250427062021_add_success_to_llm_requests.rb
│ │ ├── 20250521011731_add_original_title_to_talks.rb
│ │ ├── 20250524174213_normalize_speaker_website.rb
│ │ ├── 20250605203429_add_start_and_end_date_to_event.rb
│ │ ├── 20250609072113_remove_talk_parent_id_self.rb
│ │ ├── 20250614131810_add_kind_to_event.rb
│ │ ├── 20250617115359_add_date_precision_to_events.rb
│ │ ├── 20250618000304_add_call_for_papers_to_events.rb
│ │ ├── 20250719180249_create_sponsors.rb
│ │ ├── 20250719190411_add_slug_to_sponsors.rb
│ │ ├── 20250719200918_create_event_sponsors.rb
│ │ ├── 20250719204509_add_banner_url_to_sponsors.rb
│ │ ├── 20250719205313_add_main_location_to_sponsors.rb
│ │ ├── 20250719205756_remove_url_fields_from_sponsors.rb
│ │ ├── 20250719225634_add_tier_to_event_sponsors.rb
│ │ ├── 20250719231642_add_logo_url_to_sponsors.rb
│ │ ├── 20250808095915_add_logo_urls_to_sponsors.rb
│ │ ├── 20250808192916_clean_sponsor_website_urls.rb
│ │ ├── 20250809194302_add_badge_to_event_sponsors.rb
│ │ ├── 20250813211012_decrypt_user_name.rb
│ │ ├── 20250814214127_add_speaker_attributes_to_users.rb
│ │ ├── 20250814214146_create_user_talks.rb
│ │ ├── 20250814214412_create_users_search_index.rb
│ │ ├── 20250817060410_set_users_email_null_true.rb
│ │ ├── 20250817223427_update_user_indexes.rb
│ │ ├── 20250819022348_create_cfps.rb
│ │ ├── 20250820183955_add_progress_seconds_to_watched_talks.rb
│ │ ├── 20250820225005_add_watched_talks_count_to_users.rb
│ │ ├── 20250822175423_add_unique_index_to_event_sponsors.rb
│ │ ├── 20250823020826_remove_cfp_fields_from_events.rb
│ │ ├── 20250901185702_update_suggestions_suggestable.rb
│ │ ├── 20250903125458_create_event_participations.rb
│ │ ├── 20250920194438_add_country_code_to_event.rb
│ │ ├── 20250930165602_add_location_to_user.rb
│ │ ├── 20251010092526_create_event_involvements.rb
│ │ ├── 20251012114916_create_contributors.rb
│ │ ├── 20251019071129_add_case_insensitive_github_handle_index_to_users.rb
│ │ ├── 20251019212653_remove_badge_awards_table.rb
│ │ ├── 20251028143012_create_aliases.rb
│ │ ├── 20251028143916_add_marked_for_deletion_to_users.rb
│ │ ├── 20251028143917_migrate_canonical_users_to_aliases.rb
│ │ ├── 20251029032709_rename_organisation_to_event_series.rb
│ │ ├── 20251126171800_rename_sponsor_to_organization.rb
│ │ ├── 20251126171948_rename_event_sponsor_to_sponsor.rb
│ │ ├── 20251126203734_add_coordinates_to_events.rb
│ │ ├── 20251201143109_add_event_names_to_talks_search_index.rb
│ │ ├── 20251201144052_change_slug_to_optional_in_aliases.rb
│ │ ├── 20251203110611_change_youtube_channel_id_nullable_on_event_series.rb
│ │ ├── 20251203110659_change_youtube_channel_name_nullable_on_event_series.rb
│ │ ├── 20251204145850_add_static_id_to_talks.rb
│ │ ├── 20251204195048_change_static_id_null_constraint_on_talks.rb
│ │ ├── 20251206194409_add_additional_resources_to_talks.rb
│ │ ├── 20260101090225_add_wrapped_public_to_users.rb
│ │ ├── 20260101125732_create_active_storage_tables.active_storage.rb
│ │ ├── 20260103114901_add_feedback_to_watched_talks.rb
│ │ ├── 20260103122047_add_feedback_shared_at_to_watched_talks.rb
│ │ ├── 20260103125129_backfill_watched_at_on_watched_talks.rb
│ │ ├── 20260103130348_add_watched_to_watched_talks.rb
│ │ ├── 20260103134407_add_feedback_enabled_to_users.rb
│ │ ├── 20260103210603_add_settings_to_users.rb
│ │ ├── 20260103232220_create_topic_gems.rb
│ │ ├── 20260104181518_create_favorite_users.rb
│ │ ├── 20260105100000_add_indexes_to_ahoy_visits_for_cleanup.rb
│ │ ├── 20260105120000_add_video_provider_to_talks_search_index.rb
│ │ ├── 20260106092124_add_geocoding_to_users.rb
│ │ ├── 20260106121436_add_state_to_events.rb
│ │ ├── 20260106121459_create_featured_cities.rb
│ │ ├── 20260106135053_create_geocode_results.rb
│ │ ├── 20260106141308_add_location_to_events.rb
│ │ ├── 20260106232656_add_marked_suspicious_at_to_users.rb
│ │ ├── 20260109095858_add_geocode_metadata_to_events.rb
│ │ ├── 20260109152525_change_aliases_slug_index.rb
│ │ ├── 20260110193214_rename_featured_cities_to_cities.rb
│ │ ├── 20260110194925_rename_state_to_state_code_on_events_and_users.rb
│ │ ├── 20260110201634_remove_city_column_from_cities.rb
│ │ ├── 20260112065507_add_youtube_thumbnail_checked_at_to_talks.rb
│ │ ├── 20260112071922_add_video_availability_fields_to_talks.rb
│ │ ├── 20260301182003_add_notes_to_favorite_user.rb
│ │ └── 20260306110802_add_level_to_sponsors.rb
│ ├── queue_migrate/
│ │ ├── 20240516090838_create_solid_queue_tables.solid_queue.rb
│ │ ├── 20240516090839_add_missing_index_to_blocked_executions.solid_queue.rb
│ │ ├── 20240516090840_create_recurring_executions.solid_queue.rb
│ │ ├── 20240814230055_create_recurring_tasks.solid_queue.rb
│ │ ├── 20240908182634_add_name_to_processes.solid_queue.rb
│ │ ├── 20240908191029_make_name_not_null.solid_queue.rb
│ │ └── 20240908191030_change_solid_queue_recurring_tasks_static_to_not_null.solid_queue.rb
│ ├── queue_schema.rb
│ ├── schema.rb
│ └── seeds.rb
├── docker-compose.typesense.yml
├── docs/
│ ├── ADDING_CFPS.md
│ ├── ADDING_EVENTS.md
│ ├── ADDING_INVOLVEMENTS.md
│ ├── ADDING_MEETUPS.md
│ ├── ADDING_SCHEDULES.md
│ ├── ADDING_SPONSORS.md
│ ├── ADDING_UNPUBLISHED_TALKS.md
│ ├── ADDING_VENUES.md
│ ├── ADDING_VIDEOS.md
│ ├── ADDING_VISUAL_ASSETS.md
│ └── FIXING_PROFILE_NAMES.md
├── lib/
│ ├── assets/
│ │ └── .keep
│ ├── authenticator.rb
│ ├── generators/
│ │ ├── cfp/
│ │ │ ├── USAGE
│ │ │ ├── cfp_generator.rb
│ │ │ └── templates/
│ │ │ ├── cfp.yml.tt
│ │ │ └── header.yml.tt
│ │ ├── event/
│ │ │ ├── USAGE
│ │ │ ├── event_generator.rb
│ │ │ └── templates/
│ │ │ └── event.yml.tt
│ │ ├── event_base.rb
│ │ ├── schedule/
│ │ │ ├── USAGE
│ │ │ ├── schedule_generator.rb
│ │ │ └── templates/
│ │ │ └── schedule.yml.tt
│ │ ├── sponsors/
│ │ │ ├── USAGE
│ │ │ ├── sponsors_generator.rb
│ │ │ └── templates/
│ │ │ └── sponsors.yml.tt
│ │ ├── talk/
│ │ │ ├── USAGE
│ │ │ ├── talk_generator.rb
│ │ │ └── templates/
│ │ │ ├── lightning_talks.yml.tt
│ │ │ ├── talk.yml.tt
│ │ │ └── videos.yml.tt
│ │ └── venue/
│ │ ├── USAGE
│ │ ├── templates/
│ │ │ ├── location.yml.tt
│ │ │ └── venue.yml.tt
│ │ └── venue_generator.rb
│ ├── guard/
│ │ └── data_import.rb
│ ├── protobuf/
│ │ └── message_type.rb
│ ├── schemas/
│ │ ├── address_schema.json
│ │ ├── cfp_schema.json
│ │ ├── cfps_schema.json
│ │ ├── coordinates_schema.json
│ │ ├── event_schema.json
│ │ ├── featured_city_schema.json
│ │ ├── hotel_schema.json
│ │ ├── involvement_schema.json
│ │ ├── location_schema.json
│ │ ├── maps_schema.json
│ │ ├── schedule_schema.json
│ │ ├── series_schema.json
│ │ ├── speaker_schema.json
│ │ ├── sponsors_schema.json
│ │ ├── tiers_sponsors_schema.json
│ │ ├── transcript_schema.json
│ │ ├── venue_schema.json
│ │ ├── video_schema.json
│ │ └── videos_schema.json
│ ├── tasks/
│ │ ├── annotate_rb.rake
│ │ ├── assets.rake
│ │ ├── backfill_event_involvements.rake
│ │ ├── backfill_speaker_participation.rake
│ │ ├── cities.rake
│ │ ├── cleanup_ahoy_visits.rake
│ │ ├── contributors.rake
│ │ ├── db.rake
│ │ ├── download.rake
│ │ ├── dump.rake
│ │ ├── event_assets.rake
│ │ ├── geolocate.thor
│ │ ├── schema.rake
│ │ ├── search.rake
│ │ ├── seed.rake
│ │ ├── speakerdeck.rake
│ │ ├── speakers.rake
│ │ ├── thumbnails.rake
│ │ ├── topics.rake
│ │ ├── transcripts.rake
│ │ ├── update_video_statistics.rake
│ │ ├── users.rake
│ │ └── validate.rake
│ └── templates/
│ └── erb/
│ └── scaffold/
│ ├── _form.html.erb.tt
│ ├── edit.html.erb.tt
│ ├── index.html.erb.tt
│ ├── new.html.erb.tt
│ ├── partial.html.erb.tt
│ └── show.html.erb.tt
├── log/
│ └── .keep
├── package.json
├── postcss.config.js
├── public/
│ ├── 400.html
│ ├── 404.html
│ ├── 406-unsupported-browser.html
│ ├── 422.html
│ ├── 500.html
│ └── robots.txt
├── scripts/
│ ├── create_events.rb
│ ├── create_videos_yml.rb
│ ├── dump_speakers.rb
│ ├── dump_talks.rb
│ ├── extract_videos.rb
│ ├── import_event.rb
│ ├── prepare_series.rb
│ └── sort_speakers.rb
├── storage/
│ └── .keep
├── tailwind.config.js
├── test/
│ ├── application_system_test_case.rb
│ ├── channels/
│ │ └── application_cable/
│ │ └── connection_test.rb
│ ├── clients/
│ │ └── youtube/
│ │ ├── channels_test.rb
│ │ ├── playlist_items_test.rb
│ │ ├── playlists_test.rb
│ │ ├── transcript_test.rb
│ │ └── video_test.rb
│ ├── components/
│ │ ├── application_component_test.rb
│ │ ├── events/
│ │ │ ├── upcoming_event_banner_component/
│ │ │ │ └── helper_methods_test.rb
│ │ │ └── upcoming_event_banner_component_test.rb
│ │ ├── tito/
│ │ │ ├── button_component_test.rb
│ │ │ └── widget_component_test.rb
│ │ └── ui/
│ │ ├── avatar_component_test.rb
│ │ ├── badge_component_test.rb
│ │ ├── button_component_test.rb
│ │ └── stamp_component_test.rb
│ ├── controllers/
│ │ ├── analytics/
│ │ │ └── dashboards_controller_test.rb
│ │ ├── announcements_controller_test.rb
│ │ ├── cfp_controller_test.rb
│ │ ├── countries_controller_test.rb
│ │ ├── events/
│ │ │ ├── archive_controller_test.rb
│ │ │ ├── meetups_controller_test.rb
│ │ │ ├── participations_controller_test.rb
│ │ │ ├── related_talks_controller_test.rb
│ │ │ ├── schedule_controller_test.rb
│ │ │ ├── series_controller_test.rb
│ │ │ └── sponsors_controller_test.rb
│ │ ├── events_controller_test.rb
│ │ ├── favorite_users_controller_test.rb
│ │ ├── locations/
│ │ │ └── users_controller_test.rb
│ │ ├── organizations/
│ │ │ ├── logos_controller_test.rb
│ │ │ └── wrapped_controller_test.rb
│ │ ├── organizations_controller_test.rb
│ │ ├── page_controller_test.rb
│ │ ├── profiles/
│ │ │ ├── connect_controller_test.rb
│ │ │ ├── enhance_controller_test.rb
│ │ │ ├── involvements_controller_test.rb
│ │ │ ├── map_controller_test.rb
│ │ │ ├── notes_controller_test.rb
│ │ │ └── wrapped_controller_test.rb
│ │ ├── profiles_controller_test.rb
│ │ ├── recommendations_controller_test.rb
│ │ ├── sessions/
│ │ │ ├── omniauth_controller_test copy.rb
│ │ │ └── omniauth_controller_test.rb
│ │ ├── sessions_controller_test.rb
│ │ ├── speakers_controller_test.rb
│ │ ├── sponsors/
│ │ │ └── missing_controller_test.rb
│ │ ├── spotlight/
│ │ │ ├── events_controller_test.rb
│ │ │ ├── languages_controller_test.rb
│ │ │ ├── locations_controller_test.rb
│ │ │ ├── organizations_controller_test.rb
│ │ │ ├── series_controller_test.rb
│ │ │ ├── speakers_controller_test.rb
│ │ │ ├── talks_controller_test.rb
│ │ │ └── topics_controller_test.rb
│ │ ├── suggestions_controller_test.rb
│ │ ├── talks/
│ │ │ ├── recommendations_controller_test.rb
│ │ │ ├── slides_controller_test.rb
│ │ │ └── watched_talks_controller_test.rb
│ │ ├── talks_controller_test.rb
│ │ ├── topics_controller_test.rb
│ │ ├── watch_list_talks_controller_test.rb
│ │ ├── watch_lists_controller_test.rb
│ │ ├── watched_talks_controller_test.rb
│ │ └── wrapped_controller_test.rb
│ ├── fixtures/
│ │ ├── cfps.yml
│ │ ├── cities.yml
│ │ ├── connected_accounts.yml
│ │ ├── contributors.yml
│ │ ├── event_series.yml
│ │ ├── events.yml
│ │ ├── favorite_users.yml
│ │ ├── files/
│ │ │ └── .keep
│ │ ├── organizations.yml
│ │ ├── sponsors.yml
│ │ ├── suggestions.yml
│ │ ├── talk/
│ │ │ └── transcripts.yml
│ │ ├── talk_topics.yml
│ │ ├── talks.yml
│ │ ├── topics.yml
│ │ ├── user_talks.yml
│ │ ├── users.yml
│ │ ├── watch_lists.yml
│ │ └── watched_talks.yml
│ ├── helpers/
│ │ ├── event_tracking_helper.rb
│ │ └── icon_helper_test.rb
│ ├── integration/
│ │ └── .keep
│ ├── jobs/
│ │ ├── geocode_record_job_test.rb
│ │ └── recurring/
│ │ ├── rollup_job_test.rb
│ │ ├── youtube_thumbnail_validation_job_test.rb
│ │ ├── youtube_video_availability_job_test.rb
│ │ └── youtube_video_statistics_job_test.rb
│ ├── lib/
│ │ ├── download_sponsors_test.rb
│ │ ├── generators/
│ │ │ ├── cfp_generator_test.rb
│ │ │ ├── event_generator_test.rb
│ │ │ ├── schedule_generator_test.rb
│ │ │ ├── sponsors_generator_test.rb
│ │ │ ├── talk_generator_test.rb
│ │ │ └── venue_generator_test.rb
│ │ └── schema_export_test.rb
│ ├── models/
│ │ ├── ahoy/
│ │ │ └── visit_test.rb
│ │ ├── alias_test.rb
│ │ ├── announcement_test.rb
│ │ ├── cfp_test.rb
│ │ ├── city_test.rb
│ │ ├── concerns/
│ │ │ └── geocodeable_test.rb
│ │ ├── connected_account_test.rb
│ │ ├── continent_test.rb
│ │ ├── contributor_test.rb
│ │ ├── country_test.rb
│ │ ├── event/
│ │ │ ├── assets_test.rb
│ │ │ ├── static_metadata_test.rb
│ │ │ ├── tickets/
│ │ │ │ └── provider_detection_test.rb
│ │ │ ├── tickets_test.rb
│ │ │ └── videos_file_test.rb
│ │ ├── event_geocoding_test.rb
│ │ ├── event_participation_test.rb
│ │ ├── event_series_test.rb
│ │ ├── event_test.rb
│ │ ├── favorite_user_test.rb
│ │ ├── language_test.rb
│ │ ├── location_test.rb
│ │ ├── online_location_test.rb
│ │ ├── organization_test.rb
│ │ ├── rollup_test.rb
│ │ ├── search/
│ │ │ ├── backend/
│ │ │ │ ├── sqlite_fts/
│ │ │ │ │ └── indexer_test.rb
│ │ │ │ ├── sqlite_fts_test.rb
│ │ │ │ ├── typesense/
│ │ │ │ │ ├── indexer_test.rb
│ │ │ │ │ ├── language_indexer_test.rb
│ │ │ │ │ └── location_indexer_test.rb
│ │ │ │ └── typesense_test.rb
│ │ │ └── backend_test.rb
│ │ ├── sponsor_test.rb
│ │ ├── state_test.rb
│ │ ├── static/
│ │ │ ├── city_test.rb
│ │ │ └── event_test.rb
│ │ ├── suggestion_test.rb
│ │ ├── talk_test.rb
│ │ ├── talk_topic_test.rb
│ │ ├── template_test.rb
│ │ ├── topic_test.rb
│ │ ├── uk_nation_test.rb
│ │ ├── user/
│ │ │ ├── duplicate_detector_test.rb
│ │ │ ├── profiles_test.rb
│ │ │ ├── suspicion_detector_test.rb
│ │ │ └── talk_recommendations_test.rb
│ │ ├── user_geocoding_test.rb
│ │ ├── user_talk_test.rb
│ │ ├── user_test.rb
│ │ └── youtube/
│ │ ├── video_metadata_rails_worldtest.rb
│ │ └── video_metadata_test.rb
│ ├── system/
│ │ ├── call_for_papers_test.rb
│ │ ├── events_test.rb
│ │ ├── sessions_test.rb
│ │ ├── speakers_test.rb
│ │ ├── sponsors_test.rb
│ │ ├── spotlight_search_test.rb
│ │ └── talks_test.rb
│ ├── tasks/
│ │ ├── db_seed_test.rb
│ │ └── seed_test.rb
│ ├── test_helper.rb
│ ├── tools/
│ │ ├── cfp_create_tool_test.rb
│ │ ├── cfp_info_tool_test.rb
│ │ └── cfp_update_tool_test.rb
│ └── vcr_cassettes/
│ ├── profiles/
│ │ └── enhance_controller_test/
│ │ └── patch.yml
│ ├── recurring_youtube_statistics_job.yml
│ ├── sessions/
│ │ └── omniauth_controller_test/
│ │ └── creates_a_new_user_if_not_exists_github.yml
│ ├── talks/
│ │ ├── extract_topics.yml
│ │ ├── summarize.yml
│ │ └── transcript-enhancement.yml
│ ├── user/
│ │ └── enhance_profile_job_test.yml
│ ├── youtube/
│ │ ├── channels-scrapping.yml
│ │ ├── channels.yml
│ │ ├── playlist_items/
│ │ │ └── all.yml
│ │ ├── playlists/
│ │ │ └── all.yml
│ │ ├── transcript.yml
│ │ └── transcript_not_available.yml
│ ├── youtube_duration.yml
│ ├── youtube_statistics.yml
│ ├── youtube_statistics_invalid.yml
│ └── youtube_video_transcript.yml
├── tmp/
│ └── .keep
├── vendor/
│ └── .keep
├── vite.config.mjs
└── yaml/
├── enforce_strings.mjs
└── formatter.mjs
Showing preview only (275K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3552 symbols across 839 files)
FILE: app/avo/actions/approve_topic.rb
class Avo::Actions::ApproveTopic (line 1) | class Avo::Actions::ApproveTopic < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/assign_canonical_event.rb
class Avo::Actions::AssignCanonicalEvent (line 1) | class Avo::Actions::AssignCanonicalEvent < Avo::BaseAction
method fields (line 7) | def fields
method handle (line 13) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/assign_canonical_speaker.rb
class Avo::Actions::AssignCanonicalSpeaker (line 1) | class Avo::Actions::AssignCanonicalSpeaker < Avo::BaseAction
method fields (line 7) | def fields
method handle (line 13) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/assign_canonical_topic.rb
class Avo::Actions::AssignCanonicalTopic (line 1) | class Avo::Actions::AssignCanonicalTopic < Avo::BaseAction
method fields (line 7) | def fields
method handle (line 13) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/assign_canonical_user.rb
class Avo::Actions::AssignCanonicalUser (line 1) | class Avo::Actions::AssignCanonicalUser < Avo::BaseAction
method fields (line 4) | def fields
method handle (line 10) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/clear_user.rb
class Avo::Actions::ClearUser (line 1) | class Avo::Actions::ClearUser < Avo::BaseAction
method handle (line 5) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/enhance_transcript.rb
class Avo::Actions::EnhanceTranscript (line 1) | class Avo::Actions::EnhanceTranscript < Avo::BaseAction
method handle (line 3) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/extract_topics.rb
class Avo::Actions::ExtractTopics (line 1) | class Avo::Actions::ExtractTopics < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/fetch_duration.rb
class Avo::Actions::FetchDuration (line 1) | class Avo::Actions::FetchDuration < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/find_topic_talks.rb
class Avo::Actions::FindTopicTalks (line 1) | class Avo::Actions::FindTopicTalks < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/geocode_record.rb
class Avo::Actions::GeocodeRecord (line 1) | class Avo::Actions::GeocodeRecord < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, records:, **args)
FILE: app/avo/actions/reject_topic.rb
class Avo::Actions::RejectTopic (line 1) | class Avo::Actions::RejectTopic < Avo::BaseAction
method handle (line 11) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/summarize.rb
class Avo::Actions::Summarize (line 1) | class Avo::Actions::Summarize < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/talk_index.rb
class Avo::Actions::TalkIndex (line 1) | class Avo::Actions::TalkIndex < Avo::BaseAction
method handle (line 5) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/talk_ingest.rb
class Avo::Actions::TalkIngest (line 1) | class Avo::Actions::TalkIngest < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/transcript.rb
class Avo::Actions::Transcript (line 1) | class Avo::Actions::Transcript < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/update_from_yml.rb
class Avo::Actions::UpdateFromYml (line 1) | class Avo::Actions::UpdateFromYml < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/actions/user_fetch_github.rb
class Avo::Actions::UserFetchGitHub (line 1) | class Avo::Actions::UserFetchGitHub < Avo::BaseAction
method handle (line 4) | def handle(query:, fields:, current_user:, resource:, **args)
FILE: app/avo/cards/duplicates_summary.rb
class Avo::Cards::DuplicatesSummary (line 1) | class Avo::Cards::DuplicatesSummary < Avo::Cards::PartialCard
FILE: app/avo/cards/reversed_name_duplicates_list.rb
class Avo::Cards::ReversedNameDuplicatesList (line 1) | class Avo::Cards::ReversedNameDuplicatesList < Avo::Cards::PartialCard
FILE: app/avo/cards/reversed_name_duplicates_metric.rb
class Avo::Cards::ReversedNameDuplicatesMetric (line 1) | class Avo::Cards::ReversedNameDuplicatesMetric < Avo::Cards::MetricCard
method query (line 7) | def query
FILE: app/avo/cards/same_name_duplicates_list.rb
class Avo::Cards::SameNameDuplicatesList (line 1) | class Avo::Cards::SameNameDuplicatesList < Avo::Cards::PartialCard
FILE: app/avo/cards/same_name_duplicates_metric.rb
class Avo::Cards::SameNameDuplicatesMetric (line 1) | class Avo::Cards::SameNameDuplicatesMetric < Avo::Cards::MetricCard
method query (line 7) | def query
FILE: app/avo/cards/suspicious_signals_breakdown.rb
class Avo::Cards::SuspiciousSignalsBreakdown (line 1) | class Avo::Cards::SuspiciousSignalsBreakdown < Avo::Cards::PartialCard
FILE: app/avo/cards/suspicious_summary.rb
class Avo::Cards::SuspiciousSummary (line 1) | class Avo::Cards::SuspiciousSummary < Avo::Cards::PartialCard
FILE: app/avo/cards/suspicious_users_list.rb
class Avo::Cards::SuspiciousUsersList (line 1) | class Avo::Cards::SuspiciousUsersList < Avo::Cards::PartialCard
FILE: app/avo/cards/suspicious_users_metric.rb
class Avo::Cards::SuspiciousUsersMetric (line 1) | class Avo::Cards::SuspiciousUsersMetric < Avo::Cards::MetricCard
method query (line 7) | def query
FILE: app/avo/cards/unavailable_videos_by_event.rb
class Avo::Cards::UnavailableVideosByEvent (line 1) | class Avo::Cards::UnavailableVideosByEvent < Avo::Cards::PartialCard
FILE: app/avo/cards/unavailable_videos_list.rb
class Avo::Cards::UnavailableVideosList (line 1) | class Avo::Cards::UnavailableVideosList < Avo::Cards::PartialCard
FILE: app/avo/cards/unavailable_videos_metric.rb
class Avo::Cards::UnavailableVideosMetric (line 1) | class Avo::Cards::UnavailableVideosMetric < Avo::Cards::MetricCard
method query (line 7) | def query
FILE: app/avo/cards/unavailable_videos_summary.rb
class Avo::Cards::UnavailableVideosSummary (line 1) | class Avo::Cards::UnavailableVideosSummary < Avo::Cards::PartialCard
FILE: app/avo/dashboards/default.rb
class Avo::Dashboards::Default (line 1) | class Avo::Dashboards::Default < Avo::Dashboards::BaseDashboard
method cards (line 7) | def cards
FILE: app/avo/dashboards/duplicates.rb
class Avo::Dashboards::Duplicates (line 1) | class Avo::Dashboards::Duplicates < Avo::Dashboards::BaseDashboard
method cards (line 7) | def cards
FILE: app/avo/dashboards/suspicious.rb
class Avo::Dashboards::Suspicious (line 1) | class Avo::Dashboards::Suspicious < Avo::Dashboards::BaseDashboard
method cards (line 7) | def cards
FILE: app/avo/dashboards/unavailable_videos.rb
class Avo::Dashboards::UnavailableVideos (line 1) | class Avo::Dashboards::UnavailableVideos < Avo::Dashboards::BaseDashboard
method cards (line 7) | def cards
FILE: app/avo/filters/aliasable_type.rb
class Avo::Filters::AliasableType (line 1) | class Avo::Filters::AliasableType < Avo::Filters::SelectFilter
method apply (line 4) | def apply(request, query, type)
method options (line 12) | def options
FILE: app/avo/filters/attended_as.rb
class Avo::Filters::AttendedAs (line 1) | class Avo::Filters::AttendedAs < Avo::Filters::SelectFilter
method apply (line 4) | def apply(request, query, attended_as)
method options (line 12) | def options
FILE: app/avo/filters/bio.rb
class Avo::Filters::Bio (line 1) | class Avo::Filters::Bio < Avo::Filters::TextFilter
method apply (line 5) | def apply(request, query, value)
FILE: app/avo/filters/bio_presence.rb
class Avo::Filters::BioPresence (line 1) | class Avo::Filters::BioPresence < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 13) | def options
FILE: app/avo/filters/canonical.rb
class Avo::Filters::Canonical (line 1) | class Avo::Filters::Canonical < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 17) | def options
FILE: app/avo/filters/enhanced_transcript.rb
class Avo::Filters::EnhancedTranscript (line 1) | class Avo::Filters::EnhancedTranscript < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 19) | def options
FILE: app/avo/filters/geocoded_presence.rb
class Avo::Filters::GeocodedPresence (line 1) | class Avo::Filters::GeocodedPresence < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 16) | def options
FILE: app/avo/filters/github.rb
class Avo::Filters::GitHub (line 1) | class Avo::Filters::GitHub < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 15) | def options
FILE: app/avo/filters/github_handle.rb
class Avo::Filters::GitHubHandle (line 1) | class Avo::Filters::GitHubHandle < Avo::Filters::TextFilter
method apply (line 4) | def apply(request, query, value)
FILE: app/avo/filters/github_handle_presence.rb
class Avo::Filters::GitHubHandlePresence (line 1) | class Avo::Filters::GitHubHandlePresence < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 15) | def options
FILE: app/avo/filters/has_duplicate.rb
class Avo::Filters::HasDuplicate (line 1) | class Avo::Filters::HasDuplicate < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 16) | def options
FILE: app/avo/filters/involvement_role.rb
class Avo::Filters::InvolvementRole (line 1) | class Avo::Filters::InvolvementRole < Avo::Filters::SelectFilter
method apply (line 4) | def apply(request, query, role)
method options (line 12) | def options
FILE: app/avo/filters/language.rb
class Avo::Filters::Language (line 1) | class Avo::Filters::Language < Avo::Filters::SelectFilter
method apply (line 8) | def apply(request, query, language)
method options (line 16) | def options
FILE: app/avo/filters/location_presence.rb
class Avo::Filters::LocationPresence (line 1) | class Avo::Filters::LocationPresence < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 16) | def options
FILE: app/avo/filters/name.rb
class Avo::Filters::Name (line 1) | class Avo::Filters::Name < Avo::Filters::TextFilter
method apply (line 5) | def apply(request, query, value)
FILE: app/avo/filters/provider.rb
class Avo::Filters::Provider (line 1) | class Avo::Filters::Provider < Avo::Filters::SelectFilter
method apply (line 4) | def apply(request, query, provider)
method options (line 12) | def options
FILE: app/avo/filters/published.rb
class Avo::Filters::Published (line 1) | class Avo::Filters::Published < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 14) | def options
FILE: app/avo/filters/raw_transcript.rb
class Avo::Filters::RawTranscript (line 1) | class Avo::Filters::RawTranscript < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 19) | def options
FILE: app/avo/filters/slug.rb
class Avo::Filters::Slug (line 1) | class Avo::Filters::Slug < Avo::Filters::TextFilter
method apply (line 5) | def apply(request, query, value)
FILE: app/avo/filters/summary.rb
class Avo::Filters::Summary (line 1) | class Avo::Filters::Summary < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 16) | def options
FILE: app/avo/filters/suspicious.rb
class Avo::Filters::Suspicious (line 1) | class Avo::Filters::Suspicious < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 10) | def options
FILE: app/avo/filters/talk_event.rb
class Avo::Filters::TalkEvent (line 1) | class Avo::Filters::TalkEvent < Avo::Filters::SelectFilter
method apply (line 4) | def apply(request, query, values)
method options (line 8) | def options
FILE: app/avo/filters/title.rb
class Avo::Filters::Title (line 1) | class Avo::Filters::Title < Avo::Filters::TextFilter
method apply (line 5) | def apply(request, query, value)
FILE: app/avo/filters/topic_talks.rb
class Avo::Filters::TopicTalks (line 1) | class Avo::Filters::TopicTalks < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 23) | def options
FILE: app/avo/filters/topics.rb
class Avo::Filters::Topics (line 1) | class Avo::Filters::Topics < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 19) | def options
FILE: app/avo/filters/video_availability.rb
class Avo::Filters::VideoAvailability (line 1) | class Avo::Filters::VideoAvailability < Avo::Filters::BooleanFilter
method apply (line 4) | def apply(request, query, values)
method options (line 24) | def options
FILE: app/avo/filters/video_provider.rb
class Avo::Filters::VideoProvider (line 1) | class Avo::Filters::VideoProvider < Avo::Filters::SelectFilter
method apply (line 8) | def apply(request, query, provider)
method options (line 16) | def options
FILE: app/avo/filters/without_talks.rb
class Avo::Filters::WithoutTalks (line 1) | class Avo::Filters::WithoutTalks < Avo::Filters::BooleanFilter
method apply (line 7) | def apply(request, query, values)
method options (line 15) | def options
FILE: app/avo/resources/alias.rb
class Avo::Resources::Alias (line 1) | class Avo::Resources::Alias < Avo::BaseResource
method filters (line 10) | def filters
method fields (line 14) | def fields
method aliasable_link (line 31) | def self.aliasable_link(app, record)
FILE: app/avo/resources/city.rb
class Avo::Resources::City (line 1) | class Avo::Resources::City < Avo::BaseResource
method fields (line 34) | def fields
method filters (line 48) | def filters
method country_options (line 52) | def country_options
FILE: app/avo/resources/connected_account.rb
class Avo::Resources::ConnectedAccount (line 11) | class Avo::Resources::ConnectedAccount < Avo::BaseResource
method fields (line 18) | def fields
method filters (line 31) | def filters
FILE: app/avo/resources/contributor.rb
class Avo::Resources::Contributor (line 1) | class Avo::Resources::Contributor < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/event.rb
class Avo::Resources::Event (line 1) | class Avo::Resources::Event < Avo::BaseResource
method fields (line 17) | def fields
method actions (line 44) | def actions
method filters (line 49) | def filters
method country_options (line 57) | def country_options
FILE: app/avo/resources/event_involvement.rb
class Avo::Resources::EventInvolvement (line 1) | class Avo::Resources::EventInvolvement < Avo::BaseResource
method fields (line 13) | def fields
method filters (line 22) | def filters
FILE: app/avo/resources/event_participation.rb
class Avo::Resources::EventParticipation (line 1) | class Avo::Resources::EventParticipation < Avo::BaseResource
method fields (line 11) | def fields
method filters (line 18) | def filters
FILE: app/avo/resources/event_series.rb
class Avo::Resources::EventSeries (line 1) | class Avo::Resources::EventSeries < Avo::BaseResource
method fields (line 17) | def fields
method filters (line 34) | def filters
FILE: app/avo/resources/favorite_user.rb
class Avo::Resources::FavoriteUser (line 1) | class Avo::Resources::FavoriteUser < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/geocode_result.rb
class Avo::Resources::GeocodeResult (line 1) | class Avo::Resources::GeocodeResult < Avo::BaseResource
method fields (line 9) | def fields
FILE: app/avo/resources/organization.rb
class Avo::Resources::Organization (line 1) | class Avo::Resources::Organization < Avo::BaseResource
method fields (line 19) | def fields
FILE: app/avo/resources/session.rb
class Avo::Resources::Session (line 1) | class Avo::Resources::Session < Avo::BaseResource
method fields (line 7) | def fields
FILE: app/avo/resources/sponsor.rb
class Avo::Resources::Sponsor (line 1) | class Avo::Resources::Sponsor < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/suggestion.rb
class Avo::Resources::Suggestion (line 1) | class Avo::Resources::Suggestion < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/talk.rb
class Avo::Resources::Talk (line 1) | class Avo::Resources::Talk < Avo::BaseResource
method fields (line 19) | def fields
method actions (line 86) | def actions
method filters (line 93) | def filters
FILE: app/avo/resources/talk_topic.rb
class Avo::Resources::TalkTopic (line 1) | class Avo::Resources::TalkTopic < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/talk_transcript.rb
class Avo::Resources::TalkTranscript (line 1) | class Avo::Resources::TalkTranscript < Avo::BaseResource
method fields (line 9) | def fields
method actions (line 18) | def actions
FILE: app/avo/resources/topic.rb
class Avo::Resources::Topic (line 1) | class Avo::Resources::Topic < Avo::BaseResource
method fields (line 19) | def fields
method actions (line 34) | def actions
method filters (line 41) | def filters
FILE: app/avo/resources/topic_gem.rb
class Avo::Resources::TopicGem (line 1) | class Avo::Resources::TopicGem < Avo::BaseResource
method name (line 10) | def self.name
method fields (line 14) | def fields
FILE: app/avo/resources/user.rb
class Avo::Resources::User (line 1) | class Avo::Resources::User < Avo::BaseResource
method fields (line 22) | def fields
method filters (line 62) | def filters
method actions (line 74) | def actions
FILE: app/avo/resources/user_talk.rb
class Avo::Resources::UserTalk (line 1) | class Avo::Resources::UserTalk < Avo::BaseResource
method fields (line 8) | def fields
FILE: app/avo/resources/watch_list.rb
class Avo::Resources::WatchList (line 1) | class Avo::Resources::WatchList < Avo::BaseResource
method fields (line 5) | def fields
FILE: app/avo/resources/watch_list_talk.rb
class Avo::Resources::WatchListTalk (line 1) | class Avo::Resources::WatchListTalk < Avo::BaseResource
method fields (line 4) | def fields
FILE: app/channels/application_cable/channel.rb
type ApplicationCable (line 1) | module ApplicationCable
class Channel (line 2) | class Channel < ActionCable::Channel::Base
FILE: app/channels/application_cable/connection.rb
type ApplicationCable (line 1) | module ApplicationCable
class Connection (line 2) | class Connection < ActionCable::Connection::Base
FILE: app/clients/application_client.rb
class ApplicationClient (line 1) | class ApplicationClient
class Error (line 2) | class Error < StandardError; end
class Forbidden (line 4) | class Forbidden < Error; end
class Unauthorized (line 6) | class Unauthorized < Error; end
class RateLimit (line 8) | class RateLimit < Error; end
class NotFound (line 10) | class NotFound < Error; end
class InternalError (line 12) | class InternalError < Error; end
method initialize (line 17) | def initialize(token: nil)
method default_headers (line 21) | def default_headers
method content_type (line 28) | def content_type
method authorization_header (line 32) | def authorization_header
method default_query_params (line 36) | def default_query_params
method get (line 40) | def get(path, headers: {}, query: nil)
method post (line 44) | def post(path, headers: {}, query: nil, body: nil, form_data: nil)
method patch (line 55) | def patch(path, headers: {}, query: nil, body: nil, form_data: nil)
method put (line 66) | def put(path, headers: {}, query: nil, body: nil, form_data: nil)
method delete (line 77) | def delete(path, headers: {}, query: nil, body: nil)
method base_uri (line 81) | def base_uri
method make_request (line 87) | def make_request(klass:, path:, headers: {}, body: nil, query: nil, fo...
method handle_response (line 114) | def handle_response(response)
method build_body (line 133) | def build_body(body)
class Response (line 142) | class Response
method initialize (line 155) | def initialize(original_response)
method headers (line 159) | def headers
method content_type (line 163) | def content_type
method parsed_body (line 167) | def parsed_body
FILE: app/clients/blue_sky.rb
function build (line 9) | def self.build(host = self.host, **options)
function profile_metadata (line 17) | def profile_metadata(handle)
FILE: app/clients/github/client.rb
type GitHub (line 1) | module GitHub
class Client (line 2) | class Client < ApplicationClient
method initialize (line 5) | def initialize
method authorization_header (line 11) | def authorization_header
method content_type (line 15) | def content_type
method token (line 19) | def token
FILE: app/clients/github/contributors_client.rb
type GitHub (line 1) | module GitHub
class ContributorsClient (line 2) | class ContributorsClient < Client
method fetch_all (line 6) | def fetch_all
method fetch_contributors (line 13) | def fetch_contributors
method enrich_with_user_data (line 31) | def enrich_with_user_data(contributors)
FILE: app/clients/github/user_client.rb
type GitHub (line 1) | module GitHub
class UserClient (line 2) | class UserClient < GitHub::Client
method profile (line 3) | def profile(username)
method social_accounts (line 7) | def social_accounts(username)
method search (line 11) | def search(q, per_page: 10, page: 1)
method emails (line 15) | def emails
FILE: app/clients/ruby_conferences/client.rb
type RubyConferences (line 1) | module RubyConferences
class Client (line 2) | class Client < ApplicationClient
method conferences (line 5) | def conferences
method conferences_cached (line 9) | def conferences_cached
method content_type (line 17) | def content_type
FILE: app/clients/speakerdeck/client.rb
type Speakerdeck (line 3) | module Speakerdeck
class Client (line 4) | class Client < ApplicationClient
method oembed (line 7) | def oembed(url)
method normalize_url (line 14) | def normalize_url(url)
method authorization_header (line 22) | def authorization_header
FILE: app/clients/youtube/channels.rb
type YouTube (line 3) | module YouTube
class Channels (line 4) | class Channels < YouTube::Client
method id_by_name (line 5) | def id_by_name(channel_name:)
method default_headers (line 12) | def default_headers
method fallback_using_scrapping (line 18) | def fallback_using_scrapping(channel_name:)
FILE: app/clients/youtube/client.rb
type YouTube (line 1) | module YouTube
class Client (line 2) | class Client < ApplicationClient
method all_items (line 7) | def all_items(path, query: {})
method default_headers (line 20) | def default_headers
method token (line 26) | def token
FILE: app/clients/youtube/playlist_items.rb
type YouTube (line 1) | module YouTube
class PlaylistItems (line 2) | class PlaylistItems < YouTube::Client
method all (line 3) | def all(playlist_id:)
FILE: app/clients/youtube/playlists.rb
type YouTube (line 1) | module YouTube
class Playlists (line 2) | class Playlists < YouTube::Client
method all (line 5) | def all(channel_id:, title_matcher: nil)
method string_to_regex (line 34) | def string_to_regex(str)
FILE: app/clients/youtube/thumbnail.rb
type YouTube (line 3) | module YouTube
class Thumbnail (line 4) | class Thumbnail
method initialize (line 18) | def initialize(video_id)
method best_url (line 22) | def best_url
method best_url_for (line 26) | def best_url_for(talk_size)
method best_url_from (line 33) | def best_url_from(starting_size)
method url_for (line 55) | def url_for(size)
method default? (line 59) | def default?(url)
method valid_aspect_ratio? (line 66) | def valid_aspect_ratio?(url)
method content_length (line 86) | def content_length(url)
FILE: app/clients/youtube/transcript.rb
type YouTube (line 3) | module YouTube
class Transcript (line 4) | class Transcript
method get (line 7) | def get(video_id)
method get (line 31) | def self.get(video_id)
method encode_message (line 37) | def encode_message(message, typedef)
method get_base64_protobuf (line 42) | def get_base64_protobuf(message, typedef)
FILE: app/clients/youtube/video.rb
type YouTube (line 1) | module YouTube
class Video (line 2) | class Video < Client
method available? (line 3) | def available?(video_id)
method get_statistics (line 14) | def get_statistics(video_id)
method duration (line 33) | def duration(video_id)
FILE: app/components/application_component.rb
class ApplicationComponent (line 3) | class ApplicationComponent < ViewComponent::Base
method initialize (line 9) | def initialize(*, **options)
method render? (line 15) | def render?
FILE: app/components/events/upcoming_event_banner_component.rb
class Events::UpcomingEventBannerComponent (line 3) | class Events::UpcomingEventBannerComponent < ApplicationComponent
method render? (line 7) | def render?
method upcoming_event (line 11) | def upcoming_event
method background_style (line 19) | def background_style
method background_color (line 26) | def background_color
method text_color (line 31) | def text_color
method should_show_banner? (line 37) | def should_show_banner?
FILE: app/components/hover_card/base_component.rb
class HoverCard::BaseComponent (line 3) | class HoverCard::BaseComponent < ViewComponent::Base
method initialize (line 8) | def initialize(record:, avatar_size: :sm)
method frame_id (line 13) | def frame_id
method hover_card_url (line 17) | def hover_card_url
method render? (line 21) | def render?
FILE: app/components/hover_card/event_component.rb
class HoverCard::EventComponent (line 3) | class HoverCard::EventComponent < HoverCard::BaseComponent
method hover_card_url (line 4) | def hover_card_url
FILE: app/components/hover_card/user_component.rb
class HoverCard::UserComponent (line 3) | class HoverCard::UserComponent < HoverCard::BaseComponent
method hover_card_url (line 4) | def hover_card_url
method render? (line 8) | def render?
FILE: app/components/tito/button_component.rb
class Tito::ButtonComponent (line 3) | class Tito::ButtonComponent < ApplicationComponent
method render? (line 7) | def render?
method classes (line 11) | def classes
FILE: app/components/tito/widget_component.rb
class Tito::WidgetComponent (line 3) | class Tito::WidgetComponent < ApplicationComponent
method render? (line 7) | def render?
method event_slug (line 11) | def event_slug
FILE: app/components/ui/avatar_component.rb
class Ui::AvatarComponent (line 3) | class Ui::AvatarComponent < ApplicationComponent
method show_hover_card? (line 40) | def show_hover_card?
method show_link? (line 44) | def show_link?
method link_path (line 48) | def link_path
method avatar_classes (line 58) | def avatar_classes
method initials (line 68) | def initials
method has_custom_avatar? (line 73) | def has_custom_avatar?
method avatar_url_for_size (line 77) | def avatar_url_for_size
method image_size (line 83) | def image_size
method text_size (line 87) | def text_size
method kind_class (line 91) | def kind_class
method suspicious? (line 95) | def suspicious?
method show_custom_avatar? (line 99) | def show_custom_avatar?
FILE: app/components/ui/avatar_group_component.rb
class Ui::AvatarGroupComponent (line 3) | class Ui::AvatarGroupComponent < ApplicationComponent
method visible_avatarables (line 19) | def visible_avatarables
method remaining_count (line 23) | def remaining_count
method size_class (line 27) | def size_class
FILE: app/components/ui/badge_component.rb
class Ui::BadgeComponent (line 3) | class Ui::BadgeComponent < ApplicationComponent
method call (line 28) | def call
method classes (line 36) | def classes
method component_classes (line 40) | def component_classes
method content (line 49) | def content
FILE: app/components/ui/button_component.rb
class Ui::ButtonComponent (line 3) | class Ui::ButtonComponent < ApplicationComponent
method call (line 32) | def call
method classes (line 45) | def classes
method component_classes (line 49) | def component_classes
method content (line 60) | def content
method button_kind (line 64) | def button_kind
method default_method? (line 71) | def default_method?
FILE: app/components/ui/divider_component.rb
class Ui::DividerComponent (line 3) | class Ui::DividerComponent < ApplicationComponent
method call (line 12) | def call
method classes (line 20) | def classes
method component_classes (line 24) | def component_classes
method content (line 31) | def content
FILE: app/components/ui/dropdown_component.rb
class Ui::DropdownComponent (line 3) | class Ui::DropdownComponent < ApplicationComponent
method before_render (line 41) | def before_render
method classes (line 45) | def classes
method component_classes (line 49) | def component_classes
method content_classes (line 59) | def content_classes
FILE: app/components/ui/modal_component.rb
class Ui::ModalComponent (line 3) | class Ui::ModalComponent < ApplicationComponent
method before_render (line 25) | def before_render
method classes (line 37) | def classes
method component_classes (line 41) | def component_classes
method size_class (line 48) | def size_class
method content_classes (line 52) | def content_classes
FILE: app/components/ui/stamp_component.rb
class Ui::StampComponent (line 3) | class Ui::StampComponent < ApplicationComponent
method kind (line 19) | def kind
method clickable? (line 31) | def clickable?
method url (line 35) | def url
method link_attributes (line 48) | def link_attributes
method classes (line 52) | def classes
method image_style (line 56) | def image_style
method component_classes (line 64) | def component_classes
FILE: app/controllers/admin/suggestions_controller.rb
type Admin (line 1) | module Admin
class SuggestionsController (line 2) | class SuggestionsController < ApplicationController
method index (line 5) | def index
method update (line 9) | def update
method destroy (line 15) | def destroy
FILE: app/controllers/analytics/dashboards_controller.rb
class Analytics::DashboardsController (line 1) | class Analytics::DashboardsController < ApplicationController
method daily_visits (line 5) | def daily_visits
method daily_page_views (line 9) | def daily_page_views
method monthly_visits (line 13) | def monthly_visits
method monthly_page_views (line 17) | def monthly_page_views
method yearly_conferences (line 21) | def yearly_conferences
method yearly_talks (line 29) | def yearly_talks
method top_referrers (line 33) | def top_referrers
method top_landing_pages (line 45) | def top_landing_pages
method top_searches (line 66) | def top_searches
method admin_required (line 83) | def admin_required
FILE: app/controllers/announcements_controller.rb
class AnnouncementsController (line 1) | class AnnouncementsController < ApplicationController
method index (line 6) | def index
method show (line 14) | def show
method feed (line 24) | def feed
method announcements (line 37) | def announcements
method page_number (line 41) | def page_number
method can_view_draft_articles? (line 45) | def can_view_draft_articles?
method permitted_params (line 50) | def permitted_params
method set_announcement_meta_tags (line 54) | def set_announcement_meta_tags
FILE: app/controllers/api/v1/embed/base_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class BaseController (line 6) | class BaseController < ActionController::API
method preflight (line 12) | def preflight
method set_cors_headers (line 18) | def set_cors_headers
method not_found (line 25) | def not_found
method internal_error (line 29) | def internal_error(exception)
method build_url (line 35) | def build_url(path)
method avatar_url (line 39) | def avatar_url(user)
method full_url (line 49) | def full_url(path)
method event_avatar_url (line 55) | def event_avatar_url(event)
method event_banner_url (line 63) | def event_banner_url(event)
FILE: app/controllers/api/v1/embed/events_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class EventsController (line 6) | class EventsController < BaseController
method index (line 7) | def index
method show (line 30) | def show
method participants (line 73) | def participants
method participant_json (line 102) | def participant_json(user)
method event_summary_json (line 110) | def event_summary_json(event)
FILE: app/controllers/api/v1/embed/profiles_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class ProfilesController (line 6) | class ProfilesController < BaseController
method index (line 7) | def index
method show (line 17) | def show
FILE: app/controllers/api/v1/embed/speakers_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class SpeakersController (line 6) | class SpeakersController < BaseController
method index (line 7) | def index
method show (line 16) | def show
FILE: app/controllers/api/v1/embed/stamps_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class StampsController (line 6) | class StampsController < BaseController
method show (line 7) | def show
method stamp_json (line 28) | def stamp_json(stamp)
method grouped_stamps (line 48) | def grouped_stamps(stamps)
method stamp_image_url (line 60) | def stamp_image_url(stamp)
FILE: app/controllers/api/v1/embed/stickers_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class StickersController (line 6) | class StickersController < BaseController
method show (line 7) | def show
method sticker_image_url (line 41) | def sticker_image_url(sticker)
FILE: app/controllers/api/v1/embed/talks_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class TalksController (line 6) | class TalksController < BaseController
method index (line 7) | def index
method show (line 16) | def show
FILE: app/controllers/api/v1/embed/topics_controller.rb
type Api (line 3) | module Api
type V1 (line 4) | module V1
type Embed (line 5) | module Embed
class TopicsController (line 6) | class TopicsController < BaseController
method show (line 7) | def show
FILE: app/controllers/application_controller.rb
class ApplicationController (line 1) | class ApplicationController < ActionController::Base
method default_watch_list (line 10) | def default_watch_list
method redirect_to_ruby_events (line 16) | def redirect_to_ruby_events
method sign_in (line 27) | def sign_in(user)
FILE: app/controllers/avo/aliases_controller.rb
class Avo::AliasesController (line 3) | class Avo::AliasesController < Avo::ResourcesController
FILE: app/controllers/avo/cities_controller.rb
class Avo::CitiesController (line 3) | class Avo::CitiesController < Avo::ResourcesController
FILE: app/controllers/avo/connected_accounts_controller.rb
class Avo::ConnectedAccountsController (line 3) | class Avo::ConnectedAccountsController < Avo::ResourcesController
FILE: app/controllers/avo/contributors_controller.rb
class Avo::ContributorsController (line 3) | class Avo::ContributorsController < Avo::ResourcesController
FILE: app/controllers/avo/event_involvements_controller.rb
class Avo::EventInvolvementsController (line 3) | class Avo::EventInvolvementsController < Avo::ResourcesController
FILE: app/controllers/avo/event_participations_controller.rb
class Avo::EventParticipationsController (line 3) | class Avo::EventParticipationsController < Avo::ResourcesController
FILE: app/controllers/avo/event_series_controller.rb
class Avo::EventSeriesController (line 3) | class Avo::EventSeriesController < Avo::ResourcesController
FILE: app/controllers/avo/events_controller.rb
class Avo::EventsController (line 3) | class Avo::EventsController < Avo::ResourcesController
FILE: app/controllers/avo/favorite_users_controller.rb
class Avo::FavoriteUsersController (line 3) | class Avo::FavoriteUsersController < Avo::ResourcesController
FILE: app/controllers/avo/geocode_results_controller.rb
class Avo::GeocodeResultsController (line 3) | class Avo::GeocodeResultsController < Avo::ResourcesController
FILE: app/controllers/avo/organizations_controller.rb
class Avo::OrganizationsController (line 3) | class Avo::OrganizationsController < Avo::ResourcesController
FILE: app/controllers/avo/sessions_controller.rb
class Avo::SessionsController (line 3) | class Avo::SessionsController < Avo::ResourcesController
FILE: app/controllers/avo/speaker_talks_controller.rb
class Avo::SpeakerTalksController (line 3) | class Avo::SpeakerTalksController < Avo::ResourcesController
FILE: app/controllers/avo/speakers_controller.rb
class Avo::SpeakersController (line 3) | class Avo::SpeakersController < Avo::ResourcesController
FILE: app/controllers/avo/sponsors_controller.rb
class Avo::SponsorsController (line 3) | class Avo::SponsorsController < Avo::ResourcesController
FILE: app/controllers/avo/suggestions_controller.rb
class Avo::SuggestionsController (line 3) | class Avo::SuggestionsController < Avo::ResourcesController
FILE: app/controllers/avo/talk_topics_controller.rb
class Avo::TalkTopicsController (line 3) | class Avo::TalkTopicsController < Avo::ResourcesController
FILE: app/controllers/avo/talk_transcripts_controller.rb
class Avo::TalkTranscriptsController (line 3) | class Avo::TalkTranscriptsController < Avo::ResourcesController
FILE: app/controllers/avo/talks_controller.rb
class Avo::TalksController (line 3) | class Avo::TalksController < Avo::ResourcesController
FILE: app/controllers/avo/topic_gems_controller.rb
class Avo::TopicGemsController (line 3) | class Avo::TopicGemsController < Avo::ResourcesController
FILE: app/controllers/avo/topics_controller.rb
class Avo::TopicsController (line 3) | class Avo::TopicsController < Avo::ResourcesController
FILE: app/controllers/avo/user_talks_controller.rb
class Avo::UserTalksController (line 3) | class Avo::UserTalksController < Avo::ResourcesController
FILE: app/controllers/avo/users_controller.rb
class Avo::UsersController (line 3) | class Avo::UsersController < Avo::ResourcesController
FILE: app/controllers/avo/watch_list_talks_controller.rb
class Avo::WatchListTalksController (line 3) | class Avo::WatchListTalksController < Avo::ResourcesController
FILE: app/controllers/avo/watch_lists_controller.rb
class Avo::WatchListsController (line 3) | class Avo::WatchListsController < Avo::ResourcesController
FILE: app/controllers/browse_controller.rb
class BrowseController (line 1) | class BrowseController < ApplicationController
method index (line 16) | def index
method show (line 22) | def show
method instance_variables_for_section (line 32) | def instance_variables_for_section(section_name)
method cache_key (line 66) | def cache_key(name)
method load_featured_events (line 70) | def load_featured_events
method load_event_rows (line 78) | def load_event_rows
method load_continue_watching (line 96) | def load_continue_watching
method load_newest_talks (line 106) | def load_newest_talks
method load_recently_published (line 116) | def load_recently_published
method load_trending (line 126) | def load_trending
method load_for_you (line 136) | def load_for_you
method load_from_bookmarks (line 142) | def load_from_bookmarks
method load_favorite_rubyists (line 152) | def load_favorite_rubyists
method load_events_attended (line 166) | def load_events_attended
method load_unwatched_attended (line 179) | def load_unwatched_attended
method load_favorite_speakers (line 195) | def load_favorite_speakers
method load_popular (line 218) | def load_popular
method load_popular_youtube (line 226) | def load_popular_youtube
method load_most_bookmarked (line 234) | def load_most_bookmarked
method load_quick_watches (line 242) | def load_quick_watches
method load_deep_dives (line 250) | def load_deep_dives
method load_hidden_gems (line 258) | def load_hidden_gems
method load_evergreen (line 266) | def load_evergreen
method load_beginner_friendly (line 274) | def load_beginner_friendly
method load_mind_blowing (line 282) | def load_mind_blowing
method load_inspiring (line 290) | def load_inspiring
method load_most_liked (line 298) | def load_most_liked
method load_recommended_community (line 306) | def load_recommended_community
method load_popular_topics (line 314) | def load_popular_topics
method load_talk_kinds (line 322) | def load_talk_kinds
method load_topic_rows (line 328) | def load_topic_rows
method load_language_rows (line 346) | def load_language_rows
method featured_events_query (line 378) | def featured_events_query
method recently_published_query (line 394) | def recently_published_query
method newest_talks_query (line 401) | def newest_talks_query
method trending_talks_query (line 408) | def trending_talks_query
method popular_talks_query (line 417) | def popular_talks_query
method popular_on_youtube_query (line 425) | def popular_on_youtube_query
method most_bookmarked_query (line 432) | def most_bookmarked_query
method quick_watches_query (line 440) | def quick_watches_query
method deep_dives_query (line 447) | def deep_dives_query
method hidden_gems_query (line 454) | def hidden_gems_query
method evergreen_talks_query (line 464) | def evergreen_talks_query
method beginner_friendly_query (line 474) | def beginner_friendly_query
method mind_blowing_query (line 484) | def mind_blowing_query
method inspiring_talks_query (line 494) | def inspiring_talks_query
method most_liked_query (line 505) | def most_liked_query
method recommended_by_community_query (line 515) | def recommended_by_community_query
method popular_topics_query (line 525) | def popular_topics_query
method talk_kinds_query (line 534) | def talk_kinds_query
method build_topic_sections (line 541) | def build_topic_sections
method build_event_sections (line 557) | def build_event_sections
FILE: app/controllers/cfp_controller.rb
class CFPController (line 1) | class CFPController < ApplicationController
method index (line 5) | def index
FILE: app/controllers/cities_controller.rb
class CitiesController (line 1) | class CitiesController < ApplicationController
method index (line 7) | def index
method show (line 11) | def show
method show_by_country (line 22) | def show_by_country
method show_with_state (line 43) | def show_with_state
method load_city_data (line 73) | def load_city_data
method filter_events_by_time (line 121) | def filter_events_by_time(events)
method continent_upcoming_events (line 125) | def continent_upcoming_events(exclude_country_codes: [])
FILE: app/controllers/concerns/analytics.rb
type Analytics (line 1) | module Analytics
function disable_analytics (line 9) | def disable_analytics
function analytics_disabled? (line 16) | def analytics_disabled?
function track_action (line 20) | def track_action
FILE: app/controllers/concerns/authenticable.rb
type Authenticable (line 1) | module Authenticable
function signed_in? (line 11) | def signed_in?
function signed_in (line 15) | def signed_in(&block)
function signed_out (line 19) | def signed_out(&block)
function authenticate_user! (line 25) | def authenticate_user!
function set_current_request_details (line 29) | def set_current_request_details
FILE: app/controllers/concerns/event_data.rb
type EventData (line 1) | module EventData
function set_event (line 6) | def set_event
function set_event_meta_tags (line 11) | def set_event_meta_tags
function set_participation (line 15) | def set_participation
function set_participants (line 19) | def set_participants
FILE: app/controllers/concerns/event_map_markers.rb
type EventMapMarkers (line 3) | module EventMapMarkers
function event_map_markers (line 8) | def event_map_markers(events = Event.includes(:series).geocoded)
function event_marker_data (line 24) | def event_marker_data(event)
FILE: app/controllers/concerns/favorite_users.rb
type FavoriteUsers (line 1) | module FavoriteUsers
function set_favorite_users (line 4) | def set_favorite_users
FILE: app/controllers/concerns/geo_map_layers.rb
type GeoMapLayers (line 3) | module GeoMapLayers
function build_sidebar_geo_layers (line 8) | def build_sidebar_geo_layers(events, include_broader_scope: true)
function add_broader_layers (line 30) | def add_broader_layers(layers, current_events)
function add_nearby_layer (line 45) | def add_nearby_layer(layers, city_events)
function add_state_layer (line 54) | def add_state_layer(layers)
function add_country_layer (line 60) | def add_country_layer(layers)
function add_continent_layer (line 66) | def add_continent_layer(layers)
function maybe_add_layer (line 72) | def maybe_add_layer(layers, id:, label:, emoji:, events:, bounds: nil)
function build_layer (line 81) | def build_layer(id:, label:, emoji:, events: nil, markers: nil, bounds...
function build_city_pin (line 95) | def build_city_pin(location)
function set_first_visible_layer (line 101) | def set_first_visible_layer(layers)
function filter_events_by_time (line 111) | def filter_events_by_time(events)
FILE: app/controllers/concerns/location_events.rb
type LocationEvents (line 3) | module LocationEvents
function location_events (line 8) | def location_events
function upcoming_events (line 12) | def upcoming_events
function past_events (line 16) | def past_events
function country_upcoming_events (line 20) | def country_upcoming_events(exclude_ids: [])
function continent_upcoming_events (line 29) | def continent_upcoming_events(exclude_country_codes: [])
FILE: app/controllers/concerns/metadata.rb
type Metadata (line 3) | module Metadata
function set_default_meta_tags (line 17) | def set_default_meta_tags
FILE: app/controllers/concerns/profile_data.rb
type ProfileData (line 1) | module ProfileData
function load_common_data (line 16) | def load_common_data
function set_user (line 30) | def set_user
function set_favorite_user (line 49) | def set_favorite_user
function set_mutual_events (line 53) | def set_mutual_events
function set_user_favorites (line 61) | def set_user_favorites
function user_kind (line 67) | def user_kind
FILE: app/controllers/concerns/remote_modal.rb
type RemoteModal (line 1) | module RemoteModal
function respond_with_remote_modal (line 11) | def respond_with_remote_modal(options = {})
function enable_remote_modal (line 19) | def enable_remote_modal
function modal_options (line 23) | def modal_options
function set_modal_options (line 27) | def set_modal_options(options)
function define_layout (line 31) | def define_layout
FILE: app/controllers/concerns/spotlight_search.rb
type SpotlightSearch (line 3) | module SpotlightSearch
function search_backend_class (line 12) | def search_backend_class
function search_backend (line 19) | def search_backend
FILE: app/controllers/concerns/turbo/force_response.rb
type Turbo (line 3) | module Turbo
type ForceResponse (line 4) | module ForceResponse
function force_frame_response (line 8) | def force_frame_response(options = {})
function force_stream_response (line 12) | def force_stream_response(options = {})
function force_frame_response (line 17) | def force_frame_response
function force_stream_response (line 23) | def force_stream_response
FILE: app/controllers/concerns/watched_talks.rb
type WatchedTalks (line 1) | module WatchedTalks
function user_watched_talks (line 10) | def user_watched_talks
FILE: app/controllers/continents/base_controller.rb
class Continents::BaseController (line 3) | class Continents::BaseController < ApplicationController
method set_continent (line 12) | def set_continent
method continent_events (line 18) | def continent_events
method upcoming_events (line 22) | def upcoming_events
method past_events (line 26) | def past_events
FILE: app/controllers/continents/countries_controller.rb
class Continents::CountriesController (line 3) | class Continents::CountriesController < Continents::BaseController
method index (line 4) | def index
FILE: app/controllers/continents_controller.rb
class ContinentsController (line 3) | class ContinentsController < ApplicationController
method index (line 9) | def index
method show (line 28) | def show
FILE: app/controllers/contributions_controller.rb
class ContributionsController (line 1) | class ContributionsController < ApplicationController
method index (line 10) | def index
method show (line 48) | def show
method speakers_without_github (line 58) | def speakers_without_github
method talks_without_slides (line 63) | def talks_without_slides
method events_without_videos (line 68) | def events_without_videos
method events_without_location (line 76) | def events_without_location
method events_without_dates (line 80) | def events_without_dates
method talks_dates_out_of_bounds (line 84) | def talks_dates_out_of_bounds
method missing_videos_cue (line 98) | def missing_videos_cue
FILE: app/controllers/coordinates_controller.rb
class CoordinatesController (line 3) | class CoordinatesController < ApplicationController
method index (line 8) | def index
method show (line 22) | def show
method load_location_data (line 35) | def load_location_data
method build_geo_layers (line 52) | def build_geo_layers(events)
FILE: app/controllers/countries/base_controller.rb
class Countries::BaseController (line 1) | class Countries::BaseController < ApplicationController
method set_country (line 8) | def set_country
method country_events (line 13) | def country_events
method event_sort_date (line 17) | def event_sort_date(event)
FILE: app/controllers/countries/cities_controller.rb
class Countries::CitiesController (line 1) | class Countries::CitiesController < Countries::BaseController
method index (line 2) | def index
FILE: app/controllers/countries_controller.rb
class CountriesController (line 1) | class CountriesController < ApplicationController
method index (line 7) | def index
method show (line 30) | def show
method filter_events_by_time (line 68) | def filter_events_by_time(events)
method find_nearby_users (line 72) | def find_nearby_users(country)
method continent_upcoming_events (line 99) | def continent_upcoming_events(exclude_country_codes: [])
FILE: app/controllers/event_participations_controller.rb
class EventParticipationsController (line 1) | class EventParticipationsController < ApplicationController
method create (line 10) | def create
method destroy (line 29) | def destroy
method participation_params (line 43) | def participation_params
FILE: app/controllers/events/archive_controller.rb
class Events::ArchiveController (line 1) | class Events::ArchiveController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/events/attendances_controller.rb
class Events::AttendancesController (line 1) | class Events::AttendancesController < ApplicationController
method index (line 6) | def index
method show (line 30) | def show
method set_event (line 51) | def set_event
FILE: app/controllers/events/cfp_controller.rb
class Events::CFPController (line 1) | class Events::CFPController < ApplicationController
method index (line 5) | def index
method set_event (line 11) | def set_event
FILE: app/controllers/events/collectibles_controller.rb
class Events::CollectiblesController (line 1) | class Events::CollectiblesController < ApplicationController
method index (line 6) | def index
method set_event (line 13) | def set_event
FILE: app/controllers/events/events_controller.rb
class Events::EventsController (line 1) | class Events::EventsController < ApplicationController
method index (line 5) | def index
method show (line 9) | def show
method set_event (line 15) | def set_event
FILE: app/controllers/events/involvements_controller.rb
class Events::InvolvementsController (line 1) | class Events::InvolvementsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/events/meetups_controller.rb
class Events::MeetupsController (line 1) | class Events::MeetupsController < ApplicationController
method index (line 5) | def index
FILE: app/controllers/events/participants_controller.rb
class Events::ParticipantsController (line 1) | class Events::ParticipantsController < ApplicationController
method index (line 11) | def index
FILE: app/controllers/events/past_controller.rb
class Events::PastController (line 1) | class Events::PastController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/events/related_talks_controller.rb
class Events::RelatedTalksController (line 1) | class Events::RelatedTalksController < ApplicationController
method index (line 8) | def index
method set_event (line 15) | def set_event
method set_user_favorites (line 20) | def set_user_favorites
FILE: app/controllers/events/schedules_controller.rb
class Events::SchedulesController (line 1) | class Events::SchedulesController < ApplicationController
method index (line 5) | def index
method show (line 15) | def show
method set_event (line 23) | def set_event
method set_talks (line 37) | def set_talks(day)
FILE: app/controllers/events/series_controller.rb
class Events::SeriesController (line 1) | class Events::SeriesController < ApplicationController
method index (line 9) | def index
method show (line 14) | def show
method reimport (line 29) | def reimport
method reindex (line 41) | def reindex
method set_event_series (line 56) | def set_event_series
method require_admin! (line 65) | def require_admin!
FILE: app/controllers/events/speakers_controller.rb
class Events::SpeakersController (line 1) | class Events::SpeakersController < ApplicationController
method index (line 5) | def index
method set_event (line 27) | def set_event
FILE: app/controllers/events/sponsors_controller.rb
class Events::SponsorsController (line 1) | class Events::SponsorsController < ApplicationController
method index (line 6) | def index
method set_event (line 12) | def set_event
FILE: app/controllers/events/talks_controller.rb
class Events::TalksController (line 1) | class Events::TalksController < ApplicationController
method index (line 8) | def index
method set_event (line 14) | def set_event
method set_user_favorites (line 21) | def set_user_favorites
FILE: app/controllers/events/tickets_controller.rb
class Events::TicketsController (line 1) | class Events::TicketsController < ApplicationController
method show (line 5) | def show
method set_event (line 10) | def set_event
FILE: app/controllers/events/todos_controller.rb
class Events::TodosController (line 1) | class Events::TodosController < ApplicationController
method index (line 5) | def index
method set_event (line 13) | def set_event
FILE: app/controllers/events/upcoming_controller.rb
class Events::UpcomingController (line 1) | class Events::UpcomingController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/events/venues_controller.rb
class Events::VenuesController (line 1) | class Events::VenuesController < ApplicationController
method show (line 5) | def show
method set_event (line 16) | def set_event
FILE: app/controllers/events/videos_controller.rb
class Events::VideosController (line 1) | class Events::VideosController < ApplicationController
method index (line 8) | def index
method set_event (line 15) | def set_event
method set_user_favorites (line 20) | def set_user_favorites
FILE: app/controllers/events/years_controller.rb
type Events (line 1) | module Events
class YearsController (line 2) | class YearsController < ApplicationController
method index (line 5) | def index
FILE: app/controllers/events_controller.rb
class EventsController (line 1) | class EventsController < ApplicationController
method index (line 11) | def index
method show (line 18) | def show
method edit (line 41) | def edit
method update (line 45) | def update
method reimport (line 56) | def reimport
method reindex (line 68) | def reindex
method set_event (line 80) | def set_event
method event_params (line 92) | def event_params
method set_user_favorites (line 96) | def set_user_favorites
method require_admin! (line 102) | def require_admin!
FILE: app/controllers/favorite_users_controller.rb
class FavoriteUsersController (line 1) | class FavoriteUsersController < ApplicationController
method index (line 5) | def index
method create (line 12) | def create
method update (line 26) | def update
method destroy (line 43) | def destroy
method set_favorite_user (line 54) | def set_favorite_user
method favorite_user_params (line 59) | def favorite_user_params
FILE: app/controllers/gems_controller.rb
class GemsController (line 1) | class GemsController < ApplicationController
method index (line 9) | def index
method show (line 25) | def show
method talks (line 34) | def talks
method set_gem (line 49) | def set_gem
method set_user_favorites (line 54) | def set_user_favorites
method page_number (line 60) | def page_number
FILE: app/controllers/hotwire/native/v1/android/path_configurations_controller.rb
class Hotwire::Native::V1::Android::PathConfigurationsController (line 1) | class Hotwire::Native::V1::Android::PathConfigurationsController < Actio...
method show (line 2) | def show
FILE: app/controllers/hotwire/native/v1/ios/path_configurations_controller.rb
class Hotwire::Native::V1::IOS::PathConfigurationsController (line 1) | class Hotwire::Native::V1::IOS::PathConfigurationsController < ActionCon...
method show (line 2) | def show
FILE: app/controllers/hover_cards/events_controller.rb
class HoverCards::EventsController (line 3) | class HoverCards::EventsController < ApplicationController
method show (line 6) | def show
FILE: app/controllers/hover_cards/users_controller.rb
class HoverCards::UsersController (line 3) | class HoverCards::UsersController < ApplicationController
method show (line 8) | def show
FILE: app/controllers/leaderboard_controller.rb
class LeaderboardController (line 1) | class LeaderboardController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/locations/base_controller.rb
class Locations::BaseController (line 3) | class Locations::BaseController < ApplicationController
method set_location (line 12) | def set_location
method set_online (line 34) | def set_online
method set_coordinates (line 38) | def set_coordinates
method set_continent (line 46) | def set_continent
method set_country (line 52) | def set_country
method set_state (line 59) | def set_state
method set_featured_city (line 69) | def set_featured_city
method set_city_by_country (line 81) | def set_city_by_country
method set_city_with_state (line 106) | def set_city_with_state
method redirect_path_helper (line 139) | def redirect_path_helper
method location_events (line 143) | def location_events
method upcoming_events (line 147) | def upcoming_events
method past_events (line 151) | def past_events
method location_users (line 155) | def location_users
method load_nearby_data (line 163) | def load_nearby_data
method load_cities (line 171) | def load_cities
method load_nearby_events (line 181) | def load_nearby_events
method country_upcoming_events (line 191) | def country_upcoming_events(exclude_ids: [])
method continent_upcoming_events (line 201) | def continent_upcoming_events(exclude_country_codes: [])
method online? (line 211) | def online?
method coordinate? (line 215) | def coordinate?
method city? (line 219) | def city?
method state? (line 223) | def state?
method country? (line 227) | def country?
method continent? (line 231) | def continent?
method location_view_prefix (line 235) | def location_view_prefix
method render_location_view (line 246) | def render_location_view(action)
FILE: app/controllers/locations/map_controller.rb
class Locations::MapController (line 3) | class Locations::MapController < Locations::BaseController
method index (line 4) | def index
method build_time_filter_options (line 15) | def build_time_filter_options
method build_geographic_layers (line 28) | def build_geographic_layers
method select_default_visible_layer (line 47) | def select_default_visible_layer(layers)
method build_continent_geo_layers (line 71) | def build_continent_geo_layers
method build_country_geo_layers (line 95) | def build_country_geo_layers
method build_state_geo_layers (line 130) | def build_state_geo_layers
method build_city_geo_layers (line 178) | def build_city_geo_layers
method build_coordinate_geo_layers (line 278) | def build_coordinate_geo_layers
method fetch_online_events (line 358) | def fetch_online_events
method redirect_path_helper (line 376) | def redirect_path_helper
FILE: app/controllers/locations/meetups_controller.rb
class Locations::MeetupsController (line 3) | class Locations::MeetupsController < Locations::BaseController
method index (line 4) | def index
method redirect_path_helper (line 16) | def redirect_path_helper
FILE: app/controllers/locations/past_controller.rb
class Locations::PastController (line 3) | class Locations::PastController < Locations::BaseController
method index (line 6) | def index
method filter_events_by_time (line 21) | def filter_events_by_time(events)
method redirect_path_helper (line 25) | def redirect_path_helper
FILE: app/controllers/locations/stamps_controller.rb
class Locations::StampsController (line 3) | class Locations::StampsController < Locations::BaseController
method index (line 4) | def index
method redirect_path_helper (line 12) | def redirect_path_helper
FILE: app/controllers/locations/users_controller.rb
class Locations::UsersController (line 3) | class Locations::UsersController < Locations::BaseController
method index (line 4) | def index
method load_nearby_users (line 20) | def load_nearby_users
method load_state_users (line 30) | def load_state_users
method load_coordinate_nearby_users (line 39) | def load_coordinate_nearby_users
method load_country_users (line 47) | def load_country_users
method redirect_path_helper (line 54) | def redirect_path_helper
FILE: app/controllers/online_controller.rb
class OnlineController (line 3) | class OnlineController < ApplicationController
method show (line 6) | def show
FILE: app/controllers/organizations/logos_controller.rb
class Organizations::LogosController (line 1) | class Organizations::LogosController < ApplicationController
method show (line 5) | def show
method update (line 9) | def update
method set_organization (line 19) | def set_organization
method ensure_admin! (line 24) | def ensure_admin!
method organization_params (line 28) | def organization_params
FILE: app/controllers/organizations/wrapped_controller.rb
class Organizations::WrappedController (line 1) | class Organizations::WrappedController < ApplicationController
method index (line 8) | def index
method og_image (line 63) | def og_image
method set_organization (line 76) | def set_organization
method set_wrapped_meta_tags (line 80) | def set_wrapped_meta_tags
method ensure_card_generated (line 104) | def ensure_card_generated
method wrapped_locals (line 135) | def wrapped_locals
FILE: app/controllers/organizations_controller.rb
class OrganizationsController (line 1) | class OrganizationsController < ApplicationController
method index (line 6) | def index
method show (line 13) | def show
method prepare_organization_statistics (line 31) | def prepare_organization_statistics
method classify_event_size (line 50) | def classify_event_size(event)
method set_organization (line 74) | def set_organization
FILE: app/controllers/page_controller.rb
class PageController (line 1) | class PageController < ApplicationController
method home (line 4) | def home
method featured (line 92) | def featured
method components (line 95) | def components
method uses (line 98) | def uses
method privacy (line 101) | def privacy
method about (line 104) | def about
method stickers (line 107) | def stickers
method contributors (line 111) | def contributors
method assets (line 115) | def assets
FILE: app/controllers/profiles/aliases_controller.rb
class Profiles::AliasesController (line 1) | class Profiles::AliasesController < ApplicationController
method index (line 6) | def index
method require_admin! (line 12) | def require_admin!
FILE: app/controllers/profiles/claims_controller.rb
class Profiles::ClaimsController (line 1) | class Profiles::ClaimsController < ApplicationController
method create (line 2) | def create
FILE: app/controllers/profiles/connect_controller.rb
class Profiles::ConnectController (line 1) | class Profiles::ConnectController < ApplicationController
method index (line 4) | def index
method show (line 8) | def show
method current_user_passport? (line 23) | def current_user_passport?
method passport_already_claimed? (line 29) | def passport_already_claimed?
FILE: app/controllers/profiles/enhance_controller.rb
class Profiles::EnhanceController (line 1) | class Profiles::EnhanceController < ApplicationController
method update (line 2) | def update
FILE: app/controllers/profiles/events_controller.rb
class Profiles::EventsController (line 1) | class Profiles::EventsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/involvements_controller.rb
class Profiles::InvolvementsController (line 1) | class Profiles::InvolvementsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/map_controller.rb
class Profiles::MapController (line 1) | class Profiles::MapController < ApplicationController
method index (line 5) | def index
FILE: app/controllers/profiles/mutual_events_controller.rb
class Profiles::MutualEventsController (line 1) | class Profiles::MutualEventsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/notes_controller.rb
class Profiles::NotesController (line 4) | class Profiles::NotesController < ApplicationController
method edit (line 9) | def edit
method show (line 12) | def show
method check_favorite_user_persisted? (line 17) | def check_favorite_user_persisted?
FILE: app/controllers/profiles/stamps_controller.rb
class Profiles::StampsController (line 1) | class Profiles::StampsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/stickers_controller.rb
class Profiles::StickersController (line 1) | class Profiles::StickersController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/talks_controller.rb
class Profiles::TalksController (line 1) | class Profiles::TalksController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/profiles/wrapped_controller.rb
class Profiles::WrappedController (line 1) | class Profiles::WrappedController < ApplicationController
method index (line 10) | def index
method card (line 234) | def card
method toggle_visibility (line 261) | def toggle_visibility
method og_image (line 281) | def og_image
method generate_card (line 294) | def generate_card
method set_wrapped_meta_tags (line 313) | def set_wrapped_meta_tags
method calculate_longest_streak (line 338) | def calculate_longest_streak
method determine_personality (line 359) | def determine_personality
method find_conference_buddies (line 422) | def find_conference_buddies
method find_watch_twin (line 448) | def find_watch_twin
method check_wrapped_access (line 472) | def check_wrapped_access
method require_owner (line 481) | def require_owner
method wrapped_locals (line 487) | def wrapped_locals
FILE: app/controllers/profiles_controller.rb
class ProfilesController (line 1) | class ProfilesController < ApplicationController
method show (line 16) | def show
method edit (line 27) | def edit
method update (line 32) | def update
method reindex (line 42) | def reindex
method require_admin! (line 51) | def require_admin!
method load_profile_data_for_show (line 55) | def load_profile_data_for_show
method user_kind (line 91) | def user_kind
method set_user (line 100) | def set_user
method user_params (line 119) | def user_params
method set_favorite_user (line 136) | def set_favorite_user
method set_mutual_events (line 140) | def set_mutual_events
method set_user_favorites (line 148) | def set_user_favorites
FILE: app/controllers/recommendations_controller.rb
class RecommendationsController (line 1) | class RecommendationsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/sessions/omniauth_controller.rb
class Sessions::OmniauthController (line 1) | class Sessions::OmniauthController < ApplicationController
method create (line 5) | def create
method failure (line 56) | def failure
method omniauth_username (line 62) | def omniauth_username
method initialize_user (line 66) | def initialize_user
method email (line 76) | def email
method github_email (line 84) | def github_email
method token (line 88) | def token
method redirect_to_path (line 92) | def redirect_to_path
method username (line 96) | def username
method omniauth_params (line 100) | def omniauth_params
method omniauth (line 110) | def omniauth
method query_params (line 114) | def query_params
method state (line 118) | def state
method fetch_github_email (line 122) | def fetch_github_email(oauth_token)
FILE: app/controllers/sessions_controller.rb
class SessionsController (line 1) | class SessionsController < ApplicationController
method new (line 8) | def new
method create (line 15) | def create
method destroy (line 26) | def destroy
FILE: app/controllers/settings_controller.rb
class SettingsController (line 1) | class SettingsController < ApplicationController
method show (line 4) | def show
method update (line 7) | def update
method settings_params (line 17) | def settings_params
FILE: app/controllers/sitemaps_controller.rb
class SitemapsController (line 1) | class SitemapsController < ApplicationController
method show (line 4) | def show
method generate_sitemap_string (line 10) | def generate_sitemap_string
class SitemapStringAdapter (line 45) | class SitemapStringAdapter
method write (line 48) | def write(location, raw_data)
FILE: app/controllers/speakers_controller.rb
class SpeakersController (line 1) | class SpeakersController < ApplicationController
method index (line 6) | def index
FILE: app/controllers/sponsors/missing_controller.rb
class Sponsors::MissingController (line 1) | class Sponsors::MissingController < ApplicationController
method index (line 5) | def index
FILE: app/controllers/spotlight/events_controller.rb
class Spotlight::EventsController (line 1) | class Spotlight::EventsController < ApplicationController
method index (line 9) | def index
method search_query (line 25) | def search_query
FILE: app/controllers/spotlight/kinds_controller.rb
class Spotlight::KindsController (line 1) | class Spotlight::KindsController < ApplicationController
method index (line 9) | def index
method search_backend (line 24) | def search_backend
method search_query (line 29) | def search_query
FILE: app/controllers/spotlight/languages_controller.rb
class Spotlight::LanguagesController (line 1) | class Spotlight::LanguagesController < ApplicationController
method index (line 9) | def index
method search_query (line 25) | def search_query
FILE: app/controllers/spotlight/locations_controller.rb
class Spotlight::LocationsController (line 1) | class Spotlight::LocationsController < ApplicationController
method index (line 9) | def index
method geocode_query (line 28) | def geocode_query(query)
method search_query (line 46) | def search_query
FILE: app/controllers/spotlight/organizations_controller.rb
class Spotlight::OrganizationsController (line 1) | class Spotlight::OrganizationsController < ApplicationController
method index (line 9) | def index
method search_query (line 25) | def search_query
FILE: app/controllers/spotlight/series_controller.rb
class Spotlight::SeriesController (line 1) | class Spotlight::SeriesController < ApplicationController
method index (line 9) | def index
method search_query (line 25) | def search_query
FILE: app/controllers/spotlight/speakers_controller.rb
class Spotlight::SpeakersController (line 1) | class Spotlight::SpeakersController < ApplicationController
method index (line 9) | def index
method search_query (line 28) | def search_query
FILE: app/controllers/spotlight/talks_controller.rb
class Spotlight::TalksController (line 1) | class Spotlight::TalksController < ApplicationController
method index (line 9) | def index
method search_query (line 27) | def search_query
FILE: app/controllers/spotlight/topics_controller.rb
class Spotlight::TopicsController (line 1) | class Spotlight::TopicsController < ApplicationController
method index (line 9) | def index
method search_query (line 25) | def search_query
FILE: app/controllers/stamps_controller.rb
class StampsController (line 1) | class StampsController < ApplicationController
method index (line 4) | def index
FILE: app/controllers/states/base_controller.rb
class States::BaseController (line 3) | class States::BaseController < ApplicationController
method set_state (line 10) | def set_state
FILE: app/controllers/states/cities_controller.rb
class States::CitiesController (line 3) | class States::CitiesController < States::BaseController
method index (line 4) | def index
FILE: app/controllers/states_controller.rb
class StatesController (line 1) | class StatesController < ApplicationController
method index (line 7) | def index
method country_index (line 13) | def country_index
method show (line 24) | def show
method filter_events_by_time (line 69) | def filter_events_by_time(events)
FILE: app/controllers/talks/recommendations_controller.rb
class Talks::RecommendationsController (line 1) | class Talks::RecommendationsController < ApplicationController
method index (line 7) | def index
method set_talk (line 15) | def set_talk
FILE: app/controllers/talks/slides_controller.rb
class Talks::SlidesController (line 1) | class Talks::SlidesController < ApplicationController
method show (line 8) | def show
FILE: app/controllers/talks/watched_talks_controller.rb
class Talks::WatchedTalksController (line 1) | class Talks::WatchedTalksController < ApplicationController
method new (line 11) | def new
method create (line 16) | def create
method destroy (line 27) | def destroy
method toggle_attendance (line 36) | def toggle_attendance
method toggle_online (line 62) | def toggle_online
method update (line 89) | def update
method watched_talk_params (line 124) | def watched_talk_params
method set_talk (line 136) | def set_talk
method should_auto_mark? (line 140) | def should_auto_mark?(progress_seconds)
method broadcast_update_to_event_talks (line 148) | def broadcast_update_to_event_talks
FILE: app/controllers/talks_controller.rb
class TalksController (line 1) | class TalksController < ApplicationController
method index (line 16) | def index
method show (line 25) | def show
method edit (line 30) | def edit
method update (line 35) | def update
method search_backend (line 46) | def search_backend
method search_options (line 50) | def search_options
method sort_key (line 66) | def sort_key
method order_by_key (line 75) | def order_by_key
method filtered_search? (line 84) | def filtered_search?
method explicit_ordering_requested? (line 88) | def explicit_ordering_requested?
method created_after (line 92) | def created_after
method talk_kind (line 98) | def talk_kind
method set_talk (line 103) | def set_talk
method talk_params (line 114) | def talk_params
method search_params (line 119) | def search_params
method set_user_favorites (line 123) | def set_user_favorites
FILE: app/controllers/templates_controller.rb
class TemplatesController (line 1) | class TemplatesController < ApplicationController
method new (line 8) | def new
method create (line 12) | def create
method new_child (line 22) | def new_child
method delete_child (line 26) | def delete_child
method speakers_search (line 29) | def speakers_search
method speakers_search_chips (line 35) | def speakers_search_chips
method search_query (line 46) | def search_query
method talk_params (line 50) | def talk_params
FILE: app/controllers/todos_controller.rb
class TodosController (line 1) | class TodosController < ApplicationController
method index (line 6) | def index
method group_by_type (line 25) | def group_by_type(matches)
method group_by_series (line 38) | def group_by_series(todos)
FILE: app/controllers/topics_controller.rb
class TopicsController (line 1) | class TopicsController < ApplicationController
method index (line 8) | def index
method show (line 14) | def show
method set_user_favorites (line 28) | def set_user_favorites
method page_number (line 36) | def page_number
FILE: app/controllers/watch_list_talks_controller.rb
class WatchListTalksController (line 1) | class WatchListTalksController < ApplicationController
method create (line 5) | def create
method destroy (line 11) | def destroy
method set_watch_list (line 19) | def set_watch_list
FILE: app/controllers/watch_lists_controller.rb
class WatchListsController (line 1) | class WatchListsController < ApplicationController
method index (line 7) | def index
method show (line 12) | def show
method new (line 15) | def new
method create (line 19) | def create
method edit (line 28) | def edit
method update (line 31) | def update
method destroy (line 39) | def destroy
method set_watch_list (line 46) | def set_watch_list
method watch_list_params (line 50) | def watch_list_params
FILE: app/controllers/watched_talks_controller.rb
class WatchedTalksController (line 1) | class WatchedTalksController < ApplicationController
method index (line 5) | def index
method destroy (line 21) | def destroy
FILE: app/controllers/wrapped_controller.rb
class WrappedController (line 1) | class WrappedController < ApplicationController
method index (line 7) | def index
method set_wrapped_meta_tags (line 133) | def set_wrapped_meta_tags
method find_country_from_location (line 157) | def find_country_from_location(location_string)
method country_codes_with_events (line 171) | def country_codes_with_events
method events_by_attendees (line 179) | def events_by_attendees
method events_by_sessions (line 190) | def events_by_sessions
method languages (line 201) | def languages
method most_watched_events (line 211) | def most_watched_events
method most_watched_talks_slugs (line 220) | def most_watched_talks_slugs
method new_speakers (line 231) | def new_speakers
method open_cfps (line 240) | def open_cfps
method people_involved (line 249) | def people_involved
method rubyist_countries (line 258) | def rubyist_countries
method top_organizations_slugs (line 268) | def top_organizations_slugs
method top_topics_slugs (line 279) | def top_topics_slugs
method total_page_views (line 290) | def total_page_views
method total_visits (line 298) | def total_visits
method unique_sponsors (line 306) | def unique_sponsors
FILE: app/helpers/application_helper.rb
type ApplicationHelper (line 1) | module ApplicationHelper
function back_path (line 4) | def back_path
function back_to_from_request (line 8) | def back_to_from_request
function active_link_to (line 15) | def active_link_to(text = nil, path = nil, active_class: "", **options...
function footer_credits (line 26) | def footer_credits
function canonical_url (line 43) | def canonical_url
function sanitize_url (line 47) | def sanitize_url(url, fallback: "")
FILE: app/helpers/events/talks_helper.rb
type Events::TalksHelper (line 1) | module Events::TalksHelper
FILE: app/helpers/events_helper.rb
type EventsHelper (line 1) | module EventsHelper
function event_date_display (line 2) | def event_date_display(event, day_name: false)
function event_date_group_key (line 19) | def event_date_group_key(event)
function group_events_by_date (line 32) | def group_events_by_date(events)
function home_updated_text (line 48) | def home_updated_text(event)
FILE: app/helpers/icon_helper.rb
type IconHelper (line 3) | module IconHelper
function clear_cache (line 18) | def clear_cache
function cached_inline_svg (line 23) | def cached_inline_svg(path, **options)
function fontawesome (line 68) | def fontawesome(icon_name, size: :md, type: nil, style: :solid, **opti...
function fab (line 73) | def fab(icon_name, **options)
function fa (line 77) | def fa(icon_name, **options)
function icon (line 81) | def icon(icon_name, size: :md, **options)
FILE: app/helpers/language_helper.rb
type LanguageHelper (line 1) | module LanguageHelper
function language_to_emoji (line 139) | def language_to_emoji(language)
FILE: app/helpers/location_helper.rb
type LocationHelper (line 3) | module LocationHelper
function location_path (line 4) | def location_path(location)
function location_past_path (line 8) | def location_past_path(location)
function location_users_path (line 12) | def location_users_path(location)
function location_meetups_path (line 16) | def location_meetups_path(location)
function location_countries_path (line 20) | def location_countries_path(location)
function location_cities_path (line 24) | def location_cities_path(location)
function location_stamps_path (line 28) | def location_stamps_path(location)
function location_map_path (line 32) | def location_map_path(location)
function location_has_routes? (line 36) | def location_has_routes?(location)
FILE: app/helpers/markdown_helper.rb
type MarkdownHelper (line 1) | module MarkdownHelper
class SyntaxHighlightedRenderer (line 3) | class SyntaxHighlightedRenderer < Redcarpet::Render::HTML
method block_code (line 4) | def block_code(code, language)
function markdown_to_html (line 13) | def markdown_to_html(markdown_content)
function announcement_markdown_to_html (line 19) | def announcement_markdown_to_html(markdown_content)
function process_mentions (line 40) | def process_mentions(html)
function process_topics (line 55) | def process_topics(html)
FILE: app/helpers/page_helper.rb
type PageHelper (line 1) | module PageHelper
FILE: app/helpers/speakers/enhance_helper.rb
type Speakers::EnhanceHelper (line 1) | module Speakers::EnhanceHelper
FILE: app/helpers/speakers_helper.rb
type SpeakersHelper (line 1) | module SpeakersHelper
function form_title_for_user_kind (line 2) | def form_title_for_user_kind(user_kind)
FILE: app/helpers/spotlight_helper.rb
type SpotlightHelper (line 1) | module SpotlightHelper
function spotlight_resources (line 2) | def spotlight_resources
function spotlight_main_resource (line 6) | def spotlight_main_resource
function spotlight_main_resource_path (line 10) | def spotlight_main_resource_path
function spotlight_search_backend (line 19) | def spotlight_search_backend
function spotlight_can_search_locations? (line 23) | def spotlight_can_search_locations?
function spotlight_can_search_languages? (line 27) | def spotlight_can_search_languages?
function spotlight_can_search_series? (line 31) | def spotlight_can_search_series?
FILE: app/helpers/suggestions_helper.rb
type SuggestionsHelper (line 1) | module SuggestionsHelper
FILE: app/helpers/talks_helper.rb
type TalksHelper (line 1) | module TalksHelper
function seconds_to_formatted_duration (line 2) | def seconds_to_formatted_duration(seconds)
function ordering_title (line 6) | def ordering_title
function resource_icon (line 17) | def resource_icon(resource)
function resource_display_title (line 36) | def resource_display_title(resource)
function resource_domain (line 40) | def resource_domain(resource)
FILE: app/helpers/view_component_helper.rb
type ViewComponentHelper (line 1) | module ViewComponentHelper
function ui_tooltip (line 18) | def ui_tooltip(text, **kwargs, &block)
function external_link_to (line 24) | def external_link_to(text, url = nil, **attributes, &)
FILE: app/javascript/controllers/auto-click_controller.js
method initialize (line 10) | initialize () {
method appear (line 14) | appear () {
FILE: app/javascript/controllers/auto_submit_controller.js
method initialize (line 7) | initialize () {
method disconnect (line 14) | disconnect () {
method submit (line 19) | submit () {
FILE: app/javascript/controllers/bridge/button_controller.js
method connect (line 7) | connect () {
method targetElement (line 15) | get targetElement () {
method title (line 23) | get title () {
FILE: app/javascript/controllers/collapsible_controller.js
method connect (line 9) | connect () {
method toggle (line 13) | toggle () {
method close (line 17) | close () {
method open (line 21) | open () {
method openValueChanged (line 25) | openValueChanged () {
method updateVisibility (line 35) | updateVisibility () {
FILE: app/javascript/controllers/content_row_controller.js
method connect (line 6) | connect () {
method scrollLeft (line 10) | scrollLeft () {
method scrollRight (line 15) | scrollRight () {
method handleScroll (line 20) | handleScroll () {
method updateButtonVisibility (line 24) | updateButtonVisibility () {
method cardWidth (line 44) | get cardWidth () {
FILE: app/javascript/controllers/copy_to_clipboard_controller.js
method copy (line 12) | async copy (event) {
method fallbackCopy (line 30) | fallbackCopy (text) {
method copied (line 39) | copied () {
FILE: app/javascript/controllers/dropdown_controller.js
method connect (line 7) | connect () {
method disconnect (line 12) | disconnect () {
method clickOutside (line 17) | clickOutside (event) {
method toggle (line 23) | toggle (event) {
method #close (line 33) | #close (event) {
FILE: app/javascript/controllers/event_controller.js
method dispatchEvent (line 9) | dispatchEvent (e) {
FILE: app/javascript/controllers/event_list_controller.js
method connect (line 6) | connect () {
method reveal (line 13) | reveal (event) {
method hidePosters (line 20) | hidePosters () {
method posterTargetFor (line 24) | posterTargetFor (eventId) {
method updateGradients (line 28) | updateGradients () {
FILE: app/javascript/controllers/events_filter_controller.js
method filter (line 7) | filter (event) {
method currentValueChanged (line 11) | currentValueChanged () {
method updateButtons (line 17) | updateButtons () {
method filterEvents (line 24) | filterEvents () {
method updateGroups (line 32) | updateGroups () {
FILE: app/javascript/controllers/events_view_switcher_controller.js
method connect (line 7) | connect () {
method showPreview (line 12) | showPreview () {
method showList (line 16) | showList () {
method switchTo (line 20) | switchTo (view) {
FILE: app/javascript/controllers/geolocation_controller.js
method locate (line 6) | locate () {
method success (line 27) | success (position) {
method error (line 36) | error (error) {
method showStatus (line 56) | showStatus (message) {
FILE: app/javascript/controllers/hover_card_controller.js
method connect (line 8) | connect () {
method reveal (line 12) | reveal () {
method resize (line 21) | resize () {
method scheduleAdjustPosition (line 28) | scheduleAdjustPosition () {
method adjustPosition (line 32) | adjustPosition () {
FILE: app/javascript/controllers/lazy_loading_controller.js
method initialize (line 11) | initialize () {
method appear (line 15) | appear () {
FILE: app/javascript/controllers/map_controller.js
method connect (line 18) | connect () {
method #initTimeFilter (line 51) | #initTimeFilter () {
method toggleLayer (line 64) | toggleLayer (event) {
method filterByTime (line 74) | filterByTime (event) {
method #applyTimeFilter (line 89) | #applyTimeFilter () {
method #updateLayerBadgeCounts (line 139) | #updateLayerBadgeCounts (counts) {
method #filterEventsByTime (line 153) | #filterEventsByTime (events, filter) {
method #fitToVisibleBounds (line 161) | #fitToVisibleBounds (bounds) {
method selectLayer (line 182) | selectLayer (event) {
method #fitToLayerBounds (line 217) | #fitToLayerBounds (bounds) {
method #updateLayerVisibility (line 226) | #updateLayerVisibility (layerId, visible) {
method #updateButtonState (line 236) | #updateButtonState (input) {
method #loadLayers (line 261) | #loadLayers () {
method #fitToBounds (line 347) | #fitToBounds () {
method #loadMarkers (line 365) | #loadMarkers () {
method #loadEventMarkers (line 375) | #loadEventMarkers () {
method #loadVenueMarkers (line 409) | #loadVenueMarkers () {
method disconnect (line 435) | disconnect () {
method #createMarkerElement (line 441) | #createMarkerElement (events) {
method #createPopup (line 447) | #createPopup (events) {
method #html (line 454) | #html (strings, ...values) {
method #singleMarkerTemplate (line 460) | #singleMarkerTemplate (event) {
method #groupMarkerTemplate (line 472) | #groupMarkerTemplate (events) {
method #popupTemplate (line 505) | #popupTemplate (events) {
method #createCityPinElement (line 525) | #createCityPinElement (name) {
method #createCityPinPopup (line 536) | #createCityPinPopup (name) {
method #createVenueMarkerElement (line 547) | #createVenueMarkerElement (marker) {
method #createVenuePopup (line 570) | #createVenuePopup (marker) {
method #venuePopupTemplate (line 577) | #venuePopupTemplate (marker) {
FILE: app/javascript/controllers/modal_controller.js
method initialize (line 12) | initialize () {
method connect (line 21) | connect () {
method disconnect (line 27) | disconnect () {
method clickOutside (line 31) | clickOutside (event) {
method open (line 35) | open () {
method close (line 40) | close (e) {
method toggle (line 47) | get toggle () {
FILE: app/javascript/controllers/preserve_scroll_controller.js
method connect (line 6) | async connect () {
method updateLinkBackToWithScrollPosition (line 18) | updateLinkBackToWithScrollPosition (e) {
method scrollTop (line 44) | get scrollTop () {
FILE: app/javascript/controllers/pronouns_select_controller.js
method connect (line 9) | connect () {
method change (line 13) | change () {
method updateInputVisibility (line 23) | updateInputVisibility () {
FILE: app/javascript/controllers/scroll_controller.js
method connect (line 6) | connect () {
method checkScroll (line 10) | checkScroll () {
method isAtEnd (line 18) | get isAtEnd () {
FILE: app/javascript/controllers/scroll_into_view_controller.js
method connect (line 9) | connect () {
method elementToScroll (line 13) | get elementToScroll () {
FILE: app/javascript/controllers/splide_controller.js
method connect (line 7) | connect () {
method disconnect (line 28) | disconnect () {
method #reset (line 33) | #reset () {
method #shouldUpdateNavbar (line 37) | #shouldUpdateNavbar () {
method #updateNavbarColors (line 41) | #updateNavbarColors () {
method splideOptions (line 60) | get splideOptions () {
method hiddenSlides (line 70) | get hiddenSlides () {
FILE: app/javascript/controllers/spotlight_search_controller.js
method initialize (line 28) | initialize () {
method connect (line 37) | connect () {}
method disconnect (line 39) | disconnect () {
method search (line 46) | async search () {
method navigate (line 156) | navigate () {
method clear (line 168) | clear () {
method setBackend (line 176) | setBackend (event) {
method appear (line 190) | appear () {
method #loadDefaults (line 196) | async #loadDefaults () {
method #handleSearch (line 231) | #handleSearch (url, query, abortController) {
method #clearResults (line 261) | #clearResults () {
method #toggleClearing (line 343) | #toggleClearing () {
method #initBackendToggle (line 352) | #initBackendToggle () {
method #updateBackendBadges (line 358) | #updateBackendBadges (backend) {
method dialog (line 380) | get dialog () {
method selectedOption (line 384) | get selectedOption () {
FILE: app/javascript/controllers/tabs_controller.js
method initialize (line 7) | initialize () {
method showPanel (line 14) | showPanel (e) {
method activeIndexValueChanged (line 19) | activeIndexValueChanged () {
method #togglePanel (line 25) | #togglePanel () {
method tabPanels (line 35) | get tabPanels () {
method tabs (line 39) | get tabs () {
method activeTab (line 43) | get activeTab () {
FILE: app/javascript/controllers/talks_filter_controller.js
method connect (line 9) | connect () {
method toggle (line 13) | toggle () {
method hideWatchedValueChanged (line 17) | hideWatchedValueChanged () {
method talks (line 21) | get talks () {
method updateVisibility (line 25) | updateVisibility () {
FILE: app/javascript/controllers/talks_filter_pill_controller.js
method selectKind (line 17) | selectKind (event) {
method selectLanguage (line 26) | selectLanguage (event) {
method kindValueChanged (line 35) | kindValueChanged () {
method languageValueChanged (line 39) | languageValueChanged () {
method updateFilterResult (line 43) | updateFilterResult () {
FILE: app/javascript/controllers/talks_navigation_controller.js
method connect (line 7) | connect () {
method setActive (line 11) | setActive (e) {
method elementToScroll (line 18) | get elementToScroll () {
FILE: app/javascript/controllers/toggable_controller.js
method connect (line 13) | connect () {
method toggle (line 19) | toggle () {
method nextToggleText (line 24) | get nextToggleText () {
FILE: app/javascript/controllers/tooltip_controller.js
method connect (line 12) | connect () {
FILE: app/javascript/controllers/top_banner_controller.js
constant ONE_MONTH (line 5) | const ONE_MONTH = 30 * 24 * 60 * 60 * 1000 // 30 days in milliseconds
constant COOKIE_NAME (line 6) | const COOKIE_NAME = 'top_banner_dismissed'
method connect (line 9) | connect () {
method dismiss (line 15) | dismiss () {
method #setCookie (line 20) | #setCookie (name, value, days) {
method isDismissed (line 32) | get isDismissed () {
FILE: app/javascript/controllers/transition_controller.js
method connect (line 21) | connect () {
method elementToTransition (line 46) | get elementToTransition () {
method isPreview (line 50) | get isPreview () {
FILE: app/javascript/controllers/video_player_controller.js
method initialize (line 28) | initialize () {
method connect (line 32) | connect () {
method init (line 38) | init () {
method dismissWatchedOverlay (line 47) | dismissWatchedOverlay () {
method resumePlayback (line 54) | resumePlayback () {
method startPlayback (line 60) | startPlayback () {
method showLoadingState (line 66) | showLoadingState (overlay) {
method removeOverlays (line 83) | removeOverlays () {
method options (line 89) | get options () {
method appear (line 136) | appear () {
method disappear (line 141) | disappear () {
method handlePlayerReady (line 146) | handlePlayerReady (player) {
method setupYouTubeEventLogging (line 196) | setupYouTubeEventLogging (player) {
method startProgressTracking (line 221) | async startProgressTracking () {
method stopProgressTracking (line 240) | stopProgressTracking () {
method handleVideoPaused (line 247) | async handleVideoPaused () {
method handleVideoEnded (line 255) | async handleVideoEnded () {
method isFullyWatched (line 264) | isFullyWatched () {
method updateWatchedProgress (line 270) | updateWatchedProgress (progressSeconds) {
method createPlaybackRateSelect (line 286) | createPlaybackRateSelect (options, player) {
method seekTo (line 303) | seekTo (event) {
method pause (line 313) | pause () {
method disconnect (line 319) | disconnect () {
method #togglePictureInPicturePlayer (line 323) | #togglePictureInPicturePlayer (enabled) {
method isPlaying (line 343) | get isPlaying () {
method isPreview (line 353) | get isPreview () {
method getCurrentTime (line 357) | async getCurrentTime () {
FILE: app/javascript/controllers/watched_talk_form_controller.js
method selectWatchedOn (line 10) | selectWatchedOn (event) {
method select (line 20) | select () {
method autoSubmit (line 24) | autoSubmit () {
FILE: app/javascript/controllers/wrapped_stories_controller.js
method connect (line 7) | connect () {
method disconnect (line 13) | disconnect () {
method handleInitialHash (line 17) | handleInitialHash () {
method handleHashChange (line 30) | handleHashChange () {
method findPageIndexByName (line 43) | findPageIndexByName (name) {
method updateHash (line 47) | updateHash () {
method toggleMode (line 57) | toggleMode () {
method updateMode (line 62) | updateMode () {
method next (line 81) | next () {
method previous (line 88) | previous () {
method goToPage (line 95) | goToPage (event) {
method handleClick (line 103) | handleClick (event) {
method handleKeydown (line 117) | handleKeydown (event) {
method showPage (line 131) | showPage () {
FILE: app/javascript/helpers/timing_helpers.js
function nextEventLoopTick (line 1) | function nextEventLoopTick () {
function onNextEventLoopTick (line 5) | function onNextEventLoopTick (callback) {
function nextFrame (line 9) | function nextFrame () {
function nextEventNamed (line 13) | function nextEventNamed (eventName, element = window) {
function delay (line 17) | function delay (ms) {
FILE: app/jobs/application_job.rb
class ApplicationJob (line 1) | class ApplicationJob < ActiveJob::Base
method retries (line 11) | def self.retries(attempts, wait: :polynomially_longer)
FILE: app/jobs/generate_wrapped_screenshot_job.rb
class GenerateWrappedScreenshotJob (line 1) | class GenerateWrappedScreenshotJob < ApplicationJob
method perform (line 5) | def perform(user)
FILE: app/jobs/geocode_record_job.rb
class GeocodeRecordJob (line 3) | class GeocodeRecordJob < ApplicationJob
method perform (line 8) | def perform(record)
method geocode_concurrency_key (line 15) | def geocode_concurrency_key(record)
FILE: app/jobs/recurring/fetch_contributors_job.rb
class Recurring::FetchContributorsJob (line 1) | class Recurring::FetchContributorsJob < ApplicationJob
method perform (line 4) | def perform
FILE: app/jobs/recurring/mark_suspicious_users_job.rb
class Recurring::MarkSuspiciousUsersJob (line 1) | class Recurring::MarkSuspiciousUsersJob < ApplicationJob
method perform (line 4) | def perform
method refresh_github_metadata (line 16) | def refresh_github_metadata(user)
FILE: app/jobs/recurring/rollup_job.rb
class Recurring::RollupJob (line 1) | class Recurring::RollupJob < ApplicationJob
method perform (line 7) | def perform(*args)
method cleanup_suspicious_recent_visits (line 19) | def cleanup_suspicious_recent_visits
method find_suspicious_ips (line 31) | def find_suspicious_ips
method delete_visits_for_ip_in_batches (line 41) | def delete_visits_for_ip_in_batches(ip)
FILE: app/jobs/recurring/youtube_thumbnail_validation_job.rb
class Recurring::YouTubeThumbnailValidationJob (line 3) | class Recurring::YouTubeThumbnailValidationJob < ApplicationJob
method perform (line 10) | def perform
method talks_to_check (line 24) | def talks_to_check
method validate_thumbnail (line 31) | def validate_thumbnail(talk)
FILE: app/jobs/recurring/youtube_video_availability_job.rb
class Recurring::YouTubeVideoAvailabilityJob (line 3) | class Recurring::YouTubeVideoAvailabilityJob < ApplicationJob
method perform (line 10) | def perform
method talks_to_check (line 24) | def talks_to_check
FILE: app/jobs/recurring/youtube_video_duration_job.rb
class Recurring::YouTubeVideoDurationJob (line 1) | class Recurring::YouTubeVideoDurationJob < ApplicationJob
method perform (line 4) | def perform
FILE: app/jobs/recurring/youtube_video_statistics_job.rb
class Recurring::YouTubeVideoStatisticsJob (line 1) | class Recurring::YouTubeVideoStatisticsJob < ApplicationJob
method perform (line 4) | def perform
FILE: app/jobs/typesense_index_job.rb
class TypesenseIndexJob (line 3) | class TypesenseIndexJob < ApplicationJob
method perform (line 6) | def perform(record, method)
FILE: app/lib/command.rb
class Command (line 1) | class Command
method run (line 2) | def self.run(command)
FILE: app/lib/download_sponsors.rb
class DownloadSponsors (line 6) | class DownloadSponsors
method initialize (line 7) | def initialize
method download_sponsors (line 22) | def download_sponsors(save_file:, base_url: nil, sponsors_url: nil, ht...
method find_sponsor_page (line 39) | def find_sponsor_page(url)
method download_sponsors_data (line 70) | def download_sponsors_data(url, save_file:)
method download_sponsors_data_from_html (line 78) | def download_sponsors_data_from_html(html_content, save_file:)
method extract_and_save_sponsors_data (line 84) | def extract_and_save_sponsors_data(html_content, save_file, url = nil)
FILE: app/lib/duration.rb
class Duration (line 1) | class Duration
method seconds_to_formatted_duration (line 2) | def self.seconds_to_formatted_duration(seconds, raise: true)
FILE: app/lib/router.rb
type Router (line 1) | module Router
function image_path (line 5) | def image_path(...)
function image_url (line 9) | def image_url(...)
FILE: app/mailers/application_mailer.rb
class ApplicationMailer (line 1) | class ApplicationMailer < ActionMailer::Base
FILE: app/models/active_record/sqlite/index.rb
type ActiveRecord::SQLite::Index (line 1) | module ActiveRecord::SQLite::Index
function attributes_for_create (line 18) | def attributes_for_create(attribute_names)
FILE: app/models/ahoy/event.rb
class Ahoy::Event (line 21) | class Ahoy::Event < ApplicationRecord
FILE: app/models/ahoy/visit.rb
class Ahoy::Visit (line 42) | class Ahoy::Visit < ApplicationRecord
FILE: app/models/alias.rb
class Alias (line 20) | class Alias < ApplicationRecord
method slug_globally_unique_except_same_aliasable (line 31) | def slug_globally_unique_except_same_aliasable
method reindex (line 40) | def reindex
FILE: app/models/announcement.rb
class Announcement (line 1) | class Announcement
class Collection (line 5) | class Collection < Array
method published (line 6) | def published
method by_tag (line 10) | def by_tag(tag)
method all_tags (line 14) | def all_tags
method find_by_slug (line 18) | def find_by_slug(slug)
method find_by_slug! (line 22) | def find_by_slug!(slug)
method initialize (line 29) | def initialize(attributes = {})
method all (line 43) | def all
method published (line 49) | def published
method by_tag (line 53) | def by_tag(tag)
method all_tags (line 57) | def all_tags
method find_by_slug (line 61) | def find_by_slug(slug)
method find_by_slug! (line 65) | def find_by_slug!(slug)
method reload! (line 69) | def reload!
method load_all (line 76) | def load_all
method parse_file (line 86) | def parse_file(file_path)
method extract_frontmatter (line 106) | def extract_frontmatter(content)
method slug_from_filename (line 118) | def slug_from_filename(file_path)
method parse_date (line 123) | def parse_date(date)
method published? (line 135) | def published?
method author_user (line 139) | def author_user
method to_param (line 145) | def to_param
FILE: app/models/application_record.rb
class ApplicationRecord (line 1) | class ApplicationRecord < ActiveRecord::Base
FILE: app/models/cfp.rb
class CFP (line 23) | class CFP < ApplicationRecord
method open? (line 29) | def open?
method open_ended? (line 36) | def open_ended?
method closed? (line 40) | def closed?
method future? (line 44) | def future?
method past? (line 48) | def past?
method status (line 52) | def status
method days_remaining (line 62) | def days_remaining
method days_until_open (line 69) | def days_until_open
method days_since_close (line 77) | def days_since_close
method present? (line 85) | def present?
method formatted_open_date (line 89) | def formatted_open_date
method formatted_close_date (line 93) | def formatted_close_date
FILE: app/models/city.rb
class City (line 26) | class City < ApplicationRecord
method country (line 62) | def country
method state (line 66) | def state
method path (line 72) | def path
method past_path (line 82) | def past_path
method users_path (line 92) | def users_path
method meetups_path (line 102) | def meetups_path
method stamps_path (line 112) | def stamps_path
method map_path (line 122) | def map_path
method to_param (line 132) | def to_param
method geocoded? (line 136) | def geocoded?
method geocodeable? (line 140) | def geocodeable?
method needs_geocoding? (line 144) | def needs_geocoding?
method geocode_query (line 148) | def geocode_query
method coordinates (line 152) | def coordinates
method location_string (line 158) | def location_string
method to_location (line 166) | def to_location
method alpha2 (line 170) | def alpha2
method continent (line 174) | def continent
method bounds (line 178) | def bounds
method stamps (line 189) | def stamps
method nearby_users (line 198) | def nearby_users(radius_km: 100, limit: 12, exclude_ids: [])
method nearby_events (line 216) | def nearby_events(radius_km: 250, limit: 12, exclude_ids: [])
method with_coordinates (line 239) | def with_coordinates
method events_count (line 243) | def events_count
method users_count (line 247) | def users_count
method feature! (line 251) | def feature!
method unfeature! (line 255) | def unfeature!
method sync_aliases_from_list (line 259) | def sync_aliases_from_list(alias_names)
method clear_unsupported_state_code (line 287) | def clear_unsupported_state_code
method geocoded_as_city (line 293) | def geocoded_as_city
method find_for (line 302) | def find_for(city:, country_code:, state_code: nil)
method find_or_create_for (line 308) | def find_or_create_for(city:, country_code:, state_code: nil, latitude...
method find_by_alias (line 331) | def find_by_alias(name, country_code:)
method featured_slugs (line 348) | def featured_slugs
method clear_cache! (line 352) | def clear_cache!
method index_in_search (line 359) | def index_in_search
method remove_from_search (line 363) | def remove_from_search
FILE: app/models/concerns/appsignal/admin_namespace.rb
type Appsignal::AdminNamespace (line 1) | module Appsignal::AdminNamespace
function set_appsignal_admin_namspace (line 8) | def set_appsignal_admin_namspace
FILE: app/models/concerns/geocodeable.rb
type Geocodeable (line 3) | module Geocodeable
function geocodeable (line 9) | def geocodeable(attribute = :location)
function geocodeable? (line 41) | def geocodeable?
function clear_geocode (line 45) | def clear_geocode
function regeocode (line 54) | def regeocode
function geocode! (line 59) | def geocode!
function clear_geocode! (line 64) | def clear_geocode!
function regeocode! (line 69) | def regeocode!
function geocode_later (line 76) | def geocode_later
FILE: app/models/concerns/locatable.rb
type Locatable (line 3) | module Locatable
function location_type (line 6) | def location_type
function continent? (line 10) | def continent?
function country? (line 14) | def country?
function city? (line 18) | def city?
function state? (line 22) | def state?
function has_sub_locations? (line 26) | def has_sub_locations?
function sub_location_label (line 30) | def sub_location_label
function to_location (line 36) | def to_location
function has_routes? (line 40) | def has_routes?
FILE: app/models/concerns/rollupable.rb
type Rollupable (line 1) | module Rollupable
function rollup_default_column (line 7) | def rollup_default_column(column)
function rollup (line 11) | def rollup(*args, **options, &block)
FILE: app/models/concerns/sluggable.rb
type Sluggable (line 1) | module Sluggable
function to_param (line 10) | def to_param
function set_slug (line 16) | def set_slug
function slug_source (line 28) | def slug_source
function configure_slug (line 35) | def configure_slug(attribute:, auto_suffix_on_collision: false)
FILE: app/models/concerns/suggestable.rb
type Suggestable (line 1) | module Suggestable
function create_suggestion_from (line 8) | def create_suggestion_from(params:, user: Current.user)
function managed_by? (line 16) | def managed_by?(visiting_user)
function select_differences_for (line 21) | def select_differences_for(params)
FILE: app/models/concerns/todoable.rb
type Todoable (line 1) | module Todoable
function todos (line 4) | def todos
function todos_count (line 8) | def todos_count
function todos_data_path (line 14) | def todos_data_path
function todos_file_prefix (line 18) | def todos_file_prefix
FILE: app/models/concerns/url_normalizable.rb
type UrlNormalizable (line 3) | module UrlNormalizable
function normalize_url_string (line 6) | def self.normalize_url_string(url)
function normalize_url (line 24) | def normalize_url(field)
FILE: app/models/concerns/watchable.rb
type Watchable (line 1) | module Watchable
function mark_as_watched! (line 8) | def mark_as_watched!
function unmark_as_watched! (line 12) | def unmark_as_watched!
function watched? (line 16) | def watched?(user = Current.user)
FILE: app/models/concerns/yaml_file.rb
type YAMLFile (line 3) | module YAMLFile
function yaml_file (line 7) | def yaml_file(filename, data_method: :file)
function file_path (line 13) | def file_path
function exist? (line 17) | def exist?
function file (line 21) | def file
function entries (line 27) | def entries
function reload (line 33) | def reload
FILE: app/models/connected_account.rb
class ConnectedAccount (line 28) | class ConnectedAccount < ApplicationRecord
FILE: app/models/continent.rb
class Continent (line 3) | class Continent
method initialize (line 28) | def initialize(slug)
method name (line 32) | def name
method alpha2 (line 36) | def alpha2
method emoji_flag (line 40) | def emoji_flag
method path (line 44) | def path
method past_path (line 48) | def past_path
method users_path (line 52) | def users_path
method countries_path (line 56) | def countries_path
method stamps_path (line 60) | def stamps_path
method map_path (line 64) | def map_path
method to_param (line 68) | def to_param
method bounds (line 72) | def bounds
method latitude (line 76) | def latitude
method longitude (line 82) | def longitude
method coordinates (line 88) | def coordinates
method countries (line 94) | def countries
method country_codes (line 98) | def country_codes
method events (line 102) | def events
method users (line 106) | def users
method stamps (line 110) | def stamps
method == (line 114) | def ==(other)
method eql? (line 118) | def eql?(other)
method hash (line 122) | def hash
method to_location (line 126) | def to_location
method all (line 131) | def all
method find (line 135) | def find(term)
method find_by_name (line 144) | def find_by_name(name)
method slugs (line 153) | def slugs
method africa (line 157) | def africa = new("africa")
method antarctica (line 158) | def antarctica = new("antarctica")
method asia (line 159) | def asia = new("asia")
method australia (line 160) | def australia = new("australia")
method europe (line 161) | def europe = new("europe")
method north_america (line 162) | def north_america = new("north-america")
method south_america (line 163) | def south_america = new("south-america")
method data (line 168) | def data
FILE: app/models/contributor.rb
class Contributor (line 24) | class Contributor < ApplicationRecord
method name (line 29) | def name
FILE: app/models/coordinate_location.rb
class CoordinateLocation (line 3) | class CoordinateLocation
method initialize (line 10) | def initialize(latitude:, longitude:)
method name (line 16) | def name
method full_name (line 20) | def full_name
method slug (line 24) | def slug
method emoji_flag (line 28) | def emoji_flag
method path (line 32) | def path
method past_path (line 36) | def past_path
method users_path (line 40) | def users_path
method cities_path (line 44) | def cities_path
method stamps_path (line 48) | def stamps_path
method map_path (line 52) | def map_path
method has_routes? (line 56) | def has_routes?
method events (line 60) | def events
method users (line 64) | def users
method stamps (line 68) | def stamps
method events_count (line 72) | def events_count
method users_count (line 76) | def users_count
method geocoded? (line 80) | def geocoded?
method coordinates (line 84) | def coordinates
method to_coordinates (line 90) | def to_coordinates
method bounds (line 94) | def bounds
method to_location (line 105) | def to_location
method alpha2 (line 116) | def alpha2
method country_code (line 120) | def country_code
method country (line 124) | def country
method continent (line 130) | def continent
method nearby_users (line 134) | def nearby_users(radius_km: 100, limit: 12, exclude_ids: [])
method nearby_events (line 153) | def nearby_events(radius_km: 250, limit: 12, exclude_ids: [])
method coordinates_param (line 176) | def coordinates_param
method reverse_geocode_data (line 180) | def reverse_geocode_data
method reverse_geocode_display_name (line 188) | def reverse_geocode_display_name
method reverse_geocode_full_name (line 196) | def reverse_geocode_full_name
method reverse_geocode_city (line 206) | def reverse_geocode_city
method reverse_geocode_state (line 210) | def reverse_geocode_state
method nearby_events_query (line 214) | def nearby_events_query
method nearby_users_query (line 224) | def nearby_users_query
method bounding_box_for_radius (line 234) | def bounding_box_for_radius(radius_km)
method from_param (line 245) | def from_param(param)
FILE: app/models/country.rb
class Country (line 1) | class Country
method continent_name (line 15) | def continent_name
method continent (line 19) | def continent
method initialize (line 23) | def initialize(record)
method name (line 27) | def name
method slug (line 31) | def slug
method path (line 35) | def path
method past_path (line 39) | def past_path
method users_path (line 43) | def users_path
method meetups_path (line 47) | def meetups_path
method cities_path (line 51) | def cities_path
method stamps_path (line 55) | def stamps_path
method map_path (line 59) | def map_path
method to_location (line 63) | def to_location
method code (line 67) | def code
method latitude (line 71) | def latitude
method longitude (line 75) | def longitude
method coordinates (line 79) | def coordinates
method bounds (line 84) | def bounds
method to_param (line 94) | def to_param
method == (line 98) | def ==(other)
method eql? (line 102) | def eql?(other)
method hash (line 106) | def hash
method events (line 110) | def events
method users (line 114) | def users
method cities (line 118) | def cities
method states (line 122) | def states
method states? (line 126) | def states?
method stamps (line 130) | def stamps
method held_in_sentence (line 141) | def held_in_sentence
method uk_nation? (line 149) | def uk_nation?
method find (line 154) | def find(term)
method find_by (line 176) | def find_by(country_code:)
method all (line 183) | def all
method all_with_uk_nations (line 187) | def all_with_uk_nations
method uk_nations (line 191) | def uk_nations
method valid_country_codes (line 195) | def valid_country_codes
method all_by_slug (line 199) | def all_by_slug
method slugs (line 203) | def slugs
method select_options (line 207) | def select_options
method search (line 211) | def search(query)
method find_iso_record (line 225) | def find_iso_record(term)
FILE: app/models/cue.rb
class Cue (line 1) | class Cue
method initialize (line 4) | def initialize(start_time:, end_time:, text:)
method to_s (line 10) | def to_s
method to_h (line 14) | def to_h
method start_time_in_seconds (line 22) | def start_time_in_seconds
method time_string_to_seconds (line 26) | def time_string_to_seconds(time_string)
method sound_descriptor? (line 36) | def sound_descriptor?
FILE: app/models/current.rb
class Current (line 1) | class Current < ActiveSupport::CurrentAttributes
method session= (line 5) | def session=(session)
FILE: app/models/email_verification_token.rb
class EmailVerificationToken (line 19) | class EmailVerificationToken < ApplicationRecord
FILE: app/models/event.rb
class Event (line 42) | class Event < ApplicationRecord
method upcoming? (line 141) | def upcoming?
method past? (line 145) | def past?
method find_by_name_or_alias (line 149) | def self.find_by_name_or_alias(name)
method find_by_slug_or_alias (line 159) | def self.find_by_slug_or_alias(slug)
method grouped_by_country (line 169) | def self.grouped_by_country
method sync_aliases_from_list (line 176) | def sync_aliases_from_list(alias_names)
method assign_canonical_event! (line 203) | def assign_canonical_event!(canonical_event:)
method managed_by? (line 213) | def managed_by?(user)
method data_folder (line 217) | def data_folder
method suggestion_summary (line 221) | def suggestion_summary
method location_and_country_code (line 232) | def location_and_country_code
method location_and_country_code_previously_changed? (line 238) | def location_and_country_code_previously_changed?
method today? (line 242) | def today?
method formatted_dates (line 250) | def formatted_dates
method held_in_sentence (line 273) | def held_in_sentence
method to_location (line 277) | def to_location
method description (line 283) | def description
method keynote_speakers_text (line 293) | def keynote_speakers_text
method talks_text (line 297) | def talks_text
method to_meta_tags (line 301) | def to_meta_tags
method sort_date (line 327) | def sort_date
method watchable_talks? (line 331) | def watchable_talks?
method featurable? (line 335) | def featurable?
method website (line 339) | def website
method to_mobile_json (line 343) | def to_mobile_json(request)
method todos_data_path (line 362) | def todos_data_path
method todos_file_prefix (line 366) | def todos_file_prefix
FILE: app/models/event/assets.rb
class Event::Assets (line 3) | class Event::Assets < ActiveRecord::AssociatedObject
method event_image_path (line 9) | def event_image_path = assets.base_path
method event_image_for (line 10) | def event_image_for(filename) = assets.image_path_if_exists(filename)
method banner_image_path (line 11) | def banner_image_path = assets.banner_path
method card_image_path (line 12) | def card_image_path = assets.card_path
method avatar_image_path (line 13) | def avatar_image_path = assets.avatar_path
method featured_image_path (line 14) | def featured_image_path = assets.featured_path
method poster_image_path (line 15) | def poster_image_path = assets.poster_path
method sticker_image_paths (line 16) | def sticker_image_paths = assets.sticker_paths
method sticker_image_path (line 17) | def sticker_image_path = assets.sticker_path
method stamp_image_paths (line 18) | def stamp_image_paths = assets.stamp_paths
method stamp_image_path (line 19) | def stamp_image_path = assets.stamp_path
method avatar_url (line 21) | def avatar_url
method banner_url (line 27) | def banner_url
method base_path (line 34) | def base_path
method default_path (line 38) | def default_path
method default_series_path (line 42) | def default_series_path
method image_path_for (line 46) | def image_path_for(filename)
method image_path_if_exists (line 57) | def image_path_if_exists(filename)
method banner_path (line 63) | def banner_path
method card_path (line 67) | def card_path
method avatar_path (line 71) | def avatar_path
method featured_path (line 75) | def featured_path
method poster_path (line 79) | def poster_path
method stickers (line 83) | def stickers
method sticker_paths (line 87) | def sticker_paths
method sticker_path (line 91) | def sticker_path
method sticker? (line 95) | def sticker?
method stamp_paths (line 99) | def stamp_paths
method stamp_path (line 105) | def stamp_path
method stamp? (line 109) | def stamp?
FILE: app/models/event/cfp_file.rb
class Event::CFPFile (line 3) | class Event::CFPFile < ActiveRecord::AssociatedObject
method find_by_link (line 8) | def find_by_link(link)
method add (line 12) | def add(link:, open_date: nil, close_date: nil, name: nil)
method update (line 21) | def update(link:, open_date: nil, close_date: nil, name: nil)
method write (line 40) | def write(cfps)
method build_entry (line 46) | def build_entry(link:, open_date:, close_date:, name:)
FILE: app/models/event/involvements_file.rb
class Event::InvolvementsFile (line 3) | class Event::InvolvementsFile < ActiveRecord::AssociatedObject
method roles (line 8) | def roles
method users_for_role (line 12) | def users_for_role(role)
method organisations_for_role (line 19) | def organisations_for_role(role)
method all_users (line 26) | def all_users
method all_organisations (line 30) | def all_organisations
FILE: app/models/event/schedule.rb
class Event::Schedule (line 1) | class Event::Schedule < ActiveRecord::AssociatedObject
method days (line 6) | def days
method tracks (line 10) | def tracks
method talk_offsets (line 14) | def talk_offsets
FILE: app/models/event/sponsors_file.rb
class Event::SponsorsFile (line 2) | class Event::SponsorsFile < ActiveRecord::AssociatedObject
method tier_names (line 7) | def tier_names
method sponsors (line 13) | def sponsors
method download (line 31) | def download(base_url: nil, sponsors_url: nil, html: nil)
FILE: app/models/event/static_metadata.rb
class Event::StaticMetadata (line 1) | class Event::StaticMetadata < ActiveRecord::AssociatedObject
method featured_metadata? (line 3) | def featured_metadata? = static_metadata.featured_background?
method kind (line 8) | def kind
method conference? (line 16) | def conference?
method meetup? (line 20) | def meetup?
method retreat? (line 24) | def retreat?
method hackathon? (line 28) | def hackathon?
method frequency (line 32) | def frequency
method start_date (line 36) | def start_date
method end_date (line 42) | def end_date
method date_precision (line 48) | def date_precision
method year (line 52) | def year
method featured_background? (line 58) | def featured_background?
method featured_background (line 64) | def featured_background
method featured_color (line 73) | def featured_color
method banner_background (line 80) | def banner_background
method location (line 87) | def location
method coordinates (line 91) | def coordinates
method country (line 95) | def country
method last_edition? (line 101) | def last_edition?
method hybrid? (line 105) | def hybrid?
method cancelled? (line 109) | def cancelled?
method playlist (line 113) | def playlist
method static_repository (line 119) | def static_repository
FILE: app/models/event/tickets.rb
class Event::Tickets (line 5) | class Event::Tickets < ActiveRecord::AssociatedObject
method tickets? (line 19) | def tickets?
method next_upcoming_event_with_tickets (line 23) | def next_upcoming_event_with_tickets
method url (line 34) | def url
method exist? (line 38) | def exist?
method available? (line 42) | def available?
method tito_event_slug (line 46) | def tito_event_slug
method provider (line 53) | def provider
method provider_name (line 57) | def provider_name
method tito? (line 61) | def tito? = provider.tito?
method luma? (line 62) | def luma? = provider.luma?
method meetup? (line 63) | def meetup? = provider.meetup?
method ticket_url_host (line 67) | def ticket_url_host
method host_is? (line 75) | def host_is?(*domains)
method static_repository (line 82) | def static_repository
FILE: app/models/event/transcripts_file.rb
class Event::TranscriptsFile (line 3) | class Event::TranscriptsFile < ActiveRecord::AssociatedObject
method video_ids (line 8) | def video_ids
method find_by_video_id (line 12) | def find_by_video_id(video_id)
method cues_for_video (line 16) | def cues_for_video(video_id)
FILE: app/models/event/typesense_searchable.rb
type Event::TypesenseSearchable (line 3) | module Event::TypesenseSearchable
function trigger_typesense_job (line 151) | def trigger_typesense_job(record, remove)
function typesense_search_events (line 155) | def typesense_search_events(query, options = {})
function should_index? (line 198) | def should_index?
FILE: app/models/event/venue.rb
class Event::Venue (line 1) | class Event::Venue < ActiveRecord::AssociatedObject
method geocode (line 7) | def geocode
method name (line 18) | def name
method slug (line 22) | def slug
method description (line 26) | def description
method address (line 30) | def address
method display_address (line 34) | def display_address
method street (line 38) | def street
method city (line 42) | def city
method region (line 46) | def region
method postal_code (line 50) | def postal_code
method country (line 54) | def country
method country_code (line 58) | def country_code
method coordinates (line 62) | def coordinates
method latitude (line 66) | def latitude
method longitude (line 70) | def longitude
method maps (line 74) | def maps
method google_maps_url (line 78) | def google_maps_url
method apple_maps_url (line 82) | def apple_maps_url
method openstreetmap_url (line 86) | def openstreetmap_url
method instructions (line 90) | def instructions
method url (line 94) | def url
method accessibility (line 98) | def accessibility
method rooms (line 102) | def rooms
method spaces (line 106) | def spaces
method nearby (line 110) | def nearby
method hotels (line 114) | def hotels
method locations (line 118) | def locations
method map_markers (line 122) | def map_markers
FILE: app/models/event/videos_file.rb
class Event::VideosFile (line 3) | class Event::VideosFile < ActiveRecord::AssociatedObject
method talks_in_running_order (line 9) | def talks_in_running_order(child_talks: true)
method ids (line 14) | def ids(child_talks: true)
method find_by_id (line 28) | def find_by_id(id)
method count (line 32) | def count
FILE: app/models/event_involvement.rb
class EventInvolvement (line 26) | class EventInvolvement < ApplicationRecord
method name (line 35) | def name
FILE: app/models/event_participation.rb
class EventParticipation (line 25) | class EventParticipation < ApplicationRecord
method name (line 36) | def name
FILE: app/models/event_series.rb
class EventSeries (line 29) | class EventSeries < ApplicationRecord
method find_by_name_or_alias (line 52) | def self.find_by_name_or_alias(name)
method find_by_slug_or_alias (line 62) | def self.find_by_slug_or_alias(slug)
method sync_aliases_from_list (line 72) | def sync_aliases_from_list(alias_names)
method title (line 92) | def title
method next_upcoming_event_with_tickets (line 96) | def next_upcoming_event_with_tickets
method description (line 100) | def description
method to_meta_tags (line 123) | def to_meta_tags
method todos_data_path (line 153) | def todos_data_path
method todos_file_prefix (line 157) | def todos_file_prefix
FILE: app/models/event_series/static_metadata.rb
class EventSeries::StaticMetadata (line 1) | class EventSeries::StaticMetadata < ActiveRecord::AssociatedObject
method ended? (line 2) | def ended?
method default_country_code (line 6) | def default_country_code
method static_repository (line 12) | def static_repository
FILE: app/models/event_series/typesense_searchable.rb
type EventSeries::TypesenseSearchable (line 3) | module EventSeries::TypesenseSearchable
function trigger_typesense_job (line 82) | def trigger_typesense_job(record, remove)
function typesense_search_series (line 86) | def typesense_search_series(query, options = {})
function should_index? (line 118) | def should_index?
FILE: app/models/favorite_user.rb
class FavoriteUser (line 23) | class FavoriteUser < ApplicationRecord
method ruby_friend? (line 32) | def ruby_friend?
method recommendations_for (line 38) | def self.recommendations_for(user)
FILE: app/models/geocode_result.rb
class GeocodeResult (line 16) | class GeocodeResult < ApplicationRecord
FILE: app/models/language.rb
class Language (line 1) | class Language
method find (line 7) | def self.find(term)
method find_by_code (line 14) | def self.find_by_code(code)
method alpha2_codes (line 18) | def self.alpha2_codes
method english_names (line 22) | def self.english_names
method all (line 26) | def self.all
method by_code (line 30) | def self.by_code(code)
method used (line 34) | def self.used
method talks (line 40) | def self.talks(code)
method talks_count (line 44) | def self.talks_count(code)
method native_name (line 48) | def self.native_name(code)
method emoji_flag (line 52) | def self.emoji_flag(code)
method synonyms_for (line 59) | def self.synonyms_for(code)
method all_synonyms (line 85) | def self.all_synonyms
FILE: app/models/llm/client.rb
class LLM::Client (line 1) | class LLM::Client
method initialize (line 2) | def initialize(provider_client = OpenAI::Client.new)
method chat (line 6) | def chat(parameters:, resource:, task_name:)
FILE: app/models/llm/request.rb
class LLM::Request (line 23) | class LLM::Request < ApplicationRecord
method find_or_create_by_request! (line 34) | def find_or_create_by_request!(request_params, resource:, task_name:, ...
FILE: app/models/location.rb
class Location (line 3) | class Location
method initialize (line 12) | def initialize(city: nil, state_code: nil, country_code: nil, latitude...
method from_record (line 22) | def self.from_record(record)
method from_string (line 34) | def self.from_string(location_string)
method online (line 38) | def self.online
method city_object (line 42) | def city_object
method state (line 49) | def state
method country (line 55) | def country
method city_path (line 61) | def city_path
method state_path (line 65) | def state_path
method country_path (line 69) | def country_path
method continent (line 73) | def continent
method continent_name (line 77) | def continent_name
method continent_path (line 81) | def continent_path
method online_location (line 85) | def online_location
method online_path (line 89) | def online_path
method online? (line 93) | def online?
method hybrid? (line 99) | def hybrid?
method state_display_name (line 103) | def state_display_name
method display_city (line 107) | def display_city
method geocoded? (line 117) | def geocoded?
method present? (line 121) | def present?
method blank? (line 125) | def blank?
method has_state? (line 129) | def has_state?
method has_city? (line 133) | def has_city?
method to_s (line 137) | def to_s
method to_html (line 148) | def to_html(show_links: true, link_class: "link", upto: :country)
method to_text (line 158) | def to_text(upto: :country)
method text_upto (line 173) | def text_upto(upto)
method render_online (line 177) | def render_online(show_links:, link_class:)
method render_upto (line 181) | def render_upto(upto:, show_links:, link_class:)
method location_parts (line 189) | def location_parts(upto:)
method append_hybrid_html (line 206) | def append_hybrid_html(result, show_links:, link_class:)
method link_or_text (line 212) | def link_or_text(text, path, show_links:, link_class:)
method join_parts (line 222) | def join_parts(parts, separator)
FILE: app/models/online_location.rb
class OnlineLocation (line 3) | class OnlineLocation
method name (line 7) | def name
method slug (line 11) | def slug
method emoji_flag (line 15) | def emoji_flag
method path (line 19) | def path
method past_path (line 23) | def past_path
method users_path (line 27) | def users_path
method cities_path (line 31) | def cities_path
method stamps_path (line 35) | def stamps_path
method map_path (line 39) | def map_path
method has_routes? (line 43) | def has_routes?
method events (line 47) | def events
method users (line 51) | def users
method stamps (line 55) | def stamps
method events_count (line 59) | def events_count
method users_count (line 63) | def users_count
method geocoded? (line 67) | def geocoded?
method coordinates (line 71) | def coordinates
method to_coordinates (line 75) | def to_coordinates
method bounds (line 79) | def bounds
method to_location (line 83) | def to_location
method instance (line 88) | def instance
FILE: app/models/organization.rb
class Organization (line 25) | class Organization < ApplicationRecord
method find_by_name_or_alias (line 49) | def self.find_by_name_or_alias(name)
method find_by_slug_or_alias (line 59) | def self.find_by_slug_or_alias(slug)
method organization_image_path (line 71) | def organization_image_path
method default_organization_image_path (line 75) | def default_organization_image_path
method organization_image_or_default_for (line 79) | def organization_image_or_default_for(filename)
method organization_image_for (line 90) | def organization_image_for(filename)
method avatar_image_path (line 96) | def avatar_image_path
method banner_image_path (line 100) | def banner_image_path
method logo_image_path (line 104) | def logo_image_path
method has_logo_image? (line 115) | def has_logo_image?
method logo_background_class (line 119) | def logo_background_class
method logo_border_class (line 130) | def logo_border_class
method add_logo_url (line 141) | def add_logo_url(url)
method ensure_unique_logo_urls (line 152) | def ensure_unique_logo_urls
FILE: app/models/organization/typesense_searchable.rb
type Organization::TypesenseSearchable (line 3) | module Organization::TypesenseSearchable
function trigger_typesense_job (line 53) | def trigger_typesense_job(record, remove)
function typesense_search_organizations (line 57) | def typesense_search_organizations(query, options = {})
function should_index? (line 89) | def should_index?
FILE: app/models/organization/wrapped_screenshot_generator.rb
class Organization::WrappedScreenshotGenerator (line 3) | class Organization::WrappedScreenshotGenerator
method initialize (line 13) | def initialize(organization)
method save_to_storage (line 17) | def save_to_storage(locals)
method generate_horizontal_card (line 33) | def generate_horizontal_card(locals)
method browser_options (line 58) | def browser_options(dimensions)
method chrome_ws_url (line 80) | def chrome_ws_url
method render_card_html (line 92) | def render_card_html(locals)
method wrap_in_html_document (line 101) | def wrap_in_html_document(partial_html, locals)
FILE: app/models/password_reset_token.rb
class PasswordResetToken (line 19) | class PasswordResetToken < ApplicationRecord
FILE: app/models/prompts/base.rb
type Prompts (line 1) | module Prompts
class Base (line 2) | class Base
method to_params (line 5) | def to_params
method model (line 16) | def model
method response_format (line 20) | def response_format
method messages (line 24) | def messages
method system_message (line 31) | def system_message
method prompt (line 35) | def prompt
method service_tier (line 39) | def service_tier
FILE: app/models/prompts/talk/enhance_transcript.rb
type Prompts (line 1) | module Prompts
type Talk (line 2) | module Talk
class EnhanceTranscript (line 3) | class EnhanceTranscript < Prompts::Base
method initialize (line 6) | def initialize(talk:)
method system_message (line 15) | def system_message
method prompt (line 19) | def prompt
method response_format (line 69) | def response_format
FILE: app/models/prompts/talk/summary.rb
type Prompts (line 1) | module Prompts
type Talk (line 2) | module Talk
class Summary (line 3) | class Summary < Prompts::Base
method initialize (line 6) | def initialize(talk:)
method system_message (line 15) | def system_message
method prompt (line 19) | def prompt
method response_format (line 74) | def response_format
FILE: app/models/prompts/talk/topics.rb
type Prompts (line 1) | module Prompts
type Talk (line 2) | module Talk
class Topics (line 3) | class Topics < Prompts::Base
method initialize (line 6) | def initialize(talk:)
method system_message (line 15) | def system_message
method prompt (line 19) | def prompt
method response_format (line 76) | def response_format
FILE: app/models/prompts/topic/match_talks.rb
type Prompts (line 1) | module Prompts
type Topic (line 2) | module Topic
class MatchTalks (line 3) | class MatchTalks < Prompts::Base
method initialize (line 6) | def initialize(topic:, talks:)
method system_message (line 15) | def system_message
method prompt (line 22) | def prompt
method talks_info (line 59) | def talks_info
method response_format (line 72) | def response_format
FILE: app/models/prompts/topic/suggest_gems.rb
type Prompts (line 1) | module Prompts
type Topic (line 2) | module Topic
class SuggestGems (line 3) | class SuggestGems < Prompts::Base
method initialize (line 6) | def initialize(topics:)
method system_message (line 14) | def system_message
method prompt (line 21) | def prompt
method response_format (line 73) | def response_format
FILE: app/models/rollup.rb
class Rollup (line 17) | class Rollup < ApplicationRecord
method time_zone (line 47) | def time_zone
method series (line 51) | def series(name, interval: "day", dimensions: {})
method list (line 62) | def list
method rename (line 69) | def rename(old_name, new_name)
method make_series (line 75) | def make_series(result, interval)
method inspect (line 100) | def inspect
method time (line 108) | def time
class Aggregator (line 120) | class Aggregator
method initialize (line 121) | def initialize(klass)
method rollup (line 125) | def rollup(name, column: nil, interval: "day", dimension_names: nil,...
method validate_column (line 147) | def validate_column(column)
method perform_group (line 157) | def perform_group(name, column:, interval:, time_zone:, current:, la...
method set_dimension_names (line 219) | def set_dimension_names(dimension_names, relation)
method determine_dimension_name (line 232) | def determine_dimension_name(group)
method perform_calculation (line 248) | def perform_calculation(relation, &block)
method prepare_result (line 256) | def prepare_result(result, name, dimension_names, interval)
method maybe_clear (line 291) | def maybe_clear(clear, name, interval)
method save_records (line 302) | def save_records(records)
type Utils (line 317) | module Utils
function time_sql (line 321) | def time_sql(interval)
function date_interval? (line 329) | def date_interval?(interval)
FILE: app/models/search/backend.rb
type Search::Backend (line 3) | module Search::Backend
function without_indexing (line 9) | def without_indexing(&block)
function backends (line 17) | def backends
function available_backends (line 24) | def available_backends
function resolve (line 28) | def resolve(preferred = nil)
function default_backend (line 37) | def default_backend
function default_backend_key (line 41) | def default_backend_key
function index (line 51) | def index(record)
function remove (line 61) | def remove(record)
function reindex_all (line 69) | def reindex_all
FILE: app/models/search/backend/sqlite_fts.rb
class Search::Backend::SQLiteFTS (line 3) | class Search::Backend::SQLiteFTS
method search_talks (line 5) | def search_talks(query, limit:, **options)
method search_talks_with_pagy (line 28) | def search_talks_with_pagy(query, pagy_backend:, **options)
method search_speakers (line 57) | def search_speakers(query, limit:)
method search_events (line 64) | def search_events(query, limit:)
method search_topics (line 71) | def search_topics(query, limit:)
method search_series (line 79) | def search_series(query, limit:)
method search_organizations (line 87) | def search_organizations(query, limit:)
method search_languages (line 95) | def search_languages(query, limit:)
method search_locations (line 125) | def search_locations(query, limit:)
method search_continents (line 129) | def search_continents(query, limit:)
method search_countries (line 133) | def search_countries(query, limit:)
method search_states (line 137) | def search_states(query, limit:)
method search_cities (line 141) | def search_cities(query, limit:)
method search_kinds (line 145) | def search_kinds(query, limit:)
method available? (line 149) | def available?
method name (line 153) | def name
method indexer (line 157) | def indexer
method reset_cache! (line 161) | def reset_cache!
method apply_sort (line 175) | def apply_sort(talks, query, sort)
method languages_with_talks (line 186) | def languages_with_talks
FILE: app/models/search/backend/sqlite_fts/indexer.rb
class Search::Backend::SQLiteFTS (line 3) | class Search::Backend::SQLiteFTS
class Indexer (line 4) | class Indexer
method index (line 6) | def index(record)
method remove (line 17) | def remove(record)
method reindex_all (line 28) | def reindex_all
method reindex_talks (line 34) | def reindex_talks
method reindex_users (line 41) | def reindex_users
method reindex_events (line 48) | def reindex_events
method index_talk (line 55) | def index_talk(talk)
method remove_talk (line 63) | def remove_talk(talk)
method index_user (line 67) | def index_user(user)
method remove_user (line 75) | def remove_user(user)
method index_event (line 79) | def index_event(event)
method remove_event (line 83) | def remove_event(event)
FILE: app/models/search/backend/typesense.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
method search_talks (line 8) | def search_talks(query, limit:, **options)
method search_talks_with_pagy (line 15) | def search_talks_with_pagy(query, pagy_backend: nil, **options)
method search_speakers (line 19) | def search_speakers(query, limit:)
method search_events (line 25) | def search_events(query, limit:)
method search_topics (line 31) | def search_topics(query, limit:)
method search_series (line 37) | def search_series(query, limit:)
method search_organizations (line 43) | def search_organizations(query, limit:)
method search_languages (line 49) | def search_languages(query, limit:)
method search_locations (line 55) | def search_locations(query, limit:)
method search_continents (line 61) | def search_continents(query, limit:)
method search_countries (line 67) | def search_countries(query, limit:)
method search_states (line 73) | def search_states(query, limit:)
method search_cities (line 79) | def search_cities(query, limit:)
method search_kinds (line 85) | def search_kinds(query, limit:)
method available? (line 91) | def available?
method name (line 101) | def name
method indexer (line 105) | def indexer
method perform_search (line 111) | def perform_search(type, query)
method circuit_breaker (line 121) | def circuit_breaker
FILE: app/models/search/backend/typesense/circuit_breaker.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
class CircuitBreaker (line 4) | class CircuitBreaker
method initialize (line 5) | def initialize(ttl:)
method call (line 13) | def call
FILE: app/models/search/backend/typesense/indexer.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
class Indexer (line 4) | class Indexer
method index (line 6) | def index(record)
method remove (line 16) | def remove(record)
method reindex_all (line 26) | def reindex_all
method reindex_talks (line 38) | def reindex_talks
method reindex_users (line 42) | def reindex_users
method reindex_events (line 46) | def reindex_events
method reindex_topics (line 50) | def reindex_topics
method reindex_series (line 54) | def reindex_series
method reindex_organizations (line 58) | def reindex_organizations
method reindex_languages (line 62) | def reindex_languages
method reindex_locations (line 66) | def reindex_locations
method reindex_kinds (line 70) | def reindex_kinds
method should_index? (line 76) | def should_index?(record)
FILE: app/models/search/backend/typesense/kind_indexer.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
class KindIndexer (line 4) | class KindIndexer
method collection_schema (line 33) | def collection_schema
method client (line 49) | def client
method collection (line 53) | def collection
method ensure_collection! (line 57) | def ensure_collection!
method reindex_all (line 63) | def reindex_all
method drop_collection! (line 73) | def drop_collection!
method index_talk_kinds (line 79) | def index_talk_kinds
method index_event_kinds (line 87) | def index_event_kinds
method search (line 95) | def search(query, category: nil, limit: 10)
method build_talk_kind_documents (line 117) | def build_talk_kind_documents
method build_event_kind_documents (line 134) | def build_event_kind_documents
FILE: app/models/search/backend/typesense/language_indexer.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
class LanguageIndexer (line 4) | class LanguageIndexer
method collection_schema (line 8) | def collection_schema
method client (line 24) | def client
method collection (line 28) | def collection
method ensure_collection! (line 32) | def ensure_collection!
method reindex_all (line 38) | def reindex_all
method create_synonyms! (line 47) | def create_synonyms!
method drop_collection! (line 59) | def drop_collection!
method index_languages (line 65) | def index_languages
method search (line 73) | def search(query, limit: 10)
method build_language_documents (line 93) | def build_language_documents
method languages_with_talks (line 108) | def languages_with_talks
FILE: app/models/search/backend/typesense/location_indexer.rb
class Search::Backend::Typesense (line 3) | class Search::Backend::Typesense
class LocationIndexer (line 4) | class LocationIndexer
method collection_schema (line 8) | def collection_schema
method client (line 31) | def client
method collection (line 35) | def collection
method ensure_collection! (line 39) | def ensure_collection!
method reindex_all (line 45) | def reindex_all
method create_synonyms! (line 60) | def create_synonyms!
method city_synonyms (line 72) | def city_synonyms
method online_synonyms (line 81) | def online_synonyms
method drop_collection! (line 89) | def drop_collection!
method index_continents (line 95) | def index_continents = index_documents("continents", build_continent...
method index_countries (line 96) | def index_countries = index_documents("countries", build_country_doc...
method index_states (line 97) | def index_states = index_documents("states", build_state_documents)
method index_uk_nations (line 98) | def index_uk_nations = index_documents("UK nations", build_uk_nation...
method index_cities (line 99) | def index_cities = index_documents("cities", build_city_documents)
method index_online (line 100) | def index_online = index_documents("online location", [build_online_...
method index_city (line 102) | def index_city(city)
method remove_city (line 113) | def remove_city(city)
method index_documents (line 123) | def index_documents(name, documents)
method search (line 130) | def search(query, type: nil, limit: 10)
method build_online_document (line 152) | def build_online_document
method build_continent_documents (line 167) | def build_continent_documents
method build_country_documents (line 196) | def build_country_documents
method build_state_documents (line 218) | def build_state_documents
method build_uk_nation_documents (line 253) | def build_uk_nation_documents
method build_city_documents (line 273) | def build_city_documents
method build_city_document (line 277) | def build_city_document(city)
method normalize_coordinates (line 296) | def normalize_coordinates(coords)
method countries_with_events (line 303) | def countries_with_events
method states_with_events (line 309) | def states_with_events
method states_with_users (line 318) | def states_with_users
method coordinates_for_state (line 327) | def coordinates_for_state(country_code, state_code)
method coordinates_for_location (line 331) | def coordinates_for_location(events_scope)
FILE: app/models/session.rb
class Session (line 23) | class Session < ApplicationRecord
method sign_out_siblings! (line 31) | def sign_out_siblings!
FILE: app/models/speaker.rb
class Speaker (line 38) | class Speaker < ApplicationRecord
method title (line 39) | def title
FILE: app/models/sponsor.rb
class Sponsor (line 26) | class Sponsor < ApplicationRecord
method normalize_tier (line 36) | def normalize_tier
FILE: app/models/stamp.rb
class Stamp (line 1) | class Stamp
method all (line 15) | def all
method country_stamps (line 19) | def country_stamps
method event_stamps (line 23) | def event_stamps
method contributor_stamp (line 27) | def contributor_stamp
method passport_stamp (line 31) | def passport_stamp
method triathlon_2025_stamp (line 35) | def triathlon_2025_stamp
method conference_speaker_stamp (line 39) | def conference_speaker_stamp
method meetup_speaker_stamp (line 43) | def meetup_speaker_stamp
method attend_one_event_stamp (line 47) | def attend_one_event_stamp
method ruby_30th_anniversary (line 51) | def ruby_30th_anniversary
method online_stamp (line 55) | def online_stamp
method for (line 59) | def for(country_code: nil, state_code: nil, events: nil)
method for_user (line 90) | def for_user(user)
method for_event (line 128) | def for_event(event)
method user_attended_triathlon_2025? (line 139) | def user_attended_triathlon_2025?(user)
method user_spoke_at_conference? (line 146) | def user_spoke_at_conference?(user)
method user_spoke_at_meetup? (line 150) | def user_spoke_at_meetup?(user)
method user_attended_conference? (line 154) | def user_attended_conference?(user)
method user_attended_online_event? (line 158) | def user_attended_online_event?(user)
method grouped_by_continent (line 162) | def grouped_by_continent
method missing_for_events (line 171) | def missing_for_events
method asset_path (line 183) | def asset_path
method has_country? (line 193) | def has_country?
method has_event? (line 197) | def has_event?
method event (line 201) | def event
method load_stamps_from_filesystem (line 207) | def self.load_stamps_from_filesystem
method create_stamp_from_code (line 227) | def self.create_stamp_from_code(stamp_code)
method create_stamp_from_event_file (line 260) | def self.create_stamp_from_event_file(file, images_directory)
method uk_subdivisions_covered? (line 290) | def self.uk_subdivisions_covered?
FILE: app/models/state.rb
class State (line 1) | class State
method initialize (line 8) | def initialize(country:, record:)
method name (line 13) | def name
method code (line 17) | def code
method slug (line 21) | def slug
method abbreviation (line 25) | def abbreviation
method display_name (line 29) | def display_name
method path (line 37) | def path
method past_path (line 45) | def past_path
method users_path (line 49) | def users_path
method meetups_path (line 53) | def meetups_path
method cities_path (line 57) | def cities_path
method stamps_path (line 61) | def stamps_path
method map_path (line 65) | def map_path
method has_routes? (line 69) | def has_routes?
method to_param (line 73) | def to_param
method == (line 77) | def ==(other)
method eql? (line 81) | def eql?(other)
method hash (line 85) | def hash
method events (line 89) | def events
method users (line 93) | def users
method cities (line 97) | def cities
method stamps (line 101) | def stamps
method alpha2 (line 106) | def alpha2
method country_code (line 110) | def country_code
method geocoded? (line 114) | def geocoded?
method bounds (line 118) | def bounds
method to_location (line 122) | def to_location
method supported_country? (line 127) | def supported_country?(country)
method find (line 133) | def find(country:, term:)
method find_by_slug (line 148) | def find_by_slug(slug)
method find_by_code (line 154) | def find_by_code(code, country: nil)
method find_by_name (line 170) | def find_by_name(name, country: nil)
method all (line 186) | def all
method for_country (line 190) | def for_country(country)
method select_options (line 198) | def select_options(country: nil)
FILE: app/models/static/backends/array_backend.rb
type Static::Backends (line 1) | module Static::Backends
class ArrayBackend (line 2) | class ArrayBackend < FileBackend
method load (line 3) | def load(...)
FILE: app/models/static/backends/file_backend.rb
type Static::Backends (line 1) | module Static::Backends
class FileBackend (line 2) | class FileBackend
method initialize (line 3) | def initialize(file_path, backend: FrozenRecord::Backends::Yaml)
method filename (line 8) | def filename(_model_name = nil)
method load (line 12) | def load(file_path = @file_path)
FILE: app/models/static/backends/multi_file_backend.rb
type Static::Backends (line 1) | module Static::Backends
class MultiFileBackend (line 2) | class MultiFileBackend
method initialize (line 3) | def initialize(glob, backend: FrozenRecord::Backends::Yaml)
method filename (line 8) | def filename(_model_name = nil)
method load (line 12) | def load(file_path = @glob)
FILE: app/models/static/city.rb
type Static (line 3) | module Static
class City (line 4) | class City < FrozenRecord::Base
method import_all! (line 10) | def self.import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import! (line 14) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method find_by_yaml_aliases (line 46) | def find_by_yaml_aliases
FILE: app/models/static/event.rb
type Static (line 1) | module Static
class Event (line 2) | class Event < FrozenRecord::Base
method find_by_slug (line 11) | def find_by_slug(slug)
method import_all! (line 16) | def import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import_meetups! (line 20) | def import_meetups!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import_recent! (line 24) | def import_recent!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method create (line 29) | def create(
method featured? (line 129) | def featured?
method today? (line 133) | def today?
method within_next_days? (line 153) | def within_next_days?
method past? (line 175) | def past?
method conference? (line 185) | def conference?
method meetup? (line 189) | def meetup?
method retreat? (line 193) | def retreat?
method hackathon? (line 197) | def hackathon?
method slug (line 201) | def slug
method imported? (line 209) | def imported?
method event_record (line 213) | def event_record
method start_date (line 217) | def start_date
method end_date (line 223) | def end_date
method published_date (line 229) | def published_date
method country (line 235) | def country
method city (line 241) | def city
method home_sort_date (line 248) | def home_sort_date(event_record: nil)
method static_series (line 274) | def static_series
method import! (line 278) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import_event! (line 294) | def import_event!
method import_cfps! (line 334) | def import_cfps!(event)
method import_videos! (line 352) | def import_videos!(event, index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import_sponsors! (line 376) | def import_sponsors!(event)
method import_involvements! (line 427) | def import_involvements!(event)
method import_transcripts! (line 506) | def import_transcripts!(event)
method series_slug (line 538) | def series_slug
method __file_path (line 542) | def __file_path
FILE: app/models/static/event_series.rb
type Static (line 1) | module Static
class EventSeries (line 2) | class EventSeries < FrozenRecord::Base
method find_by_slug (line 9) | def find_by_slug(slug)
method import_all! (line 14) | def import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import_all_series! (line 18) | def import_all_series!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method create (line 22) | def create(
method slug (line 99) | def slug
method event_series_record (line 103) | def event_series_record
method import_series! (line 107) | def import_series!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import! (line 129) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method events (line 135) | def events
FILE: app/models/static/speaker.rb
type Static (line 3) | module Static
class Speaker (line 4) | class Speaker < FrozenRecord::Base
method import_all! (line 10) | def self.import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method import! (line 26) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
FILE: app/models/static/sponsor.rb
type Static (line 1) | module Static
class Sponsor (line 2) | class Sponsor < FrozenRecord::Base
FILE: app/models/static/topic.rb
type Static (line 1) | module Static
class Topic (line 2) | class Topic < FrozenRecord::Base
method import_all! (line 8) | def self.import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method name (line 14) | def name
method import! (line 18) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
FILE: app/models/static/transcript.rb
type Static (line 1) | module Static
class Transcript (line 2) | class Transcript < FrozenRecord::Base
method find_by_video_id (line 9) | def find_by_video_id(video_id)
method import_all! (line 14) | def import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method video_id (line 19) | def video_id
method cues (line 23) | def cues
method to_transcript (line 27) | def to_transcript
method import! (line 41) | def import!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method event_slug (line 53) | def event_slug
method series_slug (line 59) | def series_slug
method __file_path (line 65) | def __file_path
FILE: app/models/static/video.rb
type Static (line 1) | module Static
class Video (line 2) | class Video < FrozenRecord::Base
method child_talks (line 8) | def self.child_talks
method child_talks_map (line 12) | def self.child_talks_map
method all_talks (line 16) | def self.all_talks
method all_talks_map (line 20) | def self.all_talks_map
method find_child_talk_by_id (line 24) | def self.find_child_talk_by_id(id)
method find_by_static_id (line 28) | def self.find_by_static_id(id)
method where_event_slug (line 32) | def self.where_event_slug(event_slug)
method import_all! (line 36) | def self.import_all!(index: SEARCH_INDEX_ON_IMPORT_DEFAULT)
method raw_title (line 40) | def raw_title
method description (line 44) | def description
method start_cue (line 48) | def start_cue
method end_cue (line 52) | def end_cue
method thumbnail_cue (line 56) | def thumbnail_cue
method duration_fs (line 60) | def duration_fs
method duration_to_formatted_cue (line 64) | def duration_to_formatted_cue(duration)
method duration (line 68) | def duration
method duration_in_seconds (line 72) | def duration_in_seconds
method start_cue_in_seconds (line 76) | def start_cue_in_seconds
method end_cue_in_seconds (line 80) | def end_cue_in_seconds
method thumbnail_cue_in_seconds (line 84) | def thumbnail_cue_in_seconds
method convert_cue_to_seconds (line 88) | def convert_cue_to_seconds(cue)
method speakers (line 96) | def speakers
method talks (line 102) | def talks
method meta_talk? (line 110) | def meta_talk?
method import! (line 114) | def import!(event: nil, parent_talk: nil, index: SEARCH_INDEX_ON_IMP...
method find_event (line 140) | def find_event
method __file_path (line 147) | def __file_path
FILE: app/models/sticker.rb
class Sticker (line 1) | class Sticker
method all (line 11) | def self.all
method for_event (line 15) | def self.for_event(event)
method asset_path (line 26) | def asset_path
method event (line 30) | def event
method load_stickers_from_filesystem (line 36) | def self.load_stickers_from_filesystem
method create_sticker_from_event_file (line 46) | def self.create_sticker_from_event_file(file, images_directory)
FILE: app/models/suggestion.rb
class Suggestion (line 30) | class Suggestion < ApplicationRecord
method approved! (line 47) | def approved!(approver:)
method notice (line 54) | def notice
FILE: app/models/talk.rb
class Talk (line 63) | class Talk < ApplicationRecord
method speaker_role_titles (line 151) | def self.speaker_role_titles
method find_by_slug_or_alias (line 169) | def self.find_by_slug_or_alias(slug)
method formatted_kind (line 179) | def formatted_kind
method managed_by? (line 247) | def managed_by?(visiting_user)
method published? (line 254) | def published?
method video_available? (line 258) | def video_available?
method video_unavailable? (line 262) | def video_unavailable?
method check_video_availability! (line 266) | def check_video_availability!
method validate_thumbnail! (line 288) | def validate_thumbnail!
method to_meta_tags (line 305) | def to_meta_tags
method thumbnail_xs (line 331) | def thumbnail_xs
method thumbnail_sm (line 335) | def thumbnail_sm
method thumbnail_md (line 339) | def thumbnail_md
method thumbnail_lg (line 343) | def thumbnail_lg
method thumbnail_xl (line 347) | def thumbnail_xl
method thumbnail_classes (line 351) | def thumbnail_classes
method fallback_thumbnail (line 355) | def fallback_thumbnail
method thumbnail_url (line 359) | def thumbnail_url(size:, request:)
method thumbnail (line 369) | def thumbnail(size = :thumbnail_lg)
method external_player_utm_params (line 417) | def external_player_utm_params
method external_player_url (line 426) | def external_player_url
method provider_url (line 437) | def provider_url
method related_talks (line 460) | def related_talks(limit: 6)
method formatted_date (line 468) | def formatted_date
method formatted_duration (line 472) | def formatted_duration
method duration (line 476) | def duration
method speakers (line 482) | def speakers
method feedback_allowed? (line 488) | def feedback_allowed?
method feedback_allowed_for? (line 492) | def feedback_allowed_for?(user)
method speaker? (line 499) | def speaker?(user)
method speaker_names (line 505) | def speaker_names
method event_names (line 509) | def event_names
method language_name (line 523) | def language_name
method location (line 527) | def location
method to_location (line 531) | def to_location
method slug_candidates (line 539) | def slug_candidates
method unused_slugs (line 553) | def unused_slugs
method event_name (line 561) | def event_name
method fetch_and_update_raw_transcript! (line 567) | def fetch_and_update_raw_transcript!
method fetch_duration_from_youtube! (line 576) | def fetch_duration_from_youtube!
method update_from_yml_metadata! (line 583) | def update_from_yml_metadata!(event: nil)
method static_metadata (line 647) | def static_metadata
method suggestion_summary (line 651) | def suggestion_summary
method set_kind (line 659) | def set_kind
method to_mobile_json (line 701) | def to_mobile_json(request)
method parent_talk_id_cannot_be_self (line 716) | def parent_talk_id_cannot_be_self
FILE: app/models/talk/agents.rb
class Talk::Agents (line 1) | class Talk::Agents < ActiveRecord::AssociatedObject
method improve_transcript (line 8) | def improve_transcript
method summarize (line 22) | def summarize
method analyze_topics (line 36) | def analyze_topics
method ingest (line 58) | def ingest
method client (line 67) | def client
FILE: app/models/talk/downloader.rb
class Talk::Downloader (line 1) | class Talk::Downloader < ActiveRecord::AssociatedObject
method bin (line 2) | def bin
method download_path (line 6) | def download_path
method downloaded? (line 10) | def downloaded?
method downloadable? (line 14) | def downloadable?
method download! (line 18) | def download!
FILE: app/models/talk/index.rb
class Talk::Index (line 1) | class Talk::Index < ApplicationRecord
method search (line 10) | def self.search(query)
method snippets (line 21) | def self.snippets(**)
method snippet (line 25) | def self.snippet(column, tag: "mark", omission: "…", limit: 32)
method reindex (line 30) | def reindex
method remove_invalid_search_characters (line 39) | def self.remove_invalid_search_characters(query)
method remove_unbalanced_quotes (line 43) | def self.remove_unbalanced_quotes(query)
FILE: app/models/talk/similar_recommender.rb
class Talk::SimilarRecommender (line 1) | class Talk::SimilarRecommender < ActiveRecord::AssociatedObject
method talks (line 2) | def talks(limit: 12)
method topic_ids (line 19) | def topic_ids
method topics (line 23) | def topics
FILE: app/models/talk/sqlite_fts_searchable.rb
type Talk::SQLiteFTSSearchable (line 3) | module Talk::SQLiteFTSSearchable
function title_with_snippet (line 33) | def title_with_snippet
function fts_index (line 37) | def fts_index
function reindex_fts (line 41) | def reindex_fts
FILE: app/models/talk/thumbnails.rb
class Talk::Thumbnails (line 1) | class Talk::Thumbnails < ActiveRecord::AssociatedObject
method path (line 2) | def path
method extractable? (line 6) | def extractable?
method start_cues (line 10) | def start_cues
method extracted? (line 14) | def extracted?
method extract! (line 18) | def extract!(force: false, download: false)
method extract_thumbnail (line 58) | def extract_thumbnail(timestamp, input_file, output_file)
method directory (line 64) | def directory
FILE: app/models/talk/transcript.rb
class Talk::Transcript (line 21) | class Talk::Transcript < ApplicationRecord
method transcript (line 29) | def transcript
FILE: app/models/talk/typesense_searchable.rb
type Talk::TypesenseSearchable (line 3) | module Talk::TypesenseSearchable
function typesense_synonyms_from_aliases (line 289) | def typesense_synonyms_from_aliases
function trigger_typesense_job (line 307) | def trigger_typesense_job(record, remove)
function typesense_search_talks (line 311) | def typesense_search_talks(query, options = {})
function should_index? (line 372) | def should_index?
FILE: app/models/talk_topic.rb
class TalkTopic (line 25) | class TalkTopic < ApplicationRecord
method reset_topic_counter_cache (line 31) | def reset_topic_counter_cache
FILE: app/models/template.rb
class Template (line 1) | class Template
method initialize (line 40) | def initialize(attributes = {})
method persisted? (line 45) | def persisted?
method to_param (line 49) | def to_param
method children_attributes= (line 53) | def children_attributes=(attributes)
method valid? (line 59) | def valid?
method has_children? (line 71) | def has_children?
method to_hash (line 75) | def to_hash
method transform_attributes (line 81) | def transform_attributes(hash)
method to_yaml (line 92) | def to_yaml
method time_to_cue (line 98) | def time_to_cue(time)
method parse_speakers (line 104) | def parse_speakers(data)
FILE: app/models/todo.rb
class Todo (line 3) | class Todo < Data.define(:file, :line, :column, :content)
method all (line 6) | def self.all
method for_path (line 10) | def self.for_path(path, prefix: nil)
method url (line 33) | def url
method git_ref (line 41) | def git_ref
method github_url (line 45) | def github_url
method local_url (line 52) | def local_url
method normalized_content (line 58) | def normalized_content
method series_slug (line 66) | def series_slug
method event_slug (line 70) | def event_slug
FILE: app/models/topic.rb
class Topic (line 30) | class Topic < ApplicationRecord
method create_from_list (line 63) | def self.create_from_list(topics, status: :pending)
method assign_canonical_topic! (line 70) | def assign_canonical_topic!(canonical_topic:)
method primary_topic (line 86) | def primary_topic
method to_meta_tags (line 90) | def to_meta_tags
method rejected! (line 110) | def rejected!
FILE: app/models/topic/agents.rb
class Topic::Agents (line 1) | class Topic::Agents < ActiveRecord::AssociatedObject
method find_talks (line 6) | def find_talks
method find_candidate_talks (line 27) | def find_candidate_talks
method name_variants (line 38) | def name_variants
method filter_with_ai (line 57) | def filter_with_ai(candidates)
method client (line 79) | def client
FILE: app/models/topic/gem_info.rb
class Topic::GemInfo (line 1) | class Topic::GemInfo < ActiveRecord::AssociatedObject
method gems (line 2) | def gems
method gem? (line 6) | def gem?
method primary_gem (line 10) | def primary_gem
FILE: app/models/topic/typesense_searchable.rb
type Topic::TypesenseSearchable (line 3) | module Topic::TypesenseSearchable
function trigger_typesense_job (line 58) | def trigger_typesense_job(record, remove)
function typesense_search_topics (line 62) | def typesense_search_topics(query, options = {})
function should_index? (line 93) | def should_index?
FILE: app/models/topic_gem.rb
class TopicGem (line 21) | class TopicGem < ApplicationRecord
method info (line 26) | def info
method downloads (line 32) | def downloads
method version (line 36) | def version
method version_created_at (line 40) | def version_created_at
method authors (line 47) | def authors
method author_names (line 51) | def author_names
method author_users (line 57) | def author_users
method owners (line 63) | def owners
method owner_handles (line 69) | def owner_handles
method owner_users (line 73) | def owner_users
method maintainers (line 79) | def maintainers
method summary (line 84) | def summary
method licenses (line 88) | def licenses
method license (line 92) | def license
method homepage_url (line 96) | def homepage_url
method source_code_url (line 100) | def source_code_url
method documentation_url (line 104) | def documentation_url
method changelog_url (line 108) | def changelog_url
method bug_tracker_url (line 112) | def bug_tracker_url
method mailing_list_url (line 116) | def mailing_list_url
method wiki_url (line 120) | def wiki_url
method funding_url (line 124) | def funding_url
method runtime_dependencies (line 128) | def runtime_dependencies
method development_dependencies (line 132) | def development_dependencies
method rubygems_url (line 136) | def rubygems_url
method github_repo (line 140) | def github_repo
method github_url (line 148) | def github_url
method fetch_gem_info (line 156) | def fetch_gem_info
method fetch_owners (line 163) | def fetch_owners
method cache_key (line 170) | def cache_key
method owners_cache_key (line 174) | def owners_cache_key
FILE: app/models/transcript.rb
class Transcript (line 1) | class Transcript
method initialize (line 6) | def initialize(cues: [])
method add_cue (line 10) | def add_cue(cue)
method to_h (line 14) | def to_h
method to_json (line 18) | def to_json
method to_text (line 22) | def to_text
method to_vtt (line 26) | def to_vtt
method presence (line 35) | def presence
method present? (line 39) | def present?
method each (line 43) | def each(&)
method create_from_vtt (line 48) | def create_from_vtt(vtt_content)
method create_from_youtube_transcript (line 106) | def create_from_youtube_transcript(youtube_transcript)
method create_from_json (line 119) | def create_from_json(json)
method format_time (line 128) | def format_time(ms)
FILE: app/models/uk_nation.rb
class UKNation (line 1) | class UKNation
method initialize (line 4) | def initialize(slug)
method find_by_code (line 11) | def self.find_by_code(code)
method name (line 28) | def name
method alpha2 (line 32) | def alpha2
method alpha3 (line 36) | def alpha3
method continent (line 40) | def continent
method continent_name (line 44) | def continent_name
method emoji_flag (line 48) | def emoji_flag
method path (line 52) | def path
method past_path (line 56) | def past_path
method users_path (line 60) | def users_path
method meetups_path (line 64) | def meetups_path
method cities_path (line 68) | def cities_path
method stamps_path (line 72) | def stamps_path
method map_path (line 76) | def map_path
method has_routes? (line 80) | def has_routes?
method code (line 84) | def code
method country_code (line 88) | def country_code
method to_param (line 92) | def to_param
method == (line 96) | def ==(other)
method eql? (line 100) | def eql?(other)
method hash (line 104) | def hash
method events (line 108) | def events
method users (line 112) | def users
method cities (line 116) | def cities
method stamps (line 120) | def stamps
method bounds (line 124) | def bounds
method held_in_sentence (line 128) | def held_in_sentence
method uk_nation? (line 132) | def uk_nation?
method states? (line 136) | def states?
method parent_country (line 140) | def parent_country
method to_location (line 144) | def to_location
FILE: app/models/user.rb
class User (line 53) | class User < ApplicationRecord
method normalize_github_handle (line 205) | def self.normalize_github_handle(value)
method reset_talks_counts (line 212) | def self.reset_talks_counts
method find_by_github_handle (line 218) | def self.find_by_github_handle(handle)
method find_by_name_or_alias (line 223) | def self.find_by_name_or_alias(name)
method find_by_slug_or_alias (line 233) | def self.find_by_slug_or_alias(slug)
method default_watch_list (line 244) | def default_watch_list
method main_participation_to (line 248) | def main_participation_to(event)
method title (line 253) | def title
method country (line 257) | def country
method to_location (line 263) | def to_location
method canonical_slug (line 267) | def canonical_slug
method verified? (line 271) | def verified?
method indexable? (line 275) | def indexable?
method ruby_passport_claimed? (line 282) | def ruby_passport_claimed?
method possessive_pronoun (line 286) | def possessive_pronoun
method contributor? (line 290) | def contributor?
method managed_by? (line 294) | def managed_by?(visiting_user)
method avatar_url (line 301) | def avatar_url(...)
method avatar_rank (line 305) | def avatar_rank
method custom_avatar? (line 312) | def custom_avatar?
method bsky_avatar_url (line 316) | def bsky_avatar_url(...)
method github_avatar_url (line 320) | def github_avatar_url(size: 200)
method fallback_avatar_url (line 330) | def fallback_avatar_url(size: 200)
method broadcast_header (line 336) | def broadcast_header
method to_meta_tags (line 340) | def to_meta_tags
method to_combobox_display (line 366) | def to_combobox_display
method meta_description (line 370) | def meta_description
method assign_canonical_speaker! (line 384) | def assign_canonical_speaker!(canonical_speaker:)
method primary_speaker (line 388) | def primary_speaker
method suggestion_summary (line 392) | def suggestion_summary
method to_mobile_json (line 402) | def to_mobile_json(request)
method assign_canonical_user! (line 412) | def assign_canonical_user!(canonical_user:)
method set_slug (line 449) | def set_slug
method create_alias_for_previous_name (line 454) | def create_alias_for_previous_name
method speakerdeck_user_from_slides_url (line 467) | def speakerdeck_user_from_slides_url
method to_param (line 477) | def to_param
FILE: app/models/user/duplicate_detector.rb
class User::DuplicateDetector (line 1) | class User::DuplicateDetector < ActiveRecord::AssociatedObject
method reversed_name_duplicates (line 3) | def reversed_name_duplicates
method same_name_duplicates (line 15) | def same_name_duplicates
method reversed_name (line 37) | def reversed_name
method normalized_name (line 43) | def normalized_name
method potential_duplicates_by_reversed_name (line 49) | def potential_duplicates_by_reversed_name
method potential_duplicates_by_normalized_name (line 53) | def potential_duplicates_by_normalized_name
method has_reversed_name_duplicate? (line 64) | def has_reversed_name_duplicate?
method has_same_name_duplicate? (line 68) | def has_same_name_duplicate?
method has_any_duplicate? (line 72) | def has_any_duplicate?
method reversed_name_duplicate_ids (line 114) | def self.reversed_name_duplicate_ids
method same_name_duplicate_ids (line 158) | def self.same_name_duplicate_ids
method all_duplicate_ids (line 162) | def self.all_duplicate_ids
method find_all_reversed_name_duplicates (line 184) | def self.find_all_reversed_name_duplicates
method find_all_same_name_duplicates (line 211) | def self.find_all_same_name_duplicates
method report (line 219) | def self.report
FILE: app/models/user/index.rb
class User::Index (line 1) | class User::Index < ApplicationRecord
method search (line 10) | def self.search(query)
method snippets (line 21) | def self.snippets(**)
method snippet (line 25) | def self.snippet(column, tag: "mark", omission: "…", limit: 32)
method reindex (line 30) | def reindex
method remove_invalid_search_characters (line 34) | def self.remove_invalid_search_characters(query)
method remove_unbalanced_quotes (line 38) | def self.remove_unbalanced_quotes(query)
FILE: app/models/user/profiles.rb
class User::Profiles (line 1) | class User::Profiles < ActiveRecord::AssociatedObject
method enhance_all_later (line 4) | def enhance_all_later
method enhance_with_github (line 9) | def enhance_with_github
method enhance_with_bsky (line 33) | def enhance_with_bsky(force: false)
method github_client (line 42) | def github_client
FILE: app/models/user/speakerdeck_feed.rb
class User::SpeakerdeckFeed (line 4) | class User::SpeakerdeckFeed < ActiveRecord::AssociatedObject
method username (line 5) | def username
method has_feed? (line 9) | def has_feed?
method feed (line 13) | def feed
method decks (line 19) | def decks
method unused_decks (line 25) | def unused_decks
class FeedData (line 33) | class FeedData
method initialize (line 36) | def initialize(username)
method fetch! (line 42) | def fetch!
method decks (line 67) | def decks
method decks_count (line 73) | def decks_count
method find_unused_decks (line 79) | def find_unused_decks
class Deck (line 88) | class Deck
method initialize (line 91) | def initialize(rss_item)
method to_h (line 99) | def to_h
FILE: app/models/user/sqlite_fts_searchable.rb
type User::SQLiteFTSSearchable (line 3) | module User::SQLiteFTSSearchable
function name_with_snippet (line 24) | def name_with_snippet
function fts_index (line 28) | def fts_index
function reindex_fts (line 32) | def reindex_fts
FILE: app/models/user/suspicion_detector.rb
class User::SuspicionDetector (line 1) | class User::SuspicionDetector < ActiveRecord::AssociatedObject
method suspicious? (line 26) | def suspicious?
method suspicion_cleared? (line 30) | def suspicion_cleared?
method mark_suspicious! (line 34) | def mark_suspicious!
method clear_suspicion! (line 41) | def clear_suspicion!
method unclear_suspicion! (line 45) | def unclear_suspicion!
method github_account_newer_than? (line 49) | def github_account_newer_than?(duration)
method calculate_suspicious? (line 59) | def calculate_suspicious?
method signals (line 67) | def signals
method signal_count (line 77) | def signal_count
method github_account_new? (line 81) | def github_account_new?
method no_talks? (line 90) | def no_talks?
method no_watched_talks? (line 94) | def no_watched_talks?
method bio_contains_url? (line 98) | def bio_contains_url?
method github_account_empty? (line 104) | def github_account_empty?
FILE: app/models/user/talk_recommender.rb
class User::TalkRecommender (line 1) | class User::TalkRecommender < ActiveRecord::AssociatedObject
method talks (line 2) | def talks(limit: 4)
method watched_talk_ids (line 12) | def watched_talk_ids
method collaborative_filtering_recommendations (line 16) | def collaborative_filtering_recommendations(limit:)
method content_based_recommendations (line 35) | def content_based_recommendations(limit:)
FILE: app/models/user/typesense_searchable.rb
type User::TypesenseSearchable (line 3) | module User::TypesenseSearchable
function typesense_synonyms_from_aliases (line 107) | def typesense_synonyms_from_aliases
function trigger_typesense_job (line 125) | def trigger_typesense_job(record, remove)
function typesense_search_speakers (line 129) | def typesense_search_speakers(query, options = {})
function should_index? (line 165) | def should_index?
FILE: app/models/user/watched_talk_seeder.rb
class User::WatchedTalkSeeder (line 1) | class User::WatchedTalkSeeder < ActiveRecord::AssociatedObject
method seed_development_data (line 2) | def seed_development_data
method seed_overlapping_patterns (line 16) | def seed_overlapping_patterns(watchable_talks, existing_users)
FILE: app/models/user/wrapped_image_generator.rb
class User::WrappedImageGenerator (line 4) | class User::WrappedImageGenerator
method initialize (line 11) | def initialize(user)
method generate (line 15) | def generate
method save_to_storage (line 49) | def save_to_storage
method exists? (line 65) | def exists?
method render_html (line 71) | def render_html
method wrap_in_html_document (line 80) | def wrap_in_html_document(partial_html)
FILE: app/models/user/wrapped_screenshot_generator.rb
class User::WrappedScreenshotGenerator (line 3) | class User::WrappedScreenshotGenerator
method initialize (line 14) | def initialize(user, orientation: :vertical)
method generate (line 19) | def generate
method save_to_storage (line 42) | def save_to_storage
method exists? (line 60) | def exists?
method generate_all (line 68) | def self.generate_all(user)
method render_card_html (line 78) | def render_card_html
method build_assigns (line 86) | def build_assigns
method render_partial (line 144) | def render_partial(partial_name, locals)
method wrap_in_html_document (line 151) | def wrap_in_html_document(partial_html)
method wrap_horizontal_html_document (line 168) | def wrap_horizontal_html_document(partial_html)
method wrap_vertical_html_document (line 274) | def wrap_vertical_html_document(partial_html)
method browser_options (line 400) | def browser_options(dimensions)
method chrome_ws_url (line 422) | def chrome_ws_url
method determine_personality (line 434) | def determine_personality(top_topics)
FILE: app/models/user_talk.rb
class UserTalk (line 24) | class UserTalk < ApplicationRecord
method update_user_talks_count (line 39) | def update_user_talks_count
FILE: app/models/watch_list.rb
class WatchList (line 20) | class WatchList < ApplicationRecord
FILE: app/models/watch_list_talk.rb
class WatchListTalk (line 25) | class WatchListTalk < ApplicationRecord
method reset_watch_list_counter_cache (line 32) | def reset_watch_list_counter_cache
FILE: app/models/watched_talk.rb
class WatchedTalk (line 24) | class WatchedTalk < ApplicationRecord
method set_default_watched_at (line 86) | def set_default_watched_at
method progress_percentage (line 92) | def progress_percentage
method has_feedback? (line 99) | def has_feedback?
method has_rating_feedback? (line 103) | def has_rating_feedback?
method in_progress? (line 109) | def in_progress?
method watched_on_options_for (line 113) | def self.watched_on_options_for(talk)
FILE: app/models/youtube/null_parser.rb
type YouTube (line 3) | module YouTube
class NullParser (line 4) | class NullParser
method initialize (line 5) | def initialize(metadata:, event_name:, options: {})
method cleaned (line 10) | def cleaned
FILE: app/models/youtube/video_metadata.rb
type YouTube (line 8) | module YouTube
class VideoMetadata (line 9) | class VideoMetadata
method initialize (line 13) | def initialize(metadata:, event_name:, options: {})
method cleaned (line 18) | def cleaned
method keynote? (line 35) | def keynote?
method lighting? (line 39) | def lighting?
method extract_info_from_title (line 45) | def extract_info_from_title
method speakers (line 56) | def speakers
method raw_title (line 63) | def raw_title
method title_without_event_name (line 67) | def title_without_event_name
method remove_leading_and_trailing_separators_from (line 74) | def remove_leading_and_trailing_separators_from(title)
method title (line 80) | def title
method description (line 91) | def description
FILE: app/models/youtube/video_metadata_baltic_ruby_2024.rb
type YouTube (line 1) | module YouTube
class VideoMetadataBalticRuby2024 (line 2) | class VideoMetadataBalticRuby2024
method initialize (line 6) | def initialize(metadata:, event_name:, options: {})
method cleaned (line 11) | def cleaned
method keynote? (line 26) | def keynote?
method speakers (line 32) | def speakers
method raw_title (line 46) | def raw_title
method title_without_event_name (line 50) | def title_without_event_name
method remove_leading_and_trailing_separators_from (line 54) | def remove_leading_and_trailing_separators_from(title)
method title (line 62) | def title
method raw_description (line 68) | def raw_description
method description_without_speaker (line 72) | def description_without_speaker
FILE: app/models/youtube/video_metadata_kaigi_on_rails.rb
type YouTube (line 1) | module YouTube
class VideoMetadataKaigiOnRails (line 2) | class VideoMetadataKaigiOnRails < VideoMetadata
method title (line 4) | def title
method speakers (line 14) | def speakers
method title_parts (line 20) | def title_parts
FILE: app/models/youtube/video_metadata_rails_world.rb
type YouTube (line 8) | module YouTube
class VideoMetadataRailsWorld (line 9) | class VideoMetadataRailsWorld
method initialize (line 13) | def initialize(metadata:, event_name:, options: {})
method cleaned (line 18) | def cleaned
method keynote? (line 33) | def keynote?
method extract_info_from_title (line 39) | def extract_info_from_title
method speakers (line 50) | def speakers
method raw_title (line 54) | def raw_title
method title_without_event_name (line 58) | def title_without_event_name
method remove_leading_and_trailing_separators_from (line 65) | def remove_leading_and_trailing_separators_from(title)
method title (line 73) | def title
method description (line 77) | def description
method raw_title_parts (line 81) | def raw_title_parts
FILE: app/schemas/address_schema.rb
class AddressSchema (line 3) | class AddressSchema < RubyLLM::Schema
FILE: app/schemas/cfp_schema.rb
class CFPSchema (line 3) | class CFPSchema < RubyLLM::Schema
FILE: app/schemas/coordinates_schema.rb
class CoordinatesSchema (line 3) | class CoordinatesSchema < RubyLLM::Schema
FILE: app/schemas/event_schema.rb
class EventSchema (line 3) | class EventSchema < RubyLLM::Schema
FILE: app/schemas/featured_city_schema.rb
class FeaturedCitySchema (line 3) | class FeaturedCitySchema < RubyLLM::Schema
method to_json_schema (line 12) | def to_json_schema
FILE: app/schemas/hotel_schema.rb
class HotelSchema (line 3) | class HotelSchema < RubyLLM::Schema
FILE: app/schemas/involvement_schema.rb
class InvolvementSchema (line 3) | class InvolvementSchema < RubyLLM::Schema
FILE: app/schemas/location_schema.rb
class LocationSchema (line 3) | class LocationSchema < RubyLLM::Schema
FILE: app/schemas/maps_schema.rb
class MapsSchema (line 3) | class MapsSchema < RubyLLM::Schema
FILE: app/schemas/schedule_schema.rb
class ScheduleSchema (line 3) | class ScheduleSchema < RubyLLM::Schema
FILE: app/schemas/series_schema.rb
class SeriesSchema (line 3) | class SeriesSchema < RubyLLM::Schema
FILE: app/schemas/speaker_schema.rb
class SpeakerSchema (line 3) | class SpeakerSchema < RubyLLM::Schema
FILE: app/schemas/sponsors_schema.rb
class SponsorsSchema (line 3) | class SponsorsSchema < RubyLLM::Schema
FILE: app/schemas/transcript_schema.rb
class TranscriptSchema (line 3) | class TranscriptSchema < RubyLLM::Schema
FILE: app/schemas/venue_schema.rb
class VenueSchema (line 3) | class VenueSchema < RubyLLM::Schema
FILE: app/schemas/video_schema.rb
class VideoSchema (line 3) | class VideoSchema < RubyLLM::Schema
FILE: app/serializers/transcript_serializer.rb
class TranscriptSerializer (line 1) | class TranscriptSerializer
method dump (line 2) | def self.dump(transcript)
method load (line 8) | def self.load(transcript_json)
FILE: app/tools/cfp_create_tool.rb
class CFPCreateTool (line 3) | class CFPCreateTool < RubyLLM::Tool
method execute (line 12) | def execute(event_query:, link:, open_date: nil, close_date: nil, name...
method find_event (line 37) | def find_event(query)
FILE: app/tools/cfp_info_tool.rb
class CFPInfoTool (line 3) | class CFPInfoTool < RubyLLM::Tool
method execute (line 8) | def execute(event_query:)
method find_event (line 27) | def find_event(query)
method format_cfp (line 34) | def format_cfp(cfp)
method cfp_status (line 44) | def cfp_status(cfp)
FILE: app/tools/cfp_update_tool.rb
class CFPUpdateTool (line 3) | class CFPUpdateTool < RubyLLM::Tool
method execute (line 12) | def execute(event_query:, link:, open_date: nil, close_date: nil, name...
method find_event (line 37) | def find_event(query)
FILE: app/tools/event_create_tool.rb
class EventCreateTool (line 3) | class EventCreateTool < RubyLLM::Tool
method execute (line 20) | def execute(**params)
FILE: app/tools/event_lookup_tool.rb
class EventLookupTool (line 3) | class EventLookupTool < RubyLLM::Tool
method execute (line 7) | def execute(query:)
method event_to_hash (line 26) | def event_to_hash(event)
FILE: app/tools/event_series_create_tool.rb
class EventSeriesCreateTool (line 3) | class EventSeriesCreateTool < RubyLLM::Tool
method execute (line 19) | def execute(**params)
FILE: app/tools/event_series_events_tool.rb
class EventSeriesEventsTool (line 3) | class EventSeriesEventsTool < RubyLLM::Tool
method execute (line 7) | def execute(query:)
method event_to_hash (line 31) | def event_to_hash(event, series)
FILE: app/tools/event_series_lookup_tool.rb
class EventSeriesLookupTool (line 3) | class EventSeriesLookupTool < RubyLLM::Tool
method execute (line 7) | def execute(query:)
method series_to_hash (line 26) | def series_to_hash(series)
FILE: app/tools/event_talks_tool.rb
class EventTalksTool (line 3) | class EventTalksTool < RubyLLM::Tool
method execute (line 7) | def execute(query:)
method video_to_hash (line 38) | def video_to_hash(video)
FILE: app/tools/geocode_tool.rb
class GeocodeTool (line 3) | class GeocodeTool < RubyLLM::Tool
method execute (line 7) | def execute(location:)
FILE: app/tools/github_profile_tool.rb
class GitHubProfileTool (line 3) | class GitHubProfileTool < RubyLLM::Tool
method execute (line 7) | def execute(username:)
method client (line 20) | def client
FILE: app/tools/speaker_lookup_tool.rb
class SpeakerLookupTool (line 3) | class SpeakerLookupTool < RubyLLM::Tool
method execute (line 7) | def execute(query:)
method user_to_hash (line 31) | def user_to_hash(user)
FILE: app/tools/speaker_talks_tool.rb
class SpeakerTalksTool (line 3) | class SpeakerTalksTool < RubyLLM::Tool
method execute (line 8) | def execute(name: nil, id: nil)
method find_user (line 28) | def find_user(name:, id:)
method talk_to_hash (line 38) | def talk_to_hash(talk)
FILE: app/tools/speakerdeck_deck_tool.rb
class SpeakerdeckDeckTool (line 3) | class SpeakerdeckDeckTool < RubyLLM::Tool
method execute (line 7) | def execute(url:)
method client (line 29) | def client
method normalize_url (line 33) | def normalize_url(url)
FILE: app/tools/speakerdeck_user_decks_tool.rb
class SpeakerdeckUserDecksTool (line 3) | class SpeakerdeckUserDecksTool < RubyLLM::Tool
method execute (line 7) | def execute(name:)
method deck_to_hash (line 34) | def deck_to_hash(deck)
FILE: app/tools/venue_create_tool.rb
class VenueCreateTool (line 3) | class VenueCreateTool < RubyLLM::Tool
method execute (line 11) | def execute(event_query:, venue_name:, address: nil, source_url: nil)
method find_event (line 76) | def find_event(query)
FILE: app/tools/vimeo_video_tool.rb
class VimeoVideoTool (line 3) | class VimeoVideoTool < RubyLLM::Tool
method execute (line 9) | def execute(id:)
method format_duration (line 39) | def format_duration(seconds)
FILE: a
Copy disabled (too large)
Download .json
Condensed preview — 3709 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (16,930K chars).
[
{
"path": ".annotaterb.yml",
"chars": 1277,
"preview": "---\n:position: before\n:position_in_additional_file_patterns: before\n:position_in_class: before\n:position_in_factory: bef"
},
{
"path": ".cursor/commands/plan_commands.md",
"chars": 1508,
"preview": "The user will provide a feature description. Your job is to:\n\n1. Create a technical plan that concisely describes the fe"
},
{
"path": ".cursor/rules/cursorrules.mdc",
"chars": 8581,
"preview": "---\nalwaysApply: true\n---\n\n# RubyVideo Rails 8 Project - Cursor Rules\n\nYou are working on RubyVideo, a modern Ruby on Ra"
},
{
"path": ".cursor/rules/viewcomponents.mdc",
"chars": 10083,
"preview": "---\nalwaysApply: false\n---\n\n# RubyVideo ViewComponents - UI Component Rules\n\n> **Main Project Rules**: See [cursorrules."
},
{
"path": ".devcontainer/Dockerfile",
"chars": 1181,
"preview": "ARG RUBY_VERSION=4.0.1\nFROM docker.io/library/ruby:$RUBY_VERSION\n\n# Rails app lives here\nWORKDIR /rails\n\n# Install base "
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 1000,
"preview": "{\n \"name\": \"RubyEvents Dev\",\n \"dockerComposeFile\": \"./docker-compose.yml\",\n \"service\": \"rails-app\",\n \"workspaceFolde"
},
{
"path": ".devcontainer/docker-compose.yml",
"chars": 1148,
"preview": "name: \"RubyEvents\"\n\nservices:\n rails-app:\n build:\n context: ..\n dockerfile: .devcontainer/Dockerfile\n c"
},
{
"path": ".dockerignore",
"chars": 757,
"preview": "# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.\n\n# Ignore git d"
},
{
"path": ".erb_lint.yml",
"chars": 1183,
"preview": "---\nEnableDefaultLinters: true\nglob: \"**/*.{html}{+*,}.erb\"\nexclude:\n - vendor/bundle/**/*\n - node_modules/**/*\n - tm"
},
{
"path": ".eslintignore",
"chars": 36,
"preview": "app/javascript/controllers/index.js\n"
},
{
"path": ".eslintrc",
"chars": 26,
"preview": "{ \"extends\": \"standard\" }\n"
},
{
"path": ".gitattributes",
"chars": 348,
"preview": "# See https://git-scm.com/docs/gitattributes for more about git attribute files.\n\n# Mark the database schema as having b"
},
{
"path": ".github/pull_request_template.md",
"chars": 383,
"preview": "## Description\n<!-- Please describe your changes. -->\n\n## Screenshots\n<!-- Add screenshots or GIFs if applicable. -->\n\n#"
},
{
"path": ".github/skills/event-data/SKILL.md",
"chars": 3077,
"preview": "---\nname: event-data\ndescription: Guide for handling event data. Use when asked to update event data such as CFPs, Talks"
},
{
"path": ".github/workflows/ci.yml",
"chars": 6094,
"preview": "name: CI\n\non:\n pull_request:\n branches: [\"*\"]\n push:\n branches: [main]\n\nconcurrency:\n group: ci-${{ github.ref "
},
{
"path": ".github/workflows/copilot-setup-steps.yml",
"chars": 1303,
"preview": "name: Setup Development Environment for GitHub Copilot\n\n\n# Automatically run the setup steps when they are changed to al"
},
{
"path": ".github/workflows/deploy-staging.yml",
"chars": 1378,
"preview": "name: Deploy to Staging\n\non: workflow_dispatch\n\njobs:\n deploy-staging:\n name: Deploy to Staging\n runs-on: ubuntu-"
},
{
"path": ".github/workflows/migrate-production.yml",
"chars": 586,
"preview": "name: Migrate Production\n\non: workflow_dispatch\n\njobs:\n migrate-production:\n name: Migrate Production\n runs-on: u"
},
{
"path": ".github/workflows/migrate-staging.yml",
"chars": 585,
"preview": "name: Migrate Staging\n\non: workflow_dispatch\n\njobs:\n migrate-staging:\n name: Migrate Staging\n runs-on: ubuntu-lat"
},
{
"path": ".github/workflows/release-lock-production.yml",
"chars": 563,
"preview": "name: Release Lock Production\n\non: workflow_dispatch\n\njobs:\n release-lock-production:\n name: Release Lock Production"
},
{
"path": ".github/workflows/release-lock-staging.yml",
"chars": 568,
"preview": "name: Release Lock Staging\n\non: workflow_dispatch\n\njobs:\n release-lock-staging:\n name: Release Lock Staging\n runs"
},
{
"path": ".github/workflows/seed-production.yml",
"chars": 580,
"preview": "name: Seed Production\n\non: workflow_dispatch\n\njobs:\n seed-production:\n name: Seed Production\n runs-on: ubuntu-lat"
},
{
"path": ".github/workflows/seed-staging.yml",
"chars": 578,
"preview": "name: Seed Staging\n\non: workflow_dispatch\n\njobs:\n seed-staging:\n name: Seed Staging\n runs-on: ubuntu-latest\n t"
},
{
"path": ".gitignore",
"chars": 1650,
"preview": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring t"
},
{
"path": ".herb.yml",
"chars": 664,
"preview": "# This file configures Herb for your project and team.\n# Settings here take precedence over individual editor preference"
},
{
"path": ".kamal/hooks/docker-setup.sample",
"chars": 51,
"preview": "#!/bin/sh\n\necho \"Docker set up on $KAMAL_HOSTS...\"\n"
},
{
"path": ".kamal/hooks/post-deploy.sample",
"chars": 318,
"preview": "#!/bin/sh\n\n# A sample post-deploy hook\n#\n# These environment variables are available:\n# KAMAL_RECORDED_AT\n# KAMAL_PERFOR"
},
{
"path": ".kamal/hooks/post-proxy-reboot.sample",
"chars": 55,
"preview": "#!/bin/sh\n\necho \"Rebooted kamal-proxy on $KAMAL_HOSTS\"\n"
},
{
"path": ".kamal/hooks/pre-build.sample",
"chars": 1102,
"preview": "#!/bin/sh\n\n# A sample pre-build hook\n#\n# Checks:\n# 1. We have a clean checkout\n# 2. A remote is configured\n# 3. The bran"
},
{
"path": ".kamal/hooks/pre-connect.sample",
"chars": 1038,
"preview": "#!/usr/bin/env ruby\n\n# A sample pre-connect check\n#\n# Warms DNS before connecting to hosts in parallel\n#\n# These environ"
},
{
"path": ".kamal/hooks/pre-deploy.sample",
"chars": 2523,
"preview": "#!/usr/bin/env ruby\n\n# A sample pre-deploy hook\n#\n# Checks the Github status of the build, waiting for a pending build t"
},
{
"path": ".kamal/hooks/pre-proxy-reboot.sample",
"chars": 59,
"preview": "#!/bin/sh\n\necho \"Rebooting kamal-proxy on $KAMAL_HOSTS...\"\n"
},
{
"path": ".kamal/secrets",
"chars": 1171,
"preview": "# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,\n# and accessori"
},
{
"path": ".kamal/secrets.staging",
"chars": 1091,
"preview": "# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,\n# and accessori"
},
{
"path": ".mcp.json",
"chars": 126,
"preview": "{\n \"mcpServers\": {\n \"rubyevents\": {\n \"command\": \"bin/mcp_server\",\n \"args\": [],\n \"tools\": [\"*\"]\n }\n"
},
{
"path": ".node-version",
"chars": 8,
"preview": "22.15.1\n"
},
{
"path": ".prettierignore",
"chars": 16,
"preview": "transcripts.yml\n"
},
{
"path": ".ruby-version",
"chars": 6,
"preview": "4.0.1\n"
},
{
"path": ".standard.yml",
"chars": 0,
"preview": ""
},
{
"path": ".vscode/extensions.json",
"chars": 213,
"preview": "{\n \"recommendations\": [\n \"dbaeumer.vscode-eslint\",\n \"esbenp.prettier-vscode\",\n \"bradlc.vscode-tailwindcss\",\n "
},
{
"path": ".vscode/settings.json",
"chars": 1484,
"preview": "{\n \"editor.detectIndentation\": false,\n \"editor.formatOnPaste\": true,\n \"editor.formatOnSave\": true,\n \"editor.minimap."
},
{
"path": "CLAUDE.md",
"chars": 3733,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "CONTRIBUTING.md",
"chars": 9877,
"preview": "# Welcome to RubyEvents.org!\n\nWelcome to RubyEvents.org, and thank you for contributing your time and energy to improvin"
},
{
"path": "Dockerfile",
"chars": 2931,
"preview": "# syntax=docker/dockerfile:1\n# check=error=true\n\n# This Dockerfile is designed for production, not development. Use with"
},
{
"path": "Gemfile",
"chars": 7804,
"preview": "source \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\nruby file: \".ruby-version"
},
{
"path": "Guardfile",
"chars": 522,
"preview": "# Guardfile for watching YAML files in data/ and auto-importing them\n\nrequire_relative \"lib/guard/data_import\"\n\nguard :d"
},
{
"path": "Procfile.dev",
"chars": 112,
"preview": "web: bin/rails server -b 0.0.0.0\nvite: bin/vite dev\njobs: bin/jobs\n# guard: bundle exec guard --no-interactions\n"
},
{
"path": "README.md",
"chars": 2461,
"preview": "# RubyEvents.org (formerly RubyVideo.dev)\n\n[RubyEvents.org](https://www.rubyevents.org) (formerly RubyVideo.dev), inspir"
},
{
"path": "Rakefile",
"chars": 227,
"preview": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they wil"
},
{
"path": "app/assets/builds/.keep",
"chars": 0,
"preview": ""
},
{
"path": "app/assets/stylesheets/application.css",
"chars": 844,
"preview": "@import \"tailwindcss/base\";\n@import \"tailwindcss/components\";\n@import \"tailwindcss/utilities\";\n\n@import \"components/butt"
},
{
"path": "app/assets/stylesheets/bridge/components.css",
"chars": 145,
"preview": "html[data-bridge-platform] {\n user-select: none;\n}\n\n[data-bridge-components~=\"button\"] [data-controller~=\"bridge--butto"
},
{
"path": "app/assets/stylesheets/components/button.css",
"chars": 900,
"preview": "@layer components {\n .btn {\n @apply fill-current;\n }\n\n .btn:not(.btn-sm) {\n height: 2.5rem;\n min-height: 2.5"
},
{
"path": "app/assets/stylesheets/components/diff.css",
"chars": 121,
"preview": "@layer components {\n .diff {\n del {\n @apply text-error;\n }\n\n ins {\n @apply text-success;\n }\n }\n}"
},
{
"path": "app/assets/stylesheets/components/dropdown.css",
"chars": 479,
"preview": "@layer components {\n details > summary {\n list-style: none;\n }\n\n /* fix the dropdown button to links, by default d"
},
{
"path": "app/assets/stylesheets/components/event.css",
"chars": 89,
"preview": "@layer components {\n .event-card.cancelled,\n .card.cancelled {\n opacity: 0.5;\n }\n}\n"
},
{
"path": "app/assets/stylesheets/components/form.css",
"chars": 271,
"preview": "@layer components {\n label {\n @apply font-semibold;\n }\n\n /* because setting --rounded-btn to 1.9rem makes also all"
},
{
"path": "app/assets/stylesheets/components/hotwire-combobox.css",
"chars": 630,
"preview": "@layer components {\n .hw-combobox__main__wrapper {\n @apply min-h-12;\n }\n\n .hw-combobox__main__wrapper:has(input:fo"
},
{
"path": "app/assets/stylesheets/components/iframe.css",
"chars": 83,
"preview": "@layer components {\n .responsive-iframe-container iframe {\n width: 100%;\n }\n}\n"
},
{
"path": "app/assets/stylesheets/components/markdown.css",
"chars": 2217,
"preview": "@layer components {\n .markdown {\n p {\n @apply mb-2;\n }\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n "
},
{
"path": "app/assets/stylesheets/components/modal.css",
"chars": 199,
"preview": "@layer components {\n .modal-box {\n @apply bg-white;\n }\n\n .modal-top.spotlight {\n .modal-box {\n @apply abso"
},
{
"path": "app/assets/stylesheets/components/nav.css",
"chars": 433,
"preview": "@layer components {\n .desktop-menu {\n @apply hidden gap-6 md:flex;\n\n li,\n title {\n @apply m-0 inline-bloc"
},
{
"path": "app/assets/stylesheets/components/pagination.css",
"chars": 250,
"preview": "@layer components {\n nav.pagy.nav {\n @apply mx-auto flex justify-normal gap-4 bg-transparent text-base-content sm:ga"
},
{
"path": "app/assets/stylesheets/components/skeleton.css",
"chars": 68,
"preview": "@layer components {\n .skeleton {\n border-radius: inherit;\n }\n}\n"
},
{
"path": "app/assets/stylesheets/components/spotlight.css",
"chars": 143,
"preview": "@layer components {\n .modal-box.spotlight {\n @apply max-w-full;\n }\n\n [role=\"option\"][aria-selected=\"true\"] {\n @"
},
{
"path": "app/assets/stylesheets/components/tabs.css",
"chars": 211,
"preview": "@layer components {\n /* https://github.com/saadeghi/daisyui/issues/2988 */\n .tabs {\n display: flex;\n flex-wrap: "
},
{
"path": "app/assets/stylesheets/components/transition.css",
"chars": 950,
"preview": "@layer components {\n ::view-transition-group(root) {\n animation: none;\n mix-blend-mode: normal;\n }\n\n .navbar {\n"
},
{
"path": "app/assets/stylesheets/components/typography.css",
"chars": 760,
"preview": "@import url(\"https://fonts.googleapis.com/css2?family=Nunito:wght@200..1000&display=swap\");\n\n:root {\n font-family: Inte"
},
{
"path": "app/assets/stylesheets/components/video.css",
"chars": 779,
"preview": "@layer components {\n .banner-img .v-vlite {\n --vlite-controlsColor: #ffffff;\n --vlite-controlsOpacity: 1;\n --v"
},
{
"path": "app/avo/actions/approve_topic.rb",
"chars": 214,
"preview": "class Avo::Actions::ApproveTopic < Avo::BaseAction\n self.name = \"Publish Topic\"\n\n def handle(query:, fields:, current_"
},
{
"path": "app/avo/actions/assign_canonical_event.rb",
"chars": 722,
"preview": "class Avo::Actions::AssignCanonicalEvent < Avo::BaseAction\n self.name = \"Assign Canonical Event\"\n # self.visible = -> "
},
{
"path": "app/avo/actions/assign_canonical_speaker.rb",
"chars": 785,
"preview": "class Avo::Actions::AssignCanonicalSpeaker < Avo::BaseAction\n self.name = \"Assign Canonical Speaker\"\n # self.visible ="
},
{
"path": "app/avo/actions/assign_canonical_topic.rb",
"chars": 731,
"preview": "class Avo::Actions::AssignCanonicalTopic < Avo::BaseAction\n self.name = \"Assign Canonical Topic\"\n # self.visible = -> "
},
{
"path": "app/avo/actions/assign_canonical_user.rb",
"chars": 693,
"preview": "class Avo::Actions::AssignCanonicalUser < Avo::BaseAction\n self.name = \"Assign Canonical User\"\n\n def fields\n field "
},
{
"path": "app/avo/actions/clear_user.rb",
"chars": 352,
"preview": "class Avo::Actions::ClearUser < Avo::BaseAction\n self.name = \"Clear Suspicion\"\n self.message = \"Mark this user as manu"
},
{
"path": "app/avo/actions/enhance_transcript.rb",
"chars": 302,
"preview": "class Avo::Actions::EnhanceTranscript < Avo::BaseAction\n self.name = \"Enhance Transcript\"\n def handle(query:, fields:,"
},
{
"path": "app/avo/actions/extract_topics.rb",
"chars": 230,
"preview": "class Avo::Actions::ExtractTopics < Avo::BaseAction\n self.name = \"Extract Topics\"\n\n def handle(query:, fields:, curren"
},
{
"path": "app/avo/actions/fetch_duration.rb",
"chars": 296,
"preview": "class Avo::Actions::FetchDuration < Avo::BaseAction\n self.name = \"Fetch duration\"\n\n def handle(query:, fields:, curren"
},
{
"path": "app/avo/actions/find_topic_talks.rb",
"chars": 295,
"preview": "class Avo::Actions::FindTopicTalks < Avo::BaseAction\n self.name = \"Find Talks\"\n\n def handle(query:, fields:, current_u"
},
{
"path": "app/avo/actions/geocode_record.rb",
"chars": 865,
"preview": "class Avo::Actions::GeocodeRecord < Avo::BaseAction\n self.name = \"Geocode location\"\n\n def handle(query:, fields:, curr"
},
{
"path": "app/avo/actions/reject_topic.rb",
"chars": 311,
"preview": "class Avo::Actions::RejectTopic < Avo::BaseAction\n self.name = \"Reject Topic\"\n # self.visible = -> do\n # true\n # e"
},
{
"path": "app/avo/actions/summarize.rb",
"chars": 216,
"preview": "class Avo::Actions::Summarize < Avo::BaseAction\n self.name = \"Summarize\"\n\n def handle(query:, fields:, current_user:, "
},
{
"path": "app/avo/actions/talk_index.rb",
"chars": 429,
"preview": "class Avo::Actions::TalkIndex < Avo::BaseAction\n self.name = \"Talk Reindex\"\n self.standalone = true\n\n def handle(quer"
},
{
"path": "app/avo/actions/talk_ingest.rb",
"chars": 339,
"preview": "class Avo::Actions::TalkIngest < Avo::BaseAction\n self.name = \"Ingest talk (fetch transcript, enhance transcript, summa"
},
{
"path": "app/avo/actions/transcript.rb",
"chars": 305,
"preview": "class Avo::Actions::Transcript < Avo::BaseAction\n self.name = \"Fetch raw transcript\"\n\n def handle(query:, fields:, cur"
},
{
"path": "app/avo/actions/update_from_yml.rb",
"chars": 300,
"preview": "class Avo::Actions::UpdateFromYml < Avo::BaseAction\n self.name = \"Update talk from yml metadata\"\n\n def handle(query:, "
},
{
"path": "app/avo/actions/user_fetch_github.rb",
"chars": 353,
"preview": "class Avo::Actions::UserFetchGitHub < Avo::BaseAction\n self.name = \"Fetch GitHub profile\"\n\n def handle(query:, fields:"
},
{
"path": "app/avo/cards/duplicates_summary.rb",
"chars": 281,
"preview": "class Avo::Cards::DuplicatesSummary < Avo::Cards::PartialCard\n self.id = \"duplicates_summary\"\n self.label = \"Duplicate"
},
{
"path": "app/avo/cards/reversed_name_duplicates_list.rb",
"chars": 299,
"preview": "class Avo::Cards::ReversedNameDuplicatesList < Avo::Cards::PartialCard\n self.id = \"reversed_name_duplicates_list\"\n sel"
},
{
"path": "app/avo/cards/reversed_name_duplicates_metric.rb",
"chars": 345,
"preview": "class Avo::Cards::ReversedNameDuplicatesMetric < Avo::Cards::MetricCard\n self.id = \"reversed_name_duplicates_metric\"\n "
},
{
"path": "app/avo/cards/same_name_duplicates_list.rb",
"chars": 305,
"preview": "class Avo::Cards::SameNameDuplicatesList < Avo::Cards::PartialCard\n self.id = \"same_name_duplicates_list\"\n self.label "
},
{
"path": "app/avo/cards/same_name_duplicates_metric.rb",
"chars": 302,
"preview": "class Avo::Cards::SameNameDuplicatesMetric < Avo::Cards::MetricCard\n self.id = \"same_name_duplicates_metric\"\n self.lab"
},
{
"path": "app/avo/cards/suspicious_signals_breakdown.rb",
"chars": 318,
"preview": "class Avo::Cards::SuspiciousSignalsBreakdown < Avo::Cards::PartialCard\n self.id = \"suspicious_signals_breakdown\"\n self"
},
{
"path": "app/avo/cards/suspicious_summary.rb",
"chars": 274,
"preview": "class Avo::Cards::SuspiciousSummary < Avo::Cards::PartialCard\n self.id = \"suspicious_summary\"\n self.label = \"Suspiciou"
},
{
"path": "app/avo/cards/suspicious_users_list.rb",
"chars": 298,
"preview": "class Avo::Cards::SuspiciousUsersList < Avo::Cards::PartialCard\n self.id = \"suspicious_users_list\"\n self.label = \"Susp"
},
{
"path": "app/avo/cards/suspicious_users_metric.rb",
"chars": 270,
"preview": "class Avo::Cards::SuspiciousUsersMetric < Avo::Cards::MetricCard\n self.id = \"suspicious_users_metric\"\n self.label = \"S"
},
{
"path": "app/avo/cards/unavailable_videos_by_event.rb",
"chars": 289,
"preview": "class Avo::Cards::UnavailableVideosByEvent < Avo::Cards::PartialCard\n self.id = \"unavailable_videos_by_event\"\n self.la"
},
{
"path": "app/avo/cards/unavailable_videos_list.rb",
"chars": 299,
"preview": "class Avo::Cards::UnavailableVideosList < Avo::Cards::PartialCard\n self.id = \"unavailable_videos_list\"\n self.label = \""
},
{
"path": "app/avo/cards/unavailable_videos_metric.rb",
"chars": 285,
"preview": "class Avo::Cards::UnavailableVideosMetric < Avo::Cards::MetricCard\n self.id = \"unavailable_videos_metric\"\n self.label "
},
{
"path": "app/avo/cards/unavailable_videos_summary.rb",
"chars": 299,
"preview": "class Avo::Cards::UnavailableVideosSummary < Avo::Cards::PartialCard\n self.id = \"unavailable_videos_summary\"\n self.lab"
},
{
"path": "app/avo/dashboards/default.rb",
"chars": 338,
"preview": "class Avo::Dashboards::Default < Avo::Dashboards::BaseDashboard\n self.id = \"default\"\n self.name = \"Dashboard\"\n self.d"
},
{
"path": "app/avo/dashboards/duplicates.rb",
"chars": 422,
"preview": "class Avo::Dashboards::Duplicates < Avo::Dashboards::BaseDashboard\n self.id = \"duplicates\"\n self.name = \"Duplicate Det"
},
{
"path": "app/avo/dashboards/suspicious.rb",
"chars": 368,
"preview": "class Avo::Dashboards::Suspicious < Avo::Dashboards::BaseDashboard\n self.id = \"suspicious\"\n self.name = \"Suspicious Us"
},
{
"path": "app/avo/dashboards/unavailable_videos.rb",
"chars": 398,
"preview": "class Avo::Dashboards::UnavailableVideos < Avo::Dashboards::BaseDashboard\n self.id = \"unavailable_videos\"\n self.name ="
},
{
"path": "app/avo/filters/aliasable_type.rb",
"chars": 378,
"preview": "class Avo::Filters::AliasableType < Avo::Filters::SelectFilter\n self.name = \"Aliasable Type\"\n\n def apply(request, quer"
},
{
"path": "app/avo/filters/attended_as.rb",
"chars": 295,
"preview": "class Avo::Filters::AttendedAs < Avo::Filters::SelectFilter\n self.name = \"Attended as\"\n\n def apply(request, query, att"
},
{
"path": "app/avo/filters/bio.rb",
"chars": 210,
"preview": "class Avo::Filters::Bio < Avo::Filters::TextFilter\n self.name = \"Bio\"\n self.button_label = \"Filter by bio (contains)\"\n"
},
{
"path": "app/avo/filters/bio_presence.rb",
"chars": 317,
"preview": "class Avo::Filters::BioPresence < Avo::Filters::BooleanFilter\n self.name = \"Bio presence\"\n # self.visible = -> do\n # "
},
{
"path": "app/avo/filters/canonical.rb",
"chars": 495,
"preview": "class Avo::Filters::Canonical < Avo::Filters::BooleanFilter\n self.name = \"Canonical\"\n # self.visible = -> do\n # tru"
},
{
"path": "app/avo/filters/enhanced_transcript.rb",
"chars": 635,
"preview": "class Avo::Filters::EnhancedTranscript < Avo::Filters::BooleanFilter\n self.name = \"Enhanced transcript\"\n # self.visibl"
},
{
"path": "app/avo/filters/geocoded_presence.rb",
"chars": 435,
"preview": "class Avo::Filters::GeocodedPresence < Avo::Filters::BooleanFilter\n self.name = \"Geocoded\"\n\n def apply(request, query,"
},
{
"path": "app/avo/filters/github.rb",
"chars": 483,
"preview": "class Avo::Filters::GitHub < Avo::Filters::BooleanFilter\n self.name = \"GitHub handle\"\n\n def apply(request, query, valu"
},
{
"path": "app/avo/filters/github_handle.rb",
"chars": 217,
"preview": "class Avo::Filters::GitHubHandle < Avo::Filters::TextFilter\n self.name = \"GitHub handle (contains)\"\n\n def apply(reques"
},
{
"path": "app/avo/filters/github_handle_presence.rb",
"chars": 520,
"preview": "class Avo::Filters::GitHubHandlePresence < Avo::Filters::BooleanFilter\n self.name = \"GitHub handle presence\"\n\n def app"
},
{
"path": "app/avo/filters/has_duplicate.rb",
"chars": 607,
"preview": "class Avo::Filters::HasDuplicate < Avo::Filters::BooleanFilter\n self.name = \"Has duplicate\"\n\n def apply(request, query"
},
{
"path": "app/avo/filters/involvement_role.rb",
"chars": 298,
"preview": "class Avo::Filters::InvolvementRole < Avo::Filters::SelectFilter\n self.name = \"Role\"\n\n def apply(request, query, role)"
},
{
"path": "app/avo/filters/language.rb",
"chars": 306,
"preview": "class Avo::Filters::Language < Avo::Filters::SelectFilter\n self.name = \"Language\"\n\n # self.visible = -> do\n # true\n"
},
{
"path": "app/avo/filters/location_presence.rb",
"chars": 504,
"preview": "class Avo::Filters::LocationPresence < Avo::Filters::BooleanFilter\n self.name = \"Location presence\"\n\n def apply(reques"
},
{
"path": "app/avo/filters/name.rb",
"chars": 214,
"preview": "class Avo::Filters::Name < Avo::Filters::TextFilter\n self.name = \"Name\"\n self.button_label = \"Filter by name (contains"
},
{
"path": "app/avo/filters/provider.rb",
"chars": 274,
"preview": "class Avo::Filters::Provider < Avo::Filters::SelectFilter\n self.name = \"Provider\"\n\n def apply(request, query, provider"
},
{
"path": "app/avo/filters/published.rb",
"chars": 442,
"preview": "class Avo::Filters::Published < Avo::Filters::BooleanFilter\n self.name = \"Approved Status\"\n\n def apply(request, query,"
},
{
"path": "app/avo/filters/raw_transcript.rb",
"chars": 551,
"preview": "class Avo::Filters::RawTranscript < Avo::Filters::BooleanFilter\n self.name = \"Raw transcript\"\n # self.visible = -> do\n"
},
{
"path": "app/avo/filters/slug.rb",
"chars": 214,
"preview": "class Avo::Filters::Slug < Avo::Filters::TextFilter\n self.name = \"Slug\"\n self.button_label = \"Filter by slug (contains"
},
{
"path": "app/avo/filters/summary.rb",
"chars": 448,
"preview": "class Avo::Filters::Summary < Avo::Filters::BooleanFilter\n self.name = \"Summary\"\n\n def apply(request, query, values)\n "
},
{
"path": "app/avo/filters/suspicious.rb",
"chars": 272,
"preview": "class Avo::Filters::Suspicious < Avo::Filters::BooleanFilter\n self.name = \"Suspicious\"\n\n def apply(request, query, val"
},
{
"path": "app/avo/filters/talk_event.rb",
"chars": 283,
"preview": "class Avo::Filters::TalkEvent < Avo::Filters::SelectFilter\n self.name = \"Talk event\"\n\n def apply(request, query, value"
},
{
"path": "app/avo/filters/title.rb",
"chars": 218,
"preview": "class Avo::Filters::Title < Avo::Filters::TextFilter\n self.name = \"Title\"\n self.button_label = \"Filter by title (conta"
},
{
"path": "app/avo/filters/topic_talks.rb",
"chars": 723,
"preview": "class Avo::Filters::TopicTalks < Avo::Filters::BooleanFilter\n self.name = \"Topic talks\"\n # self.visible = -> do\n # "
},
{
"path": "app/avo/filters/topics.rb",
"chars": 480,
"preview": "class Avo::Filters::Topics < Avo::Filters::BooleanFilter\n self.name = \"Topics\"\n # self.visible = -> do\n # true\n # "
},
{
"path": "app/avo/filters/video_availability.rb",
"chars": 674,
"preview": "class Avo::Filters::VideoAvailability < Avo::Filters::BooleanFilter\n self.name = \"Video Availability\"\n\n def apply(requ"
},
{
"path": "app/avo/filters/video_provider.rb",
"chars": 323,
"preview": "class Avo::Filters::VideoProvider < Avo::Filters::SelectFilter\n self.name = \"Video Provider\"\n\n # self.visible = -> do\n"
},
{
"path": "app/avo/filters/without_talks.rb",
"chars": 328,
"preview": "class Avo::Filters::WithoutTalks < Avo::Filters::BooleanFilter\n self.name = \"Without talks\"\n # self.visible = -> do\n "
},
{
"path": "app/avo/resources/alias.rb",
"chars": 1639,
"preview": "class Avo::Resources::Alias < Avo::BaseResource\n self.includes = [:aliasable]\n self.search = {\n query: -> { query.w"
},
{
"path": "app/avo/resources/city.rb",
"chars": 1408,
"preview": "class Avo::Resources::City < Avo::BaseResource\n self.model_class = ::City\n self.title = :name\n self.includes = []\n s"
},
{
"path": "app/avo/resources/connected_account.rb",
"chars": 1013,
"preview": "# id :integer\n# uid :string\n# provider :string\n# username :string\n# user_id :integer\n# a"
},
{
"path": "app/avo/resources/contributor.rb",
"chars": 372,
"preview": "class Avo::Resources::Contributor < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search ="
},
{
"path": "app/avo/resources/event.rb",
"chars": 1969,
"preview": "class Avo::Resources::Event < Avo::BaseResource\n self.includes = []\n # self.search = {\n # query: -> { query.ransack"
},
{
"path": "app/avo/resources/event_involvement.rb",
"chars": 1063,
"preview": "class Avo::Resources::EventInvolvement < Avo::BaseResource\n self.includes = [:involvementable, :event]\n\n self.search ="
},
{
"path": "app/avo/resources/event_participation.rb",
"chars": 658,
"preview": "class Avo::Resources::EventParticipation < Avo::BaseResource\n self.includes = [:user, :event]\n # self.attachments = []"
},
{
"path": "app/avo/resources/event_series.rb",
"chars": 1172,
"preview": "class Avo::Resources::EventSeries < Avo::BaseResource\n self.single_includes = [:events]\n # self.search = {\n # query"
},
{
"path": "app/avo/resources/favorite_user.rb",
"chars": 329,
"preview": "class Avo::Resources::FavoriteUser < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search "
},
{
"path": "app/avo/resources/geocode_result.rb",
"chars": 549,
"preview": "class Avo::Resources::GeocodeResult < Avo::BaseResource\n self.model_class = ::GeocodeResult\n self.title = :query\n sel"
},
{
"path": "app/avo/resources/organization.rb",
"chars": 797,
"preview": "class Avo::Resources::Organization < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search "
},
{
"path": "app/avo/resources/session.rb",
"chars": 534,
"preview": "class Avo::Resources::Session < Avo::BaseResource\n self.includes = []\n # self.search = {\n # query: -> { query.ransa"
},
{
"path": "app/avo/resources/sponsor.rb",
"chars": 388,
"preview": "class Avo::Resources::Sponsor < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search = {\n "
},
{
"path": "app/avo/resources/suggestion.rb",
"chars": 735,
"preview": "class Avo::Resources::Suggestion < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search = "
},
{
"path": "app/avo/resources/talk.rb",
"chars": 4157,
"preview": "class Avo::Resources::Talk < Avo::BaseResource\n self.includes = [:event, :speakers, :topics]\n self.find_record_method "
},
{
"path": "app/avo/resources/talk_topic.rb",
"chars": 407,
"preview": "class Avo::Resources::TalkTopic < Avo::BaseResource\n self.includes = [:talk, :topic]\n # self.attachments = []\n # self"
},
{
"path": "app/avo/resources/talk_transcript.rb",
"chars": 728,
"preview": "class Avo::Resources::TalkTranscript < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n self.model_c"
},
{
"path": "app/avo/resources/topic.rb",
"chars": 1399,
"preview": "class Avo::Resources::Topic < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n self.search = {\n q"
},
{
"path": "app/avo/resources/topic_gem.rb",
"chars": 487,
"preview": "class Avo::Resources::TopicGem < Avo::BaseResource\n self.title = :gem_name\n self.search = {\n query: -> { query.wher"
},
{
"path": "app/avo/resources/user.rb",
"chars": 3179,
"preview": "class Avo::Resources::User < Avo::BaseResource\n self.title = :name\n self.includes = []\n self.find_record_method = -> "
},
{
"path": "app/avo/resources/user_talk.rb",
"chars": 316,
"preview": "class Avo::Resources::UserTalk < Avo::BaseResource\n # self.includes = []\n # self.attachments = []\n # self.search = {\n"
},
{
"path": "app/avo/resources/watch_list.rb",
"chars": 299,
"preview": "class Avo::Resources::WatchList < Avo::BaseResource\n self.title = :name\n self.includes = []\n\n def fields\n field :i"
},
{
"path": "app/avo/resources/watch_list_talk.rb",
"chars": 252,
"preview": "class Avo::Resources::WatchListTalk < Avo::BaseResource\n self.includes = [:talk, :watch_list]\n\n def fields\n field :"
},
{
"path": "app/channels/application_cable/channel.rb",
"chars": 79,
"preview": "module ApplicationCable\n class Channel < ActionCable::Channel::Base\n end\nend\n"
},
{
"path": "app/channels/application_cable/connection.rb",
"chars": 85,
"preview": "module ApplicationCable\n class Connection < ActionCable::Connection::Base\n end\nend\n"
},
{
"path": "app/clients/application_client.rb",
"chars": 4275,
"preview": "class ApplicationClient\n class Error < StandardError; end\n\n class Forbidden < Error; end\n\n class Unauthorized < Error"
},
{
"path": "app/clients/blue_sky.rb",
"chars": 615,
"preview": "# frozen_string_literal: true\n\nBlueSky = DelegateClass(Minisky) do\n alias_method :client, :__getobj__\n\n singleton_clas"
},
{
"path": "app/clients/github/client.rb",
"chars": 426,
"preview": "module GitHub\n class Client < ApplicationClient\n BASE_URI = \"https://api.github.com\"\n\n def initialize\n super"
},
{
"path": "app/clients/github/contributors_client.rb",
"chars": 1404,
"preview": "module GitHub\n class ContributorsClient < Client\n OWNER = \"rubyevents\"\n REPO = \"rubyevents\"\n\n def fetch_all\n "
},
{
"path": "app/clients/github/user_client.rb",
"chars": 391,
"preview": "module GitHub\n class UserClient < GitHub::Client\n def profile(username)\n get(\"/users/#{username}\")\n end\n\n "
},
{
"path": "app/clients/ruby_conferences/client.rb",
"chars": 502,
"preview": "module RubyConferences\n class Client < ApplicationClient\n BASE_URI = \"https://raw.githubusercontent.com/ruby-confere"
},
{
"path": "app/clients/speakerdeck/client.rb",
"chars": 457,
"preview": "# frozen_string_literal: true\n\nmodule Speakerdeck\n class Client < ApplicationClient\n BASE_URI = \"https://speakerdeck"
},
{
"path": "app/clients/youtube/channels.rb",
"chars": 834,
"preview": "require \"open-uri\"\n\nmodule YouTube\n class Channels < YouTube::Client\n def id_by_name(channel_name:)\n response ="
},
{
"path": "app/clients/youtube/client.rb",
"chars": 765,
"preview": "module YouTube\n class Client < ApplicationClient\n BASE_URI = \"https://youtube.googleapis.com/youtube/v3\"\n\n privat"
},
{
"path": "app/clients/youtube/playlist_items.rb",
"chars": 1107,
"preview": "module YouTube\n class PlaylistItems < YouTube::Client\n def all(playlist_id:)\n all_items(\"/playlistItems\", query"
},
{
"path": "app/clients/youtube/playlists.rb",
"chars": 1350,
"preview": "module YouTube\n class Playlists < YouTube::Client\n DEFAULT_METADATA_PARSER = \"YouTube::VideoMetadata\"\n\n def all(c"
},
{
"path": "app/clients/youtube/thumbnail.rb",
"chars": 2306,
"preview": "# frozen_string_literal: true\n\nmodule YouTube\n class Thumbnail\n SIZES = %w[maxresdefault sddefault hqdefault mqdefau"
},
{
"path": "app/clients/youtube/transcript.rb",
"chars": 1114,
"preview": "require \"protobuf/message_type\"\n\nmodule YouTube\n class Transcript\n attr_reader :response\n\n def get(video_id)\n "
},
{
"path": "app/clients/youtube/video.rb",
"chars": 1093,
"preview": "module YouTube\n class Video < Client\n def available?(video_id)\n path = \"/videos\"\n query = {\n part: "
},
{
"path": "app/components/application_component.rb",
"chars": 396,
"preview": "# frozen_string_literal: true\n\nclass ApplicationComponent < ViewComponent::Base\n extend Dry::Initializer\n\n attr_access"
},
{
"path": "app/components/events/upcoming_event_banner_component.html.erb",
"chars": 1061,
"preview": "<a href=\"<%= helpers.event_tickets_path(upcoming_event) %>\" data-turbo-frame=\"_top\" class=\"rounded-xl group border hover"
},
{
"path": "app/components/events/upcoming_event_banner_component.rb",
"chars": 981,
"preview": "# frozen_string_literal: true\n\nclass Events::UpcomingEventBannerComponent < ApplicationComponent\n option :event, option"
},
{
"path": "app/components/hover_card/base_component.html.erb",
"chars": 314,
"preview": "<div class=\"absolute left-1/2 -translate-x-1/2 bottom-full mb-2 w-80 opacity-0 invisible group-hover/card:opacity-100 gr"
},
{
"path": "app/components/hover_card/base_component.rb",
"chars": 456,
"preview": "# frozen_string_literal: true\n\nclass HoverCard::BaseComponent < ViewComponent::Base\n include Turbo::FramesHelper\n\n att"
},
{
"path": "app/components/hover_card/event_component.rb",
"chars": 174,
"preview": "# frozen_string_literal: true\n\nclass HoverCard::EventComponent < HoverCard::BaseComponent\n def hover_card_url\n Route"
},
{
"path": "app/components/hover_card/user_component.rb",
"chars": 252,
"preview": "# frozen_string_literal: true\n\nclass HoverCard::UserComponent < HoverCard::BaseComponent\n def hover_card_url\n Router"
},
{
"path": "app/components/tito/button_component.html.erb",
"chars": 72,
"preview": "<%= link_to label, helpers.event_tickets_path(event), class: classes %>\n"
},
{
"path": "app/components/tito/button_component.rb",
"chars": 276,
"preview": "# frozen_string_literal: true\n\nclass Tito::ButtonComponent < ApplicationComponent\n option :event\n option :label, defau"
},
{
"path": "app/components/tito/widget_component.html.erb",
"chars": 368,
"preview": "<% content_for :head do %>\n <script src=\"https://js.tito.io/v2\" async></script>\n<% end %>\n\n<% if wrapper %>\n <div clas"
},
{
"path": "app/components/tito/widget_component.rb",
"chars": 275,
"preview": "# frozen_string_literal: true\n\nclass Tito::WidgetComponent < ApplicationComponent\n option :event\n option :wrapper, def"
},
{
"path": "app/components/ui/avatar_component.html.erb",
"chars": 1019,
"preview": "<% avatar_markup = capture do %>\n <div class=\"avatar placeholder\">\n <div class=\"<%= avatar_classes %>\">\n <% if "
},
{
"path": "app/components/ui/avatar_component.rb",
"chars": 2306,
"preview": "# frozen_string_literal: true\n\nclass Ui::AvatarComponent < ApplicationComponent\n SIZE_MAPPING = {\n xs: {\n size_"
},
{
"path": "app/components/ui/avatar_group_component.html.erb",
"chars": 399,
"preview": "<div class=\"avatar-group <%= overlap %> rtl:space-x-reverse\">\n <% visible_avatarables.each do |avatarable| %>\n <%= r"
},
{
"path": "app/components/ui/avatar_group_component.rb",
"chars": 797,
"preview": "# frozen_string_literal: true\n\nclass Ui::AvatarGroupComponent < ApplicationComponent\n SIZE_MAPPING = {\n sm: \"w-8\",\n "
},
{
"path": "app/components/ui/badge_component.rb",
"chars": 1184,
"preview": "# frozen_string_literal: true\n\nclass Ui::BadgeComponent < ApplicationComponent\n KIND_MAPPING = {\n neutral: \"badge-ne"
},
{
"path": "app/components/ui/button_component.rb",
"chars": 2270,
"preview": "# frozen_string_literal: true\n\nclass Ui::ButtonComponent < ApplicationComponent\n KIND_MAPPING = {\n primary: \"btn-pri"
},
{
"path": "app/components/ui/divider_component.rb",
"chars": 651,
"preview": "# frozen_string_literal: true\n\nclass Ui::DividerComponent < ApplicationComponent\n KIND_MAPPING = {\n horizontal: \"div"
},
{
"path": "app/components/ui/dropdown_component.html.erb",
"chars": 533,
"preview": "<%= content_tag :details, class: classes, **attributes do %>\n <summary class=\"flex after:hidden\">\n <% if toggle_open"
},
{
"path": "app/components/ui/dropdown_component.rb",
"chars": 1793,
"preview": "# frozen_string_literal: true\n\nclass Ui::DropdownComponent < ApplicationComponent\n renders_many :menu_items, types: {\n "
},
{
"path": "app/components/ui/modal_component.html.erb",
"chars": 423,
"preview": "<%= content_tag :dialog, role: :dialog, aria: {modal: true}, class: classes, **attributes do %>\n <div class=\"modal-box "
},
{
"path": "app/components/ui/modal_component.rb",
"chars": 1567,
"preview": "# frozen_string_literal: true\n\nclass Ui::ModalComponent < ApplicationComponent\n POSITION_MAPPING = {\n top: \"modal-to"
},
{
"path": "app/components/ui/stamp_component.html.erb",
"chars": 480,
"preview": "<% if interactive && clickable? %>\n <%= link_to(url, **link_attributes.deep_merge(data: {turbo_frame: \"_top\"})) do %>\n "
},
{
"path": "app/components/ui/stamp_component.rb",
"chars": 1678,
"preview": "# frozen_string_literal: true\n\nclass Ui::StampComponent < ApplicationComponent\n SIZE_MAPPING = {\n unset: \"\",\n ful"
},
{
"path": "app/controllers/admin/suggestions_controller.rb",
"chars": 512,
"preview": "module Admin\n class SuggestionsController < ApplicationController\n include Pagy::Backend\n\n def index\n @pagy,"
},
{
"path": "app/controllers/analytics/dashboards_controller.rb",
"chars": 3757,
"preview": "class Analytics::DashboardsController < ApplicationController\n skip_before_action :authenticate_user!\n before_action :"
},
{
"path": "app/controllers/announcements_controller.rb",
"chars": 2237,
"preview": "class AnnouncementsController < ApplicationController\n include Pagy::Backend\n\n skip_before_action :authenticate_user!\n"
},
{
"path": "app/controllers/api/v1/embed/base_controller.rb",
"chars": 2029,
"preview": "# frozen_string_literal: true\n\nmodule Api\n module V1\n module Embed\n class BaseController < ActionController::AP"
},
{
"path": "app/controllers/api/v1/embed/events_controller.rb",
"chars": 4378,
"preview": "# frozen_string_literal: true\n\nmodule Api\n module V1\n module Embed\n class EventsController < BaseController\n "
}
]
// ... and 3509 more files (download for full content)
About this extraction
This page contains the full source code of the adrienpoly/rubyvideo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 3709 files (15.1 MB), approximately 4.1M tokens, and a symbol index with 3552 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.