antwort 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +249 -0
- data/Gemfile +3 -0
- data/Guardfile +14 -0
- data/README.md +108 -0
- data/Rakefile +14 -0
- data/antwort.gemspec +45 -0
- data/bin/antwort +5 -0
- data/lib/antwort.rb +13 -0
- data/lib/antwort/builder.rb +8 -0
- data/lib/antwort/builder/builder.rb +104 -0
- data/lib/antwort/builder/email.rb +61 -0
- data/lib/antwort/builder/flattener.rb +37 -0
- data/lib/antwort/builder/helpers/logic.rb +82 -0
- data/lib/antwort/builder/helpers/sanitizers.rb +29 -0
- data/lib/antwort/builder/partial.rb +80 -0
- data/lib/antwort/builder/style.rb +59 -0
- data/lib/antwort/cli.rb +7 -0
- data/lib/antwort/cli/cli.rb +275 -0
- data/lib/antwort/cli/helpers.rb +44 -0
- data/lib/antwort/cli/send.rb +79 -0
- data/lib/antwort/cli/upload.rb +89 -0
- data/lib/antwort/helpers.rb +19 -0
- data/lib/antwort/server.rb +70 -0
- data/lib/antwort/server/assets.rb +23 -0
- data/lib/antwort/server/helpers.rb +67 -0
- data/lib/antwort/server/markup.rb +39 -0
- data/lib/antwort/version.rb +3 -0
- data/spec/builder/builder_spec.rb +30 -0
- data/spec/builder/email_spec.rb +21 -0
- data/spec/builder/flattener_spec.rb +64 -0
- data/spec/builder/helpers_logic_spec.rb +244 -0
- data/spec/builder/partial_spec.rb +87 -0
- data/spec/builder/style_spec.rb +66 -0
- data/spec/cli/helpers_spec.rb +60 -0
- data/spec/cli/upload_spec.rb +47 -0
- data/spec/cli_spec.rb +46 -0
- data/spec/fixtures/assets/images/1-demo/placeholder.png +0 -0
- data/spec/fixtures/assets/images/newsletter/placeholder.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-grey.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-white.png +0 -0
- data/spec/fixtures/build/demo-123456/build.html +7 -0
- data/spec/fixtures/build/demo-123457/build.html +7 -0
- data/spec/fixtures/build/demo-bar-123/build.html +7 -0
- data/spec/fixtures/build/foo-1/build.html +7 -0
- data/spec/fixtures/data/1-demo.yml +3 -0
- data/spec/fixtures/emails/1-demo/index.html.erb +9 -0
- data/spec/fixtures/emails/2-no-layout/index.html.erb +6 -0
- data/spec/fixtures/emails/3-no-title/index.html.erb +1 -0
- data/spec/fixtures/views/404.html.erb +1 -0
- data/spec/fixtures/views/index.html.erb +14 -0
- data/spec/fixtures/views/layout.erb +8 -0
- data/spec/fixtures/views/server.erb +5 -0
- data/spec/server_spec.rb +54 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/capture.rb +17 -0
- data/template/email/css/include.scss +5 -0
- data/template/email/css/inline.scss +5 -0
- data/template/email/email.html.erb +11 -0
- data/template/email/images/.empty_directory +0 -0
- data/template/project/.env.sample +21 -0
- data/template/project/.gitignore.tt +17 -0
- data/template/project/.ruby-version +1 -0
- data/template/project/Gemfile.tt +9 -0
- data/template/project/Guardfile +9 -0
- data/template/project/assets/css/demo/include.scss +3 -0
- data/template/project/assets/css/demo/inline.scss +33 -0
- data/template/project/assets/css/server.scss +167 -0
- data/template/project/assets/css/shared/_base.scss +64 -0
- data/template/project/assets/css/shared/_mixins.scss +25 -0
- data/template/project/assets/css/shared/_reset.scss +59 -0
- data/template/project/assets/css/shared/_vars.scss +12 -0
- data/template/project/assets/css/shared/include.scss +23 -0
- data/template/project/assets/css/shared/inline.scss +9 -0
- data/template/project/assets/images/.gitkeep +0 -0
- data/template/project/assets/images/shared/placeholder.png +0 -0
- data/template/project/build/.empty_directory +0 -0
- data/template/project/data/.empty_directory +0 -0
- data/template/project/data/config.yml +3 -0
- data/template/project/data/demo.yml +4 -0
- data/template/project/emails/demo/_partial.html.erb +9 -0
- data/template/project/emails/demo/index.html.erb +54 -0
- data/template/project/views/404.html.erb +8 -0
- data/template/project/views/index.html.erb +18 -0
- data/template/project/views/layout.erb +38 -0
- data/template/project/views/markup/_button.html.erb +5 -0
- data/template/project/views/markup/_image_tag.html.erb +10 -0
- data/template/project/views/server.erb +32 -0
- metadata +443 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Antwort
|
4
|
+
class CLI
|
5
|
+
class Upload < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
include Antwort::CLIHelpers
|
8
|
+
attr_reader :email_id
|
9
|
+
|
10
|
+
def initialize(email_id, force = false)
|
11
|
+
@force = force
|
12
|
+
@email_id = email_id
|
13
|
+
@images_dir = images_dir(email_id)
|
14
|
+
check_credentials
|
15
|
+
end
|
16
|
+
|
17
|
+
no_commands do
|
18
|
+
|
19
|
+
def check_credentials
|
20
|
+
failed = false
|
21
|
+
vars = ['ASSET_SERVER', 'AWS_BUCKET', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']
|
22
|
+
vars.each do |var|
|
23
|
+
if ENV[var].nil?
|
24
|
+
say "Error ", :red
|
25
|
+
say "#{var} not set."
|
26
|
+
failed = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if failed
|
31
|
+
abort "Please fix your .env config file and try again."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def upload
|
36
|
+
count = count_files @images_dir
|
37
|
+
abort "No images in #{@images_dir} to upload." if count === 0
|
38
|
+
|
39
|
+
if confirms_upload?(count)
|
40
|
+
do_upload
|
41
|
+
else
|
42
|
+
say 'Upload aborted. ', :red
|
43
|
+
say 'No files deleted or replaced.'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def confirms_upload?(count)
|
48
|
+
yes?("Upload #{count} images and overwrite '#{email_id}' folder on assets server? (y/n)")
|
49
|
+
end
|
50
|
+
|
51
|
+
def do_upload
|
52
|
+
clean_directory!
|
53
|
+
Dir.foreach(@images_dir) do |f|
|
54
|
+
next if f.to_s[0] == '.'
|
55
|
+
directory.files.create(
|
56
|
+
key: "#{email_id}/#{f}",
|
57
|
+
body: File.open(File.join(@images_dir, f)),
|
58
|
+
public: true
|
59
|
+
)
|
60
|
+
say ' create ', :green
|
61
|
+
say "#{ENV['ASSET_SERVER']}/#{email_id}/#{f}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def connection
|
66
|
+
@connection ||=
|
67
|
+
Fog::Storage.new(
|
68
|
+
provider: 'AWS',
|
69
|
+
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
70
|
+
aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
|
71
|
+
region: ENV['FOG_REGION']
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def directory
|
76
|
+
return @directory if defined?(@directory)
|
77
|
+
|
78
|
+
@directory ||=
|
79
|
+
connection.directories.get(ENV['AWS_BUCKET'], prefix: email_id)
|
80
|
+
end
|
81
|
+
|
82
|
+
def clean_directory!
|
83
|
+
directory.files.each(&:destroy)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Antwort
|
2
|
+
module Helpers
|
3
|
+
def symbolize_keys!(hash)
|
4
|
+
hash.reduce({}) do |result, (key, value)|
|
5
|
+
new_key = case key
|
6
|
+
when String then key.to_sym
|
7
|
+
else key
|
8
|
+
end
|
9
|
+
new_value = case value
|
10
|
+
when Hash then symbolize_keys!(value)
|
11
|
+
when Array then value.map { |v| v.is_a?(Hash) ? symbolize_keys!(v) : v }
|
12
|
+
else value
|
13
|
+
end
|
14
|
+
result[new_key] = new_value
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'antwort/server/assets'
|
2
|
+
require 'antwort/server/helpers'
|
3
|
+
require 'antwort/server/markup'
|
4
|
+
require 'sinatra/base'
|
5
|
+
require 'sinatra/partial'
|
6
|
+
require 'sinatra/content_for'
|
7
|
+
require 'rack-livereload'
|
8
|
+
require 'sinatra/reloader'
|
9
|
+
require 'ostruct'
|
10
|
+
|
11
|
+
module Antwort
|
12
|
+
class Server < Sinatra::Base
|
13
|
+
use Rack::LiveReload
|
14
|
+
Tilt.register Tilt::ERBTemplate, 'html.erb'
|
15
|
+
register Sinatra::Partial
|
16
|
+
register Sinatra::Reloader
|
17
|
+
helpers Sinatra::ContentFor
|
18
|
+
helpers Antwort::Helpers
|
19
|
+
helpers Antwort::ApplicationHelpers
|
20
|
+
helpers Antwort::MarkupHelpers
|
21
|
+
|
22
|
+
configure do
|
23
|
+
enable :logging
|
24
|
+
set :root, Dir.pwd
|
25
|
+
set :root, Dir.pwd + '/spec/fixtures/' if ENV['RACK_ENV'] == 'test'
|
26
|
+
set :views, settings.root
|
27
|
+
set :templates_dir, settings.root + '/emails'
|
28
|
+
set :partial_template_engine, :erb
|
29
|
+
enable :partial_underscores
|
30
|
+
set :port, 9292
|
31
|
+
end
|
32
|
+
|
33
|
+
register Sinatra::Assets # after we set root
|
34
|
+
|
35
|
+
get '/' do
|
36
|
+
pages = Dir.entries(settings.templates_dir)
|
37
|
+
pages.delete_if { |page| page.to_s[0] == '.' }
|
38
|
+
@pages = Array.new
|
39
|
+
pages.each do |p|
|
40
|
+
path = p.split('.').first
|
41
|
+
@pages.push({
|
42
|
+
path: path,
|
43
|
+
title: get_page_title(path)
|
44
|
+
})
|
45
|
+
end
|
46
|
+
erb :'views/index', layout: :'views/server'
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/template/:template' do
|
50
|
+
@config = OpenStruct.new(fetch_data_yaml('config'))
|
51
|
+
@template = sanitize_param params[:template]
|
52
|
+
|
53
|
+
if template_exists? @template
|
54
|
+
content = read_template @template
|
55
|
+
hash_to_instance_vars content[:metadata]
|
56
|
+
hash_to_instance_vars fetch_data_yaml(@template)
|
57
|
+
layout = @layout.nil? ? :'views/layout' : @layout
|
58
|
+
erb content[:body], layout: layout
|
59
|
+
else
|
60
|
+
status 404
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
not_found do
|
65
|
+
content_type 'text/html'
|
66
|
+
erb :'views/404', layout: :'views/server'
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/extension'
|
3
|
+
require 'sprockets'
|
4
|
+
require 'sass'
|
5
|
+
|
6
|
+
module Sinatra
|
7
|
+
module Assets
|
8
|
+
extend Sinatra::Extension
|
9
|
+
|
10
|
+
configure do
|
11
|
+
set :assets, assets = Sprockets::Environment.new(root)
|
12
|
+
assets.append_path 'assets/css'
|
13
|
+
assets.append_path 'assets/images'
|
14
|
+
|
15
|
+
assets.cache = Sprockets::Cache::FileStore.new('./tmp')
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/assets/*' do
|
19
|
+
env['PATH_INFO'].sub!(%r{^/assets}, '')
|
20
|
+
settings.assets.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Antwort
|
2
|
+
module ApplicationHelpers
|
3
|
+
|
4
|
+
def sanitize_param(string)
|
5
|
+
string.nil? ? '' : string.gsub(/([^A-Za-z0-9_\/-]+)|(--)/, '_')
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_template_file(template_name)
|
9
|
+
# Dir.entries("your/folder").select {|f| !File.directory? f}
|
10
|
+
"#{settings.views}/emails/#{template_name}/index.html.erb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_page_title(template_name)
|
14
|
+
get_metadata(template_name)[:title] || 'Untitled'
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_metadata(template_name)
|
18
|
+
read_template(template_name)[:metadata]
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_template(template_name)
|
22
|
+
file = get_template_file(template_name)
|
23
|
+
|
24
|
+
data = File.read(file)
|
25
|
+
md = data.match(/^(?<metadata>---\s*\n.*?\n?)^(---\s*$\n?)/m)
|
26
|
+
{
|
27
|
+
body: (md.nil?) ? data : md.post_match,
|
28
|
+
metadata: (md.nil?) ? {} : symbolize_keys!(YAML.load(md[:metadata]))
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_data_yaml(template_name)
|
33
|
+
data = {}
|
34
|
+
data_file = settings.root + '/data/' + template_name + '.yml'
|
35
|
+
if File.file? data_file
|
36
|
+
data = YAML.load_file(data_file)
|
37
|
+
data = symbolize_keys! data if data
|
38
|
+
end
|
39
|
+
data
|
40
|
+
end
|
41
|
+
|
42
|
+
def template_exists?(template_name)
|
43
|
+
File.file? settings.templates_dir + '/' + template_name + '/index.html.erb'
|
44
|
+
end
|
45
|
+
|
46
|
+
def template_from_path
|
47
|
+
request.path_info.gsub(%r{/template/}i, '')
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash_to_instance_vars(data)
|
51
|
+
data.each { |k, v| instance_variable_set("@#{k}", v) } if data
|
52
|
+
end
|
53
|
+
|
54
|
+
def image_url_from_path(path)
|
55
|
+
p = path.split(':')[0]
|
56
|
+
if (p == 'http' || p == 'https')
|
57
|
+
url = path
|
58
|
+
else
|
59
|
+
a = [path]
|
60
|
+
a.unshift(template_from_path) unless path[0] == '/'
|
61
|
+
a.unshift('/assets')
|
62
|
+
url = File.join(a)
|
63
|
+
end
|
64
|
+
url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Antwort
|
2
|
+
module MarkupHelpers
|
3
|
+
def image_tag(path, options = {})
|
4
|
+
options[:source] = image_url_from_path(path)
|
5
|
+
options[:alt] ||= ''
|
6
|
+
partial('views/markup/image_tag', locals: options)
|
7
|
+
.gsub(/\n/, '')
|
8
|
+
end
|
9
|
+
|
10
|
+
def button(text, url, args = {})
|
11
|
+
options = {
|
12
|
+
text: text,
|
13
|
+
url: url
|
14
|
+
}.merge(args)
|
15
|
+
partial('views/markup/button', locals: options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def wrapper_table(args = {}, &block)
|
19
|
+
options = {
|
20
|
+
width: '100%',
|
21
|
+
cell_align: 'center',
|
22
|
+
cell_valign: 'top',
|
23
|
+
content: capture_html(&block)
|
24
|
+
}.merge(args)
|
25
|
+
|
26
|
+
concat_content partial('views/markup/table_wrapper', locals: options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def counter_classes(index)
|
30
|
+
# 0 index based
|
31
|
+
klass = ''
|
32
|
+
klass += index.even? ? ' is-2n' : ''
|
33
|
+
klass += (index % 3 == 0) ? ' is-3n' : ''
|
34
|
+
klass += (index % 4 == 0) ? ' is-4n' : ''
|
35
|
+
klass += (index % 6 == 0) ? ' is-6n' : ''
|
36
|
+
klass
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Antwort::Builder do
|
4
|
+
before :each do
|
5
|
+
@builder = Antwort::Builder.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Initialize" do
|
9
|
+
it "has a template_name"
|
10
|
+
it "has a build_id"
|
11
|
+
it "has a markup_dir"
|
12
|
+
it "has a source_dir"
|
13
|
+
it "has a scss_dir"
|
14
|
+
it "has an asset_server"
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Compiles" do
|
18
|
+
it "SCSS"
|
19
|
+
it "inline.scss"
|
20
|
+
it "include.scss"
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Helpers" do
|
24
|
+
it "creates files"
|
25
|
+
it "creates IDs from Timestamps"
|
26
|
+
it "changes relative assets URLs to use asset server"
|
27
|
+
it "preserves ` `s"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Antwort::EmailBuilder do
|
4
|
+
before :each do
|
5
|
+
@builder = Antwort::EmailBuilder.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Builds" do
|
9
|
+
it "builds HTML"
|
10
|
+
it "inlines CSS"
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Helpers" do
|
14
|
+
it "can inline CSS"
|
15
|
+
it "cleans up markup"
|
16
|
+
it "removes livereload"
|
17
|
+
it "adds in the included CSS"
|
18
|
+
it "removes excessive newlines"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Antwort::Flattener do
|
4
|
+
|
5
|
+
describe "initialize" do
|
6
|
+
it "saves source to a attr_reader" do
|
7
|
+
s = Antwort::Flattener.new('<td>')
|
8
|
+
expect(s.source).to eq('<td>')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "saves matches to an array" do
|
12
|
+
it "matches single style per source" do
|
13
|
+
s = Antwort::Flattener.new('<div style="color:black">')
|
14
|
+
expect(s.styles).to eq(['color:black'])
|
15
|
+
end
|
16
|
+
it "matches multiple styles per source" do
|
17
|
+
s = Antwort::Flattener.new('<td style="background-color:#cccccc"><div style="color: red">')
|
18
|
+
expect(s.styles).to eq(['background-color:#cccccc', 'color: red'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "flattens css styles" do
|
24
|
+
it "a single match" do
|
25
|
+
s = Antwort::Flattener.new('<div style="color:black;color:red;background:white;">').flatten
|
26
|
+
expect(s.flattened).to eq('<div style="color:red;background:white">')
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "multiple styles" do
|
30
|
+
let (:style) { Antwort::Flattener.new('<td style="color:black;color:red;"><div style="background:white;">').flatten }
|
31
|
+
|
32
|
+
it "stores original source as a string" do
|
33
|
+
expect(style.source).to eq('<td style="color:black;color:red;"><div style="background:white;">')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "stores flattened source as a string" do
|
37
|
+
expect(style.flattened).to eq('<td style="color:red"><div style="background:white">')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "method flattened?" do
|
42
|
+
it "returns true if changes where made" do
|
43
|
+
s = Antwort::Flattener.new('<td style="color: red; color: black;">').flatten
|
44
|
+
expect(s.flattened?).to eq(true)
|
45
|
+
end
|
46
|
+
it "returns false if chnages were not made" do
|
47
|
+
s = Antwort::Flattener.new('<td style"color:red">').flatten
|
48
|
+
expect(s.flattened?).to eq(false)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "code cleanup" do
|
54
|
+
let (:style) { Antwort::Flattener.new('<td style="color: black;">').flatten }
|
55
|
+
|
56
|
+
it "removes trailing semicolon;" do
|
57
|
+
expect(style.flattened).to eq('<td style="color:black">')
|
58
|
+
expect(style.flattened).not_to eq('<td style="color:black;">')
|
59
|
+
end
|
60
|
+
it "remove extra white space" do
|
61
|
+
expect(style.flattened).not_to eq('<td style="color: black">')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Dummy
|
4
|
+
end
|
5
|
+
|
6
|
+
describe Antwort::LogicHelpers do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@helper = Dummy.new
|
10
|
+
@helper.extend(Antwort::LogicHelpers)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "non breaking spaces" do
|
14
|
+
it "preserves by adjusting markup" do
|
15
|
+
expect(@helper.preserve_nbsps('foo bar')).to eq('foo%nbspace%bar')
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can restore to proper markup" do
|
19
|
+
expect(@helper.restore_nbsps('foo%nbspace%bar')).to eq('foo bar')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "statements" do
|
24
|
+
it "preserves erb markup" do
|
25
|
+
expect(@helper.preserve_logic('<% user.merge!({clear: true}) %>')).to eq('{% user.merge!({clear: true}) %}')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#-- Comments --
|
30
|
+
|
31
|
+
describe "comments" do
|
32
|
+
it "preserves erb comments" do
|
33
|
+
h = {
|
34
|
+
"<%# foo %>" => "{# foo #}",
|
35
|
+
"<% # foo %>" => "{# foo #}",
|
36
|
+
"<% #foo %>" => "{# foo #}",
|
37
|
+
"<%#= foo %>" => "{#= foo #}",
|
38
|
+
"<%# foo bar == cat %>" => "{# foo bar == cat #}"
|
39
|
+
}
|
40
|
+
h.each do |key, value|
|
41
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "preserves comments despite leading/trailing spaces" do
|
46
|
+
expect(@helper.preserve_logic('<% # foo %>')).to eq('{# foo #}')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#-- Variables --
|
51
|
+
|
52
|
+
describe "variable outputs" do
|
53
|
+
it "preserves interpolated strings" do
|
54
|
+
expect(@helper.preserve_logic('<%= "color: #{foo};" %>')).to eq('{{ "color: #{foo};" }}')
|
55
|
+
end
|
56
|
+
|
57
|
+
context "hashes" do
|
58
|
+
it "preserves nested" do
|
59
|
+
expect(@helper.preserve_logic('<%= foo[:bar] %>')).to eq('{{ foo.bar }}')
|
60
|
+
expect(@helper.preserve_logic('<%= foo[:bar][:cat] %>')).to eq('{{ foo.bar.cat }}')
|
61
|
+
end
|
62
|
+
|
63
|
+
it "preseves string keys" do
|
64
|
+
expect(@helper.preserve_logic("<%= foo['bar'] %>")).to eq('{{ foo.bar }}')
|
65
|
+
expect(@helper.preserve_logic("<%= foo['bar']['cat'] %>")).to eq('{{ foo.bar.cat }}')
|
66
|
+
expect(@helper.preserve_logic('<%= foo["bar"] %>')).to eq('{{ foo.bar }}')
|
67
|
+
expect(@helper.preserve_logic('<%= foo["bar"]["cat"] %>')).to eq('{{ foo.bar.cat }}')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#-- Assignments --
|
73
|
+
|
74
|
+
describe "preserves assignments" do
|
75
|
+
|
76
|
+
describe "incl. variables" do
|
77
|
+
it "well-formatted code" do
|
78
|
+
expect(@helper.preserve_logic("<% foo = bar %>")).to eq("{% set foo = bar %}")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "not well-formatted code" do
|
82
|
+
expect(@helper.preserve_logic("<% foo=bar %>")).to eq("{% set foo = bar %}")
|
83
|
+
expect(@helper.preserve_logic("<% foo =bar %>")).to eq("{% set foo = bar %}")
|
84
|
+
expect(@helper.preserve_logic("<% foo=bar %>")).to eq("{% set foo = bar %}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "incl. strings" do
|
89
|
+
expect(@helper.preserve_logic('<% foo = "bar" %>')).to eq('{% set foo = "bar" %}')
|
90
|
+
expect(@helper.preserve_logic("<% foo = 'bar' %>")).to eq("{% set foo = 'bar' %}")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "incl. integers" do
|
94
|
+
expect(@helper.preserve_logic('<% foo = 1 %>')).to eq('{% set foo = 1 %}')
|
95
|
+
expect(@helper.preserve_logic('<% foo=1 %>')).to eq('{% set foo = 1 %}')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "incl. arrays" do
|
99
|
+
h = {
|
100
|
+
"<% foo = [1] %>" => "{% set foo = [1] %}",
|
101
|
+
"<% foo = [ 1 ] %>" => "{% set foo = [ 1 ] %}",
|
102
|
+
"<% foo = [1,2] %>" => "{% set foo = [1,2] %}",
|
103
|
+
"<% foo = [1, 2, 3] %>" => "{% set foo = [1, 2, 3] %}"
|
104
|
+
}
|
105
|
+
h.each do |key, value|
|
106
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "incl. objects/hashes" do
|
111
|
+
expect(@helper.preserve_logic('<% foo = { key: val } %>')).to eq('{% set foo = { key: val } %}')
|
112
|
+
end
|
113
|
+
|
114
|
+
it "respects memoized assignemnts" do
|
115
|
+
expect(@helper.preserve_logic('<% foo ||= "bar" %>')).to eq('{% set foo = foo || "bar" %}')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#-- Conditionals --
|
120
|
+
|
121
|
+
describe "conditionals" do
|
122
|
+
describe "if" do
|
123
|
+
it "/ end" do
|
124
|
+
h = {
|
125
|
+
'<% if foo %>do something<% end %>' => '{% if foo %}do something{% end %}',
|
126
|
+
'<%if foo%>do something<%end%>' => '{% if foo %}do something{% end %}',
|
127
|
+
'<% if foo=bar %>do something<% end %>' => '{% if foo=bar %}do something{% end %}',
|
128
|
+
'<% if foo = \'bar\' %>do something<% end %>' => '{% if foo = \'bar\' %}do something{% end %}',
|
129
|
+
'<% if foo = "bar" %>do something<% end %>' => '{% if foo = "bar" %}do something{% end %}',
|
130
|
+
'<% if i < @config.products.length - 1 %>' => '{% if i < @config.products.length - 1 %}',
|
131
|
+
'<% if i<@config.products.length-1 %>' => '{% if i<@config.products.length-1 %}'
|
132
|
+
}
|
133
|
+
h.each do |key, value|
|
134
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "/ else / end" do
|
139
|
+
expect(@helper.preserve_logic('<% if foo %>bar<% else %>cat<% end %>')).to eq('{% if foo %}bar{% else %}cat{% end %}')
|
140
|
+
end
|
141
|
+
|
142
|
+
it "/ elsif / else / end" do
|
143
|
+
expect(@helper.preserve_logic('<% if foo = bar %>bar<% elsif foo = cat %>cat<% else %>dog<% end %>')).to eq('{% if foo = bar %}bar{% elseif foo = cat %}cat{% else %}dog{% end %}')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "unless" do
|
148
|
+
it "/ end" do
|
149
|
+
expect(@helper.preserve_logic('<% unless foo %>')).to eq('{% if !( foo ) %}')
|
150
|
+
expect(@helper.preserve_logic('<% unless foo == bar - 1 %>')).to eq('{% if !( foo == bar - 1 ) %}')
|
151
|
+
end
|
152
|
+
|
153
|
+
it "/ else / end" do
|
154
|
+
expect(@helper.preserve_logic('<% unless foo == bar %>this<% else %>that<% end %>')).to eq('{% if !( foo == bar ) %}this{% else %}that{% end %}')
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#-- Loops --
|
160
|
+
|
161
|
+
describe "loops" do
|
162
|
+
it "preserves each loops as for loops" do
|
163
|
+
h = {
|
164
|
+
"<% cats.each do |cat| %>" => "{% for cat in cats %}",
|
165
|
+
"<% cats.each do | cat | %>" => "{% for cat in cats %}",
|
166
|
+
"<% end %>" => "{% end %}"
|
167
|
+
}
|
168
|
+
h.each do |key, value|
|
169
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "preserves each_with_index" do
|
174
|
+
h = {
|
175
|
+
"<% cats.each_with_index do | cat , i | %>" => "{% for cat in cats with: {@index: i} %}",
|
176
|
+
"<% cats.each_with_index do |cat, i| %>" => "{% for cat in cats with: {@index: i} %}",
|
177
|
+
"<% cats.each_with_index do |cat,i| %>" => "{% for cat in cats with: {@index: i} %}"
|
178
|
+
}
|
179
|
+
h.each do |key, value|
|
180
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
#-- Helpers --
|
186
|
+
|
187
|
+
describe "Helpers use statements syntax, not output" do
|
188
|
+
it "includes button" do
|
189
|
+
expect(@helper.preserve_logic("<%= button 'foo', '#' %>")).to eq("{% button 'foo', '#' %}")
|
190
|
+
expect(@helper.preserve_logic("<%= button 'foo', '#', color: 'blue' %>")).to eq("{% button 'foo', '#', color: 'blue' %}")
|
191
|
+
end
|
192
|
+
|
193
|
+
it "includes image_tag" do
|
194
|
+
expect(@helper.preserve_logic("<%= image_tag 'photo.jpg', width: 100, alt: 'caption' %>")).to eq("{% image_tag 'photo.jpg', width: 100, alt: 'caption' %}")
|
195
|
+
expect(@helper.preserve_logic('<%= image_tag "#{user}-photo.jpg", width: 100, alt: "caption" %>')).to eq('{% image_tag "#{user}-photo.jpg", width: 100, alt: "caption" %}')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
#-- Partials --
|
200
|
+
|
201
|
+
describe "partials are converted to friendlier include syntax" do
|
202
|
+
it "with locals" do
|
203
|
+
expect(@helper.preserve_logic("{{ partial :'foo', locals: bar }}")).to eq("{% include 'foo' with: bar %}")
|
204
|
+
expect(@helper.preserve_logic("{{ partial :'foo', locals:bar }}")).to eq("{% include 'foo' with:bar %}")
|
205
|
+
expect(@helper.preserve_logic("{{ partial :'foo', locals: {cat: cat} }}")).to eq("{% include 'foo' with: {cat: cat} %}")
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
it "including multiline" do
|
210
|
+
markup = %q(
|
211
|
+
<%= partial :'user', locals: {
|
212
|
+
name: 'foo',
|
213
|
+
size: bar
|
214
|
+
} %>
|
215
|
+
)
|
216
|
+
result = %q(
|
217
|
+
{% include 'user' with: {
|
218
|
+
name: 'foo',
|
219
|
+
size: bar
|
220
|
+
} %}
|
221
|
+
)
|
222
|
+
|
223
|
+
expect(@helper.preserve_logic(markup)).to eq(result)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "without locals" do
|
227
|
+
expect(@helper.preserve_logic("{{ partial :'foo' }}")).to eq("{% include 'foo' %}")
|
228
|
+
expect(@helper.preserve_logic("{{ partial :foo }}")).to eq("{% include foo %}")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "multiple statements on one line" do
|
233
|
+
it "does not match too early" do
|
234
|
+
h = {
|
235
|
+
"<% if %>this<% else %>that<%end%>" => "{% if %}this{% else %}that{% end %}",
|
236
|
+
"<% if i==0 %><%= partial :foo %><% else %><%= partial :bar %><% end %>" => "{% if i==0 %}{% include foo %}{% else %}{% include bar %}{% end %}"
|
237
|
+
}
|
238
|
+
h.each do |key, value|
|
239
|
+
expect(@helper.preserve_logic(key)).to eq(value)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|