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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd81fe81e5e0d795e842a3cf75c4277d0ba5e7211214596d2755835994d27435
4
- data.tar.gz: 6a40e039b54a45ba5271525dbc27fea9294eadc7d0580e38f87c3f1d8579f6aa
3
+ metadata.gz: 159bfa45f2d0f53ef30fe11e2bd6dfbf1b07b0ea0e19530ca3ac3d171308c4ac
4
+ data.tar.gz: 15621955923af3e6aa17933faf75eb344e5be7a8bd34fe52ea526d1f88a01be3
5
5
  SHA512:
6
- metadata.gz: 6d314adee43b7c9450db5a230e6e8b3a35205ed764f102f875c9967e01205e8d19bef938715d64aa1c065f91b7cf9a7a014733e14eff6c0b711a004f58e4a363
7
- data.tar.gz: bbf37aa158dd6c518c472f7f10214648c1c6496437ba9373e14e52fe987c196d42738dc9d639c637d68c796cd6eb67e2cced94f3b5b3c755757e95accedd477d
6
+ metadata.gz: 7247f257169835737f3d834d8afc0cba5930314e439a3629acfa526b4f3d7197eece08194c3f85a848a17932a3aea22540a9386a79699d286f860c140cd8f9c7
7
+ data.tar.gz: 9416bc14041f5b8b261ecad1de0e8aa7ccaf61d3ddc11297cc6d3aa907cf14c029ae9c3e37b45d53325ed8c2bc6e87af44b580189fae59aa5f2cfb3dbddb70fc
data/README.md CHANGED
@@ -135,7 +135,7 @@ Txbr requires an Internet connection to access the Transifex and Braze APIs.
135
135
  Compatibility
136
136
  ---
137
137
 
138
- Txdb requires Ruby 2.5.
138
+ Txbr requires Ruby 2.5.
139
139
 
140
140
  Authors
141
141
  ---
@@ -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(message_id, ::Liquid::Template.parse(message))
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))
@@ -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 :liquid_template, :liquid_tag
7
+ attr_reader :template, :liquid_tag
4
8
 
5
- def initialize(liquid_template, liquid_tag)
6
- @liquid_template = liquid_template
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
- liquid_template.render
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(liquid_template.root, manifest)
28
+ extract_strings_from(template.root, manifest)
25
29
  end
26
30
  end
27
31
 
@@ -37,8 +37,7 @@ module Txbr
37
37
  def templates
38
38
  TEMPLATE_KEYS.each_with_object([]) do |name, ret|
39
39
  begin
40
- liquid_tmpl = ::Liquid::Template.parse(details[name])
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, _context = nil)
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 metadata
49
- @metadata ||= begin
50
- query_hash = CGI.parse(uri.query)
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
- private
44
+ def metadata
45
+ uri = URI.parse(url)
61
46
 
62
- def uri
63
- @uri ||= URI.parse(
64
- url.gsub(ASSIGNS_RE) do |assign|
65
- # remove curlies from beginning and end, look up in assigns hash
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
 
@@ -2,9 +2,12 @@ module Txbr
2
2
  class Template
3
3
  attr_reader :id, :liquid_template
4
4
 
5
- def initialize(id, liquid_template)
5
+ def initialize(id, source, prerender_variables = {})
6
6
  @id = id
7
- @liquid_template = liquid_template
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(liquid_template, tag)
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
@@ -1,3 +1,3 @@
1
1
  module Txbr
2
- VERSION = '2.4.1'
2
+ VERSION = '2.5.0'
3
3
  end
@@ -10,28 +10,26 @@ describe Txbr::Campaign do
10
10
 
11
11
  let(:first_message) do
12
12
  <<~MESSAGE
13
- {% assign project_slug = "my_project" %}
14
- {% assign resource_slug = "my_resource" %}
15
- {% assign translation_enabled = true %}
16
- {% connected_content http://my_strings_api.com?project_slug={{project_slug}}&resource_slug={{resource_slug}} :save strings %}
17
- {% connected_content http://my_strings_api.com?project_slug=my_project&resource_slug=my_footer_resource :save footer %}
18
-
19
- {{strings.header | default: 'Buy our stuff!'}}
20
- {% if user.gets_discount? %}
21
- {{strings.discount | default: 'You get a discount'}}
22
- {% else %}
23
- {{strings.no_discount | default: 'You get no discount'}}
24
- {% endif %}
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={{resource_slug}} :save strings %}
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('my_resource')
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('my_resource')
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(3)
110
+ expect(resources.size).to eq(2)
122
111
 
123
112
  expect(resources.first.phrases).to_not(
124
- include({ 'key' => 'meta.subject_line', 'string' => 'You lucky duck maybe' })
113
+ include({ 'key' => 'company', 'string' => 'Megamarketing Corp' })
125
114
  )
126
115
 
127
- expect(resources.last.phrases).to eq(
128
- [{ 'key' => 'meta.subject_line', 'string' => 'You lucky duck maybe' }]
116
+ expect(resources.last.phrases).to(
117
+ include({ 'key' => 'company', 'string' => 'Megamarketing Corp' })
129
118
  )
130
119
 
131
- expect(resources.first.tx_resource.project_slug).to eq('my_project')
132
- expect(resources.first.tx_resource.resource_slug).to eq('my_other_resource')
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.1
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-09-16 00:00:00.000000000 Z
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