one_sky 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Gemfile.lock +13 -13
- data/README.rdoc +51 -7
- data/Rakefile +17 -1
- data/lib/one_sky.rb +8 -9
- data/lib/one_sky/client.rb +80 -0
- data/lib/one_sky/platform.rb +36 -0
- data/lib/one_sky/project.rb +22 -114
- data/lib/one_sky/translation.rb +129 -0
- data/lib/one_sky/utility.rb +38 -0
- data/lib/one_sky/version.rb +1 -1
- data/one_sky.gemspec +4 -3
- data/spec/client_spec.rb +172 -0
- data/spec/files/example.po +2 -0
- data/spec/files/example.yml +5 -0
- data/spec/live_spec.rb +187 -0
- data/spec/platform_spec.rb +101 -0
- data/spec/project_spec.rb +67 -48
- data/spec/{helpers.rb → spec_helper.rb} +2 -2
- data/spec/translation_spec.rb +124 -0
- data/spec/utility_spec.rb +98 -0
- metadata +45 -21
- data/spec/data/en-us.po +0 -21
- data/spec/input_spec.rb +0 -64
- data/spec/output_spec.rb +0 -35
data/.rspec
ADDED
data/Gemfile.lock
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
one_sky (0.0
|
5
|
-
json (
|
6
|
-
rest-client (
|
4
|
+
one_sky (2.0.0)
|
5
|
+
json (>= 1.4.0)
|
6
|
+
rest-client (>= 1.4.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
-
diff-lcs (1.1.
|
12
|
-
json (1.
|
13
|
-
mime-types (1.
|
14
|
-
rest-client (1.6.
|
11
|
+
diff-lcs (1.1.3)
|
12
|
+
json (1.6.3)
|
13
|
+
mime-types (1.17.2)
|
14
|
+
rest-client (1.6.7)
|
15
15
|
mime-types (>= 1.16)
|
16
16
|
rspec (2.2.0)
|
17
17
|
rspec-core (~> 2.2)
|
18
18
|
rspec-expectations (~> 2.2)
|
19
19
|
rspec-mocks (~> 2.2)
|
20
|
-
rspec-core (2.
|
21
|
-
rspec-expectations (2.
|
20
|
+
rspec-core (2.7.1)
|
21
|
+
rspec-expectations (2.7.0)
|
22
22
|
diff-lcs (~> 1.1.2)
|
23
|
-
rspec-mocks (2.
|
23
|
+
rspec-mocks (2.7.0)
|
24
|
+
time_freeze (0.1.1)
|
24
25
|
|
25
26
|
PLATFORMS
|
26
27
|
ruby
|
27
28
|
|
28
29
|
DEPENDENCIES
|
29
|
-
bundler (
|
30
|
-
json (~> 1.4.6)
|
30
|
+
bundler (>= 1.0.0)
|
31
31
|
one_sky!
|
32
|
-
rest-client (~> 1.6.1)
|
33
32
|
rspec (~> 2.2.0)
|
33
|
+
time_freeze
|
data/README.rdoc
CHANGED
@@ -20,23 +20,67 @@ Provide the API key and secret which you can get from the web UI. Also provide t
|
|
20
20
|
|
21
21
|
require 'one_sky'
|
22
22
|
|
23
|
-
client = OneSky::
|
23
|
+
client = OneSky::Client.new("my_api_key", "my_api_secret")
|
24
|
+
|
25
|
+
== Project
|
26
|
+
|
27
|
+
Load a specific project by name.
|
28
|
+
|
29
|
+
project = client.project("my-project-name")
|
30
|
+
|
31
|
+
Get the details of the project.
|
32
|
+
|
33
|
+
project.details
|
34
|
+
|
35
|
+
See what platforms are available.
|
36
|
+
|
37
|
+
project.platforms
|
38
|
+
|
39
|
+
== Platform
|
40
|
+
|
41
|
+
Load a specific platform by id.
|
42
|
+
|
43
|
+
platform = project.platform(123)
|
44
|
+
|
45
|
+
Get the details of the platform.
|
46
|
+
|
47
|
+
platform.details
|
48
|
+
|
49
|
+
See what locales are assigned.
|
50
|
+
|
51
|
+
platform.locales
|
52
|
+
|
53
|
+
== Translation
|
54
|
+
|
55
|
+
Get the translation proxy for a platform
|
56
|
+
|
57
|
+
translation = platform.translation
|
24
58
|
|
25
59
|
== Submit a phrase for translation
|
26
60
|
|
27
|
-
Submit a string
|
61
|
+
Submit a single string:
|
62
|
+
|
63
|
+
translation.input_string("Hello World!")
|
64
|
+
|
65
|
+
Submit a string with a specified key, context, and page:
|
66
|
+
|
67
|
+
translation.input_string(:string => "Hello World!", :string_key => "hello_world", :context => "my_context", :page => "my_page")
|
28
68
|
|
29
|
-
|
69
|
+
Submit many strings at once:
|
30
70
|
|
31
|
-
|
71
|
+
translation.input_strings(["Hello World!", "Goodbye Moon"])
|
72
|
+
|
73
|
+
== Provide a translation
|
32
74
|
|
33
|
-
|
75
|
+
Submit a translation for a given key, in a given locale.
|
34
76
|
|
77
|
+
translation.translate("hello_world", "zh_CN", "世界你好")
|
78
|
+
|
35
79
|
== Get translated phrases
|
36
80
|
|
37
81
|
So you've submitted your phrases and your translators/users or the general OneSky[link:http://www.oneskyapp.com] community have kindly provided translations for them. It's time now to download those.
|
38
82
|
|
39
|
-
translations =
|
83
|
+
translations = translation.output
|
40
84
|
|
41
85
|
=> {"Default"=>{"en-us"=>{"Hello World!"=>"Hello World!"}, "zh-cn"=>{"Hello World!"=>"\u4E16\u754C\u60A8\u597D\uFF01"}, "es-es"=>{"Hello World!"=>"Hola Mundo!"}}}
|
42
86
|
|
@@ -52,7 +96,7 @@ Let's pretty print that a bit:
|
|
52
96
|
|
53
97
|
OneSky[link:http://www.oneskyapp.com] is all about community-powered translations. You can mark your project as public and any OneSky[link:http://www.oneskyapp.com] member translator can help in translating your phrases. Or if you mark it as private, only those you give access to can do so. For instance, you'd like your own website's user base to help you out. You can take a user's unique ID and bind it to OneSky:
|
54
98
|
|
55
|
-
client.get_sso_link("myapp/users/123456")
|
99
|
+
client.utility.get_sso_link("myapp/users/123456", "fred")
|
56
100
|
=> "http://rubyredtomatoes.oneskyapp.com/?time=1294020222&id=myapp/users/123456&data=3d56315499647c0419867283767aa270"
|
57
101
|
|
58
102
|
That user can then use the above link to automatically sign in to OneSky[link:http://www.oneskyapp.com] and start translating for you.
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'bundler'
|
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
4
|
# = RDoc
|
5
|
-
require '
|
5
|
+
require 'rdoc/task'
|
6
6
|
|
7
7
|
Rake::RDocTask.new do |t|
|
8
8
|
t.rdoc_dir = 'rdoc'
|
@@ -11,3 +11,19 @@ Rake::RDocTask.new do |t|
|
|
11
11
|
t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'CHANGELOG', 'CREDITS', 'lib/**/*.rb')
|
12
12
|
end
|
13
13
|
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
|
16
|
+
desc 'Default: run specs.'
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
desc "Run specs"
|
20
|
+
RSpec::Core::RakeTask.new do |t|
|
21
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
22
|
+
t.rspec_opts = %w{--tag ~live}
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Run live specs"
|
26
|
+
RSpec::Core::RakeTask.new("spec:live") do |t|
|
27
|
+
t.pattern = "./spec/**/*_spec.rb"
|
28
|
+
t.rspec_opts = %w{--tag live}
|
29
|
+
end
|
data/lib/one_sky.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'rest_client'
|
3
|
-
require 'digest/md5'
|
4
|
-
|
5
1
|
module OneSky
|
6
|
-
|
7
|
-
|
8
|
-
autoload :Project, 'one_sky/project'
|
9
|
-
|
10
|
-
%w[ApiError].each { |e| const_set(e, Class.new(StandardError)) }
|
2
|
+
class ApiError < StandardError; end
|
11
3
|
end
|
4
|
+
|
5
|
+
require 'one_sky/client'
|
6
|
+
require 'one_sky/platform'
|
7
|
+
require 'one_sky/project'
|
8
|
+
require 'one_sky/translation'
|
9
|
+
require 'one_sky/utility'
|
10
|
+
require 'one_sky/version'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module OneSky
|
6
|
+
|
7
|
+
# Base implemention of an API client
|
8
|
+
# based on the one sky api version 2.
|
9
|
+
#
|
10
|
+
# http://developer.oneskyapp.com/api/list/v/2
|
11
|
+
#
|
12
|
+
class Client
|
13
|
+
attr_accessor :api_key, :api_secret
|
14
|
+
|
15
|
+
# Provide the name of the project you created on the OneSky website. Also, the API key and secret shown there.
|
16
|
+
def initialize(api_key, api_secret)
|
17
|
+
raise ArgumentError, "api_key, api_secret cannot be nil." unless [api_key, api_secret].all?
|
18
|
+
@api_key, @api_secret = api_key, api_secret
|
19
|
+
end
|
20
|
+
|
21
|
+
# Api version 2
|
22
|
+
API_ROOT_URL = "https://api.oneskyapp.com/2/".freeze
|
23
|
+
|
24
|
+
def get(path, params = {}, options = nil)
|
25
|
+
fetch_response :get, path, params, options
|
26
|
+
end
|
27
|
+
|
28
|
+
def post(path, params = {}, options = nil)
|
29
|
+
fetch_response :post, path, params, options
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an API proxy for the Project Management API
|
33
|
+
def project(name)
|
34
|
+
Project.new(name, self)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an API proxy for the Utility API
|
38
|
+
def utility
|
39
|
+
Utility.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# dev-hash = MD5( CONCATENATE( timestamp , YOUR_API_SECRET ) )
|
45
|
+
def authorization_params
|
46
|
+
timestamp = Time.now.to_i.to_s
|
47
|
+
{:"api-key" => self.api_key, :timestamp => timestamp, :"dev-hash" => Digest::MD5.hexdigest(timestamp + self.api_secret)}
|
48
|
+
end
|
49
|
+
|
50
|
+
def fetch_response(http_verb, path, params, options)
|
51
|
+
options ||= {:content_type => "text/plain; charset=UTF-8"}
|
52
|
+
params = authorization_params.merge(params)
|
53
|
+
path = api_path(path)
|
54
|
+
|
55
|
+
response = case http_verb
|
56
|
+
when :get
|
57
|
+
RestClient.get(path, {:params => params}.merge(options))
|
58
|
+
when :post
|
59
|
+
RestClient.post(path, params.merge(options))
|
60
|
+
else
|
61
|
+
raise "bad http verb"
|
62
|
+
end
|
63
|
+
|
64
|
+
parse_response(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
def api_path(path)
|
68
|
+
URI.join(API_ROOT_URL, path.gsub(/^\//, "")).to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_response(response)
|
72
|
+
# Do not post-process response if it isn't JSON. (ie. Binary file returned from /output_mo.)
|
73
|
+
return response.body unless response.headers[:content_type] =~ /json/
|
74
|
+
response_hash = JSON.parse(response.body)
|
75
|
+
|
76
|
+
raise ApiError, response.body if response_hash.has_key?("response") and response_hash["response"] != "ok"
|
77
|
+
response_hash
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module OneSky
|
2
|
+
|
3
|
+
# Implements the Platform Management API for a given :platform_id
|
4
|
+
class Platform
|
5
|
+
|
6
|
+
attr_accessor :platform_id, :client
|
7
|
+
|
8
|
+
# Provide the id of the platform, together with an instance of OneSky::Client.
|
9
|
+
def initialize(platform_id, client)
|
10
|
+
@platform_id = platform_id
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the information of the platform.
|
15
|
+
def details
|
16
|
+
get("platform/details")["platform"]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get the details of activated locales for a platform.
|
20
|
+
def locales
|
21
|
+
get("platform/locales")["locales"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an API proxy for the Translation I/O API for this platform
|
25
|
+
def translation
|
26
|
+
Translation.new(platform_id, client)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def get(path, params={})
|
32
|
+
client.get(path, params.merge(:"platform-id" => platform_id))
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/one_sky/project.rb
CHANGED
@@ -1,127 +1,35 @@
|
|
1
1
|
module OneSky
|
2
|
-
|
2
|
+
|
3
|
+
# Implements the Project Management API for a given :project_name
|
3
4
|
class Project
|
4
|
-
attr_accessor :
|
5
|
+
attr_accessor :project_name, :client
|
5
6
|
|
6
|
-
# Provide the name of the project
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
@
|
7
|
+
# Provide the name of the project, together with an instance of OneSky::Client.
|
8
|
+
def initialize(project_name, client)
|
9
|
+
@project_name = project_name
|
10
|
+
@client = client
|
10
11
|
end
|
11
12
|
|
12
|
-
#
|
13
|
-
def
|
14
|
-
get("
|
15
|
-
end
|
16
|
-
|
17
|
-
# Returns available types to be specified when creating a new the project.
|
18
|
-
def types
|
19
|
-
get("/project/types")["types"]
|
20
|
-
end
|
21
|
-
|
22
|
-
# Returns an array of available languages.
|
23
|
-
def languages
|
24
|
-
get("/project/languages")["languages"]
|
25
|
-
end
|
26
|
-
|
27
|
-
# Returns info about your account.
|
28
|
-
def info
|
29
|
-
get("/info")["application"]
|
30
|
-
end
|
31
|
-
|
32
|
-
# Returns the SSO URL for the given unique_id
|
33
|
-
def get_sso_link(unique_id)
|
34
|
-
get("/sso/get-link", {:"unique-id" => unique_id})["url"]
|
35
|
-
end
|
36
|
-
|
37
|
-
# Submits a string for translation.
|
38
|
-
# Takes an optional hash of parameters, too:
|
39
|
-
# * :string_key
|
40
|
-
# * :context
|
41
|
-
# * :page
|
42
|
-
def input(string, options = {})
|
43
|
-
options = {:string => string}.merge(options)
|
44
|
-
post("/string/input", hash_to_params(options))
|
45
|
-
true
|
46
|
-
end
|
47
|
-
|
48
|
-
# Submits a bulk translation request.
|
49
|
-
# Parameters:
|
50
|
-
# * hashes - An array of hashes with mandatory key :string and optional keys (:string_key, :context)
|
51
|
-
# * An optional hash with keys:
|
52
|
-
# * :page
|
53
|
-
def input_bulk(hashes, options = {})
|
54
|
-
arrs = hashes.inject([[],[],[]]) do |o,e|
|
55
|
-
o[0] << e[:string]; o[1] << e[:string_key]; o[2] << e[:context]; o
|
56
|
-
end
|
57
|
-
|
58
|
-
post("/string/input-bulk", {:strings => arrs[0], :"string-keys" => arrs[1], :contexts => arrs[2]}.merge(options))
|
59
|
-
true
|
60
|
-
end
|
61
|
-
|
62
|
-
# Uploads a PO file for the given language.
|
63
|
-
# See #languages for a list of supported language codes.
|
64
|
-
# Takes an optional hash of parameters:
|
65
|
-
# * :page
|
66
|
-
def input_po(language, file_name, options = {})
|
67
|
-
response = post("/string/input-po", {:language => language, :file => File.new(file_name, "rb")}.merge(options), {})
|
68
|
-
true
|
13
|
+
# Return all platforms in a project.
|
14
|
+
def platforms
|
15
|
+
get("project/platforms")["platforms"]
|
69
16
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# Takes these optional parameters, too:
|
75
|
-
# * :language
|
76
|
-
# * :page
|
77
|
-
# * :md5
|
78
|
-
#--
|
79
|
-
# ToDo: Probably need to expose MD5 response values for caching implementations.
|
80
|
-
#++
|
81
|
-
def output(options = {})
|
82
|
-
get("/string/output", hash_to_params(options))["translation"]
|
17
|
+
|
18
|
+
# Get the information of the project.
|
19
|
+
def details
|
20
|
+
get("project/details")["project"]
|
83
21
|
end
|
84
|
-
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
# * :page
|
89
|
-
def output_mo(language, file_name, options = {})
|
90
|
-
response = get("/string/output-mo", {:language => language}.merge(options))
|
91
|
-
File.open(file_name, "w") { |f| f.print response }
|
92
|
-
true
|
22
|
+
|
23
|
+
# Returns an API proxy for the Platform Management API for the given platform_id.
|
24
|
+
def platform(platform_id)
|
25
|
+
Platform.new(platform_id, client)
|
93
26
|
end
|
94
|
-
|
95
|
-
alias_method :download_mo, :output_mo
|
96
|
-
|
27
|
+
|
97
28
|
protected
|
98
|
-
|
99
|
-
def
|
100
|
-
|
29
|
+
|
30
|
+
def get(path, params={})
|
31
|
+
client.get(path, params.merge(:project => project_name))
|
101
32
|
end
|
102
33
|
|
103
|
-
def authorization_params
|
104
|
-
timestamp = Time.now.to_i.to_s
|
105
|
-
{:project => @name, :"api-key" => @api_key, :timestamp => timestamp, :"dev-hash" => Digest::MD5.hexdigest(timestamp + @api_secret)}
|
106
|
-
end
|
107
|
-
|
108
|
-
def get(path, params = {}, options = {:content_type => "text/plain; charset=UTF-8"})
|
109
|
-
params = authorization_params.merge(params)
|
110
|
-
parse_response(RestClient.get([API_ROOT_URL, path].join, {:params => params}.merge(options)))
|
111
|
-
end
|
112
|
-
|
113
|
-
def post(path, params = {}, options = {:content_type => "text/plain; charset=UTF-8"})
|
114
|
-
params = authorization_params.merge(params)
|
115
|
-
parse_response(RestClient.post([API_ROOT_URL, path].join, params.merge(options)))
|
116
|
-
end
|
117
|
-
|
118
|
-
def parse_response(response)
|
119
|
-
# Do not post-process response if it isn't JSON. (ie. Binary file returned from /output_mo.)
|
120
|
-
return response.body unless response.headers[:content_type] =~ /json/
|
121
|
-
response_hash = JSON.parse(response)
|
122
|
-
|
123
|
-
raise ApiError, response.body if response_hash.has_key?("response") and response_hash["response"] != "ok"
|
124
|
-
response_hash
|
125
|
-
end
|
126
34
|
end
|
127
35
|
end
|