txbr 1.0.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 +7 -0
- data/LICENSE +202 -0
- data/README.md +137 -0
- data/lib/txbr.rb +81 -0
- data/lib/txbr/application.rb +48 -0
- data/lib/txbr/braze_api.rb +42 -0
- data/lib/txbr/braze_session.rb +39 -0
- data/lib/txbr/braze_session_api.rb +73 -0
- data/lib/txbr/commands.rb +14 -0
- data/lib/txbr/config.rb +57 -0
- data/lib/txbr/email_template.rb +70 -0
- data/lib/txbr/email_template_component.rb +94 -0
- data/lib/txbr/email_template_handler.rb +24 -0
- data/lib/txbr/project.rb +50 -0
- data/lib/txbr/request_methods.rb +45 -0
- data/lib/txbr/strings_manifest.rb +55 -0
- data/lib/txbr/tasks.rb +8 -0
- data/lib/txbr/uploader.rb +23 -0
- data/lib/txbr/utils.rb +9 -0
- data/lib/txbr/version.rb +3 -0
- data/spec/application_spec.rb +78 -0
- data/spec/braze_session_api_spec.rb +108 -0
- data/spec/braze_session_spec.rb +49 -0
- data/spec/config_spec.rb +69 -0
- data/spec/email_template_spec.rb +133 -0
- data/spec/fixtures/cassettes/braze_login.yml +324 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/strings_manifest_spec.rb +45 -0
- data/spec/support/env_helpers.rb +13 -0
- data/spec/support/fake_braze_session.rb +14 -0
- data/spec/support/fake_connection.rb +78 -0
- data/spec/support/standard_setup.rb +20 -0
- data/spec/support/test_config.rb +20 -0
- data/spec/uploader_spec.rb +84 -0
- data/spec/utils_spec.rb +17 -0
- data/txbr.gemspec +27 -0
- metadata +190 -0
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
$:.push(File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require 'rack/test'
|
|
5
|
+
require 'pry-byebug'
|
|
6
|
+
require 'txbr'
|
|
7
|
+
require 'txgh'
|
|
8
|
+
require 'cgi'
|
|
9
|
+
require 'vcr'
|
|
10
|
+
|
|
11
|
+
require 'support/env_helpers'
|
|
12
|
+
|
|
13
|
+
FAKE_SESSION_ID = '123abc123abc123abc123abc123abc12'
|
|
14
|
+
|
|
15
|
+
VCR.configure do |config|
|
|
16
|
+
config.cassette_library_dir = 'spec/fixtures/cassettes'
|
|
17
|
+
config.hook_into :webmock
|
|
18
|
+
|
|
19
|
+
%w(BRAZE_EMAIL_ADDRESS BRAZE_PASSWORD).each do |var|
|
|
20
|
+
config.filter_sensitive_data(var) { ENV[var] || var }
|
|
21
|
+
config.filter_sensitive_data(var) { CGI.escape(ENV[var] || var) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
config.before_record do |interaction|
|
|
25
|
+
# Clear out the body for requests to the dashboard, etc. We need to keep
|
|
26
|
+
# the body for the auth request so mechanize can find the login form.
|
|
27
|
+
if %w(/auth).none? { |str| interaction.request.uri.include?(str) }
|
|
28
|
+
interaction.response.body.replace('')
|
|
29
|
+
interaction.response.headers['Content-Length'] = ['0']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# remove session ID from request cookie header
|
|
33
|
+
interaction.request.headers.fetch('Cookie', []).each do |header|
|
|
34
|
+
header.sub!(/_session_id=[a-z0-9]{32}/, "_session_id=#{FAKE_SESSION_ID}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# remove session ID from response set cookie headers
|
|
38
|
+
interaction.response.headers.fetch('Set-Cookie', []).each do |header|
|
|
39
|
+
header.sub!(/_session_id=[a-z0-9]{32}/, "_session_id=#{FAKE_SESSION_ID}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
RSpec.configure do |config|
|
|
45
|
+
config.include(EnvHelpers)
|
|
46
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Txbr::StringsManifest do
|
|
4
|
+
let(:manifest) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#add' do
|
|
7
|
+
it 'adds the string with the given path' do
|
|
8
|
+
manifest.add(%w(foo bar), 'baz')
|
|
9
|
+
expect(manifest.to_h).to eq(
|
|
10
|
+
{ 'foo' => { 'bar' => 'baz' } }
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'nests correctly' do
|
|
15
|
+
manifest.add(%w(foo bar), 'baz')
|
|
16
|
+
manifest.add(%w(foo baz boo), 'bizz')
|
|
17
|
+
expect(manifest.to_h).to eq(
|
|
18
|
+
{ 'foo' => { 'bar' => 'baz', 'baz' => { 'boo' => 'bizz' } } }
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe '#merge!' do
|
|
24
|
+
it 'merges the strings from another manifest into this one' do
|
|
25
|
+
other = described_class.new
|
|
26
|
+
other.add(%w(foo bar), 'baz')
|
|
27
|
+
manifest.add(%w(foo baz boo), 'bizz')
|
|
28
|
+
manifest.merge!(other)
|
|
29
|
+
expect(manifest.to_h).to eq(
|
|
30
|
+
{ 'foo' => { 'bar' => 'baz', 'baz' => { 'boo' => 'bizz' } } }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#each' do
|
|
36
|
+
it 'yields each path and corresponding value' do
|
|
37
|
+
manifest.add(%w(foo bar), 'baz')
|
|
38
|
+
manifest.add(%w(foo baz boo), 'bizz')
|
|
39
|
+
|
|
40
|
+
expect(manifest.each.to_a).to(
|
|
41
|
+
eq([[['foo', 'bar'], 'baz'], [['foo', 'baz', 'boo'], 'bizz']])
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module EnvHelpers
|
|
2
|
+
def with_env(env)
|
|
3
|
+
# ENV can't be duped, so use select instead to make a copy
|
|
4
|
+
old_env = ENV.select { true }
|
|
5
|
+
env.each_pair { |k, v| ENV[k] = v }
|
|
6
|
+
yield
|
|
7
|
+
ensure
|
|
8
|
+
# reset back to old vars
|
|
9
|
+
env.each_pair { |k, _| ENV[k] = old_env[k] }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
EnvHelpers.extend(EnvHelpers)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class FakeBrazeSession
|
|
2
|
+
attr_reader :api_url, :session_id, :was_reset
|
|
3
|
+
alias reset? was_reset
|
|
4
|
+
|
|
5
|
+
def initialize(api_url, session_id)
|
|
6
|
+
@api_url = api_url
|
|
7
|
+
@session_id = session_id
|
|
8
|
+
@was_reset = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def reset!
|
|
12
|
+
@was_reset = true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
class FakeEnv
|
|
2
|
+
attr_reader :url
|
|
3
|
+
|
|
4
|
+
def initialize(url)
|
|
5
|
+
@url = url
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class FakeResponse
|
|
10
|
+
attr_reader :env, :status, :body, :headers
|
|
11
|
+
|
|
12
|
+
def initialize(url, status, body, headers)
|
|
13
|
+
@env = FakeEnv.new(url)
|
|
14
|
+
@status = status
|
|
15
|
+
@body = body
|
|
16
|
+
@headers = headers
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class FakeConnection
|
|
21
|
+
class UnexpectedRequestError < StandardError; end
|
|
22
|
+
|
|
23
|
+
attr_reader :interactions
|
|
24
|
+
|
|
25
|
+
# of the form:
|
|
26
|
+
# [{
|
|
27
|
+
# request: { verb: 'get', url: 'foo.com', params: { ... }, body: '...' },
|
|
28
|
+
# response: { status: 200, body: '...', headers: { ... } }
|
|
29
|
+
# }]
|
|
30
|
+
def initialize(interactions)
|
|
31
|
+
@interactions = interactions
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def get(url, params = {}, _headers = {})
|
|
35
|
+
idx, interaction = find_interaction('get', url)
|
|
36
|
+
raise UnexpectedRequestError, url unless interaction
|
|
37
|
+
|
|
38
|
+
if interaction_params = interaction[:request][:params]
|
|
39
|
+
raise UnexpectedRequestError, url unless params == interaction_params
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
interactions.delete_at(idx)
|
|
43
|
+
response_for(url, interaction)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def post(url, body = '', _headers = {})
|
|
47
|
+
idx, interaction = find_interaction('post', url)
|
|
48
|
+
raise UnexpectedRequestError, url unless interaction
|
|
49
|
+
|
|
50
|
+
if interaction_body = interaction[:request][:body]
|
|
51
|
+
raise UnexpectedRequestError, url unless body == interaction_body
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
interactions.delete_at(idx)
|
|
55
|
+
response_for(url, interaction)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def response_for(url ,interaction)
|
|
61
|
+
FakeResponse.new(
|
|
62
|
+
url,
|
|
63
|
+
interaction[:response][:status],
|
|
64
|
+
interaction[:response][:body],
|
|
65
|
+
interaction[:response][:headers]
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def find_interaction(verb, url)
|
|
70
|
+
idx = interactions.find_index do |interaction|
|
|
71
|
+
request = interaction[:request]
|
|
72
|
+
request[:verb] == verb && request[:url] == url
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return [nil, nil] unless idx
|
|
76
|
+
[idx, interactions[idx]]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'txgh'
|
|
2
|
+
require 'support/fake_braze_session'
|
|
3
|
+
require 'support/fake_connection'
|
|
4
|
+
require 'support/test_config'
|
|
5
|
+
|
|
6
|
+
shared_context 'standard setup' do
|
|
7
|
+
let(:session_id) { 'session_id' }
|
|
8
|
+
let(:app_group_id) { config[:braze_app_group_id] }
|
|
9
|
+
let(:braze_connection) { FakeConnection.new(braze_interactions) }
|
|
10
|
+
let(:transifex_connection) { FakeConnection.new(transifex_interactions) }
|
|
11
|
+
let(:braze_session) { FakeBrazeSession.new(config[:braze_api_url], session_id) }
|
|
12
|
+
let(:braze_api) { Txbr::BrazeSessionApi.new(braze_session, app_group_id, connection: braze_connection) }
|
|
13
|
+
let(:transifex_api) { Txgh::TransifexApi.create_from_connection(transifex_connection) }
|
|
14
|
+
let(:config) { TestConfig.config[:projects].first }
|
|
15
|
+
let(:project) { Txbr::Project.new(config.merge(braze_api: braze_api, transifex_api: transifex_api)) }
|
|
16
|
+
|
|
17
|
+
# override these
|
|
18
|
+
let(:braze_interactions) { [] }
|
|
19
|
+
let(:transifex_interactions) { [] }
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class TestConfig
|
|
2
|
+
def self.config
|
|
3
|
+
{
|
|
4
|
+
transifex_api_username: 'transifex_username',
|
|
5
|
+
transifex_api_password: 'transifex_password',
|
|
6
|
+
projects: [{
|
|
7
|
+
handler_id: 'email-templates',
|
|
8
|
+
braze_api_key: 'braze_api_key',
|
|
9
|
+
braze_api_url: 'https://somewhere.braze.com',
|
|
10
|
+
strings_format: 'YML',
|
|
11
|
+
source_lang: 'en',
|
|
12
|
+
|
|
13
|
+
# @TODO: remove once Braze implements the endpoints we asked for
|
|
14
|
+
braze_email_address: 'braze@email.com',
|
|
15
|
+
braze_password: 'braze_password',
|
|
16
|
+
braze_app_group_id: '5551212'
|
|
17
|
+
}]
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/standard_setup'
|
|
3
|
+
|
|
4
|
+
describe Txbr::Uploader do
|
|
5
|
+
include_context 'standard setup'
|
|
6
|
+
|
|
7
|
+
let(:email_template_id) { 'abc123' }
|
|
8
|
+
|
|
9
|
+
let(:braze_interactions) do
|
|
10
|
+
[{
|
|
11
|
+
request: { verb: 'get', url: 'engagement/email_templates', start: 0, length: 1 },
|
|
12
|
+
response: { status: 200, body: { results: [{ id: email_template_id }] }.to_json }
|
|
13
|
+
}, {
|
|
14
|
+
request: { verb: 'get', url: "engagement/email_templates/#{email_template_id}" },
|
|
15
|
+
response: {
|
|
16
|
+
status: 200,
|
|
17
|
+
body: {
|
|
18
|
+
name: 'Super Slick Awesome',
|
|
19
|
+
template: template_html,
|
|
20
|
+
subject: '',
|
|
21
|
+
preheader: ''
|
|
22
|
+
}.to_json
|
|
23
|
+
}
|
|
24
|
+
}]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
let(:transifex_interactions) do
|
|
28
|
+
# indicate the resource doesn't exist so the client will create it
|
|
29
|
+
[{
|
|
30
|
+
request: {
|
|
31
|
+
verb: 'get',
|
|
32
|
+
url: "#{Txgh::TransifexApi::API_ROOT}/project/my_project/resource/my_resource/"
|
|
33
|
+
},
|
|
34
|
+
response: { status: 404 }
|
|
35
|
+
}]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
let(:template_html) do
|
|
39
|
+
<<~HTML
|
|
40
|
+
<html>
|
|
41
|
+
<head>
|
|
42
|
+
{% assign project_slug = "my_project" %}
|
|
43
|
+
{% assign resource_slug = "my_resource" %}
|
|
44
|
+
{% assign translation_enabled = true %}
|
|
45
|
+
{% connected_content http://my_strings_api.com/ :save strings %}
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
{{strings.header | default: 'Buy our stuff!'}}
|
|
49
|
+
{% if user.gets_discount? %}
|
|
50
|
+
{{strings.discount | default: 'You get a discount'}}
|
|
51
|
+
{% else %}
|
|
52
|
+
{{strings.no_discount | default: 'You get no discount'}}
|
|
53
|
+
{% endif %}
|
|
54
|
+
</body>
|
|
55
|
+
</html>
|
|
56
|
+
HTML
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'uploads all resources' do
|
|
60
|
+
# unfortunately this can't be added to the transifex_interactions array
|
|
61
|
+
# because we need to check the upload contents, which is this funky
|
|
62
|
+
# UploadIO object from Faraday
|
|
63
|
+
create_url = "#{Txgh::TransifexApi::API_ROOT}/project/my_project/resources/"
|
|
64
|
+
|
|
65
|
+
expect(transifex_connection).to(
|
|
66
|
+
receive(:post)
|
|
67
|
+
.with(create_url, Hash) do |url, body|
|
|
68
|
+
expect(YAML.load(body[:content].io.string)).to eq(
|
|
69
|
+
{
|
|
70
|
+
'en' => {
|
|
71
|
+
'header'=>'Buy our stuff!',
|
|
72
|
+
'discount'=>'You get a discount',
|
|
73
|
+
'no_discount'=>'You get no discount'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
FakeResponse.new(create_url, 200, '', {})
|
|
79
|
+
end
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
described_class.new(project).upload_all
|
|
83
|
+
end
|
|
84
|
+
end
|
data/spec/utils_spec.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Txbr::Utils do
|
|
4
|
+
describe '.url_join' do
|
|
5
|
+
it 'joins urls' do
|
|
6
|
+
expect(described_class.url_join(*%w(http://foo.bar baz boo))).to(
|
|
7
|
+
eq('http://foo.bar/baz/boo')
|
|
8
|
+
)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'joins urls with leading slashes' do
|
|
12
|
+
expect(described_class.url_join(*%w(http://foo.bar/ baz /boo))).to(
|
|
13
|
+
eq('http://foo.bar/baz/boo')
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/txbr.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
|
2
|
+
require 'txbr/version'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = 'txbr'
|
|
6
|
+
s.version = ::Txbr::VERSION
|
|
7
|
+
s.authors = ['Cameron Dutro']
|
|
8
|
+
s.email = ['camertron@gmail.com']
|
|
9
|
+
s.homepage = 'https://github.com/lumoslabs/txbr'
|
|
10
|
+
|
|
11
|
+
s.description = s.summary = 'A library for syncing translation resources between Braze and Transifex.'
|
|
12
|
+
|
|
13
|
+
s.platform = Gem::Platform::RUBY
|
|
14
|
+
s.has_rdoc = true
|
|
15
|
+
|
|
16
|
+
s.add_dependency 'abroad', '~> 4.5'
|
|
17
|
+
s.add_dependency 'faraday', '~> 0.9'
|
|
18
|
+
s.add_dependency 'faraday_middleware', '~> 0.10'
|
|
19
|
+
s.add_dependency 'liquid', '~> 4.0'
|
|
20
|
+
s.add_dependency 'mechanize', '~> 2.7'
|
|
21
|
+
s.add_dependency 'sinatra', '~> 1.4'
|
|
22
|
+
s.add_dependency 'sinatra-contrib', '~> 1.4'
|
|
23
|
+
s.add_dependency 'txgh', '~> 6.6'
|
|
24
|
+
|
|
25
|
+
s.require_path = 'lib'
|
|
26
|
+
s.files = Dir['{lib,spec}/**/*', 'README.md', 'txbr.gemspec', 'LICENSE']
|
|
27
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: txbr
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Cameron Dutro
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-06-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: abroad
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.5'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.5'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: faraday
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.9'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.9'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: faraday_middleware
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.10'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.10'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: liquid
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '4.0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '4.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: mechanize
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.7'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.7'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: sinatra
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.4'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.4'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: sinatra-contrib
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '1.4'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '1.4'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: txgh
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '6.6'
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '6.6'
|
|
125
|
+
description: A library for syncing translation resources between Braze and Transifex.
|
|
126
|
+
email:
|
|
127
|
+
- camertron@gmail.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- LICENSE
|
|
133
|
+
- README.md
|
|
134
|
+
- lib/txbr.rb
|
|
135
|
+
- lib/txbr/application.rb
|
|
136
|
+
- lib/txbr/braze_api.rb
|
|
137
|
+
- lib/txbr/braze_session.rb
|
|
138
|
+
- lib/txbr/braze_session_api.rb
|
|
139
|
+
- lib/txbr/commands.rb
|
|
140
|
+
- lib/txbr/config.rb
|
|
141
|
+
- lib/txbr/email_template.rb
|
|
142
|
+
- lib/txbr/email_template_component.rb
|
|
143
|
+
- lib/txbr/email_template_handler.rb
|
|
144
|
+
- lib/txbr/project.rb
|
|
145
|
+
- lib/txbr/request_methods.rb
|
|
146
|
+
- lib/txbr/strings_manifest.rb
|
|
147
|
+
- lib/txbr/tasks.rb
|
|
148
|
+
- lib/txbr/uploader.rb
|
|
149
|
+
- lib/txbr/utils.rb
|
|
150
|
+
- lib/txbr/version.rb
|
|
151
|
+
- spec/application_spec.rb
|
|
152
|
+
- spec/braze_session_api_spec.rb
|
|
153
|
+
- spec/braze_session_spec.rb
|
|
154
|
+
- spec/config_spec.rb
|
|
155
|
+
- spec/email_template_spec.rb
|
|
156
|
+
- spec/fixtures/cassettes/braze_login.yml
|
|
157
|
+
- spec/spec_helper.rb
|
|
158
|
+
- spec/strings_manifest_spec.rb
|
|
159
|
+
- spec/support/env_helpers.rb
|
|
160
|
+
- spec/support/fake_braze_session.rb
|
|
161
|
+
- spec/support/fake_connection.rb
|
|
162
|
+
- spec/support/standard_setup.rb
|
|
163
|
+
- spec/support/test_config.rb
|
|
164
|
+
- spec/uploader_spec.rb
|
|
165
|
+
- spec/utils_spec.rb
|
|
166
|
+
- txbr.gemspec
|
|
167
|
+
homepage: https://github.com/lumoslabs/txbr
|
|
168
|
+
licenses: []
|
|
169
|
+
metadata: {}
|
|
170
|
+
post_install_message:
|
|
171
|
+
rdoc_options: []
|
|
172
|
+
require_paths:
|
|
173
|
+
- lib
|
|
174
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - ">="
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '0'
|
|
179
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ">="
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '0'
|
|
184
|
+
requirements: []
|
|
185
|
+
rubyforge_project:
|
|
186
|
+
rubygems_version: 2.7.6
|
|
187
|
+
signing_key:
|
|
188
|
+
specification_version: 4
|
|
189
|
+
summary: A library for syncing translation resources between Braze and Transifex.
|
|
190
|
+
test_files: []
|