refactor/write specs for the editable_text liquid tag

did committed Feb 04, 2015
commit dbebdbe0eb718284139040a97aa3c181f296b64b
Showing 11 changed files with 341 additions and 147 deletions
Gemfile.lock +4 -4
@@ @@ -11,7 +11,7 @@ PATH
httparty (~> 0.13.3)
kaminari (~> 0.16.2)
kramdown (~> 1.5.0)
- locomotivecms-solid (~> 4.0.0.alpha1)
+ locomotivecms-solid (~> 4.0.0.alpha2)
locomotivecms_common (~> 0.0.2)
mime-types (~> 2.4.3)
mimetype-fu (~> 0.1.2)
@@ @@ -96,9 +96,9 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kramdown (1.5.0)
- locomotivecms-liquid (4.0.0.alpha1)
- locomotivecms-solid (4.0.0.alpha1)
- locomotivecms-liquid (= 4.0.0.alpha1)
+ locomotivecms-liquid (4.0.0.alpha2)
+ locomotivecms-solid (4.0.0.alpha2)
+ locomotivecms-liquid (~> 4.0.0.alpha2)
locomotivecms_common (0.0.2)
colorize
loofah (2.0.1)
locomotive/steam/liquid/tags/editable.rb b/lib/locomotive/steam/liquid/tags/editable.rb +2 -2
@@ @@ -1,4 +1,4 @@
- # require 'locomotive/liquid/tags/editable/base'
- # require 'locomotive/liquid/tags/editable/text'
+ require_relative 'editable/base'
+ require_relative 'editable/text'
# require 'locomotive/liquid/tags/editable/file'
# require 'locomotive/liquid/tags/editable/control'
locomotive/steam/liquid/tags/editable/base.rb b/lib/locomotive/steam/liquid/tags/editable/base.rb +67 -65
@@ @@ -1,87 +1,89 @@
module Locomotive
- module Liquid
- module Tags
- module Editable
- class Base < ::Liquid::Block
-
- Syntax = /(#{::Liquid::QuotedFragment})(\s*,\s*#{::Liquid::Expression}+)?/
-
- attr_accessor :slug
-
- def initialize(tag_name, markup, tokens, context)
- if markup =~ Syntax
- @slug = $1.gsub(/[\"\']/, '')
- @options = { fixed: false }
- markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/^[\"\']/, '').gsub(/[\"\']$/, '') }
- else
- raise ::Liquid::SyntaxError.new("Syntax Error in 'editable_xxx' - Valid syntax: editable_xxx <slug>(, <options>)")
- end
+ module Steam
+ module Liquid
+ module Tags
+ module Editable
+ class Base < ::Liquid::Block
- super
- end
+ Syntax = /(#{::Liquid::QuotedFragment})(\s*,\s*#{::Liquid::Expression}+)?/
- def end_tag
- super
+ attr_accessor :slug
- if @context[:page].present?
- @context[:page].add_or_update_editable_element(default_element_attributes, document_type)
- end
- end
+ def initialize(tag_name, markup, options)
+ if markup =~ Syntax
+ @slug = $1.gsub(/[\"\']/, '')
+ @element_options = { fixed: false }
+ markup.scan(::Liquid::TagAttributes) { |key, value| @element_options[key.to_sym] = value.gsub(/^[\"\']/, '').gsub(/[\"\']$/, '') }
+ else
+ raise ::Liquid::SyntaxError.new("Valid syntax: #{tag_name} <slug>(, <options>)")
+ end
- def render(context)
- current_page = context.registers[:page]
+ super
+ end
- element = current_page.find_editable_element(context['block'].try(:name), @slug)
+ def parse(tokens)
+ super
- if element.present?
- render_element(context, element)
- else
- Locomotive.log :error, "[editable element] missing element `#{context['block'].try(:name)}` / #{@slug} on #{current_page.fullpath}"
- ''
+ if listener = options[:events_listener]
+ listener.emit(@tag_name.to_sym, page: options[:page], attributes: default_element_attributes)
+ end
end
- end
- protected
-
- def default_element_attributes
- {
- block: self.current_block_name,
- slug: @slug,
- hint: @options[:hint],
- priority: @options[:priority] || 0,
- fixed: !!@options[:fixed],
- disabled: false,
- from_parent: false,
- _type: self.document_type.to_s
- }
- end
+ def render(context)
+ repository = context.registers[:services].repositories.page
+ page = context.registers[:page]
+ block = context['block'].try(:name)
- def render_element(element)
- raise 'FIXME: has to be overidden'
- end
+ if element = repository.editable_element_for(page, block, @slug)
+ render_element(context, element)
+ else
+ Locomotive::Common::Logger.error "[#{page.fullpath}] missing #{@tag_name} \"#{@slug}\" (#{context['block'].try(:name) || 'default'})"
+ ''
+ end
+ end
- def document_type
- raise 'FIXME: has to be overidden'
- end
+ protected
+
+ def default_element_attributes
+ {
+ block: self.current_inherited_block_name,
+ slug: @slug,
+ hint: @element_options[:hint],
+ priority: @element_options[:priority] || 0,
+ fixed: !!@element_options[:fixed],
+ disabled: false,
+ inline_editing: true,
+ from_parent: false
+ }
+ end
- def current_block_name
- @options[:block] || @context[:current_block].try(:name)
- end
+ def current_inherited_block_name
+ @element_options[:block] || current_inherited_block.try(:name)
+ end
- def render_default_content(context)
- begin
- if @nodelist.all? { |n| n.is_a? String }
- context ||= ::Liquid::Context.new
+ def current_inherited_block
+ options[:inherited_blocks].try(:[], :nested).try(:last)
+ end
- render_all(@nodelist, context)
- else
- raise ::Liquid::SyntaxError.new("Error in the #{self.current_block_name || 'default'} block for the #{@slug} editable_element - No liquid tags are allowed inside.")
+ def render_default_content(context)
+ begin
+ if nodelist.all? { |n| n.is_a? String }
+ context ||= ::Liquid::Context.new
+ @body.render(context)
+ else
+ raise ::Liquid::SyntaxError.new("No liquid tags are allowed inside the \"#{@slug}\" #{@tag_name} (#{current_inherited_block_name || 'default'})")
+ end
end
end
+
+ #:nocov:
+ def render_element(element)
+ raise 'FIXME: has to be overidden'
+ end
+
end
end
-
end
end
end
locomotive/steam/liquid/tags/editable/text.rb b/lib/locomotive/steam/liquid/tags/editable/text.rb +46 -58
@@ @@ -1,78 +1,66 @@
module Locomotive
- module Liquid
- module Tags
- module Editable
- class Text < Base
+ module Steam
+ module Liquid
+ module Tags
+ module Editable
+ class Text < Base
- protected
+ protected
- def render_element(context, element)
- content = element.default_content? ? render_default_content(context) : element.content
-
- if self.editable?(context, element)
- self.render_editable_element(element, content)
- else
- content
+ def render_element(context, element)
+ with_inline_editing(context, element) do
+ if element.default_content?
+ render_default_content(context)
+ else
+ element.content
+ end
+ end
end
- end
- def render_editable_element(element, content)
- tag_name = 'div'
- css = 'editable-text'
+ def with_inline_editing(context, element, &block)
+ if editable?(context, element)
+ %{<span class="locomotive-editable-text" data-element-id="#{element.id}">#{yield}</span>}
+ else
+ yield
+ end
+ end
- unless element.line_break?
- tag_name = 'span'
- css += ' editable-single-text'
+ def editable?(context, element)
+ !!context['inline_editing'] && element.inline_editing?
end
- %{
- <#{tag_name} class='#{css}' data-element-id='#{element.id}' data-element-index='#{element._index}'>
- #{content}
- </#{tag_name}>
- }
- end
+ def default_element_attributes
+ super.merge(
+ content_from_default: self.render_default_content(nil),
+ format: @options[:format] || 'html',
+ rows: @options[:rows] || 10,
+ line_break: @options[:line_break].blank? ? true : @options[:line_break]
+ )
+ end
- def document_type
- EditableText
end
- def editable?(context, element)
- context.registers[:inline_editor] &&
- %(raw html).include?(element.format) &&
- (!element.fixed? || (element.fixed? && !element.from_parent?))
- end
+ ::Liquid::Template.register_tag('editable_text'.freeze, Text)
- def default_element_attributes
- super.merge(
- content_from_default: self.render_default_content(nil),
- format: @options[:format] || 'html',
- rows: @options[:rows] || 10,
- line_break: @options[:line_break].blank? ? true : @options[:line_break]
- )
+ class ShortText < Text
+ def initialize(tag_name, markup, options)
+ Locomotive::Common::Logger.warn %(The "#{tag_name}" liquid tag is deprecated. Use "editable_text" instead.)
+ super
+ end
+ def default_element_attributes
+ super.merge(format: 'raw', rows: 2, line_break: false)
+ end
end
+ ::Liquid::Template.register_tag('editable_short_text'.freeze, ShortText)
- end
-
- ::Liquid::Template.register_tag('editable_text', Text)
-
- class ShortText < Text
- def initialize(tag_name, markup, tokens, context)
- Rails.logger.warn %(The "editable_<short|long>_text" liquid tags are deprecated. Use "editable_text" instead.)
- super
- end
- def default_element_attributes
- super.merge(format: 'raw', rows: 2, line_break: false)
+ class LongText < ShortText
+ def default_element_attributes
+ super.merge(format: 'html', rows: 15, line_break: true)
+ end
end
- end
- ::Liquid::Template.register_tag('editable_short_text', ShortText)
+ ::Liquid::Template.register_tag('editable_long_text'.freeze, LongText)
- class LongText < ShortText
- def default_element_attributes
- super.merge(format: 'html', rows: 15, line_break: true)
- end
end
- ::Liquid::Template.register_tag('editable_long_text', LongText)
-
end
end
end
locomotive/steam/repositories/page.rb b/lib/locomotive/steam/repositories/page.rb +4 -0
@@ @@ -16,6 +16,10 @@ module Locomotive
site.pages.where(fullpath: path).first
end
+ def editable_element_for(page, block, slug)
+ page.editable_elements.where(block: block, slug: slug).first
+ end
+
end
end
locomotivecms_steam.gemspec +1 -1
@@ @@ -43,7 +43,7 @@ Gem::Specification.new do |spec|
# spec.add_dependency 'locomotivecms_models', '~> 0.0.1.pre.alpha'
- spec.add_dependency 'locomotivecms-solid', '~> 4.0.0.alpha1'
+ spec.add_dependency 'locomotivecms-solid', '~> 4.0.0.alpha2'
spec.add_dependency 'locomotivecms_common', '~> 0.0.2'
# spec.required_ruby_version = '~> 2.0'
spec/support/liquid.rb +2 -2
@@ @@ -1,7 +1,7 @@
- def render_template(source, context = nil)
+ def render_template(source, context = nil, options = {})
context ||= ::Liquid::Context.new
context.exception_handler = ->(e) { true }
- ::Liquid::Template.parse(source).render(context)
+ ::Liquid::Template.parse(source, options).render(context)
end
def parse_template(source, options = nil)
spec/unit/liquid/tags/consume_spec.rb +9 -9
@@ @@ -2,12 +2,12 @@ require 'spec_helper'
describe Locomotive::Steam::Liquid::Tags::Consume do
- let(:template) { '{% consume blog from "http://blog.locomotiveapp.org" %}{% endconsume %}' }
+ let(:source) { '{% consume blog from "http://blog.locomotiveapp.org" %}{% endconsume %}' }
let(:assigns) { {} }
let(:services) { Locomotive::Steam::Services.build_instance }
let(:context) { ::Liquid::Context.new(assigns, {}, { services: services }) }
- subject { render_template(template, context) }
+ subject { render_template(source, context) }
describe 'validating syntax' do
@@ @@ -19,17 +19,17 @@ describe Locomotive::Steam::Liquid::Tags::Consume do
end
describe 'validates more complex syntax with attributes' do
- let(:template) { '{% consume blog from "http://www.locomotiveapp.org", username: "john", password: password_from_context %}{% endconsume %}' }
+ let(:source) { '{% consume blog from "http://www.locomotiveapp.org", username: "john", password: password_from_context %}{% endconsume %}' }
it { expect { subject }.not_to raise_exception }
end
describe 'should parse the correct url with complex syntax with attributes' do
- let(:template) { '{% consume blog from "http://www.locomotiveapp.org" username: "john", password: "easyone" %}{% endconsume %}' }
+ let(:source) { '{% consume blog from "http://www.locomotiveapp.org" username: "john", password: "easyone" %}{% endconsume %}' }
it { expect { subject }.not_to raise_exception }
end
describe 'raises an error if the syntax is incorrect' do
- let(:template) { '{% consume blog http://www.locomotiveapp.org %}{% endconsume %}' }
+ let(:source) { '{% consume blog http://www.locomotiveapp.org %}{% endconsume %}' }
it { expect { subject }.to raise_exception }
end
@@ @@ -42,7 +42,7 @@ describe Locomotive::Steam::Liquid::Tags::Consume do
describe 'assign the response into the liquid variable' do
- let(:template) { "{% consume blog from \"http://blog.locomotiveapp.org/api/read\" %}{{ blog.title }}{% endconsume %}" }
+ let(:source) { "{% consume blog from \"http://blog.locomotiveapp.org/api/read\" %}{{ blog.title }}{% endconsume %}" }
it { is_expected.to eq 'Locomotive rocks!' }
end
@@ @@ -50,7 +50,7 @@ describe Locomotive::Steam::Liquid::Tags::Consume do
describe 'assign the response into the liquid variable using a url from a variable' do
let(:assigns) { { 'url' => 'http://blog.locomotiveapp.org/api/read' } }
- let(:template) { "{% consume blog from url %}{{ blog.title }}{% endconsume %}" }
+ let(:source) { "{% consume blog from url %}{{ blog.title }}{% endconsume %}" }
it { is_expected.to eq 'Locomotive rocks!' }
end
@@ @@ -58,7 +58,7 @@ describe Locomotive::Steam::Liquid::Tags::Consume do
describe 'accept options for the web service' do
let(:assigns) { { 'secret_password' => 'bar' } }
- let(:template) { "{% consume blog from \"http://blog.locomotiveapp.org/api/read\", username: 'foo', password: secret_password %}{{ blog.title }}{% endconsume %}" }
+ let(:source) { "{% consume blog from \"http://blog.locomotiveapp.org/api/read\", username: 'foo', password: secret_password %}{{ blog.title }}{% endconsume %}" }
it { is_expected.to eq 'Locomotive rocks!' }
end
@@ @@ -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(:template) { %{{% 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/editable/editable_text_spec.rb +200 -0
@@ @@ -0,0 +1,200 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Liquid::Tags::Editable::Text do
+
+ let(:page) { instance_double('Page') }
+ let(:listener) { Liquid::SimpleEventsListener.new }
+ let(:options) { { events_listener: listener, page: page } }
+
+ let(:source) { "{% editable_text title, hint: 'Simple short text' %}Hello world{% endeditable_text %}" }
+
+ describe 'parsing' do
+
+ subject { parse_template(source, options) }
+
+ context 'valid syntax' do
+
+ it 'does not raise an error' do
+ expect { subject }.to_not raise_error
+ end
+
+ end
+
+ context 'without a slug' do
+
+ let(:source) { "{% editable_text %}{% endeditable_text %}" }
+
+ it 'requires a slug' do
+ expect { subject }.to raise_error(::Liquid::SyntaxError, "Liquid syntax error: Valid syntax: editable_text <slug>(, <options>)")
+ end
+
+ end
+
+ context 'with a tag or block inside' do
+
+ let(:source) { "{% editable_text title %}{{ test }}{% endeditable_text %}" }
+
+ it 'does not allow it' do
+ expect { subject }.to raise_error(::Liquid::SyntaxError, "Liquid syntax error: No liquid tags are allowed inside the \"title\" editable_text (default)")
+ end
+
+ end
+
+ describe 'events' do
+
+ before { parse_template(source, options) }
+
+ subject { listener.events.first.first }
+
+ it 'records the name of the event' do
+ is_expected.to eq :editable_text
+ end
+
+ describe 'attributes' do
+
+ subject { listener.events.first.last[:attributes] }
+
+ it { is_expected.to include(block: nil) }
+ it { is_expected.to include(slug: 'title') }
+ it { is_expected.to include(hint: 'Simple short text') }
+ it { is_expected.to include(format: 'html') }
+ it { is_expected.to include(rows: 10) }
+ it { is_expected.to include(line_break: true) }
+ it { is_expected.to include(content_from_default: 'Hello world') }
+
+ end
+
+ end
+
+ end
+
+ describe 'rendering' do
+
+ let(:inline_editing) { false }
+
+ let(:page) { instance_double('Page', fullpath: 'hello-world') }
+ let(:element) { instance_double('Text', id: 42, default_content?: true, inline_editing?: true) }
+ let(:services) { Locomotive::Steam::Services.build_instance(nil) }
+ let(:context) { ::Liquid::Context.new({ 'inline_editing' => inline_editing }, {}, { page: page, services: services }) }
+
+ before { allow(services.repositories.page).to receive(:editable_element_for).and_return(element) }
+
+ subject { render_template(source, context, options) }
+
+ it { is_expected.to eq 'Hello world' }
+
+ context 'no element found' do
+
+ let(:element) { nil }
+ it { is_expected.to eq '' }
+
+ end
+
+ context 'modified content' do
+
+ let(:element) { instance_double('Text', content: 'Hello world!', default_content?: false) }
+ it { is_expected.to eq 'Hello world!' }
+
+ end
+
+ context 'inside blocks' do
+
+ let(:source) { '{% block wrapper %}{% block sidebar %}{% editable_text title %}Hello world{% endeditable_text %}{% endblock %}{% endblock %}' }
+
+ before { expect(services.repositories.page).to receive(:editable_element_for).with(page, 'wrapper/sidebar', 'title').and_return(element) }
+ it { is_expected.to eq 'Hello world' }
+
+ end
+
+ context 'inline-editing mode' do
+
+ let(:inline_editing) { true }
+ it { is_expected.to eq '<span class="locomotive-editable-text" data-element-id="42">Hello world</span>' }
+
+ end
+
+ describe 'deprecated elements' do
+
+ let(:listener) { Liquid::SimpleEventsListener.new }
+ let(:options) { { events_listener: listener } }
+
+ describe 'deprecated editable_long_text' do
+
+ let(:source) { "{% editable_long_text body %}Hello world{% endeditable_long_text %}" }
+ it { is_expected.to eq 'Hello world' }
+
+ end
+
+ describe 'deprecated editable_short_text' do
+
+ let(:source) { "{% editable_short_text title %}Hello world{% endeditable_short_text %}" }
+ it { is_expected.to eq 'Hello world' }
+
+ end
+
+ end
+
+ end
+
+
+
+ # let(:content) { '' }
+ # subject { build_tag('text', content).send(:default_element_attributes) }
+
+ # it { is_expected.to include(slug: 'title') }
+ # it { is_expected.to include(hint: 'Simple short text') }
+ # it { is_expected.to include(_type: 'Locomotive::EditableText') }
+ # it { is_expected.to include(format: 'html') }
+ # it { is_expected.to include(rows: 10) }
+ # it { is_expected.to include(line_break: true) }
+
+ # describe 'default content' do
+
+ # context 'only text' do
+
+ # let(:content) { 'Lorem ipsum' }
+ # it { is_expected.to include(content_from_default: 'Lorem ipsum') }
+
+ # end
+
+ # context 'liquid tags' do
+
+ # let(:content) { ['hello ', ::Liquid::Variable.new("{{ 'world' }}")] }
+ # it { expect { subject }.to raise_error(::Liquid::SyntaxError, "Error in the default block for the title editable_element - No liquid tags are allowed inside.") }
+
+ # end
+
+ # end
+
+ # context 'editable_short_text' do
+
+ # subject { build_tag('short_text').send(:default_element_attributes) }
+
+ # it { is_expected.to include(format: 'raw') }
+ # it { is_expected.to include(rows: 2) }
+ # it { is_expected.to include(line_break: false) }
+
+ # end
+
+ # context 'editable_long_text' do
+
+ # subject { build_tag('long_text').send(:default_element_attributes) }
+
+ # it { is_expected.to include(format: 'html') }
+ # it { is_expected.to include(rows: 15) }
+ # it { is_expected.to include(line_break: true) }
+
+ # end
+
+ # end
+
+ # def build_tag(tag_name = 'text', content = nil)
+ # klass = "Locomotive::Liquid::Tags::Editable::#{tag_name.camelize}".constantize
+ # klass.new("editable_#{tag_name}", markup, ["{% endeditable_#{tag_name} %}"], {}).tap do |tag|
+ # tag.instance_variable_set(:@nodelist, [*content]) if content
+ # end
+ # end
+
+ # end
+
+ end
spec/unit/liquid/tags/fetch_page_spec.rb +3 -3
@@ @@ -2,7 +2,7 @@ require 'spec_helper'
describe Locomotive::Steam::Liquid::Tags::FetchPage do
- let(:template) { "{% fetch_page about_us as a_page %}{{ a_page.title }}" }
+ let(:source) { "{% fetch_page about_us as a_page %}{{ a_page.title }}" }
let(:assigns) { {} }
let(:repositories) { Locomotive::Steam::Services.build_instance.repositories }
let(:context) { ::Liquid::Context.new(assigns, {}, { repositories: repositories }) }
@@ @@ -10,14 +10,14 @@ describe Locomotive::Steam::Liquid::Tags::FetchPage do
let(:page) { instance_double('Page', to_liquid: { 'title' => 'About Us' }) }
before { allow(repositories.page).to receive(:by_handle).and_return(page) }
- subject { render_template(template, context) }
+ subject { render_template(source, context) }
describe 'validating syntax' do
it { expect { subject }.not_to raise_exception }
describe 'raises an error if the syntax is incorrect' do
- let(:template) { "{% fetch_page 'about_us' %}{{ a_page.title }}" }
+ let(:source) { "{% fetch_page 'about_us' %}{{ a_page.title }}" }
it { expect { subject }.to raise_exception }
end
spec/unit/liquid/tags/google_analytics_spec.rb +3 -3
@@ @@ -2,14 +2,14 @@ require 'spec_helper'
describe Locomotive::Steam::Liquid::Tags::GoogleAnalytics do
- let(:template) { "{% google_analytics 42 %}" }
+ let(:source) { "{% google_analytics 42 %}" }
- subject { render_template(template) }
+ subject { render_template(source) }
it { is_expected.to include "_gaq.push(['_setAccount', '42']);" }
describe 'raises an error if the syntax is incorrect' do
- let(:template) { '{% google_analytics %}' }
+ let(:source) { '{% google_analytics %}' }
it { expect { subject }.to raise_exception }#(::Liquid::SyntaxError) }
end