mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
f41825809c
This patch has two main portions: 1. Add SQL comment support to Arel via Arel::Nodes::Comment. 2. Implement a Relation#annotate method on top of that. == Adding SQL comment support Adds a new Arel::Nodes::Comment node that represents an optional SQL comment and teachers the relevant visitors how to handle it. Comment nodes may be added to the basic CRUD statement nodes and set through any of the four (Select|Insert|Update|Delete)Manager objects. For example: manager = Arel::UpdateManager.new manager.table table manager.comment("annotation") manager.to_sql # UPDATE "users" /* annotation */ This new node type will be used by ActiveRecord::Relation to enable query annotation via SQL comments. == Implementing the Relation#annotate method Implements `ActiveRecord::Relation#annotate`, which accepts a comment string that will be appeneded to any queries generated by the relation. Some examples: relation = Post.where(id: 123).annotate("metadata string") relation.first # SELECT "posts".* FROM "posts" WHERE "posts"."id" = 123 # LIMIT 1 /* metadata string */ class Tag < ActiveRecord::Base scope :foo_annotated, -> { annotate("foo") } end Tag.foo_annotated.annotate("bar").first # SELECT "tags".* FROM "tags" LIMIT 1 /* foo */ /* bar */ Also wires up the plumbing so this works with `#update_all` and `#delete_all` as well. This feature is useful for instrumentation and general analysis of queries generated at runtime.
116 lines
4.8 KiB
Ruby
116 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Pirate < ActiveRecord::Base
|
|
belongs_to :parrot, validate: true
|
|
belongs_to :non_validated_parrot, class_name: "Parrot"
|
|
has_and_belongs_to_many :parrots, -> { order("parrots.id ASC") }, validate: true
|
|
has_and_belongs_to_many :non_validated_parrots, class_name: "Parrot"
|
|
has_and_belongs_to_many :parrots_with_method_callbacks, class_name: "Parrot",
|
|
before_add: :log_before_add,
|
|
after_add: :log_after_add,
|
|
before_remove: :log_before_remove,
|
|
after_remove: :log_after_remove
|
|
has_and_belongs_to_many :parrots_with_proc_callbacks, class_name: "Parrot",
|
|
before_add: proc { |p, pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}" },
|
|
after_add: proc { |p, pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}" },
|
|
before_remove: proc { |p, pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}" },
|
|
after_remove: proc { |p, pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}" }
|
|
has_and_belongs_to_many :autosaved_parrots, class_name: "Parrot", autosave: true
|
|
|
|
module PostTreasuresExtension
|
|
def build(attributes = {})
|
|
super({ name: "from extension" }.merge(attributes))
|
|
end
|
|
end
|
|
|
|
has_many :treasures, as: :looter, extend: PostTreasuresExtension
|
|
has_many :treasure_estimates, through: :treasures, source: :price_estimates
|
|
|
|
has_one :ship
|
|
has_one :update_only_ship, class_name: "Ship"
|
|
has_one :non_validated_ship, class_name: "Ship"
|
|
has_many :birds, -> { order("birds.id ASC") }
|
|
has_many :birds_with_method_callbacks, class_name: "Bird",
|
|
before_add: :log_before_add,
|
|
after_add: :log_after_add,
|
|
before_remove: :log_before_remove,
|
|
after_remove: :log_after_remove
|
|
has_many :birds_with_proc_callbacks, class_name: "Bird",
|
|
before_add: proc { |p, b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}" },
|
|
after_add: proc { |p, b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}" },
|
|
before_remove: proc { |p, b| p.ship_log << "before_removing_proc_bird_#{b.id}" },
|
|
after_remove: proc { |p, b| p.ship_log << "after_removing_proc_bird_#{b.id}" }
|
|
has_many :birds_with_reject_all_blank, class_name: "Bird"
|
|
|
|
has_one :foo_bulb, -> { where name: "foo" }, foreign_key: :car_id, class_name: "Bulb"
|
|
|
|
accepts_nested_attributes_for :parrots, :birds, allow_destroy: true, reject_if: proc(&:empty?)
|
|
accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
|
|
accepts_nested_attributes_for :update_only_ship, update_only: true
|
|
accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
|
|
:birds_with_method_callbacks, :birds_with_proc_callbacks, allow_destroy: true
|
|
accepts_nested_attributes_for :birds_with_reject_all_blank, reject_if: :all_blank
|
|
|
|
validates_presence_of :catchphrase
|
|
|
|
def ship_log
|
|
@ship_log ||= []
|
|
end
|
|
|
|
def reject_empty_ships_on_create(attributes)
|
|
attributes.delete("_reject_me_if_new").present? && !persisted?
|
|
end
|
|
|
|
attr_accessor :cancel_save_from_callback, :parrots_limit
|
|
before_save :cancel_save_callback_method, if: :cancel_save_from_callback
|
|
def cancel_save_callback_method
|
|
throw(:abort)
|
|
end
|
|
|
|
private
|
|
def log_before_add(record)
|
|
log(record, "before_adding_method")
|
|
end
|
|
|
|
def log_after_add(record)
|
|
log(record, "after_adding_method")
|
|
end
|
|
|
|
def log_before_remove(record)
|
|
log(record, "before_removing_method")
|
|
end
|
|
|
|
def log_after_remove(record)
|
|
log(record, "after_removing_method")
|
|
end
|
|
|
|
def log(record, callback)
|
|
ship_log << "#{callback}_#{record.class.name.downcase}_#{record.id || '<new>'}"
|
|
end
|
|
end
|
|
|
|
class DestructivePirate < Pirate
|
|
has_one :dependent_ship, class_name: "Ship", foreign_key: :pirate_id, dependent: :destroy
|
|
end
|
|
|
|
class FamousPirate < ActiveRecord::Base
|
|
self.table_name = "pirates"
|
|
has_many :famous_ships
|
|
validates_presence_of :catchphrase, on: :conference
|
|
end
|
|
|
|
class SpacePirate < ActiveRecord::Base
|
|
self.table_name = "pirates"
|
|
|
|
belongs_to :parrot
|
|
belongs_to :parrot_with_annotation, -> { annotate("that tells jokes") }, class_name: :Parrot, foreign_key: :parrot_id
|
|
has_and_belongs_to_many :parrots, foreign_key: :pirate_id
|
|
has_and_belongs_to_many :parrots_with_annotation, -> { annotate("that are very colorful") }, class_name: :Parrot, foreign_key: :pirate_id
|
|
has_one :ship, foreign_key: :pirate_id
|
|
has_one :ship_with_annotation, -> { annotate("that is a rocket") }, class_name: :Ship, foreign_key: :pirate_id
|
|
has_many :birds, foreign_key: :pirate_id
|
|
has_many :birds_with_annotation, -> { annotate("that are also parrots") }, class_name: :Bird, foreign_key: :pirate_id
|
|
has_many :treasures, as: :looter
|
|
has_many :treasure_estimates, through: :treasures, source: :price_estimates
|
|
has_many :treasure_estimates_with_annotation, -> { annotate("yarrr") }, through: :treasures, source: :price_estimates
|
|
end
|