phrase 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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: