filesystem adapter: query now supports multiple directional ordering
did
committed Feb 21, 2015
commit c28fb90364660fbf2b31ade4c620364bde22dd31
Showing 12
changed files with
219 additions
and 72 deletions
locomotive/steam/adapters/filesystem.rb b/lib/locomotive/steam/adapters/filesystem.rb
+31
-19
| @@ | @@ -1,4 +1,5 @@ |
| require_relative 'filesystem/dataset' | |
| + | require_relative 'filesystem/order' |
| require_relative 'filesystem/condition' | |
| require_relative 'filesystem/query' | |
| @@ | @@ -10,7 +11,7 @@ require_relative 'filesystem/yaml_loaders/page' |
| module Locomotive::Steam | |
| - | class FilesystemAdapter |
| + | class FilesystemAdapter < Struct.new(:site_path) |
| include Morphine | |
| @@ | @@ -18,53 +19,64 @@ module Locomotive::Steam |
| Locomotive::Steam::Adapters::Filesystem::SimpleCacheStore.new | |
| end | |
| + | register :yaml_loaders do |
| + | build_yaml_loaders(cache) |
| + | end |
| + | |
| def initialize(site_path) | |
| - | @site_path = site_path |
| - | @datasets = {} |
| + | super |
| + | @datasets = {} |
| end | |
| - | def all(mapper) |
| - | memoized_dataset(mapper) |
| + | def all(mapper, scope) |
| + | memoized_dataset(mapper, scope) |
| end | |
| def query(mapper, scope, &block) | |
| - | _query(mapper, scope.locale, &block).tap do |default| |
| + | _query(mapper, scope, &block).tap do |default| |
| if scope.site | |
| - | default + _query(mapper) { where(site_id: scope.site.id) } |
| + | default.where(site_id: scope.site.id) |
| end | |
| end | |
| end | |
| private | |
| - | def _query(mapper, locale = nil, &block) |
| - | Locomotive::Steam::Adapters::Filesystem::Query.new(all(mapper), locale, &block) |
| + | def _query(mapper, scope, &block) |
| + | Locomotive::Steam::Adapters::Filesystem::Query.new(all(mapper, scope), scope.locale, &block) |
| end | |
| - | def memoized_dataset(mapper) |
| + | def memoized_dataset(mapper, scope) |
| return @datasets[mapper.name] if @datasets[mapper.name] | |
| - | dataset(mapper) |
| + | dataset(mapper, scope) |
| end | |
| - | def dataset(mapper) |
| + | def dataset(mapper, scope) |
| Locomotive::Steam::Adapters::Filesystem::Dataset.new(mapper.name).tap do |dataset| | |
| @datasets[mapper.name] = dataset | |
| - | collection(mapper).each do |attributes| |
| + | collection(mapper, scope).each do |attributes| |
| entity = mapper.to_entity(attributes) | |
| + | |
| + | # assign the site_id to the entity + sanitize attributes |
| + | # specific to the Filesystem adapter |
| + | entity[:site_id] = scope.site.id if scope.site |
| + | |
| dataset.insert(entity) | |
| end | |
| end | |
| end | |
| - | def collection(mapper) |
| - | yaml_loader(mapper.name).load |
| + | def collection(mapper, scope) |
| + | yaml_loaders[mapper.name].load(scope) |
| end | |
| - | def yaml_loader(name) |
| - | _name = name.to_s.singularize.camelize |
| - | klass = "Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::#{_name}".constantize |
| - | klass.new(@site_path, cache) |
| + | def build_yaml_loaders(cache) |
| + | %i(site page).inject({}) do |memo, name| |
| + | _name = name.to_s.singularize.camelize |
| + | klass = "Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::#{_name}".constantize |
| + | memo[name] = klass.new(site_path, cache) |
| + | end |
| end | |
| end | |
locomotive/steam/adapters/filesystem/order.rb b/lib/locomotive/steam/adapters/filesystem/order.rb
+59
-0
| @@ | @@ -0,0 +1,59 @@ |
| + | module Locomotive::Steam |
| + | module Adapters |
| + | module Filesystem |
| + | |
| + | class Order |
| + | |
| + | attr_reader :list |
| + | |
| + | def initialize(*args) |
| + | strings = args.compact |
| + | |
| + | @list = (case args.size |
| + | when 0 then [] |
| + | when 1 then args.first.split(',').collect { |s| build(s.strip) } |
| + | else |
| + | args.collect { |s| build(s) } |
| + | end) |
| + | end |
| + | |
| + | def empty? |
| + | @list.empty? |
| + | end |
| + | |
| + | def apply_to(entry, locale) |
| + | @list.collect do |(name, direction)| |
| + | value = entry.send(name) |
| + | asc?(direction) ? Asc.new(value) : Desc.new(value) |
| + | end |
| + | end |
| + | |
| + | def asc?(direction) |
| + | direction.nil? || direction == :asc |
| + | end |
| + | |
| + | private |
| + | |
| + | def build(string) |
| + | pattern = string.include?('.') ? '.' : ' ' |
| + | string.downcase.split(pattern).map(&:to_sym) |
| + | end |
| + | |
| + | class Direction |
| + | attr_reader :obj |
| + | def initialize(obj); @obj = obj; end |
| + | end |
| + | |
| + | class Asc < Direction |
| + | def <=>(other); @obj <=> other.obj; end |
| + | end |
| + | |
| + | class Desc < Direction |
| + | def <=>(other); other.obj <=> @obj; end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
locomotive/steam/adapters/filesystem/query.rb b/lib/locomotive/steam/adapters/filesystem/query.rb
+4
-13
| @@ | @@ -36,12 +36,8 @@ module Locomotive::Steam |
| self | |
| end | |
| - | def order_by(order_string) |
| - | unless order_string.blank? |
| - | pattern = order_string.include?('.') ? '.' : ' ' |
| - | @sorting = order_string.downcase.split(pattern).map(&:to_sym) |
| - | end |
| - | self |
| + | def order_by(*args) |
| + | @sorting = Order.new(*args) |
| end | |
| def limit(num) | |
| @@ | @@ -67,14 +63,9 @@ module Locomotive::Steam |
| end | |
| def sorted(entries) | |
| - | return entries if @sorting.nil? |
| + | return entries if @sorting.empty? |
| - | name, direction = @sorting.first, (@sorting.last || :asc) |
| - | if direction == :asc |
| - | entries.sort { |a, b| a.send(name) <=> b.send(name) } |
| - | else |
| - | entries.sort { |a, b| b.send(name) <=> a.send(name) } |
| - | end |
| + | entries.sort_by { |entry| @sorting.apply_to(entry, @locale) } |
| end | |
| def limited(entries) | |
locomotive/steam/adapters/filesystem/yaml_loader.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loader.rb
+14
-0
| @@ | @@ -4,6 +4,20 @@ module Locomotive::Steam |
| module YAMLLoader | |
| + | attr_reader :site_path, :cache |
| + | |
| + | def initialize(site_path, cache) |
| + | @site_path, @cache = site_path, cache |
| + | end |
| + | |
| + | def load(scope = nil) |
| + | @scope = scope |
| + | end |
| + | |
| + | def default_locale |
| + | @scope.locale |
| + | end |
| + | |
| def fetch(key, &block) | |
| cache.nil? ? yield : cache.fetch(key, &block) | |
| end | |
locomotive/steam/adapters/filesystem/yaml_loaders/page.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/page.rb
+4
-4
| @@ | @@ -4,19 +4,19 @@ module Locomotive |
| module Filesystem | |
| module YAMLLoaders | |
| - | # class Page < Struct.new(:root_path, :default_locale, :cache) |
| - | class Page < Struct.new(:site_path, :default_locale, :cache) |
| + | class Page |
| include Adapters::Filesystem::YAMLLoader | |
| - | def load |
| + | def load(scope) |
| + | super |
| fetch('app/views/pages') { load_tree } | |
| end | |
| private | |
| def path | |
| - | @path ||= File.join(root_path, 'app', 'views', 'pages') |
| + | @path ||= File.join(site_path, 'app', 'views', 'pages') |
| end | |
| def load_tree | |
locomotive/steam/adapters/filesystem/yaml_loaders/site.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/site.rb
+2
-2
| @@ | @@ -4,11 +4,11 @@ module Locomotive |
| module Filesystem | |
| module YAMLLoaders | |
| - | class Site < Struct.new(:site_path, :cache) |
| + | class Site |
| include Adapters::Filesystem::YAMLLoader | |
| - | def load |
| + | def load(scope) |
| fetch('config/site') do | |
| [_load(File.join(site_path, 'config', 'site.yml'))] | |
| end | |
locomotive/steam/models/i18n_field.rb b/lib/locomotive/steam/models/i18n_field.rb
+4
-0
| @@ | @@ -19,6 +19,10 @@ module Locomotive::Steam |
| @translations[locale] | |
| end | |
| + | def values |
| + | @translations.values |
| + | end |
| + | |
| end | |
| end | |
locomotive/steam/repositories/page_repository.rb b/lib/locomotive/steam/repositories/page_repository.rb
+5
-3
| @@ | @@ -9,10 +9,12 @@ module Locomotive |
| set_localized_attributes :title, :slug, :permalink, :editable_elements, :template, :template_path, :redirect_url, :fullpath, :seo_title, :meta_description, :meta_keywords | |
| end | |
| - | # Engine: site.pages.ordered_pages(conditions) |
| + | # Engine: site.pages.ordered_pages(conditions) [WIP] |
| def all(conditions = {}) | |
| - | default_order = 'depth_and_position asc' |
| - | query { where(conditions || {}).order_by(default_order) }.all |
| + | query do |
| + | where(conditions || {}). |
| + | order_by('depth.asc', 'position.asc') |
| + | end.all |
| end | |
| # Engine: site.pages.where(handle: handle).first | |
spec/unit/adapters/filesystem/order_spec.rb
+67
-0
| @@ | @@ -0,0 +1,67 @@ |
| + | require 'spec_helper' |
| + | |
| + | require_relative '../../../../lib/locomotive/steam/adapters/filesystem/order.rb' |
| + | |
| + | describe Locomotive::Steam::Adapters::Filesystem::Order do |
| + | |
| + | let(:order) { Locomotive::Steam::Adapters::Filesystem::Order.new(*input) } |
| + | |
| + | describe '#list' do |
| + | |
| + | subject { order.list } |
| + | |
| + | let(:input) { nil } |
| + | it { is_expected.to eq [] } |
| + | |
| + | context 'a string' do |
| + | |
| + | let(:input) { 'name' } |
| + | it { is_expected.to eq [[:name]] } |
| + | |
| + | end |
| + | |
| + | context 'two strings' do |
| + | |
| + | let(:input) { ['name', 'date.desc'] } |
| + | it { is_expected.to eq [[:name], [:date, :desc]] } |
| + | |
| + | end |
| + | |
| + | context 'a string with a comma' do |
| + | |
| + | let(:input) { 'name, date desc' } |
| + | it { is_expected.to eq [[:name], [:date, :desc]] } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe '#apply_to' do |
| + | |
| + | subject { order.apply_to(entry, :en) } |
| + | |
| + | let(:input) { 'title, date desc' } |
| + | let(:entry) { instance_double('Entry', title: 'foo', date: Time.now) } |
| + | it { expect(subject.map(&:class)).to eq([Locomotive::Steam::Adapters::Filesystem::Order::Asc, Locomotive::Steam::Adapters::Filesystem::Order::Desc]) } |
| + | |
| + | end |
| + | |
| + | describe 'sort' do |
| + | |
| + | let(:array) { |
| + | [ |
| + | instance_double('Entry1', id: 1, title: 'b', position: 1), |
| + | instance_double('Entry2', id: 2, title: 'b', position: 2), |
| + | instance_double('Entry3', id: 3, title: 'a', position: 3), |
| + | instance_double('Entry3', id: 4, title: 'c', position: 1) |
| + | ] |
| + | } |
| + | let(:input) { 'title, position desc' } |
| + | |
| + | subject { array.sort_by { |entry| order.apply_to(entry, :en) } } |
| + | |
| + | it { expect(subject.map(&:id)).to eq([3, 2, 1, 4]) } |
| + | |
| + | end |
| + | |
| + | end |
spec/unit/adapters/filesystem/yaml_loaders/page_spec.rb
+18
-14
| @@ | @@ -1,21 +1,25 @@ |
| - | # require 'spec_helper' |
| + | require 'spec_helper' |
| - | # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Page do |
| + | require_relative '../../../../../lib/locomotive/steam/adapters/filesystem/yaml_loader.rb' |
| + | require_relative '../../../../../lib/locomotive/steam/adapters/filesystem/yaml_loaders/page.rb' |
| - | # let(:root_path) { default_fixture_site_path } |
| - | # let(:default_locale) { :en } |
| - | # let(:cache) { NoCacheStore.new } |
| - | # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Page.new(root_path, default_locale, cache) } |
| + | describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Page do |
| - | # describe '#list_of_attributes' do |
| + | let(:site_path) { default_fixture_site_path } |
| + | let(:cache) { NoCacheStore.new } |
| + | let(:loader) { Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Page.new(site_path, cache) } |
| - | # subject { loader.list_of_attributes.sort { |a, b| a[:_fullpath] <=> b[:_fullpath] } } |
| + | describe '#load' do |
| - | # it 'tests various stuff' do |
| - | # expect(subject.size).to eq 21 |
| - | # expect(subject.first[:title]).to eq({ en: 'Page not found' }) |
| - | # end |
| + | let(:scope) { instance_double('Scope', locale: :en) } |
| - | # end |
| + | subject { loader.load(scope).sort { |a, b| a[:_fullpath] <=> b[:_fullpath] } } |
| - | # end |
| + | it 'tests various stuff' do |
| + | expect(subject.size).to eq 21 |
| + | expect(subject.first[:title]).to eq({ en: 'Page not found' }) |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
spec/unit/adapters/filesystem/yaml_loaders/site_spec.rb
+5
-4
| @@ | @@ -1,16 +1,17 @@ |
| require 'spec_helper' | |
| - | require_relative '../../../lib/locomotive/steam/adapters/filesystem/yaml_loaders/site.rb' |
| + | require_relative '../../../../../lib/locomotive/steam/adapters/filesystem/yaml_loader.rb' |
| + | require_relative '../../../../../lib/locomotive/steam/adapters/filesystem/yaml_loaders/site.rb' |
| describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Site do | |
| - | let(:root_path) { default_fixture_site_path } |
| + | let(:site_path) { default_fixture_site_path } |
| let(:cache) { NoCacheStore.new } | |
| - | let(:loader) { Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Site.new(root_path, cache) } |
| + | let(:loader) { Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Site.new(site_path, cache) } |
| describe '#load' do | |
| - | subject { loader.load } |
| + | subject { loader.load(nil) } |
| it { expect(subject.first[:name]).to eq 'Sample website' } | |
spec/unit/repositories/page_repository_spec.rb
+6
-13
| @@ | @@ -4,23 +4,16 @@ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb' |
| describe Locomotive::Steam::PageRepository do | |
| + | let(:pages) { [{ title: { en: 'Home' }, handle: 'home', slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }] } |
| + | |
| let(:locale) { :en } | |
| - | let(:site) { instance_double('Site', _id: 1, default_locale: :en, locales: %i(en fr)) } |
| + | let(:site) { instance_double('Site', id: 1, default_locale: :en, locales: %i(en fr)) } |
| let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(nil) } | |
| let(:repository) { Locomotive::Steam::PageRepository.new(adapter, site, locale) } | |
| - | # describe '#collection' do |
| - | |
| - | # subject { repository.send(:collection).first } |
| - | |
| - | # it { expect(subject.class).to eq Locomotive::Steam::Repositories::Filesystem::Models::Page } |
| - | |
| - | # it 'applies the sanitizer' do |
| - | # expect(subject[:fullpath]).to eq({ en: 'index' }) |
| - | # expect(subject.depth).to eq 0 |
| - | # end |
| - | |
| - | # end |
| + | before do |
| + | allow(adapter).to receive(:collection).and_return(pages) |
| + | end |
| describe '#all' do | |