make the link_to and path_to tags work with localized data (+ write specs + refactoring)
did
committed Feb 09, 2015
commit 805811ffed52939af516867e4e6774b20b8bef6a
Showing 23
changed files with
526 additions
and 122 deletions
locomotive/steam/decorators/i18n_decorator.rb b/lib/locomotive/steam/decorators/i18n_decorator.rb
+25
-9
| @@ | @@ -4,20 +4,24 @@ module Locomotive |
| class I18nDecorator < SimpleDelegator | |
| - | attr_accessor :__translated_attributes__ |
| + | attr_accessor :__localized_attributes__ |
| + | attr_accessor :__frozen_locale__ |
| attr_reader :__locale__ | |
| attr_reader :__default_locale__ | |
| - | def initialize(object, attributes, locale, default_locale = nil) |
| - | self.__translated_attributes__ = attributes |
| - | self.__locale__ = locale |
| - | self.__default_locale__ = default_locale |
| + | def initialize(object, attributes, locale = nil, default_locale = nil) |
| + | self.__localized_attributes__ = attributes |
| + | self.__frozen_locale__ = false |
| + | self.__locale__ = locale |
| + | self.__default_locale__ = default_locale |
| super(object) | |
| end | |
| def __locale__=(locale) | |
| - | @__locale__ = locale.to_sym |
| + | unless self.__frozen_locale__ |
| + | @__locale__ = locale.try(:to_sym) |
| + | end |
| end | |
| def __default_locale__=(locale) | |
| @@ | @@ -26,13 +30,23 @@ module Locomotive |
| def __with_locale__(locale, &block) | |
| old_locale, self.__locale__ = __locale__, locale.to_sym | |
| + | self.__freeze_locale__ |
| yield.tap do | |
| + | self.__unfreeze_locale__ |
| self.__locale__ = old_locale | |
| end | |
| end | |
| + | def __freeze_locale__ |
| + | @__frozen_locale__ = true |
| + | end |
| + | |
| + | def __unfreeze_locale__ |
| + | @__frozen_locale__ = false |
| + | end |
| + | |
| def method_missing(name, *args, &block) | |
| - | if __translated_attributes__.include?(name.to_sym) |
| + | if __localized_attributes__.include?(name.to_sym) |
| field = __getobj__.public_send(:attributes)[name.to_sym] | |
| field[__locale__] || field[__default_locale__] || super | |
| else | |
| @@ | @@ -42,10 +56,12 @@ module Locomotive |
| #:nocov: | |
| def inspect | |
| - | "[Decorated #{__getobj__.class.name}][I18n] " + @__translated_attributes__.inspect + ', locale: ' + @__locale__.inspect |
| + | "[Decorated #{__getobj__.class.name}][I18n] attributes exist? " + |
| + | __getobj__.respond_to?(:attributes).inspect + |
| + | ', localized attributes: ' + @__localized_attributes__.inspect + |
| + | ', locale: ' + @__locale__.inspect |
| end | |
| - | |
| end | |
| end | |
locomotive/steam/decorators/page_decorator.rb b/lib/locomotive/steam/decorators/page_decorator.rb
+0
-1
| @@ | @@ -8,7 +8,6 @@ module Locomotive |
| # | |
| # @return [ hash ] The safe full paths | |
| # | |
| - | |
| def safe_fullpath | |
| if index_or_404? | |
| slug[current_locale] | |
locomotive/steam/liquid.rb b/lib/locomotive/steam/liquid.rb
+1
-0
| @@ | @@ -3,6 +3,7 @@ require 'solid' |
| require_relative 'liquid/errors' | |
| require_relative 'liquid/patches' | |
| require_relative 'liquid/drops/base' | |
| + | require_relative 'liquid/drops/i18n_base' |
| require_relative 'liquid/drops/proxy_collection' | |
| require_relative 'liquid/tags/hybrid' | |
| require_relative 'liquid/tags/path_helper' | |
locomotive/steam/liquid/drops/base.rb b/lib/locomotive/steam/liquid/drops/base.rb
+4
-1
| @@ | @@ -1,4 +1,3 @@ |
| - | # Liquify taken from Mephisto sources (http://mephistoblog.com/) |
| module Locomotive | |
| module Steam | |
| module Liquid | |
| @@ | @@ -35,6 +34,10 @@ module Locomotive |
| self.class.liquify(*records, &block) | |
| end | |
| + | def _source |
| + | @_source |
| + | end |
| + | |
| end | |
| end | |
| end | |
locomotive/steam/liquid/drops/content_entry.rb b/lib/locomotive/steam/liquid/drops/content_entry.rb
+1
-1
| @@ | @@ -2,7 +2,7 @@ module Locomotive |
| module Steam | |
| module Liquid | |
| module Drops | |
| - | class ContentEntry < Base |
| + | class ContentEntry < I18nBase |
| delegate :_slug, :_translated, :seo_title, :meta_keywords, :meta_description, to: :@_source | |
locomotive/steam/liquid/drops/i18n_base.rb b/lib/locomotive/steam/liquid/drops/i18n_base.rb
+37
-0
| @@ | @@ -0,0 +1,37 @@ |
| + | module Locomotive |
| + | module Steam |
| + | module Liquid |
| + | module Drops |
| + | class I18nBase < Base |
| + | |
| + | def initialize(source, localized_attributes = []) |
| + | decorated = Locomotive::Steam::Decorators::I18nDecorator.new(source, localized_attributes) |
| + | super(decorated) |
| + | end |
| + | |
| + | def context=(context) |
| + | if locale = context.registers[:locale] |
| + | @_source.__locale__ = locale |
| + | end |
| + | |
| + | @_source.__default_locale__ = context.registers[:site].default_locale |
| + | |
| + | super |
| + | end |
| + | |
| + | private |
| + | |
| + | def _change_locale(locale) |
| + | @_source.__locale__ = locale |
| + | end |
| + | |
| + | def _change_locale!(locale) |
| + | @_source.__locale__ = locale |
| + | @_source.__freeze_locale__ |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
locomotive/steam/liquid/drops/page.rb b/lib/locomotive/steam/liquid/drops/page.rb
+1
-1
| @@ | @@ -2,7 +2,7 @@ module Locomotive |
| module Steam | |
| module Liquid | |
| module Drops | |
| - | class Page < Base |
| + | class Page < I18nBase |
| delegate :fullpath, :depth, :seo_title, :meta_keywords, :meta_description, :redirect_url, :handle, to: :@_source | |
| delegate :listed?, :published?, :redirect?, :is_layout?, :templatized?, to: :@_source | |
locomotive/steam/liquid/patches.rb b/lib/locomotive/steam/liquid/patches.rb
+27
-0
| @@ | @@ -20,3 +20,30 @@ module Liquid |
| end | |
| end | |
| + | |
| + | module Liquid |
| + | module OptionsBuilder |
| + | |
| + | private |
| + | |
| + | def parse_options_from_string(string) |
| + | string.try(:strip!) |
| + | |
| + | return nil if string.blank? |
| + | |
| + | string = string.gsub(/^(\s*,)/, '') |
| + | Solid::Arguments.parse(string) |
| + | end |
| + | |
| + | def interpolate_options(options, context) |
| + | if options |
| + | options.interpolate(context).first |
| + | else |
| + | {} |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| + | |
| + | Liquid::Tag.send(:include, Liquid::OptionsBuilder) |
locomotive/steam/liquid/tags/consume.rb b/lib/locomotive/steam/liquid/tags/consume.rb
+2
-7
| @@ | @@ -22,7 +22,7 @@ module Locomotive |
| @name = $1.to_s | |
| self.prepare_url($2) | |
| - | self.prepare_api_arguments($3) |
| + | @api_options = parse_options_from_string($3) |
| else | |
| raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]") | |
| end | |
| @@ | @@ -50,13 +50,8 @@ module Locomotive |
| end | |
| end | |
| - | def prepare_api_arguments(string) |
| - | string = string.gsub(/^(\s*,)/, '').strip |
| - | @api_arguments = Solid::Arguments.parse(string) |
| - | end |
| - | |
| def set_api_options(context) | |
| - | @api_options = @api_arguments ? @api_arguments.interpolate(context).first || {} : {} |
| + | @api_options = interpolate_options(@api_options, context) |
| @expires_in = @api_options.delete(:expires_in) || 0 | |
| end | |
locomotive/steam/liquid/tags/hybrid.rb b/lib/locomotive/steam/liquid/tags/hybrid.rb
+33
-14
| @@ | @@ -5,22 +5,41 @@ module Locomotive |
| class Hybrid < ::Liquid::Block | |
| - | def parse(tokens) |
| - | nesting = 0 |
| - | tokens.each do |token| |
| - | next unless token =~ IsTag |
| - | if token =~ FullToken |
| - | if nesting == 0 && $1 == block_delimiter |
| - | @render_as_block = true |
| - | super |
| - | return |
| - | elsif $1 == block_name |
| - | nesting += 1 |
| - | elsif $1 == block_delimiter |
| - | nesting -= 1 |
| - | end |
| + | class HybridTagDetectedException < Exception; end |
| + | |
| + | def parse_body(body, tokens) |
| + | body.parse(tokens, options) do |end_tag_name, end_tag_params| |
| + | @blank &&= body.blank? |
| + | |
| + | return false if end_tag_name == block_delimiter |
| + | unless end_tag_name |
| + | # tag never closed |
| + | raise HybridTagDetectedException.new |
| end | |
| + | |
| + | # this tag is not registered with the system |
| + | # pass it to the current block for special handling or error reporting |
| + | unknown_tag(end_tag_name, end_tag_params, tokens) |
| + | end |
| + | |
| + | true |
| + | end |
| + | |
| + | def render_as_block? |
| + | @render_as_block |
| + | end |
| + | |
| + | def parse(tokens) |
| + | @render_as_block = true |
| + | begin |
| + | cloned_tokens = tokens.dup |
| + | super(cloned_tokens) |
| + | tokens.replace(cloned_tokens) |
| + | rescue HybridTagDetectedException |
| + | @body = nil |
| + | @render_as_block = false |
| end | |
| + | @blank = false |
| end | |
| end | |
locomotive/steam/liquid/tags/link_to.rb b/lib/locomotive/steam/liquid/tags/link_to.rb
+9
-10
| @@ | @@ -10,9 +10,11 @@ module Locomotive |
| render_path(context) do |page, path| | |
| label = label_from_page(page) | |
| - | if @render_as_block |
| - | context.scopes.last['target'] = page |
| - | label = super.html_safe |
| + | if render_as_block? |
| + | context.stack do |
| + | context.scopes.last['target'] = page |
| + | label = super.html_safe |
| + | end |
| end | |
| %{<a href="#{path}">#{label}</a>} | |
| @@ | @@ -26,13 +28,10 @@ module Locomotive |
| protected | |
| def label_from_page(page) | |
| - | # TODO: page is a liquid drop whose source is I18n decorated |
| - | ::Mongoid::Fields::I18n.with_locale(@options['locale']) do |
| - | if page.templatized? |
| - | page.content_entry._label |
| - | else |
| - | page.title |
| - | end |
| + | if page.templatized? |
| + | page.send(:_source).content_entry._label |
| + | else |
| + | page.title |
| end | |
| end | |
locomotive/steam/liquid/tags/nav.rb b/lib/locomotive/steam/liquid/tags/nav.rb
+4
-11
| @@ | @@ -31,7 +31,7 @@ module Locomotive |
| end | |
| def render(context) | |
| - | self.set_accessors_from_context(context) |
| + | self.set_vars_from_context(context) |
| # get all the children of a source: site (index page), parent or page. | |
| pages = children_of(fetch_starting_page) | |
| @@ | @@ -77,6 +77,7 @@ module Locomotive |
| when 'parent' then page_repository.parent_of(current_page) || current_page | |
| when 'page' then current_page | |
| else | |
| + | # TODO: locale??? |
| page_repository.by_fullpath(@source) | |
| end | |
| end | |
| @@ | @@ -168,13 +169,6 @@ module Locomotive |
| css << @_options[:active_class] if self.page_selected?(page) | |
| css << extra_css if !extra_css.blank? | |
| end.join ' ' | |
| - | |
| - | # _css = ['link'] |
| - | |
| - | # _css = 'link' |
| - | # _css += " #{@_options[:active_class]}" if self.page_selected?(page) |
| - | |
| - | # (_css + " #{css}").strip.tap { |p| puts p.inspect } |
| end | |
| # Return the HTML output of a page and its children if requested. | |
| @@ | @@ -240,10 +234,9 @@ module Locomotive |
| end | |
| end | |
| - | # Avoid to call context.registers to get the current page |
| - | # and the mounting point. |
| + | # Avoid to call context.registers to get the current page. |
| # | |
| - | def set_accessors_from_context(context) |
| + | def set_vars_from_context(context) |
| self.current_page = context.registers[:page] | |
| self.services = context.registers[:services] | |
| self.page_repository = self.services.repositories.page | |
locomotive/steam/liquid/tags/path_helper.rb b/lib/locomotive/steam/liquid/tags/path_helper.rb
+62
-49
| @@ | @@ -4,15 +4,12 @@ module Locomotive |
| module Tags | |
| module PathHelper | |
| - | Syntax = /(#{::Liquid::VariableSignature}+)/o |
| + | Syntax = /(#{::Liquid::VariableSignature}+)(\s*,.+)?/o |
| def initialize(tag_name, markup, options) | |
| if markup =~ Syntax | |
| @handle = $1 | |
| - | @path_options = {} |
| - | markup.scan(::Liquid::TagAttributes) do |key, value| |
| - | @path_options[key] = value |
| - | end |
| + | @path_options = parse_options_from_string($2) |
| else | |
| self.wrong_syntax! | |
| end | |
| @@ | @@ -21,15 +18,16 @@ module Locomotive |
| end | |
| def render_path(context, &block) | |
| - | site = context.registers[:site] |
| + | set_vars_from_context(context) |
| - | if page = self.retrieve_page_from_handle(site, context) |
| - | path = self.public_page_fullpath(site, page) |
| + | # return a drop or model? |
| + | if page = self.retrieve_page_drop_from_handle |
| + | # make sure we've got the page/content entry (if templatized) |
| + | # in the right locale |
| + | change_locale(locale, page) do |
| + | path = build_fullpath(page) |
| - | if block_given? |
| - | block.call page, path |
| - | else |
| - | path |
| + | block_given? ? block.call(page, path) : path |
| end | |
| else | |
| '' # no page found | |
| @@ | @@ -38,59 +36,74 @@ module Locomotive |
| protected | |
| - | def retrieve_page_from_handle(site, context) |
| - | handle = context[@handle] || @handle |
| + | def services |
| + | @context.registers[:services] |
| + | end |
| + | |
| + | def repository |
| + | services.repositories.page |
| + | end |
| + | |
| + | def change_locale(locale, drop, &block) |
| + | page = drop.send(:_source) |
| + | |
| + | page.__with_locale__(locale) do |
| + | if page.templatized? |
| + | page.content_entry.__with_locale__(locale) { yield } |
| + | else |
| + | yield |
| + | end |
| + | end |
| + | end |
| + | |
| + | def retrieve_page_drop_from_handle |
| + | handle = @context[@handle] || @handle |
| - | # Note/TODO: we manipulate here only Liquid drops! |
| case handle | |
| - | when String then fetch_page(site, handle) |
| - | when Locomotive::Steam::Liquid::Drops::Page then handle.instance_variable_get(:@_source) |
| - | when Locomotive::Steam::Liquid::Drops::ContentEntry then fetch_page(site, handle.instance_variable_get(:@_source), true) |
| + | when String |
| + | _retrieve_page_drop_from(handle) |
| + | when Locomotive::Steam::Liquid::Drops::ContentEntry |
| + | _retrieve_templatized_page_drop_from(handle) |
| + | when Locomotive::Steam::Liquid::Drops::Page |
| + | handle |
| else | |
| nil | |
| end | |
| - | # case handle |
| - | # when Locomotive::Page then handle |
| - | # when Locomotive::Liquid::Drops::Page then handle.instance_variable_get(:@_source) |
| - | # when String then fetch_page(site, handle) |
| - | # when Locomotive::ContentEntry then fetch_page(site, handle, true) |
| - | # when Locomotive::Liquid::Drops::ContentEntry then fetch_page(site, handle.instance_variable_get(:@_source), true) |
| - | # else |
| - | # nil |
| - | # end |
| end | |
| - | def fetch_page(site, handle, templatized = false) |
| - | # TODO: responsability of the page repository, no need of I18n |
| - | # since the source is a I18nDecorated Page model :-) |
| - | ::Mongoid::Fields::I18n.with_locale(self.locale) do |
| - | if templatized |
| - | criteria = site.pages.where(target_klass_name: handle.class.to_s, templatized: true) |
| - | criteria = criteria.where(handle: @path_options['with']) if @path_options['with'] |
| - | criteria.first.tap do |page| |
| - | page.content_entry = handle if page |
| - | end |
| - | else |
| - | site.pages.where(handle: handle).first |
| - | end |
| + | def _retrieve_page_drop_from(handle) |
| + | if page = repository.by_handle(handle) |
| + | page.to_liquid.tap { |d| d.context = @context } |
| end | |
| end | |
| - | def public_page_fullpath(site, page) |
| - | # TODO: responsability of the url_builder service |
| + | def _retrieve_templatized_page_drop_from(drop) |
| + | entry = drop.send(:_source) |
| - | fullpath = site.localized_page_fullpath(page, self.locale) |
| + | if page = repository.template_for(entry, @path_options[:with]) |
| + | page.to_liquid.tap { |d| d.context = @context } |
| + | end |
| + | end |
| - | if page.templatized? |
| - | fullpath.gsub!('content_type_template', page.content_entry._slug) |
| + | def build_fullpath(page) |
| + | services.url_builder.url_for(page, locale).tap do |fullpath| |
| + | if page.templatized? |
| + | entry = page.send(:_source).content_entry |
| + | fullpath.gsub!('content_type_template', entry._slug) |
| + | end |
| end | |
| + | end |
| - | File.join('/', fullpath) |
| + | def locale |
| + | @path_options[:locale] || @locale |
| end | |
| - | # def locale |
| - | # @path_options['locale'] || I18n.locale |
| - | # end |
| + | def set_vars_from_context(context) |
| + | @context = context |
| + | @path_options = interpolate_options(@path_options, context) |
| + | @site = context.registers[:site] |
| + | @locale = context.registers[:locale] |
| + | end |
| end | |
| end | |
locomotive/steam/repositories/page.rb b/lib/locomotive/steam/repositories/page.rb
+8
-0
| @@ | @@ -16,6 +16,14 @@ module Locomotive |
| site.pages.where(fullpath: path).first | |
| end | |
| + | def template_for(entry, handle = nil) |
| + | criteria = site.pages.where(target_klass_name: entry.class.to_s, templatized: true) |
| + | criteria = criteria.where(handle: handle) if handle |
| + | criteria.first.tap do |page| |
| + | page.content_entry = entry if page |
| + | end |
| + | end |
| + | |
| def root | |
| site.pages.root.first | |
| end | |
locomotive/steam/services/url_builder.rb b/lib/locomotive/steam/services/url_builder.rb
+21
-7
| @@ | @@ -2,20 +2,34 @@ module Locomotive |
| module Steam | |
| module Services | |
| - | class UrlBuilder < Struct.new(:site, :locale) |
| + | class UrlBuilder < Struct.new(:site, :current_locale) |
| - | def url_for(page) |
| - | if locale.to_s == site.default_locale.to_s |
| - | "/#{page.fullpath}" |
| - | else |
| - | "/#{locale}/#{page.fullpath}" |
| - | end |
| + | def url_for(page, locale = nil) |
| + | [''].tap do |segments| |
| + | locale ||= current_locale |
| + | same_locale = locale.to_sym == site.default_locale.to_sym |
| + | |
| + | segments << locale unless same_locale |
| + | |
| + | # fullpath |
| + | segments << sanitized_fullpath(page.fullpath, same_locale) |
| + | end.compact.join('/') |
| end | |
| def public_submission_url_for(content_type) | |
| "/entry_submissions/#{content_type.slug}" | |
| end | |
| + | private |
| + | |
| + | def sanitized_fullpath(path, same_locale) |
| + | if path == 'index' |
| + | same_locale ? '' : nil |
| + | else |
| + | path |
| + | end |
| + | end |
| + | |
| end | |
| end | |
spec/unit/decorators/i18n_decorator_spec.rb
+16
-5
| @@ | @@ -3,12 +3,12 @@ require 'spec_helper' |
| describe Locomotive::Steam::Decorators::I18nDecorator do | |
| let(:page) { instance_double('Page', title: 'Hello world', published?: true, attributes: { title: { en: 'Hello world!', fr: 'Bonjour monde' } }) } | |
| - | let(:translated) { [:title] } |
| + | let(:localized) { [:title] } |
| let(:locale) { 'fr' } | |
| let(:default_locale) { nil } | |
| - | let(:decorated) { Locomotive::Steam::Decorators::I18nDecorator.new(page, translated, locale, default_locale) } |
| + | let(:decorated) { Locomotive::Steam::Decorators::I18nDecorator.new(page, localized, locale, default_locale) } |
| - | it 'uses the translated version of the title attribute' do |
| + | it 'uses the localized version of the title attribute' do |
| expect(decorated.title).to eq 'Bonjour monde' | |
| end | |
| @@ | @@ -16,9 +16,9 @@ describe Locomotive::Steam::Decorators::I18nDecorator do |
| expect(decorated.published?).to eq true | |
| end | |
| - | describe 'no translated attributes: use the default method' do |
| + | describe 'no localized attributes: use the default method' do |
| - | let(:translated) { [] } |
| + | let(:localized) { [] } |
| it { expect(decorated.title).to eq 'Hello world' } | |
| end | |
| @@ | @@ -39,6 +39,17 @@ describe Locomotive::Steam::Decorators::I18nDecorator do |
| end | |
| + | describe 'freeze locale' do |
| + | |
| + | before { decorated.__freeze_locale__ } |
| + | |
| + | it 'forbids the modification of the locale' do |
| + | decorated.__locale__ = 'en' |
| + | expect(decorated.title).to eq 'Bonjour monde' |
| + | end |
| + | |
| + | end |
| + | |
| it 'uses another way to switch to a different locale' do | |
| decorated.__with_locale__(:en) do | |
| expect(decorated.title).to eq 'Hello world!' | |
spec/unit/liquid/drops/content_entry_spec.rb
+20
-1
| @@ | @@ -2,10 +2,11 @@ require 'spec_helper' |
| describe Locomotive::Steam::Liquid::Drops::ContentEntry do | |
| + | let(:site) { instance_double('Site', default_locale: 'en') } |
| let(:entry) { instance_double('Article', _id: 42, title: 'Hello world', _label: 'Hello world', _slug: 'hello-world', _translated: false, seo_title: 'seo title', meta_keywords: 'keywords', meta_description: 'description') } | |
| let(:assigns) { {} } | |
| let(:services) { Locomotive::Steam::Services.build_instance } | |
| - | let(:context) { ::Liquid::Context.new(assigns, {}, { services: services }) } |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { services: services, site: site, locale: 'en' }) } |
| let(:drop) { Locomotive::Steam::Liquid::Drops::ContentEntry.new(entry).tap { |d| d.context = context } } | |
| subject { drop } | |
| @@ | @@ -77,4 +78,22 @@ describe Locomotive::Steam::Liquid::Drops::ContentEntry do |
| end | |
| + | describe 'i18n' do |
| + | |
| + | let(:entry) { instance_double('Article', attributes: { title: { en: 'Hello world', fr: 'Bonjour monde' } }) } |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::ContentEntry.new(entry, [:title]).tap { |d| d.context = context } } |
| + | |
| + | subject { drop.before_method(:title) } |
| + | |
| + | it { is_expected.to eq 'Hello world' } |
| + | |
| + | context 'change the current locale of the context' do |
| + | |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { locale: 'fr', site: site }) } |
| + | it { is_expected.to eq 'Bonjour monde' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| end | |
spec/unit/liquid/drops/page_spec.rb
+34
-3
| @@ | @@ -4,8 +4,9 @@ describe Locomotive::Steam::Liquid::Drops::Page do |
| let(:assigns) { {} } | |
| let(:services) { Locomotive::Steam::Services.build_instance } | |
| - | let(:context) { ::Liquid::Context.new(assigns, {}, { services: services }) } |
| - | let(:page) { instance_double('Page', id: 42, title: 'Index', slug: 'index', fullpath: 'index', content_type: nil, depth: 1, templatized?: false, listed?: true, published?: true, is_layout?: true, redirect?: false, seo_title: 'seo title', redirect_url: '/', handle: 'index', meta_keywords: 'keywords', meta_description: 'description') } |
| + | let(:site) { instance_double('Site', default_locale: 'en') } |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { locale: 'en', services: services, site: site }) } |
| + | let(:page) { instance_double('Page', id: 42, localized_attributes: [], title: 'Index', slug: 'index', fullpath: 'index', content_type: nil, depth: 1, templatized?: false, listed?: true, published?: true, is_layout?: true, redirect?: false, seo_title: 'seo title', redirect_url: '/', handle: 'index', meta_keywords: 'keywords', meta_description: 'description') } |
| let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page).tap { |d| d.context = context } } | |
| subject { drop } | |
| @@ | @@ -83,7 +84,7 @@ describe Locomotive::Steam::Liquid::Drops::Page do |
| let(:entry) { liquid_instance_double('ContentEntry', _label: 'First Article', _slug: 'first-article') } | |
| let(:assigns) { { 'entry' => entry } } | |
| - | let(:page) { instance_double('Page', id: 42, title: 'Index', slug: 'index', templatized?: true, content_type: 'articles') } |
| + | let(:page) { instance_double('Page', id: 42, title: 'Index', slug: 'index', localized_attributes: [], templatized?: true, content_type: 'articles') } |
| it { expect(subject.title).to eq 'First Article' } | |
| it { expect(subject.slug).to eq 'first-article' } | |
| @@ | @@ -100,4 +101,34 @@ describe Locomotive::Steam::Liquid::Drops::Page do |
| end | |
| + | describe 'i18n' do |
| + | |
| + | let(:page) { instance_double('Page', attributes: { title: { en: 'About us', fr: 'A notre sujet' } }, templatized?: false) } |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:title]).tap { |d| d.context = context } } |
| + | |
| + | it { expect(subject.title).to eq 'About us' } |
| + | |
| + | context 'change the current locale of the context' do |
| + | |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { locale: 'fr', site: site }) } |
| + | it { expect(subject.title).to eq 'A notre sujet' } |
| + | |
| + | end |
| + | |
| + | context 'change the locale down the road' do |
| + | |
| + | before { subject.send(:_change_locale, 'fr') } |
| + | it { expect(subject.title).to eq 'A notre sujet' } |
| + | |
| + | end |
| + | |
| + | it 'prevents further modifications of the locale' do |
| + | subject.send(:_change_locale!, 'fr') |
| + | expect(subject.title).to eq 'A notre sujet' |
| + | subject.send(:_change_locale, 'en') |
| + | expect(subject.title).to eq 'A notre sujet' |
| + | end |
| + | |
| + | end |
| + | |
| end | |
spec/unit/liquid/tags/consume_spec.rb
+1
-1
| @@ | @@ -69,7 +69,7 @@ describe Locomotive::Steam::Liquid::Tags::Consume do |
| let(:response) { { 'title' => 'first response' } } | |
| let(:url) { 'http://blog.locomotiveapp.org/api/read' } | |
| - | let(:source) { %{{% consume blog from "#{url}" timeout:5.0 %}{{ blog.title }}{% endconsume %}} } |
| + | let(:source) { %{{% consume blog from "#{url}" timeout: 5.0 %}{{ blog.title }}{% endconsume %}} } |
| it 'should pass the timeout option to httparty' do | |
| expect(services.external_api).to receive(:consume).with(url, timeout: 5.0).and_return(response) | |
spec/unit/liquid/tags/link_to_spec.rb
+105
-0
| @@ | @@ -0,0 +1,105 @@ |
| + | require 'spec_helper' |
| + | |
| + | describe Locomotive::Steam::Liquid::Tags::PathTo do |
| + | |
| + | let(:assigns) { {} } |
| + | let(:services) { Locomotive::Steam::Services.build_instance } |
| + | let(:site) { instance_double('Site', default_locale: 'en') } |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { services: services, site: site, locale: 'en' }) } |
| + | |
| + | subject { render_template(source, context) } |
| + | |
| + | before { allow(services).to receive(:current_site).and_return(site) } |
| + | |
| + | describe 'parsing' do |
| + | |
| + | let(:source) { '{% link_to %}' } |
| + | it { expect { subject }.to raise_error("Syntax Error in 'link_to' - Valid syntax: link_to page_handle, locale es (locale is optional)") } |
| + | |
| + | context 'unknown tag' do |
| + | |
| + | let(:source) { '{% link_to index %}{% endbar %}{% endlink_to %}' } |
| + | it { expect { subject }.to raise_error("Liquid syntax error: Unknown tag 'endbar'") } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe 'unknown page' do |
| + | |
| + | let(:source) { '{% link_to index %}' } |
| + | |
| + | before do |
| + | expect(services.repositories.page).to receive(:by_handle).with('index').and_return(nil) |
| + | end |
| + | |
| + | it { is_expected.to eq '' } |
| + | |
| + | end |
| + | |
| + | describe '#render' do |
| + | |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:title, :fullpath]) } |
| + | let(:page) { liquid_instance_double('Index', handle: 'index', attributes: { title: { en: 'Home', fr: 'Accueil' }, fullpath: fullpath }, templatized?: false) } |
| + | let(:fullpath) { { en: 'index', fr: 'index' } } |
| + | |
| + | before do |
| + | allow(services.repositories.page).to receive(:by_handle).with('index').and_return(page) |
| + | allow(page).to receive(:to_liquid).and_return(drop) |
| + | end |
| + | |
| + | describe 'used as a tag' do |
| + | |
| + | let(:source) { 'My link: {% link_to index %}!' } |
| + | it { is_expected.to eq 'My link: <a href="/">Home</a>!' } |
| + | |
| + | context 'and a different locale' do |
| + | |
| + | let(:source) { "{% link_to index, locale: 'fr' %}" } |
| + | it { is_expected.to eq '<a href="/fr">Accueil</a>' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe 'used as a block' do |
| + | |
| + | let(:source) { 'My link: {% link_to index %}click here{% endlink_to %}!' } |
| + | it { is_expected.to eq 'My link: <a href="/">click here</a>!' } |
| + | |
| + | context 'and a different locale' do |
| + | |
| + | let(:source) { "{% link_to index, locale: 'fr' %}{{ target.title }}!{% endlink_to %}" } |
| + | it { is_expected.to eq '<a href="/fr">Accueil!</a>' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe 'link to a content entry (drop)' do |
| + | |
| + | let(:assigns) { { 'article' => entry_drop } } |
| + | let(:entry_drop) { Locomotive::Steam::Liquid::Drops::ContentEntry.new(entry, [:_label, :_slug]) } |
| + | let(:entry) { liquid_instance_double('Article', attributes: { _label: { en: 'Hello world', fr: 'Bonjour monde' }, _slug: { en: 'hello-world', fr: 'bonjour-monde' } }) } |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:fullpath]) } |
| + | let(:page) { liquid_instance_double('ArticleTemplate', title: 'Template of an article', handle: 'article', attributes: { fullpath: { en: 'my-articles/content_type_template', fr: 'mes-articles/content_type_template' } }, localized_attributes: [:fullpath], content_entry: entry_drop.send(:_source), templatized?: true) } |
| + | let(:source) { '{% link_to article %}' } |
| + | |
| + | before do |
| + | expect(services.repositories.page).to receive(:template_for).with(entry, nil).and_return(page) |
| + | end |
| + | |
| + | it { is_expected.to eq '<a href="/my-articles/hello-world">Hello world</a>' } |
| + | |
| + | context 'with a different locale' do |
| + | |
| + | let(:source) { "{% link_to article, locale: 'fr' %}" } |
| + | it { is_expected.to eq '<a href="/fr/mes-articles/bonjour-monde">Bonjour monde</a>' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
spec/unit/liquid/tags/nav_spec.rb
+1
-1
| @@ | @@ -218,7 +218,7 @@ describe 'Locomotive::Steam::Liquid::Tags::Nav' do |
| let(:source) { %({% nav parent, active_class: "active" %}) } | |
| before do | |
| - | services.url_builder.locale = 'fr' |
| + | services.url_builder.current_locale = 'fr' |
| allow(repository).to receive(:parent_of).with(page).and_return(index) | |
| allow(repository).to receive(:children_of).with(index).and_return(depth_1) | |
| end | |
spec/unit/liquid/tags/path_to_spec.rb
+100
-0
| @@ | @@ -0,0 +1,100 @@ |
| + | require 'spec_helper' |
| + | |
| + | describe Locomotive::Steam::Liquid::Tags::PathTo do |
| + | |
| + | let(:assigns) { {} } |
| + | let(:services) { Locomotive::Steam::Services.build_instance } |
| + | let(:site) { instance_double('Site', default_locale: 'en') } |
| + | let(:context) { ::Liquid::Context.new(assigns, {}, { services: services, site: site, locale: 'en' }) } |
| + | |
| + | subject { render_template(source, context) } |
| + | |
| + | before { allow(services).to receive(:current_site).and_return(site) } |
| + | |
| + | describe 'parsing' do |
| + | |
| + | let(:source) { '{% path_to %}' } |
| + | it { expect { subject }.to raise_error('Valid syntax: path_to <page|page_handle|content_entry>(, locale: [fr|de|...], with: <page_handle>') } |
| + | |
| + | end |
| + | |
| + | describe 'unknown page' do |
| + | |
| + | let(:source) { '{% path_to index %}' } |
| + | |
| + | before do |
| + | expect(services.repositories.page).to receive(:by_handle).with('index').and_return(nil) |
| + | end |
| + | |
| + | it { is_expected.to eq '' } |
| + | |
| + | end |
| + | |
| + | describe 'from a handle of a page' do |
| + | |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:fullpath]) } |
| + | let(:page) { liquid_instance_double('Index', title: 'Index', handle: 'index', attributes: { fullpath: fullpath }, templatized?: false) } |
| + | let(:fullpath) { { en: 'index', fr: 'index' } } |
| + | let(:source) { '{% path_to index %}' } |
| + | |
| + | before do |
| + | expect(services.repositories.page).to receive(:by_handle).with('index').and_return(page) |
| + | allow(page).to receive(:to_liquid).and_return(drop) |
| + | end |
| + | |
| + | it { is_expected.to eq '/' } |
| + | |
| + | context 'and a different locale' do |
| + | |
| + | let(:source) { "{% path_to index, locale: 'fr' %}" } |
| + | it { is_expected.to eq '/fr' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe 'from a page (drop) itself' do |
| + | |
| + | let(:assigns) { { 'about_us' => drop } } |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:fullpath]) } |
| + | let(:page) { liquid_instance_double('AboutUs', title: 'About us', handle: 'index', attributes: { fullpath: fullpath }, localized_attributes: [:fullpath], templatized?: false) } |
| + | let(:fullpath) { { en: 'about-us', fr: 'a-notre-sujet' } } |
| + | let(:source) { '{% path_to about_us %}' } |
| + | |
| + | it { is_expected.to eq '/about-us' } |
| + | |
| + | context 'and a different locale' do |
| + | |
| + | let(:source) { "{% path_to about_us, locale: 'fr' %}" } |
| + | it { is_expected.to eq '/fr/a-notre-sujet' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | describe 'from a content entry (drop)' do |
| + | |
| + | let(:assigns) { { 'article' => entry_drop } } |
| + | let(:entry_drop) { Locomotive::Steam::Liquid::Drops::ContentEntry.new(entry, [:_slug]) } |
| + | let(:entry) { liquid_instance_double('Article', attributes: { _slug: { en: 'hello-world', fr: 'bonjour-monde' } }) } |
| + | let(:drop) { Locomotive::Steam::Liquid::Drops::Page.new(page, [:fullpath]) } |
| + | let(:page) { liquid_instance_double('ArticleTemplate', title: 'Template of an article', handle: 'article', attributes: { fullpath: { en: 'my-articles/content_type_template', fr: 'mes-articles/content_type_template' } }, localized_attributes: [:fullpath], content_entry: entry_drop.send(:_source), templatized?: true) } |
| + | let(:source) { '{% path_to article %}' } |
| + | |
| + | before do |
| + | expect(services.repositories.page).to receive(:template_for).with(entry, nil).and_return(page) |
| + | allow(page).to receive(:to_liquid).and_return(drop) |
| + | end |
| + | |
| + | it { is_expected.to eq '/my-articles/hello-world' } |
| + | |
| + | context 'and a different locale' do |
| + | |
| + | let(:source) { "{% path_to article, locale: 'fr' %}" } |
| + | it { is_expected.to eq '/fr/mes-articles/bonjour-monde' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
spec/unit/services/url_builder_spec.rb
+14
-0
| @@ | @@ -21,6 +21,20 @@ describe Locomotive::Steam::Services::UrlBuilder do |
| end | |
| + | describe 'no need to put the index slug' do |
| + | |
| + | let(:page) { instance_double('Index', fullpath: 'index') } |
| + | it { is_expected.to eq '/' } |
| + | |
| + | context 'different locale' do |
| + | |
| + | let(:locale) { 'fr' } |
| + | it { is_expected.to eq '/fr' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| end | |
| end | |