trip_advisor 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +52 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +118 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +23 -0
  11. data/Rakefile +7 -0
  12. data/lib/trip_advisor/api.rb +45 -0
  13. data/lib/trip_advisor/build.rb +221 -0
  14. data/lib/trip_advisor/rake_tasks.rb +182 -0
  15. data/lib/trip_advisor/resource_manager.rb +101 -0
  16. data/lib/trip_advisor/strings_file.rb +112 -0
  17. data/lib/trip_advisor/translation.rb +63 -0
  18. data/lib/trip_advisor/translation_tool.rb +110 -0
  19. data/lib/trip_advisor/version.rb +3 -0
  20. data/lib/trip_advisor.rb +5 -0
  21. data/run_tests.sh +6 -0
  22. data/spec/api_spec.rb +34 -0
  23. data/spec/builder_spec.rb +262 -0
  24. data/spec/fixtures/AndroidStrings.txt +21 -0
  25. data/spec/fixtures/Strings.txt +47 -0
  26. data/spec/fixtures/project.git/.gitignore +1 -0
  27. data/spec/fixtures/project.git/BUILD.json +7 -0
  28. data/spec/fixtures/project.git/Code/Project.h +0 -0
  29. data/spec/fixtures/project.git/Code/Project.m +0 -0
  30. data/spec/fixtures/project.git/Gemfile +3 -0
  31. data/spec/fixtures/project.git/Gemfile.lock +38 -0
  32. data/spec/fixtures/project.git/HEAD +1 -0
  33. data/spec/fixtures/project.git/LICENSE +1 -0
  34. data/spec/fixtures/project.git/Podfile +6 -0
  35. data/spec/fixtures/project.git/Podfile.lock +14 -0
  36. data/spec/fixtures/project.git/Project.podspec +14 -0
  37. data/spec/fixtures/project.git/TestProject/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  38. data/spec/fixtures/project.git/TestProject/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  39. data/spec/fixtures/project.git/TestProject/TATPAppDelegate.h +15 -0
  40. data/spec/fixtures/project.git/TestProject/TATPAppDelegate.m +49 -0
  41. data/spec/fixtures/project.git/TestProject/TestProject-Info.plist +38 -0
  42. data/spec/fixtures/project.git/TestProject/TestProject-Prefix.pch +16 -0
  43. data/spec/fixtures/project.git/TestProject/en.lproj/InfoPlist.strings +1 -0
  44. data/spec/fixtures/project.git/TestProject/main.m +18 -0
  45. data/spec/fixtures/project.git/TestProject.xcodeproj/project.pbxproj +1020 -0
  46. data/spec/fixtures/project.git/TestProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  47. data/spec/fixtures/project.git/TestProject.xcodeproj/project.xcworkspace/xcuserdata/blake.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  48. data/spec/fixtures/project.git/TestProject.xcodeproj/xcuserdata/blake.xcuserdatad/xcschemes/TestProject.xcscheme +96 -0
  49. data/spec/fixtures/project.git/TestProject.xcodeproj/xcuserdata/blake.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  50. data/spec/fixtures/project.git/TestProject.xcworkspace/contents.xcworkspacedata +1 -0
  51. data/spec/fixtures/project.git/TestProjectTests/TestProjectTests-Info.plist +22 -0
  52. data/spec/fixtures/project.git/TestProjectTests/TestProjectTests.m +34 -0
  53. data/spec/fixtures/project.git/TestProjectTests/en.lproj/InfoPlist.strings +1 -0
  54. data/spec/fixtures/project.git/VERSION +1 -0
  55. data/spec/fixtures/project.git/config +7 -0
  56. data/spec/fixtures/project.git/description +1 -0
  57. data/spec/fixtures/project.git/index +0 -0
  58. data/spec/fixtures/project.git/info/exclude +6 -0
  59. data/spec/fixtures/project.git/logs/HEAD +1 -0
  60. data/spec/fixtures/project.git/logs/refs/heads/master +1 -0
  61. data/spec/fixtures/project.git/objects/00/a6b3f80ebbe4019f03461416061e8805e6eab1 +0 -0
  62. data/spec/fixtures/project.git/objects/03/c303d5552f29d366d11a13448c3c18e94c3bfa +0 -0
  63. data/spec/fixtures/project.git/objects/03/ff7c3a95a196ac8ad1d53db2bd4fb4c80507c1 +0 -0
  64. data/spec/fixtures/project.git/objects/0a/7123364a174ffda52d29a18cdd31fd325bbd6c +0 -0
  65. data/spec/fixtures/project.git/objects/0d/183cba8b0a5e477ea1d001130c222af19b8324 +0 -0
  66. data/spec/fixtures/project.git/objects/0e/6ba5232c3c2f8a7e435794e845b5dc9f339d8b +0 -0
  67. data/spec/fixtures/project.git/objects/25/5b4ee687ed1337cffb10e6d07bb89ee6e5d180 +0 -0
  68. data/spec/fixtures/project.git/objects/25/60a2b9ab9323ca61a0fdebd3b4af46dbe4c272 +0 -0
  69. data/spec/fixtures/project.git/objects/2b/f8bd93d56787a7548c7f8960a94f05c269b486 +0 -0
  70. data/spec/fixtures/project.git/objects/38/439105a884d2556443c7b5c645034e67fb7987 +0 -0
  71. data/spec/fixtures/project.git/objects/3e/efcb9dd5b38e2c1dc061052455dd97bcd51e6c +0 -0
  72. data/spec/fixtures/project.git/objects/53/8b3019ab68814604fbc8e017d09b690c22728c +0 -0
  73. data/spec/fixtures/project.git/objects/59/c178850684e21fafbebedeff15b2b522402af2 +0 -0
  74. data/spec/fixtures/project.git/objects/62/037867910d9aa803d7e02addc26574e270d3d8 +0 -0
  75. data/spec/fixtures/project.git/objects/66/05c1af131470a3b635d166f2591917b32f3b82 +0 -0
  76. data/spec/fixtures/project.git/objects/69/67e396037a52d8300eb9dc8a1e9f7155dd55bf +0 -0
  77. data/spec/fixtures/project.git/objects/6b/5ad4043aee9bc0936466760665f870d4f2f40f +0 -0
  78. data/spec/fixtures/project.git/objects/6c/fc746d4ecf2e3ac67bb0cf6c4013e1aff9b1f1 +0 -0
  79. data/spec/fixtures/project.git/objects/6f/3b0ab36651ca648408c176f81e4b2e202efa0b +0 -0
  80. data/spec/fixtures/project.git/objects/74/3435c9bee5c433fe166891e698fdc70d81a3de +0 -0
  81. data/spec/fixtures/project.git/objects/96/81bfb8ec019f74df384cfb66d68e94bd6d9005 +0 -0
  82. data/spec/fixtures/project.git/objects/99/fa9259e929fe3bee4791ff2a63070fc0358c0a +3 -0
  83. data/spec/fixtures/project.git/objects/a1/5f8980cfbf126a9a0b3ddf7fb1c3fbd6480934 +2 -0
  84. data/spec/fixtures/project.git/objects/a3/96706db4ec4cfca4d984d38b90dc8098bded40 +1 -0
  85. data/spec/fixtures/project.git/objects/b4/42134ccda2b0b1813288e7e04a401a4bef2b59 +0 -0
  86. data/spec/fixtures/project.git/objects/b9/2732c79e00cf2fad5ceceb61bbc7a9a9739d2c +0 -0
  87. data/spec/fixtures/project.git/objects/ba/01f5352549018dfdb95423b9e0974443ec0346 +1 -0
  88. data/spec/fixtures/project.git/objects/ba/8dd7d2162334426facd310f62ed923eb6f4cf5 +0 -0
  89. data/spec/fixtures/project.git/objects/bf/422763a0101a5971e15d53581acb825b4d38b8 +0 -0
  90. data/spec/fixtures/project.git/objects/c7/9ebd3ada1309bae694b8e6760592b1254e035c +2 -0
  91. data/spec/fixtures/project.git/objects/ca/7ced48ee61e517fc57576a33dc9d95f5d4f539 +2 -0
  92. data/spec/fixtures/project.git/objects/ce/eed5f059b6d374d08e0b1e732463842b3a5a91 +0 -0
  93. data/spec/fixtures/project.git/objects/d7/89fd591fee7d26c2f40c6614b5a91931377f88 +0 -0
  94. data/spec/fixtures/project.git/objects/d8/ec4dbb2804f53fd863cd0adb830c5bf4e85a1d +0 -0
  95. data/spec/fixtures/project.git/objects/dc/680e07f12748657d5a798495f08ce3ad884f0b +0 -0
  96. data/spec/fixtures/project.git/objects/de/de871ad5691708aca809557a823082a70dde69 +0 -0
  97. data/spec/fixtures/project.git/objects/e0/30730e56be52e4dc293c829f73e0c586073ad2 +0 -0
  98. data/spec/fixtures/project.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  99. data/spec/fixtures/project.git/objects/e6/f7f8a92eb3177d10a809b96f41905e9be4cc2b +0 -0
  100. data/spec/fixtures/project.git/objects/ed/7115c4ae4c1885ee75f6ffe946b559546efd28 +0 -0
  101. data/spec/fixtures/project.git/objects/f1/e01dd1256054d5717ef0c89008f0a22639e273 +2 -0
  102. data/spec/fixtures/project.git/objects/f9/3e9c03a56aba331bcd98cb2ef00ba0003de285 +0 -0
  103. data/spec/fixtures/project.git/refs/heads/master +1 -0
  104. data/spec/fixtures/search.json +14159 -0
  105. data/spec/fixtures/search_results_1-50.html +1076 -0
  106. data/spec/fixtures/search_results_101-110.html +476 -0
  107. data/spec/fixtures/search_results_51-100.html +1076 -0
  108. data/spec/fixtures/translation_details.html +728 -0
  109. data/spec/fixtures/translation_details.json +738 -0
  110. data/spec/spec_helper.rb +28 -0
  111. data/spec/strings_file_spec.rb +225 -0
  112. data/spec/translation_spec.rb +21 -0
  113. data/spec/translation_tool_spec.rb +146 -0
  114. data/trip_advisor.gemspec +38 -0
  115. metadata +474 -0
@@ -0,0 +1,182 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module TripAdvisor
5
+ class TestDevice
6
+ attr_accessor :platform, :name, :valid_schemes
7
+
8
+ def initialize
9
+ @platform = 'iOS Simulator'
10
+ @name = 'iPhone 5s'
11
+ @valid_schemes = []
12
+ end
13
+
14
+ def destination_arg(os_version)
15
+ "platform='#{platform}',OS=#{os_version},name='#{name}'"
16
+ end
17
+
18
+ def valid_for?(scheme_name)
19
+ valid_schemes.empty? || valid_schemes.include?(scheme_name)
20
+ end
21
+
22
+ def sdk
23
+ platform.eql?('iOS Simulator') ? 'iphonesimulator' : 'iphoneos'
24
+ end
25
+
26
+ def short_name
27
+ "#{name.downcase.gsub(/\s+/, '')}"
28
+ end
29
+
30
+ def to_s
31
+ "#{name} (#{platform})"
32
+ end
33
+
34
+ def hash
35
+ [platform, name].hash
36
+ end
37
+
38
+ def eql?(object)
39
+ platform.eql?(object.platform) && name.eql?(object.name)
40
+ end
41
+ end
42
+
43
+ class TestReport
44
+ include Singleton
45
+
46
+ def initialize
47
+ @namespaces = {}
48
+ @success = true
49
+ end
50
+
51
+ def add_result(namespace, ios_version, test_device, success)
52
+ @namespaces[namespace] ||= {}
53
+ @namespaces[namespace][ios_version] ||= {}
54
+ @namespaces[namespace][ios_version][test_device] = success
55
+ @success = false unless success
56
+ end
57
+
58
+ def [](ios_version)
59
+ @namespaces[ios_version]
60
+ end
61
+
62
+ def success?
63
+ @success
64
+ end
65
+
66
+ def failure?
67
+ @success == false
68
+ end
69
+
70
+ def report
71
+ @namespaces.each do |namespace, version_status|
72
+ version_status.each do |ios_version, device_status|
73
+ device_status.each do |test_device, success|
74
+ puts "\033[0;31m!! #{namespace} tests failed on #{test_device} under iOS #{ios_version}" unless success
75
+ end
76
+ end
77
+ end
78
+ puts "\033[0;32m** All tests executed successfully" if success?
79
+ end
80
+ end
81
+
82
+ class TestTasks < Rake::TaskLib
83
+ include ::Rake::DSL if defined?(::Rake::DSL)
84
+
85
+ attr_reader :namespace_name, :prepare_dependency
86
+ attr_accessor :workspace, :schemes_dir, :schemes, :ios_versions
87
+ attr_accessor :runner, :xctool_path, :xcodebuild_path, :environment_variables
88
+ attr_accessor :ios_versions, :test_devices, :settings
89
+
90
+ def initialize(namespace_name = :test)
91
+ @namespace_name = namespace_name.is_a?(Hash) ? namespace_name.keys.first : namespace_name
92
+ @prepare_dependency = namespace_name.is_a?(Hash) ? namespace_name.values.first : nil
93
+ @schemes_dir = 'Tests/Schemes'
94
+ @ios_versions = %w{7.0 8.0}
95
+ @xctool_path = '/usr/local/bin/xctool'
96
+ @xcodebuild_path = 'xcodebuild'
97
+ @runner = :xctool
98
+ @settings = {}
99
+ @environment_variables = ''
100
+
101
+ yield self if block_given?
102
+ raise "A workspace must be configured" unless workspace
103
+ raise "At least one scheme must be configured" unless schemes
104
+ raise "At least one test device must be configured" unless test_devices
105
+ define_tasks
106
+ end
107
+
108
+ def runner=(runner)
109
+ raise ArgumentError, "Must be :xctool or :xcodebuild" unless %w{xctool xcodebuild}.include?(runner.to_s)
110
+ @runner = runner.to_sym
111
+ end
112
+
113
+ def define_tasks
114
+ namespace self.namespace_name do
115
+ task (prepare_dependency ? { prepare: prepare_dependency} : :prepare ) do
116
+ run(%Q{mkdir -p "#{workspace}/xcshareddata/xcschemes" && cp Tests/Schemes/*.xcscheme "#{workspace}/xcshareddata/xcschemes/"})
117
+ end
118
+
119
+ # Iterate across the schemes and define a task for each version of iOS and an aggregate task
120
+ schemes.each do |scheme_namespace, scheme_names|
121
+ scheme_names_array = scheme_names.is_a?(Array) ? scheme_names : [scheme_names]
122
+ namespace scheme_namespace do
123
+ scheme_names_array.each do |scheme_name|
124
+ ios_versions.each do |ios_version|
125
+ namespace ios_version do
126
+ test_devices.each do |test_device|
127
+ next unless test_device.valid_for?(scheme_name)
128
+ desc "Run #{scheme_namespace} tests against #{ios_version} SDK on #{test_device}"
129
+ task test_device.short_name => :prepare do
130
+ test_scheme(scheme_name, namespace: scheme_namespace, ios_version: ios_version, test_device: test_device)
131
+ end
132
+ end
133
+ end
134
+ desc "Run #{scheme_namespace} tests against #{ios_version} SDK on valid devices"
135
+ task "#{ios_version}" => test_devices.select { |test_device| test_device.valid_for?(scheme_name) }.map { |test_device| "#{scheme_namespace}:#{ios_version}:#{test_device.short_name}" }
136
+ end
137
+ end
138
+ end
139
+
140
+ desc "Run #{scheme_namespace} tests against all SDKs and valid devices"
141
+ task scheme_namespace => ios_versions.map { |ios_version| "#{scheme_namespace}:#{ios_version}" }
142
+ end
143
+ end
144
+
145
+ desc "Run the #{schemes.keys.join(', ')} tests on all devices and SDKs"
146
+ task namespace_name => schemes.keys.map { |scheme| "#{namespace_name}:#{scheme}" } do
147
+ TripAdvisor::TestReport.instance.report
148
+ end
149
+ end
150
+
151
+ private
152
+ def run(command)
153
+ puts "Executing `#{command}`"
154
+ system(command)
155
+ end
156
+
157
+ def test_scheme(scheme, options)
158
+ system(%q{killall "iPhone Simulator"})
159
+ namespace = options[:namespace]
160
+ ios_version = options[:ios_version]
161
+ test_device = options[:test_device]
162
+ test_sdk = "#{ios_version}"
163
+
164
+ settings_arg = settings.map { |k,v| "#{k}=#{v}"}.join(' ')
165
+ destination_arg = test_device.destination_arg(ios_version)
166
+ success = if xctool?
167
+ run("#{xctool_path} -workspace #{workspace} -scheme #{scheme} -sdk #{test_device.sdk} test -freshSimulator -destination #{destination_arg} #{settings_arg} #{environment_variables} DEVICE_NAME=#{test_device.short_name}_#{ios_version} -reporter junit:#{scheme}-#{test_sdk}-#{test_device.short_name}.xml")
168
+ elsif xcodebuild?
169
+ run("#{xcodebuild_path} -workspace #{workspace} -scheme #{scheme} -sdk #{test_device.sdk} test -destination #{destination_arg} #{settings_arg} #{environment_variables} DEVICE_NAME=#{test_device.short_name}_#{ios_version} | xcpretty -cr html --output build/reports/#{scheme}-#{test_sdk}-#{test_device.short_name}.html ; exit ${PIPESTATUS[0]}")
170
+ end
171
+ TripAdvisor::TestReport.instance.add_result(namespace, ios_version, test_device, success)
172
+ end
173
+
174
+ def xctool?
175
+ runner == :xctool
176
+ end
177
+
178
+ def xcodebuild?
179
+ runner == :xcodebuild
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,101 @@
1
+ module TripAdvisor
2
+ class ResourceManager
3
+ attr_accessor :api_client, :output_dir
4
+
5
+ def initialize(options={})
6
+ options.each { |k,v| self.send("#{k}=", v) }
7
+ end
8
+
9
+ def locales
10
+ @locales ||= api_client.get_flights_locales
11
+ end
12
+
13
+ def dump_airports
14
+ provision_locale_projects
15
+ puts "Dumping Airport fixtures for #{locales.size} locales..."
16
+ locales.each do |locale|
17
+ locale_identifier = locale['locale']
18
+ begin
19
+ json = api_client.get(:airports, locale: locale_identifier)
20
+ rescue RestClient::Exception => ex
21
+ puts "ERROR: Failed Fetching Airports fixture for '#{locale_identifier}' locale_identifier: #{ex}"
22
+ next
23
+ end
24
+ language_code = TripAdvisor::Translation.locale_identifier_to_language_codes_mapping[locale_identifier] || locale_identifier
25
+ path = File.join(output_dir, "#{language_code}.lproj", "airports.json")
26
+ File.open(path, 'w') { |f| f << json }
27
+ puts " Wrote Airports fixture for '#{locale_identifier}' locale to #{path}"
28
+ end
29
+ end
30
+
31
+ def dump_airlines
32
+ provision_locale_projects
33
+ puts "Dumping Airline fixtures for #{locales.size} locales..."
34
+ locales.each do |locale|
35
+ locale_identifier = locale['locale']
36
+ begin
37
+ json = api_client.get(:airlines, locale: locale_identifier)
38
+ rescue RestClient::Exception => ex
39
+ puts "ERROR: Failed Fetching Airlines fixture for '#{locale_identifier}' locale_identifier: #{ex}"
40
+ next
41
+ end
42
+ language_code = TripAdvisor::Translation.locale_identifier_to_language_codes_mapping[locale_identifier] || locale_identifier
43
+ path = File.join(output_dir, "#{language_code}.lproj", "airlines.json")
44
+ File.open(path, 'w') { |f| f << json }
45
+ puts " Wrote Airlines fixture for '#{locale_identifier}' locale to #{path}"
46
+ end
47
+ end
48
+
49
+ def dump_purchase_link_providers
50
+ provision_locale_projects
51
+ puts "Dumping Purchase Link Provider fixtures for #{locales.size} locales..."
52
+ locales.each do |locale|
53
+ locale_identifier = locale['locale']
54
+ begin
55
+ json = api_client.get(:flight_purchase_link_providers, locale: locale_identifier)
56
+ rescue RestClient::Exception => ex
57
+ puts "ERROR: Failed Fetching Purchase Link Provider fixture for '#{locale_identifier}' locale_identifier: #{ex}"
58
+ next
59
+ end
60
+ language_code = TripAdvisor::Translation.locale_identifier_to_language_codes_mapping[locale_identifier] || locale_identifier
61
+ path = File.join(output_dir, "#{language_code}.lproj", "flight_purchase_link_providers.json")
62
+ File.open(path, 'w') { |f| f << json }
63
+ puts " Wrote Purchase Link Provider fixture for '#{locale_identifier}' locale to #{path}"
64
+ end
65
+ end
66
+
67
+ def language_tags_from_locales_api
68
+ locales.map { |l| l['locale'] }
69
+ end
70
+
71
+ def provision_locale_projects(language_tags = self.language_tags_from_locales_api)
72
+ FileUtils.mkdir(output_dir) unless File.exists?(output_dir)
73
+ language_tags.inject([]) do |paths, language_tag|
74
+ language_tag = TripAdvisor::Translation.locale_identifier_to_language_codes_mapping[language_tag] || language_tag
75
+ path = File.join(output_dir, "#{language_tag}.lproj")
76
+ unless File.exist?(path)
77
+ FileUtils.mkdir(path)
78
+ paths << path
79
+ yield path if block_given?
80
+ end
81
+ paths
82
+ end
83
+ end
84
+
85
+ def provision_localized_android_resources(language_tags = self.language_tags_from_locales_api)
86
+ FileUtils.mkdir(output_dir) unless File.exists?(output_dir)
87
+ language_tags.inject([]) do |paths, language_tag|
88
+ language_tag = TripAdvisor::Translation.locale_identifier_to_language_codes_mapping[language_tag] || language_tag
89
+ language_tag.gsub!(/-/,'_')
90
+ language_tag.gsub!(/_/,'-r')
91
+ path = File.join(output_dir, "values-#{language_tag}")
92
+ unless File.exist?(path)
93
+ FileUtils.mkdir(path)
94
+ paths << path
95
+ yield path if block_given?
96
+ end
97
+ paths
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,112 @@
1
+ require 'twine'
2
+ require 'twine/stringsfile'
3
+
4
+ module TripAdvisor
5
+ class StringsFile
6
+ attr_reader :path, :translations
7
+
8
+ def self.read(path)
9
+ self.new.tap { |sf| sf.read(path) }
10
+ end
11
+
12
+ def merge_translations(translations)
13
+ translations.each do |translation|
14
+ if twine_row = twine_row_for_key(translation['en'].to_s)
15
+ update_twine_row_with_translation(twine_row, translation)
16
+ else
17
+ add_twine_row_for_translation(translation)
18
+ end
19
+ end
20
+ end
21
+
22
+ def merge_translations_android(translations)
23
+ translations.each do |translation|
24
+ if twine_row = twine_row_for_key(translation.key)
25
+ update_twine_row_with_translation(twine_row, translation)
26
+ else
27
+ add_twine_row_for_translation_android(translation)
28
+ end
29
+ end
30
+ end
31
+
32
+ def loaded?
33
+ @twine_strings_file != nil
34
+ end
35
+
36
+ def [](key)
37
+ raise "Cannot access translations without reading a Strings file" unless loaded?
38
+ translations.detect { |translation| translation.key == key.to_s }
39
+ end
40
+
41
+ def read(path)
42
+ @path = path
43
+ @twine_strings_file = Twine::StringsFile.new
44
+ @twine_strings_file.read(path)
45
+ load_translations_from_twine
46
+ end
47
+
48
+ def write(path = self.path)
49
+ @twine_strings_file.write(path)
50
+ end
51
+
52
+ def language_tags
53
+ @twine_strings_file.language_codes
54
+ end
55
+
56
+ private
57
+ def load_translations_from_twine
58
+ @translations = @twine_strings_file.sections.collect do |section|
59
+ section.rows.map do |row|
60
+ Translation.new(key: row.key, note: row.comment).tap do |translation|
61
+ translation.localizations = row.translations.map do |locale_identifier, string|
62
+ Translation::Localization.new(locale_identifier: locale_identifier, string: string)
63
+ end
64
+ end
65
+ end
66
+ end.flatten
67
+ end
68
+
69
+ # NOTE: For Android, the Twine files are keyed by Translation Tool key, _not_ English translation.
70
+ # NOTE: The Twine files are keyed by English translation, _not_ Translation Tool key
71
+ def twine_row_for_key(key)
72
+ @twine_strings_file.sections.each do |section|
73
+ if row = section.rows.detect { |row| row.key == key }
74
+ return row
75
+ end
76
+ end
77
+ nil
78
+ end
79
+
80
+ def update_twine_row_with_translation(twine_row, translation)
81
+ twine_row.comment = translation.note
82
+ translation.localizations.each do |localization|
83
+ @twine_strings_file.add_language_code(localization.locale_identifier)
84
+ twine_row.translations[localization.locale_identifier] = localization.string
85
+ end
86
+ end
87
+
88
+ def add_twine_row_for_translation(translation)
89
+ # We merge new rows into the first section
90
+ section = @twine_strings_file.sections.first
91
+ english_string = translation['en']
92
+ raise "Cannot add Translation: No English ('en') localization available" unless english_string
93
+ row = Twine::StringsRow.new(english_string).tap do |row|
94
+ row.tags = ["key:#{translation.key}"]
95
+ update_twine_row_with_translation(row, translation)
96
+ end
97
+ section.rows << row
98
+ end
99
+
100
+ def add_twine_row_for_translation_android(translation)
101
+ # We merge new rows into the first section
102
+ section = @twine_strings_file.sections.first
103
+ translation_tool_key = translation.key
104
+ raise "Cannot add Translation: No translation tool key." unless translation_tool_key
105
+ row = Twine::StringsRow.new(translation_tool_key).tap do |row|
106
+ row.tags = ["key:#{translation.key}"]
107
+ update_twine_row_with_translation(row, translation)
108
+ end
109
+ section.rows << row
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,63 @@
1
+ module TripAdvisor
2
+ class Translation
3
+ # Returns a dictionary that specifies how locale identifiers map to BCP 47 Language Identifiers
4
+ def self.locale_identifier_to_language_codes_mapping
5
+ { "zh_TW" => "zh-Hant", "zh_CN"=> "zh-Hans", "zh_CN"=> "zh",
6
+ "no"=> "nb", "in"=> "id", "en_UK"=> "en-GB" }
7
+ end
8
+
9
+ # Supports keyed lookup by locale identifier
10
+ class LocalizationsArray < Array
11
+ def [](locale_identifier)
12
+ if locale_identifier.is_a?(String)
13
+ self.detect { |localization| localization.locale_identifier == locale_identifier.to_s }
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+
20
+ class Localization
21
+ attr_accessor :locale_identifier, :language_name, :string, :status
22
+
23
+ def initialize(attributes = {})
24
+ attributes.each { |k, v| self.send("#{k}=", v) }
25
+ end
26
+
27
+ def locale_identifier=(locale_identifier)
28
+ @locale_identifier = (Translation.locale_identifier_to_language_codes_mapping[locale_identifier] || locale_identifier)
29
+ end
30
+
31
+ def string=(string)
32
+ @string = string.strip
33
+ end
34
+
35
+ def to_s
36
+ string
37
+ end
38
+
39
+ def active?
40
+ status == 'Active'
41
+ end
42
+ end
43
+
44
+ attr_accessor :id, :key, :note, :localizations
45
+
46
+ def initialize(attributes = {})
47
+ @localizations = LocalizationsArray.new
48
+ attributes.each { |k, v| self.send("#{k}=", v) }
49
+ end
50
+
51
+ def key=(key)
52
+ @key = key.strip
53
+ end
54
+
55
+ def localizations=(localizations)
56
+ @localizations.concat(localizations)
57
+ end
58
+
59
+ def [](locale_identifier)
60
+ localizations[locale_identifier]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,110 @@
1
+ require 'trip_advisor/translation'
2
+ require 'rest-client'
3
+ require 'nokogiri'
4
+ require "active_support/core_ext"
5
+
6
+ module TripAdvisor
7
+ class TranslationTool
8
+ module Scraper
9
+ class << self
10
+ def translations_from_doc(doc)
11
+ Array.new.tap do |translations|
12
+ doc.each { |translation| translations << Translation.new(id: translation["id"], key: translation["key"], note: translation["note"]) }
13
+ end
14
+ end
15
+
16
+ def localizations_from_doc(doc)
17
+ Array.new.tap do |localizations|
18
+ doc.each { |localization|
19
+ locale_string = localization["lang"]["locale"]
20
+ localized_string = localization["displayedValue"]
21
+ status = localization["isTranslatedValue"]
22
+ language_name = localization["lang"]["localizedDisplayName"]
23
+ locale_identifier = localization["lang"]["locale"]
24
+ localizations << Translation::Localization.new(locale_identifier: locale_identifier, language_name: language_name, string: localized_string, status: status, )
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ class ResultsPaginator
32
+ attr_reader :base_url, :offset, :total, :per_page, :params, :translations
33
+
34
+ def initialize(base_url, attributes = {})
35
+ @base_url = base_url
36
+ @total = 0
37
+ @per_page = 50
38
+ @offset = attributes[:offset] || 0
39
+ @params = attributes[:params]
40
+ end
41
+
42
+ def load(options = {})
43
+ @offset = options[:offset] if options[:offset]
44
+ @offset = ((options[:page] - 1) * per_page) if options[:page]
45
+ raise ArgumentError, "Cannot load a negative offset" if @offset < 0
46
+ raise ArgumentError, "Cannot load an offset greater than the total number of objects" if total && @offset > total
47
+ response = RestClient.post(base_url, params.to_json, :content_type => :json, :accept => :json)
48
+ doc = JSON.parse(response)
49
+ @total = doc.count
50
+ @per_page = doc.count
51
+ @translations = Scraper.translations_from_doc(doc)
52
+ end
53
+
54
+ def page_count
55
+ (total / per_page.to_f).ceil
56
+ end
57
+
58
+ def loaded?
59
+ @translations
60
+ end
61
+
62
+ def paginate
63
+ load unless loaded?
64
+ 1.upto(page_count).collect do |page_number|
65
+ load(page: page_number).tap do |translations|
66
+ yield page_number, translations if block_given?
67
+ end
68
+ end.flatten
69
+ end
70
+
71
+ alias_method :all, :paginate
72
+
73
+ def current_page_index
74
+ offset / per_page
75
+ end
76
+
77
+ def next_page
78
+ load(page: current_page_index + 1)
79
+ end
80
+
81
+ def previous_page
82
+ load(page: current_page_index - 1)
83
+ end
84
+ end
85
+
86
+ attr_accessor :username, :password, :locale
87
+
88
+ def initialize(attributes = {})
89
+ attributes.each { |k, v| self.send("#{k}=", v) }
90
+ @locale ||= 'en'
91
+ end
92
+
93
+ def search(query, &block)
94
+ ResultsPaginator.new("https://#{username}:#{password}@localization.tripadvisor.com/translations/search/keys", params: { lang: locale, keyName: query }).tap do |paginator|
95
+ if block_given?
96
+ paginator.paginate(&block)
97
+ else
98
+ paginator.load
99
+ end
100
+ end
101
+ end
102
+
103
+ def get_translation(translation)
104
+ translation = Translation.new(id: translation) if translation.is_a?(Integer)
105
+ response = RestClient.get("https://#{username}:#{password}@localization.tripadvisor.com/translations/keys/id/#{translation.id}/translations")
106
+ translation.localizations = Scraper.localizations_from_doc(JSON.parse(response))
107
+ translation
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ module TripAdvisor
2
+ VERSION = "0.0.15"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "trip_advisor/version"
2
+ require "trip_advisor/api"
3
+ require "trip_advisor/resource_manager"
4
+ require 'trip_advisor/translation_tool'
5
+ require 'trip_advisor/strings_file'
data/run_tests.sh ADDED
@@ -0,0 +1,6 @@
1
+ export LC_CTYPE="en_US.UTF-8"
2
+ echo "Building and Running Tests..."
3
+ echo "Installing Gem dependencies via Bundler..."
4
+ unset RUBYOPT
5
+ DISABLE_BINSTUBS=1 bundle install --deployment --binstubs
6
+ bundle exec rake spec
data/spec/api_spec.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'trip_advisor/api'
3
+
4
+ describe TripAdvisor::API do
5
+ describe '.get_locales' do
6
+ before(:each) do
7
+ @api = TripAdvisor::API.new(api_key: 'adf6d1b8-0aca-4b0c-a492-50530aadd7aa')
8
+ WebMock.allow_net_connect!
9
+ end
10
+
11
+ after(:each) do
12
+ WebMock.disable_net_connect!
13
+ end
14
+
15
+ it "load all locales from the API" do
16
+ @api.get_locales.count.should == 46
17
+ end
18
+ end
19
+
20
+ describe '.get_flights_locales' do
21
+ before(:each) do
22
+ @api = TripAdvisor::API.new(api_key: 'adf6d1b8-0aca-4b0c-a492-50530aadd7aa')
23
+ WebMock.allow_net_connect!
24
+ end
25
+
26
+ after(:each) do
27
+ WebMock.disable_net_connect!
28
+ end
29
+
30
+ it "load all flights_enabled locales from the API" do
31
+ @api.get_flights_locales.count.should == 39
32
+ end
33
+ end
34
+ end