Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
JoshCheek / cancancan_raise_on_undeclared_abilities.rb
Created June 8, 2022 22:07
Modifying CanCanCan's default behaviour to raise when asked about undeclared abilities
gem 'cancancan', '= 2.1.2' # the version from our gemfile
require 'cancan'
module CanCan::StrictAbility
include CanCan::Ability
# Overrides this method: https://github.com/CanCanCommunity/cancancan/blob/2.1.2/lib/cancan/ability.rb#L67-L74
# NOTE: on newer cancans, the implementation of that method changed to allow attribute level rules
def can?(action, subject, *extra_args)
had_relevant_rule = false
@JoshCheek
JoshCheek / not_cancancan.rb
Last active June 4, 2022 00:25
What I feel like cancancan abilities should have been
# Library implementation of what it feels like cancan should have been
module NotCanCan
class Permission
attr_reader :verb, :direct_object, :default, :dynamic_override
def initialize(verb, direct_object, default, &dynamic_override)
@verb, @direct_object = verb, direct_object
@default, @dynamic_override = !!default, dynamic_override
end
@JoshCheek
JoshCheek / cancancan_ability_composition_strategy.rb
Last active June 7, 2022 20:04
Extending CanCanCan, abilities
require 'cancan'
# Some models
Post = Struct.new(:author, :published, :body) { alias published? published }
User = Struct.new(:name, :admin) { alias admin? admin }
# Abilities
class CommonAbility
include CanCan::Ability
def initialize(user)
@JoshCheek
JoshCheek / active_record_upsert_example.rb
Last active May 26, 2022 22:27
ActiveRecord upsert example
# configure activerecord (in-memory sqlite db to keep it simple)
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
# helper method for asserting expectations
def check!(expected, operator = :==, actual)
equal = expected.public_send(operator, actual) and return
raise RuntimeError, <<~MESSAGE, caller
test: #{expected.inspect} #{operator} #{actual.inspect}
expected: true
@JoshCheek
JoshCheek / how_do_I_fix_the_n+1_query.rb
Last active May 26, 2022 08:30
How do I define the association in a way that gets rid of this N+1 query?
# Asking the twitterverse how to deal with this: https://twitter.com/josh_cheek/status/1529326801654358023
# Configure ActiveRecord
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
# Migrations
ActiveRecord::Schema.define do
self.verbose = false
create_table :users do |t|
# Was super struggling to get my array to validate (nil is bad, empty array is fine, nonempty array should contain strings)
# Finally went and read the code, and it looked totally buggy (b/c `nil` is `blank?`:
# https://github.com/rails/rails/blob/25b126707152de3a1ec9762ee564f3c7623373b3/activemodel/lib/active_model/validator.rb#L151
# Made this test to demonstrate.
require 'active_model'
def test(*values, **validation)
[false, true].product([false, true]).map.with_index 1 do |(allow_nil, allow_blank), index|
klass = Struct.new(:values, keyword_init: true) { include ActiveModel::Validations }
@JoshCheek
JoshCheek / ruby_case_statements_rase_when_unmatched.rb
Created May 5, 2022 23:23
Example of how to deal with Ruby case statements
# https://twitter.com/josh_cheek/status/1522312126995611651
def UnmatchedCase(...)
UnmatchedCase.new(...)
end
class UnmatchedCase < RuntimeError
PENDING = Struct.new(:inspect).new('(PENDING)')
attr_reader :description, :value
def initialize(description, value = PENDING)
@JoshCheek
JoshCheek / example.rb
Created April 12, 2022 18:03
Example of using UUIDs for primary keys in ActiveRecord
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'postgresql', database: 'josh_test'
ActiveRecord::Schema.define do
self.verbose = false
enable_extension 'pgcrypto'
create_table :conversations, id: :uuid do |t|
t.string :topic
end
create_table :messages, id: :uuid do |t|
t.uuid :conversation_id, null: false, index: true
@JoshCheek
JoshCheek / active_record_deprecation_example.rb
Last active April 12, 2022 01:10
ActiveRecord deprecation... so how are you supposed to write this, then?
gem 'activerecord', '= 6.0.4.6'
require 'active_record'
ActiveRecord::VERSION::STRING # => "6.0.4.6"
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Schema.define do
self.verbose = false
create_table(:users) { |t|
t.string :user_name
t.string :first_name
@JoshCheek
JoshCheek / how_to_foreward_args_in_bash.sh
Created April 6, 2022 17:14
How to forward args in Bash (`"$@"`)
set -- a 'b c' d
ruby -e 'puts %($*), ARGV.map { " #{_1.inspect}" }, ""' $*
ruby -e 'puts %("$*"), ARGV.map { " #{_1.inspect}" }, ""' "$*"
ruby -e 'puts %($@), ARGV.map { " #{_1.inspect}" }, ""' $@
ruby -e 'puts %("$@"), ARGV.map { " #{_1.inspect}" }, ""' "$@"