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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +19 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +3 -0
  8. data/Guardfile +14 -0
  9. data/README.md +108 -0
  10. data/Rakefile +14 -0
  11. data/antwort.gemspec +45 -0
  12. data/bin/antwort +5 -0
  13. data/lib/antwort.rb +13 -0
  14. data/lib/antwort/builder.rb +8 -0
  15. data/lib/antwort/builder/builder.rb +104 -0
  16. data/lib/antwort/builder/email.rb +61 -0
  17. data/lib/antwort/builder/flattener.rb +37 -0
  18. data/lib/antwort/builder/helpers/logic.rb +82 -0
  19. data/lib/antwort/builder/helpers/sanitizers.rb +29 -0
  20. data/lib/antwort/builder/partial.rb +80 -0
  21. data/lib/antwort/builder/style.rb +59 -0
  22. data/lib/antwort/cli.rb +7 -0
  23. data/lib/antwort/cli/cli.rb +275 -0
  24. data/lib/antwort/cli/helpers.rb +44 -0
  25. data/lib/antwort/cli/send.rb +79 -0
  26. data/lib/antwort/cli/upload.rb +89 -0
  27. data/lib/antwort/helpers.rb +19 -0
  28. data/lib/antwort/server.rb +70 -0
  29. data/lib/antwort/server/assets.rb +23 -0
  30. data/lib/antwort/server/helpers.rb +67 -0
  31. data/lib/antwort/server/markup.rb +39 -0
  32. data/lib/antwort/version.rb +3 -0
  33. data/spec/builder/builder_spec.rb +30 -0
  34. data/spec/builder/email_spec.rb +21 -0
  35. data/spec/builder/flattener_spec.rb +64 -0
  36. data/spec/builder/helpers_logic_spec.rb +244 -0
  37. data/spec/builder/partial_spec.rb +87 -0
  38. data/spec/builder/style_spec.rb +66 -0
  39. data/spec/cli/helpers_spec.rb +60 -0
  40. data/spec/cli/upload_spec.rb +47 -0
  41. data/spec/cli_spec.rb +46 -0
  42. data/spec/fixtures/assets/images/1-demo/placeholder.png +0 -0
  43. data/spec/fixtures/assets/images/newsletter/placeholder.png +0 -0
  44. data/spec/fixtures/assets/images/shared/placeholder-grey.png +0 -0
  45. data/spec/fixtures/assets/images/shared/placeholder-white.png +0 -0
  46. data/spec/fixtures/build/demo-123456/build.html +7 -0
  47. data/spec/fixtures/build/demo-123457/build.html +7 -0
  48. data/spec/fixtures/build/demo-bar-123/build.html +7 -0
  49. data/spec/fixtures/build/foo-1/build.html +7 -0
  50. data/spec/fixtures/data/1-demo.yml +3 -0
  51. data/spec/fixtures/emails/1-demo/index.html.erb +9 -0
  52. data/spec/fixtures/emails/2-no-layout/index.html.erb +6 -0
  53. data/spec/fixtures/emails/3-no-title/index.html.erb +1 -0
  54. data/spec/fixtures/views/404.html.erb +1 -0
  55. data/spec/fixtures/views/index.html.erb +14 -0
  56. data/spec/fixtures/views/layout.erb +8 -0
  57. data/spec/fixtures/views/server.erb +5 -0
  58. data/spec/server_spec.rb +54 -0
  59. data/spec/spec_helper.rb +38 -0
  60. data/spec/support/capture.rb +17 -0
  61. data/template/email/css/include.scss +5 -0
  62. data/template/email/css/inline.scss +5 -0
  63. data/template/email/email.html.erb +11 -0
  64. data/template/email/images/.empty_directory +0 -0
  65. data/template/project/.env.sample +21 -0
  66. data/template/project/.gitignore.tt +17 -0
  67. data/template/project/.ruby-version +1 -0
  68. data/template/project/Gemfile.tt +9 -0
  69. data/template/project/Guardfile +9 -0
  70. data/template/project/assets/css/demo/include.scss +3 -0
  71. data/template/project/assets/css/demo/inline.scss +33 -0
  72. data/template/project/assets/css/server.scss +167 -0
  73. data/template/project/assets/css/shared/_base.scss +64 -0
  74. data/template/project/assets/css/shared/_mixins.scss +25 -0
  75. data/template/project/assets/css/shared/_reset.scss +59 -0
  76. data/template/project/assets/css/shared/_vars.scss +12 -0
  77. data/template/project/assets/css/shared/include.scss +23 -0
  78. data/template/project/assets/css/shared/inline.scss +9 -0
  79. data/template/project/assets/images/.gitkeep +0 -0
  80. data/template/project/assets/images/shared/placeholder.png +0 -0
  81. data/template/project/build/.empty_directory +0 -0
  82. data/template/project/data/.empty_directory +0 -0
  83. data/template/project/data/config.yml +3 -0
  84. data/template/project/data/demo.yml +4 -0
  85. data/template/project/emails/demo/_partial.html.erb +9 -0
  86. data/template/project/emails/demo/index.html.erb +54 -0
  87. data/template/project/views/404.html.erb +8 -0
  88. data/template/project/views/index.html.erb +18 -0
  89. data/template/project/views/layout.erb +38 -0
  90. data/template/project/views/markup/_button.html.erb +5 -0
  91. data/template/project/views/markup/_image_tag.html.erb +10 -0
  92. data/template/project/views/server.erb +32 -0
  93. 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,3 @@
1
+ module Antwort
2
+ VERSION = '0.0.12'
3
+ 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 `&nbsp;`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&nbsp;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&nbsp;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