site metafields are scoped by a namespace + set a default position to each site metafield / namespace

did committed Feb 06, 2016
commit c749b6521d447aa4b4a9f76eba017a055251477c
Showing 8 changed files with 147 additions and 65 deletions
locomotive/steam/adapters/filesystem/sanitizers/site.rb b/lib/locomotive/steam/adapters/filesystem/sanitizers/site.rb +7 -5
@@ @@ -16,21 +16,23 @@ module Locomotive::Steam
def clean_metafields_schema(schema)
return nil unless schema
- schema.map do |namespace, definitions|
+ schema.each_with_index.map do |(namespace, definitions), position|
{
- label: { default: namespace.to_s }.merge(definitions.delete(:label) || {}),
- fields: parse_metafields(definitions.delete(:fields))
+ name: namespace.to_s,
+ label: { default: namespace.to_s }.merge(definitions.delete(:label) || {}),
+ fields: parse_metafields(definitions.delete(:fields)),
+ position: definitions.delete(:position)
}.merge(definitions)
end.as_json
end
def parse_metafields(fields)
- fields.map do |name, attributes|
+ fields.each_with_index.map do |(name, attributes), position|
if attributes # Hash
attributes[:label] = { default: attributes[:label] } if attributes[:label].is_a?(String)
attributes[:hint] = { default: attributes[:hint] } if attributes[:hint].is_a?(String)
end
- { name: name.to_s }.merge(attributes || {})
+ { name: name.to_s, position: position }.merge(attributes || {})
end
end
locomotive/steam/liquid/drops/metafields.rb b/lib/locomotive/steam/liquid/drops/metafields.rb +68 -17
@@ @@ -3,42 +3,93 @@ module Locomotive
module Liquid
module Drops
- class Metafields < Base
+ class MetafieldsNamespace < Base
+
+ delegate :first, :last, :each, :each_with_index, :empty?, :any?, :size, to: :labels_and_values
+
+ alias :count :size
+ alias :length :size
def before_method(meth)
- if field = fields[meth.to_s]
- find_value(meth.to_s, field)
+ find_value(meth.to_s)
+ end
+
+ def namespace=(namespace)
+ @namespace = namespace
+ end
+
+ protected
+
+ def find_value(name)
+ if field = fields[name]
+ t(values[name], field['localized'])
else
- Locomotive::Common::Logger.warn "[Liquid template] unknown site metafield \"#{meth.to_s}\""
+ Locomotive::Common::Logger.warn "[Liquid template] unknown site metafield \"#{name}\" under #{@namespace['name']}"
nil
end
end
- private
+ def values
+ @_source.metafields[@namespace['name']] || {}
+ end
- def find_value(name, field)
- value = @_source.metafields[name]
+ def labels_and_values
+ return [] if @namespace['fields'].blank?
- return nil if value.blank?
+ return @labels_and_values if @labels_and_values
- key = field['localized'] ? @context.registers[:locale] : 'default'
- value = { 'default' => value } unless value.is_a?(Hash)
+ ordered_fields = @namespace['fields'].sort { |f| f['position'] }
- value[key]
+ @labels_and_values = ordered_fields.map do |field|
+ value, localized = values[field['name']], field['localized']
+ {
+ 'name' => field['name'],
+ 'label' => t(field['label']) || field['name'].humanize,
+ 'value' => t(value, localized)
+ }
+ end.tap { |o| puts o.inspect }
end
def fields
- return @schema if @schema
+ return @fields if @fields
- (@schema = {}).tap do
- @_source.metafields_schema.each do |definition|
- definition['fields'].each do |field|
- @schema[field['name']] = field
- end
+ (@fields = {}).tap do
+ (@namespace['fields'] || []).each do |field|
+ @fields[field['name']] = field
end
end
end
+ def t(value, localized = true)
+ key = localized ? @context.registers[:locale] : 'default'
+ value = { 'default' => value } unless value.is_a?(Hash)
+ value[key]
+ end
+
+ end
+
+ class Metafields < Base
+
+ def before_method(meth)
+ find_namespace(meth.to_s)
+ end
+
+ private
+
+ def find_namespace(name)
+ if namespace = _find_namespace(name)
+ MetafieldsNamespace.new(@_source).tap { |d| d.namespace = namespace }
+ else
+ Locomotive::Common::Logger.warn "[Liquid template] unknown site metafield namespace \"#{name}\""
+ nil
+ end
+ end
+
+ def _find_namespace(name)
+ puts @_source.metafields_schema.inspect
+ @_source.metafields_schema.find { |s| s['name'] == name }
+ end
+
end
end
spec/fixtures/default/app/views/pages/basic.liquid.haml +10 -5
@@ @@ -14,8 +14,13 @@ position: 6
This is a basic page
%ul
- %li Color scheme={{ site.metafields.color_scheme }}
- %li Facebook ID={{ site.metafields.facebook_id }}
- %li Google ID={{ site.metafields.google_id }}
- %li API URL={{ site.metafields.api_url }}
- %li Expires In={{ site.metafields.expires_in }}
+ %li Color scheme={{ site.metafields.theme.color_scheme }}
+ %li Facebook ID={{ site.metafields.social.facebook_id }}
+ %li Google ID={{ site.metafields.social.google_id }}
+ %li API URL={{ site.metafields.github.api_url }}
+ %li Expires In={{ site.metafields.github.expires_in }}
+
+ %ul
+ {% for property in site.metafields.social %}
+ %li.property {{ property.label }}({{ property.name }})={{ property.value }}
+ {% endfor %}
spec/fixtures/default/config/metafields_schema.yml +3 -3
@@ @@ -1,9 +1,9 @@
- Theme:
+ theme:
fields:
color_scheme:
localized: true
- Social:
+ social:
label:
fr: Social (FR)
position: 1
@@ @@ -11,7 +11,7 @@ Social:
- facebook_id
- google_id
- Github:
+ github:
position: 0
fields:
api_url:
spec/fixtures/default/config/site.yml +11 -8
@@ @@ -21,11 +21,14 @@ meta_description: some meta description
robots_txt: "User-agent: *\nDisallow:"
metafields:
- color_scheme:
- en: 'white'
- fr: 'blue'
- nb: 'red'
- facebook_id: 'FB42'
- google_id: 'G42'
- api_url: https://api.github.com/repos/vmg/redcarpet/issues?state=closed
- expires_in: 42
+ theme:
+ color_scheme:
+ en: 'white'
+ fr: 'blue'
+ nb: 'red'
+ social:
+ facebook_id: 'FB42'
+ google_id: 'G42'
+ github:
+ api_url: https://api.github.com/repos/vmg/redcarpet/issues?state=closed
+ expires_in: 42
spec/integration/server/metafields_spec.rb +6 -0
@@ @@ -17,4 +17,10 @@ describe 'Site metafields' do
expect(last_response.body).to include 'Expires In=42'
end
+ it 'iterates over the metafields of a namespace' do
+ get '/basic'
+ expect(last_response.body).to include "<li class='property'>Facebook(facebook_id)=FB42</li>"
+ expect(last_response.body).to include "<li class='property'>Google(google_id)=G42</li>"
+ end
+
end
spec/unit/adapters/filesystem/sanitizers/site_spec.rb +6 -5
@@ @@ -26,19 +26,20 @@ describe Locomotive::Steam::Adapters::Filesystem::Sanitizers::Site do
describe 'with a schema' do
# see the metafields_schema.yml in the fixtures folder
- let(:schema) { {:Social=>{:label=>{:fr=>"Social (FR)"}, :position=>1, :fields=>["facebook_id", "google_id"]}, :Github=>{:position=>0, :fields=>{:api_url=>{:label=>"API Url", :type=>"string", :hint=>"API endpoint"}, :expires_in=>{:label=>{:en=>"Expires in", :fr=>"Expire dans"}, :hint=>{:en=>"Cache - In milliseconds", :fr=>"Cache - En millisecondes"}, :type=>"integer", :min=>0, :max=>3600}}}} }
+ let(:schema) { {:social=>{:label=>{:fr=>"Social (FR)"}, :position=>1, :fields=>["facebook_id", "google_id"]}, :github=>{:position=>0, :fields=>{:api_url=>{:label=>"API Url", :type=>"string", :hint=>"API endpoint"}, :expires_in=>{:label=>{:en=>"Expires in", :fr=>"Expire dans"}, :hint=>{:en=>"Cache - In milliseconds", :fr=>"Cache - En millisecondes"}, :type=>"integer", :min=>0, :max=>3600}}}} }
it 'loads the full schema' do
# First namespace
- expect(subject[0]['label']).to eq('default' => 'Social', 'fr' => 'Social (FR)')
+ expect(subject[0]['name']).to eq 'social'
+ expect(subject[0]['label']).to eq('default' => 'social', 'fr' => 'Social (FR)')
expect(subject[0]['position']).to eq 1
- expect(subject[0]['fields']).to eq([{ 'name' => 'facebook_id' }, { 'name' => 'google_id' }])
+ expect(subject[0]['fields']).to eq([{ 'name' => 'facebook_id', 'position' => 0 }, { 'name' => 'google_id', 'position' => 1 }])
# Second namespace
- expect(subject[1]['label']).to eq('default' => 'Github')
+ expect(subject[1]['label']).to eq('default' => 'github')
expect(subject[1]['position']).to eq 0
expect(subject[1]['fields'].count).to eq 2
- expect(subject[1]['fields'][0]).to eq('name' => 'api_url', 'label' => { 'default' => 'API Url' }, 'type' => 'string', 'hint' => { 'default' => 'API endpoint' })
+ expect(subject[1]['fields'][0]).to eq('name' => 'api_url', 'position' => 0, 'label' => { 'default' => 'API Url' }, 'type' => 'string', 'hint' => { 'default' => 'API endpoint' })
end
end
spec/unit/liquid/drops/metafields_spec.rb +36 -22
@@ @@ -2,61 +2,75 @@ require 'spec_helper'
describe Locomotive::Steam::Liquid::Drops::Metafields do
- let(:metafields) { { 'analytics_id' => { 'default' => '42' }, 'street' => { 'en' => '7 Albert Camus Alley', 'fr' => '7 allée Albert Camus' } } }
- let(:schema) { [ { fields: [{ name: 'analytics_id' }] }, { fields: [{ name: 'street', localized: true }, { name: 'country' }] }].as_json }
+ let(:metafields) { { 'my_namespace' => { 'analytics_id' => { 'default' => '42' }, 'street' => { 'en' => '7 Albert Camus Alley', 'fr' => '7 allée Albert Camus' } } } }
+ let(:schema) { [ { 'name' => 'my_namespace', fields: [{ name: 'analytics_id' }, { name: 'street', localized: true }, { name: 'country' }] }].as_json }
let(:site) { instance_double('Site', metafields: metafields, metafields_schema: schema) }
let(:context) { ::Liquid::Context.new({}, {}, { locale: 'en' }) }
let(:drop) { described_class.new(site).tap { |d| d.context = context } }
describe 'calling a metafield' do
- context 'unknown field' do
+ context 'unknown namespace' do
- subject { drop.before_method(:unknown_field) }
+ subject { drop.before_method(:unknown_namespace) }
it { is_expected.to eq nil }
end
- context 'not localized field' do
+ context 'existing namespace' do
- context 'the value exists' do
+ let(:namespace) { drop.before_method(:my_namespace).tap { |d| d.context = context } }
- subject { drop.before_method(:analytics_id) }
+ context 'unknown field' do
- it { is_expected.to eq '42' }
+ subject { namespace.before_method(:unknown_field) }
+
+ it { is_expected.to eq nil }
end
- context "the value doesn't exist" do
+ context 'not a localized field' do
- subject { drop.before_method(:country) }
+ context 'the value exists' do
- it { is_expected.to eq nil }
+ subject { namespace.before_method(:analytics_id) }
+
+ it { is_expected.to eq '42' }
+
+ end
+
+ context "the value doesn't exist" do
+
+ subject { namespace.before_method(:country) }
+
+ it { is_expected.to eq nil }
+
+ end
end
- end
+ context 'localized field' do
- context 'localized field' do
+ subject { namespace.before_method(:street) }
- subject { drop.before_method(:street) }
+ it { is_expected.to eq '7 Albert Camus Alley' }
- it { is_expected.to eq '7 Albert Camus Alley' }
+ context 'in another locale' do
- context 'in another locale' do
+ let(:context) { ::Liquid::Context.new({}, {}, { locale: 'fr' }) }
- let(:context) { ::Liquid::Context.new({}, {}, { locale: 'fr' }) }
+ it { is_expected.to eq '7 allée Albert Camus' }
- it { is_expected.to eq '7 allée Albert Camus' }
+ end
- end
+ context 'in a locale with no translation' do
- context 'in a locale with no translation' do
+ let(:context) { ::Liquid::Context.new({}, {}, { locale: 'de' }) }
- let(:context) { ::Liquid::Context.new({}, {}, { locale: 'de' }) }
+ it { is_expected.to eq nil }
- it { is_expected.to eq nil }
+ end
end