Skip to content

Commit

Permalink
add monkey patch for joining with ONLY keyword (#35)
Browse files Browse the repository at this point in the history
* monkey patch for join on only

* update comment
  • Loading branch information
waymondo authored Dec 25, 2023
1 parent 2b9d284 commit 6a78ec9
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 93 deletions.
3 changes: 1 addition & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ Rake::TestTask.new(:test) do |t|
t.test_files = FileList["test/**/test_*.rb"]
end

SOURCE_FILES = %w[**/*.rb Rakefile Gemfile bin/console]
SOURCE_FILES = %w[test/**/*.rb lib/**/*.rb Rakefile Gemfile bin/console hoardable.gemspec]

SyntaxTree::Rake::CheckTask.new(:check) do |t|
t.source_files = SOURCE_FILES
t.print_width = 100
t.ignore_files = "vendor/**/*.rb"
end

SyntaxTree::Rake::WriteTask.new(:write) do |t|
Expand Down
49 changes: 26 additions & 23 deletions hoardable.gemspec
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
# frozen_string_literal: true

require_relative 'lib/hoardable/version'
require_relative "lib/hoardable/version"

Gem::Specification.new do |spec|
spec.name = 'hoardable'
spec.name = "hoardable"
spec.version = Hoardable::VERSION
spec.authors = ['justin talbott']
spec.email = ['[email protected]']
spec.authors = ["justin talbott"]
spec.email = ["[email protected]"]

spec.summary = 'An ActiveRecord extension for versioning and soft-deletion of records in Postgres'
spec.description = 'Rails model versioning with the power of uni-temporal inherited tables'
spec.homepage = 'https://github.com/waymondo/hoardable'
spec.license = 'MIT'
spec.required_ruby_version = '>= 2.7.0'
spec.summary = "An ActiveRecord extension for versioning and soft-deletion of records in Postgres"
spec.description = "Rails model versioning with the power of uni-temporal inherited tables"
spec.homepage = "https://github.com/waymondo/hoardable"
spec.license = "MIT"
spec.required_ruby_version = ">= 3.0"

spec.metadata['homepage_uri'] = spec.homepage
spec.metadata['source_code_uri'] = spec.homepage
spec.metadata['rubygems_mfa_required'] = 'true'
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["rubygems_mfa_required"] = "true"

spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0").reject do |f|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
spec.files =
Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0")
.reject do |f|
(f == __FILE__) ||
f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
end
end
end
spec.bindir = 'exe'
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.require_paths = ["lib"]

spec.add_dependency 'activerecord', '>= 6.1', '< 8'
spec.add_dependency 'activesupport', '>= 6.1', '< 8'
spec.add_dependency 'railties', '>= 6.1', '< 8'
spec.add_dependency "activerecord", ">= 7", "< 8"
spec.add_dependency "activesupport", ">= 7", "< 8"
spec.add_dependency "railties", ">= 7", "< 8"

spec.add_dependency 'fx', '>= 0.8', '< 1'
spec.add_dependency 'pg', '>= 1', '< 2'
spec.add_dependency "fx", ">= 0.8", "< 1"
spec.add_dependency "pg", ">= 1", "< 2"
end
1 change: 1 addition & 0 deletions lib/hoardable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "active_record"
require "fx"
require_relative "hoardable/version"
require_relative "hoardable/arel_visitors"
require_relative "hoardable/engine"
require_relative "hoardable/finder_methods"
require_relative "hoardable/scopes"
Expand Down
51 changes: 51 additions & 0 deletions lib/hoardable/arel_visitors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module Hoardable
# This is a monkey patch of JOIN related {Arel::Visitors} for PostgreSQL so that they can append
# the ONLY clause when known to be operating on a {Hoardable::Model}. Ideally, {Arel} itself would
# provide a mechanism to support this keyword.
module ArelVisitors
def visit_Arel_Nodes_FullOuterJoin(o, collector)
collector << "FULL OUTER JOIN "
hoardable_maybe_add_only(o, collector)
collector = visit o.left, collector
collector << " "
visit o.right, collector
end

def visit_Arel_Nodes_OuterJoin(o, collector)
collector << "LEFT OUTER JOIN "
hoardable_maybe_add_only(o, collector)
collector = visit o.left, collector
collector << " "
visit o.right, collector
end

def visit_Arel_Nodes_RightOuterJoin(o, collector)
collector << "RIGHT OUTER JOIN "
hoardable_maybe_add_only(o, collector)
collector = visit o.left, collector
collector << " "
visit o.right, collector
end

def visit_Arel_Nodes_InnerJoin(o, collector)
collector << "INNER JOIN "
hoardable_maybe_add_only(o, collector)
collector = visit o.left, collector
if o.right
collector << " "
visit(o.right, collector)
else
collector
end
end

private def hoardable_maybe_add_only(o, collector)
return unless o.left.instance_variable_get("@klass").in?(Hoardable::REGISTRY)
return if Hoardable.instance_variable_get("@at")

collector << "ONLY "
end
end
end

Arel::Visitors::PostgreSQL.prepend Hoardable::ArelVisitors
2 changes: 2 additions & 0 deletions lib/hoardable/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# An +ActiveRecord+ extension for keeping versions of records in uni-temporal inherited tables.
module Hoardable
REGISTRY = Set.new

# Symbols for use with setting contextual data, when creating versions. See
# {file:README.md#tracking-contextual-data README} for more.
DATA_KEYS = %i[meta whodunit event_uuid].freeze
Expand Down
4 changes: 1 addition & 3 deletions lib/hoardable/has_many.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ def scope
@scope ||= hoardable_scope
end

private

def hoardable_scope
private def hoardable_scope
if Hoardable.instance_variable_get("@at") &&
(hoardable_id = @association.owner.hoardable_id)
@association.scope.rewhere(@association.reflection.foreign_key => hoardable_id)
Expand Down
1 change: 1 addition & 0 deletions lib/hoardable/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def with_hoardable_config(hash)
object_namespace.const_set(version_class_name, Class.new(self) { include VersionModel })
end
include SourceModel
REGISTRY.add(self)

trace.disable
end
Expand Down
2 changes: 1 addition & 1 deletion test/support/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class PostWithUnhoardableRichText < ActiveRecord::Base

class User < ActiveRecord::Base
include Hoardable::Model
has_many :posts
has_many :posts, hoardable: true
has_one :profile, hoardable: true
has_rich_text :bio, hoardable: true
serialize :preferences, coder: JSON
Expand Down
Loading

0 comments on commit 6a78ec9

Please sign in to comment.