Skip to content

Commit cf1c33c

Browse files
committed
feat: persist and fetch mirrored sections
1 parent 321850a commit cf1c33c

File tree

9 files changed

+212
-31
lines changed

9 files changed

+212
-31
lines changed

app/models/concerns/maglev/sections_concern.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ def prepare_sections_translations(theme)
2121
end
2222
end
2323

24+
def find_section_by_type(type)
25+
sections&.find { |section| section['type'] == type }
26+
end
27+
28+
def find_section(id)
29+
sections.find { |section| section['id'] == id }
30+
end
31+
32+
def update_section_content(id, attributes)
33+
find_section(id).tap do |section|
34+
replace_section_content(section, attributes)
35+
end
36+
end
37+
38+
def replace_section_content(section, attributes)
39+
section['settings'] = attributes['settings']
40+
section['blocks'] = attributes['blocks']
41+
section
42+
end
43+
2444
private
2545

2646
def prepare_section(theme, section)

app/models/maglev/sections_content_store.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ class SectionsContentStore < ApplicationRecord
1414

1515
validates :handle, presence: true, uniqueness: true
1616

17-
def find_section(type)
18-
sections&.find { |section| section['type'] == type }
19-
end
20-
2117
def translate_in(locale, source_locale)
2218
translate_attr_in(:sections, locale, source_locale)
2319
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# It requires the parent class to implement 2 methods:
2+
# - scoped_pages
3+
# - find_store
4+
module Maglev::MirroredSectionsConcern
5+
6+
private
7+
8+
def persist_mirrored_sections(sections_content)
9+
sections_content.map { |group| group['sections'] }.flatten.find do |section|
10+
next unless section.dig('mirror_of', 'enabled')
11+
persist_mirror_section(section.except('mirror_of'), section['mirror_of'])
12+
end
13+
end
14+
15+
def persist_mirror_section(section, mirror_of)
16+
store = find_store_from_mirrored_section(mirror_of)
17+
store.update_section_content(mirror_of['section_id'], section)
18+
store.save
19+
end
20+
21+
def find_store_from_mirrored_section(mirror_of)
22+
other_page = scoped_pages.find_by(id: mirror_of['page_id'])
23+
return unless other_page
24+
layout = fetch_layout(other_page.layout_id)
25+
group = layout.groups.find { |group| group.id == mirror_of['layout_group_id'] }
26+
return unless group
27+
find_store(group.guess_store_handle(other_page))
28+
end
29+
30+
def find_section_from_mirrored_section(mirror_of)
31+
store = find_store_from_mirrored_section(mirror_of)
32+
return unless store
33+
store.find_section(mirror_of['section_id'])
34+
end
35+
end

app/services/maglev/fetch_sections_content.rb

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Maglev
77
class FetchSectionsContent
88
include Injectable
99
include Maglev::FetchSectionsContent::TransformSectionConcern
10+
include Maglev::MirroredSectionsConcern
1011

1112
dependency :fetch_site
1213
dependency :fetch_theme
@@ -18,13 +19,8 @@ class FetchSectionsContent
1819
argument :locale, default: nil
1920

2021
def call
21-
store = find_store
22-
[
23-
store.sections.map do |section|
24-
transform_section(section.dup)
25-
end.compact,
26-
store.lock_version
27-
]
22+
store = find_or_build_store
23+
[transform_sections(store), store.lock_version]
2824
end
2925

3026
private
@@ -33,14 +29,46 @@ def theme
3329
@theme ||= fetch_theme.call
3430
end
3531

36-
def find_store
37-
scoped_store.find_or_initialize_by(handle: handle) do |store|
32+
def find_or_build_store
33+
scoped_stores.find_or_initialize_by(handle: handle) do |store|
3834
store.sections = []
3935
end
4036
end
4137

42-
def scoped_store
38+
def transform_sections(store)
39+
# look for mirrored sections and get the fresh content
40+
set_content_from_mirror_sections(store)
41+
42+
store.sections.map do |section|
43+
transform_section(section.dup)
44+
end.compact
45+
end
46+
47+
def set_content_from_mirror_sections(store)
48+
store.sections.each do |section|
49+
next unless section.dig('mirror_of', 'enabled')
50+
mirror_section = find_section_from_mirrored_section(section['mirror_of'])
51+
next unless mirror_section
52+
store.replace_section_content(section, mirror_section)
53+
end
54+
end
55+
56+
def fetch_layout(layout_id = nil)
57+
theme.find_layout(layout_id || page.layout_id).tap do |layout|
58+
raise Maglev::Errors::MissingLayout, "#{layout_id || page.layout_id} doesn't match a layout of the theme" if layout.nil?
59+
end
60+
end
61+
62+
def find_store(handle)
63+
scoped_stores.find_by(handle: handle)
64+
end
65+
66+
def scoped_stores
4367
Maglev::SectionsContentStore
4468
end
69+
70+
def scoped_pages
71+
Maglev::Page
72+
end
4573
end
4674
end

app/services/maglev/persist_sections_content.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module Maglev
44
class PersistSectionsContent
55
include Injectable
6+
include Maglev::MirroredSectionsConcern
67

78
dependency :fetch_theme
89

@@ -20,7 +21,9 @@ def call
2021
def unsafe_call
2122
layout.groups.map do |group|
2223
[group.id, persist_group_content(group)]
23-
end.to_h
24+
end.to_h.tap do
25+
persist_mirrored_sections(sections_content)
26+
end
2427
end
2528

2629
private
@@ -30,9 +33,7 @@ def theme
3033
end
3134

3235
def layout
33-
theme.find_layout(page.layout_id).tap do |layout|
34-
raise Maglev::Errors::MissingLayout, "#{layout_id} doesn't match a layout of the theme" if layout.nil?
35-
end
36+
@layout ||= fetch_layout(page.layout_id)
3637
end
3738

3839
def persist_group_content(group)
@@ -49,11 +50,21 @@ def extract_store_attributes(group)
4950
end
5051

5152
def find_store(handle)
52-
scoped_store.find_or_create_by(handle: handle)
53+
scoped_stores.find_or_create_by(handle: handle)
5354
end
5455

55-
def scoped_store
56+
def fetch_layout(layout_id = nil)
57+
theme.find_layout(layout_id || page.layout_id).tap do |layout|
58+
raise Maglev::Errors::MissingLayout, "#{layout_id || page.layout_id} doesn't match a layout of the theme" if layout.nil?
59+
end
60+
end
61+
62+
def scoped_stores
5663
Maglev::SectionsContentStore
5764
end
65+
66+
def scoped_pages
67+
Maglev::Page
68+
end
5869
end
5970
end

spec/factories/maglev/pages.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
sections do
1111
[
1212
{
13+
id: 'def',
1314
type: 'jumbotron',
1415
settings: [
1516
{ id: :title, value: 'Hello world' },
@@ -18,6 +19,7 @@
1819
blocks: []
1920
},
2021
{
22+
id: 'ghi',
2123
type: 'showcase',
2224
settings: [
2325
{ id: :title, value: 'Our projects' }

spec/factories/maglev/sections_content_stores.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
# work with the sidebar trait
134134
trait :page_link_in_link do
135135
after :build do |record|
136-
record.find_section('navbar')['blocks'][0]['settings'][1]['value'] = {
136+
record.find_section_by_type('navbar')['blocks'][0]['settings'][1]['value'] = {
137137
link_type: 'page',
138138
link_id: '42',
139139
open_new_window: true,

spec/services/maglev/fetch_sections_content_spec.rb

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626

2727
# rubocop:disable Style/StringHashKeys
2828
context 'the store has some sections' do
29-
let(:page) { create(:page) }
29+
let!(:page) { create(:page) }
3030
let(:handle) { "main-#{page.id}" }
3131

32-
let!(:store) { create(:sections_content_store, page: page) }
33-
3432
it 'returns the sections' do
3533
expect(subject).to eq([[
3634
{
@@ -61,7 +59,7 @@
6159
end
6260

6361
context 'the section have unused settings' do
64-
let!(:store) { create(:sections_content_store, :with_unused_settings, page: page) }
62+
let!(:page) { create(:page, :with_unused_settings) }
6563
it 'skips the unused settings' do
6664
expect(subject).to eq([[
6765
{
@@ -85,6 +83,7 @@
8583
end
8684

8785
context 'the sections include a link in text type setting' do
86+
let(:page) { create(:page, sections: nil) }
8887
let!(:store) { create(:sections_content_store, :page_link_in_text, page: page) }
8988

9089
it 'sets the href properties' do
@@ -95,7 +94,8 @@
9594
end
9695

9796
context 'the sections include a link in link type setting' do
98-
let!(:store) { create(:sections_content_store, :sidebar, :page_link_in_link) }
97+
let(:page) { create(:page, sections: nil) }
98+
let!(:store) { create(:sections_content_store, :sidebar, :page_link_in_link, page: page) }
9999
let(:handle) { 'sidebar' }
100100

101101
it 'sets the href properties' do
@@ -106,7 +106,7 @@
106106
end
107107

108108
context 'the sections include collection items' do
109-
let!(:store) { create(:sections_content_store, :featured_product, page: page) }
109+
let!(:page) { create(:page, :featured_product) }
110110

111111
it 'fetches the product from the DB' do
112112
expect(fetch_collection_items).to receive(:call).with(collection_id: 'products', id: 42).and_return(
@@ -117,7 +117,7 @@
117117
end
118118

119119
context 'the setting content points to the any item' do
120-
let!(:store) { create(:sections_content_store, :any_featured_product, page: page) }
120+
let!(:page) { create(:page, :any_featured_product) }
121121

122122
it 'fetches the first product' do
123123
expect(fetch_collection_items).to receive(:call).with(collection_id: 'products', id: 'any').and_return(
@@ -128,6 +128,56 @@
128128
end
129129
end
130130
end
131+
132+
context 'there is a mirrored section' do
133+
let(:another_sections_content) do
134+
JSON.parse([
135+
{
136+
id: 'fake-section-id',
137+
type: 'jumbotron',
138+
settings: [{ id: :title, value: 'Hello world 🤓' }, { id: :body, value: '<p>Lorem ipsum!</p>' }],
139+
blocks: []
140+
},
141+
].to_json)
142+
end
143+
let(:another_page) { create(:page, title: 'another page', path: 'another-page', sections: another_sections_content) }
144+
145+
let(:sections_content) do
146+
JSON.parse([
147+
{
148+
type: 'jumbotron',
149+
settings: [{ id: :title, value: '' }, { id: :body, value: '' }],
150+
blocks: [],
151+
mirror_of: {
152+
enabled: true,
153+
page_id: another_page.id,
154+
layout_group_id: 'main',
155+
section_id: 'fake-section-id'
156+
}
157+
},
158+
].to_json)
159+
end
160+
let!(:page) { create(:page, sections: sections_content) }
161+
162+
it 'returns the sections' do
163+
expect(subject).to eq([[
164+
{
165+
'type' => 'jumbotron',
166+
'settings' => [
167+
{ 'id' => 'title', 'value' => 'Hello world 🤓' },
168+
{ 'id' => 'body', 'value' => '<p>Lorem ipsum!</p>' }
169+
],
170+
'blocks' => [],
171+
'mirror_of' => {
172+
'enabled' => true,
173+
'page_id' => another_page.id,
174+
'layout_group_id' => 'main',
175+
'section_id' => 'fake-section-id'
176+
}
177+
}
178+
], 0])
179+
end
180+
end
131181
end
132182

133183
# rubocop:enable Style/StringHashKeys

0 commit comments

Comments
 (0)