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 | |