diff --git a/app/avo/resources/event.rb b/app/avo/resources/event.rb index 2a757159e..97a1ad8db 100644 --- a/app/avo/resources/event.rb +++ b/app/avo/resources/event.rb @@ -7,7 +7,7 @@ class Avo::Resources::Event < Avo::BaseResource if id.is_a?(Array) query.where(slug: id) else - query.find_by(slug: id) + query.find_by(slug: id) || query.find_by(id:) end } self.external_link = -> { @@ -27,6 +27,7 @@ def fields field :talks, as: :has_many field :speakers, as: :has_many, through: :talks field :topics, as: :has_many + field :social_profiles, as: :has_many, use_resource: "Avo::Resources::SocialProfile" end def actions diff --git a/app/avo/resources/social_profile.rb b/app/avo/resources/social_profile.rb new file mode 100644 index 000000000..0c8afa5fa --- /dev/null +++ b/app/avo/resources/social_profile.rb @@ -0,0 +1,8 @@ +class Avo::Resources::SocialProfile < Avo::BaseResource + def fields + field :id, as: :id + field :provider, enum: ::SocialProfile.providers, as: :select, required: true + field :value, as: :text + field :sociable, as: :belongs_to, polymorphic_as: :sociable, types: [::Speaker, ::Event], foreign_key: :slug + end +end diff --git a/app/avo/resources/speaker.rb b/app/avo/resources/speaker.rb index eb02779bc..e69a70a9b 100644 --- a/app/avo/resources/speaker.rb +++ b/app/avo/resources/speaker.rb @@ -4,7 +4,7 @@ class Avo::Resources::Speaker < Avo::BaseResource if id.is_a?(Array) query.where(slug: id) else - query.find_by(slug: id) + query.find_by(slug: id) || query.find_by(id:) end } self.search = { @@ -17,19 +17,13 @@ class Avo::Resources::Speaker < Avo::BaseResource def fields field :id, as: :id, link_to_record: true field :name, as: :text, link_to_record: true, sortable: true - field :twitter, as: :text - field :github, as: :text - field :speakerdeck, as: :text - field :mastodon, as: :text, hide_on: :index - field :linkedin, as: :text, hide_on: :index - field :bsky, as: :text, hide_on: :index field :bio, as: :textarea, hide_on: :index - field :website, as: :text, hide_on: :index field :slug, as: :text, hide_on: :index field :talks_count, as: :number, sortable: true field :canonical, as: :belongs_to, hide_on: :index # field :suggestions, as: :has_many # field :speaker_talks, as: :has_many + field :social_profiles, as: :has_many, use_resource: "Avo::Resources::SocialProfile" field :talks, as: :has_many, use_resource: "Avo::Resources::Talk", attach_scope: -> { query.order(title: :asc) }, searchable: true end diff --git a/app/controllers/avo/social_profiles_controller.rb b/app/controllers/avo/social_profiles_controller.rb new file mode 100644 index 000000000..dccf10aa3 --- /dev/null +++ b/app/controllers/avo/social_profiles_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/3.0/controllers.html +class Avo::SocialProfilesController < Avo::ResourcesController +end diff --git a/app/models/concerns/sociable.rb b/app/models/concerns/sociable.rb new file mode 100644 index 000000000..6851b42ad --- /dev/null +++ b/app/models/concerns/sociable.rb @@ -0,0 +1,7 @@ +module Sociable + extend ActiveSupport::Concern + + included do + has_many :social_profiles, as: :sociable, dependent: :destroy + end +end diff --git a/app/models/event.rb b/app/models/event.rb index d1a2bb34c..812f72bd8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -32,6 +32,7 @@ class Event < ApplicationRecord include Suggestable include Sluggable + include Sociable slug_from :name # associations diff --git a/app/models/social_profile.rb b/app/models/social_profile.rb new file mode 100644 index 000000000..174cf8be4 --- /dev/null +++ b/app/models/social_profile.rb @@ -0,0 +1,47 @@ +# == Schema Information +# +# Table name: social_profiles +# +# id :integer not null, primary key +# provider :integer +# sociable_type :string indexed => [sociable_id] +# value :string +# created_at :datetime not null +# updated_at :datetime not null +# sociable_id :integer indexed => [sociable_type] +# +# Indexes +# +# index_social_profiles_on_sociable (sociable_type,sociable_id) +# +class SocialProfile < ApplicationRecord + belongs_to :sociable, polymorphic: true + + enum :provider, { + github: 0, + twitter: 1, + linkedin: 2, + bsky: 3, + mastadon: 4 + }, + suffix: true, + validate: {presence: true} + + before_save do + self.value = self.class.normalize_value_for(provider.to_sym, value) + end + + # normalizes + normalizes :github, with: ->(value) { value.gsub(/^(?:https?:\/\/)?(?:www\.)?github\.com\//, "").gsub(/^@/, "") } + normalizes :twitter, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:x\.com|twitter\.com)/}, "").gsub(/@/, "") } + normalizes :linkedin, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:linkedin\.com/in)/}, "") } + normalizes :bsky, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:[^\/]+\.com)/}, "").gsub(/@/, "") } + normalizes :mastodon, with: ->(value) { + return value if value&.match?(URI::DEFAULT_PARSER.make_regexp) + return "" unless value.count("@") == 2 + + _, handle, instance = value.split("@") + + "https://#{instance}/@#{handle}" + } +end diff --git a/app/models/speaker.rb b/app/models/speaker.rb index 4cac259b8..038e27cb6 100644 --- a/app/models/speaker.rb +++ b/app/models/speaker.rb @@ -38,6 +38,7 @@ class Speaker < ApplicationRecord include ActionView::RecordIdentifier include Sluggable include Suggestable + include Sociable include Speaker::Searchable slug_from :name @@ -72,24 +73,6 @@ class Speaker < ApplicationRecord scope :canonical, -> { where(canonical_id: nil) } scope :not_canonical, -> { where.not(canonical_id: nil) } - # normalizes - normalizes :github, with: ->(value) { value.gsub(/^(?:https?:\/\/)?(?:www\.)?github\.com\//, "").gsub(/^@/, "") } - normalizes :twitter, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:x\.com|twitter\.com)/}, "").gsub(/@/, "") } - normalizes :bsky, with: ->(value) { - value.gsub(%r{https?://(?:www\.)?(?:x\.com|bsky\.app/profile)/}, "").gsub(/@/, "") - } - normalizes :linkedin, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:linkedin\.com/in)/}, "") } - normalizes :bsky, with: ->(value) { value.gsub(%r{https?://(?:www\.)?(?:[^\/]+\.com)/}, "").gsub(/@/, "") } - - normalizes :mastodon, with: ->(value) { - return value if value&.match?(URI::DEFAULT_PARSER.make_regexp) - return "" unless value.count("@") == 2 - - _, handle, instance = value.split("@") - - "https://#{instance}/@#{handle}" - } - def title name end diff --git a/db/migrate/20250123120534_create_social_profiles.rb b/db/migrate/20250123120534_create_social_profiles.rb new file mode 100644 index 000000000..38f7a4537 --- /dev/null +++ b/db/migrate/20250123120534_create_social_profiles.rb @@ -0,0 +1,10 @@ +class CreateSocialProfiles < ActiveRecord::Migration[8.0] + def change + create_table :social_profiles do |t| + t.string :value + t.integer :provider + t.belongs_to :sociable, polymorphic: true + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f5e024970..e4f84b4cd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_01_15_215944) do +ActiveRecord::Schema[8.0].define(version: 2025_01_23_120534) do create_table "ahoy_events", force: :cascade do |t| t.integer "visit_id" t.integer "user_id" @@ -131,6 +131,16 @@ t.index ["user_id"], name: "index_sessions_on_user_id" end + create_table "social_profiles", force: :cascade do |t| + t.string "value" + t.integer "provider" + t.string "sociable_type" + t.integer "sociable_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["sociable_type", "sociable_id"], name: "index_social_profiles_on_sociable" + end + create_table "speaker_talks", force: :cascade do |t| t.integer "speaker_id", null: false t.integer "talk_id", null: false diff --git a/test/fixtures/social_profiles.yml b/test/fixtures/social_profiles.yml new file mode 100644 index 000000000..14a5c8ce7 --- /dev/null +++ b/test/fixtures/social_profiles.yml @@ -0,0 +1,27 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +# == Schema Information +# +# Table name: social_profiles +# +# id :integer not null, primary key +# provider :integer +# sociable_type :string indexed => [sociable_id] +# value :string +# created_at :datetime not null +# updated_at :datetime not null +# sociable_id :integer indexed => [sociable_type] +# +# Indexes +# +# index_social_profiles_on_sociable (sociable_type,sociable_id) +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/social_profile_test.rb b/test/models/social_profile_test.rb new file mode 100644 index 000000000..2d7da1b8f --- /dev/null +++ b/test/models/social_profile_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class SocialProfileTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end