Skip to content

Commit

Permalink
more complicated queries: scopes, bangs
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffpeterson committed Nov 21, 2024
1 parent 38b886a commit aa4dbcd
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 7 deletions.
43 changes: 36 additions & 7 deletions lib/cafe_car/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,45 @@ def initialize(scope)
@scope = scope
end

def unscoped = QueryBuilder.new(@scope.unscoped)

def arel = @scope.arel_table

def update!(&)
scope = instance_exec(@scope, &)
scope = @scope.instance_exec(@scope, &)
@scope = scope if scope
self
end

def not!(&)
inverted = unscoped.instance_exec(&).scope.invert_where
update! { _1.and(inverted) }
end

def association?(name) = @scope.reflect_on_association(name).present?
def attribute?(name) = @scope.columns_hash[name.to_s].present?
def scope?(name) = name.intern.in? @scope.local_methods

def param!(key, value)
association?(key) ? association!(key, value) : attribute!(key, value)
case key
when /^(.*)~$/
param!($1, Regexp.new(value))
when /^(.*)!$/
not! { param!($1, value) }
when method(:association?)
association!(key, value)
when method(:attribute?)
attribute!(key, value)
when method(:scope?)
scope!(key, value)
else
raise "what is this param? #{key.inspect}"
end
end

def attribute!(key, value)
case value
when Regexp
case [key, value]
in _, Regexp
@scope.where!(arel[key].matches_regexp(value.source, !value.casefold?))
else @scope.where!(key => value)
end
Expand All @@ -34,13 +56,20 @@ def attribute!(key, value)
def association!(name, value, ...)
update! do
case value
when true then @scope.where_assoc_exists(name)
when false then @scope.where_assoc_not_exists(name)
else @scope.where_assoc_exists(name) { query(value, ...) }
when true then where_assoc_exists(name)
when false then where_assoc_not_exists(name)
else where_assoc_exists(name) { query(value, ...) }
end
end
end

def scope!(name, value)
arity = (@scope.scopes[name] || @scope.method(name)).arity
value = nil if arity == 0 and value == true

update! { public_send(name, *value) }
end

def query!(params = nil)
params.each { param!(_1, _2) } if params
self
Expand Down
15 changes: 15 additions & 0 deletions lib/cafe_car/queryable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ module CafeCar::Queryable
extend ActiveSupport::Concern

class_methods do
def scope(name, body)
scopes[name] = body
super
end

def query(params) = query_builder.query(params).scope
def query!(params) = query_builder.query!(params).scope

def query_builder
CafeCar::QueryBuilder.new(self)
end

def scopes
@scopes ||= {}.with_indifferent_access
end

def local_methods
@local_methods ||= public_methods -
ActiveRecord::Base.public_methods -
Kaminari::ConfigurationMethods::ClassMethods.instance_methods
end
end
end
3 changes: 3 additions & 0 deletions test/dummy/app/models/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ class Article < ApplicationRecord

validates :title, presence: true

scope :published, -> { where(published_at: ..Time.zone.now) }
scope :unpublished, -> { where(published_at: Time.zone.now..) }

def published? = published_at && published_at < Time.zone.now
end

0 comments on commit aa4dbcd

Please sign in to comment.