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

Add Elements repository #2039

Merged
merged 6 commits into from
Mar 5, 2021
Merged
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
3 changes: 0 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,6 @@ Layout/MultilineMethodCallBraceLayout:
Layout/MultilineMethodCallIndentation:
Enabled: false

Layout/MultilineOperationIndentation:
EnforcedStyle: indented

Layout/SpaceBeforeBlockBraces:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylespacebeforeblockbraces
Expand Down
122 changes: 122 additions & 0 deletions app/models/alchemy/elements_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

module Alchemy
# Mimics ActiveRecord query interface
# but does this on the preloaded elements
class ElementsRepository
include Enumerable

# An empty set of elements
def self.none
new([])
end

# @param [ActiveRecord::Relation]
def initialize(elements)
@elements = elements.to_a
end

# All visible elements
# @return [Alchemy::ElementRepository]
def visible
self.class.new select(&:public)
end

# All not fixed elements
# @return [Alchemy::ElementRepository]
def hidden
self.class.new reject(&:public)
end

# All elements with given name(s)
# @param [Array<String|Symbol>|String|Symbol]
# @return [Alchemy::ElementRepository]
def named(*names)
names.flatten!
self.class.new(select { |e| e.name.in?(names.map!(&:to_s)) })
end

# Filter elements by given attribute and value
# @param [Array|Hash]
# @return [Alchemy::ElementRepository]
def where(attrs)
self.class.new(
select do |element|
attrs.all? do |attr, value|
element.public_send(attr) == value
end
end
)
end

# All elements excluding those wth given name(s)
# @param [Array<String|Symbol>|String|Symbol]
# @return [Alchemy::ElementRepository]
def excluded(*names)
names.flatten!
self.class.new(reject { |e| e.name.in?(names.map!(&:to_s)) })
end

# All fixed elements
# @return [Alchemy::ElementRepository]
def fixed
self.class.new select(&:fixed)
end

# All not fixed elements
# @return [Alchemy::ElementRepository]
def unfixed
self.class.new reject(&:fixed)
end

# All folded elements
# @return [Alchemy::ElementRepository]
def folded
self.class.new select(&:folded)
end

# All expanded elements
# @return [Alchemy::ElementRepository]
def expanded
self.class.new reject(&:folded)
end

# All not nested top level elements
# @return [Alchemy::ElementRepository]
def not_nested
self.class.new(select { |e| e.parent_element_id.nil? })
end

# Elements in reversed order
# @return [Alchemy::ElementRepository]
def reverse
tvdeyen marked this conversation as resolved.
Show resolved Hide resolved
self.class.new elements.reverse
end

# Elements in random order
# @return [Alchemy::ElementRepository]
def random
self.class.new Array(elements).shuffle
end

# Elements off setted by
# @return [Alchemy::ElementRepository]
def offset(offset)
tvdeyen marked this conversation as resolved.
Show resolved Hide resolved
self.class.new elements[offset.to_i..-1]
end

# Elements limitted by
# @return [Alchemy::ElementRepository]
def limit(limit)
self.class.new elements[0..(limit.to_i - 1)]
end

def each(&blk)
elements.each(&blk)
end

private

attr_reader :elements
end
end
20 changes: 6 additions & 14 deletions lib/alchemy/elements_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ def initialize(options = {})

# @param page [Alchemy::PageVersion]
# The page version the elements are loaded from.
# @return [ActiveRecord::Relation]
# @return [Alchemy::ElementsRepository]
def elements(page_version:)
elements = find_elements(page_version)

if options[:reverse]
elements = elements.reverse_order
elements = elements.reverse
end

if options[:random]
elements = elements.reorder(Arel.sql(random_function))
elements = elements.random
end

elements.offset(options[:offset]).limit(options[:count])
Expand All @@ -52,9 +52,10 @@ def elements(page_version:)
attr_reader :options

def find_elements(page_version)
return Alchemy::Element.none unless page_version
return Alchemy::ElementsRepository.none unless page_version

elements = page_version.elements.not_nested.available
elements = Alchemy::ElementsRepository.new(page_version.elements.available)
elements = elements.not_nested
elements = options[:fixed] ? elements.fixed : elements.unfixed

if options[:only]
Expand All @@ -67,14 +68,5 @@ def find_elements(page_version)

elements
end

def random_function
case ActiveRecord::Base.connection_config[:adapter]
when "postgresql", "sqlite3"
"RANDOM()"
else
"RAND()"
end
end
end
end
15 changes: 3 additions & 12 deletions spec/libraries/elements_finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
end

context "with page_version object given" do
subject { finder.elements(page_version: page_version) }
subject { finder.elements(page_version: page_version).to_a }

it "returns all public elements from page_version" do
is_expected.to eq([visible_element])
Expand Down Expand Up @@ -132,18 +132,9 @@
{ random: true }
end

let(:random_function) do
case ActiveRecord::Base.connection_config[:adapter]
when "postgresql", "sqlite3"
"RANDOM()"
else
"RAND()"
end
end

it "returns elements in random order" do
expect_any_instance_of(ActiveRecord::Relation).to \
receive(:reorder).with(random_function).and_call_original
expect_any_instance_of(Alchemy::ElementsRepository).to \
receive(:random).and_call_original
subject
end
end
Expand Down
Loading