diff --git a/.ruby-version b/.ruby-version
index 7bde84d..9e79f6c 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-ruby-3.1.2
+ruby-3.2.2
diff --git a/Gemfile b/Gemfile
index 0ad0f1e..67ffec6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,10 @@
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
-ruby "3.2.1"
+ruby "3.2.2"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
-gem "rails", "~> 7.0.4"
+gem "rails", "~> 7.0.5"
gem 'acts-as-taggable-on', '~> 9.0'
gem 'acts_as_favoritor'
@@ -14,6 +14,7 @@ gem "sprockets-rails"
# Use postgresql as the database for Active Record
gem "pg", "~> 1.1"
+gem "brick"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", "~> 5.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index ecff717..0b01200 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,8 +1,8 @@
GIT
remote: https://github.com/ahmad-elassuty/rails_graph.git
- revision: 790f9143c99fd1ad1f32ede82654f9cc318521ad
+ revision: 6fb267210d5f21379dc1f1d41fb365b530470eec
specs:
- rails_graph (0.2.0)
+ rails_graph (0.3.0)
activerecord (>= 6.0, < 7.1)
neo4j-ruby-driver (>= 4.4.0.alpha.8)
railties (>= 6.0, < 7.1)
@@ -90,6 +90,9 @@ GEM
bindex (0.8.1)
bootsnap (1.16.0)
msgpack (~> 1.2)
+ brick (1.0.149)
+ activerecord (>= 3.1.1)
+ fancy_gets
builder (3.2.4)
capybara (3.39.1)
addressable
@@ -110,6 +113,7 @@ GEM
irb (>= 1.5.0)
reline (>= 0.3.1)
erubi (1.12.0)
+ fancy_gets (0.1.10)
fiber-local (1.0.0)
globalid (1.1.0)
activesupport (>= 5.0)
@@ -243,13 +247,14 @@ DEPENDENCIES
acts-as-taggable-on (~> 9.0)
acts_as_favoritor
bootsnap
+ brick
capybara
debug
importmap-rails
jbuilder
pg (~> 1.1)
puma (~> 5.0)
- rails (~> 7.0.4)
+ rails (~> 7.0.5)
rails_graph!
selenium-webdriver
sprockets-rails
@@ -260,7 +265,7 @@ DEPENDENCIES
webdrivers
RUBY VERSION
- ruby 3.2.1p31
+ ruby 3.2.2p53
BUNDLED WITH
2.4.10
diff --git a/app/models/project.rb b/app/models/project.rb
index 17a360f..9b5bd07 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,3 +1,3 @@
class Project < ApplicationRecord
- belongs_to :owner, class_name: 'User'
+ belongs_to :owner, class_name: 'User', inverse_of: :projects
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 13e0816..6dbb2e6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,4 +1,4 @@
class User < ApplicationRecord
has_one_attached :avatar
- has_many :projects, foreign_key: :owner_id
+ has_many :projects, foreign_key: :owner_id, inverse_of: :owner
end
diff --git a/config/initializers/brick.rb b/config/initializers/brick.rb
new file mode 100644
index 0000000..fee8720
--- /dev/null
+++ b/config/initializers/brick.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+# Settings for the Brick gem
+# (By default this auto-creates models, controllers, views, and routes on-the-fly.)
+
+if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
+ # Mode -- generally :on or :off, or only in :development. Also available is :diag_env which enables only
+ # when the environment variable BRICK is set.
+ Brick.mode = :development
+ # Can be further overridden by placing this line in development.rb / test.rb / production.rb:
+ # # Brick.mode = :on # (or :off to entirely disable)
+
+ # # Custom path prefix to apply to all auto-generated Brick routes. Also causes auto-generated controllers
+ # # to be created inside a module with the same name.
+ # ::Brick.path_prefix = 'admin'
+
+ # # Normally all are enabled in development mode, and for security reasons only models are enabled in production
+ # # and test. This allows you to either (a) turn off models entirely, or (b) enable controllers, views, and routes
+ # # in production.
+ # Brick.enable_routes = true # Setting this to "false" will disable routes in development
+ # Brick.enable_models = false
+ # Brick.enable_controllers = true # Setting this to "false" will disable controllers in development
+ # Brick.enable_views = true # Setting this to "false" will disable views in development
+
+ # # If The Brick sees that RSwag gem is present, it allows for API resources to be automatically served out.
+ # # You can configure one or more root path(s) for these resources, and when there are multiple then an attempt
+ # # is made to return data from that version of the view or table name, or the most recent prior to that version:
+ # ::Brick.api_roots = ['/api/v1/']
+ # # You may also want to add an OpenAPI 3.0 documentation endpoint using Rswag::Ui:
+ # Rswag::Ui.configure do |config|
+ # config.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
+ # end
+
+ # # By default models are auto-created for database views, and set to be read-only. This can be skipped.
+ # Brick.skip_database_views = true
+
+ # # Any tables or views you'd like to skip when auto-creating models
+ # Brick.exclude_tables = ['custom_metadata', 'version_info']
+
+ # # Class that auto-generated models should inherit from
+ # Brick.models_inherit_from = ApplicationRecord
+
+ # # Class that auto-generated controllers should inherit from
+ # # (Would generally want to un-comment this line if you want to enforce any authentication which is
+ # # established in your ApplicationController, or for auto-created controllers to support
+ # # Turbo Stream broadcasts.)
+ # ::Brick.controllers_inherit_from = 'ApplicationController'
+
+ # # When table names have specific prefixes automatically place them in their own module with a table_name_prefix.
+ # Brick.table_name_prefixes = { 'nav_' => 'Navigation' }
+
+ # # COLUMN SEQUENCING AND INCLUSION / EXCLUSION
+
+ # # By default if there is a primary key present then rows in an index view are ordered by this primary key. To
+ # # use a different rule for doing ORDER BY, you can override this default ordering done by The Brick, for instance
+ # # to have the rows in a contact list sorted by email:
+ # Brick.order = { 'contacts' => { _brick_default: :email } }
+ # # or by last name then first name:
+ # Brick.order = { 'contacts' => { _brick_default: [:lastname, :firstname] } }
+ # # Totally legitimate to have the default order be the name of a belongs_to or has_many association instead of an
+ # # actual column name, in which case for has_many it just orders by the count of how many records are associated,
+ # # and for belongs_to it's based on the primary table's DSL if any is defined (since that is what is used to
+ # # calculate what is shown when a foreign table lists out related records). If contacts relates to addresses,
+ # # then this is perfectly fine:
+ # Brick.order = { 'contacts' => { _brick_default: :address } }
+ # # You can even have a specific custom clause used in the ORDER BY. In this case it is recommended to include a
+ # # special placeholder for the table name with the sequence "^^^". Here is an example of having the default
+ # # ordering happening on the "code" column, and also defining custom sorting to be done, in this case proper
+ # # ordering if that code is stored as a dotted numeric value:
+ # Brick.order = { 'document_trees' => { _brick_default: :code,
+ # code: "ORDER BY STRING_TO_ARRAY(^^^.code, '.')::int[]" } }
+
+ # # Sequence of columns for each model. This also allows you to add read-only calculated columns in the same
+ # # kind of way that they can be added in the include: portion of include/exclude columns, below.
+ # # Designated by {
=> [, ] }
+ # Brick.column_sequence = { 'users' => ['email', 'profile.firstname', 'profile.lastname'] }
+
+ # # Specific columns to include or exclude for each model. If there are only inclusions then only those
+ # # columns show. If there are any exclusions then all non-excluded columns are attempted to be shown,
+ # # which negates the usefulness of inclusions except to add calculated column detail built from DSL.
+ # # Designated by .
+ # Brick.column_sequence = { 'users' => { include: ['email', 'profile.firstname', 'profile.lastname'] },
+ # 'profile' => { exclude: ['birthdate'] } }
+
+ # # EXTRA FOREIGN KEYS AND OTHER HAS_MANY SETTINGS
+
+ # # Additional table references which are used to create has_many / belongs_to associations inside auto-created
+ # # models. (You can consider these to be "virtual foreign keys" if you wish)... You only have to add these
+ # # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
+ # # performance reasons or just out of sheer laziness these might be missing.
+ # # Each of these virtual foreign keys is defined as an array having three values:
+ # # foreign table name / foreign key column / primary table name.
+ # # (We boldly expect that the primary key identified by ActiveRecord on the primary table will be accurate,
+ # # usually this is "id" but there are some good smarts that are used in case some other column has been set
+ # # to be the primary key.)
+ # Brick.additional_references = [['orders', 'customer_id', 'customer'],
+ # ['customer', 'region_id', 'regions']]
+
+ # # Custom columns to add to a table, minimally defined with a name and DSL string.
+ # Brick.custom_columns = { 'users' => { messages: ['[COUNT(messages)] messages', 'messages'] },
+ # 'orders' => { salesperson: '[salesperson.first] [salesperson.last]',
+ # products: ['[COUNT(order_items.product)] products', 'order_items.product' ] }
+ # }
+
+ # # Skip creating a has_many association for these (only retain the belongs_to built from this additional_reference).
+ # # (Uses the same exact three-part format as would define an additional_reference)
+ # # Say for instance that we didn't care to display the favourite colours that users have:
+ # Brick.exclude_hms = [['users', 'favourite_colour_id', 'colours']]
+
+ # # Skip showing counts for these specific has_many associations when building auto-generated #index views.
+ # # When there are related tables with a significant number of records (generally 100,000 or more), this can lessen
+ # # the load on the database considerably, sometimes fixing what might appear to be an index page that just "hangs"
+ # # for no apparent reason.
+ # Brick.skip_index_hms = ['User.litany_of_woes']
+
+ # # By default primary tables involved in a foreign key relationship will indicate a "has_many" relationship pointing
+ # # back to the foreign table. In order to represent a "has_one" association instead, an override can be provided
+ # # using the primary model name and the association name which you instead want to have treated as a "has_one":
+ # Brick.has_ones = [['User', 'user_profile']]
+ # # If you want to use an alternate name for the "has_one", such as in the case above calling the association "profile"
+ # # instead of "user_profile", then apply that as a third parameter like this:
+ # Brick.has_ones = [['User', 'user_profile', 'profile']]
+
+ # # We normally don't show the timestamp columns "created_at", "updated_at", and "deleted_at", and also do
+ # # not consider them when finding associative tables to support an N:M association. (That is, ones that can be a
+ # # part of a has_many :through association.) If you want to use different exclusion columns than our defaults
+ # # then this setting resets that list. For instance, here is an override that is useful in the Sakila sample
+ # # database:
+ # Brick.metadata_columns = ['last_update']
+
+ # # Columns for which to add a validate presence: true even though the database doesn't have them marked as NOT NULL.
+ # # Designated by .
+ # Brick.not_nullables = ['users.name']
+
+ # # String or text columns which for editing purposes should be treated as JSON. Format for the hash is:
+ # # { table_name => [column names] }
+ # Brick.json_columns = { 'users' => ['info'] }
+
+ # # FRIENDLY DSL
+
+ # # A simple DSL is available to allow more user-friendly display of objects. Normally a user object might be shown
+ # # as its first non-metadata column, or if that is not available then something like "User #42" where 42 is that
+ # # object's ID. If there is no primary key then even that is not possible, so the object's .to_s method is called.
+ # # To override these defaults and specify exactly what you want shown, such as first names and last names for a
+ # # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
+ # Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
+
+ # # SINGLE TABLE INHERITANCE
+
+ # # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
+ # # parent STI class. The prefixed :: here for these examples is mandatory. Also having a suffixed :: means instead of
+ # # a class reference, this is for a general namespace reference. So in this case requests for, say, either of the
+ # # non-existent classes Animals::Cat or Animals::Goat (or anything else with the module prefix of "Animals::" would
+ # # build a model that inherits from Animal. And a request specifically for the class Snake would build a new model
+ # # that inherits from Reptile, and no other request would do this -- only specifically for Snake. The ending ::
+ # # indicates that it's a module prefix instead of a specific class name.
+ # Brick.sti_namespace_prefixes = { '::Animals::' => 'Animal',
+ # '::Snake' => 'Reptile' }
+
+ # # Custom inheritance_column to be used for STI. This is by default "type", and applies to all models. With this
+ # # option you can change this either for specific models, or apply a new overall name generally:
+ # Brick.sti_type_column = 'sti_type'
+ # Brick.sti_type_column = { 'rails_type' => ['sales.specialoffer'] }
+
+ # # POLYMORPHIC ASSOCIATIONS
+
+ # Polymorphic associations are set up by providing a model name and polymorphic association name.
+ # Here are possible polymorphic associations that have been auto-identified for the ahmad_blog database:
+ Brick.polymorphics = {
+ 'taggings.taggable' => ['Project'],
+ 'taggings.tagger' => ['User'],
+ 'favorites.favoritable' => ['Project'],
+ 'favorites.favoritor' => ['User']
+ }
+
+ # # For multi-tenant databases that use a separate schema for each tenant, a single representative database schema
+ # # can be analysed to determine the range of polymorphic classes that can be used for each association. Hopefully
+ # # the schema chosen is one loaded with existing data that is representative of all possible polymorphic
+ # # associations.
+ # Brick.schema_behavior = :namespaced
+ # Brick.schema_behavior = { multitenant: { schema_to_analyse: 'engineering' } }
+
+ # # DEFAULT ROOT ROUTE
+
+ # # If a default route is not supplied, Brick attempts to find the most "central" table and wires up the default
+ # # route to go to the :index action for what would be a controller for that table. You can specify any controller
+ # # name and action you wish in order to override this and have that be the default route when none other has been
+ # # specified in routes.rb or elsewhere. (Or just use an empty string in order to disable this behaviour.)
+ # This defaults to "customers#index", and if there was also a prefix set called "admin" then it would instead
+ # go to "admin/customers#index".
+ Brick.default_route_fallback = 'projects'
+ # Brick.default_route_fallback = 'orders#outstanding' # Example of a non-RESTful route
+ # Brick.default_route_fallback = '' # Omits setting a default route in the absence of any other
+end
diff --git a/db/migrate/20230531094942_create_active_storage_tables.active_storage.rb b/db/migrate/20230531094942_create_active_storage_tables.active_storage.rb
new file mode 100644
index 0000000..8a7bfe1
--- /dev/null
+++ b/db/migrate/20230531094942_create_active_storage_tables.active_storage.rb
@@ -0,0 +1,57 @@
+# This migration comes from active_storage (originally 20170806125915)
+class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
+ def change
+ # Use Active Record's configured type for primary and foreign keys
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
+
+ create_table :active_storage_blobs, id: primary_key_type do |t|
+ t.string :key, null: false
+ t.string :filename, null: false
+ t.string :content_type
+ t.text :metadata
+ t.string :service_name, null: false
+ t.bigint :byte_size, null: false
+ t.string :checksum
+
+ if connection.supports_datetime_with_precision?
+ t.datetime :created_at, precision: 6, null: false
+ else
+ t.datetime :created_at, null: false
+ end
+
+ t.index [ :key ], unique: true
+ end
+
+ create_table :active_storage_attachments, id: primary_key_type do |t|
+ t.string :name, null: false
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
+ t.references :blob, null: false, type: foreign_key_type
+
+ if connection.supports_datetime_with_precision?
+ t.datetime :created_at, precision: 6, null: false
+ else
+ t.datetime :created_at, null: false
+ end
+
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+
+ create_table :active_storage_variant_records, id: primary_key_type do |t|
+ t.belongs_to :blob, null: false, index: false, type: foreign_key_type
+ t.string :variation_digest, null: false
+
+ t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+ end
+
+ private
+ def primary_and_foreign_key_types
+ config = Rails.configuration.generators
+ setting = config.options[config.orm][:primary_key_type]
+ primary_key_type = setting || :primary_key
+ foreign_key_type = setting || :bigint
+ [primary_key_type, foreign_key_type]
+ end
+end
diff --git a/db/migrate/20230531184246_create_action_text_tables.action_text.rb b/db/migrate/20230531184246_create_action_text_tables.action_text.rb
new file mode 100644
index 0000000..1be48d7
--- /dev/null
+++ b/db/migrate/20230531184246_create_action_text_tables.action_text.rb
@@ -0,0 +1,26 @@
+# This migration comes from action_text (originally 20180528164100)
+class CreateActionTextTables < ActiveRecord::Migration[6.0]
+ def change
+ # Use Active Record's configured type for primary and foreign keys
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
+
+ create_table :action_text_rich_texts, id: primary_key_type do |t|
+ t.string :name, null: false
+ t.text :body, size: :long
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
+
+ t.timestamps
+
+ t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
+ end
+ end
+
+ private
+ def primary_and_foreign_key_types
+ config = Rails.configuration.generators
+ setting = config.options[config.orm][:primary_key_type]
+ primary_key_type = setting || :primary_key
+ foreign_key_type = setting || :bigint
+ [primary_key_type, foreign_key_type]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 395ec87..ed0a062 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,10 +10,48 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2022_09_17_102636) do
+ActiveRecord::Schema[7.0].define(version: 2023_05_31_184246) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "action_text_rich_texts", force: :cascade do |t|
+ t.string "name", null: false
+ t.text "body"
+ t.string "record_type", null: false
+ t.bigint "record_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
+ end
+
+ create_table "active_storage_attachments", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "record_type", null: false
+ t.bigint "record_id", null: false
+ t.bigint "blob_id", null: false
+ t.datetime "created_at", null: false
+ t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
+ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
+ end
+
+ create_table "active_storage_blobs", force: :cascade do |t|
+ t.string "key", null: false
+ t.string "filename", null: false
+ t.string "content_type"
+ t.text "metadata"
+ t.string "service_name", null: false
+ t.bigint "byte_size", null: false
+ t.string "checksum"
+ t.datetime "created_at", null: false
+ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
+ end
+
+ create_table "active_storage_variant_records", force: :cascade do |t|
+ t.bigint "blob_id", null: false
+ t.string "variation_digest", null: false
+ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
+ end
+
create_table "favorites", force: :cascade do |t|
t.string "favoritable_type", null: false
t.bigint "favoritable_id", null: false
@@ -79,5 +117,7 @@
t.datetime "updated_at", null: false
end
+ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "taggings", "tags"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index bc25fce..54ee913 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -5,3 +5,165 @@
#
# movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
# Character.create(name: "Luke", movie: movies.first)
+
+# Simpsons stuff
+# 66 People
+burns=User.create(username: "Charles Montgomery Burns", age: 104) # "https://en.wikipedia.org/wiki/Mr._Burns", blurb: "Owner of the Springfield Nuclear Power Plant.")
+roger_meyers=User.create(username: "Roger Meyers, Jr.", admin: true) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Roger_Meyers_Jr.", blurb: "Chairman of Itchy & Scratchy International.")
+krusty=User.create(username: "Krusty the Clown") # "https://en.wikipedia.org/wiki/Krusty_the_Clown", blurb: "TV show clown host, original name Herschel Shmoikel Pinchas Yerucham Krustofski.")
+sideshow_bob=User.create(username: "Sideshow Bob") # "https://en.wikipedia.org/wiki/Sideshow_Bob", blurb: "Criminal mastermind and Krusty the Clown Show ex-operator and sidekick.")
+sideshow_mel=User.create(username: "Sideshow Mel") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Sideshow_Mel", blurb: "The Krusty the Clown Show sidekick.")
+mr_teeny=User.create(username: "Mr. Teeny") # "https://en.wikipedia.org/wiki/List_of_celebrities_in_The_Simpsons#Mr._Teeny", blurb: "The Krusty the Clown Show trained monkey.")
+radioactive_man=User.create(username: "Radioactive Man") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Radioactive_Man", blurb: "Comic book superhero.")
+bailey=User.create(username: "Mary Bailey") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Mary_Bailey", blurb: "Governor of Springfield's state.")
+smithers=User.create(username: "Waylon Smithers") # "https://en.wikipedia.org/wiki/Waylon_Smithers", blurb: "Mr. Burns's personal assistant.")
+fat_tony=User.create(username: "Fat Tony") # "https://en.wikipedia.org/wiki/Fat_Tony", blurb: "Mob boss.")
+quimby=User.create(username: "Mayor \"Diamond Joe\" Quimby") # "https://en.wikipedia.org/wiki/Mayor_Quimby", blurb: "Mayor of Springfield.")
+wiggum=User.create(username: "Chief Clancy Wiggum") # "https://en.wikipedia.org/wiki/Chief_Wiggum", blurb: "Springfield Chief of Police.")
+lou=User.create(username: "Lou") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Eddie_and_Lou", blurb: "Springfield police officer.")
+eddie=User.create(username: "Eddie") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Eddie_and_Lou", blurb: "Springfield police officer.")
+lovejoy=User.create(username: "Reverend Timothy Lovejoy") # "https://en.wikipedia.org/wiki/Reverend_Lovejoy", blurb: "Minister of the First Church of Springfield.")
+helen=User.create(username: "Helen Lovejoy", admin: true) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Helen_Lovejoy", blurb: "Reverend Lovejoy's wife.")
+carl=User.create(username: "Carl Carlson") # "https://en.wikipedia.org/wiki/Carl_Carlson", blurb: "Springfield Nuclear Power Plant employee.")
+lenny=User.create(username: "Lenny Leonard") # "https://en.wikipedia.org/wiki/Lenny_Leonard", blurb: "Springfield Nuclear Power Plant employee; one of Homer Simpson's friends.")
+marge=User.create(username: "Marge Simpson", admin: true, age: 36) # "https://en.wikipedia.org/wiki/Marge_Simpson", blurb: "Wife of Homer; mother of Bart, Lisa, and Maggie.")
+homer=User.create(username: "Homer Simpson", age: 39) # "https://en.wikipedia.org/wiki/Homer_Simpson", blurb: "Husband of Marge; father of Bart, Lisa, and Maggie.")
+bart=User.create(username: "Bart Simpson", age: 10) # "https://en.wikipedia.org/wiki/Bart_Simpson", blurb: "Eldest child of Homer and Marge; brother of Lisa and Maggie.")
+lisa=User.create(username: "Lisa Simpson", age: 8) # "https://en.wikipedia.org/wiki/Lisa_Simpson", blurb: "Middle child and oldest daughter of Homer and Marge; sister of Bart and Maggie.")
+maggie=User.create(username: "Maggie Simpson", age: 1) # "https://en.wikipedia.org/wiki/Maggie_Simpson"
+ralph=User.create(username: "Ralph Wiggum", age: 8) # "https://en.wikipedia.org/wiki/Ralph_Wiggum", blurb: "Student at Springfield Elementary School.")
+atkins=User.create(username: "State Comptroller Atkins", admin: true) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#State_Comptroller_Atkins", blurb: "State comptroller.")
+chalmers=User.create(username: "Gary Chalmers") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Superintendent_Chalmers", blurb: "Public schools superintendent.")
+skinner=User.create(username: "Principal Seymour Skinner") # "https://en.wikipedia.org/wiki/Principal_Skinner", blurb: "Principal of Springfield Elementary School.")
+doris=User.create(username: "Lunchlady Doris") # "https://en.wikipedia.org/wiki/List_of_recurring_characters_in_The_Simpsons#Lunchlady_Doris", blurb: "Springfield Elementary School lunch lady.")
+
+brockman=User.create(username: "Kent Brockman") # "https://en.wikipedia.org/wiki/Kent_Brockman", blurb: "Channel 6 News anchor.")
+comic_book_guy=User.create(username: "Comic Book Guy") # "https://en.wikipedia.org/wiki/Comic_Book_Guy", blurb: "Proprietor of The Android's Dungeon comic book store. Name revealed to be Jeff Albertson in \"Homer and Ned's Hail Mary Pass\".")
+ned=User.create(username: "Ned Flanders") # "https://en.wikipedia.org/wiki/Ned_Flanders", blurb: "The Simpson family's next door neighbor; owner of The Leftorium.")
+maude_flanders=User.create(username: "Maude Flanders") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Maude_Flanders", blurb: "Ned Flanders's deceased wife.")
+rod_flanders=User.create(username: "Rod Flanders") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Rod_Flanders", blurb: "Ned and Maude Flanders's elder son.")
+todd_flanders=User.create(username: "Todd Flanders") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Todd_Flanders", blurb: "Ned and Maude Flanders's younger son.")
+professor_john_frink=User.create(username: "Professor John Frink") # "https://en.wikipedia.org/wiki/Professor_Frink", blurb: "Scientist, inventor.")
+moe=User.create(username: "Moe Szyslak", admin: true) # "https://en.wikipedia.org/wiki/Moe_Szyslak", blurb: "Owner of Moe's Tavern.")
+barney_gumble=User.create(username: "Barney Gumble") # "https://en.wikipedia.org/wiki/Barney_Gumble", blurb: "Homer's drunken old pal.")
+hibbert=User.create(username: "Dr. Julius Hibbert") # "https://en.wikipedia.org/wiki/Julius_Hibbert", blurb: "Medical doctor.")
+
+snyder=User.create(username: "Roy Snyder") # "", blurb: "Old softie known for lenient punishments and unorthodox rulings.")
+harm=User.create(username: "Constance Harm", admin: true) # "", blurb: "Harsh, unforgiving disciplinarian.")
+hutz=User.create(username: "Lionel Hutz") # "https://en.wikipedia.org/wiki/Lionel_Hutz", blurb: "Shyster of an attorney with an office in a strip mall.")
+gil=User.create(username: "Gil Gunderson") # "", blurb: "Crackpot real estate agent and paralegal.")
+
+hoover=User.create(username: "Elizabeth Hoover") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Elizabeth_Hoover", blurb: "Lisa Simpson's teacher at Springfield Elementary School.")
+krabappel=User.create(username: "Edna Krabappel", admin: true) # "https://en.wikipedia.org/wiki/Edna_Krabappel", blurb: "Bart Simpson's teacher at Springfield Elementary School.")
+largo=User.create(username: "Dewey Largo") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Dewey_Largo", blurb: "Springfield Elementary School's music teacher.")
+willie=User.create(username: "Groundskeeper Willie") # "https://en.wikipedia.org/wiki/Groundskeeper_Willie", blurb: "Janitor of Springfield Elementary School.")
+otto=User.create(username: "Otto Mann") # "https://en.wikipedia.org/wiki/Otto_Mann", blurb: "Springfield Elementary School bus driver.")
+
+johnny_tightlips=User.create(username: "Johnny Tightlips") # "https://en.wikipedia.org/wiki/Springfield_Mafia#Johnny_Tightlips", blurb: "One of Fat Tony's henchmen.")
+legs=User.create(username: "Legs") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Legs_and_Louie", blurb: "Fat Tony's henchmen.")
+louie=User.create(username: "Louie") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Legs_and_Louie", blurb: "Fat Tony's henchmen.")
+frankie_the_squealer=User.create(username: "Frankie the Squealer") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Frankie_the_Squealer", blurb: "Informant for Fat Tony.")
+
+leopold=User.create(username: "Leopold") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Leopold", blurb: "Superintendent Chalmers' assistant.")
+troy_mcclure=User.create(username: "Troy McClure") # "https://en.wikipedia.org/wiki/Troy_McClure", blurb: "Actor.")
+apu=User.create(username: "Apu Nahasapeemapetilon") # "https://en.wikipedia.org/wiki/Apu_Nahasapeemapetilon", blurb: "Operator of the Kwik-E-Mart.")
+arnie_pye=User.create(username: "Arnie Pye") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Arnie_Pye", blurb: "Channel 6 helicopter reporter.")
+
+nelson=User.create(username: "Nelson Muntz", age: 12) # "https://en.wikipedia.org/wiki/Nelson_Muntz", blurb: "Springfield Elementary School bully.")
+jimbo=User.create(username: "Jimbo Jones", age: 12) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Jimbo_Jones", blurb: "Springfield Elementary School bully.")
+kearney=User.create(username: "Kearney Zzyzwicz", age: 29) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Kearney_Zzyzwicz", blurb: "Springfield Elementary School bully.")
+
+milhouse=User.create(username: "Milhouse Van Houten", age: 10) # "https://en.wikipedia.org/wiki/Milhouse_Van_Houten", blurb: "Bart Simpson's best friend; Student at Springfield Elementary School.")
+janey=User.create(username: "Janey Powell", age: 8) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Janey_Powell", blurb: "Student at Springfield Elementary School.")
+martin=User.create(username: "Martin Prince", admin: true, age: 10) # "https://en.wikipedia.org/wiki/Martin_Prince", blurb: "Student at Springfield Elementary School.")
+wendell=User.create(username: "Wendell Borton", age: 10) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Wendell_Borton", blurb: "Student at Springfield Elementary School.")
+lewis=User.create(username: "Lewis Clark", age: 10) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Lewis", blurb: "Student at Springfield Elementary School.")
+uter=User.create(username: "Üter Zörker") # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#.C3.9Cter", blurb: "Student at Springfield Elementary School.")
+sherri=User.create(username: "Sherri", age: 10) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Sherri_and_Terri", blurb: "Identical twin; Student at Springfield Elementary School.")
+terri=User.create(username: "Terri", age: 10) # "https://en.wikipedia.org/wiki/List_of_recurring_The_Simpsons_characters#Sherri_and_Terri", blurb: "Identical twin; Student at Springfield Elementary School.")
+
+# Everyone's Projects
+snpp_boss = Project.create(name: "Power plant", owner: burns)
+Project.create(name: "Social disruption", owner: burns)
+Project.create(name: "Media", owner: roger_meyers)
+Project.create(name: "TV", owner: krusty)
+Project.create(name: "Connecting with his Rabbi Father", owner: krusty)
+Project.create(name: "Crime", owner: sideshow_bob)
+Project.create(name: "Terrorising Bart", owner: sideshow_bob)
+Project.create(name: "TV", owner: sideshow_mel)
+Project.create(name: "TV", owner: mr_teeny)
+Project.create(name: "Comic books", owner: radioactive_man)
+Project.create(name: "Politics", owner: bailey)
+Project.create(name: "Errands", owner: smithers)
+Project.create(name: "Malibu Stacy Dolls", owner: smithers)
+mob_boss = Project.create(name: "Mob tricks", owner: fat_tony)
+Project.create(name: "Politics", owner: quimby)
+Project.create(name: "Battling Crime", owner: wiggum)
+Project.create(name: "Battling Crime", owner: lou)
+Project.create(name: "Battling Crime", owner: eddie)
+religion = Project.create(name: "God", owner: lovejoy)
+Project.create(name: "Church Socials", owner: helen)
+Project.create(name: "Nuclear energy", owner: carl)
+Project.create(name: "Nuclear energy", owner: lenny)
+Project.create(name: "Curating large mammals", owner: marge)
+Project.create(name: "Beer", owner: homer)
+Project.create(name: "The Boy", owner: homer)
+Project.create(name: "Hyperactivity", owner: bart)
+Project.create(name: "Paying people to kiss", owner: bart)
+Project.create(name: "Critical thinking", owner: lisa)
+Project.create(name: "Environment", owner: lisa)
+Project.create(name: "Comptrolling Schools", owner: atkins)
+Project.create(name: "Teaching", owner: chalmers)
+Project.create(name: "Teaching", owner: skinner)
+Project.create(name: "Stories about Viet Nam", owner: skinner)
+Project.create(name: "Cafeteria", owner: doris)
+Project.create(name: "TV", owner: brockman)
+Project.create(name: "Comics", owner: comic_book_guy)
+Project.create(name: "God", owner: ned)
+Project.create(name: "House", owner: maude_flanders)
+Project.create(name: "Conformity", owner: rod_flanders)
+Project.create(name: "Fear of moths", owner: todd_flanders)
+Project.create(name: "Science and Inventions", owner: professor_john_frink)
+Project.create(name: "Pouring Alcohol", owner: moe)
+Project.create(name: "Singing", owner: barney_gumble)
+Project.create(name: "Medicine", owner: hibbert)
+Project.create(name: "Law", owner: snyder)
+Project.create(name: "Law", owner: harm)
+Project.create(name: "Attorney", owner: hutz)
+Project.create(name: "Real estate", owner: gil)
+Project.create(name: "Teaching", owner: hoover)
+Project.create(name: "Teaching", owner: krabappel)
+Project.create(name: "Music", owner: largo)
+Project.create(name: "Cleaning", owner: willie)
+Project.create(name: "Driving bus", owner: otto)
+Project.create(name: "Smoking weed", owner: otto)
+Project.create(name: "Mob tricks", owner: johnny_tightlips)
+Project.create(name: "Mob tricks", owner: legs)
+Project.create(name: "Mob tricks", owner: louie)
+Project.create(name: "Mob tricks", owner: frankie_the_squealer)
+Project.create(name: "Paperwork", owner: leopold)
+Project.create(name: "Acting", owner: troy_mcclure)
+Project.create(name: "Satisfying customers", owner: apu)
+Project.create(name: "Raising children", owner: apu)
+Project.create(name: "Helicopter reporting", owner: arnie_pye)
+Project.create(name: "Guns", owner: nelson)
+Project.create(name: "Bullying", owner: nelson)
+Project.create(name: "Bullying", owner: jimbo)
+Project.create(name: "Bullying", owner: kearney)
+Project.create(name: "The Cracker Factory", owner: milhouse)
+Project.create(name: "Keep up with Bart", owner: milhouse)
+Project.create(name: "School", owner: janey)
+Project.create(name: "Aerodynamics", owner: martin)
+Project.create(name: "School", owner: wendell)
+Project.create(name: "School", owner: lewis)
+Project.create(name: "School", owner: uter)
+Project.create(name: "School", owner: sherri)
+Project.create(name: "School", owner: terri)
+
+# A couple tags on projects
+ceo = Tag.find_or_create_by(name: "CEO")
+Tagging.create(taggable: mob_boss, tagger: fat_tony, tag: ceo)
+Tagging.create(taggable: snpp_boss, tagger: burns, tag: ceo)
+
+# A favourite
+Favorite.create(favoritor: marge, favoritable: religion)