1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/test/models/pirate.rb
Matt Yoho f41825809c Add Relation#annotate for SQL commenting
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.
2019-03-21 20:30:56 -07:00

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