Andrew Evans - @agius
Hired: 2.5yrs
Startups: 9yrs
Rails + JS: 10+yrs
Bugs: 14yrs
Best way to get tech job / good candidates
Two-sided marketplace
Go "live" when hunting, don't get badgered otherwise
Companies ask you to interview, give salary, equity, etc
Best way to get tech job / good candidates
Two-sided marketplace
Go "live" when hunting, don't get badgered otherwise
Companies ask you to interview, give salary, equity, etc
Reactor - pub/sub message bus for Sidekiq
Stretchy - Flexible query builder for Elasticsearch
Fortitude - Rock-solid SCSS framework for teams
Puma Stats Logger - Rack middleware for lawgz
lledci - Turn lights red/green from your CI
...and 41 more!
Reactor - pub/sub message bus for Sidekiq
Stretchy - Flexible query builder for Elasticsearch
should you Open™?
what are the challenges?
how did Open™ affect our team?
what did Open™ teach us about Ruby & Rails?
Q&A
Everyone needs this! It's so useful!
Community will fix bugs, add features! (free labor!)
Developer Street Cred!
Thought Leadering!
Everyone will be clapping! For me!
Everyone needs this! It's so useful! (maybe?)
Community will fix bugs, add features! (free labor!)
Developer Street Cred!
Thought Leadering!
Everyone will be clapping! For me!
Rank | Avg ⬇︎ | ✭ | |
---|---|---|---|
Reactor | 15,022 | 48.38 | 45 |
Fortitude | 9,297 | 27.82 | 38 |
Stretchy | 33,887 | 52.59 | 43 |
Puma Logger | 14,729 | 39.38 | 39 |
rave - build bots for Google Wave
pirate_bay_ruby - CLI for dead web site
activetrail - ActiveAdmin + Trailblazer = ???
hedbergism - Random Mitch Hedberg quote
def update
candidate.save!
WorkerOne.perform_async(candidate)
WorkerTwo.perform_async(candidate)
WorkerThree.perform_async(candidate)
# ...
WorkerTwentySeven.perform_async(candidate)
end
⌘+C, ⌘+V
# WorkerOne
def perform(candidate)
WorkerTwo.perform_async(candidate)
WorkerThree.perform_async(candidate)
end
# WorkerTwo
def perform(candidate)
WorkerOne.perform_async(candidate) # OOPS
end
Reactor::Event.publish :my_event, some: :data
candidate.publish :profile_updated
Kicks off abstracted Sidekiq worker
Locates "subscriber" workers & enqueues their jobs
Each job gets all event data
def action_event(event_name, options = {})
current_user.publish(event_name,
{controller: params[:controller]}.merge(options))
end
class CandidateCacheBuster
include Reactor::Subscribable
on_event :profile_updated do |event|
id = event.actor.id # User id
REDIS.del "candidate-profile-cache-#{id}"
end
end
class ActivityLogger
include Reactor::Subscribable
on_event '*' do |event|
# log it to places
end
end
class AdminEmailer < ActionMailer::Base
include Reactor::Subscribable
on_event :profile_updated, :activity_email
def activity_email(event)
# ... send the admin an email
end
end
class ReminderMailer < ActionMailer::Base
include Reactor::Subscribable
on_event :interview_request, delay: 2.days do |event|
# send reminder that this happened
end
end
class AdminNotifier < Reactor::Subscriber
# t.string "event_name", limit: 255
# t.string "type", limit: 255
# t.json "data"
# t.datetime "created_at", null: false
# t.datetime "updated_at", null: false
on_fire do
notify_admin! if should_notify?
end
def notify_admin! ; end
def should_notify? ; end
end
class Interview < ApplicationRecord
# t.string "status", limit: 255
# t.datetime "end_at", limit: 255
publishes :interview_ended, at: :end_at,
if: -> { status == 'scheduled' }
publishes :interview_start_reminder, at: :interview_reminder_time,
watch: :start_at, if: -> { status == 'scheduled' }
end
will auto-reschedule jobs if end_at
changes
reactor code is out-of-sight, out-of-mind
extra friction discourages arbitrary changes
keeps pattern & convention clear
open visibility adds pressure to do it "right"
pinning versions and releasing thoughtfully
Different context forces thinking about the pattern
What's the minimum info Reactor needs, vs what our app will use?
requiring Rails is harder than it sounds!
message bus: subscribe to "channels," data is arbitrary
different from event delegation
ActionCable - subscriber_map.rb
ActiveSupport::Notifications - subscriber.rb
didn't make us famous
helped our team establish a convention
quietly kept working
smaller, loosely-coupled code
learning cool pattern stuff
ActiveRecord-ish syntax, immutable queries
just a query builder; use Reactor for indexing
flexible enough for complicated queries
no dependencies except Elasticsearch
composable scopes for common query functions
Candidate.search
.match(university: 'Michigan')
.where(
recognized_enough: false,
born_between: 100.years.ago..50.years.ago
)
.boost.match(study_field: 'microchips')
.results
ActiveRecord-ish query syntax
{"function_score" : {
"functions" : [{
"filter" : { "match" : { "study_field" : "microchips" } },
"weight" : 1.5
}],
"query" : {
"bool" : {
"must" : [{"match" : { "university" : "Michigan"}}],
"filter" : [
{ "terms" : { "recognized_enough" : [ false ] } },
{ "range" : { "born_between" : {
"gte" : "1917-04-13T16:16:59.883-08:00",
"lte" : "1967-04-13T16:16:59.884-08:00" }
}}
]}}}}
api = Stretchy.query(index: 'candidates', type: 'candidate')
# range filter - remember, immutable! api = api.query_method...
api = api.where created_at: (2.days.ago)..(1.days.ago)
# match query
api = api.match name: 'Grace Hopper'
# multiple should filters (OR-like)
api = api.should.where language: 'ruby', awesome: true
# function_score functions
api = api.boost.near field: 'coords', origin: [lon, lat]
# raw query
api = api.query multi_match: {name: 'Adele Goldberg', fields: [...]}
this 2010 post from John Nunemaker is still relevant
def add_nodes(additional)
self.class.new(opts.merge(
nodes: collector.nodes + Array(additional),
root: root,
body: body,
context: {}
))
end
new self
with added things (collect into array)
Stretchy doesn't need multiple visitors
only one dialect
confusing: other gems do much more
API too like ActiveRecord hides ES complexity, features
hard to tell what common query types to extract
updating the gem shouldn't block new features
make it more flexible for internal customization
make it more flexible for internal customization
...
several times
make it more flexible for internal customization
...
several times
...
then some more
learned cool new pattern
excuse to dive deep on framework code
core query interface simple, business logic complex
keep ES concerns out of models
Open™ is cool
even if your code isn't great
even if this guy is your only Github watcher
even if you don't get free labor
a little more friction can be good
thinking off the Rails can make code clearer
learn more about computers
be more thoughtful