diff --git a/app/services/profile_fields/add_base_fields.rb b/app/services/profile_fields/add_base_fields.rb new file mode 100644 index 000000000..cffce55c9 --- /dev/null +++ b/app/services/profile_fields/add_base_fields.rb @@ -0,0 +1,13 @@ +module ProfileFields + class AddBaseFields + include FieldDefinition + + group "Basic" do + field "Display email on profile", :check_box + field "Name", :text_field, placeholder: "John Doe" + field "Website URL", :text_field, placeholder: "https://yoursite.com" + field "Summary", :text_area, placeholder: "A short bio..." + field "Location", :text_field, placeholder: "Halifax, Nova Scotia" + end + end +end diff --git a/app/services/profile_fields/add_branding_fields.rb b/app/services/profile_fields/add_branding_fields.rb new file mode 100644 index 000000000..937b93bdb --- /dev/null +++ b/app/services/profile_fields/add_branding_fields.rb @@ -0,0 +1,13 @@ +module ProfileFields + class AddBrandingFields + include FieldDefinition + + group "Branding" do + field "Brand color 1", :color_field, placeholder: "#000000", description: "Used for backgrounds, borders etc." + field "Brand color 2", + :color_field, + placeholder: "#000000", + description: "Used for texts (usually put on Brand color 1)." + end + end +end diff --git a/app/services/profile_fields/add_link_fields.rb b/app/services/profile_fields/add_link_fields.rb new file mode 100644 index 000000000..ea57aa5a4 --- /dev/null +++ b/app/services/profile_fields/add_link_fields.rb @@ -0,0 +1,19 @@ +module ProfileFields + class AddLinkFields + include FieldDefinition + + group "Links" do + field "Facebook profile URL", :text_field, placeholder: "https://facebook.com/..." + field "Youtube URL", :text_field, placeholder: "https://www.youtube.com/channel/..." + field "StackOverflow profile URL", :text_field, placeholder: "https://stackoverflow.com/users/..." + field "LinkedIn profile URL", :text_field, placeholder: "https://www.linkedin.com/in/..." + field "Behance profile URL", :text_field, placeholder: "https://..." + field "Dribble profile URL", :text_field, placeholder: "https://dribble.com/..." + field "Medium profile URL", :text_field, placeholder: "https://..." + field "GitLab profile URL", :text_field, placeholder: "https://..." + field "Instagram profile URL", :text_field, placeholder: "https://..." + field "Mastodon profile URL", :text_field, placeholder: "https://..." + field "Twitch profile URL", :text_field, placeholder: "https://..." + end + end +end diff --git a/app/services/profile_fields/add_work_fields.rb b/app/services/profile_fields/add_work_fields.rb new file mode 100644 index 000000000..5e9604286 --- /dev/null +++ b/app/services/profile_fields/add_work_fields.rb @@ -0,0 +1,15 @@ +module ProfileFields + class AddWorkFields + include FieldDefinition + + group "Work" do + field "Education", :text_field + field "Employer name", :text_field, placeholder: "Acme Inc." + field "Employer URL", :text_field, placeholder: "https://dev.com" + field "Employment title", :text_field, placeholder: "Junior Frontend Engineer" + field "Looking for work", :check_box + field 'Display "looking for work" on profile', :check_box + field "Recruiters can contact me about job opportunities", :check_box + end + end +end diff --git a/app/services/profile_fields/field_definition.rb b/app/services/profile_fields/field_definition.rb new file mode 100644 index 000000000..be3242f14 --- /dev/null +++ b/app/services/profile_fields/field_definition.rb @@ -0,0 +1,38 @@ +module ProfileFields + # This module defines a tiny DSL for declarative profile field seeders. + module FieldDefinition + extend ActiveSupport::Concern + + class_methods do + def call + new.add_fields + end + + def group(group = nil) + @group = group + yield if block_given? + @group = nil + end + + def fields + @fields ||= [] + end + + private + + def field(label, input_type, placeholder: nil, description: nil, group: @group) + fields << { + label: label, + input_type: input_type, + placeholder_text: placeholder, + description: description, + group: group + }.compact + end + end + + def add_fields + self.class.fields.each { |field| ProfileField.create(field) } + end + end +end diff --git a/app/services/profile_fields/import_from_csv.rb b/app/services/profile_fields/import_from_csv.rb new file mode 100644 index 000000000..e9a34f4e0 --- /dev/null +++ b/app/services/profile_fields/import_from_csv.rb @@ -0,0 +1,11 @@ +module ProfileFields + class ImportFromCsv + HEADERS = %i[label input_type placeholder_text description group].freeze + + def self.call(file) + CSV.foreach(file, headers: HEADERS, skip_blanks: true) do |row| + ProfileField.create(row.to_h) + end + end + end +end diff --git a/bin/setup b/bin/setup index bc1e0aec8..384ca0121 100755 --- a/bin/setup +++ b/bin/setup @@ -33,7 +33,8 @@ chdir APP_ROOT do puts "\n== Preparing Test Elasticsearch ==" system! 'RAILS_ENV="test" bin/rails search:setup' - system! "bin/rails app_initializer:setup" + puts "\n== Initializing the application ==" + system! "bin/rails app_initializer:setup forem:setup" puts "\n== Removing old logs and tempfiles ==" system! "bin/rails log:clear tmp:clear" diff --git a/db/migrate/20200804035648_add_description_to_profile_fields.rb b/db/migrate/20200804035648_add_description_to_profile_fields.rb new file mode 100644 index 000000000..98ec20654 --- /dev/null +++ b/db/migrate/20200804035648_add_description_to_profile_fields.rb @@ -0,0 +1,5 @@ +class AddDescriptionToProfileFields < ActiveRecord::Migration[6.0] + def change + add_column :profile_fields, :description, :string + end +end diff --git a/db/migrate/20200805050048_add_group_to_profile_fields.rb b/db/migrate/20200805050048_add_group_to_profile_fields.rb new file mode 100644 index 000000000..780eab880 --- /dev/null +++ b/db/migrate/20200805050048_add_group_to_profile_fields.rb @@ -0,0 +1,5 @@ +class AddGroupToProfileFields < ActiveRecord::Migration[6.0] + def change + add_column :profile_fields, :group, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 0e5b6a379..2b0b35239 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.define(version: 2020_07_31_041554) do +ActiveRecord::Schema.define(version: 2020_08_05_050048) do # These are extensions that must be enabled in order to support this database enable_extension "citext" @@ -924,6 +924,8 @@ ActiveRecord::Schema.define(version: 2020_07_31_041554) do create_table "profile_fields", force: :cascade do |t| t.boolean "active", default: true, null: false t.datetime "created_at", precision: 6, null: false + t.string "description" + t.string "group" t.integer "input_type", default: 0, null: false t.citext "label", null: false t.string "placeholder_text" diff --git a/db/seeds.rb b/db/seeds.rb index f43f37173..b220a4946 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -492,6 +492,18 @@ seeder.create_if_none(Page) do end ############################################################################## + +seeder.create_if_none(ProfileField) do + ProfileFields::AddBaseFields.call + ProfileFields::AddLinkFields.call + ProfileFields::AddWorkFields.call + coding_fields_csv = Rails.root.join("lib/data/coding_profile_fields.csv") + ProfileFields::ImportFromCsv.call(coding_fields_csv) + ProfileFields::AddBrandingFields.call +end + +############################################################################## + puts <<-ASCII ``````````````````````````````````````````````````````````````````````````` diff --git a/lib/data/coding_profile_fields.csv b/lib/data/coding_profile_fields.csv new file mode 100644 index 000000000..612d8f37e --- /dev/null +++ b/lib/data/coding_profile_fields.csv @@ -0,0 +1,4 @@ +Skills/Languages,text_area,,What tools and languages are you most experienced with? Are you specialized or more of a generalist?,Coding +I'm getting into,text_area,,What are you learning right now? What are the new tools and languages you're picking up right now?,Coding +My projects and hacks,text_area,,What projects are currently occupying most of your time?,Coding +Available for,text_area,,What kinds of collaborations or discussions are you available for? What's a good reason to say Hey! to you these days?,Coding diff --git a/lib/tasks/forem.rake b/lib/tasks/forem.rake new file mode 100644 index 000000000..eefff64b9 --- /dev/null +++ b/lib/tasks/forem.rake @@ -0,0 +1,9 @@ +namespace :forem do + desc "Performs basic setup for new Forem instances" + task setup: :environment do + puts "\n== Setting up profile fields ==" + # TODO: [@forem/oss] Remove the destroy_all call + ProfileField.destroy_all + ProfileFields::AddBaseFields.call + end +end diff --git a/spec/fixtures/files/profile_fields.csv b/spec/fixtures/files/profile_fields.csv new file mode 100644 index 000000000..a309f3197 --- /dev/null +++ b/spec/fixtures/files/profile_fields.csv @@ -0,0 +1,4 @@ +Name,text_field,John Doe,,Basic +Skills/Languages,text_area,,Programming languages,Coding + +Color,color_field,#000000,"Used for backgrounds, borders etc.",Branding diff --git a/spec/services/profile_fields/field_definition_spec.rb b/spec/services/profile_fields/field_definition_spec.rb new file mode 100644 index 000000000..8f1c406b8 --- /dev/null +++ b/spec/services/profile_fields/field_definition_spec.rb @@ -0,0 +1,24 @@ +require "rails_helper" + +RSpec.describe ProfileFields::FieldDefinition, type: :service do + let(:test_class) do + Class.new do + include ProfileFields::FieldDefinition + group "Test" do + field "Test 1", :text_area, placeholder: "Test", description: "For testing" + field "Test 2", :check_box + end + end + end + + context "when adding fields" do + it "creates all the profile fields in the DB", :aggregate_failures do + expect do + test_class.call + end.to change(ProfileField, :count).by(2) + + expect(ProfileField.pluck(:label)).to match_array(["Test 1", "Test 2"]) + expect(ProfileField.pluck(:group)).to match_array(%w[Test Test]) + end + end +end diff --git a/spec/services/profile_fields/import_from_csv_spec.rb b/spec/services/profile_fields/import_from_csv_spec.rb new file mode 100644 index 000000000..178c82f16 --- /dev/null +++ b/spec/services/profile_fields/import_from_csv_spec.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +RSpec.describe ProfileFields::ImportFromCsv do + # Importing is slow, so we only do it once and then clean up after outselves. + # rubocop:disable RSpec/BeforeAfterAll + before(:all) { described_class.call(file_fixture("profile_fields.csv")) } + + after(:all) { ProfileField.destroy_all } + # rubocop:enable RSpec/BeforeAfterAll + + it "ignores empty lines" do + expect(ProfileField.count).to eq 3 + end + + it "handles missing descriptions", :aggregate_failures do + field = ProfileField.find_by!(label: "Name") + expect(field.input_type).to eq "text_field" + expect(field.placeholder_text).to eq "John Doe" + expect(field.description).to be_nil + expect(field.group).to eq "Basic" + end + + it "handles missing placeholder_texts", :aggregate_failures do + field = ProfileField.find_by!(label: "Skills/Languages") + expect(field.input_type).to eq "text_area" + expect(field.placeholder_text).to be_nil + expect(field.description).to eq "Programming languages" + expect(field.group).to eq "Coding" + end + + it "handles commas in correctly quoted fields", :aggregate_failures do + field = ProfileField.find_by!(label: "Color") + expect(field.input_type).to eq "color_field" + expect(field.placeholder_text).to eq "#000000" + expect(field.description).to eq "Used for backgrounds, borders etc." + expect(field.group).to eq "Branding" + end +end