handle the associations of a content entry + write more specs

did committed Feb 14, 2015
commit 05ae9622e46c0f4f0d030b296007a0fbaea5c8df
Showing 10 changed files with 192 additions and 43 deletions
locomotive/steam/repositories/filesystem.rb b/lib/locomotive/steam/repositories/filesystem.rb +1 -1
@@ @@ -41,7 +41,7 @@ module Locomotive
register :content_entry do
Filesystem::ContentEntry.new(
YAMLLoaders::ContentEntry.new(options[:path], cache),
- current_site, current_locale)
+ current_site, current_locale, content_type)
end
register :theme_asset do
locomotive/steam/repositories/filesystem/content_entry.rb b/lib/locomotive/steam/repositories/filesystem/content_entry.rb +57 -15
@@ @@ -3,7 +3,7 @@ module Locomotive
module Repositories
module Filesystem
- class ContentEntry < Struct.new(:loader, :site, :current_locale)
+ class ContentEntry < Struct.new(:loader, :site, :current_locale, :content_type_repository)
include Concerns::Queryable
@@ @@ -11,15 +11,23 @@ module Locomotive
# Engine: ???
def all(type, conditions = {})
- conditions ||= {}
+ conditions = { _visible: true }.merge(conditions || {})
- # TODO: order_by goes here (get settings from the type)
+ # priority:
+ # 1/ order_by passed in the conditions parameter
+ # 2/ the default order (_position) defined in the content type
+ order_by = conditions.delete(:order_by) || type.order_by
query(type) do
- where(conditions.merge(_visible: true)).order_by(conditions.delete(:order_by))
+ where(conditions).order_by(order_by)
end.all
end
+ # Engine: not necessary
+ def by_slug(type, slug)
+ query(type) { where(_slug: slug) }.first
+ end
+
# Engine: entry.name :-)
def value_for(name, entry, conditions = {})
value = entry.send(name)
@@ @@ -31,17 +39,6 @@ module Locomotive
end
end
- # Note:
- def association(metadata, conditions = {})
- # only visible entries
- # conditions[:_visible] = true
-
- # order_by = conditions.delete(:order_by).try(:split)
-
- # association.filtered(conditions, order_by)
- raise 'TODO filter'
- end
-
# Engine: entry.next
def next(entry)
raise 'TODO next'
@@ @@ -64,6 +61,51 @@ module Locomotive
private
+ def type_from(slug)
+ content_type_repository.by_slug(slug)
+ end
+
+ def localized_slug(entry)
+ if (values = entry._slug).is_a?(Hash)
+ values[current_locale]
+ else
+ values
+ end
+ end
+
+ def association(metadata, conditions = {})
+ case metadata.type
+ when :belongs_to then belongs_to_association(metadata)
+ when :has_many then has_many_association(metadata, conditions)
+ when :many_to_many then many_to_many_association(metadata, conditions)
+ end
+ end
+
+ def belongs_to_association(metadata)
+ type = type_from(metadata.target_class_slug)
+ by_slug(type, metadata.target_slugs.first)
+ end
+
+ def has_many_association(metadata, conditions)
+ many_association(metadata,
+ { metadata.target_field => localized_slug(metadata.source) }.merge(conditions))
+ end
+
+ def many_to_many_association(metadata, conditions)
+ many_association(metadata,
+ { '_slug.in' => metadata.target_slugs }.merge(conditions))
+ end
+
+ def many_association(metadata, conditions)
+ type = type_from(metadata.target_class_slug)
+
+ if order_by = metadata.order_by
+ conditions = { order_by: order_by }.merge(conditions)
+ end
+
+ all(type, conditions)
+ end
+
def memoized_collection(content_type)
slug = content_type.slug
@collections ||= {}
locomotive/steam/repositories/filesystem/models/content_entry.rb b/lib/locomotive/steam/repositories/filesystem/models/content_entry.rb +7 -2
@@ @@ -14,8 +14,10 @@ module Locomotive
def initialize(attributes = {})
super({
- _visible: true,
- _position: 0
+ _visible: true,
+ _position: 0,
+ created_at: Time.now,
+ updated_at: Time.now
}.merge(attributes))
end
@@ @@ -68,6 +70,9 @@ module Locomotive
class AssociationMetadata < Struct.new(:type, :source, :field, :target_slugs)
def association; true; end
+ def inverse_of; field.inverse_of; end
+ def target_class_slug; field.class_name; end
+ def order_by; field[:order_by]; end
end
end
locomotive/steam/repositories/filesystem/models/content_type.rb b/lib/locomotive/steam/repositories/filesystem/models/content_type.rb +12 -0
@@ @@ -8,6 +8,13 @@ module Locomotive
attr_accessor :fields, :fields_by_name
+ def initialize(attributes = {})
+ super({
+ order_by: '_position',
+ order_direction: 'asc'
+ }.merge(attributes))
+ end
+
def label_field_name
(self[:label_field_name] || fields.first.name).to_sym
end
@@ @@ -16,6 +23,11 @@ module Locomotive
query_fields { where(localized: true) }.all.map(&:name)
end
+ def order_by
+ name = self[:order_by] == 'manually' ? '_position' : self[:order_by]
+ "#{name} #{self.order_direction}"
+ end
+
def query_fields(&block)
Filesystem::MemoryAdapter::Query.new(fields, &block)
end
spec/support.rb +1 -0
@@ @@ -4,4 +4,5 @@ require_relative 'support/liquid'
require_relative 'support/matchers/hash'
require_relative 'support/examples/matching_locale'
require_relative 'support/examples/locale_file'
+ require_relative 'support/time'
require_relative 'support/pry'
spec/support/time.rb +3 -0
@@ @@ -0,0 +1,3 @@
+ require 'chronic'
+ Time.zone = 'UTC'
+ Chronic.time_class = Time.zone
spec/unit/repositories/filesystem/content_entry_spec.rb +61 -24
@@ @@ -3,12 +3,13 @@ require 'spec_helper'
describe Locomotive::Steam::Repositories::Filesystem::ContentEntry do
# let(:fields) { [{ title: { hint: 'Title of the article' } }, { author: { type: 'string', label: 'Fullname of the author' } }] }
- let(:type) { instance_double('Articles', slug: 'articles', label_field_name: :title, localized_fields_names: [:title], fields_by_name: { title: instance_double('Field', type: :string) }) }
+ let(:type) { instance_double('Articles', slug: 'articles', order_by: nil, label_field_name: :title, localized_fields_names: [:title], fields_by_name: { title: instance_double('Field', type: :string) }) }
let(:loader) { instance_double('Loader', list_of_attributes: [{ content_type: type, _position: 0, _label: 'Update #1', title: { fr: 'Mise a jour #1' }, text: { en: 'added some free stuff', fr: 'phrase FR' }, date: '2009/05/12', category: 'General' }]) }
let(:site) { instance_double('Site', default_locale: :en, locales: [:en, :fr]) }
let(:locale) { :en }
- let(:repository) { Locomotive::Steam::Repositories::Filesystem::ContentEntry.new(loader, site, locale) }
+ let(:content_type_repository) { instance_double('ContentTypeRepository') }
+ let(:repository) { Locomotive::Steam::Repositories::Filesystem::ContentEntry.new(loader, site, locale, content_type_repository) }
describe '#collection' do
@@ @@ -29,6 +30,20 @@ describe Locomotive::Steam::Repositories::Filesystem::ContentEntry do
end
+ describe '#by_slug' do
+
+ let(:slug) { nil }
+ subject { repository.by_slug(type, slug) }
+
+ it { is_expected.to eq nil }
+
+ context 'existing slug' do
+ let(:slug) { 'update-1' }
+ it { expect(subject.title).to eq({ en: 'Update #1', fr: 'Mise a jour #1' }) }
+ end
+
+ end
+
describe '#value_for' do
let(:name) { :title }
@@ @@ -38,44 +53,66 @@ describe Locomotive::Steam::Repositories::Filesystem::ContentEntry do
it { is_expected.to eq 'Hello world' }
- context 'association' do
+ describe 'association do' do
- # TODO
+ let(:author_type) { instance_double('AuthorType') }
+ let(:entry) { instance_double('Article', _slug: 'hello-world', author: association, authors: association) }
- end
+ before do
+ allow(content_type_repository).to receive(:by_slug).with(:authors).and_return(:author_type)
+ end
- end
+ context 'belongs_to association' do
- describe '#all' do
+ let(:association) { instance_double('Association', type: :belongs_to, association: true, target_class_slug: :authors, target_slugs: ['john-doe'], order_by: nil) }
+ let(:name) { :author }
- let(:conditions) { nil }
- subject { repository.all(type, conditions) }
+ before do
+ expect(repository).to receive(:by_slug).with(:author_type, 'john-doe').and_return('John Doe')
+ end
- it { expect(subject.size).to eq 1 }
+ it { expect(subject).to eq 'John Doe' }
- end
+ end
+
+ context 'has_many association' do
+
+ let(:association) { instance_double('Association', type: :has_many, association: true, target_class_slug: :authors, target_field: :article, order_by: 'created_at') }
+ let(:name) { :authors }
+
+ before do
+ allow(association).to receive(:source).and_return(entry)
+ expect(repository).to receive(:all).with(:author_type, { article: 'hello-world', order_by: 'created_at' }).and_return(%w(jane john))
+ end
+
+ it { expect(subject).to eq %w(jane john) }
+
+ end
+
+ context 'many_to_many association' do
- # describe '#by_slug' do
+ let(:association) { instance_double('Association', type: :many_to_many, association: true, target_class_slug: :authors, target_slugs: %w(jane john), order_by: nil) }
+ let(:name) { :authors }
- # let(:slug) { nil }
- # subject { repository.by_slug(slug) }
+ before do
+ expect(repository).to receive(:all).with(:author_type, { '_slug.in' => %w(jane john) }).and_return(%w(jane john))
+ end
- # it { is_expected.to eq nil }
+ it { expect(subject).to eq %w(jane john) }
- # context 'existing content type' do
+ end
- # let(:slug) { 'articles' }
- # it { expect(subject.name).to eq 'Articles' }
+ end
- # end
+ end
- # context 'slug is already a content type' do
+ describe '#all' do
- # let(:slug) { instance_double('ContentType') }
- # it { is_expected.to eq slug }
+ let(:conditions) { nil }
+ subject { repository.all(type, conditions) }
- # end
+ it { expect(subject.size).to eq 1 }
- # end
+ end
end
spec/unit/repositories/filesystem/models/content_entry_spec.rb +11 -1
@@ @@ -91,7 +91,7 @@ describe Locomotive::Steam::Repositories::Filesystem::Models::ContentEntry do
end
context 'a date time' do
- let(:field_type) { :date }
+ let(:field_type) { :date_time }
let(:value) { '2007/06/29 00:00:00' }
let(:datetime) { DateTime.parse('2007/06/29 00:00:00') }
it { is_expected.to eq datetime }
@@ @@ -101,6 +101,16 @@ describe Locomotive::Steam::Repositories::Filesystem::Models::ContentEntry do
end
end
+ context 'a file' do
+ let(:field_type) { :file }
+ let(:value) { 'foo.png' }
+ it { is_expected.to eq({ 'url' => 'foo.png' }) }
+ context 'localized' do
+ let(:value) { { en: 'foo-en.png', fr: 'foo-fr.png' } }
+ it { is_expected.to eq({ en: { 'url' => 'foo-en.png' }, fr: { 'url' => 'foo-fr.png' } }) }
+ end
+ end
+
context 'a belongs_to relationship' do
let(:field_type) { :belongs_to }
let(:value) { 'john-doe' }
spec/unit/repositories/filesystem/models/content_type_spec.rb +17 -0
@@ @@ -30,4 +30,21 @@ describe Locomotive::Steam::Repositories::Filesystem::Models::ContentType do
end
+ describe '#order_by' do
+
+ subject { content_type.order_by }
+ it { is_expected.to eq '_position asc' }
+
+ context 'specifying manually' do
+
+ before do
+ content_type.attributes[:order_by] = 'manually'
+ content_type.attributes[:order_direction] = 'desc'
+ end
+ it { is_expected.to eq '_position desc' }
+
+ end
+
+ end
+
end
spec/unit/repositories/filesystem/models/page_spec.rb +22 -0
@@ @@ -0,0 +1,22 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Repositories::Filesystem::Models::Page do
+
+ let(:attributes) { {} }
+ let(:page) { Locomotive::Steam::Repositories::Filesystem::Models::Page.new(attributes) }
+
+ describe '#not_found' do
+
+ let(:attributes) { { fullpath: { en: 'index' } } }
+
+ subject { page.not_found? }
+ it { is_expected.to eq false }
+
+ context 'true' do
+ let(:attributes) { { fullpath: { en: '404' } } }
+ it { is_expected.to eq true }
+ end
+
+ end
+
+ end