txbr 2.4.1 → 2.5.0
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 +4 -4
- data/README.md +1 -1
- data/lib/txbr/campaign.rb +9 -1
- data/lib/txbr/content_tag.rb +9 -5
- data/lib/txbr/email_template.rb +1 -2
- data/lib/txbr/liquid/connected_content_tag.rb +12 -27
- data/lib/txbr/template.rb +30 -3
- data/lib/txbr/version.rb +1 -1
- data/spec/campaign_spec.rb +27 -32
- data/spec/template_spec.rb +27 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 159bfa45f2d0f53ef30fe11e2bd6dfbf1b07b0ea0e19530ca3ac3d171308c4ac
|
|
4
|
+
data.tar.gz: 15621955923af3e6aa17933faf75eb344e5be7a8bd34fe52ea526d1f88a01be3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7247f257169835737f3d834d8afc0cba5930314e439a3629acfa526b4f3d7197eece08194c3f85a848a17932a3aea22540a9386a79699d286f860c140cd8f9c7
|
|
7
|
+
data.tar.gz: 9416bc14041f5b8b261ecad1de0e8aa7ccaf61d3ddc11297cc6d3aa907cf14c029ae9c3e37b45d53325ed8c2bc6e87af44b580189fae59aa5f2cfb3dbddb70fc
|
data/README.md
CHANGED
data/lib/txbr/campaign.rb
CHANGED
|
@@ -27,6 +27,12 @@ module Txbr
|
|
|
27
27
|
{}
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def prerender_variables
|
|
31
|
+
@prerender_variables ||= {
|
|
32
|
+
'campaign.${api_id}' => campaign_id
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
private
|
|
31
37
|
|
|
32
38
|
def template_group
|
|
@@ -38,7 +44,9 @@ module Txbr
|
|
|
38
44
|
TEMPLATE_KEYS.each_with_object([]) do |key, ret|
|
|
39
45
|
begin
|
|
40
46
|
if message = props[key]
|
|
41
|
-
ret << Txbr::Template.new(
|
|
47
|
+
ret << Txbr::Template.new(
|
|
48
|
+
message_id, message, prerender_variables
|
|
49
|
+
)
|
|
42
50
|
end
|
|
43
51
|
rescue => e
|
|
44
52
|
Txgh.events.publish_error!(e, metadata.merge(template_key: key))
|
data/lib/txbr/content_tag.rb
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
require 'liquid'
|
|
2
|
+
require 'cgi'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
1
5
|
module Txbr
|
|
2
6
|
class ContentTag
|
|
3
|
-
attr_reader :
|
|
7
|
+
attr_reader :template, :liquid_tag
|
|
4
8
|
|
|
5
|
-
def initialize(
|
|
6
|
-
@
|
|
9
|
+
def initialize(template, liquid_tag)
|
|
10
|
+
@template = template
|
|
7
11
|
@liquid_tag = liquid_tag
|
|
8
12
|
end
|
|
9
13
|
|
|
@@ -14,14 +18,14 @@ module Txbr
|
|
|
14
18
|
# set local variables for the project and resource slugs. It's less
|
|
15
19
|
# error-prone to let Liquid do the template evaluation instead of
|
|
16
20
|
# grepping through the nodelist looking for assignment statements.
|
|
17
|
-
|
|
21
|
+
template.render
|
|
18
22
|
liquid_tag.metadata
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def strings_manifest
|
|
23
27
|
@strings_manifest ||= StringsManifest.new.tap do |manifest|
|
|
24
|
-
extract_strings_from(
|
|
28
|
+
extract_strings_from(template.root, manifest)
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
|
data/lib/txbr/email_template.rb
CHANGED
|
@@ -37,8 +37,7 @@ module Txbr
|
|
|
37
37
|
def templates
|
|
38
38
|
TEMPLATE_KEYS.each_with_object([]) do |name, ret|
|
|
39
39
|
begin
|
|
40
|
-
|
|
41
|
-
ret << Txbr::Template.new(email_template_id, liquid_tmpl)
|
|
40
|
+
ret << Txbr::Template.new(email_template_id, details[name])
|
|
42
41
|
rescue => e
|
|
43
42
|
Txgh.events.publish_error!(e, metadata.merge(template_key: name))
|
|
44
43
|
end
|
|
@@ -23,51 +23,36 @@ module Txbr
|
|
|
23
23
|
# template might contain {{strings.bar}}, which would print
|
|
24
24
|
# out "baz".
|
|
25
25
|
PARSE_RE = /(:#{IDENTIFIER})\s+(#{IDENTIFIER})/
|
|
26
|
-
ASSIGNS_RE = /\{\{(?:(?!\}\}).)*\}\}/
|
|
27
26
|
|
|
28
27
|
attr_reader :tag_name, :url, :arguments
|
|
29
28
|
|
|
30
|
-
def initialize(tag_name, arg,
|
|
29
|
+
def initialize(tag_name, arg, _parse_context = nil)
|
|
31
30
|
@tag_name = tag_name
|
|
32
31
|
@url, @arguments = parse_arg(arg)
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
# this method is called inside Txbr::ContentTag#metadata
|
|
36
|
-
def render(context)
|
|
37
|
-
@metadata_hash =
|
|
38
|
-
Metadata::ASSIGNMENTS.each_with_object({}) do |assignment, ret|
|
|
39
|
-
ret[assignment] = context[assignment]
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
34
|
def prefix
|
|
44
35
|
# we want to blow up if "save" isn't present
|
|
45
36
|
arguments.fetch('save').first
|
|
46
37
|
end
|
|
47
38
|
|
|
48
|
-
def
|
|
49
|
-
@
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Metadata.new(
|
|
53
|
-
query_hash
|
|
54
|
-
.each_with_object({}) { |(k, v), ret| ret[k] = v.first }
|
|
55
|
-
.merge('prefix' => prefix)
|
|
56
|
-
)
|
|
57
|
-
end
|
|
39
|
+
def render(context)
|
|
40
|
+
@url = ::Liquid::Template.parse(url).render(context)
|
|
41
|
+
''
|
|
58
42
|
end
|
|
59
43
|
|
|
60
|
-
|
|
44
|
+
def metadata
|
|
45
|
+
uri = URI.parse(url)
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@metadata_hash[assign.gsub(/\A\{\{|\}\}\z/, '')]
|
|
67
|
-
end
|
|
47
|
+
Metadata.new(
|
|
48
|
+
CGI.parse(uri.query)
|
|
49
|
+
.each_with_object({}) { |(k, v), ret| ret[k] = v.first }
|
|
50
|
+
.merge('prefix' => prefix)
|
|
68
51
|
)
|
|
69
52
|
end
|
|
70
53
|
|
|
54
|
+
private
|
|
55
|
+
|
|
71
56
|
def parse_arg(arg)
|
|
72
57
|
url, *components = arg.split(PARSE_RE)
|
|
73
58
|
|
data/lib/txbr/template.rb
CHANGED
|
@@ -2,9 +2,12 @@ module Txbr
|
|
|
2
2
|
class Template
|
|
3
3
|
attr_reader :id, :liquid_template
|
|
4
4
|
|
|
5
|
-
def initialize(id,
|
|
5
|
+
def initialize(id, source, prerender_variables = {})
|
|
6
6
|
@id = id
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
@liquid_template = ::Liquid::Template.parse(
|
|
9
|
+
prerender(source, prerender_variables)
|
|
10
|
+
)
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
def each_content_tag
|
|
@@ -16,6 +19,14 @@ module Txbr
|
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
|
|
22
|
+
def render
|
|
23
|
+
liquid_template.render
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def root
|
|
27
|
+
liquid_template.root
|
|
28
|
+
end
|
|
29
|
+
|
|
19
30
|
private
|
|
20
31
|
|
|
21
32
|
def translation_enabled?
|
|
@@ -39,7 +50,7 @@ module Txbr
|
|
|
39
50
|
|
|
40
51
|
def content_tags
|
|
41
52
|
@content_tags ||= connected_content_tags.each_with_object([]) do |tag, ret|
|
|
42
|
-
tag = ContentTag.new(
|
|
53
|
+
tag = ContentTag.new(self, tag)
|
|
43
54
|
ret << tag if tag.contains_translations?
|
|
44
55
|
end
|
|
45
56
|
end
|
|
@@ -51,5 +62,21 @@ module Txbr
|
|
|
51
62
|
node.is_a?(Txbr::Liquid::ConnectedContentTag)
|
|
52
63
|
end
|
|
53
64
|
end
|
|
65
|
+
|
|
66
|
+
# Designed to replace special Braze variables like {{campaign.${api_id}}},
|
|
67
|
+
# which Liquid can't natively handle. If the variable exists in the given
|
|
68
|
+
# variables hash, replace it with the value directly. Otherwise, transform
|
|
69
|
+
# the var into something Liquid-friendly so it doesn't jam up the parser.
|
|
70
|
+
def prerender(source, variables)
|
|
71
|
+
source.gsub(/\{\{[\w\-\.\[\]]+\.\$\{[\w\-\.\[\]]+\}\}\}/) do |orig|
|
|
72
|
+
orig = orig[2..-3] # remove curlies
|
|
73
|
+
next variables[orig] if variables.include?(orig)
|
|
74
|
+
"{{#{normalize_braze_var(orig)}}}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_braze_var(var)
|
|
79
|
+
"__braze__#{var.gsub(/[^\w\-\.\[\]]/, '')}"
|
|
80
|
+
end
|
|
54
81
|
end
|
|
55
82
|
end
|
data/lib/txbr/version.rb
CHANGED
data/spec/campaign_spec.rb
CHANGED
|
@@ -10,28 +10,26 @@ describe Txbr::Campaign do
|
|
|
10
10
|
|
|
11
11
|
let(:first_message) do
|
|
12
12
|
<<~MESSAGE
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{{footer.company | default: 'Megamarketing Corp'}}
|
|
13
|
+
{% assign project_slug = "my_project" %}
|
|
14
|
+
{% assign translation_enabled = true %}
|
|
15
|
+
{% connected_content http://my_strings_api.com?project_slug={{project_slug}}&resource_slug={{campaign.${api_id}}} :save strings %}
|
|
16
|
+
{% connected_content http://my_strings_api.com?project_slug=my_project&resource_slug=my_footer_resource :save footer %}
|
|
17
|
+
|
|
18
|
+
{{strings.header | default: 'Buy our stuff!'}}
|
|
19
|
+
{% if user.gets_discount? %}
|
|
20
|
+
{{strings.discount | default: 'You get a discount'}}
|
|
21
|
+
{% else %}
|
|
22
|
+
{{strings.no_discount | default: 'You get no discount'}}
|
|
23
|
+
{% endif %}
|
|
24
|
+
{{footer.company | default: 'Megamarketing Corp'}}
|
|
26
25
|
MESSAGE
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
let(:second_message) do
|
|
30
29
|
<<~HTML
|
|
31
30
|
{% assign project_slug = "my_project" %}
|
|
32
|
-
{% assign resource_slug = "my_resource" %}
|
|
33
31
|
{% assign translation_enabled = true %}
|
|
34
|
-
{% connected_content http://my_strings_api.com?project_slug={{project_slug}}&resource_slug={{
|
|
32
|
+
{% connected_content http://my_strings_api.com?project_slug={{project_slug}}&resource_slug={{campaign.${api_id}}} :save strings %}
|
|
35
33
|
{{strings.meta.subject_line | default: 'You lucky duck maybe'}}
|
|
36
34
|
HTML
|
|
37
35
|
end
|
|
@@ -60,7 +58,7 @@ describe Txbr::Campaign do
|
|
|
60
58
|
it 'extracts and groups all strings with the same project, resource, and prefix' do
|
|
61
59
|
resource = campaign.each_resource.to_a.first
|
|
62
60
|
expect(resource.tx_resource.project_slug).to eq('my_project')
|
|
63
|
-
expect(resource.tx_resource.resource_slug).to eq(
|
|
61
|
+
expect(resource.tx_resource.resource_slug).to eq(campaign_id)
|
|
64
62
|
|
|
65
63
|
# notice how it combined strings from both messages,
|
|
66
64
|
expect(resource.phrases).to eq([
|
|
@@ -76,7 +74,7 @@ describe Txbr::Campaign do
|
|
|
76
74
|
tx_resource = resource.tx_resource
|
|
77
75
|
|
|
78
76
|
expect(tx_resource.project_slug).to eq('my_project')
|
|
79
|
-
expect(tx_resource.resource_slug).to eq(
|
|
77
|
+
expect(tx_resource.resource_slug).to eq(campaign_id)
|
|
80
78
|
expect(tx_resource.source_file).to eq('World Domination')
|
|
81
79
|
expect(tx_resource.source_lang).to eq(project.source_lang)
|
|
82
80
|
expect(tx_resource.type).to eq(project.strings_format)
|
|
@@ -107,29 +105,26 @@ describe Txbr::Campaign do
|
|
|
107
105
|
end
|
|
108
106
|
|
|
109
107
|
context 'when the message comes from a separate resource' do
|
|
110
|
-
let(:first_message) do
|
|
111
|
-
super().tap do |subj|
|
|
112
|
-
subj.sub!(
|
|
113
|
-
'resource_slug = "my_resource"',
|
|
114
|
-
'resource_slug = "my_other_resource"'
|
|
115
|
-
)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
108
|
it 'includes the additional resource' do
|
|
120
109
|
resources = campaign.each_resource.to_a
|
|
121
|
-
expect(resources.size).to eq(
|
|
110
|
+
expect(resources.size).to eq(2)
|
|
122
111
|
|
|
123
112
|
expect(resources.first.phrases).to_not(
|
|
124
|
-
include({ 'key' => '
|
|
113
|
+
include({ 'key' => 'company', 'string' => 'Megamarketing Corp' })
|
|
125
114
|
)
|
|
126
115
|
|
|
127
|
-
expect(resources.last.phrases).to
|
|
128
|
-
|
|
116
|
+
expect(resources.last.phrases).to(
|
|
117
|
+
include({ 'key' => 'company', 'string' => 'Megamarketing Corp' })
|
|
129
118
|
)
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
|
|
120
|
+
first = resources.first.tx_resource
|
|
121
|
+
second = resources.last.tx_resource
|
|
122
|
+
|
|
123
|
+
expect(first.project_slug).to eq('my_project')
|
|
124
|
+
expect(first.resource_slug).to eq(campaign_id)
|
|
125
|
+
|
|
126
|
+
expect(second.project_slug).to eq('my_project')
|
|
127
|
+
expect(second.resource_slug).to eq('my_footer_resource')
|
|
133
128
|
end
|
|
134
129
|
end
|
|
135
130
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Txbr::Template do
|
|
4
|
+
subject { described_class.new(id, source, prerender_variables) }
|
|
5
|
+
|
|
6
|
+
let(:prerender_variables) { {} }
|
|
7
|
+
let(:id) { 'foobar' }
|
|
8
|
+
let(:source) do
|
|
9
|
+
<<~SRC
|
|
10
|
+
<h1>Braze campaign is {{campaign.${api_id}}}</h1>
|
|
11
|
+
SRC
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'with a direct variable replacement' do
|
|
15
|
+
let(:prerender_variables) { { 'campaign.${api_id}' => 'abc123' } }
|
|
16
|
+
|
|
17
|
+
it 'prerenders correctly' do
|
|
18
|
+
expect(subject.render.strip).to eq("<h1>Braze campaign is abc123</h1>")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'with no variable replacements' do
|
|
23
|
+
it 'renders without erroring' do
|
|
24
|
+
expect { subject.render }.to_not raise_error
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: txbr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cameron Dutro
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-11-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: abroad
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- spec/support/fake_connection.rb
|
|
156
156
|
- spec/support/standard_setup.rb
|
|
157
157
|
- spec/support/test_config.rb
|
|
158
|
+
- spec/template_spec.rb
|
|
158
159
|
- spec/uploader_spec.rb
|
|
159
160
|
- spec/utils_spec.rb
|
|
160
161
|
- txbr.gemspec
|