trip_advisor 0.0.15

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 (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