Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database create many #160

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions lib/noticed/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,15 @@ def deliver(recipients)
validate!

run_callbacks :deliver do
Array.wrap(recipients).uniq.each do |recipient|
run_delivery(recipient, enqueue: false)
end
run_delivery(Array.wrap(recipients).uniq, enqueue: false)
end
end

def deliver_later(recipients)
validate!

run_callbacks :deliver do
Array.wrap(recipients).uniq.each do |recipient|
run_delivery(recipient, enqueue: true)
end
run_delivery(Array.wrap(recipients).uniq, enqueue: true)
end
end

Expand All @@ -78,22 +74,42 @@ def params
private

# Runs all delivery methods for a notification
def run_delivery(recipient, enqueue: true)
def run_delivery(recipients, enqueue: true)
delivery_methods = self.class.delivery_methods.dup

# Run database delivery inline first if it exists so other methods have access to the record
if (index = delivery_methods.find_index { |m| m[:name] == :database })
delivery_method = delivery_methods.delete_at(index)
record = run_delivery_method(delivery_method, recipient: recipient, enqueue: false, record: nil)
records = run_database_delivery_method(delivery_method, recipients: recipients)
end

delivery_methods.each do |delivery_method|
run_delivery_method(delivery_method, recipient: recipient, enqueue: enqueue, record: record)
# build hash with recipients as keys
records = Array.wrap(records).to_h { |record| [record.recipient, record] }

recipients.each do |recipient|
delivery_methods.each do |delivery_method|
run_delivery_method(delivery_method, recipient: recipient, enqueue: enqueue, record: records[recipient])
end
end
end

# Run database delivery method for all recipients
def run_database_delivery_method(delivery_method, recipients:)
args = {
notification_class: self.class.name,
options: delivery_method[:options],
params: params,
recipients: recipients
}

run_callbacks :database do
method = delivery_method_for(:database, delivery_method[:options])
method.perform_now(args)
end
end

# Actually runs an individual delivery
def run_delivery_method(delivery_method, recipient:, enqueue:, record:)
def run_delivery_method(delivery_method, recipient:, record:, enqueue:)
args = {
notification_class: self.class.name,
options: delivery_method[:options],
Expand Down
6 changes: 3 additions & 3 deletions lib/noticed/delivery_methods/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ def perform(args)
@notification = args[:notification_class].constantize.new(args[:params])
@options = args[:options]
@params = args[:params]
@recipient = args[:recipient]
@record = args[:record]
@recipient = args[:recipient]

# Make notification aware of database record and recipient during delivery
@notification.record = args[:record]
@notification.recipient = args[:recipient]
@notification.record = record
@notification.recipient = recipient

return if (condition = @options[:if]) && [email protected](condition)
return if (condition = @options[:unless]) && @notification.send(condition)
Expand Down
55 changes: 54 additions & 1 deletion lib/noticed/delivery_methods/database.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
module Noticed
module DeliveryMethods
class Database < Base
attr_reader :recipients
# Must return the database record
def deliver
recipient.send(association_name).create!(attributes)
# build array of notification attributes
notifications = build_notifications
# save all the notification
save_notifications(notifications)
end

def self.validate!(options)
Expand All @@ -13,6 +17,12 @@ def self.validate!(options)
raise ArgumentError, "database delivery cannot be delayed" if options.key?(:delay)
end

def perform(args)
@recipients = args[:recipients]

super
end

private

def association_name
Expand All @@ -29,6 +39,49 @@ def attributes
}
end
end

# retun the class notifications or the association
def klass
association_name.to_s.upcase_first.singularize.constantize
end

# with the recipients build an array of notifications
def build_notifications
Array.wrap(recipients).uniq.map do |recipient|
build_notification(recipient)
end
end

# new notification and then return the attributes without id and with timestamps
def build_notification(recipient)
recipient.send(association_name)
.new(attributes)
.attributes
.merge({created_at: DateTime.current, updated_at: DateTime.current})
.except("id")
end

# if the notification can bulk, use insert_all if not creates records
def save_notifications(notifications)
if bulk?
ids = klass.insert_all!(notifications).rows
klass.preload(:recipient).find(ids)
else
klass.create!(notifications)
end
Comment on lines +66 to +71
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also... maybe instead of asking for the rails version and the current adapter, we can ask if klass responds to insert_all. Don't you think?

Suggested change
if bulk?
ids = klass.insert_all!(notifications).rows
klass.preload(:recipient).find(ids)
else
klass.create!(notifications)
end
if klass.respond_to?(:insert_all!)
ids = klass.insert_all!(notifications).rows
klass.preload(:recipient).find(ids)
else
klass.create!(notifications)
end

end

def current_adapter
if ActiveRecord::Base.respond_to?(:connection_db_config)
ActiveRecord::Base.connection_db_config.adapter
else
ActiveRecord::Base.connection_config[:adapter]
end
end

def bulk?
Rails::VERSION::MAJOR > 6 && ["postgresql", "postgis"].include?(current_adapter)
end
end
end
end
4 changes: 2 additions & 2 deletions test/delivery_methods/database_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ class WithDelayedDatabaseDelivery < Noticed::Base
test "deliver returns the created record" do
args = {
notification_class: "Noticed::Base",
recipient: user,
recipients: user,
options: {}
}
record = Noticed::DeliveryMethods::Database.new.perform(args)
record = Noticed::DeliveryMethods::Database.new.perform(args).first

assert_kind_of ActiveRecord::Base, record
end
Expand Down