Skip to content

Commit 4f39a73

Browse files
committed
Include future-scheduled elements when publishing pages
Previously, only currently visible elements were copied to the public version when publishing a page. This meant elements scheduled for future publication required a republish once their public_on date arrived. Now the publisher copies all publishable elements, which includes both currently visible elements and those scheduled for the future. This allows elements with a future public_on date to automatically become visible once their scheduled time passes without requiring a republish.
1 parent adac074 commit 4f39a73

8 files changed

Lines changed: 90 additions & 11 deletions

File tree

app/models/alchemy/element.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def nestable_elements
313313

314314
def set_default_public_on
315315
return if @public_on_explicitely_set
316-
self.public_on = Time.current
316+
self.public_on ||= Time.current
317317
end
318318

319319
def generate_nested_elements

app/models/alchemy/elements_repository.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def visible
2222
self.class.new select(&:public)
2323
end
2424

25+
# All publishable elements
26+
# @return [Alchemy::ElementRepository]
27+
def publishable
28+
self.class.new select(&:publishable?)
29+
end
30+
2531
# All not fixed elements
2632
# @return [Alchemy::ElementRepository]
2733
def hidden

app/models/alchemy/page/publisher.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def initialize(page)
1010
@page = page
1111
end
1212

13-
# Copies all currently visible elements to the public version of page
13+
# Copies all currently publishable elements to the public version of page
1414
#
1515
# Creates a new published version if none exists yet and updates
1616
# the `published_at` timestamp of the page.
@@ -29,7 +29,7 @@ def publish!(public_on:)
2929
repository = page.draft_version.element_repository
3030
ActiveRecord::Base.no_touching do
3131
Element.acts_as_list_no_update do
32-
repository.visible.not_nested.each.with_index(1) do |element, position|
32+
repository.publishable.not_nested.each.with_index(1) do |element, position|
3333
Alchemy::DuplicateElement.new(element, repository: repository, publishable_only: true).call(
3434
page_version_id: version.id,
3535
position: position

app/models/concerns/alchemy/publishable.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ def public?(time = Time.current)
2828
end
2929
alias_method :public, :public?
3030

31+
# Determines if this record is publishable
32+
#
33+
# A record is publishable if a +public_on+ timestamp is set and not expired yet.
34+
#
35+
# @returns Boolean
36+
def publishable?
37+
!public_on.nil? && still_public_for?
38+
end
39+
3140
# Determines if this record is already public for given time
3241
# @param time [DateTime] (Time.current)
3342
# @returns Boolean

app/services/alchemy/duplicate_element.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def call(differences = {})
3636
new_element.save!
3737

3838
nested_elements = repository.children_of(source_element)
39-
nested_elements = nested_elements.visible if publishable_only
39+
nested_elements = nested_elements.publishable if publishable_only
4040
Element.acts_as_list_no_update do
4141
nested_elements.each.with_index(1) do |nested_element, position|
4242
self.class.new(nested_element, repository: repository, publishable_only: publishable_only).call(

spec/dummy/db/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.1].define(version: 2026_01_15_171818) do
13+
ActiveRecord::Schema[8.1].define(version: 2026_01_20_163927) do
1414
create_table "active_storage_attachments", force: :cascade do |t|
1515
t.bigint "blob_id", null: false
1616
t.datetime "created_at", null: false

spec/models/alchemy/page/publisher_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,52 @@
5454
end
5555
end
5656

57+
context "with elements scheduled for future publication" do
58+
let(:page) { create(:alchemy_page) }
59+
60+
let!(:current_element) do
61+
create(:alchemy_element,
62+
page_version: page.draft_version,
63+
public_on: 1.day.ago)
64+
end
65+
66+
let!(:future_element) do
67+
create(:alchemy_element,
68+
page_version: page.draft_version,
69+
public_on: 1.day.from_now)
70+
end
71+
72+
let!(:draft_element) do
73+
create(:alchemy_element, page_version: page.draft_version).tap do |element|
74+
element.update_columns(public_on: nil)
75+
end
76+
end
77+
78+
let!(:expired_element) do
79+
create(:alchemy_element,
80+
page_version: page.draft_version,
81+
public_on: 2.days.ago,
82+
public_until: 1.day.ago)
83+
end
84+
85+
it "copies currently public and future scheduled elements" do
86+
publish
87+
expect(page.reload.public_version.elements.count).to eq(2)
88+
end
89+
90+
it "does not copy draft elements without public_on" do
91+
publish
92+
public_ons = page.reload.public_version.elements.pluck(:public_on)
93+
expect(public_ons).to all(be_present)
94+
end
95+
96+
it "does not copy expired elements" do
97+
publish
98+
public_untils = page.reload.public_version.elements.pluck(:public_until)
99+
expect(public_untils).to all(be_nil)
100+
end
101+
end
102+
57103
context "with draft version metadata" do
58104
before do
59105
page.draft_version.update!(

spec/services/alchemy/duplicate_element_spec.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,38 @@
7474
context "with publishable_only option" do
7575
let(:element) { create(:alchemy_element) }
7676

77-
let!(:visible_nested) do
78-
create(:alchemy_element, parent_element: element, page_version: element.page_version, public: true)
77+
let!(:current_nested) do
78+
create(:alchemy_element, parent_element: element, page_version: element.page_version, public_on: 1.day.ago)
7979
end
8080

81-
let!(:hidden_nested) do
82-
create(:alchemy_element, parent_element: element, page_version: element.page_version, public: false)
81+
let!(:future_nested) do
82+
create(:alchemy_element, parent_element: element, page_version: element.page_version, public_on: 1.day.from_now)
83+
end
84+
85+
let!(:draft_nested) do
86+
create(:alchemy_element, parent_element: element, page_version: element.page_version, public_on: nil)
87+
end
88+
89+
let!(:expired_nested) do
90+
create(:alchemy_element, parent_element: element, page_version: element.page_version, public_on: 2.days.ago, public_until: 1.day.ago)
8391
end
8492

8593
subject do
8694
described_class.new(element, publishable_only: true).call(differences)
8795
end
8896

89-
it "only copies visible nested elements" do
90-
expect(subject.all_nested_elements).to all(be_public)
97+
it "only copies publishable nested elements" do
98+
expect(subject.all_nested_elements.count).to eq(2)
99+
end
100+
101+
it "does not copy draft nested elements" do
102+
public_ons = subject.all_nested_elements.map(&:public_on)
103+
expect(public_ons).to all(be_present)
104+
end
105+
106+
it "does not copy expired nested elements" do
107+
public_untils = subject.all_nested_elements.map(&:public_until)
108+
expect(public_untils).to all(be_nil)
91109
end
92110
end
93111
end

0 commit comments

Comments
 (0)