phraseapp_updater 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b4b5ca69acb3ee2e8aa1b40c1325872ac307c50
4
- data.tar.gz: 0f1e8961e0e35908ab8c8094ee845a9d71c8badf
3
+ metadata.gz: a0c061126d02d4f01d387770c2c9340336be6e5f
4
+ data.tar.gz: 31e72d551e6852fa680e477c2e0a9ba185bd5160
5
5
  SHA512:
6
- metadata.gz: 70c04afe5d8a011e3f7d68872f82bfa7a67150c6868d29f2b7b2a0990647a9496a92165c1d44f7753ec72b59b4fd5649f0e93b41ca68c8041829f830033d3715
7
- data.tar.gz: b6880095d635a3b72dade7bbe19ea363b6c1fb38db91cae84f230e307f34f9f7757e6b9a3bfdcdeeb208a36f3d8689705f5bc58211916bf1f68df44db5c60720
6
+ metadata.gz: 6b64d248d78459245df8f5f17121b85268f11cf35273addb88d8d8f876e3d24325644d6ba7696f112bca6003aefc5c8e3ac752c3a4a64e6959246bfe89d93203
7
+ data.tar.gz: 4ada8a07261f8e5966a6a72ed5f8223bf18999704fccfaa1f1f7c6dc4d98506b8195ed8a179b1287aef6fe88e4031ba431bffbe71e76dd5d7cc5a651a2fae848
data/README.markdown CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/iknow/phraseapp_updater.svg?branch=master)](https://travis-ci.org/iknow/phraseapp_updater)
4
4
 
5
- **Version** 0.1.4
5
+ **Version** 0.1.5
6
6
 
7
7
  This is a tool for merging PhraseApp locale data with locale data
8
8
  committed in your project.
@@ -89,7 +89,7 @@ committed to your application's respository. These will be used in the
89
89
  merge with the files on PhraseApp.
90
90
 
91
91
  ```
92
- phraseapp_updater push --new_locales_path="/data/previous", --previous_locales_path="/data/new" --phraseapp_api_key="yourkey" --phraseapp_project_id="projectid"
92
+ phraseapp_updater push --new_locales_path="/data/previous", --previous_locales_path="/data/new" --phraseapp_api_key="yourkey" --phraseapp_project_id="projectid --file_format=json"
93
93
  ```
94
94
 
95
95
  The arguments provided to the command can also be specified as shell
@@ -100,6 +100,7 @@ PA_NEW_LOCALES_PATH
100
100
  PA_PREVIOUS_LOCALES_PATH
101
101
  PA_API_KEY
102
102
  PA_PROJECT_ID
103
+ PA_FILE_FORMAT
103
104
  ```
104
105
 
105
106
  Additionally, PhraseApp credentials can be loaded from a
@@ -118,7 +119,7 @@ If you want to pull without this fallback behavior, PhraseApp's [client](https:/
118
119
  is the best tool to use.
119
120
 
120
121
  ```
121
- phraseapp_updater pull --fallback_path="/data/app/locales" --phraseapp_api_key="yourkey" --phraseapp_project_id="projectid"
122
+ phraseapp_updater pull --fallback_path="/data/app/locales" --phraseapp_api_key="yourkey" --phraseapp_project_id="projectid --file_format=json""
122
123
  ```
123
124
 
124
125
  The PhraseApp data passed to the command can also be specified as shell
@@ -127,6 +128,7 @@ variables:
127
128
  ```
128
129
  PA_API_KEY
129
130
  PA_PROJECT_ID
131
+ PA_FILE_FORMAT
130
132
  ```
131
133
 
132
134
  Additionally, PhraseApp credentials can be loaded from a
@@ -138,8 +140,8 @@ Ruby
138
140
  `PhraseAppUpdater.push` and `PhraseAppUpdater.pull` are analogous to the command line versions:
139
141
 
140
142
  ```ruby
141
- PhraseAppUpdater.push("api_key", "project_id", "previous/path", "current/path")
142
- PhraseAppUpdater.pull("api_key", "project_id", "fallback/path")
143
+ PhraseAppUpdater.new("api_key", "project_id", "file_format").push("previous/path", "current/path")
144
+ PhraseAppUpdater.new("api_key", "project_id", "file_format").pull("fallback/path")
143
145
  ```
144
146
 
145
147
 
@@ -11,9 +11,10 @@ class PhraseAppUpdaterCLI < Thor
11
11
  option :config_file_path, type: :string, desc: "Path to .phraseapp.yml config file to read API key and project ID."
12
12
  option :store_results_path, type: :string, desc: "Path to write the resolved files. Shell variable: PA_STORE_RESULTS_PATH"
13
13
  option :store_phraseapp_originals_path, type: :string, desc: "Path to write the files downloaded from PhraseApp before the merge. Shell variable: PA_STORE_PHRASEAPP_ORIGINALS_PATH"
14
+ option :file_format, type: :string, desc: "Filetype of localization files."
14
15
 
15
16
  def push
16
- phraseapp_api_key, phraseapp_project_id = load_phraseapp_credentials(options)
17
+ phraseapp_api_key, phraseapp_project_id, file_format = load_phraseapp_configuration(options)
17
18
 
18
19
  new_locales_path = options.fetch(:new_locales_path, ENV["PA_NEW_LOCALES_PATH"]).to_s
19
20
  validate_readable_path!('new_locales_path', new_locales_path)
@@ -28,7 +29,7 @@ class PhraseAppUpdaterCLI < Thor
28
29
  validate_writable_path!('store_phraseapp_originals_path', store_phraseapp_originals_path)
29
30
 
30
31
  begin
31
- result = PhraseAppUpdater.new(phraseapp_api_key, phraseapp_project_id).push(previous_locales_path, new_locales_path)
32
+ result = PhraseAppUpdater.new(phraseapp_api_key, phraseapp_project_id, file_format).push(previous_locales_path, new_locales_path)
32
33
 
33
34
  unless store_results_path.empty?
34
35
  write_locale_files(store_results_path, result.resolved_files)
@@ -41,8 +42,10 @@ class PhraseAppUpdaterCLI < Thor
41
42
  puts "Bad PhraseApp API key."
42
43
  rescue PhraseAppUpdater::PhraseAppAPI::BadProjectIDError => e
43
44
  puts "Bad PhraseApp project ID: #{phraseapp_project_id}"
45
+ rescue PhraseAppUpdater::LocaleFile::BadFileTypeError => e
46
+ puts "Bad filetype for localization files: #{e.message}"
44
47
  rescue StandardError => e
45
- puts "Unknown error when pushing files"
48
+ puts "Unknown error when pushing files."
46
49
  raise e
47
50
  end
48
51
  end
@@ -56,7 +59,7 @@ class PhraseAppUpdaterCLI < Thor
56
59
  option :config_file_path, type: :string, desc: "Path to .phraseapp.yml config file to read API key and project ID."
57
60
 
58
61
  def pull
59
- phraseapp_api_key, phraseapp_project_id = load_phraseapp_credentials(options)
62
+ phraseapp_api_key, phraseapp_project_id, file_format = load_phraseapp_configuration(options)
60
63
 
61
64
  fallback_path = options[:fallback_path]
62
65
  validate_readable_path!('fallback_path', fallback_path)
@@ -65,12 +68,14 @@ class PhraseAppUpdaterCLI < Thor
65
68
  validate_writable_path!('destination_path', destination_path)
66
69
 
67
70
  begin
68
- files = PhraseAppUpdater.new(phraseapp_api_key, phraseapp_project_id).pull(fallback_path)
71
+ files = PhraseAppUpdater.new(phraseapp_api_key, phraseapp_project_id, file_format).pull(fallback_path)
69
72
  write_locale_files(destination_path, files)
70
73
  rescue PhraseAppUpdater::PhraseAppAPI::BadAPIKeyError => e
71
74
  puts "Bad PhraseApp API key."
72
75
  rescue PhraseAppUpdater::PhraseAppAPI::BadProjectIDError => e
73
76
  puts "Bad PhraseApp project ID: #{phraseapp_project_id}"
77
+ rescue PhraseAppUpdater::LocaleFile::BadFileTypeError => e
78
+ puts "Bad filetype for localization files: #{e.message}"
74
79
  rescue StandardError => e
75
80
  puts "Unknown error when pulling files"
76
81
  raise e
@@ -92,10 +97,10 @@ class PhraseAppUpdaterCLI < Thor
92
97
 
93
98
  private
94
99
 
95
- def load_phraseapp_credentials(options)
100
+ def load_phraseapp_configuration(options)
96
101
  if options[:config_file_path]
97
102
 
98
- if options[:phraseapp_api_key] || options[:phraseapp_project_id]
103
+ if [:phraseapp_api_key, :phraseapp_project_id, :file_format].any? { |option| options.has_key?(:option) }
99
104
  raise RuntimeError.new("Provided both a path to PhraseApp config file and command line arguments. Specify only one. #{options}")
100
105
  end
101
106
 
@@ -103,9 +108,11 @@ class PhraseAppUpdaterCLI < Thor
103
108
 
104
109
  phraseapp_api_key = config.api_key
105
110
  phraseapp_project_id = config.project_id
111
+ file_format = config.file_format
106
112
  else
107
113
  phraseapp_api_key = options.fetch(:phraseapp_api_key, ENV["PA_API_KEY"]).to_s
108
114
  phraseapp_project_id = options.fetch(:phraseapp_project_id, ENV["PA_PROJECT_ID"]).to_s
115
+ file_format = options.fetch(:file_format, ENV["PA_FILE_FORMAT"]).to_s
109
116
  end
110
117
 
111
118
  if phraseapp_api_key.empty?
@@ -116,7 +123,11 @@ class PhraseAppUpdaterCLI < Thor
116
123
  raise RuntimeError.new("Must provide Phraseapp project ID. --phraseapp_project_id or PA_PROJECT_ID")
117
124
  end
118
125
 
119
- return [phraseapp_api_key, phraseapp_project_id]
126
+ if file_format.empty?
127
+ raise RuntimeError.new("Must provide file format for Phraseapp project. --file_format or PA_FILE_FORMAT")
128
+ end
129
+
130
+ return [phraseapp_api_key, phraseapp_project_id, file_format]
120
131
  end
121
132
 
122
133
  def validate_path!(name, path)
@@ -2,15 +2,16 @@ require 'phraseapp_updater/version'
2
2
  require 'phraseapp_updater/index_by'
3
3
  require 'phraseapp_updater/differ'
4
4
  require 'phraseapp_updater/locale_file'
5
- require 'phraseapp_updater/locale_file_loader'
5
+ require 'phraseapp_updater/locale_file/loader'
6
6
  require 'phraseapp_updater/phraseapp_api'
7
7
  require 'phraseapp_updater/yml_config_loader'
8
8
 
9
9
  class PhraseAppUpdater
10
10
  using IndexBy
11
11
 
12
- def initialize(phraseapp_api_key, phraseapp_project_id)
13
- @phraseapp_api = PhraseAppAPI.new(phraseapp_api_key, phraseapp_project_id)
12
+ def initialize(phraseapp_api_key, phraseapp_project_id, file_format)
13
+ @locale_file_class = LocaleFile.class_for_file_format(file_format)
14
+ @phraseapp_api = PhraseAppAPI.new(phraseapp_api_key, phraseapp_project_id, @locale_file_class)
14
15
  end
15
16
 
16
17
  def self.load_config(config_file_path)
@@ -38,7 +39,7 @@ class PhraseAppUpdater
38
39
  primary: new_locale_file.parsed_content,
39
40
  secondary: phraseapp_file.parsed_content)
40
41
 
41
- LocaleFile.from_hash(previous_locale_file.name, resolved_content)
42
+ @locale_file_class.from_hash(previous_locale_file.name, resolved_content)
42
43
  end
43
44
 
44
45
  # Upload all of the secondary languages first,
@@ -88,7 +89,7 @@ class PhraseAppUpdater
88
89
  fallback = clear_empty_strings!(fallback_files[phraseapp_without_unverified_file.name].parsed_content)
89
90
 
90
91
  restore_unverified_originals!(fallback, with_unverified, without_unverified)
91
- LocaleFile.from_hash(phraseapp_without_unverified_file.name, without_unverified)
92
+ @locale_file_class.from_hash(phraseapp_without_unverified_file.name, without_unverified)
92
93
  end
93
94
  end
94
95
 
@@ -107,8 +108,9 @@ class PhraseAppUpdater
107
108
  end
108
109
 
109
110
  def load_locale_files(*paths)
111
+ loader = LocaleFile::Loader.new(@locale_file_class::EXTENSION)
110
112
  paths.map do |path|
111
- LocaleFileLoader.filenames(path).map { |l| LocaleFileLoader.load(l) }
113
+ loader.filenames(path).map { |l| loader.load(l) }
112
114
  end
113
115
  end
114
116
 
@@ -1,18 +1,28 @@
1
- require 'multi_json'
2
- require 'oj'
3
-
4
- # We're working with pure JSON, not
5
- # serialized Ruby objects
6
- Oj.default_options = {mode: :strict}
1
+ require "phraseapp_updater/locale_file/json_file"
2
+ require "phraseapp_updater/locale_file/yaml_file"
7
3
 
8
4
  class PhraseAppUpdater
9
5
  class LocaleFile
10
6
  attr_reader :name, :content, :parsed_content
11
7
 
8
+ class BadFileTypeError < StandardError ; end
9
+
12
10
  def self.from_hash(name, hash)
13
- new(name, MultiJson.dump(hash))
11
+ raise RuntimeError.new("Must be implemented in a subclass.")
12
+ end
13
+
14
+ def self.class_for_file_format(type)
15
+ case type.downcase
16
+ when "json"
17
+ JSONFile
18
+ when "yml", "yaml"
19
+ YAMLFile
20
+ else
21
+ raise BadFileTypeError.new("Invalid file type: #{type}")
22
+ end
14
23
  end
15
24
 
25
+ # Expects a Ruby hash
16
26
  def initialize(name, content)
17
27
  @name = name
18
28
  @content = content
@@ -25,20 +35,17 @@ class PhraseAppUpdater
25
35
  end
26
36
 
27
37
  def name_with_extension
28
- "#{name}.json"
38
+ "#{name}.#{self.class::EXTENSION}"
29
39
  end
30
40
 
31
41
  private
32
42
 
33
43
  def parse(content)
34
- MultiJson.load(content)
35
- rescue MultiJson::ParseError => e
36
- raise ArgumentError.new("Provided content was not valid JSON")
44
+ raise RuntimeError.new("Must be implemented in a subclass.")
37
45
  end
38
46
 
39
47
  def format_content!
40
- # Add indentation for better diffs
41
- @content = MultiJson.dump(MultiJson.load(@content), pretty: true)
48
+ raise RuntimeError.new("Must be implemented in a subclass.")
42
49
  end
43
50
  end
44
51
  end
@@ -0,0 +1,29 @@
1
+ require 'multi_json'
2
+ require 'oj'
3
+
4
+ # We're working with pure JSON, not
5
+ # serialized Ruby objects
6
+ Oj.default_options = {mode: :strict}
7
+
8
+ class PhraseAppUpdater
9
+ class LocaleFile
10
+ class JSONFile < LocaleFile
11
+ EXTENSION = "json"
12
+ def self.from_hash(name, hash)
13
+ new(name, MultiJson.dump(hash))
14
+ end
15
+
16
+ def parse(content)
17
+ MultiJson.load(content)
18
+ rescue MultiJson::ParseError => e
19
+ raise ArgumentError.new("Provided content was not valid JSON: #{e}")
20
+ end
21
+
22
+ def format_content!
23
+ # Add indentation for better diffs
24
+ @content = MultiJson.dump(MultiJson.load(@content), pretty: true)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,24 @@
1
+ require 'phraseapp_updater/locale_file'
2
+
3
+ class PhraseAppUpdater
4
+ class LocaleFile
5
+ class Loader
6
+ def initialize(extension)
7
+ @extension = extension
8
+ end
9
+
10
+ def load(filename)
11
+ unless File.readable?(filename) && File.file?(filename)
12
+ raise RuntimeError.new("Couldn't read localization file at #{filename}")
13
+ end
14
+
15
+ LocaleFile.class_for_file_format(@extension).new(File.basename(filename).chomp(".#{@extension}"), File.read(filename))
16
+ end
17
+
18
+ def filenames(locale_directory)
19
+ Dir["#{locale_directory}/*.#{@extension}"]
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,20 @@
1
+ require 'psych'
2
+ class PhraseAppUpdater
3
+ class LocaleFile
4
+ class YAMLFile < LocaleFile
5
+ EXTENSION = "yml"
6
+ def self.from_hash(name, hash)
7
+ new(name, Psych.dump(hash))
8
+ end
9
+
10
+ def parse(content)
11
+ Psych.load(content)
12
+ rescue Psych::SyntaxError => e
13
+ raise ArgumentError.new("Provided content was not valid YAML")
14
+ end
15
+
16
+ def format_content!
17
+ end
18
+ end
19
+ end
20
+ end
@@ -4,9 +4,10 @@ require 'thread'
4
4
 
5
5
  class PhraseAppUpdater
6
6
  class PhraseAppAPI
7
- def initialize(api_key, project_id)
8
- @client = PhraseApp::Client.new(PhraseApp::Auth::Credentials.new(token: api_key))
9
- @project_id = project_id
7
+ def initialize(api_key, project_id, locale_file_class)
8
+ @client = PhraseApp::Client.new(PhraseApp::Auth::Credentials.new(token: api_key))
9
+ @project_id = project_id
10
+ @locale_file_class = locale_file_class
10
11
  end
11
12
 
12
13
  def download_locales
@@ -23,7 +24,7 @@ class PhraseAppUpdater
23
24
  puts "Downloading file for #{locale}"
24
25
  download_file(locale, skip_unverified)
25
26
  end.map do |locale, file_contents|
26
- LocaleFile.new(locale.name, file_contents)
27
+ @locale_file_class.new(locale.name, file_contents)
27
28
  end
28
29
  end
29
30
 
@@ -1,3 +1,3 @@
1
1
  class PhraseAppUpdater
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -2,7 +2,7 @@ require 'yaml'
2
2
 
3
3
  class PhraseAppUpdater
4
4
  class YMLConfigLoader
5
- attr_reader :api_key, :project_id
5
+ attr_reader :api_key, :project_id, :file_format
6
6
  def initialize(file_path)
7
7
  unless File.readable?(file_path)
8
8
  raise RuntimeError.new("Can't read config file at #{file_path}")
@@ -14,8 +14,33 @@ class PhraseAppUpdater
14
14
  raise RuntimeError.new("Couldn't parse file contents: #{File.read(file_path)}")
15
15
  end
16
16
 
17
- @api_key = parsed_yaml.fetch("phraseapp").fetch("access_token")
18
- @project_id = parsed_yaml.fetch("phraseapp").fetch("project_id")
17
+ config = parsed_yaml.fetch("phraseapp")
18
+
19
+ @api_key = config.fetch("access_token")
20
+ @project_id = config.fetch("project_id")
21
+
22
+ push_file_format = config.fetch("push").fetch("sources").first.fetch("params").fetch("file_format")
23
+ pull_file_format = config.fetch("pull").fetch("targets").first.fetch("params").fetch("file_format")
24
+
25
+ unless push_file_format == pull_file_format
26
+ raise ArgumentError.new("Push and pull must be the same format")
27
+ end
28
+
29
+ @file_format = convert(push_file_format)
30
+ end
31
+
32
+
33
+ private
34
+
35
+ def convert(phraseapp_file_format)
36
+ case phraseapp_file_format
37
+ when "nested_json"
38
+ "json"
39
+ when "yml"
40
+ "yml"
41
+ else
42
+ raise ArugmentError.new("Unsupported type: #{phraseapp_file_format}")
43
+ end
19
44
  end
20
45
  end
21
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phraseapp_updater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Griffin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-21 00:00:00.000000000 Z
11
+ date: 2017-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -174,7 +174,9 @@ files:
174
174
  - lib/phraseapp_updater/differ.rb
175
175
  - lib/phraseapp_updater/index_by.rb
176
176
  - lib/phraseapp_updater/locale_file.rb
177
- - lib/phraseapp_updater/locale_file_loader.rb
177
+ - lib/phraseapp_updater/locale_file/json_file.rb
178
+ - lib/phraseapp_updater/locale_file/loader.rb
179
+ - lib/phraseapp_updater/locale_file/yaml_file.rb
178
180
  - lib/phraseapp_updater/phraseapp_api.rb
179
181
  - lib/phraseapp_updater/version.rb
180
182
  - lib/phraseapp_updater/yml_config_loader.rb
@@ -199,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
201
  version: '0'
200
202
  requirements: []
201
203
  rubyforge_project:
202
- rubygems_version: 2.5.1
204
+ rubygems_version: 2.6.11
203
205
  signing_key:
204
206
  specification_version: 4
205
207
  summary: A three-way differ for PhraseApp projects.
@@ -1,18 +0,0 @@
1
- require 'phraseapp_updater/locale_file'
2
-
3
- class PhraseAppUpdater
4
- class LocaleFileLoader
5
- def self.load(filename)
6
- unless File.readable?(filename) && File.file?(filename)
7
- raise RuntimeError.new("Couldn't read localization file at #{filename}")
8
- end
9
-
10
- LocaleFile.new(File.basename(filename).chomp(".json"), File.read(filename))
11
- end
12
-
13
- def self.filenames(locale_directory)
14
- Dir["#{locale_directory}/*.json"]
15
- end
16
- end
17
- end
18
-