Here's the second weekly recap!
In last weeks recap, I shared where we were at in code and it looked like this:
# test_helper.rb
Oaken::Data.load_from "test/seeds"
# test/seeds/users.rb
User = Struct.new(:name, keyword_init: true)
class Tapper < BasicObject | |
def initialize(object) = @object = object | |
def method_missing(meth) | |
@object.tap(&meth) | |
self | |
end | |
end | |
class Object |
# Spotify Mix Playlists: Assuming we have a history of played songs for a user, | |
# we have song recommendations via nearest neighbor search, | |
# and we have categorizations (genre, mood, era, instrumental/vocal, cultural/regional, theme), | |
# let system admins create mix templates based on music categorizations | |
# and then generate refreshable custom playlists for each user. | |
class User | |
has_many :playlists | |
has_one :history | |
end |
# Here's the problem statement we worked on: | |
# 1. Spam post detection: Run a forum post through a series of configurable checks to give it a spam score, | |
# and flag the post when it crosses the threshold, with details on what led to the score. | |
# We had a Gemfile with `gem "rails"` and `gem "sqlite3"` in it, but that was it. | |
require "bundler/setup" | |
require "active_record" | |
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") |
create_table :stowaway_records do |t| | |
t.string :record_type, null: false | |
t.jsonb :record_attributes, null: false, default: {} | |
t.timestamps | |
t.index :record_type | |
end | |
class Stowaway::Record < ActiveRecord::Base | |
def self.preserve(record) | |
create! record_type: record.class, record_attributes: record.attributes |
donuts = accounts.create :kaspers_donuts, name: "Kasper's Donuts" | |
kasper = users.create :kasper, name: "Kasper", accounts: [donuts] | |
coworker = users.create :coworker, name: "Coworker", accounts: [donuts] | |
menu = menus.create account: donuts | |
plain_donut = menu_items.create menu: menu, name: "Plain", price_cents: 10_00 | |
sprinkled_donut = menu_items.create menu: menu, name: "Sprinkled", price_cents: 10_10 | |
supporter = users.create name: "Super Supporter" |
Here's the second weekly recap!
In last weeks recap, I shared where we were at in code and it looked like this:
# test_helper.rb
Oaken::Data.load_from "test/seeds"
# test/seeds/users.rb
User = Struct.new(:name, keyword_init: true)
class FormBuilder | |
def text_field(method, **options) | |
tag.input type: :text, **options_for(__method__, method, **options) | |
end | |
def options_for(type, method, index: @index, namespace: @options[:namespace], multiple: false, **options) | |
Options.new(self, type, method, index:, namespace:, multiple:, **options) | |
end | |
class Options |
# Provide a BulletTrain::Theme::FormBuilder, with extensions to | |
# go between Rails standard form helpers and BulletTrain's themed versions seamlessly. | |
# With standard Rails: | |
# form_with model: Post.new do |form| | |
# form.themed.text_field # Use BulletTrain theming on just one field | |
# | |
# form.themed.fields do # Need a nested section of fields but themed? | |
# end | |
# end |
class Object::Proxy < SimpleDelegator | |
def initialize(object, **values) | |
super(object) | |
@values = values | |
end | |
def method_missing(name, ...) | |
@values.fetch(name) { super } | |
end | |
end |
class Developer | |
has_object :search_scorer, after_save_commit: :rebuild_later # Forward callback onto context object. | |
end | |
# From my https://github.com/kaspth/active_record-associated_object gem. | |
# app/models/developer/search_scorer.rb | |
class Developer::SearchScorer < ActiveRecord::AssociatedObject | |
performs :rebuild # Adds `rebuild_later` to automatically generate a job to run the scoring calculation in. | |
def rebuild | |
record.update! search_score: new_score |