rosette-client 1.0.0 → 1.0.1

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: ca6f02478fbb347742958dd575df86db72b742c6
4
- data.tar.gz: c43753c2cedbfa451b2c69215df10c82a49d8979
3
+ metadata.gz: c5c191cd4c4fab60eb16d3fe33c9b2f0c3e26c65
4
+ data.tar.gz: 3a059a1b64f2bfd6e57d5dea12b26eaccc5825fe
5
5
  SHA512:
6
- metadata.gz: 28b9c44f0d32376b59a3f98e653e59050be2339293de23c8bb6315f4f90b5dac263a3e6e32aedc915a1c916446dc49bd64fc7ac7f5c21560cb052c4e19bf5012
7
- data.tar.gz: 5a85c962749d9ff7ba04082641ecac2d055822357491b0fab6e904dac377ee0eaee840d13fb09ecbea8b610e212ec3afd4c824f6ce1d3e64c24b6750f2a99829
6
+ metadata.gz: 2c1399a9fc2a39f05df85f840e2da3553114087c640372afc72a9cf10f695a5e5e2961292655b84814cd74c671f607f1cb026e0e3b0e31399149550bcc5d3279
7
+ data.tar.gz: d3a0980d7c2f063650a070385bb9e937e13d11696fc7a36973caab37396d556ad95b7a8cd04bc2a030196fb612bda205ed25daf64a01bb3ef670f1960c1cbcba
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
1
  == 1.0.0
2
2
 
3
- * Birthday!
3
+ * Birthday!
4
+
5
+ == 1.0.1
6
+
7
+ * Added locales endpoint to API.
data/README.md CHANGED
@@ -2,3 +2,57 @@ rosette-core
2
2
  ========
3
3
 
4
4
  [![Build Status](https://travis-ci.org/rosette-proj/rosette-client.svg?branch=master)](https://travis-ci.org/rosette-proj/rosette-client.svg?branch=master)
5
+
6
+ ## Installation
7
+
8
+ `gem install rosette-client`
9
+
10
+ ## Usage
11
+
12
+ Configure it! Create the file `~/.rosette/config.yml` with configuration options for your Rosette setup:
13
+
14
+ ```yaml
15
+ :host: rosette.mycompany.com
16
+ :port: 8080
17
+ :version: v1
18
+ ```
19
+
20
+ You should now be able to change directory into a git repository and run rosette commands. If you installed Ruby with rbenv, don't forget to run `rbenv rehash` to make rosette-client's executable available.
21
+
22
+ ## Commands
23
+
24
+ `git rosette commit [<ref>]`
25
+
26
+ Causes the given ref to be processed. If ref is omitted, the current `HEAD` is assumed. Phrases will be extracted and stored in the data store.
27
+
28
+ `git rosette diff <ref1> [<ref2> | <path>] [-- <path1> <path2> ...]`
29
+
30
+ Show the diff between two refs. If the second ref is omitted, the current `HEAD` is assumed. You can separate paths from ref arguments with `--` for consistency with the `git diff` command. This command will print phrases that were added, removed, or changed between the two refs.
31
+
32
+ `git rosette show [<ref>]`
33
+
34
+ Print the phrases that were added, removed, or changed in the given ref. If the ref is omitted, the current `HEAD` is assumed.
35
+
36
+ `git rosette status [<ref>]`
37
+
38
+ Print the translation status of the given ref. This includes how many phrases were found and the percentage translated in each supported locale. If ref is omitted, the current `HEAD` is assumed.
39
+
40
+ `git rosette snapshot [<ref>]`
41
+
42
+ Print a snapshot of the phrases for the given ref. If ref is omitted, the current `HEAD` is assumed.
43
+
44
+ `git rosette repo_snapshot [<ref>]`
45
+
46
+ Print the snapshot hash for the given ref. A snapshot is a hash of file paths to commit ids, where each commit id represents the commit the file last changed in. If ref is omitted, the current `HEAD` is assumed.
47
+
48
+ ## Requirements
49
+
50
+ You've gotta be running a properly configured instance of [rosette-server](https://github.com/rosette-proj/rosette-server) to communicate with, but otherwise there are no external requirements.
51
+
52
+ ## Running Tests
53
+
54
+ `bundle exec rake` should do the trick.
55
+
56
+ ## Authors
57
+
58
+ * Cameron C. Dutro: http://github.com/camertron
@@ -5,9 +5,12 @@ require 'rosette/client/repo'
5
5
 
6
6
  module Rosette
7
7
  module Client
8
- autoload :Api, 'rosette/client/api'
9
- autoload :Terminal, 'rosette/client/terminal'
10
- autoload :Commands, 'rosette/client/commands'
11
- autoload :Response, 'rosette/client/response'
8
+ autoload :Api, 'rosette/client/api'
9
+ autoload :Terminal, 'rosette/client/terminal'
10
+ autoload :Writer, 'rosette/client/writer'
11
+ autoload :Commands, 'rosette/client/commands'
12
+ autoload :Response, 'rosette/client/response'
13
+ autoload :HashResponse, 'rosette/client/response'
14
+ autoload :ArrayResponse, 'rosette/client/response'
12
15
  end
13
16
  end
@@ -16,6 +16,13 @@ module Rosette
16
16
 
17
17
  attr_reader :host, :port, :version
18
18
 
19
+ def self.url_join(*args)
20
+ args
21
+ .map { |arg| arg.gsub(/\A\/(.*)\/\z/, '\1') } # remove slashes
22
+ .reject { |arg| arg.empty? } # get rid of empties
23
+ .join('/')
24
+ end
25
+
19
26
  def initialize(options = {})
20
27
  @host = options.fetch(:host, DEFAULT_HOST)
21
28
  @port = options.fetch(:port, DEFAULT_PORT)
@@ -54,6 +61,10 @@ module Rosette
54
61
  wrap(make_request(:get, 'translations/export.json', params))
55
62
  end
56
63
 
64
+ def locales(params)
65
+ wrap(make_request(:get, 'locales.json', params))
66
+ end
67
+
57
68
  private
58
69
 
59
70
  def wrap(api_response)
@@ -92,11 +103,15 @@ module Rosette
92
103
  end
93
104
 
94
105
  def make_get_url(path, params)
95
- File.join(base_url, path, "?#{make_param_string(params)}")
106
+ url_join(base_url, path, "?#{make_param_string(params)}")
96
107
  end
97
108
 
98
109
  def make_post_url(path, params)
99
- File.join(base_url, path)
110
+ url_join(base_url, path)
111
+ end
112
+
113
+ def url_join(*args)
114
+ self.class.url_join(*args)
100
115
  end
101
116
 
102
117
  def post(url, params)
@@ -6,17 +6,18 @@ module Rosette
6
6
  module Client
7
7
 
8
8
  class Cli
9
- attr_reader :api, :terminal, :repo
9
+ attr_reader :api, :terminal, :writer, :repo
10
10
 
11
- def initialize(terminal, api, repo)
11
+ def initialize(terminal, writer, api, repo)
12
12
  @api = api
13
13
  @terminal = terminal
14
+ @writer = writer
14
15
  @repo = repo
15
16
  end
16
17
 
17
18
  def start(argv)
18
19
  if command_const = find_command_const(argv.first)
19
- command_const.new(api, terminal, repo, argv[1..-1]).execute
20
+ command_const.new(api, terminal, writer, repo, argv[1..-1]).execute
20
21
  else
21
22
  terminal.say("Command '#{argv.first}' not recognized.")
22
23
  end
@@ -16,18 +16,27 @@ module Rosette
16
16
  autoload :SnapshotCommandArgs, 'rosette/client/commands/snapshot_command'
17
17
  autoload :RepoSnapshotCommand, 'rosette/client/commands/repo_snapshot_command'
18
18
  autoload :RepoSnapshotCommandArgs, 'rosette/client/commands/repo_snapshot_command'
19
+ autoload :PullCommand, 'rosette/client/commands/pull_command'
20
+ autoload :PullCommandArgs, 'rosette/client/commands/pull_command'
19
21
 
20
22
  class Command
21
- attr_reader :api, :terminal, :repo
23
+ attr_reader :api, :terminal, :writer, :repo, :args
22
24
 
23
- def initialize(api, terminal, repo)
25
+ def initialize(api, terminal, writer, repo, argv)
24
26
  @api = api
25
27
  @terminal = terminal
28
+ @writer = writer
26
29
  @repo = repo
30
+ @args = parse_args(argv)
27
31
  end
28
32
 
29
33
  protected
30
34
 
35
+ def parse_args(args)
36
+ raise NotImplementedError,
37
+ "#{__method__} must be defined in derived classes"
38
+ end
39
+
31
40
  def print_hash(hash)
32
41
  hash.each_pair do |key, value|
33
42
  terminal.say("#{key}: #{value}")
@@ -13,13 +13,6 @@ module Rosette
13
13
  end
14
14
 
15
15
  class CommitCommand < Command
16
- attr_reader :args
17
-
18
- def initialize(api, terminal, repo, argv)
19
- super(api, terminal, repo)
20
- @args = CommitCommandArgs.from_argv(argv, repo)
21
- end
22
-
23
16
  def execute
24
17
  terminal.say("Committing phrases for '#{args.ref}'...")
25
18
 
@@ -35,6 +28,12 @@ module Rosette
35
28
  terminal.say('done.')
36
29
  end
37
30
  end
31
+
32
+ private
33
+
34
+ def parse_args(args)
35
+ CommitCommandArgs.from_argv(args, repo)
36
+ end
38
37
  end
39
38
 
40
39
  end
@@ -41,13 +41,6 @@ module Rosette
41
41
  end
42
42
 
43
43
  class DiffCommand < Command
44
- attr_reader :args
45
-
46
- def initialize(api, terminal, repo, argv)
47
- super(api, terminal, repo)
48
- @args = DiffCommandArgs.from_argv(argv, repo)
49
- end
50
-
51
44
  def execute
52
45
  response = api.diff(
53
46
  repo_name: derive_repo_name,
@@ -63,6 +56,10 @@ module Rosette
63
56
 
64
57
  private
65
58
 
59
+ def parse_args(args)
60
+ DiffCommandArgs.from_argv(args, repo)
61
+ end
62
+
66
63
  def add_str_for(change)
67
64
  str = "#{change['key']}"
68
65
  meta_key = change['meta_key']
@@ -0,0 +1,115 @@
1
+ # encoding: UTF-8
2
+
3
+ # git rs pull [ref] -f file_pattern -s serializer
4
+
5
+ require 'base64'
6
+ require 'optparse'
7
+
8
+ module Rosette
9
+ module Client
10
+ module Commands
11
+
12
+ PullCommandArgs = Struct.new(:ref, :file_pattern, :serializer) do
13
+ def self.from_argv(argv, repo, terminal)
14
+ options = {}
15
+
16
+ parser = OptionParser.new do |opts|
17
+ desc = "File pattern to use. Can contain these placeholders:\n" +
18
+ "* %{locale.code}: The full locale code, eg. pt-BR\n" +
19
+ "* %{locale.territory}: The locale's territory part, eg. 'BR'" +
20
+ "* %{locale.language}: The locale's language part, eg. 'pt'"
21
+
22
+ opts.on('-f pattern', '--file-pattern pattern', desc) do |pattern|
23
+ pattern.strip!
24
+ options[:file_pattern] = pattern.empty? ? nil : pattern
25
+ end
26
+
27
+ desc = 'The serializer to use. Translations will be requested in ' +
28
+ 'this format.'
29
+
30
+ opts.on('-s serializer', '--serializer serializer', desc) do |serializer|
31
+ serializer.strip!
32
+ options[:serializer] = serializer.empty? ? nil : serializer
33
+ end
34
+ end
35
+
36
+ parser.parse(argv[1..-1])
37
+ options[:ref] = repo.rev_parse(argv[0] || repo.get_head)
38
+ validate_options!(options, terminal)
39
+
40
+ new(
41
+ options[:ref],
42
+ options[:file_pattern],
43
+ options[:serializer]
44
+ )
45
+ end
46
+
47
+ def self.validate_options!(options, terminal)
48
+ unless options.include?(:file_pattern)
49
+ terminal.say('Please supply a file pattern via the -f option')
50
+ exit 1
51
+ end
52
+
53
+ unless options.include?(:serializer)
54
+ terminal.say('Please supply a serializer via the -s option')
55
+ exit 1
56
+ end
57
+ end
58
+ end
59
+
60
+ # a show is really just a diff against your parent (so the inheritance makes sense)
61
+ class PullCommand < Command
62
+ def execute
63
+ locales = api.locales(
64
+ repo_name: derive_repo_name
65
+ )
66
+
67
+ handle_error(locales) do |locales|
68
+ export_locales(locales)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def parse_args(args)
75
+ PullCommandArgs.from_argv(args, repo, terminal)
76
+ end
77
+
78
+ def export_locales(locales)
79
+ locales.each do |locale|
80
+ export_locale(locale)
81
+ end
82
+ end
83
+
84
+ def export_locale(locale)
85
+ response = api.export({
86
+ repo_name: derive_repo_name,
87
+ ref: args.ref,
88
+ locale: locale['code'],
89
+ serializer: args.serializer,
90
+ base_64_encode: true
91
+ })
92
+
93
+ handle_error(response) do |response|
94
+ payload = Base64.decode64(response.payload)
95
+ path = path_for(locale)
96
+ terminal.say("Writing #{path}")
97
+
98
+ writer.open(path, 'w+') do |f|
99
+ f.write(payload)
100
+ end
101
+ end
102
+ end
103
+
104
+ def path_for(locale)
105
+ args.file_pattern % {
106
+ :'locale.code' => locale['code'],
107
+ :'locale.territory' => locale['territory'],
108
+ :'locale.language' => locale['language']
109
+ }
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -14,13 +14,6 @@ module Rosette
14
14
 
15
15
  # a show is really just a diff against your parent (so the inheritance makes sense)
16
16
  class RepoSnapshotCommand < Command
17
- attr_reader :args
18
-
19
- def initialize(api, terminal, repo, argv)
20
- super(api, terminal, repo)
21
- @args = RepoSnapshotCommandArgs.from_argv(argv, repo)
22
- end
23
-
24
17
  def execute
25
18
  response = api.repo_snapshot(
26
19
  repo_name: derive_repo_name,
@@ -31,6 +24,12 @@ module Rosette
31
24
  print_hash(response.attributes)
32
25
  end
33
26
  end
27
+
28
+ private
29
+
30
+ def parse_args(args)
31
+ RepoSnapshotCommandArgs.from_argv(args, repo)
32
+ end
34
33
  end
35
34
 
36
35
  end
@@ -14,15 +14,6 @@ module Rosette
14
14
 
15
15
  # a show is really just a diff against your parent (so the inheritance makes sense)
16
16
  class ShowCommand < DiffCommand
17
- attr_reader :args
18
-
19
- def initialize(api, terminal, repo, argv)
20
- @api = api
21
- @terminal = terminal
22
- @repo = repo
23
- @args = ShowCommandArgs.from_argv(argv, repo)
24
- end
25
-
26
17
  def execute
27
18
  response = api.show(
28
19
  repo_name: derive_repo_name,
@@ -33,6 +24,12 @@ module Rosette
33
24
  print_diff(response.attributes)
34
25
  end
35
26
  end
27
+
28
+ private
29
+
30
+ def parse_args(args)
31
+ ShowCommandArgs.from_argv(args, repo)
32
+ end
36
33
  end
37
34
 
38
35
  end
@@ -14,13 +14,6 @@ module Rosette
14
14
 
15
15
  # a show is really just a diff against your parent (so the inheritance makes sense)
16
16
  class SnapshotCommand < Command
17
- attr_reader :args
18
-
19
- def initialize(api, terminal, repo, argv)
20
- super(api, terminal, repo)
21
- @args = SnapshotCommandArgs.from_argv(argv, repo)
22
- end
23
-
24
17
  def execute
25
18
  response = api.snapshot(
26
19
  repo_name: derive_repo_name,
@@ -34,6 +27,10 @@ module Rosette
34
27
  end
35
28
  end
36
29
  end
30
+
31
+ def parse_args(args)
32
+ SnapshotCommandArgs.from_argv(args, repo)
33
+ end
37
34
  end
38
35
 
39
36
  end
@@ -14,15 +14,6 @@ module Rosette
14
14
 
15
15
  # a show is really just a diff against your parent (so the inheritance makes sense)
16
16
  class StatusCommand < Command
17
- attr_reader :args
18
-
19
- def initialize(api, terminal, repo, argv)
20
- @api = api
21
- @terminal = terminal
22
- @repo = repo
23
- @args = StatusCommandArgs.from_argv(argv, repo)
24
- end
25
-
26
17
  def execute
27
18
  response = api.status(
28
19
  repo_name: derive_repo_name,
@@ -40,6 +31,10 @@ module Rosette
40
31
 
41
32
  private
42
33
 
34
+ def parse_args(args)
35
+ StatusCommandArgs.from_argv(args, repo)
36
+ end
37
+
43
38
  HEADER = ['Locale', 'Phrases', 'Translations', 'Percent']
44
39
 
45
40
  def build_locale_table(locales, phrase_count)
@@ -4,11 +4,18 @@ module Rosette
4
4
  module Client
5
5
 
6
6
  class Response
7
- attr_reader :attributes
8
-
9
- def self.from_api_response(hash)
10
- new(hash)
7
+ def self.from_api_response(hash_or_array)
8
+ case hash_or_array
9
+ when Hash
10
+ HashResponse.new(hash_or_array)
11
+ else
12
+ ArrayResponse.new(hash_or_array)
13
+ end
11
14
  end
15
+ end
16
+
17
+ class HashResponse
18
+ attr_reader :attributes
12
19
 
13
20
  def initialize(hash)
14
21
  @attributes = hash
@@ -34,5 +41,23 @@ module Rosette
34
41
  end
35
42
  end
36
43
 
44
+ class ArrayResponse < Array
45
+ def initialize(array)
46
+ replace(array)
47
+ end
48
+
49
+ def error?
50
+ false
51
+ end
52
+
53
+ def success?
54
+ true
55
+ end
56
+
57
+ def attributes
58
+ self
59
+ end
60
+ end
61
+
37
62
  end
38
63
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rosette
4
4
  module Client
5
- VERSION = "1.0.0"
5
+ VERSION = "1.0.1"
6
6
  end
7
7
  end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yaml'
4
+
5
+ module Rosette
6
+ module Client
7
+
8
+ class Writer
9
+ def self.open(path, mode, &block)
10
+ File.open(path, mode, &block)
11
+ end
12
+ end
13
+
14
+ end
15
+ end
data/spec/api_spec.rb CHANGED
@@ -27,28 +27,36 @@ describe Api do
27
27
  let(:api) { api_class.new }
28
28
  let(:params) { { param: 'value' } }
29
29
  let(:endpoint) { method }
30
+ let(:response_type) { HashResponse }
30
31
 
31
32
  before(:each) do
32
- url = "http://localhost:8080/v1/#{path}"
33
+ url = Api.url_join('http://localhost:8080/v1', path)
33
34
 
34
35
  args = case verb
35
36
  when :get
36
- ["#{url}/#{endpoint_override rescue endpoint}.json/?param=value"]
37
+ [Api.url_join(url, "#{endpoint_override rescue endpoint}.json", '?param=value')]
37
38
  else
38
- ["#{url}/#{endpoint_override rescue endpoint}.json", params]
39
+ [Api.url_join(url, "#{endpoint_override rescue endpoint}.json"), params]
39
40
  end
40
41
 
41
42
  allow(api).to receive(verb).with(*args).and_return('{"foo":"bar"}')
42
43
  end
43
44
 
44
45
  it 'wraps the response in a Response object' do
45
- expect(api.send(endpoint, params)).to be_a(Response)
46
+ expect(api.send(endpoint, params)).to be_a(response_type)
46
47
  end
47
48
  end
48
49
 
49
50
  context 'get requests' do
50
51
  let(:verb) { :get }
51
52
 
53
+ describe '#locales' do
54
+ let(:path) { '' }
55
+ let(:method) { :locales }
56
+ let(:response_type) { ArrayResponse }
57
+ it_behaves_like 'an api endpoint'
58
+ end
59
+
52
60
  describe '#diff' do
53
61
  let(:path) { 'git' }
54
62
  let(:method) { :diff }
data/spec/cli_spec.rb CHANGED
@@ -9,7 +9,8 @@ describe Cli do
9
9
  let(:base_repo) { TmpRepo.new }
10
10
  let(:repo) { Repo.new(base_repo.working_dir) }
11
11
  let(:terminal) { FakeTerminal.new }
12
- let(:cli) { Cli.new(terminal, api, repo) }
12
+ let(:writer) { FakeWriter.new }
13
+ let(:cli) { Cli.new(terminal, writer, api, repo) }
13
14
 
14
15
  before(:each) do
15
16
  add_user_to(base_repo)
@@ -21,7 +22,7 @@ describe Cli do
21
22
  base_repo.add_all
22
23
  base_repo.commit('Initial commit')
23
24
 
24
- expect(api).to receive(:commit).and_return(Response.new({ 'foo' => 'bar' }))
25
+ expect(api).to receive(:commit).and_return(Response.from_api_response({ 'foo' => 'bar' }))
25
26
  cli.start(['commit', base_repo.git('rev-parse HEAD').strip])
26
27
  end
27
28
 
@@ -10,9 +10,10 @@ describe CommitCommand do
10
10
  let(:base_repo) { TmpRepo.new }
11
11
  let(:repo) { Repo.new(base_repo.working_dir) }
12
12
  let(:terminal) { FakeTerminal.new }
13
+ let(:writer) { FakeWriter.new }
13
14
  let(:repo_name) { 'my_awesome_repo' }
14
15
  let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
- let(:command) { CommitCommand.new(api, terminal, repo, [commit_id]) }
16
+ let(:command) { CommitCommand.new(api, terminal, writer, repo, [commit_id]) }
16
17
 
17
18
  before(:each) do
18
19
  add_user_to(base_repo)
@@ -32,7 +33,7 @@ describe CommitCommand do
32
33
  it 'makes a commit api call' do
33
34
  expect(api).to receive(:commit)
34
35
  .with(repo_name: repo_name, ref: commit_id)
35
- .and_return(Response.new({}))
36
+ .and_return(Response.from_api_response({}))
36
37
 
37
38
  command.execute
38
39
  end
@@ -41,7 +42,7 @@ describe CommitCommand do
41
42
  expect(api).to receive(:commit)
42
43
  .with(repo_name: repo_name, ref: commit_id)
43
44
  .and_return(
44
- Response.new(
45
+ Response.from_api_response(
45
46
  { 'added' => 1, 'removed' => 2, 'modified' => 3 }
46
47
  )
47
48
  )
@@ -57,7 +58,9 @@ describe CommitCommand do
57
58
  expect(api).to receive(:commit)
58
59
  .with(repo_name: repo_name, ref: commit_id)
59
60
  .and_return(
60
- Response.new({ 'error' => 'Jelly beans', 'added' => 1 })
61
+ Response.from_api_response(
62
+ 'error' => 'Jelly beans', 'added' => 1
63
+ )
61
64
  )
62
65
 
63
66
  command.execute
@@ -84,8 +84,15 @@ describe 'diff' do
84
84
 
85
85
  describe DiffCommand do
86
86
  let(:api) { double }
87
- let(:command) { DiffCommand.new(api, terminal, repo, [master_commit_id, new_branch_commit_id]) }
87
+ let(:command) do
88
+ DiffCommand.new(
89
+ api, terminal, writer, repo,
90
+ [master_commit_id, new_branch_commit_id]
91
+ )
92
+ end
93
+
88
94
  let(:terminal) { FakeTerminal.new }
95
+ let(:writer) { FakeWriter.new }
89
96
 
90
97
  describe '#execute' do
91
98
  it 'makes a diff api call' do
@@ -96,7 +103,7 @@ describe 'diff' do
96
103
  diff_point_ref: master_commit_id,
97
104
  paths: ''
98
105
  )
99
- .and_return(Response.new({}))
106
+ .and_return(Response.from_api_response({}))
100
107
 
101
108
  command.execute
102
109
  end
@@ -110,7 +117,7 @@ describe 'diff' do
110
117
  paths: ''
111
118
  )
112
119
  .and_return(
113
- Response.new(
120
+ Response.from_api_response(
114
121
  sample_diff(new_branch_commit_id)
115
122
  )
116
123
  )
@@ -136,7 +143,7 @@ describe 'diff' do
136
143
  paths: ''
137
144
  )
138
145
  .and_return(
139
- Response.new({ 'error' => 'Jelly beans' }.merge(
146
+ Response.from_api_response({ 'error' => 'Jelly beans' }.merge(
140
147
  sample_diff(new_branch_commit_id))
141
148
  )
142
149
  )
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+ include Rosette::Client::Commands
7
+
8
+ describe PullCommandArgs do
9
+ let(:base_repo) { TmpRepo.new }
10
+ let(:repo) { Repo.new(base_repo.working_dir) }
11
+ let(:terminal) { FakeTerminal.new }
12
+ let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
13
+ let(:repo_name) { 'my_awesome_repo' }
14
+
15
+ before(:each) do
16
+ add_user_to(base_repo)
17
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
18
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
19
+ base_repo.add_all
20
+ base_repo.commit('Initial commit')
21
+ end
22
+
23
+ describe 'from_argv' do
24
+ it 'exits and prints message when not given a file pattern' do
25
+ expect do
26
+ PullCommandArgs.from_argv(
27
+ [commit_id, '--serializer', 'foo/bar'], repo, terminal
28
+ )
29
+ end.to raise_error(SystemExit)
30
+
31
+ expect(terminal).to have_said(
32
+ 'Please supply a file pattern via the -f option'
33
+ )
34
+ end
35
+
36
+ it 'exits and prints message when not given a serializer' do
37
+ expect do
38
+ PullCommandArgs.from_argv(
39
+ [commit_id, '--file-pattern', 'teapot'], repo, terminal
40
+ )
41
+ end.to raise_error(SystemExit)
42
+
43
+ expect(terminal).to have_said(
44
+ 'Please supply a serializer via the -s option'
45
+ )
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'base64'
5
+
6
+ include Rosette::Client
7
+ include Rosette::Client::Commands
8
+
9
+ describe PullCommand do
10
+ let(:api) { double }
11
+ let(:base_repo) { TmpRepo.new }
12
+ let(:repo) { Repo.new(base_repo.working_dir) }
13
+ let(:terminal) { FakeTerminal.new }
14
+ let(:writer) { FakeWriter.new }
15
+ let(:repo_name) { 'my_awesome_repo' }
16
+ let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
17
+ let(:serializer) { 'yaml/rails' }
18
+ let(:file_pattern) { 'config/locales/%{locale.code}.yml' }
19
+ let(:command) do
20
+ PullCommand.new(
21
+ api, terminal, writer, repo, [
22
+ commit_id, "-f #{file_pattern}", "-s #{serializer}"
23
+ ]
24
+ )
25
+ end
26
+
27
+ let(:locales) do
28
+ [
29
+ {
30
+ 'code' => 'ja-JP',
31
+ 'language' => 'ja',
32
+ 'territory' => 'JP'
33
+ }, {
34
+ 'code' => 'pt-BR',
35
+ 'language' => 'pt',
36
+ 'territory' => 'BR'
37
+ }
38
+ ]
39
+ end
40
+
41
+ let(:params) do
42
+ {
43
+ repo_name: repo_name,
44
+ ref: commit_id,
45
+ serializer: serializer,
46
+ base_64_encode: true
47
+ }
48
+ end
49
+
50
+ before(:each) do
51
+ add_user_to(base_repo)
52
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
53
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
54
+ base_repo.add_all
55
+ base_repo.commit('Initial commit')
56
+ end
57
+
58
+ around(:each) do |example|
59
+ Dir.chdir(base_repo.working_dir) do
60
+ example.run
61
+ end
62
+ end
63
+
64
+ context 'with a mocked list of locales' do
65
+ before(:each) do
66
+ expect(api).to receive(:locales)
67
+ .with(repo_name: repo_name)
68
+ .and_return(Response.from_api_response(locales))
69
+
70
+ locales.each do |locale|
71
+ expect(api).to receive(:export)
72
+ .with(params.merge(locale: locale['code']))
73
+ .and_return(
74
+ Response.from_api_response({
75
+ 'payload' => Base64.encode64("payload for #{locale['code']}")
76
+ })
77
+ )
78
+ end
79
+ end
80
+
81
+ describe '#execute' do
82
+ it 'exports translations for each locale' do
83
+ command.execute
84
+
85
+ locales.each do |locale|
86
+ file = file_pattern % { :'locale.code' => locale['code'] }
87
+ expect(writer).to have_written_content_for(file)
88
+ expect(writer.contents_of(file)).to eq("payload for #{locale['code']}")
89
+ end
90
+ end
91
+
92
+ context 'with a territory-based file pattern' do
93
+ let(:file_pattern) { 'config/locales/%{locale.territory}.yml' }
94
+
95
+ it 'exports translations for each locale' do
96
+ command.execute
97
+
98
+ locales.each do |locale|
99
+ file = file_pattern % { :'locale.territory' => locale['territory'] }
100
+ expect(writer).to have_written_content_for(file)
101
+ expect(writer.contents_of(file)).to eq("payload for #{locale['code']}")
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'with a language-based file pattern' do
107
+ let(:file_pattern) { 'config/locales/%{locale.language}.yml' }
108
+
109
+ it 'exports translations for each locale' do
110
+ command.execute
111
+
112
+ locales.each do |locale|
113
+ file = file_pattern % { :'locale.language' => locale['language'] }
114
+ expect(writer).to have_written_content_for(file)
115
+ expect(writer.contents_of(file)).to eq("payload for #{locale['code']}")
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -10,9 +10,14 @@ describe RepoSnapshotCommand do
10
10
  let(:base_repo) { TmpRepo.new }
11
11
  let(:repo) { Repo.new(base_repo.working_dir) }
12
12
  let(:terminal) { FakeTerminal.new }
13
+ let(:writer) { FakeWriter.new }
13
14
  let(:repo_name) { 'my_awesome_repo' }
14
15
  let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
- let(:command) { RepoSnapshotCommand.new(api, terminal, repo, [commit_id]) }
16
+ let(:command) do
17
+ RepoSnapshotCommand.new(
18
+ api, terminal, writer, repo, [commit_id]
19
+ )
20
+ end
16
21
 
17
22
  before(:each) do
18
23
  add_user_to(base_repo)
@@ -32,7 +37,7 @@ describe RepoSnapshotCommand do
32
37
  it 'makes a repo_snapshot api call' do
33
38
  expect(api).to receive(:repo_snapshot)
34
39
  .with(repo_name: repo_name, ref: commit_id)
35
- .and_return(Response.new({}))
40
+ .and_return(Response.from_api_response({}))
36
41
 
37
42
  command.execute
38
43
  end
@@ -41,7 +46,7 @@ describe RepoSnapshotCommand do
41
46
  expect(api).to receive(:repo_snapshot)
42
47
  .with(repo_name: repo_name, ref: commit_id)
43
48
  .and_return(
44
- Response.new({
49
+ Response.from_api_response({
45
50
  'file1.txt' => 'abc123',
46
51
  'path/file2.txt' => 'def456',
47
52
  'my/awesome/file3.rb' => 'ghi789'
@@ -59,7 +64,7 @@ describe RepoSnapshotCommand do
59
64
  expect(api).to receive(:repo_snapshot)
60
65
  .with(repo_name: repo_name, ref: commit_id)
61
66
  .and_return(
62
- Response.new({ 'error' => 'Jelly beans', 'file1.txt' => 'abc123' })
67
+ Response.from_api_response({ 'error' => 'Jelly beans', 'file1.txt' => 'abc123' })
63
68
  )
64
69
 
65
70
  command.execute
@@ -10,9 +10,10 @@ describe ShowCommand do
10
10
  let(:base_repo) { TmpRepo.new }
11
11
  let(:repo) { Repo.new(base_repo.working_dir) }
12
12
  let(:terminal) { FakeTerminal.new }
13
+ let(:writer) { FakeWriter.new }
13
14
  let(:repo_name) { 'my_awesome_repo' }
14
15
  let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
- let(:command) { ShowCommand.new(api, terminal, repo, [commit_id]) }
16
+ let(:command) { ShowCommand.new(api, terminal, writer, repo, [commit_id]) }
16
17
 
17
18
  before(:each) do
18
19
  add_user_to(base_repo)
@@ -32,7 +33,7 @@ describe ShowCommand do
32
33
  it 'makes a commit api call' do
33
34
  expect(api).to receive(:show)
34
35
  .with(repo_name: repo_name, ref: commit_id)
35
- .and_return(Response.new({}))
36
+ .and_return(Response.from_api_response({}))
36
37
 
37
38
  command.execute
38
39
  end
@@ -41,7 +42,7 @@ describe ShowCommand do
41
42
  expect(api).to receive(:show)
42
43
  .with(repo_name: repo_name, ref: commit_id)
43
44
  .and_return(
44
- Response.new(sample_diff(commit_id))
45
+ Response.from_api_response(sample_diff(commit_id))
45
46
  )
46
47
 
47
48
  command.execute
@@ -60,7 +61,9 @@ describe ShowCommand do
60
61
  expect(api).to receive(:show)
61
62
  .with(repo_name: repo_name, ref: commit_id)
62
63
  .and_return(
63
- Response.new({ 'error' => 'Jelly beans' }.merge(sample_diff(commit_id)))
64
+ Response.from_api_response(
65
+ { 'error' => 'Jelly beans' }.merge(sample_diff(commit_id))
66
+ )
64
67
  )
65
68
 
66
69
  command.execute
@@ -10,9 +10,10 @@ describe SnapshotCommand do
10
10
  let(:base_repo) { TmpRepo.new }
11
11
  let(:repo) { Repo.new(base_repo.working_dir) }
12
12
  let(:terminal) { FakeTerminal.new }
13
+ let(:writer) { FakeWriter.new }
13
14
  let(:repo_name) { 'my_awesome_repo' }
14
15
  let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
- let(:command) { SnapshotCommand.new(api, terminal, repo, [commit_id]) }
16
+ let(:command) { SnapshotCommand.new(api, terminal, writer, repo, [commit_id]) }
16
17
 
17
18
  before(:each) do
18
19
  add_user_to(base_repo)
@@ -32,7 +33,7 @@ describe SnapshotCommand do
32
33
  it 'makes a snapshot api call' do
33
34
  expect(api).to receive(:snapshot)
34
35
  .with(repo_name: repo_name, ref: commit_id)
35
- .and_return(Response.new({}))
36
+ .and_return(Response.from_api_response({}))
36
37
 
37
38
  command.execute
38
39
  end
@@ -41,7 +42,7 @@ describe SnapshotCommand do
41
42
  expect(api).to receive(:snapshot)
42
43
  .with(repo_name: repo_name, ref: commit_id)
43
44
  .and_return(
44
- Response.new([
45
+ Response.from_api_response([
45
46
  { 'key' => 'Foo', 'commit_id' => 'abc123' },
46
47
  { 'key' => 'Bar', 'commit_id' => 'def456' },
47
48
  { 'key' => 'Baz', 'commit_id' => 'ghi789' }
@@ -62,7 +63,7 @@ describe SnapshotCommand do
62
63
  expect(api).to receive(:snapshot)
63
64
  .with(repo_name: repo_name, ref: commit_id)
64
65
  .and_return(
65
- Response.new({ 'error' => 'Jelly beans' })
66
+ Response.from_api_response({ 'error' => 'Jelly beans' })
66
67
  )
67
68
 
68
69
  command.execute
@@ -10,9 +10,10 @@ describe StatusCommand do
10
10
  let(:base_repo) { TmpRepo.new }
11
11
  let(:repo) { Repo.new(base_repo.working_dir) }
12
12
  let(:terminal) { FakeTerminal.new }
13
+ let(:writer) { FakeWriter.new }
13
14
  let(:repo_name) { 'my_awesome_repo' }
14
15
  let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
- let(:command) { StatusCommand.new(api, terminal, repo, [commit_id]) }
16
+ let(:command) { StatusCommand.new(api, terminal, writer, repo, [commit_id]) }
16
17
 
17
18
  before(:each) do
18
19
  add_user_to(base_repo)
@@ -32,7 +33,7 @@ describe StatusCommand do
32
33
  it 'makes a commit api call' do
33
34
  expect(api).to receive(:status)
34
35
  .with(repo_name: repo_name, ref: commit_id)
35
- .and_return(Response.new({}))
36
+ .and_return(Response.from_api_response({}))
36
37
 
37
38
  command.execute
38
39
  end
@@ -41,7 +42,7 @@ describe StatusCommand do
41
42
  expect(api).to receive(:status)
42
43
  .with(repo_name: repo_name, ref: commit_id)
43
44
  .and_return(
44
- Response.new({
45
+ Response.from_api_response({
45
46
  'commit_id' => commit_id,
46
47
  'status' => 'UNTRANSLATED',
47
48
  'phrase_count' => 10,
@@ -67,7 +68,7 @@ describe StatusCommand do
67
68
  expect(api).to receive(:status)
68
69
  .with(repo_name: repo_name, ref: commit_id)
69
70
  .and_return(
70
- Response.new({ 'error' => 'Jelly beans' })
71
+ Response.from_api_response({ 'error' => 'Jelly beans' })
71
72
  )
72
73
 
73
74
  command.execute
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+
3
+ class FakeWriter
4
+ attr_reader :streams
5
+
6
+ def initialize
7
+ @streams = {}
8
+ end
9
+
10
+ def open(file, mode)
11
+ streams[file] ||= StringIO.new
12
+ yield streams[file]
13
+ end
14
+
15
+ def contents_of(file)
16
+ streams[file].string
17
+ end
18
+
19
+ def has_written_content_for?(file)
20
+ streams.include?(file)
21
+ end
22
+ end
@@ -5,7 +5,34 @@ require 'spec_helper'
5
5
  include Rosette::Client
6
6
 
7
7
  describe Response do
8
- let(:response_class) { Response }
8
+ describe 'from_api_response' do
9
+ it 'accepts a hash of attributes and returns an instance of HashResponse' do
10
+ response = Response.from_api_response({ 'foo' => 'bar' })
11
+ expect(response).to be_a(HashResponse)
12
+ expect(response.attributes).to eq({ 'foo' => 'bar' })
13
+ end
14
+
15
+ it 'accepts an array and returns an instance of ArrayResponse' do
16
+ response = Response.from_api_response(['foo', 'bar'])
17
+ expect(response).to be_a(ArrayResponse)
18
+ expect(response.attributes).to eq(['foo', 'bar'])
19
+ end
20
+ end
21
+ end
22
+
23
+ describe ArrayResponse do
24
+ let(:response_class) { ArrayResponse }
25
+
26
+ describe '#attributes' do
27
+ it 'returns itself' do
28
+ response = response_class.new(['foo', 'bar'])
29
+ expect(response.attributes).to eq(['foo', 'bar'])
30
+ end
31
+ end
32
+ end
33
+
34
+ describe HashResponse do
35
+ let(:response_class) { HashResponse }
9
36
 
10
37
  it 'responds to methods that are also keys in the attributes hash' do
11
38
  response = response_class.new({ 'hello' => 'world' })
@@ -17,14 +44,6 @@ describe Response do
17
44
  expect(response.nothing).to be_nil
18
45
  end
19
46
 
20
- describe 'from_api_response' do
21
- it 'accepts a hash of attributes and returns an instance of Response' do
22
- response = response_class.from_api_response({ 'foo' => 'bar' })
23
- expect(response).to be_a(response_class)
24
- expect(response.attributes).to eq({ 'foo' => 'bar' })
25
- end
26
- end
27
-
28
47
  describe '#error?' do
29
48
  it 'returns true if the attributes hash contains an "error" key' do
30
49
  response = response_class.new({ 'error' => 'jelly beans' })
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,7 @@ require 'pry-nav'
5
5
  require 'rspec'
6
6
  require 'rosette/client'
7
7
  require 'helpers/fake_terminal'
8
+ require 'helpers/fake_writer'
8
9
  require 'tmp-repo'
9
10
 
10
11
  RSpec.configure do |config|
metadata CHANGED
@@ -1,45 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rosette-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-12 00:00:00.000000000 Z
11
+ date: 2015-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: colorize
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ~>
18
17
  - !ruby/object:Gem::Version
19
18
  version: 0.7.0
20
- type: :runtime
19
+ name: colorize
21
20
  prerelease: false
21
+ type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.7.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: json
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ~>
32
31
  - !ruby/object:Gem::Version
33
32
  version: 1.8.0
34
- type: :runtime
33
+ name: json
35
34
  prerelease: false
35
+ type: :runtime
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.8.0
41
- description: Git command integration for the Rosette internationalization platform
42
- that manages the translatable content in the source files of a git repository.
41
+ description: Git command integration for the Rosette internationalization platform that manages the translatable content in the source files of a git repository.
43
42
  email:
44
43
  - camertron@gmail.com
45
44
  executables:
@@ -47,33 +46,38 @@ executables:
47
46
  extensions: []
48
47
  extra_rdoc_files: []
49
48
  files:
49
+ - lib/rosette/client.rb
50
50
  - lib/rosette/client/api.rb
51
51
  - lib/rosette/client/cli.rb
52
+ - lib/rosette/client/commands.rb
53
+ - lib/rosette/client/repo.rb
54
+ - lib/rosette/client/response.rb
55
+ - lib/rosette/client/terminal.rb
56
+ - lib/rosette/client/version.rb
57
+ - lib/rosette/client/writer.rb
52
58
  - lib/rosette/client/commands/commit_command.rb
53
59
  - lib/rosette/client/commands/diff_command.rb
60
+ - lib/rosette/client/commands/pull_command.rb
54
61
  - lib/rosette/client/commands/repo_snapshot_command.rb
55
62
  - lib/rosette/client/commands/show_command.rb
56
63
  - lib/rosette/client/commands/snapshot_command.rb
57
64
  - lib/rosette/client/commands/status_command.rb
58
- - lib/rosette/client/commands.rb
59
- - lib/rosette/client/repo.rb
60
- - lib/rosette/client/response.rb
61
- - lib/rosette/client/terminal.rb
62
- - lib/rosette/client/version.rb
63
- - lib/rosette/client.rb
64
65
  - spec/api_spec.rb
65
66
  - spec/cli_spec.rb
67
+ - spec/repo_spec.rb
68
+ - spec/response_spec.rb
69
+ - spec/spec_helper.rb
70
+ - spec/terminal_spec.rb
66
71
  - spec/commands/commit_command_spec.rb
67
72
  - spec/commands/diff_command_spec.rb
73
+ - spec/commands/pull_command_args_spec.rb
74
+ - spec/commands/pull_command_spec.rb
68
75
  - spec/commands/repo_snapshot_command_spec.rb
69
76
  - spec/commands/show_command_spec.rb
70
77
  - spec/commands/snapshot_command_spec.rb
71
78
  - spec/commands/status_command_spec.rb
72
79
  - spec/helpers/fake_terminal.rb
73
- - spec/repo_spec.rb
74
- - spec/response_spec.rb
75
- - spec/spec_helper.rb
76
- - spec/terminal_spec.rb
80
+ - spec/helpers/fake_writer.rb
77
81
  - Gemfile
78
82
  - History.txt
79
83
  - README.md
@@ -83,7 +87,7 @@ files:
83
87
  homepage: http://github.com/camertron
84
88
  licenses: []
85
89
  metadata: {}
86
- post_install_message:
90
+ post_install_message:
87
91
  rdoc_options: []
88
92
  require_paths:
89
93
  - lib
@@ -98,11 +102,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
102
  - !ruby/object:Gem::Version
99
103
  version: '0'
100
104
  requirements: []
101
- rubyforge_project:
102
- rubygems_version: 2.0.14
103
- signing_key:
105
+ rubyforge_project:
106
+ rubygems_version: 2.1.9
107
+ signing_key:
104
108
  specification_version: 4
105
- summary: Git command integration for the Rosette internationalization platform that
106
- manages the translatable content in the source files of a git repository.
109
+ summary: Git command integration for the Rosette internationalization platform that manages the translatable content in the source files of a git repository.
107
110
  test_files: []
108
111
  has_rdoc: true