app/services/page_updater.rb
# frozen_string_literal: true
##
# The <tt>PageUpdater</tt> class serves to update a Page model along with
# its associate Plugins and Share Variants. It allows for comprehensive saving
# and updating with one form from the user perspective.
#
# The +update+ method updates pages, plugins, and share variants that are passed
# to it. It returns true if all saves passed without errors and false if any of
# the saves returned errors. It will not rollback save on one entity if save
# failed on another. It expects params of the following format. Any of the hashes
# can be left out, only those present will be updated.
# {
# page: {
# content: "my content",
# id: "608"
# },
# plugins_petition_150: {
# cta: "Sign the Petition",
# description: "Tell those meanieheads to do something!",
# id: "150",
# name: "Petition",
# ref: "",
# target: ""
# },
# plugins_thermometer_81: {
# id: "81",
# name: "Thermometer",
# offset: "0",
# ref: ""
# },
# share_facebook_12: {
# description: "and this is the message",
# id: "12",
# image_id: "16",
# name: "facebook",
# title: "This is the title"
# },
# share_twitter_1: {
# description: "I want you to {LINK} for me",
# id: "1",
# name: "twitter"
# },
# share_twitter_2: {
# description: "I want you to {LINK} for me",
# id: "2",
# name: "twitter"
# }
# }
#
# Some fields change the plugins available to the page, so these are listed
# as REFRESH_TRIGGERS. If one of these fields is updated, the +refresh?+ method
# will return true, otherwise it will return false.
#
# Finally, the +errors+ method will return a hash where each key is the name of the
# updated entity (eg :share_twitter_2 or :page) and the value is the AR errors hash
# for that object. The key will only be present if there are errors for that entity.
#
class PageUpdater
attr_reader :errors, :page
def initialize(page, page_url = nil)
@page = page
@page_url = page_url
end
def update(params)
@params = params
@errors = {}
@refresh = false
update_plugins
update_shares
update_page
@errors.empty?
end
def refresh?
@refresh || false
end
def ak_resource_empty?
(page.ak_petition_resource_uri.blank? ||
page.ak_donation_resource_uri.blank?) &&
page.created_at < (DateTime.now - 2.minutes)
end
private
def important_changes_made
page_tags_before = @page.pages_tags.map(&:tag_id)
yield if block_given?
page_tags_after = @page.pages_tags.map(&:tag_id)
@page.changed_attributes.keys.any? do |attr|
%w[language_id title campaign_id].include?(attr)
end || page_tags_before != page_tags_after
end
def update_page
return unless @params[:page]
plugins_before = @page.plugins
ak_sensitive_changes = important_changes_made do
@page.assign_attributes(@params[:page])
end
QueueManager.push(@page, job_type: :update_pages) if @page.save && ak_sensitive_changes
@refresh = (@page.plugins != plugins_before)
@errors[:page] = @page.errors.to_h unless @page.errors.empty?
end
def update_plugin(plugin_params)
plugin = plugins.select { |p| p.id == plugin_params[:id].to_i && p.name == plugin_params[:name] }.first
raise ActiveRecord::RecordNotFound if plugin.blank?
plugin.update_attributes(plugin_params.except(:name))
plugin.errors
end
def update_share(share_params, _name)
variant = if share_params[:id].present?
ShareVariantBuilder.update(
params: share_params.except(:name),
variant_type: share_params[:name],
page: @page,
id: share_params[:id]
)
else
ShareVariantBuilder.create(
params: share_params.except(:name),
variant_type: share_params[:name],
page: @page,
url: @page_url
)
end
variant.errors
end
def update_plugins
params_for('plugins').each_pair do |name, plugin_params|
errors = update_plugin(plugin_params)
@errors[name] = errors.to_h unless errors.blank?
end
end
def update_shares
params_for('share').each_pair do |name, share_params|
errors = update_share(share_params, name)
@errors[name] = errors.to_h unless errors.blank?
end
end
def params_for(query)
@params.select do |key, _value|
key.to_s =~ /.*#{query}.*/i
end
end
def plugins
@page.plugins
end
end