Skip to content

Commit

Permalink
Use the ElementsFinder in Page#find_elements
Browse files Browse the repository at this point in the history
The element find logic is now abstracted into the elements finder.

If you have custom element finding logic, you can pass your own elements finder instance to the method as options[:finder]. This custom class needs to implement the interface of Alchemy::ElementsFinder.
  • Loading branch information
tvdeyen committed Feb 21, 2019
1 parent 8532524 commit 342d6b3
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 77 deletions.
32 changes: 32 additions & 0 deletions app/models/alchemy/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,38 @@ def new_name_for_copy(custom_name, source_name)
# Instance methods
#

# Returns elements from page.
#
# @option options [Array<String>|String] :only
# Returns only elements with given names
# @option options [Array<String>|String] :except
# Returns all elements except the ones with given names
# @option options [Integer] :count
# Limit the count of returned elements
# @option options [Integer] :offset
# Starts with an offset while returning elements
# @option options [Boolean] :include_hidden (false)
# Return hidden elements as well
# @option options [Boolean] :random (false)
# Return elements randomly shuffled
# @option options [Boolean] :reverse (false)
# Reverse the load order
# @option options [Class] :finder (Alchemy::ElementsFinder)
# A class that will return elements from page.
# Use this for your custom element loading logic.
#
# @return [ActiveRecord::Relation]
def find_elements(options = {}, show_non_public = false)
if show_non_public
Alchemy::Deprecation.warn "Passing true as second argument to page#find_elements to include" /
" invisible elements has been removed. Please implement your own ElementsFinder" /
" and pass it with options[:finder]."
end

finder = options[:finder] || Alchemy::ElementsFinder.new(options)
finder.elements(page: self)
end

# The page's view partial is dependent from its page layout
#
# == Define page layouts
Expand Down
37 changes: 0 additions & 37 deletions app/models/alchemy/page/page_elements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,43 +61,6 @@ def copy_elements(source, target)
end
end

# Finds elements of page.
#
# @param [Hash]
# options hash
# @param [Boolean] (false)
# Pass true, if you want to also have not published elements.
#
# @option options [Array] only
# Returns only elements with given names
# @option options [Array] except
# Returns all elements except the ones with given names
# @option options [Fixnum] count
# Limit the count of returned elements
# @option options [Fixnum] offset
# Starts with an offset while returning elements
# @option options [Boolean] random (false)
# Return elements randomly shuffled
#
# @return [ActiveRecord::Relation]
#
def find_elements(options = {}, show_non_public = false)
elements = self.elements
if options[:only].present?
elements = elements.named(options[:only])
elsif options[:except].present?
elements = elements.excluded(options[:except])
end
if options[:reverse_sort] || options[:reverse]
elements = elements.reverse_order
end
elements = elements.offset(options[:offset]).limit(options[:count])
if options[:random]
elements = elements.order("RAND()")
end
show_non_public ? elements : elements.published
end

# All available element definitions that can actually be placed on current page.
#
# It extracts all definitions that are unique or limited and already on page.
Expand Down
53 changes: 13 additions & 40 deletions spec/models/alchemy/page_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1103,52 +1103,25 @@ module Alchemy
end

describe '#find_elements' do
before do
create(:alchemy_element, public: false, page: public_page)
create(:alchemy_element, public: false, page: public_page)
end
subject { page.find_elements(options) }

context "with show_non_public argument TRUE" do
it "should return all elements from empty options" do
expect(public_page.find_elements({}, true).to_a).to eq(public_page.elements.to_a)
end

it "should only return the elements passed as options[:only]" do
expect(public_page.find_elements({only: ['article']}, true).to_a).to eq(public_page.elements.named('article').to_a)
end

it "should not return the elements passed as options[:except]" do
expect(public_page.find_elements({except: ['article']}, true).to_a).to eq(public_page.elements - public_page.elements.named('article').to_a)
end

it "should return elements offsetted" do
expect(public_page.find_elements({offset: 2}, true).to_a).to eq(public_page.elements.offset(2))
end
let(:page) { build(:alchemy_page) }
let(:options) { {} }
let(:finder) { instance_double(Alchemy::ElementsFinder) }

it "should return elements limitted in count" do
expect(public_page.find_elements({count: 1}, true).to_a).to eq(public_page.elements.limit(1))
end
it 'passes self and all options to elements finder' do
expect(Alchemy::ElementsFinder).to receive(:new).with(options) { finder }
expect(finder).to receive(:elements).with(page: page)
subject
end

context "with show_non_public argument FALSE" do
it "should return all elements from empty arguments" do
expect(public_page.find_elements.to_a).to eq(public_page.elements.published.to_a)
end

it "should only return the public elements passed as options[:only]" do
expect(public_page.find_elements(only: ['article']).to_a).to eq(public_page.elements.published.named('article').to_a)
end

it "should return all public elements except the ones passed as options[:except]" do
expect(public_page.find_elements(except: ['article']).to_a).to eq(public_page.elements.published.to_a - public_page.elements.published.named('article').to_a)
end

it "should return elements offsetted" do
expect(public_page.find_elements({offset: 2}).to_a).to eq(public_page.elements.published.offset(2))
context 'with a custom finder given in options' do
let(:options) do
{ finder: CustomNewsElementsFinder.new }
end

it "should return elements limitted in count" do
expect(public_page.find_elements({count: 1}).to_a).to eq(public_page.elements.published.limit(1))
it 'uses that to load elements to render' do
expect(subject.map(&:name)).to eq(['news'])
end
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
require_relative "support/hint_examples.rb"
require_relative "support/transformation_examples.rb"
require_relative "support/capybara_select2.rb"
require_relative 'support/custom_news_elements_finder'

ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
Expand Down
7 changes: 7 additions & 0 deletions spec/support/custom_news_elements_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class CustomNewsElementsFinder
def elements(*)
[Alchemy::Element.new(name: 'news', id: 1001)]
end
end

0 comments on commit 342d6b3

Please sign in to comment.