phrase 0.2.0 → 0.2.1

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.
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  *.gem
2
2
  .phrase
3
+ .DS_Store
4
+ /phrase/
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Phrase - the Gem
1
+ # phrase
2
2
 
3
3
  ### Installation and usage
4
4
 
@@ -12,4 +12,4 @@
12
12
 
13
13
  ### More Information
14
14
 
15
- http://phraseapp.com
15
+ https://phraseapp.com
@@ -1,6 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  require 'addressable/uri'
4
+ require 'cgi'
4
5
  require 'net/http'
5
6
  require 'net/https'
6
7
  require 'phrase'
@@ -44,24 +45,6 @@ class Phrase::Api::Client
44
45
  keys = extract_structured_object(result["translate"]) if result["translate"]
45
46
  keys
46
47
  end
47
-
48
- def extract_structured_object(translation)
49
- if translation.is_a?(Hash)
50
- symbolize_keys(translation)
51
- else
52
- translation
53
- end
54
- end
55
-
56
- def symbolize_keys(hash)
57
- hash.symbolize_keys!
58
- hash.each do |key, value|
59
- if value.is_a?(Hash)
60
- hash[key] = symbolize_keys(value)
61
- end
62
- end
63
- hash
64
- end
65
48
 
66
49
  def find_keys_by_name(key_names=[])
67
50
  result = JSON.parse(perform_api_request("/translation_keys", :get, {:key_names => key_names}))
@@ -103,12 +86,14 @@ class Phrase::Api::Client
103
86
  end
104
87
  end
105
88
 
106
- def upload(filename, file_content)
89
+ def upload(filename, file_content, tags=[])
107
90
  begin
108
- perform_api_request("/translation_keys/upload", :post, {
91
+ params = {
109
92
  "filename" => filename,
110
93
  "file_content" => file_content,
111
- })
94
+ "tags[]" => tags
95
+ }
96
+ perform_api_request("/translation_keys/upload", :post, params)
112
97
  rescue Phrase::Api::Exceptions::ServerError => e
113
98
  raise "File #{filename} could not be uploaded"
114
99
  end
@@ -116,6 +101,24 @@ class Phrase::Api::Client
116
101
  end
117
102
 
118
103
  private
104
+ def extract_structured_object(translation)
105
+ if translation.is_a?(Hash)
106
+ symbolize_keys(translation)
107
+ else
108
+ translation
109
+ end
110
+ end
111
+
112
+ def symbolize_keys(hash)
113
+ hash.symbolize_keys!
114
+ hash.each do |key, value|
115
+ if value.is_a?(Hash)
116
+ hash[key] = symbolize_keys(value)
117
+ end
118
+ end
119
+ hash
120
+ end
121
+
119
122
  def display_api_error(response)
120
123
  error_message = api_error_message(response)
121
124
  $stderr.puts error_message
@@ -171,7 +174,7 @@ private
171
174
  params.merge!({
172
175
  'auth_token' => @auth_token
173
176
  })
174
- request.set_form_data(params)
177
+ set_form_data(request, params)
175
178
  request
176
179
  end
177
180
 
@@ -180,7 +183,7 @@ private
180
183
  params.merge!({
181
184
  'auth_token' => @auth_token
182
185
  })
183
- request.set_form_data(params)
186
+ set_form_data(request, params)
184
187
  request
185
188
  end
186
189
 
@@ -195,4 +198,17 @@ private
195
198
  client.ca_file = File.join(File.dirname(__FILE__), "..", "..", "cacert.pem")
196
199
  client
197
200
  end
198
- end
201
+
202
+ # Support for arrays in POST data
203
+ # http://blog.assimov.net/blog/2010/06/01/postput-arrays-with-ruby-nethttp-set_form_data/
204
+ def set_form_data(request, params, separator='&')
205
+ request.body = params.map do |key, value|
206
+ if value.instance_of?(Array)
207
+ value.map {|value_item| "#{CGI::escape(key.to_s)}=#{CGI::escape(value_item.to_s)}"}.join(separator)
208
+ else
209
+ "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"
210
+ end
211
+ end.join(separator)
212
+ request.content_type = 'application/x-www-form-urlencoded'
213
+ end
214
+ end
@@ -1,24 +1,27 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- require 'optparse'
4
- require 'net/http'
5
- require 'net/https'
6
3
  require 'fileutils'
7
- require 'phrase/tool_config'
4
+ require 'rubygems'
5
+ require 'phrase'
8
6
 
9
7
  class Phrase::Tool
8
+ autoload :Config, 'phrase/tool/config'
9
+ autoload :Options, 'phrase/tool/options'
10
+ autoload :TagValidator, 'phrase/tool/tag_validator'
10
11
 
11
- attr_accessor :config
12
+ attr_accessor :config, :options
12
13
 
13
14
  def initialize(argv)
14
15
  @args = argv
15
16
  end
16
17
 
17
18
  def run
18
- @config = Phrase::ToolConfig.new
19
- command = args.first
19
+ subcommand = @args.first
20
+
21
+ @config = Phrase::Tool::Config.new
22
+ @options = Phrase::Tool::Options.new(@args, subcommand)
20
23
 
21
- case command
24
+ case subcommand
22
25
  when /init/
23
26
  init
24
27
  when /push/
@@ -26,42 +29,47 @@ class Phrase::Tool
26
29
  when /pull/
27
30
  pull
28
31
  else
29
- print_usage
32
+ if @options.get(:version)
33
+ print_version
34
+ else
35
+ print_usage
36
+ end
30
37
  end
31
38
  end
32
39
 
33
- protected
40
+ protected
41
+
34
42
  def init
35
- secret_param = args.find{ |arg| arg =~ /secret=/ }
36
- unless secret_param.to_s.match(/secret=.+/)
43
+ secret = @options.get(:secret)
44
+ unless secret.present?
37
45
  $stderr.puts "Need a secret to init, but found none."
38
46
  $stderr.puts "Please provide the --secret=YOUR_SECRET parameter."
39
47
  exit(41)
40
48
  end
41
-
42
- secret = secret_param.split("=", 2).last
49
+
43
50
  @config.secret = secret
44
51
  puts "Wrote secret to config file .phrase"
45
52
 
46
- default_locale_param = args.find{ |arg| arg =~ /default-locale=/ }
47
- if default_locale_param.to_s.match(/default-locale=.+/)
48
- locale_name = default_locale_param.split("=", 2).last
49
- else
50
- locale_name = "en"
51
- end
52
- create_locale(locale_name)
53
- make_locale_default(locale_name)
53
+ default_locale_name = @options.get(:default_locale)
54
+ create_locale(default_locale_name)
55
+ make_locale_default(default_locale_name)
54
56
  end
55
57
 
56
58
  def push
57
59
  check_config_available
60
+ tags = @options.get(:tags)
61
+ unless tags.empty? or valid_tags_are_given?(tags)
62
+ $stderr.puts "Invalid tags: Only letters, numbers, underscores and dashes are allowed"
63
+ exit(43)
64
+ end
58
65
 
59
66
  files = choose_files_to_upload
60
67
  if files.empty?
61
68
  puts "Could not find any files to upload :("
62
69
  exit(43)
63
70
  end
64
- upload_files(files)
71
+
72
+ upload_files(files, tags)
65
73
  end
66
74
 
67
75
  def pull
@@ -85,17 +93,23 @@ protected
85
93
 
86
94
  def print_usage
87
95
  $stderr.puts <<USAGE
88
- Welcome to phrase!
89
-
90
- phrase Prints usage
96
+ usage: phrase <command> [<args>]
91
97
 
92
- phrase init --secret=<YOUR SECRET> --default-locale=<YOUR DEFAULT LOCALE>
98
+ phrase init --secret=<YOUR SECRET> --default-locale=<DEFAULT LOCALE>
93
99
 
94
- phrase push FILE
95
- phrase push DIRECTORY
100
+ phrase push FILE [--tags=<tags>]
101
+ phrase push DIRECTORY [--tags=<tags>]
102
+
103
+ phrase pull [LOCALE]
104
+
105
+ phrase --version
96
106
  USAGE
97
107
  end
98
108
 
109
+ def print_version
110
+ puts "phrase version #{Phrase::VERSION}"
111
+ end
112
+
99
113
  private
100
114
  def choose_files_to_upload
101
115
  file_name = args[1]
@@ -124,7 +138,7 @@ private
124
138
  end
125
139
  end
126
140
 
127
- def upload_files(files)
141
+ def upload_files(files, tags=[])
128
142
  files.each do |file|
129
143
  proceed_with_upload = true
130
144
 
@@ -134,13 +148,14 @@ private
134
148
 
135
149
  if is_yaml_file(file)
136
150
  proceed_with_upload = false
137
- $stderr.puts "Notice: Could not upload #{file} (extension not supported - see http://phraseapp.com/help for more information)"
151
+ $stderr.puts "Notice: Could not upload #{file} (extension not supported)"
138
152
  end
139
153
 
140
154
  if proceed_with_upload
141
155
  begin
142
- puts "Uploading #{file}..."
143
- api_client.upload(file, File.read(file))
156
+ tagged = " (tagged: #{tags.join(", ")})" if tags.size > 0
157
+ puts "Uploading #{file}#{tagged}..."
158
+ api_client.upload(file, File.read(file), tags)
144
159
  puts "OK"
145
160
  rescue Exception => e
146
161
  puts "Failed"
@@ -209,11 +224,6 @@ private
209
224
  def args
210
225
  @args
211
226
  end
212
-
213
- def puts_debug_info
214
- puts "ARGS: #{args.join(",")}"
215
- puts "Dir.pwd: #{Dir.pwd}"
216
- end
217
227
 
218
228
  def is_yaml_file(filepath)
219
229
  !File.directory?(filepath) && filepath.split('.').last != 'yml'
@@ -230,6 +240,13 @@ private
230
240
  end
231
241
  end
232
242
 
243
+ def valid_tags_are_given?(tags)
244
+ tags.each do |tag|
245
+ return false unless TagValidator.valid?(tag)
246
+ end
247
+ true
248
+ end
249
+
233
250
  def self.rails_default_locale_folder
234
251
  "./config/locales/"
235
252
  end
@@ -1,9 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- require 'phrase'
4
3
  require 'json'
5
4
 
6
- class Phrase::ToolConfig
5
+ class Phrase::Tool::Config
7
6
  def initialize
8
7
  if File.exist?(".phrase")
9
8
  begin
@@ -0,0 +1,69 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'optparse'
4
+
5
+ class Phrase::Tool::Options
6
+
7
+ def initialize(args, command="")
8
+ @command = command
9
+ @data = {
10
+ default: {
11
+ version: false,
12
+ help: false
13
+ },
14
+ init: {
15
+ secret: "",
16
+ default_locale: "en"
17
+ },
18
+ push: {
19
+ tags: []
20
+ },
21
+ pull: {
22
+ }
23
+ }
24
+ options.parse!(args)
25
+ end
26
+
27
+ def get(name)
28
+ return @data.fetch(command_name).fetch(name.to_sym)
29
+ rescue => e
30
+ $stderr.puts "Invalid or missing option \"#{name}\" for command \"#{command_name}\""
31
+ end
32
+
33
+ private
34
+
35
+ def options
36
+ case command_name
37
+ when :init
38
+ OptionParser.new do |opts|
39
+ opts.on("--secret=YOUR_AUTH_TOKEN", String, "Your auth token") do |secret|
40
+ @data[command_name][:secret] = secret
41
+ end
42
+
43
+ opts.on("--default-locale=en", String, "The default locale for your application") do |default_locale|
44
+ @data[command_name][:default_locale] = default_locale
45
+ end
46
+ end
47
+ when :push
48
+ OptionParser.new do |opts|
49
+ opts.on("--tags=foo,bar", Array, "List of tags for phrase push (separated by comma)") do |tags|
50
+ @data[command_name][:tags] = tags
51
+ end
52
+ end
53
+ else
54
+ OptionParser.new do |opts|
55
+ opts.on_tail("-v", "--version", "Show version number") do |version|
56
+ @data[:default][:version] = true
57
+ end
58
+
59
+ opts.on_tail("-h", "--help", "Show help") do |help|
60
+ @data[:default][:help] = true
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def command_name
67
+ @command_name ||= (@command.present? and @data.has_key?(@command.to_sym)) ? @command.to_sym : :default
68
+ end
69
+ end
@@ -0,0 +1,9 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::TagValidator
4
+ FORMAT = /\A[a-zA-Z0-9_-]+\z/
5
+
6
+ def self.valid?(tag_name)
7
+ (tag_name.to_s =~ FORMAT)
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Phrase
2
+ VERSION = "0.2.1"
3
+ end
@@ -2,15 +2,17 @@
2
2
  lib = File.expand_path('../lib/', __FILE__)
3
3
  $:.unshift lib unless $:.include?(lib)
4
4
 
5
+ require 'phrase/version'
6
+
5
7
  Gem::Specification.new do |s|
6
8
  s.name = "phrase"
7
- s.version = "0.2.0"
9
+ s.version = Phrase::VERSION
8
10
  s.platform = Gem::Platform::RUBY
9
11
  s.authors = ["Dynport GmbH"]
10
12
  s.email = ["info@phraseapp.com"]
11
- s.homepage = "http://phraseapp.com"
13
+ s.homepage = "https://phraseapp.com"
12
14
  s.summary = %q{The best way to manage i18n.}
13
- s.description = %q{phrase allows you to edit translations inline, on the page itself. More information at phraseapp.com}
15
+ s.description = %q{phrase allows you to edit translations in-place on the page itself. More information at phraseapp.com}
14
16
  s.required_rubygems_version = ">= 1.3.6"
15
17
  s.rubyforge_project = "phrase"
16
18
  git_files = `git ls-files | grep -v spec/`.split("\n") rescue ''
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phrase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-26 00:00:00.000000000 Z
12
+ date: 2012-08-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &70308573413560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,15 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '3.0'
24
+ version_requirements: *70308573413560
30
25
  - !ruby/object:Gem::Dependency
31
26
  name: addressable
32
- requirement: !ruby/object:Gem::Requirement
27
+ requirement: &70308573428880 !ruby/object:Gem::Requirement
33
28
  none: false
34
29
  requirements:
35
30
  - - ~>
@@ -37,13 +32,8 @@ dependencies:
37
32
  version: 2.2.8
38
33
  type: :runtime
39
34
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: 2.2.8
46
- description: phrase allows you to edit translations inline, on the page itself. More
35
+ version_requirements: *70308573428880
36
+ description: phrase allows you to edit translations in-place on the page itself. More
47
37
  information at phraseapp.com
48
38
  email:
49
39
  - info@phraseapp.com
@@ -70,10 +60,13 @@ files:
70
60
  - lib/phrase/delegate.rb
71
61
  - lib/phrase/engine.rb
72
62
  - lib/phrase/tool.rb
73
- - lib/phrase/tool_config.rb
63
+ - lib/phrase/tool/config.rb
64
+ - lib/phrase/tool/options.rb
65
+ - lib/phrase/tool/tag_validator.rb
66
+ - lib/phrase/version.rb
74
67
  - lib/phrase/view_helpers.rb
75
68
  - phrase.gemspec
76
- homepage: http://phraseapp.com
69
+ homepage: https://phraseapp.com
77
70
  licenses: []
78
71
  post_install_message:
79
72
  rdoc_options: []
@@ -93,9 +86,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
86
  version: 1.3.6
94
87
  requirements: []
95
88
  rubyforge_project: phrase
96
- rubygems_version: 1.8.24
89
+ rubygems_version: 1.8.10
97
90
  signing_key:
98
91
  specification_version: 3
99
92
  summary: The best way to manage i18n.
100
93
  test_files: []
101
- has_rdoc: